這是一張網上調侃js類型轉換的圖,可以先看看上面的計算與你的認知是否符合,再看下面的解答。
js類型6種:undefind,null,String,Number,Boolean,Object
先上幾個js類型轉換的規則,
轉換成Boolean
輸入類型 | 結果 | 特例 |
---|---|---|
String | 非空字符串都是true | 空字符串("")爲false |
Number | 任何非零數字值(包括無窮大)都是true | +0、0、-0、NaN均爲false |
undefined | false | |
null | false | |
Object | true |
轉換爲Number
輸入類型 | 結果 | 特例 |
---|---|---|
String | 數字、浮點、十六進制:對應的值,其它:NaN | 空字符串:0 |
Boolean | True:1,False:0 | |
undefined | NaN | |
null | 0 | |
Object | object.valueOf(),object.toString() |
NaN
NaN是屬於Number類似的一個特殊值,NaN是js中唯一一個和自身不相等的值。
typeof NaN == "number" //true
如何判斷NaN
- window.isNaN():只對數值有效,其它類型會被轉爲NaN再返回true
window.isNaN(Number.NaN); // true
window.isNaN("js") //true
- Number.isNaN():先判斷是不是number類型,再判斷是不是NaN
Number.isNaN(0/0); // true
Number.isNaN(Number.NaN); // true
Number.isNaN('NaN'); // false
- 利用 NaN 是 JavaScript 之中唯一不等於自身的值
function judgeNaN (value) {
return value !== value;
}
judgeNaN(1) //false
judgeNaN(NaN) //true
judgeNaN( "我是字符串" ) //false
- Object.is() :ES6的新方法,類似於===
Object.is(NaN, NaN) //true
Number的最大安全值
如果你遇到過後端傳遞很大的數字到前端但是數字卻總時不一致就會知道這個問題,JS 中所有的數字類型,實際存儲都是通過 8 字節 double 浮點型 表示的。浮點數並不是能夠精確表示範圍內的所有數的, 雖然 double 浮點型的範圍看上去很大:
4.9x10^(-324) ~ 1.79x10^308。
所以圖中的第2到第4個運算都是因爲js的浮點型不精確導致的,浮點數是計算機用來表示小數的一種數據結構,在數學中,採用科學計數法來表示一個極小或者極大的數。業界流行的浮點數標準是IEEE754,該標準規定了4種浮點數類型:單精度、雙精度、延伸單精度、延伸雙精度。前兩種是我們常用的。
精度 | 字節 | 正數取值範圍 | 負數取值範圍 |
---|---|---|---|
單精度類似 | 4 | 1.4e-45至3.4e+38 | -3.4e+38至-1.4e-45 |
雙精度類似 | 8 | 4.9e-324至1.798e+308 | -1.798e+308至-4.9e-324 |
- 符號位
在最高二進制位上分配一位表示浮點數的符號,0表示正數,1表示負數。 - 階碼位(指數)
在符號位右側分配11位用來存儲指數,IEEE754標準規定存儲的是移碼,移碼就是將一個真值在數軸上正向平移一個偏移量後得到的(說人話就是把這個區間的數字都加上一個固定的值,例子:[-10,10]這個範圍,都加上10,把數值映射到[0,20]這個範圍),特點是可以直觀的反應兩個真值的大小。計算機用移碼比較兩個值只要高位對齊逐個比較即可,不用考慮符號的問題。 - 尾數位
最右側的數就是有效數字,IEE754標準規定尾數以原碼錶示,根據科學計數法,有效數字的值1<=x<2。所以整數部分一定是1,省略這個1,可以增加一個小數位。因爲有效位是53位(52位加上省略的1),所以能精確表示的數爲253。
舉個例子
二進制:0100-0000-0011-1000-0000-0000-0000-0000-0000-0000-0000-0000-0000-0000-0000-0000
第一個是符號位,0說明是正數,後面11位1000-0000-011,1027-1023=4(1023就是移碼的偏移量,32位的偏移量爲127)
有效位是1,
所以結果就是+1x2^4=16
圖中的0.1+0.2得出不等於0.3,就在於二進制無法精確表示0.1,所以計算的時候得出的就不是精確的0.3。
其實計算機中只有加法器,沒有減法器的實現,減法只是被減數加上減數的補碼。
類型轉換
對象參與運算:先valueof再tostring
let object = {
toString(){
console.log("toString");
return {};
},
valueOf(){
console.log("valueOf");
return "value"
}
};
alert(object+1);
//valueOf
//彈出value1
let object = {
toString(){
console.log("toString");
return "2";
},
valueOf(){
console.log("valueOf");
return {}
}
};
alert(object+1);
//toString
//valueOf
//彈出21
如果valueof和tostring都返回對象會報錯
Uncaught TypeError: Cannot convert object to primitive value
- 1、對象參與計算或比較先調用valueOf,如果返回不是基礎類型,再調用toString
- 2、有一個數是布爾值,需要轉爲數字運算或者比較
- 3、字符串和其它類型相加,都要把另一個值轉爲字符串。
!可將變量轉換成boolean類型,null、undefined、NaN以及空字符串(’’)取反都爲true,其餘都爲false。
對象轉字符串,先調用toString,如果不算基本類型,就調用valueOf()
現在來看圖中的運算:
1、[]+[]
[]調用valueOf得[],不是基礎類型,調用toString得到"",所以是""+""=""
2、[]+{}
[]轉爲"",{}調用valueOf得{},不是基礎類型,調用toString得[object Object],所以結果是[object Object]
3、{}+[]
這裏其實是有迷惑性的一個地方,如果你在代碼裏面打印這個結果是[object Object],
但是在控制檯出來結果是0,因爲控制檯處會把開始的{}當成代碼塊,直接計算+[],會調
調用valueOf得[],不是基礎類型,再調用[]的toString()得到""再轉爲0
4、true+true+true===3
如上規則,boolean會轉爲數字進行運算,true轉爲數字是1,所以得出結果爲3
5、true-true
如上所述,得出結果爲0
6、true==1
如上
7、true===1
===會進行類型判斷,所以未false
8、(!+[]+[]+![]).length
從左到右進行計算,!+[]就是!(+[]),[]轉數字未0,所以!0=true。true+[]得到true
+""="true",根據規則![]=false,最終"true"+false="truefalse",所以length=9
9、9+"1"
有一個是字符串,所以轉爲字符串計算,91
10、91-"1"
減是number的操作,所以轉爲number,得到90
11、[]==0
[]轉爲數字,調用ValueOf方法得到不是基礎類型,再調用toString得到"",轉爲0
==比較
- 如果一個操作數是字符串,另一個操作數是數值,在比較相等性之前先將字符串轉換爲數值
[] == ![] //true
!的優先級高於==,所以![]得出false,然後根據規則需要轉爲數字,[]==0,規則得出
[]轉爲"",轉爲數字是0,所以是true
{} == !{}
同上得出!{}轉爲0,{}轉爲數字,調用valueOf返回對象,所以繼續調用toString,得到
[object Object],轉number得到NaN。所以false。