本篇內容參考連結
PJCHENder
首先必須了解, 在Javascript中, 有需多資料型別, 主要分成兩大類. 分別是基本型別(primitive type)和物件型別(Object).
一般來說, Primitive type會是Call by Value, 而Object則是 Call by Reference. 接下來會用幾個程式範例來解釋.
直接來看簡單的範例, 先建立一個變數 a
並賦予值 3
. 再來建立變數 b
, 並把b
的值等於a
. 最後再把 a
的值改成2
.
var a = 3;
var b;
b = a;
a = 2;
console.log(a);
console.log(b);
再來我們看到結果很直觀的呈現
a is 2
b is 3
由此可知道當把一個變數等於另一個變數時, 只把值賦予給另一個變數, 之後兩者是獨立的, 操作並不互相影響. 往更底層去了解的話, 即是當我們在建立 primitive type 的變數時, 會把他存在記憶體的某個地方(假設為 0x001), 而當我們宣告另一個變數, 並把前一個的值賦予給他的時候, 他其實會建立自己的記憶體位址(假設為 0x002). 所以當之後操作的時候, 因為本身就處於不同的記憶體位址, 所以不會互相干擾. 這個就是所謂的 Call by Value.
Call by Value 大部分都會發生在 primitive type 的變數
直接來看簡單的範例, 先建立一個變數 a
並賦予一個Object { text: "Hello"}
. 再來建立變數 b
, 並把b
的值等於a
. 最後再把 a
裡的Property text的值改成Hi
.
var a = { text: "Hello"};
var b;
b = a;
a.text = "Hi";
console.log(a);
console.log(b);
結果b的值也一起被更新了
{ text: 'Hi' }
{ text: 'Hi' }
當變數為Object時, 賦予給先的變數. 兩者會相依, 在之後的操作也會互相影響. 從記憶體的方面來說, 當建立a時, 會存在位址(假設為 0x001). 而當建立b並把a的值賦予b時, 這時候並不會再給他一個新的位址, 而是把b指定到相同位址(0x001). 所以當在操作更改值時, 從變數a或變數b, 其實都是對存在位址0x001的值做操作, 所以才會更改a的時候, b也一起被更新. 這個就是所謂的 Call by Reference.
Call by Reference 大部分都會發生在 Object 的變數
等於(== 或是 ===)被用reference type上, 則會比較reference不會是value. 參考以下例子
var a = { text: "Hello"};
var b;
console.log(a == b); // true
var c = { text: "Hello"};
var d = { text: "Hello"};
console.log(c == d); // false
所以在比較時, 可以先把其轉成primitive type: string
var c = { text: "Hello" };
var d = { text: "Hello" };
console.log(c == d); // false
var e = JSON.stringify({ text: "Hello" });
var f = JSON.stringify({ text: "Hello" });
console.log(e == f); // true
這次來看一個比較特別的例子, 對上面call by reference的例子做點修改, 這次不是更新Object裡的Property, 而是直接給一個新的Object.
var a = { text: "Hello" };
var b;
b = a;
a = { text: "Hi" };
console.log(a);
console.log(b);
這次結果則不會一起更新
{ text: 'Hi' }
{ text: 'Hello' }
當Object被重新賦予值時(並非更新Property), 則會記憶體給予一個新個位置. 事實上, Javascript不屬於單純的by value 或是 by Reference. 更準確來說就是Call by sharing. 所以當Object被重新賦值時, 基本上就會跟Pass by Value的結果一模一樣. 當Object僅內容被更新時, 則會回到call by reference的結果.