我不只是重申您在问题中展示的结果,而是尝试描述为什么您会得到该结果。
说明
仅以第一个例子为例(因为其余的会做类似的事情),我们可以按照规范来看看会发生什么。
从11.4.6 Unary + Operator,我们可以看到发生了ToNumber 转换。
Return ToNumber(GetValue(expr)).
从9.3 ToNumber 我们看到,如果给定一个对象(如您的数组),则会发生ToPrimitive 转换,然后再尝试ToNumber。
对象
应用以下步骤:
- 设 primValue 为 ToPrimitive(输入参数,提示编号)。
- 返回到数字(primValue)。
从9.1 To Primitive,如果ToPrimitive得到一个Object,我们可以看到它的[[DefaultValue]]被获取了:
对象
返回对象的默认值。通过调用对象的 [[DefaultValue]] 内部方法检索对象的默认值,并传递可选提示 PreferredType。本规范为 8.12.8 中的所有原生 ECMAScript 对象定义了 [[DefaultValue]] 内部方法的行为。
从8.12.8 [[DefaultValue]] (hint) 开始,最终将在数组上调用toString() 并返回。如上所述,此字符串被发送到递归 ToNumber。
那么当ToNumber 对字符串进行转换时会发生什么?好吧,它在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;
};
在这里,我将Array.prototype 上的toString 替换为对数组求和的函数。显然您不想这样做,但我们可以展示我们现在将如何获得不同的结果。
Number([1,2,3]); // 6
+[1,2,3]; // 6
所以现在您可以看到,我们的 Array 的 ToNumber 转换之前会导致 NaN 现在导致 Array 中的项目总和。