对于CodeMash 2012的“Wat”演讲中提到的这些奇怪的JavaScript行为,有什么解释?
CodeMash 2012的'Wat'演讲基本上指出了Ruby和JavaScript的几个奇怪的怪癖。
我在http://jsfiddle.net/fe479/9/上做了一个JSFiddle的结果。
下面列出了特定于JavaScript的行为(因为我不知道Ruby)。
我在JSFiddle中发现,我的一些结果与视频中的结果不一致,我不知道为什么。 然而,我很想知道JavaScript在每种情况下如何处理幕后的工作。
Empty Array + Empty Array
[] + []
result:
<Empty String>
在JavaScript中使用数组时,我非常好奇+
操作符。 这与视频的结果相符。
Empty Array + Object
[] + {}
result:
[Object]
这与视频的结果相符。 这里发生了什么? 为什么这是一个对象。 +
运营商做了什么?
Object + Empty Array
{} + []
result
[Object]
这与视频不符。 视频显示结果为0,而我得到[Object]。
Object + Object
{} + {}
result:
[Object][Object]
这与视频不匹配,以及如何将变量结果输出到两个对象? 也许我的JSFiddle是错误的。
Array(16).join("wat" - 1)
result:
NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
做wat + 1结果在wat1wat1wat1wat1
...
我怀疑这只是简单的行为,试图从字符串中减去一个数字导致NaN。
以下是你看到的结果(应该看到的结果)的解释清单。 我使用的参考文献来自ECMA-262标准。
[] + []
使用加法运算符时,左边和右边的操作数会先转换为基元(第11.6.1节)。 根据§9.1,将一个对象(本例中为一个数组)转换为一个基元返回它的默认值,这对于具有有效的toString()
方法的对象是调用object.toString()
(§8.12.8)的结果。 对于数组,这与调用array.join()
(第15.4.4.2节)相同。 加入一个空数组会得到一个空字符串,因此加法运算符的第7步返回两个空字符串的连接,即空字符串。
[] + {}
与[] + []
类似,两个操作数都先转换为基元。 对于“对象对象”(第15.2节),这又是调用object.toString()
的结果,对非空,非未定义的对象是"[object Object]"
(第15.2.4.2节)。
{} + []
这里的{}
不是作为一个对象进行分析,而是作为一个空白块(§12.1,至少只要你不强迫这个语句成为一个表达式,而是稍后关于这个表达式)。 空块的返回值为空,因此该语句的结果与+[]
相同。 一元+
运算符(§11.4.6)返回ToNumber(ToPrimitive(operand))
。 正如我们已经知道的, ToPrimitive([])
是空字符串,根据§9.3.1, ToNumber("")
是0。
{} + {}
与前面的情况类似,第一个{}
被解析为具有空返回值的块。 再次, +{}
与ToNumber(ToPrimitive({}))
,并且ToPrimitive({})
是"[object Object]"
(请参阅[] + {}
)。 所以要得到+{}
的结果,我们必须在字符串"[object Object]"
上应用ToNumber
。 当遵循§9.3.1的步骤时,我们得到了NaN
:
如果语法不能将String解释为StringNumericLiteral的扩展,则ToNumber的结果是NaN 。
Array(16).join("wat" - 1)
根据§15.4.1.1和§15.4.2.2, Array(16)
创建了一个长度为16的新数组。为了获得要加入的参数的值,§11.6.2步骤#5和#6表明我们必须转换两个操作数都使用ToNumber
编号。 ToNumber(1)
简单地为1(§9.3),而ToNumber("wat")
又是NaN
,按照§9.3.1。 遵循第11.6.2节的第7步,§11.6.3规定
如果任一操作数是NaN ,则结果是NaN 。
所以Array(16).join
的参数是NaN
。 在§15.4.4.5( Array.prototype.join
)之后,我们必须在参数"NaN"
(§9.8.1)上调用ToString
:
如果m是NaN ,则返回字符串"NaN"
。
根据第15.4.4.5节的步骤10,我们得到15个重复的"NaN"
和空字符串的连接,这与您所看到的结果相同。 当使用"wat" + 1
而不是"wat" - 1
作为参数时,加法运算符将1
转换为字符串,而不是将"wat"
转换为数字,因此它有效地调用Array(16).join("wat1")
。
至于为什么你会看到{} + []
情况的不同结果:将它用作函数参数时,你迫使语句成为一个ExpressionStatement,这使得无法将{}
分析为空块,所以它被解析为一个空的对象文字。
这不仅仅是一个回答的评论,但由于某种原因,我不能评论你的问题。 我想更正你的JSFiddle代码。 但是,我在Hacker News上发布了这个消息,有人建议我在这里重新发布。
JSFiddle代码中的问题是({})
(括号内的开放大括号)与{}
(作为一行代码的开头处打开大括号)不一样。 因此,当您键入out({} + [])
您将强制{}
成为键入{} + []
时不会显示的内容。 这是Javascript的整体'wat'-ness的一部分。
基本思想很简单JavaScript想要允许这两种形式:
if (u)
v;
if (x) {
y;
z;
}
要做到这一点,有两个解释是用大括号表示的:1.它不是必需的,2.它可以出现在任何地方。
这是一个错误的举动。 真正的代码没有在中间出现的大括号,而实际的代码在使用第一种形式而非第二种形式时往往更脆弱。 (在我上一次工作中每隔一个月大约一次,当他们对我的代码的修改不起作用时,我会打电话给同事的办公桌,问题是他们在“if”中添加了一行而未添加卷发我最终采用了大括号总是需要的习惯,即使你只写了一行。)
幸运的是,在很多情况下,eval()将复制JavaScript的全部功能。 JSFiddle代码应为:
function out(code) {
function format(x) {
return typeof x === "string" ?
JSON.stringify(x) : x;
}
document.writeln('>>> ' + code);
document.writeln(format(eval(code)));
}
document.writeln("<pre>");
out('[] + []');
out('[] + {}');
out('{} + []');
out('{} + {}');
out('Array(16).join("wat" + 1)');
out('Array(16).join("wat - 1")');
out('Array(16).join("wat" - 1) + " Batman!"');
document.writeln("</pre>");
[这也是我多年来第一次编写document.writeln,并且对涉及document.writeln()和eval()的任何东西都感到有点肮脏。]
我第二@Ventero的解决方案。 如果你愿意,你可以进入更详细的如何+
转换其操作数。
第一步(§9.1):将两个操作数转换为基元(原始值是undefined
, null
,布尔值,数字,字符串;所有其他值都是对象,包括数组和函数)。 如果操作数已经是原始的,那么就完成了。 如果不是,则它是一个obj
并执行以下步骤:
obj.valueOf()
。 如果它返回一个原语,你就完成了。 Object
和数组的直接实例会自行返回,所以你还没有完成。 obj.toString()
。 如果它返回一个原语,你就完成了。 {}
和[]
都返回一个字符串,所以你完成了。 TypeError
。 对于日期,第1步和第2步交换。 您可以观察转换行为,如下所示:
var obj = {
valueOf: function () {
console.log("valueOf");
return {}; // not a primitive
},
toString: function () {
console.log("toString");
return {}; // not a primitive
}
}
交互( Number()
首先转换为原始,然后转换为数字):
> Number(obj)
valueOf
toString
TypeError: Cannot convert object to primitive value
第二步(第11.6.1节):如果其中一个操作数是字符串,另一个操作数也会转换为字符串,并且结果是通过连接两个字符串生成的。 否则,两个操作数都转换为数字,并通过添加结果生成结果。
有关转换过程的更详细说明:“JavaScript中的{} + {}是什么?”
链接地址: http://www.djcxy.com/p/59557.html