JavaScript 中的傳值與傳址
JavaScript 的變數再賦予不同型別的時候,會有同的傳遞方式,以 “基本型別” Primitive
和 “物件型別” Object
作為區分
型別 Types
基本型別 Primitive type
當宣告兩個變數並賦予相同的基本型別時,兩個變數的會是 “相等” 的,這似乎很直觀
1 | var a = 10; |
物件型別 Object type
但是當宣告兩個變數並賦予相同屬性與值的物件時,兩個變數卻是 “不相等” 的,因為 javascript 中的物件可以看成是個別的 “實體”,兩個物件雖然可以看成 “等值”,但卻不是同一個 “實體”
1 | var obj1 = { value: 10 } |
傳值與傳址
- 傳值
pass by value
- 傳址
pass by reference
傳值 pass by value
在基本型別的變數中,當我們複製變數時,取得的是原先變數的 “值”,而不是這個變數本身
因為當指定 b
的值等於 a
時,會建立一個新的記憶體來存放相同的值,彼此不會互相干擾,這種情況稱為 “傳值” pass by value
1 | var a = 10; |
所以我們更改原先變數 a
的值時,並不會影響新變數 b
的值
1 | a = 100; |
傳址 pass by reference
當我們建立一個物件型別的變數 a 時,一樣會建立一個位置在記憶體中;但是當我們另建立一個變數 b 並賦予他的值等於 a,這時和基本型別不一樣的是,並不會建立一個新的位置在記憶體中,而是讓變數 b 指向和 a 同一個記憶體中的位置,稱為 “傳址” pass by reference
1 | var a = { value: 10 }; |
所以當我們更改 a
的內容時,b
自然會跟著更改
例外情況
有一些例外的情況透過傳值的方式複製物件,而複製又分為 “深度複製” Deep copy
和 “淺度複製” Shallow copy
,”深度複製” 全部透過 “傳值” 的方式複製,而 “淺度複製” 只有 “第一層傳值”,內層還是 “傳址”
重新賦值
凡事都有例外,當物件型別的變數被重新賦值時,就會在記憶體中產生一個新的位置來存放這個內容
1
2
3
4
5
6var a = {value: 10}
var b = a;
a = {value: 100}
console.log(a); // {value: 100}
console.log(b); // {value: 10}手動賦值 (淺度複製)
手動賦值很直接的把 “物件傳址” 改用 “傳值” 的方式傳遞,傳值的部分就會完全獨立 ; 但要注意的是,”內層物件” 還是 “傳址”
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19var Person = {
name: 'Jerry',
bwh: {
strength: 34,
agility: 25,
intelligence: 96
},
};
Person2 = {
name: Person.name,
bwh: Person.bwh,
}
Person.name = 'Tom';
Person.bwh.strength = 0;
console.log(Person2.name); // 還是 "Jerry"
console.log(Person2.bwh.strength); // 一起改成 0- for…in (淺度複製)
透過 for…in 將原本的物件屬性的值傳到新的物件
- 轉字串
將物件轉型成字串,再將字串轉回物件,兩個物件會 “完全無關”
1
2
3
4
5
6
7
8var str = JSON.stringify(Person);
var P = JSON.parse(Person);
Person.name = 'Tom';
Person.bwh.strength = 0;
console.log(p.name); // 還是 "Jerry"
console.log(p.bwh.strength); // 還是 34- jQuery
jQuery 中可以透過
extend()
來複製物件,可以透過更改第一個參數來調整深度或淺度複製1
2
3
4
5
6
7
8
9
10var p1 = jQuery.extend({}, Person); // 淺度複製
var p2 = jQuery.extend(true, {}, Person); // 深度複製
Person.name = 'Tom';
Person.bwh.strength = 0;
console.log(p1.name); // 還是 "Jerry"
console.log(p1.bwh.strength); // 改成 0
console.log(p2.name); // 還是 "Jerry"
console.log(p2.bwh.strength); // 還是 34- ES6
在原生 JavaScript 中 ES6 也推出
Object.assign()
新的方法,但只適用於 “淺度複製”1
2
3
4
5
6
7const p = Object.assign({}, Person);
Person.name = 'Tom';
Person.bwh.strength = 0;
console.log(p.name); // 還是 "Jerry"
console.log(p.bwh.strength); // 改成 0
資料參考
[筆記] 談談JavaScript中by reference和by value的重要觀念
JavaScript 是「傳值」或「傳址」?