前陣子剛寫完 JavaScript,差不多可以開始進行 Vue 的時候,突然想起在 Vue 開發的時候總是搞不清楚什麼是淺拷貝 ( Shallow Copy ) ,什麼是深拷貝 ( Deep Copy ),也因此在練習的時候很常出現令自己感到問號的錯誤,那麼今天也算是為了讓 Vue 的開發更順暢,來研究一下這兩種寫法。
只能對第一層進行淺層複製,如果有第二層結構,還是會依據參考特性作處理;也就是說,在第二層開始他們的記憶體位置還是一樣的,因此依然會覆蓋掉原物件。
淺層拷貝分成兩種方法:
Object.assign
Object.assign()
方法,被用來複製一個或多個物件,回傳的值則為複製的該物件。
Object.assign(target, ...sources)
target / 目標物件
sources / 複製來源物件
範例:
let ary = ['齊天大聖孫悟空', '豬八戒', '沙悟淨', '唐僧'];
let obj = {woman1: '白骨精', woman2: '紫霞仙子', man: '牛魔王'};
let aryNew = Object.assign([], ary);
aryNew[1] = '紫霞仙子';
console.log(aryNew);
// ['齊天大聖孫悟空', '紫霞仙子', '沙悟淨', '唐僧'] 新陣列資料已更改
console.log(ary);
// ['齊天大聖孫悟空', '豬八戒', '沙悟淨', '唐僧'] 原陣列不受影響
let objNew = Object.assign({}, obj);
objNew.woman1 = '鐵扇公主';
// {woman1: '鐵扇公主', woman2: '紫霞仙子', man: '牛魔王'} 新物件資料更改
console.log(objNew);
// {woman1: '白骨精', woman2: '紫霞仙子', man: '牛魔王'} 原物件沒有被更動
console.log(obj);
但目前我們做的都只是第一層的拷貝,底下我們來試試看在更深入的拷貝,結果會是如何。
let ary = [{woman1: '白骨精', woman2: '紫霞仙子', man: '牛魔王'}];
let aryNew = Object.assign([], ary);
aryNew[0].woman1 = '鐵扇公主';
console.log(ary);
// {woman1: '鐵扇公主', woman2: '紫霞仙子', man: '牛魔王'}
可以發現到了第二層,會直接更動原物件。
運算子展開的淺拷貝方式,是 ES6 新增的特性,主要是把一個陣列展開變成個別值,再放入指定的物件或陣列。
範例:
let ary = ['齊天大聖孫悟空', '豬八戒', '沙悟淨', '唐僧'];
let obj = {woman1: '白骨精', woman2: '紫霞仙子', man: '牛魔王'};
let aryNew = [...ary];
aryNew[1] = '紫霞仙子';
console.log(aryNew);
// ['齊天大聖孫悟空', '紫霞仙子', '沙悟淨', '唐僧'] 新陣列資料已更改
console.log(ary);
// ['齊天大聖孫悟空', '豬八戒', '沙悟淨', '唐僧'] 原陣列不受影響
let objNew = {...obj};
objNew.woman1 = '鐵扇公主';
// {woman1: '鐵扇公主', woman2: '紫霞仙子', man: '牛魔王'} 新物件資料更改
console.log(objNew);
// {woman1: '白骨精', woman2: '紫霞仙子', man: '牛魔王'} 原物件沒有被更動
console.log(obj);
這邊到目前為止跟上面的範例是差不多的,不一樣的地方只在於展開這個動作,而在第二層也同樣會出現改變原物件的情況。
能深度複製同樣的物件,但記憶體位置不同,兩個為獨立的記憶體空間,因此不會互相影響。
深拷貝也有兩種方式:
JSON.stringify
,主要利用 JSON.stringify
把物件轉成字串,再用 JSON.parse
把字串轉為物件。這裡稍微說明一下 JSON.stringify
和 JSON.parse
,JSON.stringify
為物件變 JSON 字串,JSON.parse
則是將 JSON 字串轉物件。
JSON.stringify
範例:
let me = {name: 'Jemma'};
console.log(JSON.stringify(me)); // {"name":"Jemma"}
JSON.parse
範例:
let myName = JSON.parse('{"name":"Jemma"}');
console.log(myName); // {name: 'Jemma'}
稍微解釋完 JSON.stringify
和 JSON.parse
,回歸深拷貝的話題,剛剛說到深拷貝可以使用JSON.stringify
方法,我們一樣直接來看範例:
let ary = [{woman1: '白骨精', woman2: '紫霞仙子', man: '牛魔王'}];
let aryNew = JSON.parse(JSON.stringify(ary));
aryNew[0].woman1 = '鐵扇公主';
console.log(ary);
// {woman1: '白骨精', woman2: '紫霞仙子', man: '牛魔王'} 原資料沒有被更動
console.log(aryNew);
// {woman1: '鐵扇公主', woman2: '紫霞仙子', man: '牛魔王'}
可以看到這個範例,其實是拿示範淺拷貝第二層資料是否改變的那個範例,來做深拷貝示範,在淺拷貝的時候原資料確實被改變了,但在深拷貝的時候可以看到,aryNew 是一筆新的資料,因此不影響原本的 ary。
$.extend
在 jQuery 也提供一個深拷貝的方法,利用 $.extend
指定強制深拷貝,這裡也稍微解釋一下 $.extend
。
$.extend
/ 將兩個或更多對象的內容合併到第一個對象。
語法:
jQuery.extend( [deep ], target, object1 [, objectN ] )
// deep 深拷貝
回到利用 $.extend
做深拷貝的範例,這裡我一直無法 console 出來滿滿問號臉,為了避免花太多時間卡在這裡,因此暫時先借用網路大神的範例:
let data = [
{
name: 'Eric',
weight: 60,
},
];
let dataCP = $.extend(true, [], data);
// 操作第一層 : 不影響原物件
dataCP.push({
name: 'Alice',
weight: 50,
});
// 操作第二層 : 不影響原物件
dataCP[0].name = 'Emma';
console.log(data); // [ { name: 'Eric', weight: 60 } ]
console.log(dataCP); // [ { name: 'Emma', weight: 60 }, { name: 'Alice', weight: 50 } ]
今天的文章就先分享到這,日後有研究出個什麼再補上了。
參考資料:
JavaScript 淺拷貝 (Shallow Copy) 與深拷貝 (Deep Copy)