1. 程式人生 > 其它 >徹底搞懂深拷貝與淺拷貝

徹底搞懂深拷貝與淺拷貝

深拷貝淺拷貝只是針對引用資料型別

JavaScript資料型別包括基本資料型別(Number,String,boolean,Null,Undefined,Symbol)、引用資料型別(Array,Object)

  1. 基本資料型別的特點:直接儲存在棧(stack)中的資料

  2. 引用資料類在棧中存有指標,實體資料存在堆中,通過棧中的指標找到堆中的引用資料型別的實體
    堆和棧的區別

淺拷貝只複製指向某個物件的指標,而不復制物件本身,新舊物件還是共享同一塊記憶體。但深拷貝會另外創造一個一模一樣的物件,新物件跟原物件不共享記憶體,修改新物件不會改到原物件。

賦值和淺拷貝的區別

當我們把一個物件賦值給一個新的變數時,賦的其實是該物件的在棧中的地址,而不是堆中的資料。

也就是兩個物件指向的是同一個儲存空間,無論哪個物件發生改變,其實都是改變的儲存空間的內容,因此,兩個物件是聯動的。

淺拷貝是按位拷貝物件,它會建立一個新物件,這個物件有著原始物件屬性值的一份精確拷貝。如果屬性是基本型別,拷貝的就是基本型別的值;如果屬性是記憶體地址(引用型別),拷貝的就是記憶體地址 ,因此如果其中一個物件改變了這個地址,就會影響到另一個物件。即預設拷貝建構函式只是對物件進行淺拷貝複製(逐個成員依次拷貝),即只複製物件空間而不復制資源。

// 物件賦值
var obj1 = {
   'name' : 'zhangsan',
   'age' :  '18',
   'language' : [1,[2,3],[4,5]],
};
var obj2 = obj1;
obj2.name = "lisi";
obj2.language[1] = ["二","三"];
console.log('obj1',obj1)
console.log('obj2',obj2)
// 淺拷貝
var obj1 = {
   'name' : 'zhangsan',
   'age' :  '18',
   'language' : [1,[2,3],[4,5]],
};
var obj3 = shallowCopy(obj1);
obj3.name = "lisi";
obj3.language[1] = ["二","三"];
function shallowCopy(src) {
   var dst = {};
   for (var prop in src) {
       if (src.hasOwnProperty(prop)) {
           dst[prop] = src[prop];
       }
   }
   return dst;
}
console.log('obj1',obj1)
console.log('obj3',obj3)

上面例子中,obj1是原始資料,obj2是賦值操作得到,而obj3淺拷貝得到。我們可以很清晰看到對原始資料的影響,具體請看下錶:

淺拷貝的實現方法

1. Object.assign()

Object.assign() 方法可以把任意多個的源物件自身的可列舉屬性拷貝給目標物件,然後返回目標物件。但是 Object.assign() 進行的是淺拷貝,拷貝的是物件的屬性的引用,而不是物件本身。

對於Object.assign()而言,如果物件的屬性值為簡單型別(string,number),通過Object.assign({},srcobj);得到的新物件為深拷貝;如果屬性值為物件或其他引用型別,那對於這個物件而言其實是淺拷貝

2. Array.prototype.concat()

let arr = [1, 3, {
   username: 'kobe'
}];
let arr2=arr.concat();
arr2[2].username = 'wade';
console.log(arr);

3. Array.prototype.slice()

let arr = [1, 3, {
   username: ' kobe'
}];
let arr3 = arr.slice();
arr3[2].username = 'wade'
console.log(arr);

深拷貝的實現

1. JSON.parse(JSON.stringify())

原理: 用JSON.stringify將物件轉成JSON字串,再用JSON.parse()把字串解析成物件,一去一來,新的物件產生了,而且物件會開闢新的棧,實現深拷貝。

這種方法雖然可以實現陣列或物件深拷貝,但不能處理函式。

這是因為 JSON.stringify() 方法是將一個JavaScript值(物件或者陣列)轉換為一個 JSON字串,不能接受函式

2. 手寫遞迴方法

//遞迴來實現深拷貝
    function deepClone(obj){
      var newObj = obj instanceof Array ? []:{};
      if(typeof obj !== 'object'){
        return obj;
      }else {
        for(var i in obj) {
          newObj[i] = typeof obj[i] === 'object' ? deepClone(obj[i]) : obj[i];
        }
      } 
      return newObj;
    }
    var q = [1, 2, 4, 6, { age: 12 }, [1, 2]];
    var d = deepClone(q)
    console.log(d)

3. 函式庫lodash

var _ = require('lodash');
var obj1 = {
   a: 1,
   b: { f: { g: 1 } },
   c: [1, 2, 3]
};
var obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f);
// false