因為公司前端資料已經處理成單層結構,所以都沒注意到淺拷貝、深拷貝的實際差別。
在讀完高手文章後,才發現和自己想的不一樣。
也順手將文章重點整理,分享給大家,別枉費自己寫這麼多Code。
string
、number
、boolean
、null
、undefined
Object.assign()
...
array.slice()
array.concat()
array.map()
array.filter()
forEach
+push()
...
JSON.parse(JSON.stringify())
_.cloneDeep()
...等等var a = 10;
var b = 10;
console.log(a === b); // true
a
、b
皆為基本型別,故互相比較時回傳true
var obj1 = { a : 1 };
var obj2 = { a : 1 };
console.log(obj1 === obj2); // false
雖然物件內容相等,但是物件的記憶體位置卻不同,故比較結果為false
。
不同的例子
var obj1 = { a : 1 };
var obj2 = obj1;
obj1.a = 0;
console.log(obj1.a); // 0
console.log(obj2.a); // 0
obj2.a = 2;
console.log(obj1.a); // 2
console.log(obj2.a); // 2
console.log(obj1 === obj2); // true
當我們修改任何一邊的屬性時,會一起變動,這是因為物件透過傳址的方式指派,所以兩個物件會指向同一個記憶體位置,實際上也意味著並未產生新的物件。
但若是這樣呢?
var obj1 = { a : 1 };
var obj2 = obj1;
obj1.a = 0;
console.log(obj1.a); // 0
console.log(obj2.a); // 0
obj2.a = 2;
console.log(obj1.a); // 2
console.log(obj2.a); // 2
obj1 = {}; // 指派新的物件
console.log(obj1 === obj2); // false
當obj1
被指向新的記憶體位置,但是obj2
依然保持原本的記憶體位置,因此這時obj1
、obj2
彼此就毫無關係了。
複製物件可以分為兩種
物件的淺拷貝
Object.assign()
...
Object.assign(target, source)
能複製一個或多個物件自身所有可數的屬性到另一個目標物件。
將原本的obj
內容複製到另一個空物件
var obj = { a : 1, b : 2 };
var obj2 = Object.assign({}, obj);
console.log(obj2); // { a : 1, b : 2 }
console.log(obj == obj2); // false
展開運算子
var obj = { a : 1, b : 2 };
var obj2 = { ...obj1 };
console.log(obj2); // { a : 1, b : 2 }
console.log(obj === obj2); // false
陣列的淺拷貝
array.slice()
array.concat()
array.map()
array.filter()
forEach
+push()
...
slice()
原本是用在分割陣列,但用參數為0或不傳入的話,相當於淺拷貝
var arr = [1, 2, 3, 4];
var arr2 = arr;
console.log(arr === arr2); // true
var arr3 = arr.slice(0);
console.log(arr === arr3); // false
console.log(arr3); // [1, 2, 3, 4]
concat()
原本是用在組合陣列,但可以使用空陣列合併,也相當於淺拷貝
var arr = [1, 2, 3, 4];
var arr2 = [].concat(arr);
console.log(arr === arr2); // false
consoloe.log(arr2); // [1, 2, 3, 4]
map()
是將執行結果會存至新陣列,若回傳原本的元素,也相於淺拷貝
var arr = [1, 2, 3, 4];
var arr2 = arr.map(x => x);
console.log(arr === arr2); // false
console.log(arr2); // [1, 2, 3, 4]
filter
是將符合條件的值存至新陣列,若條件皆為true
,也相當於淺拷貝
var arr = [1, 2, 3, 4];
var arr2 = arr.filter(x => { return true; } );
console.log(arr === arr2); // false
console.log(arr2); // [1, 2, 3, 4]
...
也可用於陣列,效果也是淺拷貝
var arr = [1, 2, 3, 4];
var arr2 = [ ...arr ];
console.log(arr === arr2); // false
console.log(arr2); // [1, 2, 3, 4]
至於為什麼叫淺拷貝,可以看以下程式碼
var obj1 = {
foo : 10,
bar : {
baz : 20,
},
};
var obj2 = { ...obj1 };
console.log(obj1 === obj2); // false
console.log(obj1.bar === obj2.bar); // true
淺拷貝後,obj1
、obj2
已經是兩個不同的物件,但是第二層的物件卻是相同的記憶體位置。
再看看陣列
var arr = [{a : 1}, {b : 2}];
var arr2 = [...arr];
console.log(arr === arr2); // false
console.log(arr[0] === arr2[0]); // true
淺拷貝後,arr
、arr2
已經是不同的陣列,但是裡面的物件卻是同一個。
上述只是有容器不同,若有巢狀或多維的狀況,仍然是傳址。因為深層的物件或陣列還是傳址,不會完全複製一份,所以稱為『淺拷貝』。
深拷貝是完全複製一份新的,不會共用記憶體位置。
Json.parse(Json.stringify())
_.cloneDeep()
...等等利用JSON
var obj1 = {
a : 1,
b : {
val: 5
}
};
var obj2 = JSON.parse(JSON.stringify(obj1));
console.log(obj1 === obj2); // false
console.log(obj1.b === obj2.b); // false
但JSON的轉法須注意
undefined
、Symbol
會被忽略轉換NaN
、Infinity
會被轉換成 null
var obj1 = {
a: function() {},
b: undefined,
c: Symbol(''),
d: NaN,
e: Infinity,
f: -Infinity,
};
var obj2 = JSON.parse(JSON.stringify(obj1));
console.log(obj2); // {d: null, e: null, f: null}
你這篇87%的ctrl + c、ctrl + v,不太優喔
是整理參考文章,也很希望大家去看原本的文章~
竹白大大的筆記很優質,看他筆記學習長大的XDDD,推推
你的文章也不錯,回想起一些忘記的基礎