深拷貝和淺拷貝詳解

深拷貝和淺拷貝詳解

一.概念

其實一般來說,我們遇到深淺拷貝的問題,都是針對引用數據類型的變量操作.先了解下數據類型

1.值類型和引用類型

值類型:直接存儲其值,在內存中,是存在棧內存中

引用類型: 存儲對值的引用,在內存中,是存在堆內存中,變量本身僅僅是一個指向堆中的實際數據地址,存在棧內存中(說白了,就是引用數據類型,實際上存在堆內存中,雜亂無序的放着,但是會有一個指針,指向堆內存中的地址, 而這個指針則是在棧內存中存儲)

1.1值類型
  • Boolean
  • String
  • Undefined
  • Null (typeof 去判斷時,會是object,因爲null被轉換成機器語言時很多個0,而只要前三個爲0的話,就會被機器語言判斷爲object)
  • Number
  • Symbol(ES6 唯一性)
1.2 引用類型
  • Object
  • Array
  • Function
  • RegExp
  • Date
  • Function

在這裏插入圖片描述

1.淺拷貝

淺拷貝拷貝的是對象的指針,沒有新建空間地址,新老對象共用的同一份地址,指向對內存中同一個地方,修改原來的對象會影響新對象

var obj1={
    a:1
}

var obj2=obj1;

obj2.a=5;
console.log('obj1.a',obj1.a); //5
console.log('obj2.a',obj2.a); //5

上述代碼,就是一個淺拷貝,對象通過等於號直接賦值,是一個淺拷貝,obj1對象賦值給了obj2,實際上obj2只是拷貝了obj1在內存中棧中的一個地址,也就是指針,他們倆會共同指向堆中數據

2.深拷貝

深拷貝會在內存中,開闢一個相同的空間地址,並且複製相同的數值,兩者互不干擾

2.1 JSON.parse+JSON.stringify

用JSON.stringify把對象轉爲字符串,再用JSON.parse把字符串轉爲新的對象.

      var obj3 = {
            classId: 2001,
            userinfo: {
                name: 'liuqiao',
                age: '27'
            }
        }

        var obj4 = JSON.parse(JSON.stringify(obj3));

        obj4.userinfo.name='zhangsan';
        console.log(obj3.userinfo.name); //liuqiao
        console.log(obj4.userinfo.name); //zhangsan

侷限性:Number,String,Boolean,Array,可以轉爲JSON對象,但是function這種不行

2.2 lodash 實現深拷貝

lodash是一個高性能的 JavaScript 實用工具庫,裏面的lodash.cloneDeep()實現深拷貝,原理也是遞歸實現的

  <script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>
  <script>
          var obj3 = {
            classId: 2001,
            userinfo: {
                name: 'liuqiao',
                age: '27'
            }
          }

		var obj4 = _.cloneDeep(obj3);
        obj4.userinfo.name = 'zhangsan';
        console.log(obj3.userinfo.name); //liuqiao
        console.log(obj4.userinfo.name); //zhangsan
  </script>
2.3 遞歸拷貝

實現原理就是通過遞歸,循環遍歷從最頂級開始,去找下一級,直到最後一層位置,然後賦值

  function deepClone(source) {
      		// 判斷複製的目標是數組還是對象
            const targetObj = source.constructor === Array ? [] : {}; 
            for (let keys in source) { // 遍歷目標
                if (source.hasOwnProperty(keys)) {
                    // 如果值是對象,就遞歸一下
                    if (source[keys] && typeof source[keys] === 'object') { 
                        targetObj[keys] = source[keys].constructor === Array ? [] : {};
                        targetObj[keys] = deepClone(source[keys]);
                    } else { 
                        // 如果不是,就直接賦值
                        targetObj[keys] = source[keys];
                    }
                }
            }
            return targetObj;
        }

        var obj3 = {
            classId: 2001,
            userinfo: {
                name: 'liuqiao',
                age: '27'
            }
        }
        
        var obj4 = deepClone(obj3);

        obj4.userinfo.name = 'zhangsan';
        console.log(obj3.userinfo.name); //liuqiao
        console.log(obj4.userinfo.name); //zhangsan
2.4 Object.assign()

實際上Object.assign是一個淺拷貝,但是如果是數據結構只有一層,可以實現深拷貝

		var obj6={};
        var obj5={
            a:1
        }

        obj6= Object.assign(obj6,obj3);
        obj6.a=2;
        console.log(obj6.a); //2
        console.log(obj5.a); //1

Object.assign,如果數據結構有多層,則是淺拷貝

    	var obj3 = {
            classId: 2001,
            userinfo: {
                name: 'liuqiao',
                age: '27'
            }
        }		

		var obj4={};

        obj4= Object.assign({},obj3);
        obj4.userinfo.name = 'zhangsan';
        console.log(obj3.userinfo.name); //zhangsan
        console.log(obj4.userinfo.name); //zhangsan
2.5 ES6擴展運算符 …

與Object.assign() 一樣,如果數據結構只有一層,是深拷貝,多層結構了是淺拷貝,只能拷貝第一層的

擴展運算符淺拷貝

  		 var obj3 = {
            classId: 2001,
            userinfo: {
                name: 'liuqiao',
                age: '27'
            }
        }

        obj4 = { ...obj3 }
        obj4.userinfo.name = 'zhangsan';
        obj4.classId=3004;
        console.log(obj3.userinfo.name); //zhangsan
        console.log(obj4.userinfo.name); //zhangsan
        console.log(obj3.classId); //2001
        console.log(obj4.classId); //3004

擴展運算符深拷貝

	   var obj6 = {};
        var obj5 = {
            a: 1
        }

        obj6 = { ...obj5 };
        obj6.a = 2;
        console.log(obj6.a); //2
        console.log(obj5.a); //1

二.總結

實際項目中,根據不同的業務場景,判斷是否需要使用深拷貝,方式有很多種,根據需求來自己實現即可!

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