JS - 深拷贝 & 浅拷贝

介绍了常见的js 深拷贝与浅拷贝方法, 以及手写深拷贝。


在理解拷贝机制前,需要了解JavaScript的内存模型:

栈内存:存储基本类型值(Number、String、Boolean等)和对象引用

堆内存:存储复杂对象(Object、Array等)


浅拷贝

创建一个新对象,复制原始对象的第一层属性:
特点:

基本类型值被复制

引用类型属性被共享(复制引用地址)

修改嵌套对象会影响原始对象

数组浅拷贝

我们通常使用slice(), concat(), 扩展运算符... 对数组进行浅拷贝。

javascript 复制代码
let arr = [1, 2, 3, 4, { value: 5 }];
const arr1 = arr.concat();
const arr2 = arr.slice();
const arr3 = [...arr]
arr[4].value = 6;
console.log(arr1); //[1, 2, 3, 4, { value: 6 }]
console.log(arr2); //[1, 2, 3, 4, { value: 6 }]
console.log(arr3); //[1, 2, 3, 4, { value: 6 }]

数组里有对象,那么不会拷贝对象的值,而是拷贝对象的引用,原来数据对象的值发生改变,由于拷贝的是对象的引用,新拷贝的数组中对象的值也发生改变,拷贝的不是很彻底,slice() concat() 扩展运算符...是浅拷贝

对象浅拷贝

对于对象的浅拷贝通常使用扩展运算符...assign()

javascript 复制代码
let obj = {
    name: "slim",
    address: { city: "shanghai" }
}
const obj1 = Object.assign({}, obj);
const obj2 = { ...obj };
obj.address.city = "hebei";
console.log(obj1); //{ name: 'slim', address: { city: 'hebei' } }
console.log(obj2); //{ name: 'slim', address: { city: 'hebei' } }

深拷贝

创建一个完全独立的对象副本,递归复制所有层级:

特点:
所有层级都被复制

嵌套对象也是新创建的

修改副本不影响原始对象

JSON.stringfy

javascript 复制代码
let arr = [1, 2, 3, 4, { value: 5 }];
const arr1 = JSON.parse(JSON.stringify(arr));
arr[4].value = 6;
console.log(arr1); //[1, 2, 3, 4, { value: 5 }]

var obj = {
    name: "slim",
    address: { city: "shanghai" }
}
var obj1 = JSON.parse(JSON.stringify(obj));
obj.address.city = "hebei";
console.log(obj1); //{ name: 'slim', address: { city: 'shanghai' } }


可以看到JSON.stringfy 实现了对象的深拷贝,但该方法有一定的局限性,不能对undefined symbol 函数 进行深度拷贝。

递归实现

javascript 复制代码
function deepClone(target) {
    if (typeof target !== 'object') {
        return;
    }
    const newObj = Array.isArray(target) ? [] : {};
    for (let key in target) {
        if (target.hasOwnProperty(key)) {
            newObj[key] = typeof target[key] === 'object' ? deepClone(target[key]) : target[key];
        }
    }
    return newObj;
}