第二季

前戏

学一些 JS 里面就有提到过 JS 的常见问题 [滑稽]。最近又看到了类似的总结。前戏到此为止,马上进入吧。

true 等于 false

1
[] == ![] // -> true

[] 是 JavaScript 里面的数组(Array)。也就是数组不等于数组!

要想知道为什么,当然是看语言规范啦!(不要再吭哧吭哧地扣汇编代码)

因为非运算符 ! 比相等判别运算符== 的优先级高,所以先看非运算符的规范

12.5.9Logical NOT Operator (!)

12.5.9.1Runtime Semantics: Evaluation

  1. Let expr be the result of evaluating UnaryExpression.
  2. Let oldValue be ToBoolean(? GetValue(expr)).
  3. If oldValue is true, return false.
  4. Return true.

其中最关键的一步是第二条的 ToBoolean(![]) 。再看 ToBoolean 的规范。

7.1.2ToBoolean ( argument )

The abstract operation ToBoolean converts argument to a value of type Boolean according to Table 9:

Argument Type Result
Undefined Return false.
Null Return false.
Boolean Return argument.
Number If argument is +0, -0, or NaN, return false; otherwise return true.
String If argument is the empty String (its length is zero), return false; otherwise return true.
Symbol Return true.
Object Return true.

[] 虽然是数组,但是 typeof [] 是 Object 。所以查表 ![] 的结果为 true

结果转化为 [] == true

再看 == 相等判别运算符的规范

7.2.13Abstract 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), thenReturn the result of performing Strict Equality Comparison x === y.
    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.

显然 [] == true 对应的是第 7 条。

再看 ToNumber 的规范

7.1.3ToNumber ( argument )

The abstract operation ToNumber converts argument to a value of type Number according to Table 10:

Argument Type Result
Undefined Return NaN.
Null Return +0.
Boolean If argument is true, return 1. If argument is false, return +0.
Number Return argument (no conversion).
String See grammar and conversion algorithm below.
Symbol Throw a TypeError exception.
Object Apply the following steps:Let primValue be ? ToPrimitive(argument, hint Number).Return ? ToNumber(primValue).

true 的类型是 Boolean , 所以结果是 1 。

结果又转化为 [] == 1

在看相等判断运算符的规范,这时候符合第九条。 要运算 ToPrimitive([]) == 1

查看 ToPrimitive 的规范即可。这里有个偷懒的办法。因为 ToBoolean([]) == true

所以 true == 1 的结果是 true

baNaNa

1
'b' + 'a' + + 'a' + 'a' // => baNaNa

这里其实耍了个花招,实际上是这样的。

1
'b' + 'a' + (+ 'a') + 'a'

因为 +'a'NaN 。所以整个式子的结果就是 baNaNa 。

NaN 不是 NaN

1
NaN === NaN // -> false

这个看严格判等运算符 ===规范就能很容易得到答案

7.2.14Strict 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 different from Type(y), return false.
  2. If Type(x) is Number, then
    1. If x is NaN, return false.
    2. If y is NaN, return false.
    3. If x is the same Number value as y, return true.
    4. If x is +0 and y is -0, return true.
    5. If x is -0 and y is +0, return true.
    6. Return false.
  3. Return SameValueNonNumber(x, y).

所以根据 2.1 ,结果是 false

因为 IEEE 是这样定义 NaN

四种互斥的关系是:小于,等于,大于以及无序。最后一种情况出现在至少有一个操作符为 NaN 的情况。NaN 与所有相比的结果都应该是无序,包括自身。

“What is the rationale for all comparisons returning false for IEEE754 NaN values?” at StackOverflow

##NaN 是 number

1
typeof NaN            // -> 'number'

这样看似不合理。实际上,稍微思考一下就觉得是非常自然的。

比如有一个除法函数。

1
2
3
4
function div(x, y)
{
return x / y // 返回的类型是什么?
}

div 的返回类型当然是 number 。所以当 div(0, 0) 产生结果为 NaN 时,如果 NaN 不是 number 类型就比较难办了。

参考 typeof 运算符规范

最小值大于零

1
Number.MIN_VALUE > 0 // -> true

因为 Number.MIN_VALUE 的值为 5e-324,也就是 $5 \times 10^{-324}$ ,所以当然是大于 0 的啊。

这里的最小值想要表达的是浮点数的最小精度。

HTML 注释在 JS 是合法的

1
2
// valid comment
<!-- valid comment too -->

没想到吧!JS 中也可以使用 HTML 式的注释方式。而且这是写在规范里面的。

疯狂打 CALL

1
console.log.call.call.call.call.call.apply(a => a, [1, 2]) // => 2

熟悉 call 和 apply 的用法就能很快看出

1
2
3
console.log.call.call.call.call.call.apply(a => a, [1, 2])
// => console.log.call.apply(a => a, [1, 2])
// => 2

事后

以上所有例子出自于 WTFJS,里面还有很多跟新语法有关的例子。