前言
接下來讓我們來了解什麼是淺層複製與深層複製吧。
淺層複製與深層複製
前面那麼多章節我們都明白了物件有所謂傳參考問題,簡單來講就是傳入一個物件,修改裡面的屬性會影響原有的物件資料,而這個狀況其實在開發上會引發相當多的 bug,舉凡來講我們想保留原始的物件資料,但是卻因為我們搞不清楚如何處理,那麼就會發生修改到原始資料,例如下面的範例
1 2 3 4 5 6 7 8
| var b = { name: 'Ray', };
var e = b;
e.name = 'QQ'; console.log(b.name);
|
這邊的運作方式允許我貼先前的圖

那我們該如何解決這個問題呢?基本上解決方式滿多的,舉例來講使用 Object.assign
來達到拷貝一個新的物件
1 2 3 4 5 6 7 8 9
| var b = { name: 'Ray', };
var e = Object.assign({}, b);
e.name = 'QQ'; console.log(b.name); console.log(e.name);
|
除此之外也可以使用 ES6 的展開語法達到相同效果
1 2 3 4 5 6 7 8 9
| var b = { name: 'Ray', };
var e = { ...b };
e.name = 'QQ'; console.log(b.name); console.log(e.name);
|
但是這邊要注意一件事情,不論是 Object.assign
或是 ES6 展開語法,通通都是屬於 淺層拷貝,什麼是淺層拷貝呢?簡單來講就是這個物件底下還有另一個物件的時候還是會修改到原始物件,例如以下程式碼
1 2 3 4 5 6 7 8 9 10 11 12
| var b = { name: 'Ray', obj: { name: 'Two' } };
var e = Object.assign({}, b);
e.obj.name = 'Tree'; console.log(b.obj.name); console.log(e.obj.name);
|
就算你使用 ES6 展開的語法也是一樣的
1 2 3 4 5 6 7 8 9 10 11 12
| var b = { name: 'Ray', obj: { name: 'Two' } };
var e = {...b}
e.obj.name = 'Tree'; console.log(b.obj.name); console.log(e.obj.name);
|
在維基百科上其實也有說明 Object.assign 並不是一個深層複製的語法
深層複製(deep clone)需要使用其他的替代方案,因為 Object.assign() 僅複製屬性值。若來源物件的值參照到一個子物件,它只會複製該子物件的參照。
那麼該如何解決這個問題呢?其實最簡單方式就是使用 JSON.stringify
,並搭配 JSON.parse
1 2 3 4 5 6 7 8 9 10 11 12
| var b = { name: 'Ray', obj: { name: 'Two' } };
var e = JSON.parse(JSON.stringify(b));
e.obj.name = 'Tree'; console.log(b.obj.name); console.log(e.obj.name);
|
這是一個最簡單解決淺層拷貝的方式,這稱之為深層拷貝,其運作原理是將物件轉換為純值,也就是 string 之後再轉回一個物件,此時的物件就會是新的記憶體空間。
但是使用 JSON.stringify
,並搭配 JSON.parse
的方式來做深層拷貝其實是有一個很大的問題,只要你的物件中有 undefined
、 NaN
或是 function
就無法生成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| var b = { name: 'Ray', obj: { name: 'Two' }, fn: function() { console.log('123'); }, un: undefined, nan: NaN, };
var e = JSON.parse(JSON.stringify(b));
console.log(e);
|
那麼在此如果想要解決這個問題的話,基本上會建議使用框架,例如 jQuery 的 $.extend
就可以做到完整的深層拷貝
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| var b = { name: 'Ray', obj: { name: 'Two' }, fn: function() { console.log('123'); }, un: undefined, nan: NaN, };
var e = $.extend(true, {}, b);
console.log(e);
|
參考文獻