0
! + [] === true

Can someone explain what's happening here? I understand this is not normal, but I'm struggling to understand what the compiler is doing when it seems operators used in place of variables in mathematical operations.

more examples of this strangeness:

(! + '') === (!'')

but

(! + []) !== (![])
Get Off My Lawn
  • 34,175
  • 38
  • 176
  • 338
  • 2
    Read it like this: `!(+[])`. I suggest you just add all the examples to https://astexplorer.net/, and see what operators are being used in what order. – ASDFGerte Jul 05 '18 at 16:54
  • 1
    What's _not_ happening here is operators being used as variables. Things like `[]` and `{}` are *values* in any expression context, just like `3` and `true` are. The operators like `===`, `!`, etc are being used as operators. – Pointy Jul 05 '18 at 17:03
  • 1
    @ASDFGerte 's comment made it make sense to me. I should read it as (+[]) which coerces an object -> string -> number. – Carson the Powers Jul 05 '18 at 17:10

2 Answers2

0

JavaScript always convert inputs to Primitive types. So in your case of ! + [] it will try to convert +[] to string. which will be "" and then because of ! it will convert into boolean. in JavaScript "" is consider as false. So ultimately it will return !false which will be true.

Edit 1

As @mhodges has commented below. [] getting convert to string as "" then + will convert it to number, so it will become 0.

You can learn more about object to primitive from here. object to primitive

You can check behavior below.

var emptyBrackets = [].toString();
// conversion to string.
console.log(emptyBrackets === '');
// conversion to number.
console.log(+emptyBrackets);
// conversion to boolean.
console.log(!0);
Karan
  • 12,059
  • 3
  • 24
  • 40
  • 1
    Isn't the `+` being used to convert to a number here? `+"" === 0`, then `!0 === true` – mhodges Jul 05 '18 at 17:02
  • 1
    @Keatinge I think you're partially correct. I think Karan is right in that [] is converted to `""` before being coerced to a number by `+` as you said. – mhodges Jul 05 '18 at 17:04
  • 1
    The **very** important missing piece to this answer is that the `+` is being used to coerce `""` into a number (`0`). Saying that "it will try to convert `+[]` to a string" is just flat wrong. – mhodges Jul 05 '18 at 17:08
  • 1
    @mhodges all the coercions that are being talked about here are happening because of `+`, but yes, internally, it will be coerced to string before being again coerced to number. The text implies that `+[] === ""` though, which is simply wrong (apart from several other not-so-well-formumated parts, see "JavaScript always convert inputs to Primitive types" - what?). I don't understand why this even gets upvoted. – ASDFGerte Jul 05 '18 at 17:09
  • @mhodges. Thanks for drawing my attention about `+`. I have updated answer accordingly. – Karan Jul 05 '18 at 17:18
  • 1
    @Karan Close, still not entirely accurate. There is no such thing as an `int` in JavaScript - it's a `Number` type. Coercing the array (via `+`) will call `.toPrimitive()` on the empty array internally, which converts it to its [default value](http://www.ecma-international.org/ecma-262/5.1/#sec-8.12.8) (an empty string), before coercing it to a Number with `+`. It's a matter of semantics, but with questions like this, semantics are extremely important – mhodges Jul 05 '18 at 17:38
  • 1
    The part about `+[] => 0` is explained in [this question](https://stackoverflow.com/questions/36101362/explain-why-0-output-true-in-javascript?noredirect=1&lq=1) – Barmar Jul 05 '18 at 17:42
0

First, it is important to know what operators are being used, and in what order. When this is not clear, an easy way to find out is to look at what the syntax parser makes out of it, e.g. using https://astexplorer.net/, which shows for the first example that:

! + [] === true uses unary logical not (!), unary plus (+), an array literal which is empty ([]), strict equality (===) and the keyword for true (true).

Execution order is (!(+[])) === true, or seen as a tree:

   ===
 /     \
!     true
|
+
|
[]

What follows is to understand, what each of the operators are transforming their operands to. This is a bit tedious, when you want to know in detail.

I assume you know what an empty array literal or true produce. Strict equality === is likely also known, as it does not have very many unintuitive aspects (contrary to loose equality ==, which has a LOT). Relevant are therefore only the steps of the unary + and unary logical not.

Given the order of operations, we first look at unary + with [] as operand. It performs ToNumber on the operand, which, for objects like our example [], performs ToPrimitive followed by another ToNumber. ToPrimitive will try valueOf (which will not return a primitive and is therefore not taken), followed by toString. The latter, for arrays, searches for the function join and calls it without arguments (setting the array as this). For an empty array, this results in the empty string "", which will be converted to 0 by the following second call of ToNumber.

Next is the unary logical not, which first transforms any operand with ToBoolean. This intermediary step results in false for our operand 0. Then, as expected, the opposite is being returned (true).

Last but least, as we know, true === true evaluates to true.

An interesting sidenote here is the use of Array.prototype.join in the default case. I would expect many to be surprised, why changes to join would affect the outcome:

console.log(! + [] === true);

let x = [];
x.join = () => "1";
console.log(! + x === true);

Array.prototype.join = () => "1";
console.log(! + [] === true);

The other examples work in a similar way:

  • (! + '') is almost the same, just that ToNumber does not even need the prior ToPrimitive

  • For (!''), ToBoolean of an empty string is false, therefore resulting in true

  • For (![]), ToBoolean of any object is true, resulting in false

ASDFGerte
  • 4,695
  • 6
  • 16
  • 33