上一篇講到pass by value/ reference/ sharing,了解基本型別及物件型別的儲存空間方式不同之後就可以來講淺拷貝(shallow copy)
與深拷貝(deep copy)
了。
這邊的版本會用pass by value, pass by reference的方式來說~
複習一下:
如果要拷貝一個物件型別可以分為淺拷貝、深拷貝的方式。
淺拷貝的意思是說只有一層的物件的話會是不同的物件,不會參考同個reference,但如果物件裡面還有物件的話,則第二層的參考還是會是同個reference。
所以不會完全的拷貝,只有第一層會是pass by value,再多一層就會是pass by reference,就會改變到原本的物件。
淺拷貝還有可以使用Object.assign()
的方式,不過這裡就只介紹展開運算子的方式,因為例子都差不多,只是要知道這些都無法完全拷貝一個新的物件。
ES6新增的展開運算子可以做到第一層的拷貝。
const a = {
a: 1,
};
const b = { ...a };
b.a = 2;
console.log(b === a); // false;
console.log(b.a === a.a); // false
console.log(b); // { a: 2 }
console.log(a); // { a: 1 }
可以看到用這種方式a !== b
,所以這兩個並不是相同的物件,我們就成功了嗎?
那把a的value變成一個物件{ a: 1 }
const a = {
a: { a: 1 },
};
const b = { ...a };
b.a.a = 2;
console.log(b === a); //false;
console.log(b.a === a.a); //true
console.log(b.a.a === a.a.a); // true
console.log(b); // { a: { a: 2 } }
console.log(a); // { a: { a: 2 } }
雖然a, b並不是相同的物件了,但因為a的value又是一個物件,而這個物件型別就還是pass by reference!所以第二層的b.a === a.a // true
跟第三層的 b.a.a === a.a.a // true
我們又沒有用展開運算子再把第二層的物件再次拷貝一下,所以就算物件用展開運算子拷貝就不會是同個物件,卻顧不到裡面第二層的物件。
如果要變成深拷貝的話也不是不可能,可以用JSON.stringify
把物件變成字串,再用JSON.parse()
把他轉回來,這樣就可以完全變成獨立的物件,並不會有任何參考reference的地方了。
深拷貝就是就算裡面還有第二層的物件也不會是參考同的reference,兩個物件會完全不相同。
第一個實作來用JSON來做:
const c = JSON.parse(JSON.stringify(a));
c.a.a = 2;
console.log(a === c); //false
console.log(a.a === c.a); //false
console.log(a.a.a === c.a.a); //false
console.log(c); // { a: { a: 2 } }
console.log(a); // { a: { a: 1 } }
然而我們在上面知道是因為第二層的物件沒有經過展開運算子的處理所以才讓他pass by reference,只有處理到第一層的物件而已。
那麼我們可以寫一個function來讓裡面不管有幾多層物件,都可以被處理才對。
建立一個叫做deepClone的funcion
for...in
去迭代keyinputObject[key]
是否等於物件,如果還是一個物件就再遞迴一次,讓他直到裡面不是物件的時候才跑到else去賦值給outputObject[key]
const a = {
a: { a: 1 },
};
function deepClone(inputObject) {
const outputObject = Array.isArray(inputObject) ? [] : {};
for (const key in inputObject) {
if (typeof inputObject[key] === "object") {
outputObject[key] = deepClone(inputObject[key]);
} else {
outputObject[key] = inputObject[key];
}
}
return outputObject;
}
const b = deepClone(a);
b.a.a = 2;
console.log(a === b); // false
console.log(a.a === b.a); //false
console.log(a.a.a === b.a.a); //false
console.log(a); // { a: { a: 1 } }
console.log(b); // { a: { a: 2 } }
這樣就算第二層也是物件也不會影響到原本的物件了,因為第二層的物件我們也已經再去做處理了。
這個實作看起來很複雜我懂!可是建議可以自己寫寫看會比較清楚他們之間的關係。
Object.assign()
去拷貝一個一樣的物件。今天就這樣拉!
當初剛唸完pass系列的時候想說休息一下感覺深拷貝淺拷貝很難,但最近在看的時候覺得也還好呀,所以能一鼓作氣就一次學完吧!
明天見了!
參考資料:JS 中的淺拷貝 (Shallow copy) 與深拷貝 (Deep copy) 原理與實作
如何写出一个惊艳面试官的深拷贝?
關於JS中的淺拷貝(shallow copy)以及深拷貝(deep copy)