天天看點

javaScript記錄淺拷貝與深拷貝

在js中,一切皆對象(null和undefined有待另外的了解),JavaScript 中的幾乎所有對象都是位于原型鍊頂部 

Object

 的執行個體。

對象的基本的一些操縱,我們是必須要知道的。

let obj = {
  a: 1,
  b: 2,
};
let copy = obj;
copy.a = 5;
console.log(obj.a); // ==> 5
           

上面的代碼應該都能了解,指派運算符不會建立一個對象的副本,它隻配置設定一個引用,當然數組也提供了concat 和slice 來進行淺拷貝。

那我們就想到了,循環周遊對象

var obj = { a:1, arr: [2,3] };
var shallowObj = shallowCopy(obj);
function shallowCopy(src) {
  var dst = {};
  for (var prop in src) {
    if (src.hasOwnProperty(prop)) {
      dst[prop] = src[prop];
    }
  }
  return dst;
}
shallowObj.a = 4  ==>  obj.a =1
shallowObj.arr[1] = 5 ==> obj.arr[1] = 5
           

顯然上面的周遊是起了一定的效果的,但是這隻能算對象的淺拷貝(shallow copy),原因:因為淺拷貝隻會将對象的各個屬性進行依次複制,并不會進行遞歸複制,而 JavaScript 存儲對象都是存位址的,是以淺拷貝會導緻 obj.arr 和 shallowObj.arr 指向同一塊記憶體位址。

那改變一下上面的代碼,我們需要用疊代來實作對對象的深拷貝(deep copy)

var obj = { a:1, arr: [2,3] };
var deepObj = deepCopy(obj);
function deepCopy(obj) {
	var newObj = obj.constructor === Array ? [] : {}
	if (typeof obj !== 'object') {
		return
	} else {
		for (var i in obj) {
			if (obj.hasOwnProperty(i)) {
                // 判斷子元素是否為對象
				newObj[i] = typeof obj[i] === 'object' ? deepCopy(obj[i]) : obj[i]
			}
		}
	}
	return newObj
}
deepObj.arr[1] = 5
console.log(obj.arr[1])  ==> 3
           

解釋:知乎網友的解釋

對于字元串類型,淺複制是對值的複制,對于對象來說,淺複制是對對象位址的複制,并沒 有開辟新的棧,也就是複制的結果是兩個對象指向同一個位址,修改其中一個對象的屬性,則另一個對象的屬性也會改變,而深複制則是開辟新的棧,兩個對象對應兩個不同的位址,修改一個對象的屬性,不會改變另一個對象的屬性。

項目中,我也遇到Object.assign({},obj) 這種淺拷貝與JSON.parse(JSON.stringfy(obj))這種深拷貝,但是JSON.parse(JSON.stringfy(obj)) 這種方法是有缺陷的

  • 不能解決循環引用的對象
  • 會忽略 

    undefined

  • 不能序列化函數
let obj = {
  a: 1,
  b: {
    c: 2
  },
}
obj.c = obj.b
obj.b.c = obj.c
let newObj = JSON.parse(JSON.stringify(obj))
           
javaScript記錄淺拷貝與深拷貝
let obj = {
  age: undefined,
  name: 'demo',
  test: function test() {
    return true;
  },
}
 
let method1 = Object.assign({}, obj);
let method2 = JSON.parse(JSON.stringify(obj));
 
console.log(method1);
/* result
 age: undefined,
{
  test: function test() {
    return true;
  },
  name: "demo"
}
*/
 
console.log(method2);
/* result
{
  name: "demo"
}
*/
           

最後,我也看到了一篇文章,《深入剖析 JavaScript 的深複制》,涉及到 jQuery, underscore, lodash 等庫關于深複制的實作,有興趣可以看看。

繼續閱讀