JavaScript中的~[]构造如何工作?

8
我发现了一段可工作的JavaScript代码,但我无法解释。
例如:
  • +[]===0
  • -[]===0
  • ~[]===-1
  • ~-~[]===-2
  • ~-~-~-~-~[]===-5
  • ~-~-~-~-~[]+~[]===-6
  • ~+~[]===0
  • ~+~+~[]===-1
  • ~+~+~+~[]===0
你能解释这些表达式的逻辑吗?

2
如果我遇到那样的代码,我想我会翻桌子。 - J. Holmes
1
答案是:+[NaN]==!!!!0&&+[{}]+~~[[]]-[] - jAndy
1
如果我在任何代码库中看到这样的代码,我会要求原始开发人员重写它。这是非常糟糕的做法! - Gary Willoughby
1
这些是 JavaScript 的利器。用它们来创造伟大的成果。 - Matt Greer
5个回答

11

[] 是一个空数组对象,因此:

+[]: 强制将空数组转换为正整数0,即 === 0
-[]: 强制将空数组转换为负整数0,即 === 0
~[]: 对空数组进行按位取反运算,得到-1,即 === -1
~-~[]: 对取反两次的空数组进行按位取反运算:~-(-1) -> ~1 -> -2

等等...


1
在最后一个字符串上应该是:~1 -> -2 - Denis
-1 +[]: 强制空数组成为正整数,即0,与0相等并没有解释为什么会发生这种情况。它只是重申了结果。显然,如果 +[] === 0,那么 +[] 必须已经被转换为 0。该问题要求解释逻辑。 - RightSaidFred

2
当你使用+-运算符时,它会调用NumberNumber([])返回0,所以你得到了前两个答案。 ~运算符是按位取反。基本上它会将一个数字中的所有位取反,从而将0变为-1。更多关于按位运算符的信息请参考这里
其余的只是这些情况的组合。你可以将这些东西组合起来制作任何你想要的数字。

明白了。我错过了将[]转换为0的部分。 - Denis

2

不仅要重新陈述你在问题中展示的结果,我还会尝试描述为什么会得到那个结果。

解释

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

11.4.6 一元加操作符我们可以看到进行了 ToNumber 转换。

返回 ToNumber(GetValue(expr))。

9.3 ToNumber中我们可以看到,如果给定一个对象(如您的数组),则会进行一个 ToPrimitive 转换,然后再尝试进行 ToNumber 转换。

对象

执行以下步骤:

  1. 将 primValue 设置为 ToPrimitive(输入参数,提示数字)的结果。
  2. 返回 ToNumber(primValue) 的结果。

9.1 To Primitive中,如果 ToPrimitive 得到了一个对象,我们可以看到它的 [[DefaultValue]] 将被获取:

对象

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

8.12.8 [[DefaultValue]](提示)中,最终发生的是调用数组的 toString() 并返回它的字符串。这个字符串将被发送到上述递归的 ToNumber 中。

那么在字符串上执行 ToNumber 转换会发生什么呢?在9.3.1 应用于字符串类型的 ToNumber中有详细说明,但有点冗长。简单地直接进行转换可以看出会发生什么:

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

一些证明

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

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

在这里,我已经用一个将数组求和的函数替换了Array.prototype上的toString。显然,你不想这样做,但我们可以展示现在会得到不同的结果。

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

现在你可以看到,我们之前会得到NaN的数组的ToNumber转换现在将得到数组中所有项的总和。


1

我会尽力而为:

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

+-[]有效,因为加或减将[]转换为实数。

~0(0的按位反转)是-1。因此,~[]===-1有效。

其他的都是通过多次加或减-1来实现的。


0

我相信,如果我错了,请纠正我,但是将值添加到数组中(例如,将一个值添加到数组中而不是将一个值添加到数组中)会将其转换为数字。其余部分只是使用基本的“+”、“-”运算符(波浪线(~)表示按位取反)修改数字,然后进行等式计算。

So [] == array ([]);
[] + 1 == number (0);
+[]===0 (true)

实际上它将其转换为字符串,而不是数字。 [1] + 1 = "11" 所以 +[] = 0 因为这就像执行 +"" - pleasedontbelong

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接