首先让我们澄清一下浅克隆和深克隆的区别:
浅克隆是克隆了其原始属性但其 REFERENCE 属性仍引用原始属性的克隆。
请允许我澄清一下:
let original = {
foo: "brlja",
howBigIsUniverse: Infinity,
mrMethodLookAtMe: () => "they call me mr. Method",
moo: {
moo: "MOO"
}
};
// shallow copy
let shallow = Object.assign({}, original);
console.log(original, shallow); // looks OK
shallow.moo.moo = "NOT MOO";
console.log(original, shallow); // changing the copy changed the original
注意如何改变浅拷贝的非原始属性的内部属性反映在原始对象上。
那么我们为什么要使用浅拷贝呢?
什么时候使用浅拷贝?
- 对象的所有属性都是基元
- 您正在制作部分副本,其中所有复制的属性都是基元
- 您不关心原件的命运(是否有理由复制而不使用原件?)
好的,让我们开始制作一个正确的(深层)副本。深拷贝显然应该将原始对象按值而不是引用处理到克隆中。当我们深入研究对象时,这应该会持续存在。因此,如果我们在原始属性中获得 X 级深度嵌套对象,它仍然应该是副本,而不是内存中对同一事物的引用。
大多数人的建议是滥用 JSON API。他们认为将一个对象变成一个字符串,然后通过它返回一个对象会产生一个深拷贝。嗯,是的,也不是。让我们尝试这样做。
扩展我们原来的例子:
let falseDeep = JSON.parse(JSON.stringify(original));
falseDeep.moo.moo = "HEY I CAN MOO AGAIN";
console.log(original, falseDeep); // moo.moo is decoupled
看起来不错,对吧? 错了!
看看我从一开始就潜入的 mrMethodLookAtMe 和 howBigIsUniverse 属性发生了什么:)
一个返回null,它绝对不是Infinity,另一个是GONE。那不是bueno。
简而言之:“更智能”的值(例如 NaN 或 Infinity)会被 JSON API 变为 null。如果您使用,还有其他问题:
方法、RegExps、Maps、Sets、Blobs、FileLists、ImageDatas、稀疏数组、类型化数组作为原始对象的属性。
简而言之。如果您告诉我您是中级 JS 开发人员,并且您建议我通过 JSON API 对对象进行深度复制,您将获得初级职位。如果你声称自己是一名大四学生并建议 JSON API 是正确答案,我会告诉你面试已经结束:)
为什么?嗯这会产生一些最讨厌的跟踪错误。。在 Typescript 成为事物之前,我一直在做噩梦,跟踪消失的方法或类型被转换为另一个(通过了某人的错误输入参数检查但随后无法产生有效结果)。
是时候总结一下了!那么正确答案是什么?
- 您编写自己的深拷贝实现。我喜欢你,但是当我们有最后期限时,请不要这样做。
- 使用您已在项目中使用的库或框架为您提供的深度克隆功能。
- Lodash 的 cloneDeep
很多人仍然使用 jQuery。所以在我们的例子中(请把 import 放在它所属的地方,在文件的顶部):
import jQ from "jquery";
let trueDeep = jQ.extend(true, original, {});
console.log(original, trueDeep);
这很有效,它是一个很好的深层副本,并且是单行的。但是我们必须导入整个 jQuery。对于一些它已经存在,但对我来说,我倾向于避免它,因为它过于臃肿并且命名非常不一致。
Angular 用户可以使用angular.copy()。对于您的项目依赖项,如果它在那里,您可以 google :)
但是如果我的框架/库没有类似的功能怎么办?
您可以在 JS 库中使用我个人的 SUPERSTAR(我不参与该项目,只是一个忠实的粉丝)- Lodash(或 _ 为朋友)。
因此扩展我们的示例(再次注意导入的位置):
import _ from "lodash"; // cool kids know _ is low-dash
var fastAndDeepCopy = _.cloneDeep(objects);
console.log(original, lodashDeep);
这是一个简单的oneliner,它可以工作,而且速度很快。
差不多就是这样:)
现在你知道 JS 中浅拷贝和深拷贝的区别了。您意识到 JSON API 滥用就是这样,滥用而不是真正的解决方案。如果您已经在使用 jQuery 或 Angular,那么您现在知道已经有适合您的解决方案了。如果没有,您可以自己编写或考虑使用 lodash。
完整的例子可以在这里找到:
codesandbox - entire example