在CodeMash 2012的“ Wat”演讲中提到的这些怪异的JavaScript行为有何解释?

2020/09/21 09:31 · javascript ·  · 0评论

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标准

  1. [] + []

    使用加法运算符时,左操作数和右操作数都首先转换为基元(第11.6.1节)。按照§9.1的规定,将对象(在这种情况下为数组)转换为基元将返回其默认值,对于具有有效toString()方法的对象,这是调用的结果object.toString()(第8.12.8节)。对于数组,这与调用array.join()§15.4.4.2相同连接一个空数组会导致一个空字符串,因此加法运算符的第7步将返回两个空字符串的串联,即空字符串。

  2. [] + {}

    与相似[] + [],两个操作数都首先转换为基元。对于“对象对象”(第15.2节),这又是调用的结果object.toString(),对于非空,非未定义的对象,调用"[object Object]"(第15.2.4.2节)。

  3. {} + []

    {}这里不会被解析为一个对象,而是作为一个空块(§12.1,至少只要你不逼这种说法是一种表达,但稍后详细说明)。空块的返回值为空,因此该语句的结果与相同+[]一元运算+符(第11.4.6节)返回ToNumber(ToPrimitive(operand))众所周知,ToPrimitive([])是空字符串,根据9.3.1节ToNumber("")为0。

  4. {} + {}

    与前一种情况类似,第一种情况{}被解析为具有空返回值的块。同样,+{}和相同ToNumber(ToPrimitive({})),并且ToPrimitive({})"[object Object]"(请参阅[] + {})。因此,要获得结果+{},我们必须ToNumber在字符串上应用"[object Object]"按照§9.3.1的步骤进行操作时,我们得到NaN的结果是:

    如果语法无法将String解释为StringNumericLiteral的扩展,则ToNumber的结果NaN

  5. Array(16).join("wat" - 1)

    根据§15.4.1.1§15.4.2.2Array(16)创建一个长度为16的新数组。要获取要加入的参数的值,§11.6.2步骤#5和#6显示我们必须将两个操作数都转换为a。使用编号ToNumberToNumber(1)仅仅是1(第9.3节),而ToNumber("wat")再次NaN§9.3.1下面的步骤7 §11.6.2§11.6.3决定了

    如果任一操作数为NaN,则结果为NaN

    因此,的论据Array(16).joinNaN在§15.4.4.5(Array.prototype.join)之后,我们必须调用ToString参数"NaN"§9.8.1):

    如果mNaN,则返回String "NaN"

    §15.4.4.5的第10步之后,我们得到"NaN"了空字符串的串联的15次重复,这等于您看到的结果。当使用"wat" + 1代替"wat" - 1作为参数时,加法运算符将转换1为字符串而不是"wat"数字,因此它有效地调用Array(16).join("wat1")

关于为什么看到这种{} + []情况的不同结果:当将其用作函数参数时,您将强制该语句为ExpressionStatement,这使得无法将其解析{}为空块,因此将其解析为空对象文字。

This is more of a comment than an answer, but for some reason I can't comment on your question. I wanted to correct your JSFiddle code. However, I posted this on Hacker News and someone suggested that I repost it here.

JSFiddle代码中的问题在于({})(括号内的大括号)与{}(括号内的代码行开头)不同。因此,当您键入内容时,out({} + [])您将强制输入{}而不是在键入内容时{} + []这是Java整体“浪费”的一部分。

基本思想是简单的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然后执行以下步骤:

  1. 致电obj.valueOf()如果返回原语,则操作完成。Object和数组的直接实例会返回自身,因此您尚未完成。
  2. 致电obj.toString()如果返回原语,则操作完成。{}并且[]都返回一个字符串,所以您完成了。
  3. 否则,抛出一个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中的{} + {}是什么?

我们可能会参考该规范,它是很棒且最准确的,但是大多数情况也可以通过以下陈述以更易理解的方式进行解释:

  • +-运算符只能使用原始值。更具体地说,+(加法)适用于字符串或数字,+(一元)和-(减法和一元)仅适用于数字。
  • 所有期望原始值作为参数的本机函数或运算符,都将首先将该参数转换为所需的原始类型。这是通过valueOf完成的,toString在任何对象上都可用。这就是为什么这样的函数或运算符在对象上调用时不会引发错误的原因。

所以我们可以这样说:

  • [] + []String([]) + String([])相同'' + ''我在上面提到,+(加法)对数字也有效,但是JavaScript中没有有效的数组数字表示法,因此使用字符串加法代替。
  • [] + {}与... String([]) + String({})相同'' + '[object Object]'
  • {} + []这个值得更多解释(请参阅Ventero答案)。在这种情况下,花括号不被视为对象而是空块,因此与相同+[]一元制+仅适用于数字,因此实现尝试从中获取数字[]首先,它尝试valueOf在数组的情况下返回相同的对象,然后尝试最后的手段:将toString结果转换为数字。我们可以将其写+Number(String([]))为与+Number('')相同+0
  • Array(16).join("wat" - 1)减法-仅适用于数字,因此与:相同Array(16).join(Number("wat") - 1),因为"wat"不能转换为有效数字。我们收到NaN,并且对进行NaN结果的任何算术运算NaN,因此我们有:Array(16).join(NaN)

为了支持先前共享的内容。

此行为的根本原因部分是由于JavaScript的弱类型性质。例如,表达式1 +“ 2”是模棱两可的,因为基于操作数类型(int,string)和(int int)有两种可能的解释:

  • 用户打算连接两个字符串,结果为:“ 12”
  • 用户打算将两个数字相加,结果为:3

因此,随着输入类型的变化,输出的可能性也会增加。

加法算法

  1. 将操作数强制转换为原始值

JavaScript原语是字符串,数字,null,未定义和布尔值(Symbol在ES6中即将推出)。其他任何值都是一个对象(例如,数组,函数和对象)。因此描述了将对象转换为原始值的强制过程:

  • 如果在调用object.valueOf()时返回了原始值,则返回该值,否则继续

  • 如果在调用object.toString()时返回了原始值,则返回该值,否则继续

  • 引发TypeError

注意:对于日期值,顺序是在valueOf之前调用toString。

  1. 如果任何操作数值是字符串,则进行字符串连接

  2. 否则,将两个操作数都转换为其数值,然后将这些值相加

了解JavaScript中各种类型的强制值确实有助于使混乱的输出更加清晰。参见下面的强制表

+-----------------+-------------------+---------------+
| Primitive Value |   String value    | Numeric value |
+-----------------+-------------------+---------------+
| null            | null            | 0             |
| undefined       | undefined       | NaN           |
| true            | true            | 1             |
| false           | false           | 0             |
| 123             | 123             | 123           |
| []              | “”                | 0             |
| {}              | “[object Object]” | NaN           |
+-----------------+-------------------+---------------+

最好知道JavaScript的+运算符是左关联的,因为这决定了输出涉及多个+运算的情况。

因此,利用1 + 2将得到“ 12”,因为涉及字符串的任何加法将始终默认为字符串连接。

您可以在此博客文章中阅读更多示例(我写了免责声明)。

本文地址:http://javascript.askforanswer.com/zaicodemash-2012de-watyanjiangzhongtidaodezhexieguaiyidejavascriptxingweiyouhejieshi.html
文章标签:
版权声明:本文为原创文章,版权归 javascript 所有,欢迎分享本文,转载请保留出处!

文件下载

老薛主机终身7折优惠码boke112

上一篇:
下一篇:

评论已关闭!