Should I be using object literals or constructor functions?

It essentially boils down to if you need multiple instances of your object or not; object defined with a constructor lets you have multiple instances of that object. Object literals are basically singletons with variables/methods that are all public.

// define the objects:
var objLit = {
  x: 0,
  y: 0,
  z: 0,
  add: function () {
    return this.x + this.y + this.z;
  }
};

var ObjCon = function(_x, _y, _z) {
  var x = _x; // private
  var y = _y; // private
  this.z = _z; // public
  this.add = function () {
    return x + y + this.z; // note x, y doesn't need this.
  };
};

// use the objects:
objLit.x = 3; 
objLit.y = 2; 
objLit.z = 1; 
console.log(objLit.add());    

var objConIntance = new ObjCon(5,4,3); // instantiate an objCon
console.log(objConIntance.add());
console.log((new ObjCon(7,8,9)).add()); // another instance of objCon
console.log(objConIntance.add()); // same result, not affected by previous line

The code below shows three methods of creating an object, Object Literal syntax, a Function Constructor and Object.create(). Object literal syntax simply creates and object on the fly and as such its __prototype__ is the Object object and it will have access to all the properties and methods of Object. Strictly from a design pattern perspective a simple Object literal should be used to store a single instance of data.

The function constructor has a special property named .prototype. This property will become the __prototype__ of any objects created by the function constructor. All properties and methods added to the .prototype property of a function constructor will be available to all objects it creates. A constructor should be used if you require multiple instances of the data or require behavior from your object. Note the function constructor is also best used when you want to simulate a private/public development pattern. Remember to put all shared methods on the .prototype so they wont be created in each object instance.

Creating objects with Object.create() utilizes an object literal as a __prototype__ for the objects created by this method. All properties and methods added to the object literal will be available to all objects created from it through true prototypal inheritance. This is my preferred method.

//Object Example

//Simple Object Literal
var mySimpleObj = {
    prop1 : "value",
    prop2 : "value"
}

// Function Constructor
function PersonObjConstr()  {
    var privateProp = "this is private";
    this.firstname = "John";
    this.lastname = "Doe";
}
PersonObjConstr.prototype.greetFullName = function()    {
    return "PersonObjConstr says: Hello " + this.firstname + 
    " " + this.lastname;
};

// Object Literal
var personObjLit = {
    firstname : "John",
    lastname: "Doe",
    greetFullName : function() {
        return "personObjLit says: Hello " + this.firstname +
        ", " + this.lastname;
    }
} 

var newVar = mySimpleObj.prop1;
var newName = new PersonObjConstr();
var newName2 = Object.create(personObjLit);

If you don't have behaviour associated with an object (i.e. if the object is just a container for data/state), I would use an object literal.

var data = {
    foo: 42,
    bar: 43
};

Apply the KISS principle. If you don't need anything beyond a simple container of data, go with a simple literal.

If you want to add behaviour to your object, you can go with a constructor and add methods to the object during construction or give your class a prototype.

function MyData(foo, bar) {
    this.foo = foo;
    this.bar = bar;

    this.verify = function () {
        return this.foo === this.bar;
    };
}

// or:
MyData.prototype.verify = function () {
    return this.foo === this.bar;
};

A class like this also acts like a schema for your data object: You now have some sort of contract (through the constructor) what properties the object initializes/contains. A free literal is just an amorphous blob of data.

You might as well have an external verify function that acts on a plain old data object:

var data = {
    foo: 42,
    bar: 43
};

function verify(data) {
    return data.foo === data.bar;
}

However, this is not favorable with regards to encapsulation: Ideally, all the data + behaviour associated with an entity should live together.


Another way to create objects in a uniform way is to use a function that returns an object:

function makeObject() {
    var that = {
        thisIsPublic: "a public variable"
        thisIsAlsoPublic: function () {
            alert(that.thisIsPublic);
        }
    };

    var secret = "this is a private variable"

    function secretFunction() { // private method
        secret += "!"; // can manipulate private variables
        that.thisIsPublic = "foo";     
    }

    that.publicMethod = function () {
        secret += "?"; // this method can also mess with private variables
    }

    that.anotherPublicVariable = "baz";

    return that; // this is the object we've constructed
}

makeObject.static = "This can be used to add a static varaible/method";

var bar = makeObject();
bar.publicMethod(); // ok
alert(bar.thisIsPublic); // ok
bar.secretFunction(); // error!
bar.secret // error!

Since functions in JavaScript are closures we can use private variables and methods and avoid new.

From http://javascript.crockford.com/private.html on private variables in JavaScript.

Tags:

Javascript