JavaScript
做爲一門弱類型語言,其變量能夠任意賦值從而轉換類型,這既是優勢也有缺點。若是開發者明白本身的賦值或操做會引起類型轉換,那麼也就無所謂了,但不少狀況下開發者是不清楚本身的操做可能引起隱式類型轉換,這個就有點危險,並且還很差問題定位時麻煩多多。 好比下面這張來源於Here的圖,相信不少人都會感謝Brendan Eich(布蘭登·艾克),哈哈。javascript
笑過以後咱們開始揭開JavaScript
隱式轉換的一角,先了解一些預備知識,而後對這些題目解疑,最後進行一些拓展html
==
弱相等運算符規範文檔Abstract Equality Comparison的翻譯過來就是下面的:java
- 若是
x
非正常值(好比x
自己會拋出錯誤),則中斷執行- 若是
y
非正常值(同上),則中斷執行- 若是
x
的數據類型和y
的數據類型相同,則返回以嚴格運算符執行判斷的結果,即x===y
的結果- 若是
x
是null
,y
是undefined
,返回true
- 若是
x
是undefined
,y
是null
,返回true
- 若是
x
的數據類型是Number
,y
的數據類型是String
,則將y
轉成Number
,而後返回x==toNumber(y)
的結果- 若是
x
的數據類型是String
,y
的數據類型是Number
,則將x
轉成Number
,而後返回toNumber(x)==y
的結果- 若是
x
的數據類型是Boolean
,則將x
轉成Number
,而後返回toNumber(x)==y
的結果- 若是
y
的數據類型是Boolean
,則將y
轉成Number
,而後返回x==toNumber(y)
的結果- 若是
x
的數據類型是String
、Number
或者Symbol
,y
的數據類型是Object
,則將y
轉成原始類型,而後返回x==toPrimitive(y)
的結果- 若是
x
的數據類型是Object
,y
的數據類型是String
、Number
或者Symbol
,則將x
轉成原始類型,而後返回toPrimitive(x)==y
的結果- 返回
false
ToNumber
規範文檔ToNumber翻譯過來就是:數組
參數類型 結果 完成標誌( 例如 return
、break
、throw
等)若是參數是一個異常中斷,就返回這個參數,不然就返回該參數轉換成 Number
以後的數值Undefined
返回 NaN
Null
返回 +0
Boolean
若是參數是 true
,返回1
;若是參數是false
,返回+0
Number
返回參數(不作轉換) String
見 StringToNumber
Symbol
拋出一個 TypeError
異常Object
採用下述的步驟:
1.利用ToPrimitive(argument,hint Number)
的方式轉成原始類型
2.將上述步驟的原始類型轉成數值,即ToNumber(primValue)
,並返回該數值
StringToNumber
規範文檔ToNumber Applied to the String Type裏面東西有點多,感興趣可自行跳轉查閱,這裏簡單的說一下:瀏覽器
- 若是字符串中只包含數字(包括前面帶加號或負號的狀況),則將其轉換爲十進制數值,即
"1"
會變成1
,"123"
會變成123
,而"011"
會變成11
(注意:前導的零被忽略了);- 若是字符串中包含有效的浮點格式,如
"1.1"
,則將其轉換爲對應的浮點數值(一樣,也會忽略前導零);- 若是字符串中包含有效的十六進制格式,例如
"0xf"
,則將其轉換爲相同大小的十進制整數值;- 若是字符串是空的(不包含任何字符),則將其轉換爲
0
;- 若是字符串中包含除上述格式以外的字符,則將其轉換爲
NaN
。
ToPrimitive
規範文檔ToPrimitive。東西比較多,簡單概述就是。ToPrimitive(input[, preferredType])
函數接受兩個參數,第一個input
爲被轉換的數據,第二個preferredType
爲但願轉換成的類型(默認爲default
,接受的值爲Number
或String
)。若是input
不是Oject
,即基礎類型(Null
, Undefinded
, String
, Boolean
, Number
,Symbol
, 以及未來歸入規範的BigInt
)時,直接返回輸入input
。若是input
爲Object
時,在第二個參數爲空的狀況下,而且input
爲Date
的實例時,此時preferredType
會被設置爲String
,其餘爲空的狀況preferredType
默認爲Number
。安全
若是preferredType
爲Number
,ToPrimitive
執行過程以下:app
- 若是
obj
爲原始值,直接返回;- 不然調用
obj.valueOf()
,若是執行結果是原始值,返回之;- 不然調用
obj.toString()
,若是執行結果是原始值,返回之;- 不然拋異常。
若是preferredType爲String
,將上面的第2步和第3步調換,即:函數
- 若是
obj
爲原始值,直接返回;- 不然調用
obj.toString()
,若是執行結果是原始值,返回之;- 不然調用
obj.valueOf()
,若是執行結果是原始值,返回之;- 不然拋異常。
ToBoolean
規範文檔ToBoolean,概述一下就是:ui
undefined
、null
直接返回false
Symbol
、Object
永遠返回true
- 自己就是
Boolean
型直接返回本來值Number
型的除+0
、-0
和NaN
返回false
之外,其他都返回true
String
型中空字符串(即長度爲0
)返回false
,其餘狀況返回true
。
typeof NaN //"number"
NaN
,即Not a Number
,不是一個數字,它自己是Number
類型的,因此利用typeof
判斷類型是天然返回"number"
。可是NaN
又是一個特殊的Number
類型,它永遠爲假,同時自身也不等於自身。因此有時候在判斷一個變量是否是NaN
時,能夠經過是否等於自身來判斷。spa
function isNaN(value) {
return !value == value;
}
isNaN(NaN); //true
複製代碼
9999999999999999 //10000000000000000
JavaScript
針對數值,只有Number
類型,採用64bits
雙浮點精度,以下圖所示。
1
位:符號位,0
表示正數,1
表示負數2
位到第12
位(共11
位):指數部分,0~2047
13
位到第64
位(共52
位):小數部分(即有效數字)由於有效位數只有53
位,因此JavaScript
能精確表示的最大整數就是Math.pow(2, 53)
,十進制就是9007199254740992
,在JavaScript
也設置了Number.MAX_SAFE_INTEGER
(最大安全整數)和Number.MIN_SAFE_INTEGER
(最小安全整數)。當JavaScript
在存儲超過9007199254740992
的數值時,可能會存在精度丟失的狀況(超過52
位的會被自動去掉)。例如:
如下爲摘錄阮一峯的文章:
- 符號位決定了一個數的正負,指數部分決定了數值的大小,小數部分決定了數值的精度。
- 指數部分一共有
11
個二進制位,所以大小範圍就是0
到2047
。IEEE 754
規定,若是指數部分的值在0
到2047
之間(不含兩個端點),那麼有效數字的第一位默認老是1
,不保存在64
位浮點數之中。也就是說,有效數字這時老是1.xx...xx
的形式,其中xx..xx
的部分保存在64
位浮點數之中,最長可能爲52
位。所以,JavaScript
提供的有效數字最長爲53
個二進制位。***(-1)^符號位 * 1.xx...xx * 2^指數部分***- 上面公式是正常狀況下(指數部分在
0
到2047
之間),一個數在JavaScript
內部實際的表示形式。- 精度最多隻能到
53
個二進制位,這意味着,絕對值小於等於2
的53
次方的整數,即-253
到253
,均可以精確表示。
0.1+0.2 == 0.3 //false
一樣的這也是由於JavaScript
數值精度丟失的緣由致使等於判斷爲false
。JavaScript
自己也考慮到了這一點,因此設置一個可接受的偏差範圍,即Number.EPSILON
,當兩個值的差值小於等於這個可接受偏差範圍時,就能夠認爲這兩個數值時相等的。
function isEqual(num1, num2) {
return Math.abs(num1-num2) <= Number.EPSILON;
}
isEqual(0.1 + 0.2, 0.3); //true
複製代碼
查看規範文檔可知,當Math.min()
方法無參數時返回Infinity
,而Math.max()
無參數時返回-Infinity
。
JavaScript
可以表示的最大數值和最小數值分別爲Number.MAX_VALUE
和Number.MIN_VALUE
中,在大多數瀏覽器中,它們分別是1.7976931348623157e+308
和5e-324
。能夠看到,它們都是正數,是絕對值中的最大和最小數值。
還有比他們更大或者更小的值,當計算結果獲得一個超過數值範圍的值,那麼就會轉成Ifinity
(正無窮)和-Infinity
(負無窮)
JavaScript
也提供了2個屬性保存這兩個無窮值,分別是Number.NEGATIVE_INFINITY
(負無窮)和Number.POSITIVE_INFINITY
(正無窮)
[]+[]
、[]+{}
下面的解釋引自《JavaScript高級程序設計第三版》:
在使用一元操做符(如+、-、++、--)時,
JavaScript
存在隱式類型轉換。
- 在應用於一個包含有效數字字符的字符串時,先將其轉換爲數字值
- 在應用於一個不包含有效數字字符的字符串時,將變量的值設置爲
NaN
- 在應用於布爾值
false
和true
是,分別轉換爲0
和1
- 在應用於對象時,先調用對象的
valueOf()
方法,取的一個可供操做的值。若是結果爲NaN
,就在調用toString()
方式轉成字符串,在執行前面的操做。
上述表述就是ToPrimitive的另外一種翻譯。未被重定義的狀況下valueOf()
方法返回指定對象的基礎類型值;toString()
方法返回一個表示該對象的字符串。
在這裏[]
和{}
都是引用類型,是對象,因此先調用valueOf
方法,後面調用toString
方法。
第一個[]+[]
,Array
對象重寫了Object
的valueOf
方法和toString
方法,valueOf
返回數組自己,toString
返回與沒有參數(默認爲逗號拼接)的 join()
方法返回的字符串相同。因此這裏先返回了數組自己,沒法進行+
操做,在調用toString
方法,變成了空字符串,兩個空字符串相加,因此最後輸出空字符串。
第二個[]+{}
,[]
最終轉成空字符串,+運算實際上變成了字符串拼接方法,因而{}
調用Object
的原生toString
方法,轉成了「[object Object]」
,最終拼接爲了「[object Object]」
。
{} + []
這個和[]+[]
、[]+{}
點類似,若是按照第五項中的解釋去理解,是得不到結果的。爲何呢?這要說到JavaScript
引擎自己解釋代碼的問題了,在JavaScript
解釋{}
時,有兩種狀況,一種是語句塊,一種是對象定義。
當直接在控制檯輸入{}+[]
時,此時解釋器將{}
解釋爲語句塊,即{};+[]
,因此輸出就變成了+[]
的結果,這裏+
符號會強制轉換,執行toNumber()
操做,空數組返回數字0
。
若是在外面加上括號,即({}+[])
,那麼{}
就會被解釋爲對象,最後返回「[object Object]」
。
true+true+true === 3
有運算操做符時,Boolean
類型false
轉爲0
,true
轉爲1
,因此左側結果爲3
,值相等,類型也相等,故返回true
true - true
和上面一樣的理由,轉變成數值運算1-1
,因此返回0
true == 1
相等操做符,兩側會進行toNumber
操做,進行值判斷,不進行類型判斷。因此1 == 1
,返回true
true === 1
全等操做符,既判斷值,也判斷類型,即不作類型轉換,這裏值雖然相同,一個爲Boolean
類型,一個爲Number
類型,因此返回false
(! + [] + [] + ![]).length
運算符具備優先級
這裏能夠看到邏輯非!
操做符優先級比+
高,因此第一個邏輯非!
先執行,至關於!(+[])
,+[]
進行toNumber
操做返回0
,而後邏輯非進行toBoolean
操做返回true
,而後![]
執行,因而[]
先進行toBoolean
的操做,返回true
,而後邏輯非操做變成false
,而後執行從左至右+
運算,即true+[]+false
,變成了字符串拼接,因而返回「truefalse」
,這個字符串的長度也就是9
了
9 + "1"
不管是9+"1"
,仍是"1"+9
,結果都是91
。+
運算符能夠是數字相加運算,也能夠是字符拼接運算。可是規範文檔規定了,若是+
運算符兩側存在字符串時,就調用toString()
方法,進行字符串拼接操做,因此這裏結果都是91
。
9 - "1"
-
運算符和+
運算符不一樣,由於-
運算符就是數字運算減的操做,因此先轉成Number
類型,因此不管是‘9’ -1
仍是9 -‘1’
,結果都是8
[] == 0
根據==弱相等運算符中的規則,空數組最後會執行toNumber
轉成數字0
,因此返回true
相信經過上面的解疑,應該掌握了大部分技巧,如今再來檢驗一波掌握的如何。
/* 猜猜下面的輸出 */
// 第一題
'true' == true
// 第二題
0 == null
複製代碼
先思考一波
.
開始思考
.
思考ing
.
完成思考
就認爲你們都思考一波了
'true' == true
嘿嘿,確定有看錯而後答錯的。
不先說答案,按照流程走一波:
'true'
的數據類型是String
,右側true
的數據類型是Boolean
9
條,因此布爾值true
轉成數值1
,返回'true'==1
的值'true'==1
又知足第7
條,因此字符串true
根據上面講的規則,轉換成NaN
,故返回NaN==1
NaN
都不等於任何值,包括它自己,即NaN==NaN
返回false
'true'==true
返回false
0 == null
在這個相等運算中,左側0
的數據類型是Number
,右側null
的數據類型是Null
(內部Type
運算的結果,與typeof
運算符無關),因此根據上面的規則,前面11
條都不知足,直到第12
步才返回false
。
另附上一份圖,自行按照流程走便可獲得答案。