keywords:reference-copy
,scalar primitives
值 VS. 參考
值的型別完全控制是值的拷貝或參考的拷貝 ( by reference-copy )
let a = 6; let b = a; // b 一定是 a 中的值的一份拷貝 b++; a; // 6 b; // 7 ------------------------------------------ let c = [4,5,6]; let d = c; // d 是指向共有值 [4,5,6] 的一個參考 d.push(7); c; // [4,5,6,7] d; // [4,5,6,7]
上方 c、d 都是對同一個共有值 [4,5,6] ( 這是一個複合值 ) 的個別參考,c 與 d 不會有誰比較擁有那個 [4,5,6] 值,兩者都是該值同等的參考
簡單的值 ( 純量的基型值 scalar primitives ) 永遠都是藉由值的拷貝來指定或傳遞:null、undefined、string、number、boolean、symbol ( ES6 )、bigint
let a = 1n; let b = a; b++; a; // 1n b; // 2n
複合值的 object、array、function,永遠在指定或傳遞時,建立參考的一份拷貝
let a = [4,5,6]; let b = a; a; // [4,5,6] b; // [4,5,6] b = [7,8,9]; a; // [4,5,6] b; // [7,8,9]
參考指向的是值本身,而非變數,無法使用一個參考 ( b ) 來變更另一個參考 ( a ) 所指向的東西,進行
b = [7,8,9]
指定時,完全不會影響到 a 所指的東西 [4,5,6]這種混淆最常發生在函式參數上,面試也很愛考這種題目
function f1(p) { p.push(4); p; // [1,2,3,4] p = [4,5,6]; // 在記憶體建立一個 [4,5,6] 並將它的參考放到 p p.push(7); p; // [4,5,6,7] } let c = [1,2,3]; f1(c); c; // [1,2,3,4]
沒辦法使用 p 參考變更 c 所指向的東西,只能修改 c 及 p 兩者都指向的那個共有值的內容。
如果要修改 c 變成 [4,5,6,7] 不能建立一個新的 array 再指定給它,必須修改現有的那個 array 值function f1(p) { p.push(4); p; // [1,2,3,4] p.length = 0; p.push(4,5,6,7); p; // [4,5,6,7] } let c = [1,2,3]; f1(c); c; // [4,5,6,7]
要藉由值的拷貝傳入一個複合值,得手動製作它的一個拷貝,讓傳入的參考不能指向原複合值
不帶參數的 Array.prototype.slice() 預設會製作出 array 的一個全新淺層拷貝function f1(p){ p.push(4); p; // [1,2,3,4] p.length = 0; p.push(4,5,6,7); p; // [4,5,6,7] } let c = [1,2,3]; f1(c.slice()); c; // [1,2,3]
相反的,如果想要傳入一個純量的基型值,並讓其值可被更新,類似參考那樣,可以將該值包裹在一個能夠以參考的拷貝傳遞的複合值
function f1(wrapper) { wrapper.c = 50; } var obj = { c:9 }; f1(obj); obj.c; //50 obj 就是純量基型值 c 的包裹器