How to emulate a constructor with ES5 Object.create and object literal syntax?

Seth Ladd picture Seth Ladd · Jan 31, 2011 · Viewed 9.4k times · Source

Presume I have an object like this:

var Foo = {
  x: 5,
  sprite: new Image()
}

Problem: I want to initialize that sprite with the right src. However, when I use the following creation technique:

var f = Object.create(Foo);

I don't have a constructor method (aka init function) to setup sprite.src = 'cool.png';

My question:

If I am using the object literal technique, and Object.create(), when do I actually initialize some of my internal state (like the example of the new Image())

My solution:

var Foo = {
  create: function() {
    var f = Object.create(Foo);
    f.sprite.src = 'cool.png';
    return f;
  }
}

However, I don't know if that's a great pattern. I'd like to do this the "JavaScript Way" if there is a way. :)

Thanks!

Answer

Sean McMillan picture Sean McMillan · Apr 5, 2011

I do something very similar to what you've written above, but I combine it with the module pattern:

var Vehicle = (function(){
        var exports = {};
        exports.prototype = {};
        exports.prototype.init = function() {
                this.mph = 5;
        };
        exports.prototype.go = function() {
                console.log("Going " + this.mph.toString() + " mph.");
        };

        exports.create = function() {
                var ret = Object.create(exports.prototype);
                ret.init();
                return ret;
        };

        return exports;
})();

From the outside, this exposes Vehicle.create() and Vehicle.prototype. Then if I want to make a Derived type, I can do this:

var Car = (function () {
        var exports = {};
        exports.prototype = Object.create(Vehicle.prototype);
        exports.prototype.init = function() {
                Vehicle.prototype.init.apply(this, arguments);
                this.wheels = 4;
        };

        exports.create = function() {
                var ret = Object.create(exports.prototype);
                ret.init();
                return ret;
        };

        return exports; 

})();

This pattern lets me derive types without making the error of Car.prototype = new Vehicle(), which is fail if my constructors take parameters.