# [] == ![] ?

## 最终结论

`[] == ![]` -> `[] == false` -> `[] == 0` -> `[].valueOf() == 0` -> `[].toString() == 0` -> `'' == 0` -> `0 == 0` -> `true`

## 分析

### 逻辑非 ！

mozilla 逻辑非： !

• 如果expr能转换为true，返回false；
• 如果expr能转换为false，则返回true。

### 转 bool

js 中能够转换为false的字面量是可枚举的，包含

• null；
• NaN；
• 0；
• 空字符串（""）；
• undefined。

### == 运算符

ECMAScript® 2019 : 7.2.14 Abstract Equality Comparison

1. If Type(x) is the same as Type(y), then

1. Return the result of performing Strict Equality Comparison x === y.
2. If x is null and y is undefined, return true.
3. If x is undefined and y is null, return true.
4. If Type(x) is Number and Type(y) is String, return the result of the comparison x == ! ToNumber(y).
5. If Type(x) is String and Type(y) is Number, return the result of the comparison ! ToNumber(x) == y.
6. If Type(x) is Boolean, return the result of the comparison ! ToNumber(x) == y.
7. If Type(y) is Boolean, return the result of the comparison x == ! ToNumber(y).
8. If Type(x) is either String, Number, or Symbol and Type(y) is Object, return the result of the comparison x == ToPrimitive(y).
9. If Type(x) is Object and Type(y) is either String, Number, or Symbol, return the result of the comparison ToPrimitive(x) == y.
10. Return false.

### ToNumber

ECMAScript® 2019 : 7.1.3ToNumber

If argument is true, return 1. If argument is false, return +0.

### ToPrimitive

ECMAScript® 2019 : 7.1.1ToPrimitive ( input [ , PreferredType ] )

The abstract operation ToPrimitive converts its input argument to a non-Object type. [尝试转换为原始对象]

If an object is capable of converting to more than one primitive type, it may use the optional hint PreferredType to favour that type. Conversion occurs according to the following algorithm. [如果一个对象可以被转换为多种原语类型, 则参考 PreferredType， 依据如下规则转换]

1. Assert: input is an ECMAScript language value.
2. If Type(input) is Object, then

1. If PreferredType is not present, let hint be "default".
2. Else if PreferredType is hint String, let hint be "string".
3. Else PreferredType is hint Number, let hint be "number".
4. Let exoticToPrim be ? GetMethod(input, @@toPrimitive).
5. If exoticToPrim is not undefined, then

1. Let result be ? Call(exoticToPrim, input, « hint »).
2. If Type(result) is not Object, return result.
3. Throw a TypeError exception.
6. If hint is "default", set hint to "number".
7. Return ? OrdinaryToPrimitive(input, hint).
3. Return input.

1. Assert: IsPropertyKey(P) is true.
2. Let O be ? ToObject(V).
3. Return ? O.[[Get]](P, V).

### [[Get]]

ECMAScript® 2019 : 9.1.8[[Get]] ( P, Receiver )

Return the value of the property whose key is propertyKey from this object[检索对象的 propertyKey 属性值]

### OrdinaryToPrimitive( O, hint )

ECMAScript® 2019 : 7.1.1.1OrdinaryToPrimitive ( O, hint )

1. Assert: Type(O) is Object.
2. Assert: Type(hint) is String and its value is either "string" or "number".
3. If hint is "string", then

• Let methodNames be « "toString", "valueOf" ».
4. Else,

• Let methodNames be « "valueOf", "toString" ».
5. For each name in methodNames in List order, do

• 5.1 Let method be ? Get(O, name).
• 5.2 If IsCallable(method) is true, then

• 5.2.1 Let result be ? Call(method, O).
• 5.2.2 If Type(result) is not Object, return result.
6. Throw a TypeError exception.

### Array.prototype.valueOf from Object.prototype.valueOf

ECMAScript® 2019 : 19.1.3.7Object.prototype.valueOf ( )

