Object Literals vs. Constructed Objects in JavaScript

November 9, 2009Justin5 Comments

Another discussion born from a question at stackoverflow.  Today I’m laying out the differences between object literals and constructed objects, when to use which, how to get the most out of them, and a few other tricks along the way.  Let’s get started.

Object Literals / Static Objects

Object literals, or static objects, don’t require instantiation with the new operator and also behave like singletons. They are often used for providing utility classes since methods can be called statically, or as parameter packages to provide named arguments or lists to other functions. Consider the following example that illustrates this singleton property of object literals:

Example 1: Static object singleton behavior

var staticObject1 = {
    a: 123,
    b: 456
};

var staticObject2 = staticObject1;
staticObject2.b = "hats";
console.log(staticObject1, staticObject2);

Output:

Object a=123 b=456  Object a=123 b=456
Object a=123 b=hats Object a=123 b=hats

Notice that changing staticObject1.b also affected staticObject2.b. This happens because in JavaScript, when you assign an object to another variable, it simply creates a reference.

The singleton behavior may not always be the desired effect. Many libraries, such as Dojo, offer an object cloning method that can avoid this behavior if just you want to make a copy of a static object. Continuing the previous example, consider the following:

Example 2: Circumventing the singleton behavior

var staticObject3 = dojo.clone(staticObject1); // See the documentation in the link above
staticObject1.a = "pants";
console.log(staticObject1, staticObject2, staticObject3);

Output:

Object a=pants b=hats Object a=pants b=hats Object a=123 b=hats

Notice that the values of the members of staticObject1 and staticObject2 are the same, whereas staticObject3 is not affected by changes to these other objects.

Static objects are also useful for creating project or library namespaces, rather than filling up the global scope, and promote compatibility like no one’s business. This is useful when creating libraries that require portability or interoperability. This can be seen in popular libraries such as Dojo, YUI, ExtJs, and others where all or most methods are called within that library’s specific context (e.g.: dojo.examplMethod(), YUI().exampleMethod(), and Ext.exampleMethod() respectively).

One thing to remember is that in object literals, all members and methods are  necessarily public. Forever and always, so help me Douglas Crockford.  However, this is not the case for constructed objects, as you’ll see shortly.  Access restrictions notwithstanding, static objects can also be considered loosely analogous in purpose and use to C/C++'s struct type.

Constructed Objects / Instantiated Objects

Classes in JavaScript are based on prototypal inheritance, which can be a lengthy subject in itself and can be read about here, here, and here. For now, we will just compare the differences between instantiated and static objects and leave inheritance for another day.

As opposed to static objects, this method of object creation gives the unique opportunity for private scope object members and methods thanks to JavaScript’s closure property. Consider the following example of private class members:

Example 3: Private class members

var SomeObject = function() {
    var privateMember = "I am a private member";
    this.publicMember = "I am a public member";

    this.publicMethod = function() {
        console.log(privateMember, this.publicMember);
    };
};

var o = new SomeObject();
console.log(typeof o.privateMember, typeof o.publicMember);
o.publicMethod();

Output:

undefined string
I am a private member I am a public member

Notice that typeof o.privateMember is “undefined” and not accessible outside of the object, but is from within.

Private methods can also be defined. They are not as straight forward but are still simple to implement. The issue lies in that the value of this within a private method will refer to window if you try to execute it as if it were just any method. There are at least three techniques that can be applied to ensure that this refers to the object that we are working within, in this case, the instance of SomeObject. Consider the following example:

Example 4: Private class methods with call

var SomeObject = function() {
    var privateMember = "I am a private member";
    var privateMethod = function() {
        console.log(privateMember, this.publicMember);
    };

    this.publicMember = "I am a public member";
    this.publicMethod = function() {
        console.log(privateMember, this.publicMember);
    };
    this.privateMethodWrapper = function() {
        privateMethod.call(this);
    }
};

var o = new SomeObject();
console.log(typeof o.privateMethod, typeof o.publicMethod, typeof o.privateMethodWrapper);
o.privateMethodWrapper();

Output:

undefined function function
I am a private member I am a public member

Notice that within privateMethodWrapper(), privatemethod was executed using call and passing in this for the function’s context. This is all fine and allows us to use this within the private method as we normally would; however, the following technique is slightly more preferable as it simplifies the calling context. The previous example can be rearranged to the following:

Example 5: Private class methods with closure reference to this

var SomeObject = function() {
    var self          = this;
    var privateMember = "I am a private member";
    var privateMethod = function() {
        console.log(self.publicMember);
    };

    this.publicMember = "I am a public member";
    this.publicMethod = function() {
        console.log(privateMember, this.publicMember);
    };
    this.privateMethodWrapper = function() {
        privateMethod();
    }
};

var o = new SomeObject();
console.log(typeof o.privateMethod, typeof o.publicMethod, typeof o.privateMethodWrapper);
o.privateMethodWrapper();

Output:

undefined function function
I am a private member I am a public member

Notice that a new private member, self, was introduced and provides a reference to this within privateMethod via the closure property. This method is agreeable for most situations; however, my syntax highlighter doesn’t highlight self the same as it does this, so I prefer the following method.

