How does the ~[] construction work in JavaScript?

I’ve come across a working JavaScript code that I can’t explain.
For example:

  • +[]===0
  • -[]===0
  • ~[]===-1
  • ~-~[]===-2
  • ~-~-~-~-~[]===-5
  • ~-~-~-~-~[]+~[]===-6
  • ~+~[]===0
  • ~+~+~[]===-1
  • ~+~+~+~[]===0

Can you explain the logic of these expressions?

  • use brackets in checkbox name when using php and javascript?
  • What is this JavaScript construct: “ = [y for each …]”
  • Rules for the use of angle brackets in Typescript
  • Why () are needed in this Javascript code block?
  • Fix Sublime Text 2 brackets matching with > 28 if()-tests?
  • difference between dot notation and bracket notation in javascript
  • Curly Brackets in Arrow Functions
  • What is this JavaScript construct: “ = [y for each …]”
  • use brackets in checkbox name when using php and javascript?
  • get all inner brackets/parentheses
  • JavaScript: comma after opening bracket of parameter in syntax example
  • Curly Brakets in Es2015
  • 5 Solutions collect form web for “How does the ~[] construction work in JavaScript?”

    [] is an empty array object, so:

    +[]: force empty array to be positive integer, aka 0, which is === to 0
    -[]: force empty array to be negative integer, aka 0, which is === to 0
    ~[]: bitwise NOT empty array, which evaluates to -1, which is === to -1
    ~-~[]: bitwise NOT of negated NOTted empty array: ~-(-1) -> ~1 -> -2

    etc…

    When you use the + or - operator on anything, it calls Number on it. Number([]) returns 0, so you get your first two answers.

    The ~ operator is bitwise NOT. Basically it inverses all the bits in a number, which changes 0 to -1. More on bitwise operators here.

    The rest are just combinations of those cases. You can pretty much combine those things to make any number you want.

    Instead of just restating the result you demonstrated in the question, I’ll try to give a description of why you get that result.

    Explanation

    Taking only the first example (because the rest will do something similar), we can follow the specification to see what happens.

    From 11.4.6 Unary + Operator, we can see that a ToNumber conversion takes place.

    Return ToNumber(GetValue(expr)).

    From 9.3 ToNumber we see that if given an Object (like your Array), a ToPrimitive conversion takes place followed by another attempt at ToNumber.

    Object

    Apply the following steps:

    1. Let primValue be ToPrimitive(input argument, hint Number).
    2. Return ToNumber(primValue).

    From 9.1 To Primitive, if ToPrimitive gets an Object, we can see that its [[DefaultValue]] is fetched:

    Object

    Return a default value for the Object. The default value of an object is retrieved by calling the [[DefaultValue]] internal method of the object, passing the optional hint PreferredType. The behaviour of the [[DefaultValue]] internal method is defined by this specification for all native ECMAScript objects in 8.12.8.

    From 8.12.8 [[DefaultValue]] (hint), what will ultimately happen will be that toString() will be called on the Array, and returned. This string gets sent to the recursive ToNumber as described above.

    So what happens when ToNumber conversion is done on a String? Well it is described in 9.3.1 ToNumber Applied to the String Type, and is a bit lengthy. Simpler is to just do the conversion directly, and see what happens:

    Number("");     // result is 0 on an empty string
    Number("    "); // result is 0 on a string with only whitespace
    Number("123");  // result is 123 on a numeric string
    Number(" 123 ");// result is 123 on a numeric string with leading & trailing spaces
    Number("abc");  // result is NaN (not a number) on non-numeric strings
    

    So the question is, what string do we get back from our Array. Again, this is easy to simply test.

    [].toString();  // result is "" (empty string)
    

    Since the result is an empty string, and a ToNumber conversion of an empty string is 0 as shown above, that would mean we are comparing 0 === 0.

    It would be the same as if we did:

    Number( [].toString() ) === 0; // true
    

    Or to draw it out a bit more:

    var x = [];
    x = x.toString();  // ""
    x = Number( x );   // 0
    x === 0;  // true
    

    More toString results.

    To show more toString conversions of Arrays, consider the following:

    [1].toString();            // "1"
    [1,2,3].toString();        // "1,2,3"
    ["a",1,"b",2].toString();  // "a,1,b,2"
    

    So to do a ToNumber conversion on the above Arrays, the first would give us a number, and the last two would result in NaN.

    Number([1]);            // 1
    Number([1,2,3]);        // NaN
    Number(["a",1,"b",2]);  // NaN
    

    Some proof

    To provide further proof that this toString() conversion happens before the ToNumber conversion, we can actually modify Array.prototype.toString to provide a different result, and any ToNumber conversions will use that modified result.

    Array.prototype.toString = function() {
        var n = 0;
        for( var i = 0; i < this.length; i++ ) {
            n += this[i];
        }
        return n;
    };
    

    Here I’ve replaced the toString on Array.prototype with a function that sums the Array. Obviously you don’t want to do this, but we can show how we will now get a different result.

    Number([1,2,3]);   // 6
    +[1,2,3];          // 6
    

    So now you can see that the ToNumber conversion of our Array that would previously have resulted in NaN is now resulting in the sum of the items in the Array.

    I’ll do my best:

    []===0 is of course false because [] is not exactly equal to 0. However, []==0 is true because an implicit cast exists.

    + and -[] work because the plus or minus casts [] to a real number.

    ~0 (the bitwise inverse of 0) is -1. Thus ~[]===-1 works.

    The others work just by subtracting or adding -1 a bunch of times.

    I believe, correct me if I’m wrong, but adding to an array (as in, adding a value to an array rather than adding a value into an array) casts it to a number. The rest is just using basic +, – operators (the tilde (~) is a bitwise NOT) to modify the number and then an equation.

    So [] == array ([]);
    [] + 1 == number (0);
    +[]===0 (true)