深入`Abstract Equality Comparison`,就是`==`啦

前后两次去了某蹦跶公司面试,都有这么一道题.

    if ([] == false) { console.log(1) } // 1
    if ({} == false) { console.log(2) } // 2
    if ([1] == false) { console.log(3) } // 3
复制代码

虽然都答对了,但是说不出来为啥.我是不爱学屠龙技的!但是招架不住面试官那种你是我面过最差的那一个的眼神,决定一探究竟,也省得第三次翻车.

好吧,要想解释这个现象,得首先打开EcmaScript的说明书,要是没有,点这里下载,它是前端的基石,是每个前端er都需要牢牢背会的东西.(xinnishishabi)

首先,我们把上面的灰吹一下.然后翻到110页,不出意外你能在最上面找到7.2.14 Abstract Equality Comparison,就它了:

The comparison x == y, where x and y are values, produces true or false. Such a comparison is performed as follows:

  1. If Type(x) is the same as Type(y), then a. Return the result of performing Strict Equality Comparison x === y.

...

  1. ...

总共10个步骤,我觉得你不会看,所以放最下面.阮哥已经帮你翻译好了.传送门.

上面这段算法,一共有 12 步,翻译如下:

  1. 如果x不是正常值(比如抛出一个错误),中断执行。
  2. 如果y不是正常值,中断执行。
  3. 如果Type(x)与Type(y)相同,执行严格相等运算x === y。
  4. 如果x是null,y是undefined,返回true。
  5. 如果x是undefined,y是null,返回true。
  6. 如果Type(x)是数值,Type(y)是字符串,返回x == ToNumber(y)的结果。
  7. 如果Type(x)是字符串,Type(y)是数值,返回ToNumber(x) == y的结果。
  8. 如果Type(x)是布尔值,返回ToNumber(x) == y的结果。
  9. 如果Type(y)是布尔值,返回x == ToNumber(y)的结果。
  10. 如果Type(x)是字符串或数值或Symbol值,Type(y)是对象,返回x == ToPrimitive(y)的结果。
  11. 如果Type(x)是对象,Type(y)是字符串或数值或Symbol值,返回ToPrimitive(x) == y的结果。
  12. 返回false。

我们拿最诡异的if ([] == false) { ... }套一下.

  • A = []; B = false走到了第9条,会对B执行ToNumber(B)操作.针对我们的Boolean会返回+0.

  • 现在A = []; B = +0,匹配到了第11条,执行ToPrimitive(A) == B.然后执行OrdinaryToPrimitive(input, preferedType),这是关键点,由于太多,不全部翻译了.

可以这么理解,不全是哈:传送门.

input是输入的值,preferedType是期望转换的类型,他可以是字符串,也可以是数字。
如果转换的类型是number,会执行以下步骤:
1. 如果input是原始值,直接返回这个值;
2. 否则,如果input是对象,调用input.valueOf(),如果结果是原始值,返回结果;
3. 否则,调用input.toString()。如果结果是原始值,返回结果;
4. 否则,抛出错误。
如果转换的类型是String,2和3会交换执行,即先执行toString()方法。
你也可以省略preferedType,此时,日期会被认为是字符串,而其他的值会被当做Number。
复制代码

针对([] == false),是这么操作的:

  • 由于preferedType是undefined,会先把hint设为default
  • 调用[][Symbol.toPrimitive]的值是undefined,把hint设为number
  • 这时,调用OrdinaryToPrimitive([], number)
  • 它让我们对[]按顺序调用['valueOf', toString], 返回不是对象的值.

If Type(result) is not Object, return result.

  • 绕了这么久,返回的值就是[].toString(),也就是"".
  • 如果是[1],返回的值就是[1].toString(),也就是"1".

那么现在([] == false)就变成了("" == +0)的对比,走到第6条.

  1. 如果Type(x)是数值,Type(y)是字符串,返回x == ToNumber(y)的结果。

(ToNumber("") == +0), 变成(+0 == +0),走到第3条, 变成最终的True.

A StringNumericLiteral that is empty or contains only white space is converted to +0.

  1. 如果Type(x)与Type(y)相同,执行严格相等运算x === y。

敲重点: 所以,[] == falsetrue.

对于([1] == false),会变成("1" == +0), 变成(1 == +0),最终是false

由于({}).toString()"[object Object]", 所以对于({} == false), 会变成("[object Object]" == +0), 变成(NaN == +0), 最终也是false.

Therefore, the result of ToNumber will be NaN if the string contains any leading surrogate or trailing surrogate code units, whether paired or unpaired.

If Type(x) is Number, then, If x is NaN, return false.

相互之间的调用实在太多了,就不在文章里面放了,虽然是英文写的,其实很好懂的.(骗你的,哈哈?).想看的小朋友翻下规格书吧.

如果你真看到这里了,那我们相互关注吧.我都写吐了,感觉自己真是脑抽了,花半天时间写这破玩意儿.不过也是有点积极意义的.

  1. 以后就不要用==了,好不好.
  2. 隐式转换是不能完全避免的.有些很奇葩的问题,遇见了,那是缘分.翻翻规格书就好.
  3. 希望面试中这样的知识点能少点.起码开卷吧...

涉及到的规格书内的引用: (排名不分先后)

  • ToNumber 97页 7.1.3
  • ToPrimitive 95页 7.1.1
  • GetMethod 113页 7.3.9
  • Call 113页 7.3.12
  • OrdinaryToPrimitive 96页 7.1.1.1
  • Type 70页
  • Strict Equality Comparison 114页
  • Get 114页
  • GetV 114页
  • IsCallable 110页

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值