JS中的浅拷贝与深拷贝

时间:2021-02-24 19:54:05

浅拷贝与深拷贝的区别:

浅拷贝:
    对基本类型和引用类型只进行值的拷贝,即,拷贝引用对象的时候,只对引用对象的内存地址拷贝,新旧引用属性指向同一个对象,修改任意一个都会影响所有引用当前对象的变量。

深拷贝:
    对引用类型所引用的对象也进行拷贝。使得新旧引用属性指向不同的对象,达到两者状态分离的效果。

实现方案:

方案一:
Object.assign()方法可以用于合并对象,并且只在顶层属性上进行合并。

var obj1 = { x: 1, y: 2 },
    obj2 = Object.assign({}, obj1);
console.log(obj1); //{x: 1, y: 2}
console.log(obj2); //{x: 1, y: 2}
obj2.x = 2; //修改obj2.x
console.log(obj1); //{x: 1, y: 2}
console.log(obj2); //{x: 2, y: 2}

缺点:只实现了合并时顶层这一层的深拷贝。

方案二:
JSON.parse(JSON.stringify(obj))利用 JSON 对象中转一次,实现深拷贝。

var obj1 = {
    x: 1,
    y: {
        m: 1
    }
};
var obj2 = JSON.parse(JSON.stringify(obj1));
console.log(obj1); //{x: 1, y: {m: 1}}
console.log(obj2); //{x: 1, y: {m: 1}}
obj2.y.m = 2; //修改obj2.y.m
console.log(obj1); //{x: 1, y: {m: 1}}
console.log(obj2); //{x: 2, y: {m: 2}}

缺点:由于 JSON 对象转化规则,undefined、任意的函数以及 symbol 值,在序列化过程中会被忽略(出现在非数组对象的属性值中时)或者被转换成 null(出现在数组中时)且只适用于能够用 JSON 表示的对象。

例子:

var obj1 = {
    x: 1,
    y: undefined,
    z: function add(z1, z2) {
        return z1 + z2;
    },
    a: Symbol("foo")
};
var obj2 = JSON.parse(JSON.stringify(obj1));
console.log(obj1); //{x: 1, y: undefined, z: ƒ, a: Symbol(foo)}
console.log(JSON.stringify(obj1)); //{"x":1}
console.log(obj2); //{x: 1}

方案三:
使用 for in 遍历原型以及自身所有可枚举属性,在遇到 object 时递归自身完成对象深拷贝。

var deepClone = function fnDeepClone(obj) {
    var result = typeof obj.splice === "function" ? [] : {},
        key;
    if (obj && typeof obj === "object") {
        for (key in obj) {
            if (obj[key] && typeof obj[key] === "object") {
                result[key] = fnDeepClone(obj[key]); //如果对象的属性值为object的时候,递归调用deepClone,即再把某个值对象复制一份到新的对象的对应值中
            } else {
                result[key] = obj[key]; //如果对象的属性值不为object的时候,直接复制参数对象的每一个键/值到新对象对应的键/值中
            }
        }
        return result;
    }
    return obj;
};

var obj1 = {
    family: { brother: "wangzhipeng", father: "wanglicai", mother: "sunaiyun" },
    name: "gino",
    sex: "male",
    age: "27",
    test: undefined,
    a: Symbol("foo"),
    f() {}
};
var obj2 = deepClone(obj1);
obj1.sex = "close";
obj2.age = "33";
obj1.f = () => {
    console.log(1111);
};
console.log(obj1);
console.log(obj2);

缺点:只支持object和array类型

方案四:
使用 Object.create 以 obj 原型创建一个对象,然后使用 Objcet.getOwnPropertyDescriptors 获取对象属性描述符数组来创建这个对象。

var deepClone = function(o) {
    var copy = Object.create(
        Object.getPrototypeOf(o),
        Object.getOwnPropertyDescriptors(o)
    );
    return copy;
};

var obj1 = {
    family: { brother: "wangzhipeng", father: "wanglicai", mother: "sunaiyun" },
    name: "gino",
    sex: "male",
    age: "27"
    f(){

    }
};
var obj2 = deepClone(obj1);
obj1.sex = "close";
obj2.age = "33";
console.log(obj1);
console.log(obj2);

优点:在对象属性中允许函数、undefined、Symbol 拷贝,还能继承原型

总结:

以上方法暂未实现包装对象,Date对象,正则对象的深拷贝,尚未验证DOM对象深拷贝后功能函数是否正常,对于后台JSON对象拷贝,已经足够