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
。
另附上一份图,自行按照流程走便可获得答案。