When the valueOf method is called, the following steps are taken:

1. Return ? ToObject(this value).

This function is the %ObjProto_valueOf% intrinsic object.

### ToObject

ECMAScript® 2019 : 7.1.13ToObject ( argument )

Object ： Return argument?! 这步算是白走了。我们接着看 toString，同样的我们要考虑覆写的问题。

### Array.prototype.toString()

ECMAScript® 2019 : 22.1.3.28Array.prototype.toString ( )

1. Let array be ? ToObject(this value).
2. Let func be ? Get(array, "join").
3. If IsCallable(func) is false, set func to the intrinsic function %ObjProto_toString%.
4. Return ? Call(func, array).

`[].valueOf().toString() == 0` => `[].join() == 0` => `'' == 0`

1. If Type(x) is Number and Type(y) is String, return the result of the comparison x == ! ToNumber(y).
1. If Type(x) is String and Type(y) is Number, return the result of the comparison ! ToNumber(x) == y.

### ToNumber Applied to the String Type

ECMAScript® 2019 : 7.1.3.1ToNumber Applied to the String Type

``````StringNumericLiteral:::
StrWhiteSpaceopt
StrWhiteSpaceoptStrNumericLiteralStrWhiteSpaceopt
StrWhiteSpace:::
StrWhiteSpaceCharStrWhiteSpaceopt
StrWhiteSpaceChar:::
WhiteSpace
LineTerminator
StrNumericLiteral:::
StrDecimalLiteral
BinaryIntegerLiteral
OctalIntegerLiteral
HexIntegerLiteral
StrDecimalLiteral:::
StrUnsignedDecimalLiteral
+StrUnsignedDecimalLiteral
-StrUnsignedDecimalLiteral
StrUnsignedDecimalLiteral:::
Infinity
DecimalDigits.DecimalDigitsoptExponentPartopt
.DecimalDigitsExponentPartopt
DecimalDigitsExponentPartopt``````

ECMAScript® 2019 : 11.8.3Numeric Literals

``````...
DecimalIntegerLiteral::
0
NonZeroDigitDecimalDigitsopt``````

1. 如果字符串中只包含数字（包括前面带加号或负号的情况），则将其转换为十进制数值，即"1"会变成1，"123"会变成123，而"011"会变成11（注意：前导的零被忽略了）；
2. 如果字符串中包含有效的浮点格式，如"1.1"，则将其转换为对应的浮点数值（同样，也会忽略前导零）；
3. 如果字符串中包含有效的十六进制格式，例如"0xf"，则将其转换为相同大小的十进制整数值；
4. 什么八进制二进制同上；
5. 如果字符串是空的（不包含任何字符），则将其转换为0；
6. 如果字符串中包含除上述格式之外的字符，则将其转换为NaN。

`'' == 0` => `0 == 0`

true

## 胡说八道

• Q： 这么做是矫枉过正么？
• A： 这个问题写博客的时候确实追的非常全，其中涉及到的所有规范都做了详细解释，但其实面试时候只需要知道一些关键点就可以了：
1. 左边空数组不是转 bool 而是转 number。
2. 空数组转 number 怎么调用 valueOf 和 toString 的，调用顺序和调用规则是什么？
3. 「==」大致的隐式转换规则。
4. js 中那些字面量转 bool 是 false？

• Q： 有人会认认真真看到这里么？
• A： 有。
• Q： 这么做有什么用啊？
• A： 没用，下一个。【ps： 面试中也经常有人问我这个问题，我认为这本质上是你对自己定位的问题，你定位自己是前端，就学应用层，你定位自己是程序员，就看全栈，如果你定位自己是工程师，就看底层，看规范。工作五年以上的程序员，不应该问这个问题。【pss： 我定位自己就是爱好，于是我就瞎鸡儿看】】
• Q： 工作中用的到么？ 工作这么忙哪来的时间？
• A： pass.
• Q： 这么写博客，累么？
• A： 很累，我要查很多很多资料，还要甄别，很多英文文档，对我这个持有「大不列颠负十级的英语认证」的人来说，简直就是美利坚版诗经。一篇博客，起码三四天起。而且大家看起来也需要基础和成本，我也不知道能坚持多久。
• Q：...
• A：...

