• JavaScript中浅拷贝和深拷贝的区别和实现


    深拷贝和浅拷贝的区别

      浅拷贝(shallow copy):只复制指向某个对象的指针,而不复制对象本身,新旧对象共享一块内存; 
      深拷贝(deep copy):复制并创建一个一摸一样的对象,不共享内存,修改新对象,旧对象保持不变。

    var a = 25;

    var b = a;

    b = 10;

    console.log(a);//25

    console.log(b);//10

     

    //浅拷贝

    var obj1 = { a: 10, b: 20, c: 30 };

    var obj2 = obj1;

    obj2.b = 40;

    console.log(obj1);// { a: 10, b: 40, c: 30 }

    console.log(obj2);// { a: 10, b: 40, c: 30 }

     

    //深拷贝

    var obj1 = { a: 10, b: 20, c: 30 };

    var obj2 = { a: obj1.a, b: obj1.b, c: obj1.c };

    obj2.b = 40;

    console.log(obj1);// { a: 10, b: 20, c: 30 }

    console.log(obj2);// { a: 10, b: 40, c: 30 }

     

    传值与传址

    上一篇博客说明了什么是内存中的堆、栈以及变量类型,实际上是为这篇服务的,就是为了更好的理解什么是“浅拷贝”和“深拷贝”。

    基本类型与引用类型最大的区别实际就是传值与传址的区别。测试用例:

    var a = [1,2,3,4,5];

    var b = a;

    var c = a[0];

    alert(b);//1,2,3,4,5

    alert(c);//1

    //改变数值       

    b[4] = 6;

    c = 7;

    alert(a[4]);//6

    alert(a[0]);//1

    从上面我们可以得知,当我改变b中的数据时,a中数据也发生了变化;但是当我改变c的数据值时,a却没有发生改变。  

    这就是传值与传址的区别

    因为a是数组,属于引用类型,所以它赋予给b的时候传的是栈中的地址(相当于新建了一个不同名“指针”),而不是堆内存中的对象。而c仅仅是从a堆内存中获取的一个数据值,并保存在栈中。所以b修改的时候,会根据地址回到a堆中修改,c则直接在栈中修改,并且不能指向a堆内存中。

     

    浅拷贝

    前面已经提到,在定义一个对象或数组时,变量存放的往往只是一个地址。当我们使用对象拷贝时,如果属性是对象或数组时,这时候我们传递的也只是一个地址。因此子对象在访问该属性时,会根据地址回溯到父对象指向的堆内存中,即父子对象发生了关联,两者的属性值会指向同一内存空间。

    var a = {
        key1:"11111"
    }
    function Copy(p) {
        var c = {};
        for (var i in p) { 
          c[i] = p[i];
        }
        return c;
    }
    a.key2 = ['小辉','小辉'];
     
    var b = Copy(a);
    b.key3 = '33333';
     
    alert(b.key1);     //1111111
    alert(b.key3);    //33333
    alert(a.key3);    //undefined
     

    a对象中key1属性是字符串,key2属性是数组。a拷贝到b,12属性均顺利拷贝。给b对象新增一个字符串类型的属性key3时,b能正常修改,而a中无定义。说明子对象的key3(基本类型)并没有关联到父对象中,所以undefined。

    b.key2.push("大辉");
    alert(b.key2);    //小辉,小辉,大辉
    alert(a.key2);    //小辉,小辉,大辉

    但是,若修改的属性变为对象或数组时,那么父子对象之间就会发生关联。从以上弹出结果可知,我对b对象进行修改,a、b的key2属性值(数组)均发生了改变。其在内存的状态,可以用下图来表示。

     

    原因是key1的值属于基本类型,所以拷贝的时候传递的就是该数据段;但是key2的值是堆内存中的对象,所以key2在拷贝的时候传递的是指向key2对象的地址,无论复制多少个key2,其值始终是指向父对象的key2对象的内存空间。

     

    深拷贝

    或许以上并不是我们在实际编码中想要的结果,我们不希望父子对象之间产生关联,那么这时候可以用到深拷贝。既然属性值类型是数组和或象时只会传址,那么我们就用递归来解决这个问题,把父对象中所有属于对象的属性类型都遍历赋给子对象即可。测试代码如下:

    function Copy(p, c) {
      var c = c || {};
      for (var i in p) {
        if (typeof p[i] === 'object') {
                c[i] = (p[i].constructor === Array) ? [] : {};
                Copy(p[i], c[i]);
        } else {
                c[i] = p[i];
        }
      }
      return c;
    }
    a.key2 = ['小辉','小辉'];
    var b={};
    b = Copy(a,b);        
    b.key2.push("大辉");
    alert(b.key2);    //小辉,小辉,大辉
    alert(a.key2);    //小辉,小辉

    由上可知,修改b的key2数组时,没有使a父对象中的key2数组新增一个值,即子对象没有影响到父对象a中的key2。其存储模式大致如下:

     

     

    如何实现深拷贝

    1递归递归去复制所有层级属性。

    function deepClone(obj){

        let objClone = Array.isArray(obj)?[]:{};

        if(obj && typeof obj==="object"){

            for(key in obj){

                if(obj.hasOwnProperty(key)){

                    //判断ojb子元素是否为对象,如果是,递归复制

                    if(obj[key]&&typeof obj[key] ==="object"){

                        objClone[key] = deepClone(obj[key]);

                    }else{

                        //如果不是,简单复制

                        objClone[key] = obj[key];

                    }

                }

            }

        }

        return objClone;

    }   

    let a=[1,2,3,4],

        b=deepClone(a);

    a[0]=2;

    console.log(a,b);

     

    2通过JSON去解析

    let obj = {name:'fiona-SUN'};

    let copyObj = JSON.parse(JSON.stringify(obj));

    copyObj.name = 'fiona';

    console.log(copyObj.name); // 'fiona'

    console.log(obj.name); // 'fiona-SUN'

     

     

    3 es6之展开Object.assign(拷贝obj的内容到一个新的堆内存,copyObj存储新内存的引用)

    let obj = {name:'fiona-SUN'};

    let copyObj = Object.assign({}, obj);

    copyObj.name = 'fiona';

    console.log(copyObj.name); // 'fiona'

    console.log(obj.name); // 'fiona-SUN'

     

    4 es6之展开运算符(仅用于数组)

    let arr = [1,2,3];

    let copyArr = [...obj];

    copyArr[2] = 0;

    console.log(copyArr[2]);  // 0

    console.log(arr[2]);     // 2

     

     

  • 相关阅读:
    【Educational Codeforces Round 101 (Rated for Div. 2) C】Building a Fence
    【Codeforces Round #698 (Div. 2) C】Nezzar and Symmetric Array
    【Codeforces Round #696 (Div. 2) D】Cleaning
    【Codeforces Round #696 (Div. 2) C】Array Destruction
    【Educational Codeforces Round 102 D】Program
    【Educational Codeforces Round 102 C】No More Inversions
    【Good Bye 2020 G】Song of the Sirens
    【Good Bye 2020 F】Euclid's nightmare
    使用mobx入门
    requestAnimationFrame 控制速度模拟setinterval
  • 原文地址:https://www.cnblogs.com/ranyonsue/p/9287417.html
Copyright © 2020-2023  润新知