1. 程式人生 > >js淺拷貝與深拷貝方法

js淺拷貝與深拷貝方法

js有五種基本資料型別,string,number,boolean,null,undefind。這五種型別的賦值,就是值傳遞。物件的賦值是將物件地址的引用賦值。這時候修改物件中的屬性或者值,會導致所以引用這個物件的值改變。如果想要真的複製一個新的物件,而不是複製物件的引用,就要用到物件的深拷貝。

淺拷貝實現方式

1.‘=’賦值。

不多說,最基礎的賦值方式,只是將物件的引用賦值。

2.Object.assign()

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

Object.assign(target, ...sources)

引數:target:目標物件。sources:任意多個源物件。返回值:目標物件會被返回。

var obj = { a: {a: "hello", b: 21} };
var initalObj = Object.assign({}, obj);

initalObj.a.a = "changed";
console.log(obj.a.a); // "changed"

需要注意的是:Object.assign()可以處理一層的深度拷貝,如下:

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

深拷貝

1.手動複製

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 } <-- 沒被改到
console.log(obj2);
// { a: 10, b: 100, c: 30 }

2.JSON做字串轉換

用JSON.stringify把物件轉成字串,再用JSON.parse把字串轉成新的物件。

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

這樣做是真正的Deep Copy,這種方法簡單易用。

但是這種方法也有不少壞處,譬如它會拋棄物件的constructor。也就是深拷貝之後,不管這個物件原來的建構函式是什麼,在深拷貝之後都會變成Object。

這種方法能正確處理的物件只有 Number, String, Boolean, Array, 扁平物件,即那些能夠被 json 直接表示的資料結構。RegExp物件是無法通過這種方式深拷貝。

也就是說,只有可以轉成JSON格式的物件才可以這樣用,像function沒辦法轉成JSON。

var obj1 = { fun: function(){ console.log(123) } };
var obj2 = JSON.parse(JSON.stringify(obj1));
console.log(typeof obj1.fun);
// 'function'
console.log(typeof obj2.fun);
// 'undefined' <-- 沒複製

3.遞迴拷貝

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) ? [] : {};            
      arguments.callee(prop, obj[i]);
    } else {
      obj[i] = prop;
    }
  }    
  return obj;
}
var str = {};
var obj = { a: {a: "hello", b: 21} };
deepClone(obj, str);
console.log(str.a);

4.使用Object.create()方法

直接使用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;
}

5.jquery

jquery 有提供一個$.extend可以用來做 Deep Copy。

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

6.第三方函式

還有一些其它的第三方函式庫有深拷貝function,如lodash。