JavaScript淺嚐紀錄No.3-JS變數值為基本型別與影響
ECMAScript標準定義的七種資料型別
基本型別 (primitive type)
- String-字串”hello”。
- Number-數字 (在 -(253 -1) and 253 -1 之間的數字), +Infinity、-Infinity、NaN也是數字型別。
- Boolean-布林值true / false。
- Undefined-已宣告,但未賦值的變數。
- Null-空值。
- Symbol()-ES6新創的,還不熟。
物件(object)
基本資料型態以外的都是物件型別。
- object-ket-value組合{name:’Celeste’,age:18,…}。
- Array-陣列[1,2,3,…],使用typeof foo() {}時,會顯示’object’。
- Function-使用
typeof foo()
時,會顯示”function”。
參考資料:
Fooish - JavaScript ES6 Symbol 資料型態
MDN-JavaScript 的資料型別與資料結構。
變數儲存By value?By reference?
先簡單解釋一下什麼是by value跟by reference的差異:
By value
傳遞的值的方法是透過複製「值」,在傳遞基本型別值時都是透過「by value」。簡單的測試一下:
1
2
3
4
5
6
7
8
9
10let a = 1
let b = a
console.log(a) //1
console.log(b) //1
console.log(a === b) //true
a = 2 // a重新賦值
console.log(a) //2
console.log(b) //1
console.log(a === b) //false變數
a
重新賦值後並不會更改到變數b
的值,這是因為變數a
值跟變數b
值儲存位置不同,變數b
僅僅只是複製了變數a
的值(By value)。By reference
物件通常是透過「By reference」傳遞儲存位址。先來看看下面例子:
1
2
3
4
5
6
7
8
9let obj = {
a: 1
}
let main = obj
main.a = 2
main.b = 3
console.log(obj) //{a:2,b:3}
console.log(main) //{a:2,b:3}
console.log(obj === main) // true變數
main
修改了值卻影響了變數obj
,這都是因為變數obj
跟變數main
都是指向同一個儲存位址(有點像是我們的地址),地址不會變,但是地址裡面的東西可以被更改(請注意是更改,像是這個地址裡的戶口會有變更,但不會影響這個地址的位置)。將變數
obj
代入function
的參數,也是透過「By reference」,將變數obj
的儲存位址複製給這個參數,所以調用function
的話,也會變更原本的物件值。1
2
3
4
5
6
7
8
9let obj = {
a: 1
}
function change(main) {
main.a = 2
main.b = 3
}
change(obj)
console.log(obj) //{a: 2, b: 3}需要特別注意,當物件被重新賦值的話,這個物件會被儲存到新的位址(有點像是搬家了,但原本的地址還是存在)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18let address = {
dad: 'Paul'
}
let main = address
main.dad = 'Mark'
main.mom = 'Mary'
console.log(address) //{dad: "Mark", mom: "Mary"}
console.log(main) //{dad: "Mark", mom: "Mary"}
console.log(address === main) //true
//address重新賦值
address= {
dad: 'Mark',
mom: 'Mary'
}
console.log(address) //{dad: "Mark", mom: "Mary"}
console.log(main) //{dad: "Mark", mom: "Mary"}
console.log(address === main) //false注意到最後一行
console.log(address=== main)
顯示的是false
,但上面的console.log(address=== main)
卻是true
,這是因為上面的變數main
只是將儲存位址裡的值做了修改(有點像是地址裡的戶口中人的名字作改變並且增加人口),但是將變數address
重新賦值就代表有了新的儲存位址(類似搬家了,並且新家裡也是有同樣名字的dad跟mom),但變數main
還是指向原本的的位址,所以變數address
的儲存位址不等於變數main
的。
在重新賦值的情況下內容當然是可以改變,只是為了說明2個變數雖然內容相同但是物件是透過「By reference」儲存的,即使內容相同但是儲存位置不同,那麼2者比較就會是false
。如果不想要重新賦值原本設定好的物件,請一定要用
const
宣告喔!
參考資料:
Programming with Mosh-Value Types and Reference Types in JavaScript
PJCHENder-[筆記] 談談 JavaScript 中 by reference 和 by value 的重要觀念
型別強制轉換(Type coercion)
MDN對JavaScript的型別解釋:JavaScript 是弱型別,也能說是動態的程式語言。這代表你不必特別宣告變數的型別。程式在運作時,型別會自動轉換。這也代表你可以以不同的型別使用同一個變數。
來分開解釋上面的意思:
- 因為JavaScript 的型別針對的是變數值,而非是變數,所以不必特別宣告變數的型別。
- 因為JavaScript 是一個對型別轉換非常寬鬆的語言,JavaScript 引擎很少會直接顯示錯誤,它會盡可能接受你寫給它的程序,甚至自己猜測你的意思(要數字、字串或布林值等等),幫你做型別自動轉換(像是把數字變字串、把false變成數字0)。
Falsy家族
falsy 值 (虚值) 是在 Boolean 上下文中认定为 false 的值。在型別轉換時,被視為0。
- false。
- 0。
- “”、’’、`` -空字串。
- null。
- undefined。
- NaN。
Falsy家族跟型別轉換有甚麼關係呢?看看下面的程式碼:
1 | //JS的型別是針對變數值,所以是值的型別再更換 |
使用「===」或「!==」來避免型別轉換發生的問題
兩種等號
==(相等)
型別會自動轉換,所以相互比較的值,只要內容相同,就會呈現true。
1 | //== |
=== (嚴格相等)
型別無法自動轉換,也因此相互比較的值,除了內容相同外,型別也必須一樣,才會呈現true。
1 | //=== |
兩種不等號
!= (不相等)
相互比較的值,內容相同,但型別不同,就會呈現true。
1 | //!= |
!== (嚴格不相等)
相互比較的值,內容相同,但型別不同,就會呈現true。
1 |
|
JavaScript 的 == 和 != 會「先將兩個值轉換成相同的資料型別」,請記得永遠使用 === 和 !==。參考MDN-JavaScript 的資料型別與資料結構、Falsy。