The third method effectively “locks in” the value of this within the private method. It is the most preferable in that it allows you to have a normal definition using this with the private method as in example #4, and a clean calling context as in example #5. Both the Dojo and Prototype libraries offer utility methods to do this for us: dojo.hitch and function.prototype.bind respectively. I will show how to use these methods, as well as how write your own utility method for achieving the same results for those of you not using those libraries.

Example 6: Private class methods with context locking using dojo.hitch

var SomeObject = function() {
    var privateMember = "I am a private member";
    var privateMethod = dojo.hitch(this, function() {
        console.log(privateMember, this.publicMember);
    });

    this.publicMember = "I am a public member";
    this.publicMethod = function() {
        console.log(privateMember, this.publicMember);
    };
    this.privateMethodWrapper = function() {
        privateMethod();
    }
};

var o = new SomeObject();
o.privateMethodWrapper();

Output:

I am a private member I am a public member

Example 7: Private class methods with context locking using Prototype’s bind

var SomeObject = function() {
    var privateMember = "I am a private member";
    var privateMethod = function() {
        console.log(privateMember, this.publicMember);
    }.bind(this);

    this.publicMember = "I am a public member";
    this.publicMethod = function() {
        console.log(privateMember, this.publicMember);
    };
    this.privateMethodWrapper = function() {
        privateMethod();
    }
};

var o = new SomeObject();
o.privateMethodWrapper();

Output:

I am a private member I am a public member

Example 8: Private class methods with context locking using custom utility method

var lockContext = function(context, callback) {
    return function() {
        callback.apply(context, arguments);
    }
};

var SomeObject = function() {
    var privateMember = "I am a private member";
    var privateMethod = lockContext(this, function() {
        console.log(privateMember, this.publicMember);
    });

    this.publicMember = "I am a public member";
    this.publicMethod = function() {
        console.log(privateMember, this.publicMember);
    };
    this.privateMethodWrapper = function() {
        privateMethod();
    }
};

var o = new SomeObject();
o.privateMethodWrapper();

Output:

I am a private member I am a public member

Example 9: Private class methods with context locking using custom utility method (prototypal variation)

Function.prototype.lockContext = function(context) {
    var self = this;
    return function() {
        self.apply(context, arguments);
    }
};

var SomeObject = function() {
    var privateMember = "I am a private member";
    var privateMethod = function() {
        console.log(privateMember, this.publicMember);
    }.lockContext(this);

    this.publicMember = "I am a public member";
    this.publicMethod = function() {
        console.log(privateMember, this.publicMember);
    };
    this.privateMethodWrapper = function() {
        privateMethod();
    }
};

var o = new SomeObject();
o.privateMethodWrapper();

Output:

I am a private member I am a public member

As you can see, all four of these context locking methods (examples #6 – #9) allow you to keep the executing scope of your private method clean,  freeing you to write the body of your private method as you would any other, and removes the superfluous <tt>self</tt> reference. I’ll let you decide which method works best for you, but I think the prototypal approach is the cleanest. For more information on how context locking works, see the MDC references for apply and call.

Wrapping it Up

There are definite roles that static and instantiated objects fill. For me, part of the joy of writing in JavaScript is knowing when to use which, and how to get the most milage out of their differences. What about you? Let me know if you have any preferences or guidelines when it comes to creating and using objects in JavaScript.

5 comments to “Object Literals vs. Constructed Objects in JavaScript”

  1. Shawn | November 11, 2009 | Permalink Reply

    This is exactly the information I’ve been searching for. Thanks for the excellent explanation.

  2. Andrew | November 16, 2009 | Permalink Reply

    Just a couple things:

    1) I assume you mean Douglas Crockford, not John Crockford.

    2) Also, I think you might be confusing static and global variables. A static variable only implies that it was allocated at compile-time, not that it is globally available. The same thing goes for calling the global variable a singleton, a singleton is a design pattern, a global variable is a variable that is available within all execution scopes.

    3) Correct me if I’m wrong, but I think the first class of objects you descrive are called anonymous objects, not static objects. This is because the value of the objects at creation time can be affected by the runtime stack.

    Other than that, nice article. This should clear up the difference between the two types of objects for ALOT of people

    • Justin | November 20, 2009 | Permalink Reply

      First off, thanks and I’m glad you liked it. It’s always good to have friends looking over your shoulder to make sure that you don’t pop off and spread misinformation.

      1) Ya….I don’t know who the hell John is. Good catch.

      2) I’m not saying it is an out-right singleton, but only that it behaves like one since object assignment is by reference, and not by value. Also “static object” is a common second name for object literals.

      3) I don’t believe there is such a thing, but I couldn’t find any definitive source stating one way or the other. Anonymous functions, yes, because you can create and execute a function in one statement without ever storing a reference to it. But anonymous object? I’m not so sure. It may be anonymous in one scope, but eventually a reference to it is stored in a very un-anonymous variable; otherwise, it is useless. If you look at it from a type perspective, all user defined objects are of type `object`, regardless if it is a literal or instantiated object.

  3. Brendan Flemons | June 25, 2010 | Permalink Reply

    hello, love your site, great vision

Leave a Reply

Contact Zebra Kick

Ready to find out how Zebra Kick can help you? Fill out the contact form below. We will be in touch with you as soon as possible.