在JavaScript中,克隆对象可以分为浅拷贝(shallow copy)和深拷贝(deep copy)两种方式。这两种方式在处理对象时有着本质的区别,尤其是在处理对象中包含对象(嵌套对象)时。
### 浅拷贝(Shallow Copy)
浅拷贝会创建一个新对象,这个新对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是引用类型(比如对象或数组),拷贝的就是内存地址,因此如果属性值是一个对象,那么浅拷贝后两个对象会共享这个对象的引用。
#### 实现方式
- **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
- **递归函数**
对于更复杂的对象或需要处理函数等特殊值的情况,可以使用递归函数来实现深拷贝。
function deepCopy(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 clone = Object.create(Object.getPrototypeOf(obj), allDesc);
hash.set(obj, clone);
for (let key of Reflect.ownKeys(obj)) {
clone[key] = (typeof obj[key] === 'object' && obj[key] !== null)
? deepCopy(obj[key], hash)
: obj[key];
}
return clone;
}
const original = { a: 1, b: { c: 2 } };
const copy = deepCopy(original);
copy.b.c = 3;
console.log(original.b.c); // 输出: 2
深拷贝和浅拷贝的选择取决于具体的应用场景。在需要完全隔离原始数据的情况下,深拷贝是更好的选择。