## 花絮

### ！ToNumber: ！前缀

ECMAScript® 2019 : 5.2.3.4 ReturnIfAbrupt Shorthands

Similarly, prefix ! is used to indicate that the following invocation of an abstract or syntax-directed operation will never return an abrupt completion[The term “abrupt completion” refers to any completion with a [[Type]] value other than normal.] and that the resulting Completion Record's [[Value]] field should be used in place of the return value of the operation. For example, the step:

• Let val be ! OperationName().

is equivalent to the following steps:

1. Let val be OperationName().
2. Assert: val is never an abrupt completion.
3. If val is a Completion Record, set val to val.[[Value]].

Syntax-directed operations for runtime semantics make use of this shorthand by placing ! or ? before the invocation of the operation:

• Perform ! SyntaxDirectedOperation of NonTerminal.

### ? ToNumber: ? 前缀

ECMAScript® 2019 : 5.2.3.4 ReturnIfAbrupt Shorthands

Invocations of abstract operations and syntax-directed operations that are prefixed by ? indicate that ReturnIfAbrupt should be applied to the resulting Completion Record.

## 拓展

• [] == ![]
• [] == []
• [] == false
• [] == 0
• [] == '' // [注意转换过程，并不会转 numbe, 看下一题]
• [] == '0'
• {} == '0'

#### 你可能感兴趣的

30 条评论
whiteplayer · 2018年12月14日

+1 回复

0

hooper 作者 · 2018年12月14日
squallyan · 2018年12月18日

+1 回复

0

1. 左边空数组不是转 bool 而是转 number。
2. 空数组转 number 怎么调用 valueof 和 toString 的， 为什么这么做？
3. ==号 大致的隐式转换规则。

hooper 作者 · 2018年12月18日
0

@hooper 点赞，哈哈

squallyan · 2018年12月18日

+1 回复

0

hooper 作者 · 2018年12月19日
xianshenglu · 2018年12月13日

0

bat，tmd 中两家公司面试题有这道题的变种。 是一道拉大区分度的题，答不出来没事，回答对了就很加分。其他的我没呆过不知道。

hooper 作者 · 2018年12月13日
0

@hooper 我并没有去疑问有什么用，我只是觉得，这属于，查文档就可以弄清楚的知识，为什么要记？跟考我一个罕见的api没什么区别，时间都是有成本的。ps，我不看重的点，就算bat考，我也仍然觉得这并不是什么好点，觉得他很重要，拿出理由来，而不是来一句，我乐意。

xianshenglu · 2018年12月13日
0

hooper 作者 · 2018年12月13日
laoLiueizo · 2018年12月14日

0

hooper 作者 · 2018年12月14日
daixujie666 · 2018年12月14日

0

hooper 作者 · 2018年12月16日
1

daixujie666 · 2018年12月17日
xunmengxingyu · 2018年12月21日

0

hooper 作者 · 2018年12月21日

0

hooper 作者 · 2018年12月21日
0

0
1. ==的判断规则是靠什么判断的

== 转换规则就是这 https://segmentfault.com/a/11... 10步骤的描述

1. 如何区分x.y类型 这个问题

If Type(x) is the same as Type(y), then

Return the result of performing Strict Equality Comparison x === y.

hooper 作者 · 2018年12月21日
shayyee · 2018年12月27日

0

hooper 作者 · 2018年12月27日

// This works because == invokes toString which calls .join for Arrays.
a = [1,2,3];
a.join = a.shift;
console.log(a == 1 && a == 2 && a == 3);

0

hooper 作者 · 2018年12月29日