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