Value vs Reference - Which One?

傳值與傳址,都幾咧?

傳值 (Pass by value)

在 JavaScript 中,當變數的值是原生型別時,就是傳值

如果傳遞的變數是原生型別🔗時,傳遞的就會是值的複本,而不是傳遞變數的記憶體位置。我們可以使用 = 來賦予變數一個值舉以下的例子,分別賦予不同數字給 ab

const a = 1;
const b = a + 1;

可以注意到第 2 行,將 b 指定為 a 的值 +1,這時候 c 的值就會是 1 + 1 = 2,非常直覺也很好理解,這就是所謂的「傳值(Pass by value)」

傳址 (Pass by reference)

在 JavaScript 中,當變數不是原生型別時,就是傳址

當變數是物件或陣列的情況下,JavaScript 會需要額外的紀錄代表其記憶體位置,因此變數內儲存的並不是實際的內容,而是一個內容所在的記憶體位置。舉以下圖表為例,圖表中的 b 變數實際上持存的是記憶體位址:

const a = 1;
const b = [1, 2];
變數
a1
b0x01
記憶體位址
0x01[1,2]

了解了傳址的概念後,我們來延伸前面的例子,如果這時候有個變數 c = b 那麼畫成圖表就會是這樣:

const a = 1;
const b = [1, 2];
const c = b;
變數
a1
b0x01
c0x01
記憶體位址
0x01[1,2]

如此一來 bc 所指向的記憶體位置都是 0x01,因此當我們對 c 做修改時,b 也會跟著改變:

let a = 1;
let b = [1, 2];
let c = b;
b.push(3);
// 結果:b 與 c 都變成 [ 1, 2, 3 ] 了!

這也是為什麼 bc 的值都會變成 [1, 2, 3],因為 bc 都指向了同一個記憶體位置。

相等但不相等

了解前面傳值與傳址的差異後,會發現記憶體位置與值是全然不同的東西,一個是指標,一個是內容,實際就像以下圖表範例。

const a = [1, 2];
const b = [1, 2];
console.log(a === b); // false
變數
a0x01
b0x02
記憶體位址
0x01[1,2]
0x02[1,2]
let a = [1, 2];
let b = a;
console.log(a === b); // true
變數
a0x01
b0x01
記憶體位址
0x01[1,2]

總結

了解 JavaScript 是如何儲存變數有助於更好的操控資料,避免出現改 A 卻動到 B 的狀況。

參考資料