min less max

june 1 , 2011

This beauty is courtesy of TiTi ...lets look at some code.



    Math.max();
    // -Infinity

    Math.min();
    // Infinity

Ok, so, there is a good reason for this behaviour. It might even make sense if you happen to occasionally omit args from your min/max calls. ;)

You see, the min/max implementations need something to compare to and Infinity and -Infinity are the only safe values to use for that comparison. @kriskowell goes into more better detail here and was quickly followed by @brendaneich whom not only wrote js in 10 days but can rock out unicode Infinity symbols without looking them up ...I shit you not.

Of course, due to this behaviour js allows for this code humour:

Math.min() < Math.max();
// false

Oh JavaScript, I still love you.


all your commas are belong to Array

feb 5 , 2011

This installment of wtfjs has to do with the Abstract Equality Comparison Algorithm (as most do), Array's constructor, and expressions.

Let's take the following example:


    new Array([],null,undefined,null) == ",,,"; // true

WTF? Why does this work?

Firstly, the == causes type coersion (pretty common). From the ECMAScript Specification, 5th edition (final draft), 11.9.3 The Abstract Equality Comparison Algorithm:

The comparison x == y, where x and y are values produces true or false. Such a comparison is performed as follows:

[snip]

  1. If Type(x) is either String or Number and Type(y) is Object, return the result of the comparison x == ToPrimitive(y).
  2. If Type(x) is Object and Type(y) is either String or Number, return the result of the comparison ToPrimitive(x) == y.

So both uses of Array are run through ToPrimitive. But what does ToPrimitive do, exactly? Well, according to another part of the Final final final final draft Standard ECMA-262 5th edition (the document seriously has this title...), 9.1. ToPrimitive:

[snip]

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 behavior of the [[DefaultValue]] internal method is defined by this specification for all native ECMAScript objects in 8.12.8.

So we're hinting to the [[DefaultValue]] method within Array with the type of String, so according (again) to the spec, 8.12.8 [[DefaultValue]] (hint):

  1. Let toString be the results of calling the [[Get]] internal method of object O with argument "toString".

Unless of course, IsCallable(toString) (i.e. the object has a .toString method on it's prototype).

  1. If IsCallable(toString) is true, then, a. Let str be the results of calling the [[Call]] internal metho of toString with O as the this value and an empty argument list.

And according to 15.4.4.2 Array.prototype.toString ():

When the toString method is called, the following steps are taken:

  1. Let array be the result of calling ToObject on the this value.
  2. Let func be the result of calling the [[Get]] internal method of array with argument "join".

Oh, but we're not done yet!

Stay with me - we're type-coersing to a string, and Array.prototype.toString calls Array.prototype.join with no arguments, so we're joining all the internal members of the array with the default separator is the single-character String "," (again, according to the spec). When an Array calls join on itself, it's going from 1 .. len (all it's members) and calling ToString on these members and concatenating them together. Essentially doing this:

Array.prototype.join = function (separator) {
    var result = "";
    if ("undefined" === typeof separator) {
        separator = ",";
    }
    for (var k = 0, len = this.length; k < len; ++k && result += separator) {
        var isToS = this[k] !== null && this[k] !== undefined && "function" === typeof this[k].toString
        result += isToS ? this[k].toString() : String(this[k]);
    }
    return result;
};

So in the end, we end up with weird stuff like this actually working, as [], null, and undefined all result in "" when their respective ToPrimitive methods ask for [[DefaultValue]] with String as the type hint.

Another similar WTF on the same topic:


    ",,," == new Array(4); // true

This is similar, but not quite the same. When you call Array's constructor, if there are multiple arguments, they're intepretted as being members of the Array. If you've only put 1 Integer (n) as the argument, an Array object is initiatilized with (n) undefined items. Again, from the spec 15.4.2.2 new Array (len):

If the argument len is a Number and ToUint32(len) is equal to len, then the length property of the newly constructed object is set to ToUint32(len).

So essentially end up with


    [undefined,undefined,undefined,undefined].join(),

Which yields something like:


    "" + String(undefined) + "," + String(undefined) + "," + String(undefined) + "," + String(undefined)

Which ends up being ",,," (which evaluates to true, as it matches).

Lastly, adding just one more level of WTF to this post, you can also accidentally (or intetionally?) add an expression within Array's constructor function (and you can also omit new, as the spec also says: "When Array is called as a function rather than as a constructor, it creates and initialises a new Array object.").

So we can finally end up with the weirdest rendition of this WTF as so:


    ",,," == Array((null,'cool',false,NaN,4)); // true

If this doesn't make you WTF, I'm not sure what will.

@danbeam


convert to integer

dec 1 , 2010

The following will return an Ingeger with a default of 0 from any String or Number.


    function toInt(number) {
      return number && + number | 0 || 0;
    }
    console.log(toInt("1"));  // 1
    console.log(toInt("1.2"));  // 1
    console.log(toInt("-1.2"));  // -1
    console.log(toInt(1.2));  // 1
    console.log(toInt(0));  // 0
    console.log(toInt("0"));  // 0
    console.log(toInt(Number.NaN));  // 0
    console.log(toInt(1/0));  // 0

Explanation


    number && x

will return

x
if
number
is truthy, and
number
otherwise.


    x || 0

will return

x
if
x
is truthy, and 0 otherwise.

+ number
will convert number to a Number.

12.34 | 0
will convert 12.34 to the integer
12
. It will do it, as all binary operations can only be done on 32 bit integers in JavaScript.

-- @Poetro


false advertising

nov 3 , 2010

What do you think this constructor returns for new Dude('Bob')? Doug or Bob?


    function Dude(name){
        this.name = name;
        return {name: 'Doug'};
    }

Answer:


    var bob = new Dude('Bob');
    // { name: 'Doug' }
    bob instanceof Dude
    // false

Huh!? So you can just slip in anything? What about arrays?


    function Dude(name){
        this.name = name;
        return [1, 2, 3];
    }
    new Dude('Bob');
    // [1, 2, 3]

That can't be! What about...


    function Dude(name){
        this.name = name;
        return 3;
    }
    new Dude('Bob');
    // { name: 'Bob' }

Wah? No way! So, if you try to return a primitive type from a constructor(number, string, date), it will ignore the return value and return the originally initialized object, but otherwise, the returned value overrides.

--- @airportyh


Fork me on GitHub