JavaScript Pass by Value and Reference, The Difference?
JavaScript 傳值與傳址,都幾咧?
傳值 (Pass by value)
在 JavaScript 中,當變數的值是原生型別時,就是傳值
如果傳遞的變數是原生型別時,傳遞的就會是值的複本,而不是傳遞變數的記憶體位置。我們可以使用 = 來賦予變數一個值舉以下的例子,分別賦予不同數字給 a、b:
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];| 變數 | 值 |
|---|---|
| a | 1 |
| b | 0x01 |
| 記憶體位址 | 值 |
|---|---|
| 0x01 | [1,2] |
了解了傳址的概念後,我們來延伸前面的例子,如果這時候有個變數 c = b 那麼畫成圖表就會是這樣:
const a = 1;const b = [1, 2];const c = b;| 變數 | 值 |
|---|---|
| a | 1 |
| b | 0x01 |
| c | 0x01 |
| 記憶體位址 | 值 |
|---|---|
| 0x01 | [1,2] |
如此一來 b 與 c 所指向的記憶體位置都是 0x01,因此當我們對 c 做修改時,b 也會跟著改變:
let a = 1;let b = [1, 2];let c = b;b.push(3);// 結果:b 與 c 都變成 [ 1, 2, 3 ] 了!這也是為什麼 b 與 c 的值都會變成 [1, 2, 3],因為 b 與 c 都指向了同一個記憶體位置。
相等但不相等
了解前面傳值與傳址的差異後,會發現記憶體位置與值是全然不同的東西,一個是指標,一個是內容,實際就像以下圖表範例。
const a = [1, 2];const b = [1, 2];
console.log(a === b); // false| 變數 | 值 |
|---|---|
| a | 0x01 |
| b | 0x02 |
| 記憶體位址 | 值 |
|---|---|
| 0x01 | [1,2] |
| 0x02 | [1,2] |
let a = [1, 2];let b = a;console.log(a === b); // true| 變數 | 值 |
|---|---|
| a | 0x01 |
| b | 0x01 |
| 記憶體位址 | 值 |
|---|---|
| 0x01 | [1,2] |
總結
了解 JavaScript 是如何儲存變數有助於更好的操控資料,避免出現改 A 卻動到 B 的狀況。