前方提醒: 篇幅較長,點個贊或者收藏一下,能夠在下一次閱讀時方便查找javascript
提到js的隱式轉換,不少人第一反應都是:坑。java
的確,對於不熟悉的人來講,js隱式轉換
存在着不少的讓人沒法預測的地方,相信不少人都深受其害,因此,你們在開發過程當中,可能會使用===
來儘可能避免隱式轉換。可是,爲了更加深刻的理解javascript
,本着對知識渴望的精神,咱們來經過大量的例子分析分析js隱式轉換
,熟悉js隱式轉換
的規則,讓其在你的眼裏變成「顯式」。面試
先來看看一個經典的面試題數組
定義一個變量
a
,使得下面的表達式結果爲true
markdown
a == 1 && a == 2 && a == 3 複製代碼
還有這種操做?先試試看吧,定義a = true
?oop
var a = true a == 1 && a == 2 && a == 3 // false 複製代碼
可是並無達到預期,好像觸碰到知識盲區了。。。不要緊,先放下吧,來看看幾個更坑的this
[] == ![] // true [] == 0 // true [2] == 2 // true ['0'] == false // true '0' == false // true [] == false // true [null] == 0 // true null == 0 // false [null] == false // true null == false // false [undefined] == false // true undefined == false // false 複製代碼
一臉懵逼? 沒關係!接下來帶你完徹底全的認識javascript的隱式轉換
。spa
咱們須要先了解一下js數據類型之間轉換的基本規則,好比數字、字符串、布爾型、數組、對象之間的相互轉換。prototype
這裏所說的
ToString
可不是對象的toString方法
,而是指其餘類型的值轉換爲字符串類型的操做。code
這裏咱們討論null
、undefined
、布爾型
、數字
、數組
、普通對象
轉換爲字符串的規則。
"null"
"undefined"
true
和false
分別被轉爲"true"
和"false"
10
轉爲"10"
, 1e21
轉爲"1e+21"
Array.prototype.join()
方法,如[1, 2, 3]
轉爲"1,2,3"
,空數組[]
轉爲空字符串,數組中的null
或undefined
,會被當作空字符串處理Object.prototype.toString()
,返回"[object Object]"
String(null) // 'null' String(undefined) // 'undefined' String(true) // 'true' String(10) // '10' String(1e21) // '1e+21' String([1,2,3]) // '1,2,3' String([]) // '' String([null]) // '' String([1, undefined, 3]) // '1,,3' String({}) // '[object Objecr]' 複製代碼
對象的toString
方法,知足ToString
操做的規則。
注意:上面所說的規則是在默認的狀況下,若是修改默認的
toString()
方法,會致使不一樣的結果
ToNumber
指其餘類型轉換爲數字類型的操做。
0
NaN
0
, 不然一概按轉換失敗處理,轉爲NaN
true
和false
被轉爲1
和0
ToPrimitive
,而後在根據轉換後的原始類型按照上面的規則處理,關於ToPrimitive
,會在下文中講到Number(null) // 0 Number(undefined) // NaN Number('10') // 10 Number('10a') // NaN Number('') // 0 Number(true) // 1 Number(false) // 0 Number([]) // 0 Number(['1']) // 1 Number({}) // NaN 複製代碼
ToBoolean
指其餘類型轉換爲布爾類型的操做。
js中的假值只有false
、null
、undefined
、空字符
、0
和NaN
,其它值轉爲布爾型都爲true
。
Boolean(null) // false Boolean(undefined) // false Boolean('') // flase Boolean(NaN) // flase Boolean(0) // flase Boolean([]) // true Boolean({}) // true Boolean(Infinity) // true 複製代碼
ToPrimitive
指對象類型類型(如:對象、數組)轉換爲原始類型的操做。
valueOf
方法,若是valueOf
方法返回原始類型的值,則ToPrimitive
的結果就是這個值valueOf
不存在或者valueOf
方法返回的不是原始類型的值,就會嘗試調用對象的toString
方法,也就是會遵循對象的ToString
規則,而後使用toString
的返回值做爲ToPrimitive
的結果。注意:對於不一樣類型的對象來講,
ToPrimitive
的規則有所不一樣,好比Date對象
會先調用toString
,具體能夠參考ECMA標準
若是valueOf
和toString
都沒有返回原始類型的值,則會拋出異常。
Number([]) // 0 Number(['10']) //10 const obj1 = { valueOf () { return 100 }, toString () { return 101 } } Number(obj1) // 100 const obj2 = { toString () { return 102 } } Number(obj2) // 102 const obj3 = { toString () { return {} } } Number(obj3) // TypeError 複製代碼
前面說過,對象類型在ToNumber
時會先ToPrimitive
,再根據轉換後的原始類型ToNumber
Number([])
, 空數組會先調用valueOf
,但返回的是數組自己,不是原始類型,因此會繼續調用toString
,獲得空字符串
,至關於Number('')
,因此轉換後的結果爲"0"
Number(['10'])
至關於Number('10')
,獲得結果10
obj1
的valueOf
方法返回原始類型100
,因此ToPrimitive
的結果爲100
obj2
沒有valueOf
,但存在toString
,而且返回一個原始類型,因此Number(obj2)
結果爲102
obj3
的toString
方法返回的不是一個原始類型,沒法ToPrimitive
,因此會拋出錯誤看到這裏,覺得本身徹底掌握了?別忘了,那道面試題和那一堆讓人懵逼的判斷還沒解決呢,本着對知識渴望的精神,繼續往下看吧。
寬鬆相等(==)
和嚴格相等(===)
的區別在於寬鬆相等會在比較中進行隱式轉換
。如今咱們來看看不一樣狀況下的轉換規則。
布爾類型
參與比較,該布爾類型
的值首先會被轉換爲數字類型
布爾類型
的ToNumber
規則,true
轉爲1
,false
轉爲0
false == 0 // true true == 1 // true true == 2 // false 複製代碼
以前有的人可能以爲數字
2
是一個真值,因此true == 2
應該爲真,如今明白了,布爾類型true
參與相等比較會先轉爲數字1
,至關於1 == 2
,結果固然是false
咱們平時在使用if
判斷時,通常都是這樣寫
const x = 10 if (x) { console.log(x) } 複製代碼
這裏if(x)
的x
會在這裏被轉換爲布爾類型,因此代碼能夠正常執行。可是若是寫成這樣:
const x = 10 if (x == true) { console.log(x) } 複製代碼
代碼不會按照預期執行,由於x == true
至關於10 == 1
數字類型
和字符串類型
作相等比較時,字符串類型
會被轉換爲數字類型
ToNumber
規則,若是是純數字形式的字符串,則轉爲對應的數字,空字符轉爲0
, 不然一概按轉換失敗處理,轉爲NaN
0 == '' // true 1 == '1' // true 1e21 == '1e21' // true Infinity == 'Infinity' // true true == '1' // true false == '0' // true false == '' // true 複製代碼
上面比較的結果和你預期的一致嗎? 根據規則,字符串轉爲數字,布爾型也轉爲數字,因此結果就顯而易見了。
這裏就不討論
NaN
了,由於NaN
和任何值都不相等,包括它本身。
對象類型
和原始類型
作相等比較時,對象類型
會依照ToPrimitive
規則轉換爲原始類型
'[object Object]' == {} // true '1,2,3' == [1, 2, 3] // true 複製代碼
看一下文章開始時給出的例子
[2] == 2 // true 複製代碼
數組[2]
是對象類型,因此會進行ToPrimitive
操做,也就是先調用valueOf
再調用toString
,根據數組ToString
操做規則,會獲得結果"2"
, 而字符串"2"
再和數字2
比較時,會先轉爲數字類型,因此最後獲得的結果爲true
。
[null] == 0 // true [undefined] == 0 // true [] == 0 // true 複製代碼
根據上文中提到的數組ToString
操做規則,數組元素爲null
或undefined
時,該元素被當作空字符串
處理,而空數組[]
也被轉爲空字符串
,因此上述代碼至關於
'' == 0 // true '' == 0 // true '' == 0 // true 複製代碼
空字符串
會轉換爲數字0
,因此結果爲true
。
試試valueOf方法
const a = { valueOf () { return 10 } toString () { return 20 } } a == 10 // true 複製代碼
對象的
ToPrimitive
操做會先調用valueOf
方法,而且a
的valueOf
方法返回一個原始類型的值,因此ToPrimitive
的操做結果就是valueOf
方法的返回值10
。
講到這裏,你是否是想到了最開始的面試題? 對象每次和原始類型作==
比較時,都會進行一次ToPrimitive
操做,那咱們是否是能夠定義一個包含valueOf
方法的對象,而後經過某個值的累加來實現?
試一試
const a = { // 定義一個屬性來作累加 i: 1, valueOf () { return this.i++ } } a == 1 && a == 2 && a == 3 // true 複製代碼
結果正如你所想的,是正確的。固然,當沒有定義valueOf
方法時,用toString
方法也是能夠的。
const a = { // 定義一個屬性來作累加 i: 1, toString () { return this.i++ } } a == 1 && a == 2 && a == 3 // true 複製代碼
null
和undefined
寬鬆相等的結果爲true,這一點你們都知道其次,null
和undefined
都是假值,那麼
null == false // false undefined == false // false 複製代碼
竟然跟我想的不同?爲何呢? 首先,false
轉爲0
,而後呢? 沒有而後了,ECMAScript規範
中規定null
和undefined
之間互相寬鬆相等(==)
,而且也與其自身相等,但和其餘全部的值都不寬鬆相等(==)
。
如今再看前面的這一段代碼就明瞭了許多
[] == ![] // true [] == 0 // true [2] == 2 // true ['0'] == false // true '0' == false // true [] == false // true [null] == 0 // true null == 0 // false [null] == false // true null == false // false [undefined] == false // true undefined == false // false 複製代碼
最後想告訴你們,不要一味的排斥javascript的隱式轉換,應該學會如何去利用它,你的代碼中可能存在着不少的隱式轉換,只是你忽略了它,要作到知其然,並知其因此然,這樣纔能有助於咱們深刻的理解javascript。
(看了這麼久了,辛苦了,不過我也寫了好久啊,點個贊再走吧)