开始之前
首先我们要搞清楚什么是深拷贝和浅拷贝,JavaScript 对象是一种复杂的数据类型,可以包含各种数据类型。
浅拷贝(Shallow Copy):当使用赋值运算符将引用变量复制到新的引用变量中时,会创建引用对象的浅拷贝。简单来说,引用变量主要存储它所引用的对象的地址。当一个新的引用变量被赋予旧引用变量的值时,存储在旧引用变量中的地址被复制到新引用变量中。这意味着新旧引用变量都指向内存中的同一个对象。因此,如果对象的状态通过任何参考变量发生变化,则两者都会反映出来。
深拷贝(Deep Copy):与浅拷贝不同,深拷贝复制旧对象的所有成员,为新对象分配单独的内存位置,然后将复制的成员分配给新对象。这样,两个对象相互独立,并且在对其中任何一个进行任何修改的情况下,另一个不受影响。此外,如果其中一个对象被删除,另一个对象仍保留在内存中。
浅拷贝方法
制作数组或对象的浅拷贝意味着创建对对象内部原始值的新引用,并复制它们。
...扩展符
扩展语法可用于制作对象的浅表副本。这意味着它将复制对象。然而,更深的对象被引用。是一种创建数组或对象的浅拷贝的便捷方法——当没有嵌套时,它工作得很好。
const person = {
name: 'John',
age: 21,
}
// 复制对象
const clonePerson = { ... person}
console.log(clonePerson);
//{name: "John", age: 21}
// 改变clonePerson的值
clonePerson.name = 'Peter';
console.log(clonePerson.name);
console.log(person.name);
//Peter
//John
扩展语法可用于制作对象的浅表副本。这意味着它将复制对象。然而,更深的对象被引用。例如,
const person = {
name: 'John',
age: 21,
// the inner objects will change in the shallow copy
marks: { math: 66, english: 73}
}
// 复制对象
const clonePerson = { ... person}
console.log(clonePerson); // {name: "John", age: 21, marks: {…}}
// 改变clonePerson的值
clonePerson.marks.math = 100;
console.log(clonePerson.marks.math); // 100
console.log(person.marks.math); // 100
这里,当内部对象的值 math 变为 clonePerson 对象的 100 时,person 对象的 math 键的值也发生了变化。
.slice()
特别是对于数组,使用内置的 .slice() 方法与展开运算符的工作方式相同——创建一层的浅拷贝。
.assign()
Object.assign() 方法是 ES6 标准的一部分。 Object.assign() 方法执行复制并从一个或多个对象复制所有属性。可以使用 Object.assign() 创建相同类型的浅拷贝,它可以与任何对象或数组一起使用。
const person = {
name: 'John',
age: 21,
}
// 复制对象
const clonePerson = Object.assign({}, person);
//作为第一个参数的空 {} 确保您不会更改原始对象
console.log(clonePerson);
//改变clonePerson的值
clonePerson.name = 'Peter';
console.log(clonePerson.name);
console.log(person.name);
//Peter
//John
Array.from()
复制 JavaScript 数组的另一种方法是使用 Array.from(),它也会进行浅拷贝。 如果一个对象或数组包含其他对象或数组,浅拷贝将不会按照预期工作,因为嵌套对象实际上并未被克隆。 对于深度嵌套的对象,需要一个深拷贝。
深拷贝方法
lodash
lodash 库是 JavaScript 开发人员制作深拷贝的最常见方式。它非常容易使用。请注意,Ramda 的 R.clone() 等效于 lodash 的 _.cloneDeep(),因为 Ramda 没有浅拷贝辅助方法。
Ramda
函数式编程库 Ramda 包括 R.clone() 方法,该方法可以对对象或数组进行深层复制。
自定义递归函数
编写一个递归的 JavaScript 函数来制作嵌套对象或数组的深层副本非常容易。请注意,还需要检查 null,因为 null 的类型是“object”。
const deepCopyFunction = (inObject) => {
let outObject, value, key
if (typeof inObject !== "object" || inObject === null) {
return inObject // 如果不是对象返回这个值
}
// 创建一个对象或数组保存值
outObject = Array.isArray(inObject) ? [] : {}
for (key in inObject) {
value = inObject[key]
// 递归复制嵌套的对象或数组
outObject[key] = deepCopyFunction(value)
}
return outObject
}
JSON.parse/stringify
const person = {
name: 'John',
age: 21,
}
// 复制对象
const clonePerson = JSON.parse(JSON.stringify(person));
console.log(clonePerson);
//{name: "John", age: 21}
// 改变clonePerson的值
clonePerson.name = 'Peter';
console.log(clonePerson.name);
console.log(person.name);
//Peter
//John
注意:
JSON.parse()仅适用于Number和String对象字面量。它不适用于具有函数或符号属性的对象文字。
rfdc (Really Fast Deep Clone)
const clone = require('rfdc')()
clone({a: 1, b: {c: 2}}) // => {a: 1, b: {c: 2}}