iT邦幫忙

0

JavaScript. 淺拷貝與深拷貝

  • 分享至 

  • xImage
  •  

前陣子剛寫完 JavaScript,差不多可以開始進行 Vue 的時候,突然想起在 Vue 開發的時候總是搞不清楚什麼是淺拷貝 ( Shallow Copy ) ,什麼是深拷貝 ( Deep Copy ),也因此在練習的時候很常出現令自己感到問號的錯誤,那麼今天也算是為了讓 Vue 的開發更順暢,來研究一下這兩種寫法。

淺拷貝 ( Shallow Copy )

只能對第一層進行淺層複製,如果有第二層結構,還是會依據參考特性作處理;也就是說,在第二層開始他們的記憶體位置還是一樣的,因此依然會覆蓋掉原物件。

淺層拷貝分成兩種方法:

  • 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); 

這邊到目前為止跟上面的範例是差不多的,不一樣的地方只在於展開這個動作,而在第二層也同樣會出現改變原物件的情況。


深拷貝 ( Deep Copy )

能深度複製同樣的物件,但記憶體位置不同,兩個為獨立的記憶體空間,因此不會互相影響。

深拷貝也有兩種方式:

  • JSON.stringify,主要利用 JSON.stringify 把物件轉成字串,再用 JSON.parse 把字串轉為物件。

這裡稍微說明一下 JSON.stringifyJSON.parseJSON.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.stringifyJSON.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)


圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言