在JavaScript中,拷贝对象是一个常见的需求,尤其是当需要修改对象副本而不影响原始对象时。根据拷贝的深度,可以分为浅拷贝(Shallow Copy)和深拷贝(Deep Copy)。
浅拷贝(Shallow Copy)
浅拷贝是指创建一个新对象,这个新对象的属性值与原对象的属性值相同,但这些属性值实际上是对原始数据的引用。也就是说,如果属性值是基本数据类型(如数字、字符串等),拷贝的就是值本身;如果属性值是引用类型(如对象、数组等),拷贝的就是引用地址,新旧对象的这些属性实际上指向同一个数据。
在JavaScript中,可以通过以下方式实现浅拷贝:
Object.assign() 方法:这个方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它只进行一层拷贝,如果源对象的属性值是对象,那么复制的是引用。
let original = { a: 1, b: { c: 2 } }; let shallowCopy = Object.assign({}, original);
展开运算符(...):在ES6中引入,可以用来浅拷贝数组或对象。
let original = { a: 1, b: { c: 2 } }; let shallowCopy = { ...original };
Array.prototype.slice() 方法:对于数组,slice() 方法可以用来创建一个数组的浅拷贝。
let originalArray = [1, 2, { a: 3 }]; let shallowCopyArray = originalArray.slice();
浅拷贝的问题在于,如果原始对象的属性值是对象或数组,那么新旧对象的这些属性值仍然指向同一个实例。因此,修改新对象的嵌套对象或数组会影响到原始对象。
深拷贝(Deep Copy)
深拷贝则是创建一个新对象,并递归地复制原始对象的所有层级的属性值。对于嵌套的对象或数组,深拷贝会创建新的实例,因此新旧对象之间不会相互影响。
在JavaScript中,实现深拷贝的方法有:
JSON.parse() 和 JSON.stringify() 方法:这种方法通过将对象转换为JSON字符串,然后再将字符串解析回对象来实现深拷贝。这种方法简单但有局限性,比如不能复制函数、undefined、循环引用等。
let original = { a: 1, b: { c: 2 } }; let deepCopy = JSON.parse(JSON.stringify(original));
递归拷贝:通过编写一个递归函数,遍历原始对象的所有属性,并对每个属性值进行拷贝。这种方法可以更精确地控制拷贝过程,处理特殊类型和循环引用等问题。
function deepCopy(obj, hash = new WeakMap()) { if (Object(obj) !== obj) return obj; // primitives if (hash.has(obj)) return hash.get(obj); // cyclic reference let result = Array.isArray(obj) ? [] : obj.constructor ? new obj.constructor() : Object.create(null); hash.set(obj, result); for (let key of Reflect.ownKeys(obj)) { result[key] = (typeof obj[key] === 'object' && obj[key] !== null) ? deepCopy(obj[key], hash) : obj[key]; } return result; }
深拷贝比浅拷贝复杂,且在性能上通常会更慢,因为它需要复制更多的数据。选择使用浅拷贝还是深拷贝取决于具体的应用场景和需求。