8

我遇到了无法解释的有效 JavaScript 代码。例如:

  • +[]===0
  • -[]===0
  • ~[]===-1
  • ~-~[]===-2
  • ~-~-~-~-~[]===-5
  • ~-~-~-~-~[]+~[]===-6
  • ~+~[]===0
  • ~+~+~[]===-1
  • ~+~+~+~[]===0

你能解释一下这些表达的逻辑吗?

4

5 回答 5

11

[]是一个空数组对象,所以:

+[]:强制空数组为正整数,即 0,即 === 为 0
-[]:强制空数组为负整数,即 0,即 === 为 0
~[]:按位非空数组,计算结果为 -1,即 === 到 -1
~-~[]: 取反的 NOTted 空数组的按位非:~-(-1) -> ~1 -> -2

ETC...

于 2011-11-23T17:23:44.507 回答
2

当您在任何东西上使用+or-运算符时,它会调用Number它。Number([])返回0,所以你得到你的前两个答案。

~运算符是按位非。基本上它反转一个数字中的所有位,0变为-1. 有关按位运算符的更多信息,请点击此处

其余的只是这些情况的组合。您几乎可以将这些东西组合起来以产生您想要的任何数字。

于 2011-11-23T17:25:16.720 回答
2

我将尝试描述您获得该结果的原因,而不是仅仅重申您在问题中展示的结果。

解释

只拿第一个例子(因为其余的会做类似的事情),我们可以按照规范来看看会发生什么。

11.4.6 Unary + Operator,我们可以看到发生了ToNumber转换。

返回 ToNumber(GetValue(expr))。

9.3 ToNumber我们看到,如果给定一个对象(如您的数组),ToPrimitive则会发生转换,然后再尝试ToNumber.

目的

应用以下步骤:

  1. 设 primValue 为 ToPrimitive(输入参数,提示编号)。
  2. 返回到数字(primValue)。

9.1 到 Primitive,如果ToPrimitive得到一个 Object ,我们可以看到它[[DefaultValue]]是被获取的:

目的

返回对象的默认值。通过调用对象的 [[DefaultValue]] 内部方法检索对象的默认值,并传递可选提示 PreferredType。本规范为 8.12.8 中的所有原生 ECMAScript 对象定义了 [[DefaultValue]] 内部方法的行为。

8.12.8 [[DefaultValue]] (hint)开始,最终会发生的将是toString()在 Array 上调用并返回。ToNumber如上所述,此字符串被发送到递归。

那么当ToNumber对 String 进行转换时会发生什么?好吧,它在9.3.1 ToNumber Applied to the String Type中有描述,而且有点冗长。更简单的是直接进行转换,看看会发生什么:

Number("");     // result is 0 on an empty string
Number("    "); // result is 0 on a string with only whitespace
Number("123");  // result is 123 on a numeric string
Number(" 123 ");// result is 123 on a numeric string with leading & trailing spaces
Number("abc");  // result is NaN (not a number) on non-numeric strings

所以问题是,我们从数组中得到什么字符串。同样,这很容易测试。

[].toString();  // result is "" (empty string)

由于结果是空字符串,而空字符串的ToNumber转换0如上所示,这意味着我们正在比较0 === 0.

就像我们这样做一样:

Number( [].toString() ) === 0; // true

或者把它画得更清楚一点:

var x = [];
x = x.toString();  // ""
x = Number( x );   // 0
x === 0;  // true

更多toString结果。

要显示更多toString的数组转换,请考虑以下内容:

[1].toString();            // "1"
[1,2,3].toString();        // "1,2,3"
["a",1,"b",2].toString();  // "a,1,b,2"

因此,要ToNumber对上述数组进行转换,第一个会给我们一个数字,最后两个会产生NaN.

Number([1]);            // 1
Number([1,2,3]);        // NaN
Number(["a",1,"b",2]);  // NaN

一些证据

为了进一步证明这种toString()转换发生在ToNumber转换之前,我们实际上可以进行修改Array.prototype.toString以提供不同的结果,并且任何ToNumber转换都将使用该修改后的结果。

Array.prototype.toString = function() {
    var n = 0;
    for( var i = 0; i < this.length; i++ ) {
        n += this[i];
    }
    return n;
};

在这里,我将toStringon替换为对Array.prototype数组求和的函数。显然你不想这样做,但我们可以展示我们现在将如何获得不同的结果。

Number([1,2,3]);   // 6
+[1,2,3];          // 6

所以现在你可以看到,ToNumber我们之前会导致的 Array 转换NaN现在导致 Array 中的项目的总和。

于 2011-11-23T19:45:29.400 回答
1

我将尽我所能:

[]===0当然是假的,因为[]不完全等于 0。但是,[]==0是真的,因为存在隐式转换。

+并且-[]工作,因为加号或减号转换[]为实数。

~0(0 的按位倒数)为 -1。因此~[]===-1有效。

其他的只是通过减去或添加 -1 来工作。

于 2011-11-23T17:27:18.767 回答
0

我相信,如果我错了,请纠正我,但是添加到数组(例如,向数组添加值而不是向数组添加值)会将其转换为数字。其余的只是使用基本的 +, - 运算符(波浪号 (~) 是按位 NOT)来修改数字,然后是等式。

So [] == array ([]);
[] + 1 == number (0);
+[]===0 (true)
于 2011-11-23T17:26:08.473 回答