对于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('&gt;&gt;&gt; ' + 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):将两个操作数转换为基元(原始值是undefinednull ,布尔值,数字,字符串;所有其他值都是对象,包括数组和函数)。 如果操作数已经是原始的,那么就完成了。 如果不是,则它是一个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

    上一篇: What is the explanation for these bizarre JavaScript behaviours mentioned in the 'Wat' talk for CodeMash 2012?

    下一篇: Getting confused with empty, isset, !empty, !isset