Oliver Nassar

__defineGetter__ & __defineSetter__ in IE9

May 30, 2011

Found myself in a situation in which I needed to set getters/setters for an instance's properties. Using JS, the technique I inherited was as follows:

HTMLCanvasElement.prototype.__defineGetter__("bitmapData", function() { 
    if(!this._bitmapData) {
        this._bitmapData = new BitmapData(this.width, this.height, false, 0, this);
    }
    return this._bitmapData;
});

Unfortunately, although Internet Explorer 9 is light-years ahead of it's predecessors, it doesn't support this method. But after looking into it, there's a reason it doesn't; it actually supports a much cleaner API for dynamic getters and setters. This can be seen as follows:

Object.defineProperty(obj, 'property', {
    get: function() {
        return 'value';
    },
    set: function(value) {
        // set-logic
    }
});

The above code will operate on the obj-object, dynamically changing the 'property'-property.
So for example.

clear();
var obj = {name: 'Oliver'};
console.log(obj.name);
Object.defineProperty(obj, 'name', {
    get: function() {
        return 'Nassar';
    }
});
console.log(obj.name);

The above code will log, in-order, the strings 'Oliver' and 'Nassar'. An issue I ran into was setting the getters and setters for a property independently. For example:

clear();
var obj = {name: 'Oliver'};
Object.defineProperty(obj, 'name', {
    get: function() {
        return 'Nassar';
    }
});
Object.defineProperty(obj, 'name', {
    set: function(value) {
        // set-logic
    }
});

This isn't allowed, and IE9 will throw an error. Something like 'cannot redefine defineProperty'. You need to set the dynamic-properties of the object in one-call as follows:

clear();
var obj = {name: 'Oliver'};
Object.defineProperty(obj, 'name', {
    get: function() {
        return 'Nassar';
    },
    set: function(value) {
        // set-logic
    }
});

For the exact signature of this method, check out: defineProperty - MDC Docs

Finally, to pass this getter/setter onto an instantiable-class, I used the following syntax:

Object.defineProperty(klass.prototype, 'property', {
    get: function() {
        // get-logic
    }
});

I tested the above on Firefox 4+, Chrome 11+ and IE9. I don't know how far back it goes, but it's good on those modern-browsers.

Hope this helps :)