JS - 隱式型別轉換
技術標籤:JS
一、ECMAScript資料型別
6種原始型別(基本資料型別)
- Boolean
- Null
- Undefined
- Number
- String
- Symbol (ECMAScript 6)
Object (引用型別)
- Object 型別
- Array 型別
- Date 型別
- RegExp
- 型別 Function 型別
原生型別內建的包裝物件,包括:
- String()
- Number()
- Boolean()
- Array()
- Object()
- Function()
- RegExp()
- Date()
- Symbol()
- Error()
在操作原始資料型別的屬性和方法時,JS會自動將原始型別轉換成一個對應的包裝物件。此時該物件就擁有了屬於它本身的一系列屬性和方法。所以當我們定義了一些基本資料型別,想訪問其對應的屬性或方法時,就需要將其轉換為這個值的包裝物件,然而現實中並不需要這麼麻煩,這是因為JS執行的宿主物件(瀏覽器等)將會自動地包裝基本型別值來滿足這樣的訪問。
二、嚴格相等和寬鬆相等
相等操作符(==)比較兩個值是否相等,在比較前將兩個被比較的值轉換為相同型別
全等操作符(===)比較兩個值是否相等,兩個被比較的值在比較前都不進行隱式型別轉換。
抽象相等比較演算法
在比較x == y,其中 x 和 y 是值,結果返回 true 或 false 。比較規則如下:
- 當 x 的型別與 y 的型別相同時:
a. 如果x的型別為Undefined,返回true。 b. 如果x的型別為Null,返回true。 c. 如果x的型別為Number,則如下: i. 如果 x 為 NaN, 返回 false.(NaN == NaN 為false) ii. 如果 y 為 NaN, 返回 false. iii. 如果 x 和 y 的值相同, 返回 true. iv. 如果x為+0,y為−0,返回 true. v. 如果x為−0,y為+0,返回 true. 其他返回 false. d. 如果x是string型別,那麼假如x和y是完全相同的字元序列(相同的長度和相應位置的相同字元),則返回true。否則,返回false。 e. 如果x是布林型別,假如x和y都為true或都為false,則返回true。否則,返回false。 f. 如果x和y引用同一物件,則返回true。否則,返回false。
- x 和 y 型別不相同的情況:
a. 如果x為null,y為undefined,則返回true。 b. 如果x為undefined,y為null,則返回true。(其餘的任何型別與undefined和null相比都為false) c. 如果x是數字型別,y是字串型別,則返回x == ToNumber(y)的比較結果. d. 如果x是字串型別,y是數字型別,則返回ToNumber(x) == y的比較結果. e. 如果x是布林型別,則返回ToNumber(x)==y的比較結果. f. 如果y是布林型別,則返回x == ToNumber(y)的比較結果. g. 如果x是字串或數字型別,y 是物件型別,返回比較結果x== ToPrimitive(y)。 h. 如果x是物件型別,y是字串或數字型別,返回比較結果ToPrimitive(x)==y。 i. 其他則返回false
上述規則看起來很複雜,但總結起來其實只有以下幾點:
- 兩個引用型別比較,只需判斷它們是不是引用了同一個物件,是返回true,否則為false。
- undefined 和 null 兩者互相比較或者與自身比較,結果是true。它倆與其他任何值比較的都為false。
- NaN與任何值比較包括它自身結果都是false。
- 引用型別和基本資料型別(String、Number)進行比較,引用型別會轉換成與之所比較的基本資料的型別,再進行比較。
- 引用型別和基本資料型別(Boolean)進行比較,Boolean型別先轉換為數字型別,引用型別再轉換成與之所比較的基本資料的型別進行比較。
- String,Boolean,Number中的任意兩個進行比較,最後都會轉為Number型別再進行比較。
三、具體的型別轉換過程
1. ToNumber
抽象操作ToNumber將非數字值轉換為數字型別。
- 布林型別 true 轉換為1,false 轉換為0。
- 字串型別,""(空字串)轉為0,‘123’ 轉為 123,‘123px’
則被轉為NaN(按照上述總結,NaN與任何型別比較都為false,此時就不需要再考慮其他情況)。 - undefined 會轉換為NaN,null 會轉換為 0。(按上述總結第3條,null 和 undefined 跟其他任何非該兩個型別中其一的比較都為false。結果容易得出,就不需要再考慮其他型別轉換了)。
- 引用型別,它們都需要先進行ToPrimitive轉換為基本資料型別後再轉換為數字型別。
2. ToPrimitive
抽象操作ToPrimitive將引用型別轉為基本資料型別。
JS引擎內部轉換為原始值ToPrimitive(obj,preferredType)函式接受兩個引數,第一個obj為被轉換的物件,第二個preferredType為希望轉換成的型別(預設為空,接受的值為Number或String)
在執行ToPrimitive(obj,preferredType)時如果第二個引數為空並且obj為Date的例項時,此時preferredType會被設定為String,其他情況下preferredType都會被設定為Number如果preferredType為Number。
轉換為Number數字型別的過程為首先呼叫obj.valueOf(),如果執行結果是基本數值型別就返回該值,否則就呼叫obj.toString(),返回該字串型別值。
轉換為String數字型別的過程為首先呼叫obj.toString(),如果執行結果是基本數值型別就返回該值,否則就呼叫obj.valueOf(),返回該字串型別值。
上述規則總結起來基本就是:
- ToPrimitive轉換為原始資料型別時,如果是基本資料型別就直接返回該型別值。
- 如果不是,則優先呼叫valueOf方法(如果有),看其返回結果是否是基本型別,如果是,則返回。否則,再呼叫toString方法,轉換為字串型別後返回。
- 當型別為Date日期型別時,JS希望其優先被轉換成字串型別,則先呼叫toString方法,轉換為字串。
- 其他情況報錯。因為從ES5開始,使用Object.create(null)建立的物件由於沒有原型鏈,自然就也沒有valueOf()和toString()方法,這種情況出現的很少,基本可以忽略。
物件、陣列、日期型別最後基本都被轉換為字串型別。所以我們姑且可以將ToPrimitive看做將引用型別最後都轉換為字串型別。
3.ToBoolean
抽象操作ToBoolean將轉為布林型別。
對於布林型別轉換來說,假值(undefined、null、false、+0、-0、NaN、"")會被轉換為false,除假值外的所有值都會被轉換成true。
4.ToString
抽象操作ToString將轉為字串型別。
對於字串型別也比較好理解,大部分的基本資料型別轉換為字元型別時,基本就是在它基礎上加了引號這麼直觀,比較特殊的就是+0、0、-0它們幾個都會被轉成相同的’0’,還有那些極小和極大的數字將會轉換為指數形式的字串來表現,而引用型別轉換成字串的時候也跟它們ToPrimitive時候的規則一致(見上文ToPrimitive)。
四、 常見的隱式強制型別轉換
- + - * / % 這幾個運算子都可以將結果轉為數字型別
- + 運算子常用來進行數字型別強制轉換,因為它不會像**-**運算子一樣影響結果
- + 運算子可以將日期型別new Date()轉換成數字型別的時間戳
- ! 邏輯運算子可以將任何型別轉為布林型別,因為!運算子轉換後的結果是被反轉的,我們也可以使用兩個!來得出正確的結果
- ** if()、 while() **括號內可以對所有資料型別預設執行強制布林型別轉換
- 三元運算子的條件也會執行強制布林型別轉換