写在最前
本次分享一下通过ES5规范来总结如何准确的计算“==”的执行结果。由于规范是枯燥无味的,所以作者试图总结了其中的规律,并希望可以让读完这篇文章的读者不再去“死记硬背”==的结果是什么,而是通过几次简单的计算便心有成竹的得出结论!
欢迎关注我的博客,不定期更新中——
JavaScript小众系列开始更新啦
——何时完结不确定,写多少看我会多少!这是已经更新的地址:
- 小众系列之终极类型转换:从hello world看JavaScript隐藏的黑魔法制
- 小众系列之隐式类型转换:从[] == ![]看隐式强制转换机制
- 小众系列之事件循环:从HTML5与PromiseA+规范来看事件循环
这个系列旨在对一些人们不常用遇到的知识点,以及可能常用到但不曾深入了解的部分做一个重新梳理,虽然可能有些部分看起来没有什么用,因为平时开发真的用不到!但个人认为糟粕也好精华也罢里面全部蕴藏着JS一些偏本质的东西或者说底层规范,如果能适当避开舒适区来看这些小细节,也许对自己也会有些帮助~文章更新在我的博客,欢迎不定期关注。
先看实验代码
2 == true //false
2 == false //false
[] == false //true
"0" == false //true
[] == ![] //true 神奇吧
我相信大部分的童鞋看着这种等式一般的反应都是xxx是真值,可以转换为true。xxx是假的所以是false!好的摒弃这种想法吧,不然也不会出现这么多神奇的结果了,我们需要做的是通过一步步计算来得出结论。
前置知识
这部分知识属于真·死记硬背,因为你问我为什么,我只能说规范就是这么定义的。
假值
为什么提到假值,而不是真值是因为真值真的是太!多!了!但是假值只有以下这么几个:
- undefined
- null
- false
- +0、-0、NaN
- ""
除此以外别的值做强制类型转换的时候都是真值,so记住就好。
PS:有兴趣的同学可以试试new Number(0)
之类的通过对象包装的假值的结果,不过这并不常用故不属于本次讨论范畴。
!
! 这个运算符,会进行显式强制转化,将结果转化为布尔值即true或false。例如:
![] //false
!1 //false
!0 //true
以此类推来进行显式的强制转换
undefined == null
参考规范11.9.3节抽象相等比较算法可得出
undefined == null 为true的结论。
PS:本次计算规则为抽象相等比较算法的总结,细节可参考上文11.9.3节规范。
ToPrimitive
这是规范9.1节的内容,简单来说你只需要知道如果某个对象([], {})之类的要进行隐式类型转换,那么里面会顺序执行两个操作。即
x.valueOf().toString()
。这里有一个不常用的点要注意。我说的是对象类型进行“隐式”类型转化,如果是显式则不是如此。看下例子:
var a = {
valueOf: () => 1,
toString: () => 233
}
a + "" // 1
String(a) // 233
隐式转化是按照先valueOf后toString的顺序执行,如果显式调用会直接执行oString,不过显式调用在js中覆盖率没有隐式的多,知道即可。
计算 x == y 规则
x,y如果类型相同
这个部分相信有问题的同学百度一下就好。数字的比大小,字符串比大小。里面需要小心的就是NaN != NaN 以及 对象如何比较大小?([1] != [1])
重点:x,y类型不同
x,y一方为布尔值
如果x,y其中一个是布尔值,那么对这个布尔值进行toNumber操作。发现问题了么童鞋们,来看下面代码:
42 == true // false
不了解规范的会认为,42是真值啊!42会转换为true!你别说如if(42){}
这个42确实是真值。但是我们现在在讨论“==”下的转换,那么请记住规范规定了:类型不同时若一方是布尔值,是对布尔值进行类型转化即true => 1
,之后我们就可以理解为什么42不等于true了因为1!= 42
x,y为数字和字符串
将字符串的一方进行toNumber()操作,这个不难理解哈
x,y一方为对象
将对象进行ToPrimitive()操作。如何操作见上文。
计算示例代码结果
2 == true
true => 一方为布尔值:true => 1
2 != 1
2 == false
true => 一方为布尔值:false => 0
2 != 0
[] == false
1、[]为对象: ToPrimitive([]) => [].valueOf().toString() => ""
2、false为布尔:false => 0
3、等式变为:"" == 0
4、一方为数字,一方为字符
Number("") => 0
=> 0 == 0
"0" == false
1、false为布尔:false => 0
2、等式变为:"0" == 0
3、一方为数字,一方为字符
Number("0") => 0
=> 0 == 0
终极版 [] == ![]
1、左侧[]为对象: ToPrimitive([]) => [].valueOf().toString() => ""
2、右侧![]先进行显式类型转换:false(除了上文提到的假值剩下都是真值)
3、等式变为: "" == false
4、一方为布尔:false => 0
5、等式变为:"" == 0
5、一方为数字,一方为字符
Number("") => 0
=> 0 == 0
所以你会发现这些看起来神奇的效果,不过是一步步按照规则进行强制转换罢了。希望以后大家再遇到这种神奇等式的时候不要靠记忆谁是谁,而是一步步推算你会发现结果也不过如此,扮猪吃老虎罢了~
参考文献
最后
惯例po作者的博客,不定时更新中——
有问题欢迎在issues下交流,捂脸求star=。=