關於深拷貝和淺拷貝的我的理解

關於深拷貝和淺拷貝的我的理解javascript

1、深拷貝和淺拷貝區別java

如何區分深拷貝與淺拷貝,簡單點來講,就是假設B複製了A,當修改A時,看B是否會發生變化,若是B也跟着變了,說明這是淺拷貝,拿人手短,若是B沒變,那就是深拷貝,自食其力。typescript

具體可看這篇文章:https://www.jianshu.com/p/1c142ec2ca45數組

示例:ruby

let obj = {
    name: "hahah",
    age: 18,
    sex: "男",
  }  // 複雜數據類型 賦值至關因而 賦引用地址
  const newObj = obj;  // 修改newObj其中一個值 , 另外一個obj也發生改變
  newObj.name = "hahahahahahah";  console.log(obj, newObj);

輸出結果:兩個複雜數據類型的 name 值所有改變 ,複雜數據類型 = 賦值只是 引用地址的傳遞 ,指向的仍是相同的引用。bash

簡單的第一層淺拷貝:app

let obj = {
    name: "lili",
    age: 18,
    sex: "男",
    arr: [1, "2", "3"],
    obj: {
      name: "hahaha",
      sex: "女",
    }
  };  // 方法 一 
  // let newObj = Object.assign({}, obj);
  // 方法 二 
  let newObj = { ...obj };  // 再次改變時就是 改變不同的數據
  newObj.name = "hahaha";
  newObj.obj.sex = "男";
  newObj.arr[1] = 10;  console.log(obj, newObj);

輸出結果:能夠看到第一層的 name 沒有互相影響 ,可是更深層次的 arr 數組 和 obj 對象 仍是發生了相同改變,這仍是一個淺拷貝。咱們可使用不少方式在js中複製數據,好比 擴展運算符...,Object.assign, Object.freeze,slice, concat, map,filter, reduce等方式進行復制,ide

這裏能夠得出一個結論:淺拷貝,就是隻拷貝第一層數據,更深層的數據仍是同一個引用。函數

2、簡單數據類型(基本數據類型)spa

若是是基本數據類型,名字和值都會儲存在棧內存中

簡單數據類型能夠隨意賦值都不會相互影響。

3、複雜數據類型(引用數據類型)

複雜數據類型的存儲方法:存儲空間分爲堆空間 和 棧空間 ,複雜數據類型的值存在於 堆空間中 ,而名(www.taobaoosx.com,obj)則存在於棧空間中 ,堆空間返回一個引用地址給 名 ,名根據引用地址去訪問 棧空間中的值。

圖示:

當b=a進行拷貝時,其實複製的是a的引用地址,而並不是堆裏面的值。

此時 a 和 b 指向的是 堆空間的同一個地址。而當咱們a[0]=1時進行數組修改時,因爲a與b指向的是同一個地址,因此天然b也受了影響,這就是所謂的淺拷貝了。

順着往下想能夠獲得這麼一個結論:在堆內存中也開闢一個新的內存專門爲b存放值,就像基本類型那樣,豈不就達到深拷貝的效果了 。

4、實現深拷貝的方法

一、for in 循環實現深拷貝

let obj = {      name: "hahaha",      age: 18,      objChild: {        sex: "男",        addres: "深圳",        arr: [1, 2, 3, "10", "12", { aaa: "aaa", bbb: "bbb" }],
      },      func: function () {        console.log("我是一個函數");
      }
    }    function simpleCopy(obj) {      // 判斷 obj 爲數組仍是對象
      let newObj = Array.isArray(obj) ? [] : {}      // 遍歷該對象
      for (let i in obj) {        // 判斷 該對象是否 真的包含 i 這個鍵
        if (obj.hasOwnProperty(i)) {          // 判斷 obj[i] 是否爲 對象或者數組
          if (obj[i] && (typeof obj[i]) === "object") {            // 是對象遞歸再次執行
            newObj[i] = simpleCopy(obj[i]);
          } else {            // 不是直接簡單賦值
            newObj[i] = obj[i];
          }
        }
      }      return newObj;
    }    let newObj = simpleCopy(obj);

    newObj.name = "ccccc";
    newObj.objChild.sex = "女";
    newObj.objChild.arr[5].aaa = "小帥";
    newObj.func = () => {      console.log("hahahahahahah");
    }
    obj.func();  // 我是一個函數
    newObj.func(); // hahahahahahah
    console.log(obj);    console.log(newObj);    console.log(newObj === obj); // false

