JavaScript的淺拷貝和深拷貝

一、理解

淺拷貝只複製指向某個對象的指針,而不復制對象本身,新舊對象還是共享同一塊內存。但深拷貝會另外創造一個一模一樣的對象,新對象跟原對象不共享內存,修改新對象不會改到原對象。

二、淺拷貝與深拷貝

  1. 淺拷貝:賦值時,基本數據類型按值傳遞,對象按引用傳遞

    var a = 25;
    var b = a;
    b = 18;
    console.log(a);//25
    console.log(b);//18
    // b的修改並不會影響到a,因爲它們是基本類型數據
    var obj1 = { a: 10, b: 20, c: 30 };
    var obj2 = obj1;
    obj2.b = 100;
    console.log(obj1);
    // { a: 10, b: 100, c: 30 } <-- b 被改到了
    console.log(obj2);
    // { a: 10, b: 100, c: 30 }
    // obj2修改了b的值,同時obj1的b也會被修改,因爲他們根本是同一個對象,這就是所謂的淺拷貝
  2. 深拷貝

    var obj1 = { a: 10, b: 20, c: 30 };
    var obj2 = { a: obj1.a, b: obj1.b, c: obj1.c };
    obj2.b = 100;
    console.log(obj1);
    // { a: 10, b: 20, c: 30 } <-- b 沒被改到
    console.log(obj2);
    // { a: 10, b: 100, c: 30 }

三、實現方式

  1. 淺拷貝的實現方式
    (1)直接賦值,就是淺拷貝。對象直接複製,則新對象的改變也會修改到舊對象。
    (2) Object.assign(target, ...sources)

    Object.assignES6的新函數。Object.assign()方法可以把任意多個的源對象自身的可枚舉屬性拷貝給目標對象,然後返回目標對象。但是Object.assign()進行的是淺拷貝,拷貝的是屬性值。假如源對象的屬性值是一個指向對象的引用,它也只拷貝那個引用值。
    var obj = { a: {a: "hello", b: 21} };
    var initalObj = Object.assign({}, obj);
    
    initalObj.a.a = "changed";
    console.log(obj.a.a); // "changed"

    (3)對象展開符...

    var obj = [{index: 1, msg: 'one'}, {index: 2, msg: 'two'}];
    var obj2 = [...obj];
    
    console.log(obj2); //[{index: 1, msg: 'one'}, {index: 2, msg: 'two'}]
    
    obj2.push({index: 3, msg: 'three'});
    console.log(obj2); // [{index: 1, msg: 'one'}, {index: 2, msg: 'two'}, {index: 3, msg: 'three'}]
    console.log(obj); // [{index: 1, msg: 'one'}, {index: 2, msg: 'two'}]
    
    obj2[1].msg = 'two again';
    console.log(obj2); // [{index: 1, msg: 'one'}, {index: 2, msg: 'two again'}, {index: 3, msg: 'three'}]
    console.log(obj); // [{index: 1, msg: 'one'}, {index: 2, msg: 'two again'}]
  2. 深拷貝的實現方式
    (1)手動複製

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

    (2)轉成JSON再轉回來(只有可以轉成JSON格式的對象纔可以這樣用,像function沒辦法轉成JSON)

    var obj1 = { body: { a: 10 } };
    var obj2 = JSON.parse(JSON.stringify(obj1));
    obj2.body.a = 20;
    console.log(obj1);
    // { body: { a: 10 } } <-- 沒被改到
    console.log(obj2);
    // { body: { a: 20 } }
    console.log(obj1 === obj2);
    // false
    console.log(obj1.body === obj2.body);
    // false

    (3)使用var newObj = Object.create(oldObj)方法

    function deepClone(initalObj, finalObj) {    
      var obj = finalObj || {};    
      for (var i in initalObj) {        
        var prop = initalObj[i];        // 避免相互引用對象導致死循環,如initalObj.a = initalObj的情況
        if(prop === obj) {            
          continue;
        }        
        if (typeof prop === 'object') {
          obj[i] = (prop.constructor === Array) ? [] : Object.create(prop);
        } else {
          obj[i] = prop;
        }
      }    
      return obj;
    }
    
    var str = {};
    var obj = { a: {a: "hello", b: 21} };
    deepClone(obj, str);

四、參考

【1】關於JavaScript的淺拷貝和深拷貝

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章