'2'>'10'==true? JS是如何進行隱式型別轉換的?

前端南玖發表於2021-12-27

前言

'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真值表

真值表.png
我是南玖,感謝各位的:「點贊、關注和評論」,我們下期見!

相關文章