深拷贝和浅拷贝

102 阅读1分钟

深拷贝和浅拷贝

  • 浅拷贝:只是复制对象的第一级属性值,如果对象的第一级属性中包含引用类型,则只复制地址
  • 深拷贝:不但复制对象的第一级属性值,即使对象中又包含引用类型的属性值,也会继续复制内嵌类型的属性值。形成两个毫无瓜葛的对象

浅拷贝

Object.assign(target, source) - 浅拷贝

var obj1 = {
    x: 1,
    a: { b: 4 },
    c: undefined,
    f: function() { console.info("f") }
}
var obj2 = Object.assign({}, obj1)
obj1 == obj2 // false;
var obj3 = obj1;
obj1 == obj3; // true

展开运算符...

和Object.assign()的功能相同

var obj1 = {
    x: 1,
    a: { b: 4 },
    c: undefined,
    f: function() { console.info("f") }
}
var obj2 = {...obj1}

Array.concat()

Array.slice()

深拷贝

JSON.stringify & JSON.parse - 深拷贝

var obj1 = {
    x: 1,
    a: { b: 4 },
    c: undefined, // 会丢失
    f: function() { console.info("f") } // 会丢失
}
var str = JSON.stringify(obj1);
var obj2 = JSON.parse(str)

缺点:

  • 无法拷贝undefined;上面的例子中,obj2没有c属性
  • 无法拷贝内嵌函数;上面例子中,obj2中没有f属性

递归方法(重要)

基础版本
function cloneDeep(target) {
    var newObj;
    if (typeof target === "object") {
        if (Array.isArray(target)) {
            newObj = []
        } else {
            newObj = {}
        }
        for (const key in target) {
            // 递归
            newObj[key] = deepClone(target[key]);
        }
    } else {
        newObj = target;
    }
    return newObj;
}

测试

var target = {
    field1: 1,
    field2: undefined,
    field3: {
        child: 'child'
    },
    field4: [2, 4, 8]
};
var res = cloneDeep(target);
console.info(res);

似乎没问题,但是如果遇到循环引用时,会报内存溢出的错误

target.target = target;

image.png

优化

思路:使用一个Map,用于保存已经遍历过的,如果已经存在过,直接返回

function cloneDeep(target, map = new Map()) {
    var newObj;
    if (typeof target === "object") {
        newObj = Array.isArray(target) ? [] : {};
        if (map.get(target)) {
            return map.get(target)
        }
        map.set(target, newObj)
        for (var key in target) {
            newObj[key] = cloneDeep(target[key], map);
        }
    } else {
        newObj = target
    }
    return newObj;
}

var target = {
    field1: 1,
    field2: undefined,
    field3: {
        child: 'child'
    },
    field4: [2, 4, 8]
};
target.target = target;
var res = cloneDeep(target);
console.info(res);