array comparison

jan 28 , 2013

Did you know that JavaScript can compare arrays using lexicographical ordering?

  [1, 2, 4] < [1, 2, 5]  // true
  [1, 3, 4] < [1, 2, 5]  // false

Just don't expect trichotomy to hold.

  [1, 2, 3] === [1, 2, 3]   // false
  [1, 2, 3] <   [1, 2, 3]   // false
  [1, 2, 3] ==  [1, 2, 3]   // false
  [1, 2, 3] >   [1, 2, 3]   // false

Oh, and just in case you're wondering, it knows it's messing with you.

  [1, 2, 3] <= [1, 2, 3]   // true
  [1, 2, 3] >= [1, 2, 3]   // true

@pwnall


undefined props on numbers

dec 28 , 2012
    function getBounds(node) {
        var n = node || 0;
        return { width: n.width, height: n.height };
    }

If you call getBounds() with null, you'll get back { width: undefined, height: undefined } because in JS, numbers have properties (inherited from Number), and using an undefined property doesn't throw, it just returns undefined. null and undefined are propertyless. Definitely not what I expected in a quick reading of code like this.

@unwiredben


infinity madness

oct 10 , 2012
    parseFloat( 'Infinity' ) // returns Infinity
    Number( 'Infinity' ) // returns Infinity
    parseInt( 'Infinity' ) // returns NaN, of course. With any radix passed as well.

@fivetanley


Hexadecimal weirdness

sept 19 , 2012

In JS you can represent numbers in hexadecimal, right?


    var hex = 0xFF55;

You can also perform shift operations, right? Left shift is equivalent to a multiplication...

    var hex = 0xFF55 << 8;  // Shift 8 bits = add 0x00 at the end.
    alert(hex.toString(16)); // 0xFF5500

But from a certain point, this produces negative numbers

    // Before 0x800000 it's ok
    alert((0x777777 << 8).toString(16)); // 0x77777700

    // After 0x800000 it's not ok
    alert((0x888888 << 8).toString(16)); // -77777800, WTF?

    // The only way to remain positive is to multiply instead of shifting
    alert((0x888888 * 0x100).toString(16)); // 88888800

Thanks JS for making left shift different than a multiplication!

I do have an explanation!

ES5 states that the left-shift operator is a signed shift on 32 bits represented by two's complement which means that the highest order bit defines the sign.

7 in hexa = 0111 in binary (every number below 7 starts with 0) 8 in hexa = 1000 in binary (every number above 8 starts with 1)

So in binary:

(0x777777 << 8) in hexa = 0111 0111 0111 0111 0111 0111 0111 0000 0000 in binary (the 32nd bit is still 0)

(0x888888 << 8) in hexa = 1000 1000 1000 1000 1000 1000 0000 0000 in binary (the 32nd bit becomes 1 => negative)

As for why multiplying 0x888888 0x100 yields a different result, both operands to the operator are numeric, so each is casted to a IEEE 64-bit double, then multiplied, which means this is what is really happening:

    0x888888 * 0x100 === 8947848.0 * 256.0 === 2290649088.0

When this is converted into a string with base 16, what you get is 88888800.

@MaximeEuziere


Slashes!

aug 11 , 2012

What should the following JS yield?


    n = 1
    /1*"\/\//.test(n + '"//')

Should it be true? (It is NaN.)

Using semicolons, on the other hand, makes it behave like you'd expect:


    n = 1;
    /1*"\/\//.test(n + '"//');

(Returns true.)

The fun around this WTFJS comes from trying to understand how this should be parsed, so please have fun experimenting with the snippet of code.

When you're done understanding the different levels on which it plays, read on.

The trick revolves around the different cases in which the slash character has meaning. There are three cases: comments, regular expressions, and division, all of which are meant to be on display here.

In the first, semicolon-free snippet, the first slash on the second line is a division. We divide 1 by 1, multiply it by a long string, and after that there is a comment.

In the second snippet, with semicolons, the second line is a regex, tested against the global variable in the first line, transmuted to a string via concatenation.

An interesting variation that shows what happens more clearly is the following:


    n = 1
    /1?":n\/\//.test(n + '":n//')

That returns ":n///.test(n + '". It gives the trick away. (It uses the ternary operator ?:, if you're wondering.)

Obviously, if you really want your friends to have a headache, give them this version:


    n = 0
    /0?":n\/\//.test(n + '":n//')

That returns undefined, which is even more obscure than the NaN that we got in the first snippet. Yes, 0/0 is NaN, which is falsy.

@espadrine.


Fork me on GitHub