0%

JS - 「傳值」或「傳址」

基本型別

1
2
3
4
var a = 10;
var b = 10;

console.log( a === b ); // true

在基本型別時,會認為兩個變數的「值」是相等的,因為兩個變數的「值」都是 10 ,同樣的在字串的情況下也是相同的。

1
2
3
4
5
6
var a = 'yswu';
var b = 'yswu';
var c = 'wu';

console.log( a === c ); // false
console.log( a === b ); // true

因此在基本型別(如stringnumberbooleannullundefined),判斷兩個變數是否相等,看的是裡面的內容,也就是裡面的「值」。

繼續來看:

1
2
3
4
5
var c = a;
c++

console.log( a ); //10
console.log( c ); // 11

c 指定 a 時,由於 a基本型別,所以 c 得到的是 a 的值而不是 a 的記憶體位置,所以儘管 c 改變了 a 也不會受到引響,兩者是獨立的。

當指定(賦值)一個基本型別給變數,就稱作「傳值」(pass by value)。

物件型別

在物件的情況下,先分別宣告兩個物件,兩個物件都有個 value 的屬性。

1
2
3
4
var obj1 = { value : 10 };
var obj2 = { value : 10 };

console.log( obj1 === obj2 ); // false

可以觀察到,obj1obj2 的屬性和值都相同,得到的卻是false。

這是因為在JavaScript物件,可以把object當作是獨立存在的實體,兩者的記憶體位置並不相同,在比較物件型別時,比較的是記憶體位置,而非值。

繼續來看:將 obj2 利用 obj2 = obj1 的方式來賦值。

1
2
3
4
5
6
7
8
9
var obj1 = { value : 10 };
var obj2 = obj1;
console.log( obj1 === obj2 ); // true

obj1.value = 1;
console.log( obj2.value ); //1

obj1.flag = true;
console.log ( obj2.flag ); //true

在這邊可以看到,因為 obj1obj2 兩者變數指向相同的記憶體位置,所以 obj1 做修改或新增, obj2 也會跟著改變。

但將 obj1 賦值新的物件時:

1
2
3
obj1 = {};

console.log( obj1 === obj2 ); // false

此時因為 obj1指向新的記憶體位置,而 obj2 還在原來的記憶體位置,因此 obj1obj2 就沒有關係了。

當指定(賦值)一個物件型別給變數,就稱作「傳址」(pass by reference)。

JavaScript 是「傳值」或「傳址」?

在大多數的情況下,基本型別是「傳值」,而物件型別會是「傳址」的方式,但凡事都有例外

1
2
3
4
5
6
7
8
var obj1 = { value: 10 };

function changeValue(obj) {
obj = { value: 999 };
}

changeValue(obj1);
console.log(obj1); // { value: 10 }

前面說過物件是利用「傳址」的方式來更新資料,那應該會變成 { value: 999 } 怎麼還是一樣的呢?

事實上,JavaScript 不屬於單純的「pass by value」或「pass by reference」。 更準確一點來說,JavaScript 應該屬於透過 pass by sharing 來傳遞資料。

Pass by sharing

「Pass by sharing」的特點在於,當 function 的參數,如 function changeValue(obj){ ... } 中的 obj 被重新賦值的時候,外部變數的內容是不會被影響的。

如果不是重新賦值的情況,則又會回到大家所熟悉的狀況:

1
2
3
4
5
6
7
8
var obj1 = { value: 10 };

function changeValue(obj) {
obj.value = 999 ;
}

changeValue(obj1);
console.log(obj1); // { value: 999 }

不少人將 JavaScript 的變數內容傳遞方式,稱為 Pass by sharing:

  • 碰到基本型別,表現行為是 Pass by value。
  • 碰到物件型別,如果只是對物件內容作操作(例如陣列元素或物件屬性),表現行為是 Pass by reference。
  • 碰到物件型別,如果對物件作重新賦值,表現行為是 Pass by value。

或者也有人視為:JavaScript 的基本型別是 Pass by Value,物件型別是 Pass by sharing。