Fig. 1. call by value 示意, PJCHENder,https://pjchender.blogspot.com/2016/03/javascriptby-referenceby-value.html
在 JS 中,建立屬於原始型別(Primitive type)的變數 a ( let a = 1
) 與 b ( let b
) 時,這兩個變數會存於個別獨立的記憶體位址(0x001、0x002)。將 a 賦值給 b 時 ( b = a
),會將 0x001 記憶體位址指向的空間的資料,複製到 0x002 記憶體位址指向的空間。
// number
let num = 1;
let newNum;
newNum = num;
num = 3;
console.log(num === newNum); // false
console.log(newNum); // 1
備註:原始型別(Primitive type)為 string、number、bigint(任意大正整數)、boolean、undefined、symbol、null 都屬於 call by value。
Fig. 2. call by reference 示意, PJCHENder,https://pjchender.blogspot.com/2016/03/javascriptby-referenceby-value.html
類似的狀況,變數 a 的型別為 object 時,建立變數 b 並將 a 賦值給 b (b = a
),實際上是使 a 與 b 的記憶體指向同一個位址(0x001)。此時再更改 a 或 b 的值,a 或 b 都會被一同變更。
你以為賦的是值,但其實只是複製了一份參考用的地址。
// object 1
let user = {
name: "amy",
};
let newUser;
newUser = user;
user.name = "otter";
console.log(newUser === user); // true;比較的是 reference
console.log(newUser.name); // otter
// object 2
let a = { animal: "otter" };
let b = { animal: "otter" };
console.log(a === b); // false;比較的是 reference
// 轉成字串再比較內容
let aStr = JSON.stringify(a);
let bStr = JSON.stringify(b);
console.log(aStr === bStr); // true
// object
let user = {
name: "amy",
habbit: "eat"
};
let newUser;
newUser = user;
user = { animal: "otter" }; // 物件實字
console.log(newUser); // name: 'amy', habbit: 'eat'
console.log(user); // animal: 'otter'
let user = { name: "amy" };
function reName(obj) {
obj = { eat: "apple" };
console.log(obj);
}
reName(user); // eat: 'apple'
console.log(user); // name: 'amy'
Object.assign()
// 語法
Object.assign(target, ...sources)
// 等同於
Object.assign(target, {obj1},{obj2},{obj3},...)
參數說明:
[{ obj1 }, { obj2 }, { obj3 }, ...]
所以剛剛的 object 1 例子可以這樣改寫,就不會誤更動物件了。
// 修改 object 1
let user = {
name: "amy"
};
let newUser;
newUser = Object.assign({}, user); // 包成物件
user.name = "otter";
console.log(newUser === user); // false
console.log(newUser.name); // amy
console.log(user.name); // otter
...
// 修改 object 1
let user = {
name: "amy"
};
let newUser;
newUser = { ...user }; // 使用展開運算子
user.name = "otter";
console.log(newUser === user); // false
console.log(newUser.name); // amy
console.log(user.name); // otter
JSON.parse(JSON.stringify(obj))
JSON.stringify()
轉成字串,再以 JSON.parse()
轉成物件。這樣可以確保轉出來的是一個全新物件,且使用的是不同 reference。// 修改 object 1
let user = {
name: "amy"
};
let newUser;
newUser = JSON.parse(JSON.stringify(user)); // 物件轉字串再轉物件
user.name = "otter";
console.log(newUser === user); // false
console.log(newUser.name); // amy
console.log(user.name); // otter
……差點沒暈死過去 (つд⊂)
Fig. 3. 遮臉海獺。原出處連結已死,這邊用的是宅宅新聞的獺圖❤️:https://news.gamme.com.tw/589666
參考資料