What Is the Explanation For These Bizarre JavaScript Behaviours Mentioned in the 'Wat' Talk For Codemash 2012

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

Here's a list of explanations for the results you're seeing (and supposed to be seeing). The references I'm using are from the ECMA-262 standard.

  1. [] + []

    When using the addition operator, both the left and right operands are converted to primitives first (§11.6.1). As per §9.1, converting an object (in this case an array) to a primitive returns its default value, which for objects with a valid toString() method is the result of calling object.toString() (§8.12.8). For arrays this is the same as calling array.join() (§15.4.4.2). Joining an empty array results in an empty string, so step #7 of the addition operator returns the concatenation of two empty strings, which is the empty string.

  2. [] + {}

    Similar to [] + [], both operands are converted to primitives first. For "Object objects" (§15.2), this is again the result of calling object.toString(), which for non-null, non-undefined objects is "[object Object]" (§15.2.4.2).

  3. {} + []

    The {} here is not parsed as an object, but instead as an empty block (§12.1, at least as long as you're not forcing that statement to be an expression, but more about that later). The return value of empty blocks is empty, so the result of that statement is the same as +[]. The unary + operator (§11.4.6) returns ToNumber(ToPrimitive(operand)). As we already know, ToPrimitive([]) is the empty string, and according to §9.3.1, ToNumber("") is 0.

  4. {} + {}

    Similar to the previous case, the first {} is parsed as a block with empty return value. Again, +{} is the same as ToNumber(ToPrimitive({})), and ToPrimitive({}) is "[object Object]" (see [] + {}). So to get the result of +{}, we have to apply ToNumber on the string "[object Object]". When following the steps from §9.3.1, we get NaN as a result:

    If the grammar cannot interpret the String as an expansion of StringNumericLiteral, then the result of ToNumber is NaN.

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

    As per §15.4.1.1 and §15.4.2.2, Array(16) creates a new array with length 16. To get the value of the argument to join, §11.6.2 steps #5 and #6 show that we have to convert both operands to a number using ToNumber. ToNumber(1) is simply 1 (§9.3), whereas ToNumber("wat") again is NaN as per §9.3.1. Following step 7 of §11.6.2, §11.6.3 dictates that

    If either operand is NaN, the result is NaN.

    So the argument to Array(16).join is NaN. Following §15.4.4.5 (Array.prototype.join), we have to call ToString on the argument, which is "NaN" (§9.8.1):

    If m is NaN, return the String "NaN".

    Following step 10 of §15.4.4.5, we get 15 repetitions of the concatenation of "NaN" and the empty string, which equals the result you're seeing.
    When using "wat" + 1 instead of "wat" - 1 as argument, the addition operator converts 1 to a string instead of converting "wat" to a number, so it effectively calls Array(16).join("wat1").

As to why you're seeing different results for the {} + [] case: When using it as a function argument, you're forcing the statement to be an ExpressionStatement, which makes it impossible to parse {} as empty block, so it's instead parsed as an empty object literal.

What does this expression (({}+[])[+[]]) mean in JavaScript?

This does nothing useful. Like you said, it gets [0] from the result of ({}+[]), which is [:

  1. ({}+[]) == '[object Object]' because you're concatenating two objects that don't have a regular way of adding together, so this just returns that the thing between the brackets is an object,
  2. '[object Object]'[0] == '[' because [0] always gets the first character of a string (strings are treated as arrays containing single letters).

Considering your second part of the question, it doesn't work because there are two plus signs next to each other, so it assumes you're using postfix incrementation (like i++ in loops). Simply adding a space between the spaces will fix this:

console.log(+!![]+ +!![])
> 2

but, since you're adding the boolean value of the array anyway, which is 1 here, you could also leave the prefix + out, since the expression already converts this boolean value to a number:

console.log(!![] + !![])
> 2

The !! part simply converts the thing coming after it to a boolean. Since !myVar returns a boolean opposite to the boolean value of myVar, !(!myVar), which is the same as !!myVar would return a boolean opposite of that. In this case, myVar would be your empty array [].

How can {} + [] and [] + {} results be different

This is because the {} in the code is not an object literal, but an empty block.

It is parsed as:

{};   // empty block
+ []; // this result is shown in the console

Compare with ({}) + [] which yields the same results as [] + {}; in this case the parenthesis force the {} to be treated/parsed "in an expression context".

There are a bunch of duplicates on SO about this particular dual-nature of {} (as an expression or block?) but, finding them can be somewhat tricky ..


I found https://meta.stackexchange.com/questions/83911/how-do-i-search-stackoverflow-for-at-keywords-like-private-or-synthesize on Meta, and using Symbolhound the "closest" duplicates I could find resolved around questions like this (that relate to the need to add parenthesis when "eval'ing JSON") or this (where the use of constructs like "{} == false" is a syntax error).

If anyone knows a better way to search SO for this sort question, or has a link to such a duplicate handy ..



Related Topics



Leave a reply



Submit