这是一张网上调侃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。