iT邦幫忙

2023 iThome 鐵人賽

DAY 14
0
Modern Web

JS30 x 鐵人30 x MDN doc系列 第 14

[Day14] - JavaScript References VS Copying(JS30 x 鐵人 30 x MDN)

  • 分享至 

  • xImage
  •  

了解 javascript 傳值(拷貝)、傳址(參照)、淺拷貝、深拷貝

今天沒有畫面,而是要來認識在 JavaScript 中的傳值(拷貝)、傳址(參照)、淺拷貝。

深拷貝,傳值(Pass by Value)和傳址(Pass by Reference)是關於如何處理變數的兩種不同方式。此外,淺拷貝(Shallow Copy)和深拷貝(Deep Copy)也都是因為傳址而衍生的關於複製物件(陣列)時的兩種不同方法。我將用自己寫的例子來介紹這幾個名詞代表的意思及關係性。

傳值(Pass by Value)和傳址(Pass by Reference)

傳值是指當你將一個原始資料類型(如數字、字串、布林值)賦值給另一個變數時,實際上是複製了原始值的內容。這樣,原始變數和新變數是獨立的,修改一個不會影響到另一個。

let a = 5;
let b = a; // 傳值
console.log(a, b); // 5 5
a = 10;
console.log(a, b); // 10 5

傳址是指當你將一個物件或陣列賦值給另一個變數時,實際上是傳遞了參考,而不是完全複製了一份內容。這表示原始變數和新變數都指向相同的物件或陣列,因此修改其中一個將影響到另一個,那麼我們要如何複製陣列或物件呢?這時候就需要用到下面的淺拷貝、深拷貝。

let arr1 = [1, 2, 3];
let arr2 = arr1; // 傳址
arr2.push(4);
console.log(arr1); // [1, 2, 3, 4]
console.log(arr2); // [1, 2, 3, 4]

淺拷貝(Shallow Copy)和深拷貝(Deep Copy)

淺拷貝是一種複製物件或陣列的方法,但它只複製了物件的第一層,內部的嵌套物件或陣列仍維持 Referencest 參照(傳址)。

let obj1 = { name: "Tim", hobbies: ["coding", "kpop"] };
let obj2 = { ...obj1 }; // 淺拷貝
obj2.hobbies.push("swimming");
console.log(obj1); // { name: 'Tim', hobbies: ['coding', 'kpop', 'swimming'] }
console.log(obj2); // { name: 'Tim', hobbies: ['coding', 'kpop', 'swimming'] }

淺拷貝Array的方式有:

  1. 使用陣列展開運算子(Array Spread Operator)
const arr = [1, 2, 3];
const newArray = [...arr];
  1. 使用 Array.from()方法:將可迭代的物件(例如陣列)轉換為新的陣列
const arr = [1, 2, 3];
const newArray = Array.from(arr);
  1. 使用 Array.slice()方法:不傳入任何參數,複製整個陣列
const arr = [1, 2, 3];
const newArray = arr.slice();
  1. 使用 Array.concat()方法:創造一個空陣列並拼接要複製的陣列
const arr = [1, 2, 3];
const newArray = [].concat(arr);

淺拷貝Object的方式有:

  1. 使用物件展開運算子(Object Spread Operator):已
    於 ECMAScript 2018(ES9)引入
const obj = { name: "Tim", age: 28 };
const newObj = { ...obj };
  1. 使用 Object.assign()方法:創建一個空物件為目標,將其他物件屬性複製過去
const obj = { name: "Tim", age: 28 };
const newObj = Object.assign({}, obj);

深拷貝是一種複製物件的方法,它會複製整個物件,包括內部的嵌套物件或陣列,並且創建一個全新的物件,與原始物件完全獨立。要進行深拷貝的方式有

  1. 使用 JSON.parse 和 JSON.stringify:這個是最簡單的方法,將物件轉換為 JSON 字串,再轉換回物件。這種方法只能處理可以被 JSON 表示的內容,無法處理函式或特殊類型的物件。
const obj = { name: "Tim", hobbies: ["coding", "kpop"] };
const newObj = JSON.parse(JSON.stringify(obj));
newObj.hobbies.push("swimming");
console.log(obj); // { name: 'Tim', hobbies: ['coding', 'kpop'] }
console.log(newObj); // { name: 'Tim', hobbies: ['coding', 'kpop', 'swimming'] }
  1. 使用第三方函式庫:許多 JavaScript 函式庫提供了深拷貝的實用功能,如 Lodash 的 _.cloneDeep 方法。
const _ = require("lodash");

let obj = { name: "Tim", hobbies: ["coding", "kpop"] };
let newObj = _.cloneDeep(obj); // 深拷貝
newObj.hobbies.push("swimming");
console.log(obj); // { name: 'Tim', hobbies: ['coding', 'kpop'] }
console.log(newObj); // { name: 'Tim', hobbies: ['coding', 'kpop', 'swimming'] }
  1. 使用自行編寫的深拷貝函式:或是自己寫一個深拷貝函式,遍歷陣列和物件,複製它們的屬性和內容。這需要一些程式碼,但可以根據特定需求進行自訂。

但是深拷貝可能會導致性能問題,特別是對於大型物件或陣列。因此,在選擇拷貝方式時,應根據實際需求來選擇淺拷貝或深拷貝。

👉Github Demo 頁面 👈

👉 好想工作室 15th 鐵人賽看板 👈

參考資料

  1. Javascript 30 官網
    https://javascript30.com/
  2. MDN 官網
    https://developer.mozilla.org/en-US/
  3. Object Rest/Spread -Properties -by Titangene
    https://ithelp.ithome.com.tw/articles/10245719

上一篇
[Day13] - Slide in on Scroll(JS30 x 鐵人 30 x MDN)
下一篇
[Day15] - LocalStorage(JS30 x 鐵人 30 x MDN)
系列文
JS30 x 鐵人30 x MDN doc30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言