‘2’>’10’==true? JS是如何進行隱式類型轉換的?
- 2021 年 12 月 27 日
- 筆記
- javascript, 前端面試題
前言
‘2’>’10’返回的true,可能很多人都不是很能理解吧? 在js中,當運算符在運算時,如果兩邊數據不統一,CPU就無法計算,這時我們編譯器會自動將運算符兩邊的數據做一個數據類型轉換,轉成一樣的數據類型再計算。 這種無需程式設計師手動轉換,而由編譯器自動轉換的方式就稱為隱式轉換。
如果這篇文章有幫助到你,❤️關注+點贊❤️鼓勵一下作者,文章公眾號首發~
想要知道’2′>’10’為什麼是true,我們得先來了解一下JavaScript的隱式類型轉換規則。
隱式類型轉換規則
1. == 操作符的強制類型轉換規則?
- 字元串和數字之間的相等比較,將字元串轉換為數字之後再進行比較。
- 其他類型和布爾類型之間的相等比較,先將布爾值轉換為數字後,再應用其他規則進行比較。
- null 和 undefined 之間的相等比較,結果為真。其他值和它們進行比較都返回假值。
- 對象和非對象之間的相等比較,對象先調用 ToPrimitive 抽象操作後,再進行比較。
- 如果一個操作值為 NaN ,則相等比較返回 false( NaN 本身也不等於 NaN )。
- 如果兩個操作值都是對象,則比較它們是不是指向同一個對象。如果兩個操作數都指向同一個對象,則相等操作符返回true,否則,返回 false。
2.遞增遞減操作符(前置與後置)、一元正負操作符
這些操作符適用於任何數據類型的值,針對不同類型的值,該操作符遵循以下規則(經過對比發現,其規則與Number()規則基本相同):
- 如果是包含有效數字字元的字元串,先將其轉換為數字值(轉換規則同Number()),在執行加減1的操作,字元串變數變為數值變數。
- 如果是不包含有效數字字元的字元串,將變數的值設置為NaN,字元串變數變成數值變數。
- 如果是布爾值false,先將其轉換為0再執行加減1的操作,布爾值變數編程數值變數。
- 如果是布爾值true,先將其轉換為1再執行加減1的操作,布爾值變數變成數值變數。
- 如果是浮點數值,執行加減1的操作。
- 如果是對象,先調用對象的valueOf()方法,然後對該返回值應用前面的規則。如果結果是NaN,則調用toString()方法後再應用前面的規則。對象變數變成數值變數。
3.加法運算操作符
加號運算操作符在Javascript也用於字元串連接符,所以加號操作符的規則分兩種情況:
如果兩個操作值都是數值,其規則為:
- 如果一個操作數為NaN,則結果為NaN
- 如果是Infinity+Infinity,結果是Infinity
- 如果是-Infinity+(-Infinity),結果是-Infinity
- 如果是Infinity+(-Infinity),結果是NaN
- 如果是+0+(+0),結果為+0
- 如果是(-0)+(-0),結果為-0
- 如果是(+0)+(-0),結果為+0
如果有一個操作值為字元串,則:
- 如果兩個操作值都是字元串,則將它們拼接起來
- 如果只有一個操作值為字元串,則將另外操作值轉換為字元串,然後拼接起來
- 如果一個操作數是對象、數值或者布爾值,則調用toString()方法取得字元串值,然後再應用前面的字元串規則。對於undefined和null,分別調用String()顯式轉換為字元串。
- 可以看出,加法運算中,如果有一個操作值為字元串類型,則將另一個操作值轉換為字元串,最後連接起來。
4. 乘除、減號運算符、取模運算符
這些操作符針對的是運算,所以他們具有共同性:如果操作值之一不是數值,則被隱式調用Number()函數進行轉換。
5.邏輯操作符(!、&&、||)
邏輯非(!)操作符首先通過Boolean()函數將它的操作值轉換為布爾值,然後求反。
邏輯與(&&)操作符,如果一個操作值不是布爾值時,遵循以下規則進行轉換:
- 如果第一個操作數經Boolean()轉換後為true,則返回第二個操作值,否則返回第一個值(不是Boolean()轉換後的值)
- 如果有一個操作值為null,返回null
- 如果有一個操作值為NaN,返回NaN
- 如果有一個操作值為undefined,返回undefined
邏輯或(||)操作符,如果一個操作值不是布爾值,遵循以下規則: - 如果第一個操作值經Boolean()轉換後為false,則返回第二個操作值,否則返回第一個操作值(不是Boolean()轉換後的值)
- 對於undefined、null和NaN的處理規則與邏輯與(&&)相同
6.關係操作符(<, >, <=, >=)
與上述操作符一樣,關係操作符的操作值也可以是任意類型的,所以使用非數值類型參與比較時也需要系統進行隱式類型轉換:
- 如果兩個操作值都是數值,則進行數值比較
- 如果兩個操作值都是字元串,則比較字元串對應的字元編碼值
- 如果只有一個操作值是數值,則將另一個操作值轉換為數值,進行數值比較
- 如果一個操作數是對象,則調用valueOf()方法(如果對象沒有valueOf()方法則調用toString()方法),得到的結果按照前面的規則執行比較
- 如果一個操作值是布爾值,則將其轉換為數值,再進行比較
註:NaN是非常特殊的值,它不和任何類型的值相等,包括它自己,同時它與任何類型的值比較大小時都返回false。
7. 其他值到字元串的轉換規則?
- Null 和 Undefined 類型 ,null 轉換為 「null」,undefined 轉換為 「undefined」,
- Boolean 類型,true 轉換為 「true」,false 轉換為 「false」。
- Number 類型的值直接轉換,不過那些極小和極大的數字會使用指數形式。
- Symbol 類型的值直接轉換,但是只允許顯式強制類型轉換,使用隱式強制類型轉換會產生錯誤。
- 對普通對象來說,除非自行定義 toString() 方法,否則會調用 toString()(Object.prototype.toString())來返回內部屬性 [[Class]] 的值,如」[object Object]」。如果對象有自己的 toString() 方法,字元串化時就會調用該方法並使用其返回值。
8. 其他值到數字值的轉換規則?
- Undefined 類型的值轉換為 NaN。
- Null 類型的值轉換為 0。
- Boolean 類型的值,true 轉換為 1,false 轉換為 0。
- String 類型的值轉換如同使用 Number() 函數進行轉換,如果包含非數字值則轉換為 NaN,空字元串為 0。
- Symbol 類型的值不能轉換為數字,會報錯。
- 對象(包括數組)會首先被轉換為相應的基本類型值,如果返回的是非數字的基本類型值,則再遵循以上規則將其強制轉換為數字。
為了將值轉換為相應的基本類型值,抽象操作 ToPrimitive 會首先(通過內部操作 DefaultValue)檢查該值是否有valueOf()方法。如果有並且返回基本類型值,就使用該值進行強制類型轉換。如果沒有就使用 toString() 的返回值(如果存在)來進行強制類型轉換。
如果 valueOf() 和 toString() 均不返回基本類型值,會產生 TypeError 錯誤。
9. 其他值到布爾類型的值的轉換規則?
以下這些是假值: undefined、 null、 false、 +0、-0 和 NaN 、「」
假值的布爾強制類型轉換結果為 false。從邏輯上說,假值列表以外的都應該是真值。
總結
- null、undefined 是相等的,且等於自身
- false 、 0、 ” 、 [] 是相等的
- NaN、{} 和什麼都不相等,自己跟自己都不相等
NaN == NaN //false
NaN == undefined //false
NaN == false //false
NaN == null //false
NaN==[] //false
NaN=='' //false
NaN=={} //false
false == false //true
false == undefined //false
false == null //false
false == [] //true
false == {} //false
false == '' //true
undefined == undefined //true
undefined == null //true
undefined == false //false
undefined == [] //false
undefined == {} //false
undefined == '' //false
null == null //true
null == NaN //false
null == [] //false
null == {} //false
null == undefined //true
0==false //true
0 == [] //true
0 == {} //false
0 == null //false
0 == undefined //false
0 == '' //true
0 == NaN //false
false == [] //true
false == {} //false
false == null //false
false == undefined //false
false == '' //true
false == NaN //false
[]=={} //false
Boolean([]) //true
Boolean({}) //true
Boolean(null) //false
Boolean(NaN) //false
Boolean(undefined) //false
Boolean('') //false
Boolean(0) //false
Number(undefined) //NaN
Number({}) //NaN
Number(NaN) //NaN
Number('') //0
Number([]) //0
Number(false) //0
Number(null) //0
‘2’>’10’為什麼是true?
上面我們列了這麼多轉換的規則,那麼這道題我們就可以在上面這些規則中找到答案了,首先找到關係操作符,該規則中的第二點是兩個操作符都是字元串的話,則比較字元串對應的字元編碼值,按我們常規思維是不是會覺得他會轉為數字再比較,然後2>10,返回false,然而並不是的,是不是覺得JavaScript很坑🦢。JavaScript
中用charCodeAt()
來獲取字元編碼
console.log('2'>'10') // true
//首先將操作符兩邊都轉為字元編碼再進行比較
'2'.charCodeAt() //50
'10'.charCodeAt() // 49
// 所以 '2'>'10' 會返回true
我們再來看幾道有趣(很坑)的題
1.複雜數據類型轉string
先調用valueOf()
獲取原始值,如果原始值不是string類型,則調用toString()
轉成string
console.log([1,2] == '1,2') //true
[1,2].toString() // '1,2'
var a = {}
console.log(a.toString()) // "[object Object]"
console.log(a == "[object Object]") //true
解析:
先將左邊數據類型轉成string,然後兩邊都是string,再比較字元編碼值
2.邏輯非隱式轉換與關係運算符隱式轉換
console.log([] == 0) // true
console.log(![] == 0) // true
console.log([] == ![]) // true 是不是覺得很離譜???
console.log([] == []) //false
console.log({} == !{}) //false
console.log({} == {}) // false
看到這些結果是不是很吃驚,是的我也覺得很吃驚,簡直深坑。玩笑歸玩笑,我們還是一起來看看到底是為什麼吧!!
解析:
console.log([] == 0) // true
/*關係運算符(3)如果只有一個操作值是數值,則將另一個操作值轉換為數值,進行數值比較
原理:
1.[].valueOf().toString() 得到字元串""
2.將""轉為數字Number("") 得到數字0
所以 [] == 0 成立
*/
console.log(![] == 0) // true
/*
原理:與上面類似,只是邏輯運算符優先順序高於關係運算符,所以先執行![] 得到false
false == 0 成立
*/
console.log([] == ![]) // true
/*
上面我們知道了 []==0 ![] == 0
所以 [] == ![]
*/
console.log([] == []) //false
/*
引用數據類型數據存在堆中,棧中存儲的是它們的地址,兩個[]地址肯定不一樣,所以是false
*/
console.log({} == !{}) //false
/*
原理:
1. {}.valueOf().toString() 得到"[object,Object]"
2. !{} == false
3. Number("[object,Object]") // NaN
Number(false) //0
4. NaN != 0
*/
console.log({} == {}) // false
/*
引用數據類型數據存在堆中,棧中存儲的是它們的地址,所以肯定不一樣
*/
JavaScript真值表
我是南玖
,感謝各位的:「點贊、關注和評論」,我們下期見!