輸出結果:能夠看到拷貝後的修改並無影響到原先 對象 ,新的對象的引用地址已經改變,與原先對象屬於兩個不一樣對象,此時 console.log(newObj === obj); // false

二、JSON.stringfiy和JSON.parse方法實現深拷貝

let obj = {      name: "hahaha",      age: 18,      objChild: {        sex: "男",        addres: "深圳",        arr: [1, 2, 3, "10", "12", { aaa: "aaa", bbb: "bbb" }],        func: function () {          console.log("我是一個函數");
        }
      },

    }    function simpleCopy(obj) {      let objClone = JSON.parse(JSON.stringify(obj));      return objClone;
    }    let newObj = simpleCopy(obj);

    newObj.name = "ccccc";
    newObj.objChild.sex = "女";
    newObj.objChild.arr[5].aaa = "小帥";
    newObj.func = () => {      console.log("hahahahahahah");
    }    console.log(obj);    console.log(newObj);    console.log(newObj === obj); // false

    obj.objChild.func();  // 我是一個函數
    newObj.objChild.func(); // 報錯

輸出結果:

**缺點: 沒法實現對對象中方法的深拷貝,會顯示爲www.taobaoosx.com,undefined **

三、經過jQuery的extend方法實現深拷貝

let newObj = $.extend(true, {}, obj); // true爲深拷貝,false爲淺拷貝

四、lodash函數庫實現深拷貝

let result = _.cloneDeep(test)

官網截圖:

五、Reflect法(與 for in 遍歷相似)

// 代理法function deepClone(obj) {    // 判斷是否爲一個對象
    if (!isObject(obj)) {        throw new Error('obj 不是一個對象!')
    }    let isArray = Array.isArray(obj)    let cloneObj = isArray ? [...obj] : { ...obj }    // 靜態方法 Reflect.ownKeys() 返回一個由目標對象自身的屬性鍵組成的數組。
    Reflect.ownKeys(cloneObj).forEach(key => {
        cloneObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key]
    })    return cloneObj
}

六、手動實現深拷貝

let obj1 = {
   a: 1,
   b: 2}let obj2 = {
   a: obj1.a,
   b: obj1.b}obj2.a = 3;alert(obj1.a); // 1alert(obj2.a); // 3

七、Object.assign來實現深拷貝

// 只適用於只有一層複雜數據類型var obj = {
    a: 1,
    b: 2}var obj1 = Object.assign({}, obj); // obj賦值給一個空{}obj1.a = 3;console.log(obj.a);// 1

八、數組方法slice實現對數組的深拷貝

// 當數組裏面的值是基本數據類型,好比String,Number,Boolean時,屬於深拷貝// 當數組裏面的值是引用數據類型,好比Object,Array時,屬於淺拷貝var arr1 = ["1","2","3"]; 
var arr2 = arr1.slice(0);
arr2[1] = "9";console.log("數組的原始值:" + arr1 );console.log("數組的新值:" + arr2 );

九、數組方法concat實現對數組的深拷貝

// 當數組裏面的值是基本數據類型,好比String,Number,Boolean時,屬於深拷貝var arr1 = ["1","2","3"];var arr2 = arr1.concat();
arr2[1] = "9";console.log("數組的原始值:" + arr1 );console.log("數組的新值:" + arr2 );// 當數組裏面的值是引用數據類型,好比Object,Array時,屬於淺拷貝var arr1 = [{a:1},{b:2},{c:3}];var arr2 = arr1.concat();
arr2[0].a = "9";console.log("數組的原始值:" + arr1[0].a ); // 數組的原始值:9console.log("數組的新值:" + arr2[0].a ); // 數組的新值:9

十、使用擴展運算符實現深拷貝

// 當value是基本數據類型,好比String,Number,Boolean時,是可使用拓展運算符進行深拷貝的
// 當value是引用類型的值,好比Object,Array,引用類型進行深拷貝也只是拷貝了引用地址,因此屬於淺拷貝
var car = {brand: "BMW", price: "380000", length: "5米"}
var car1 = { ...car, price: "500000" }
console.log(car1); // { brand: "BMW", price: "500000", length: "5米" }
console.log(car); // { brand: "BMW", price: "380000", length: "5米" }

十一、Object.create()實現深拷貝

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;
}
相關文章
相關標籤/搜索