搞懂Javascript傳值、傳址觀念

前言

剛開始接觸傳值(pass by value)和傳址(pass by reference)時,常常會搞混,如果觀念似懂非懂的話在開發專案的時候可能會不自覺踩到雷,這一篇就把這個觀念好好記錄下來。

傳值(pass by value)

string、number、boolean、null、undefined都是基本型別,所以會根據pass by value的方式來運作,另外function 也是以此方式運作
舉例:

1
2
3
4
5
6
7
8
let a = 666;
let b = a;
console.log(a); //666
console.log(b); //666

a = 777;
console.log(a); //777
console.log(b); //666

第2行:把a值指派給b變數,所以b值為666
第6行:把a值設為777
第8行:b值還是本來的值,也就是666

傳址(pass by reference)

物件裡面的屬性會根據pass by reference來運作

1
2
3
4
5
6
7
8
9
10
11
let son = {
age: 18
};
let father = son;
console.log(son.age); // 18
console.log(father.age); // 18
son.age = 40;
console.log(son.age); // 40
console.log(father.age); // 40

console.log(son === father); // true

第4行:把son值指派給father變數,所以father值為{age: 18}
第7行:將son的age屬性改為40
第9行:father的age屬性值也會變成40
第11行:son 完全等於father,因為是共用一份記憶體

陣列也會依據pass by reference來運作

1
2
3
4
5
6
let arr1 = [1, 2, 3];
let arr2 = arr1;

arr1.push(4);

console.log(arr2);

第4行:針對arr1再多push4的值
第6行:也改動到了arr2的值

所以常常在實戰中,習慣複製一份出來,再做相關操作
可以在第2行改為let arr2 = arr1.slice(0);或是用ES6擴展符let arr2 = [ ...arr1 ]方式來賦值 (前提裡面的元素不要是物件或陣列)
如此一來arr2就不會受到任何影響了

其他種情況

如果我們現在是把整個物件取代掉,會發生什麼事呢?

1
2
3
4
5
6
7
8
9
let son = {
age: 18
};
let father = son;
console.log(son.age); // 18
console.log(father.age); // 18
son = { age: 5 }
console.log(son.age); // 5
console.log(father.age); // 18

第7行:son賦予整個物件{ age: 5 }
第9行:father的age仍然是18,因為son是重新賦值,會產生一個新的實體參考,所以不會影響到father的age值

這種情況有人稱它為pass by sharing,也有人會認為說這個其實就是pass by value,我們就不在名詞上面爭議,理解一下概念即可。
陣列也會有這樣的情況,如果是賦予一個全新陣列,也會跟本來的獨立,這邊就不額外舉例了。

小結

  1. 了解pass by value和pass by reference的差異
  2. 僅修改物件或陣列裡面的值以及賦予整個物件或陣列,結果會不同(因記憶體指向的差異)