iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 25
3

之前在第 15 天有跟各位捧油們提到過 物件 在實務上出場次數之頻繁,是必需要去了解的一塊;那篇底下也有貼出阿宅 PO 很常拜讀的幾篇大師的文章,非常推薦新手們服用!

今天要聊的主題 -- 深拷貝、淺拷貝,就是與 JavaScript 物件 (Object) 有相關喔!

Call by Value

之前有提過 JavaScript 的型別有分為兩種 -- 基本型別物件
這兩者在傳值行為上有很大的不同,怎麼說呢?
來~我們來看幾段程式碼:

let number01 = 520;
let number02 = number01;
console.log(number02);// 520

number01 = 1314;

console.log(number01);// 1314
console.log(number02);// 520

在上面這段程式碼中可以看到,number02 的值一開始是複製 number01 的值,所以 console.log 出來的值是 520,這邊沒有問題~
而後面我們在另外賦新的值給 number01 之後再 console.log 出來看看,number01 的值已改成新的值,但 number02 的值並 沒有改變

原因是因為在 JavaScript 的 基本型別 裡的傳值方式是 Call by Value
number02 單純複製了 number01 的罷了,所以當我們在修改 number01 的值的時並不會修改到 number02 的值。 就是像我們在打文章的 Copy、Paste

但是在 物件 上的實作,情況可就大大的不同囉~~

Call by Reference

一樣按照上面的複製的情境來一次,但對象改成 物件

let object01 = {a:"Jack", b:"Anne"};
let object02 = object01;
console.log(object02);// {a:"Jack", b:"Anne"}

object01.a = "Peter";

console.log(object01);// {a:"Peter", b:"Anne"}
console.log(object02);// {a:"Peter", b:"Anne"}

想必眼睛雪亮亮的觀眾們一定發現了詭異之處...
那就是,為什麼我重新賦值給 object01 但 object02 的值卻也跟著改變了!?

那是因為在 JavaScript Object 的傳值方式是 Call by Reference
什麼是 Reference ? 意思就是你複製出來的另一個物件,他們共用一個參考,也就是共用一個記憶體位置

這邊用圖片解釋會更清楚

從上圖我們可以看到,object01 跟 object02 他們都是指向同一個記憶體位置,這個記憶體內容放著 {a:"Jack", b:"Anne"} 這個資料,當我們修改無論是 object01 OR object02,實際上都是改到同一個記憶體內的物件資料

淺拷貝 (Shallow Copy)

那我們如果希望可以像第一種基本型別的複製資料的方式,使用在物件上呢?
阿宅 PO 收集了以下的幾種方法:

  1. 建立一個新物件
let object01 = {a:"Jack", b:"Anne"};
let object02 = {a:object01.a, b:object01.b};
object01.a = "Peter";

console.log(object01);// {a:"Peter", b:"Anne"}
console.log(object02);// {a:"Jack", b:"Anne"}
  1. ES6 Object.assign(target, ...sources)
let object01 = {a:"Jack", b:"Anne"};
let object02 = Object.assign({}, object01);
object01.a = "Peter";

console.log(object01.a)// "Peter"
console.log(object02.a)// "Jack"
  1. ES6 展開運算子(Spread Operator)
let object01 = {a:"Jack", b:"Anne"};
let object02 = {...object01};
object01.a = "Peter";

console.log(object01.a)// "Peter"
console.log(object02.a)// "Jack"

以上這 3 個方式只能用在於 只有一層的物件資料格式
所以才會叫做 淺拷貝
當資料內是 物件內又包物件 的時候,這 3 個方式在資料的第二層以上的內容,又會回到 Call by Reference 的情形囉~

深拷貝 (Deep Copy)

  1. 轉換成 JSON 格式,再轉回物件 (JSON.stringify & JSON.parse)
let object01 = {a:"Jack", b:{c:"Anne"}};
let object02 = {...object01}; // 實作上方方法來操作 兩層物件
let transJSON = JSON.parse(JSON.stringify(object01));
object01.a = "Peter"
object01.b.c = "Mary"

console.log(object01);// {a:"Peter", b:{c:"Mary"}};

console.log(object02);// {a:"Jack", b:{c:"Mary"}}; 第二層物件依然被改變

console.log(transJSON);// {a:"Jack", b:{c:"Anne"}}; 完美複製

Tips: 要 依序 先 JSON.stringify 再 JSON.parse

  1. 使用 Lodash Library _.cloneDeep(obj)
let object01 = {a:"Jack", b:{c:"Anne"}};
let object02 = _.cloneDeep(object01)
object01.b.c = "Mary"

console.log(object01);// {a:"Jack", b:{c:"Mary"}};
console.log(object02);// {a:"Jack", b:{c:"Anne"}}; 完美複製

Reference Data


上一篇
前端營養補給品 - 為自己念點英文保平安
下一篇
前端心法碎碎念 - 真的千萬不要怕問蠢問題,聽懂、做對事,團隊會很愛你
系列文
前端設計轉前端工程師-JS踩坑雜記 30 天30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言