JavaScript中的深拷贝和浅拷贝

时间:2022-02-10 22:37:09

1、概念

  ECMAScript中的变量有两种类型:基本类型值和引用类型值。基本类型值指的是那些保存在栈内存中的简单数据段,即这种值完全保存在内存中的一个位置,基本类型值有Number、String、Boolean、Null、Undefined、Symbol。引用类型值指的是那些保存在堆内存中的对象,意思是变量中保存的实际上只是一个指针,这个指针指向内存中的另一个位置,该位置保存对象,引用类型值有Object、Array、Function等。

  深拷贝和浅拷贝只存在引用类型值中。

  浅拷贝类似于将一个对象obj1赋值给一个变量obj2,赋值后obj1和obj2指向同一个内存地址,修改其中一个会影响另外一个(浅拷贝时对象只会被拷贝最外部的一层,至于更深层的对象,则依然是通过引用指向同一块堆内存.)。深拷贝则是在内存中的另外一个地址创建一个对象obj3,除了指针指向的内存位置不同,obj3与obj1完全相同,obj3独立于obj1,操作一个对象时不会对另一个对象造成影响。。

2、深拷贝探究

  2-1、 Array

  Array的slice()、concat()、Array.from()方法只能实现一维数组的深拷贝,对二维及以上数组就无法深拷贝了。

  Eg: [1, 2, 3, 'a']可以实现深拷贝,[1, 2, 3, [4, 5]]无法实现深拷贝。

  2-2、Objec

  Object.assign()方法只能实现一维对象的深拷贝,对二维及以上对象无法进行深拷贝。

  Eg: {x: 1, y: 1}能实现深拷贝, {x: 1, y: {m: 2}}无法实现深拷贝。

  2-3:、JSON.parse(JSON.stringify(obj)):

  能实现一些比较简单的深拷贝,但是缺陷比较明显:当Object或Array中有值为undefined、所有函数及Symbol时,JSON.stringify(obj)将Object序列化时会忽略这些值,JSON.stringify(obj)将Array序列化时会将这些值转换为null。但是该方法足以应对比较简单的不含有上述值的对象。

  2-4:JavaScript自身提供的方法不足以解决深拷贝的问题,所以需要借助其他方法。其一是递归函数,其二是使用第三方库,如jQuery的$.extendlodash的_.deepCopy方法

  2-5、递归方法:

function deepCopy(obj) {
    // 创建一个新对象
    let result = {}
    let keys = Object.keys(obj),
        key = null,
        temp = null;

    for (let i = 0; i < keys.length; i++) {
        key = keys[i];    
        temp = obj[key];
        // 如果字段的值也是一个对象则递归操作
        if (temp && typeof temp === 'object') {
            result[key] = deepCopy(temp);
        } else {
        // 否则直接赋值给新对象
            result[key] = temp;
        }
    }
    return result;
}

var obj1 = {
    x: {
        m: 1
    },
    y: undefined,
    z: function add(z1, z2) {
        return z1 + z2
    },
    a: Symbol("foo")
};

var obj2 = deepCopy(obj1);
obj2.x.m = 2;

console.log(obj1); //{x: {m: 1}, y: undefined, z: ƒ, a: Symbol(foo)}
console.log(obj2); //{x: {m: 2}, y: undefined, z: ƒ, a: Symbol(foo)}

// 代码出自掘金的一篇博文,链接:https://juejin.im/post/5ad5b908f265da23870f540d