在JavaScript中克隆对象的3种方法
因为#JavaScript中的对象是引用值,所以不能简单地使用 =
。但不用担心,这里有三种方法可以克隆一个对象?
const food = { beef: '?', bacon: '?' } // "Spread" { ...food } // "Object.assign" Object.assign({}, food) // "JSON" JSON.parse(JSON.stringify(food)) // RESULT: // { beef: '?', bacon: '?' }
对象是引用类型
你的第一个问题可能是,我不能使用乳清 =
。让我们看看如果我们这样做会发生什么:
const obj = {one: 1, two: 2}; const obj2 = obj; console.log( obj, // {one: 1, two: 2}; obj2 // {one: 1, two: 2}; )
到目前为止,这两个对象似乎输出相同的东西。没问题,对吧。但是让我们看看如果我们编辑第二个对象会发生什么:
const obj2.three = 3; console.log(obj2); // {one: 1, two: 2, three: 3}; <-- ✅ console.log(obj); // {one: 1, two: 2, three: 3}; <-- ?
WTH?我变了 obj2
但为什么呢 obj
也受到影响。那是因为对象是引用类型。所以当你使用 =
,它将指针复制到它占用的内存空间。引用类型不包含值,它们是指向内存中值的指针。
如果您想了解更多相关信息,请查看Gordon的Zhu Watch and Code价格。可以免费注册并观看视频“与对象比较”。他给出了一个超级棒的解释。
使用Spread
使用spread会克隆你的对象。请注意,这将是一个浅表副本。截至本文,用于克隆对象的扩展运算符位于第4阶段。因此,它尚未正式出现在规范中。因此,如果您要使用它,则需要使用Babel(或类似的东西)进行编译。
const food = { beef: '?', bacon: '?' }; const cloneFood = { ...food }; console.log(cloneFood); // { beef: '?', bacon: '?' }
使用Object.assign
或者, Object.assign
是在官方发布并且还创建了对象的浅表副本。
const food = { beef: '?', bacon: '?' }; const cloneFood = Object.assign({}, food); console.log(cloneFood); // { beef: '?', bacon: '?' }
使用JSON
这最后的方式会给你一个深刻的副本。现在我要提一下,这是一种深度克隆对象的快速而肮脏的方法。对于更强大的解决方案,我建议使用像lodash这样的东西
const food = { beef: '?', bacon: '?' }; const cloneFood = JSON.parse(JSON.stringify(food)) console.log(cloneFood); // { beef: '?', bacon: '?' }
Lodash DeepClone vs JSON
以下是社区的评测。是的,这是我以前的帖子,如何深度克隆数组。但这个想法仍然适用于对象。
Alfredo Salzillo:我想请你注意一下deepClone和JSON.stringify / parse之间存在一些差异。
- JSON.stringify / parse仅适用于Number和String以及Object literal而不使用函数或Symbol属性。
- deepClone使用所有类型,函数和符号通过引用复制。
这是一个例子:
const lodashClonedeep = require("lodash.clonedeep"); const arrOfFunction = (() => 2, { test: () => 3, }, Symbol('4')); // deepClone copy by refence function and Symbol console.log(lodashClonedeep(arrOfFunction)); // JSON replace function with null and function in object with undefined console.log(JSON.parse(JSON.stringify(arrOfFunction))); // function and symbol are copied by reference in deepClone console.log(lodashClonedeep(arrOfFunction)(0) === lodashClonedeep(arrOfFunction)(0)); console.log(lodashClonedeep(arrOfFunction)(2) === lodashClonedeep(arrOfFunction)(2));
@OlegVaraksin:JSON方法存在循环依赖性问题。此外,克隆对象中的属性顺序可能不同。
浅克隆与深克隆
当我使用spread …复制一个对象时,我只创建一个浅拷贝。如果数组是嵌套的或多维的,它将无法工作。让我们来看看:
const nestedObject = { country: '??', { city: 'vancouver' } }; const shallowClone = { ...nestedObject }; // Changed our cloned object clonedNestedObject.country = '??' clonedNestedObject.country.city = 'taipei';
所以我们通过改变城市改变了克隆对象。我们来看看输出。
console.log(shallowClone); // {country: '??', {city: 'taipei'}} <-- ✅ console.log(nestedObject); // {country: '??', {city: 'taipei'}} <-- ?
浅拷贝意味着复制第一级,引用更深级别。
深拷贝
我们采用相同的示例,但使用“JSON”应用深层副本
const deepClone = JSON.parse(JSON.stringify(nestedObject)); console.log(deepClone); // {country: '??', {city: 'taipei'}} <-- ✅ console.log(nestedObject); // {country: '??', {city: 'vancouver'}} <-- ✅
如您所见,深层副本是嵌套对象的真实副本。通常时间浅的副本足够好,你真的不需要深层复制。它就像钉枪和锤子。大多数时候,锤子非常精细。使用钉枪进行一些小型的艺术和工艺往往是一种矫枉过正,锤子就好了。这就是为正确的工作使用正确的工具?
性能
不幸的是,我不能为传播做一个测试,因为它尚未正式出现在规范中。不过,我将它包含在测试中,以便您将来可以运行它。但结果显示 Object.assign
要快得多 JSON
。
性能测试
资源
- MDN Web Docs:Object.assign
- Stack Overflow:在JavaScript中深度克隆对象的最有效方法是什么?
- 2ality:休息/传播属性
- Stack Overflow:对象传播与Object.assign
感谢您阅读❤
问好 Instagram | Twitter | Facebook |博客| SamanthaMing.com