在JavaScript中,深拷贝和浅拷贝是两种常用的对象复制方法,它们的主要区别在于复制对象时是否也复制了对象内部的嵌套对象或数组。
浅拷贝(Shallow Copy)
浅拷贝只复制对象的第一层属性,如果对象的属性值是基本数据类型(如Number、String、Boolean、Null、Undefined、Symbol),则直接复制该值;如果属性值是引用数据类型(如Object、Array、Function等),则只复制引用(即内存地址),因此原对象及其副本会共享这个内部对象的引用。
实现方式
-
Object.assign() 方法
const original = { a: 1, b: { c: 2 } };
const copy = Object.assign({}, original);
copy.b.c = 3; // 修改副本的内部对象会反映到原对象
console.log(original.b.c); // 输出: 3
-
展开运算符(Spread Operator)
const original = { a: 1, b: { c: 2 } };
const copy = { ...original };
copy.b.c = 3; // 修改副本的内部对象会反映到原对象
console.log(original.b.c); // 输出: 3
深拷贝(Deep Copy)
深拷贝不仅复制对象的第一层属性,还递归地复制所有嵌套的对象或数组,确保原对象和副本完全独立,互不影响。
实现方式
-
JSON.parse(JSON.stringify())
这是最简便的方法,但它有局限性,比如无法复制函数、undefined
、Symbol
、循环引用的对象等。const original = { a: 1, b: { c: 2 } };
const copy = JSON.parse(JSON.stringify(original));
copy.b.c = 3;
console.log(original.b.c); // 输出: 2
-
手动递归实现
为了处理JSON.parse/stringify方法不能处理的情况,可以手动编写一个递归函数来实现深拷贝。function deepClone(obj, hash = new WeakMap()) {
if (obj === null) return null; // null 的情况
if (obj instanceof Date) return new Date(obj); // 日期对象直接返回一个新的日期对象
if (obj instanceof RegExp) return new RegExp(obj); // 正则对象直接返回一个新的正则对象
// 如果循环引用了就用 weakMap 的键存储,值存储拷贝对象
if (hash.has(obj)) return hash.get(obj);
let allDesc = Object.getOwnPropertyDescriptors(obj);
let cloneObj = Object.create(Object.getPrototypeOf(obj), allDesc);
hash.set(obj, cloneObj);
for (let key of Reflect.ownKeys(obj)) {
cloneObj[key] = (typeof obj[key] === 'object' && obj[key] !== null)
? deepClone(obj[key], hash)
: obj[key];
}
return cloneObj;
}
const original = { a: 1, b: { c: 2 } };
const copy = deepClone(original);
copy.b.c = 3;
console.log(original.b.c); // 输出: 2
区别
- 浅拷贝:只复制对象的第一层属性,对于嵌套的对象或数组,复制的是引用。
- 深拷贝:递归复制对象的所有层级,确保原对象和副本完全独立。
选择使用哪种拷贝方式取决于你的具体需求,如果需要确保对象之间完全独立,不相互影响,那么应该使用深拷贝;如果只是简单地复制一些基本数据类型的属性,或者不需要保持对象的独立性,那么浅拷贝可能就足够了。