Speed Testing JavaScript Pattern Styles
In December I wrote a post about how to encapsulate JavaScript Objects. I knew at the time that the class would be a heavy hitter in terms of performance but never really tested it as such. Being able to encapsulate was such a powerful concept at the time. A recent question about how to write JavaScript Objects made me sit down and run a performance test.
After running the tests what I found not only made me go wooooo but also confirmed some suspicions I had. The question being which style of writing a JavaScript object was the fastest for set-up and creation. Now that’s a tough question to answer, I mean do you use a #1 phillips head screw driver to remove a #3 phillips head screw? I boils down to using the right style for the job at hand.
OK back to the task at hand. I set up a page that created an object that consisted of: loose functions, JSON object, closure object, prototype object, and the encapsulated object. During the test the object will be created and run over an integration of 20000 times. Let me say to compare apples to apples I need to run the loose functions as new during the loop which is not normally done but tests their creation in the scheme of things.
For the results:
Object Test in: FireFox
Loose Functions: 167ms
Prototype Pattern: 109ms
JSON Pattern: 174ms
Closure Pattern: 214ms
Encapsulated Pattern: 596ms
Object Test in: Safari
Loose Functions: 310ms
Prototype Pattern: 272ms
JSON Pattern: 221ms
Closure Pattern: 381ms
Encapsulated Pattern: 417ms
Object Test in: IE7
Loose Functions: 691ms
Prototype Pattern: 198ms
JSON Pattern: 371ms
Closure Pattern: 355ms
Encapsulated Pattern: 596ms
The differences in the above browsers I suspect is primarily due to how they treat the object set-up and the ECMA standards. Definitely building object using the prototype method gains the best performance. Which is understandable and I’ll describe.
Loose Functions:
These are the bread and butter of JavaScript. We learned JavaScript by using these and they are still important. A JavaScript function attached to the global scope and can be used throughout the DOM is a powerful thing. However when a loose function relies on another loose function pandemonium starts to ensure. Loose function grow, duplication starts to happen and explosive global variables ruins the day. So the advantages of loose functions is their one hit wonders. The disadvantages is that when one function relies on another then it’s time to start thinking object relations.
JSON Objects:
Building an object using JSON notation is relatively easy once you get your head around them:
var myObject = {var1 : “”, var2 : “”; someFunction : function(arg) { this.var1 = arg } }
JSON objects are singleton in nature. Once you declare myObject as in above you can not re-declare it without removing the previous object notation. If done on the global scope these objects can be used throughout the DOM as needed. I see the dis-advantages of these objects as not only performance hitters (compared to prototyped) but large classes, especially those with multiple inner function levels as unwieldy. The advantages of these are for small objects that have only a few inner methods.
Closure Pattern:
Closure objects contain inner functions that are created during the instantiation of the object. This is the reason for the longer creation period. A closure object looks like this:
function outerFunction() {
this.var = “”;
this.innerFunction();
function innerFunction() {
this.var = “Hi”;
}
}
Closure patterns, as I found, are great for working with large objects that contain a lot of inner or even nested inner function. The dis-advantages of these object is that the instance variables are also publicly accessible. So a lazy programmer can modify a variable directly rather than using the appropriate accessors/mutators or public functions.
Prototype Pattern:
Prototype functions have proved themselves to be the overall performance winners during usage. That is because at declaration:
function myBase() {
this.variable1 = “”
}
myBase.prototype.setVar = function(x) {
this.variable1 = x;
}
the setVar object is created independently of the myBase object. Yet it is associated via the myBase prototype. So when another myBase object is instantiated then another setVar object is not created but also associated with the new myBase object. That’s were the speed comes in, you are only creating one object no matter how large your object with prototype scoping is. The dis-advantage to this is that when you need to create dynamic DHTML objects with DOM events it can really be tricky making sure the correct element triggers the event.
Take for instance:
function myObject() {
this.element = document.getElementById(’someDiv’);
this.setEvent(this.element);
function setEvent(obj) {
obj.onmouseover = function(e) { this.sayHi(); };
}
function sayHi() {
alert(”hi”);
}
}
This will fail miserably because the reference to “this” in the event function actually references the element “someDiv” which does not have the method sayHi. When doing this type of programming the only recourse I have found is to include the name of the object which means hard coding a page.
var someObject = new myObject(”name”);
function myObject(name) {
this.name = name;
this.element = document.getElementById(’someDiv’);
this.setEvent(this.element);
function setEvent() {
var name = this.name;
obj.onmouseover = function(e) { eval(name).sayHi(); }
}
function sayHi() {
alert(”Hi”);
}
}
It’s in those associations where the “this” reference can really be a pain and cause the script to crash. Undeniably it’s faster through object re-use.
Encapsulated Patterns:
I am perfectly willing to say that this pattern is slow and is really a beast of burden compared to the others. This pattern creates internal objects just as the closure pattern, and additional objects (as pointers) to the public internal object and attributes. That is why it’s slower.
function myObject() {
var var1 = “Hi”; //Private to this instance
var2 = “There”; //Shared between instances
this.NAME = function() { return “myObject” } //Read only attribute
this.Method1 = myMethod1; //Public Object to point to internal Object myMethod1
function myMethod1() {
alert(”Hi”);
}
}
However using this method means you can be rest assured that internal attributes and methods are hidden (by function scope) so external scripts don’t modify or access them. The number of bug reports fell drastically from the companies I contracted for because their full time programmers were correctly using the object and could not be lazy.
So it’s not the saying “How to do it” that works it’s “What’s the right way to do it” that works. Singular and small but highly re-used objects may fall into the part of JSON patterns, Multiple instances of same objects on a high traffic site may fall into prototype patterns, intra-net applications may make better use of the encapsulation pattern or in environments where multiple skill levels of programmer’s are involved.
Subscribe