Aaaaaaaty / blog

趁还能折腾的时候多读书——前端何时是个头
1.78k stars 272 forks source link

从[] == ![] 看隐式强制转换机制 #14

Open Aaaaaaaty opened 7 years ago

Aaaaaaaty commented 7 years ago

写在最前

本次分享一下通过ES5规范来总结如何准确的计算“==”的执行结果。由于规范是枯燥无味的,所以作者试图总结了其中的规律,并希望可以让读完这篇文章的读者不再去“死记硬背”==的结果是什么,而是通过几次简单的计算便心有成竹的得出结论!

欢迎关注我的博客,不定期更新中——

JavaScript小众系列开始更新啦

——何时完结不确定,写多少看我会多少!这是已经更新的地址:

这个系列旨在对一些人们不常用遇到的知识点,以及可能常用到但不曾深入了解的部分做一个重新梳理,虽然可能有些部分看起来没有什么用,因为平时开发真的用不到!但个人认为糟粕也好精华也罢里面全部蕴藏着JS一些偏本质的东西或者说底层规范,如果能适当避开舒适区来看这些小细节,也许对自己也会有些帮助~文章更新在我的博客,欢迎不定期关注。

先看实验代码

2 == true               //false
2 == false              //false
[] == false             //true
"0" == false            //true
[] == ![]               //true 神奇吧

我相信大部分的童鞋看着这种等式一般的反应都是xxx是真值,可以转换为true。xxx是假的所以是false!好的摒弃这种想法吧,不然也不会出现这么多神奇的结果了,我们需要做的是通过一步步计算来得出结论。

前置知识

这部分知识属于真·死记硬背,因为你问我为什么,我只能说规范就是这么定义的。

假值

为什么提到假值,而不是真值是因为真值真的是太!多!了!但是假值只有以下这么几个:

除此以外别的值做强制类型转换的时候都是真值,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

image 这是规范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

所以你会发现这些看起来神奇的效果,不过是一步步按照规则进行强制转换罢了。希望以后大家再遇到这种神奇等式的时候不要靠记忆谁是谁,而是一步步推算你会发现结果也不过如此,扮猪吃老虎罢了~

参考文献

  1. ES5规范
  2. 《你不知道的JavaScript(中卷)》

    最后

    不定时更新中—— 有问题欢迎在issues下交流。

jiaqiu-09 commented 6 years ago

向大佬大学。