28 May, 2009

Constructors and "this".

2009/08/14 Update:
It has come to my attention that there is a better way of doing this:

var Point = function (x, y) {
    if (this === window) {
        return new Point(x, y);
    }
    this.x = x;
    this.y = y;
};
Although this article is still useful, please use the above, instead of using call.


The this keyword in JavaScript is very powerful, when used correctly. Unfortunately, JavaScript has a bit of infatuation with the global scope, so most of the time this isn't very helpful at all.

You can rebind this with the methods call() and apply(). Ordinarily, you wouldn't want to do this; however, there is one interesting case.

JavaScript constructors look like this:

// Constructor:
var Point = function (x, y) {
    this.x = x;
    this.y = y;
};

// Instance:
var point = new Point(10, 5);

When you use the new keyword, this is bound to the newly created object, which makes it easy to set up properties on the new object.

However, if you fail to use the new keyword, this will bind the properties to the global object! This is a serious mistake. You can correct this with call() or apply().

Now you can write your constructors like this:

// Constructor:
var Point = function anon(x, y) {
    if (this === window) {
        return anon.call({});
    }
    this.x = x;
    this.y = y;
    return this;
};

// Instance:
var point = new Point(10, 5);

// Instance:
var point = Point(10, 5);

The if and return this; statements are mandatory. This is used to ensure that if you accidentally leave off new, it will not bind the properties to the global object.

Of course, you could have written it like this:

var Point = function (x, y) {
    return {
        x: x,
        y: y
    };
};

Which has the same effect. However, now you give up the ability to use call and apply to change the this binding. In addition, you can't use prototypical inheritance anymore! If you don't care about that, then feel free to write your constructors without this.

However, if you do care, then you can use the above construct to allow the use of this, without worrying about binding to the global object.

This will no longer apply in ECMAScript 5 strict mode, which throws an error when you try to use a constructor without new.

No comments:

Post a Comment