Constructors in JavaScript objects

Solution 1:

Using prototypes:

function Box(color) // Constructor
{
    this.color = color;
}

Box.prototype.getColor = function()
{
    return this.color;
};

Hiding "color" (somewhat resembles a private member variable):

function Box(col)
{
   var color = col;

   this.getColor = function()
   {
       return color;
   };
}

Usage:

var blueBox = new Box("blue");
alert(blueBox.getColor()); // will alert blue

var greenBox = new Box("green");
alert(greenBox.getColor()); // will alert green

Solution 2:

Here's a template I sometimes use for OOP-similar behavior in JavaScript. As you can see, you can simulate private (both static and instance) members using closures. What new MyClass() will return is an object with only the properties assigned to the this object and in the prototype object of the "class."

var MyClass = (function () {
    // private static
    var nextId = 1;

    // constructor
    var cls = function () {
        // private
        var id = nextId++;
        var name = 'Unknown';

        // public (this instance only)
        this.get_id = function () { return id; };

        this.get_name = function () { return name; };
        this.set_name = function (value) {
            if (typeof value != 'string')
                throw 'Name must be a string';
            if (value.length < 2 || value.length > 20)
                throw 'Name must be 2-20 characters long.';
            name = value;
        };
    };

    // public static
    cls.get_nextId = function () {
        return nextId;
    };

    // public (shared across instances)
    cls.prototype = {
        announce: function () {
            alert('Hi there! My id is ' + this.get_id() + ' and my name is "' + this.get_name() + '"!\r\n' +
                  'The next fellow\'s id will be ' + MyClass.get_nextId() + '!');
        }
    };

    return cls;
})();

I've been asked about inheritance using this pattern, so here goes:

// It's a good idea to have a utility class to wire up inheritance.
function inherit(cls, superCls) {
    // We use an intermediary empty constructor to create an
    // inheritance chain, because using the super class' constructor
    // might have side effects.
    var construct = function () {};
    construct.prototype = superCls.prototype;
    cls.prototype = new construct;
    cls.prototype.constructor = cls;
    cls.super = superCls;
}

var MyChildClass = (function () {
    // constructor
    var cls = function (surName) {
        // Call super constructor on this instance (any arguments
        // to the constructor would go after "this" in call(…)).
        this.constructor.super.call(this);

        // Shadowing instance properties is a little bit less
        // intuitive, but can be done:
        var getName = this.get_name;

        // public (this instance only)
        this.get_name = function () {
            return getName.call(this) + ' ' + surName;
        };
    };
    inherit(cls, MyClass); // <-- important!

    return cls;
})();

And an example to use it all:

var bob = new MyClass();
bob.set_name('Bob');
bob.announce(); // id is 1, name shows as "Bob"

var john = new MyChildClass('Doe');
john.set_name('John');
john.announce(); // id is 2, name shows as "John Doe"

alert(john instanceof MyClass); // true

As you can see, the classes correctly interact with each other (they share the static id from MyClass, the announce method uses the correct get_name method, etc.)

One thing to note is the need to shadow instance properties. You can actually make the inherit function go through all instance properties (using hasOwnProperty) that are functions, and automagically add a super_<method name> property. This would let you call this.super_get_name() instead of storing it in a temporary value and calling it bound using call.

For methods on the prototype you don't need to worry about the above though, if you want to access the super class' prototype methods, you can just call this.constructor.super.prototype.methodName. If you want to make it less verbose you can of course add convenience properties. :)