我遇到了无法解释的有效 JavaScript 代码。例如:
+[]===0
-[]===0
~[]===-1
~-~[]===-2
~-~-~-~-~[]===-5
~-~-~-~-~[]+~[]===-6
~+~[]===0
~+~+~[]===-1
~+~+~+~[]===0
你能解释一下这些表达的逻辑吗?
我遇到了无法解释的有效 JavaScript 代码。例如:
+[]===0
-[]===0
~[]===-1
~-~[]===-2
~-~-~-~-~[]===-5
~-~-~-~-~[]+~[]===-6
~+~[]===0
~+~+~[]===-1
~+~+~+~[]===0
你能解释一下这些表达的逻辑吗?
[]
是一个空数组对象,所以:
+[]:强制空数组为正整数,即 0,即 === 为 0
-[]:强制空数组为负整数,即 0,即 === 为 0
~[]:按位非空数组,计算结果为 -1,即 === 到 -1
~-~[]: 取反的 NOTted 空数组的按位非:~-(-1) -> ~1 -> -2
ETC...
当您在任何东西上使用+
or-
运算符时,它会调用Number
它。Number([])
返回0
,所以你得到你的前两个答案。
~
运算符是按位非。基本上它反转一个数字中的所有位,0
变为-1
. 有关按位运算符的更多信息,请点击此处。
其余的只是这些情况的组合。您几乎可以将这些东西组合起来以产生您想要的任何数字。
我将尝试描述您获得该结果的原因,而不是仅仅重申您在问题中展示的结果。
解释
只拿第一个例子(因为其余的会做类似的事情),我们可以按照规范来看看会发生什么。
从11.4.6 Unary + Operator,我们可以看到发生了ToNumber
转换。
返回 ToNumber(GetValue(expr))。
从9.3 ToNumber我们看到,如果给定一个对象(如您的数组),ToPrimitive
则会发生转换,然后再尝试ToNumber
.
目的
应用以下步骤:
- 设 primValue 为 ToPrimitive(输入参数,提示编号)。
- 返回到数字(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;
};
在这里,我将toString
on替换为对Array.prototype
数组求和的函数。显然你不想这样做,但我们可以展示我们现在将如何获得不同的结果。
Number([1,2,3]); // 6
+[1,2,3]; // 6
所以现在你可以看到,ToNumber
我们之前会导致的 Array 转换NaN
现在导致 Array 中的项目的总和。
我将尽我所能:
[]===0
当然是假的,因为[]
不完全等于 0。但是,[]==0
是真的,因为存在隐式转换。
+
并且-[]
工作,因为加号或减号转换[]
为实数。
~0
(0 的按位倒数)为 -1。因此~[]===-1
有效。
其他的只是通过减去或添加 -1 来工作。
我相信,如果我错了,请纠正我,但是添加到数组(例如,向数组添加值而不是向数组添加值)会将其转换为数字。其余的只是使用基本的 +, - 运算符(波浪号 (~) 是按位 NOT)来修改数字,然后是等式。
So [] == array ([]);
[] + 1 == number (0);
+[]===0 (true)