I’ve been working extensively with JavaScript for the past few weeks, developing an all-Javascript application (the code base is exceeding 2000 lines of JavaScript already). Since the application needs to scale well with a large amount of dynamically generated elements, I have discovered, learend, and used quite a few shortcuts to boost the speed, minimize memory usage with mem-leak checks, and reduce file-size in general while still maintain the readability of the code.
This article is mainly about the nuggets that I extract out from my experience. I learned a few things from reading the source of PrototypeJS and Scriptaculous, while I learned other things (ideas, concepts) from different languages such as Ruby (famous for one-liners). I have to admit, nothing beats reading other people’s source code. Prototype is extremely well-written and there are tons of programming gems that one can learn from.
A few tricks are possible only in JavaScript because it is NOT Java, or C#, or C, and it is extremely powerful and flexible. With the Prototype cool-aid, Object-Oriented JavaScript is not only possible, but easy, standardized, and straight-forward to implement
Tip 0: Use a JavaScript framework
Pick a framework and use it. Any framework is *much* better than no framework at all. Different browser behaves differently and the framework will help smooth out those wrinkles. You are simply insane if you are not using one.
Our menu comes with several choices: so it’s a matter of personal preference. The learning curve will a bit steep as you will have to Google alot for the API’s, but once you are familiar and comfortable, you will become a much better scripter.
- Prototype: Easy to use. One of the first that popularized the idea of frameworks. Lowest learning curve with virtually NO dependencies (1 file, ~ 130Kb)
- jQuery: almost feel like writing javascript short-hand notation. Will take sometime to get used to the syntax. Core library is very small (~30Kb)
- YUI: there’s a lot being offered in this framework. Get quite large (ZIP file: ~10MB!). I just don’t like everything being stuffed inside the YUI namespace, even when I register my own namespace.
Tip 1: Format your code well.
Nothing is worse than writing a large ( or any) application with poor code readability. As the application gets more complex, as it did in my case, having the ability to scan through the code and pin-point the hot-spot to fix is extremely helpful. Simply by consistently indent the code with NO EXCEPTION and keep yourself to a high coding-standard, your code will look pretty, work well, and have less bugs. The key is to do this as you code, not after everything has been done. You think you won’t ever look at your code again once it’s done, well, YOU ARE WRONG!
Once you are in the habit of formatting the code yourself, you become more aware of the “beauty” of your code. You’ll start seeing cool things, similar to the guys in Matrix see our world. You’ll become Neil and fly through your code and feel its force-field.
Tip 2: Having a high coding standard.
The reason why you have to keep your code in order and of high standard with no exception is that I believe in the Broken-window theory (wikipedia). Once there is a sub-standard spot in your code and you ACCEPT that it is there, you’ll more likely to repeat it and produce more sub-standard code. Nobody vandalizes your code better than you are. Soon, it will become an unmanageable, unreadable mess. Imagine debug these kinds of code? I’ll be cursing all day long.
I highly recommend the Code Complete book. It is thick enough to use as a pillow, but it is a mandatory read if you are serious about programming.
Tip 3: Explicitly write out your coding style and standard.
This is essential when you are working in a team with other people. Also, once you have established a coding style guideline, you will spend less time on thinking about the formatting, how to name variables, how to organize your code. I fondly remember DHH’s word: constraint is liberation. Yup, having a certain constraints will allow you to be more freely to do other productive programming.
Tip 4. Namespacing your code
If you are develop something more complicated than an alert box, put your code inside namespaces to avoid cluttering up the global scope, reducing potential conflicts with accidental over-written from other scripts, and organizing your code much better.
Repeat after me: Namespace your code, it’s easy. Do it.
Example:
Our application is a JavaScript drawing program called JsPaint. We can separate the code into different namespaces as follow:
/* Include in your first included JS file */
var JsPaint = {
Data: {}
,UI: {}
,Util: {}
};
To add more methods or classes to JsPaint.UI:
JsPaint.UI = {
Menu: {}
,MenuItem: {}
,Canvas: {}
,getCanvas: function() { /* return reference to the canvas here */ }
};
To add a Renderer sub-namespace later on to the JsPaint.UI:
JsPaint.UI.Renderer = {
Simple: function() {} /* class */
,Fancy: function() {} /* class */
};
/* define shortcut to Renderer */
var jsr = JsPaint.UI.Renderer;
var simpleRenderer = new jsr.Simple();
Namespace is awesome to organize your code. However, if you have lots of level within your namespace, you can, and should, use aliases for the different namespaces. By aliasing, you also reduce the amount of “dot” operations to query the object, thus helping the properties lookup faster.
Tip 5: Putting comma-separator at the begining of the line.
I ran into this post on Thomas Fuchs’s blog a couple days ago. Thomas is the guy behind Scriptaculous. I couldn’t help but smile.
Internet Explorer is every picky about JSON format (I think this is good in a way, as it forces you to pay attention to the code). If you leave an extra trailing comma, IE will silently crash, while good old FireFox still works just fine.
Example:
var ieWontLikeThis = {
isIEBadForYou: true
,hoursWastedOnDebuggingIE: function() { throw "Number Out Of Range";}
,noticeTheTrailingCommaBelow: true
,
}
The extra comma will cause IE to be so confused and throw up.
My solution is to always put comma right before your properties, so that you won’t end up forgetting about the extra comma. The code may look ugly and weird a little bit at first, but you’ll save yourself lots of pain and aggravation down the road. Moreover, you can comment out a properties without further re-formatting of the code. If I don’t want the hoursWastedOnDebuggingIE() method, I only need to comment it out. If you put the comma trailing the previous line, you will have to remember to remove it too. Not fun.
Bonus:
This coding style with the comma or the separator as the prefix is also useful for other languages as well, especially for SQL statements. It helps with 1) List of Columns to be SELECT’ed, 2) WHERE conditions, 3) Different temp variables in Common Table Expression (SQL Server 2005). (Besides that, I also force myself to indent the code in a consistent way, indentation matters!).
SELECT p.title ,p.description ,p.is_published -- ,p.category_id /* don't need this column*/ ,a.user ,a.author_id ,p.created_at FROM posts p INNER JOIN author a ON p.author_id = a.author_id WHERE a.user = 'alexle' AND p.type = 'post' AND p.is_published = true -- AND p.category_id = 1 /* this line can be commented out without affecting the previous code */ ORDER BY p.created_at DESC -- AND p.category_id /* this line also can be commented out without affecting the code */
Another example with writing and debugging Common Table Expressions. This comes directly from my experience with writing a humongous CTE in SQL Server 2005
WITH firstCTE AS ( ) -- SELECT * FROM firstCTE /* uncomment this line to debug the first CTE */ , secondCTE AS ( ) -- SELECT * FROM firstCTE /* uncomment this line to debug the second CTE */ , thirdCTE AS ( ) -- SELECT * FROM firstCTE /* uncomment this line to debug the third CTE */ SELECT * FROM firstCTE INNER JOIN secondCTE ON ... INNER JOIN thirdCTE ON ...
Tip: 6 Having fun with Function Arguments
If you have to write functions with lots of arguments, well, you don’t have to. Instead, pass an array (or hash to be exact) of parameters as the argument and have your code handled it intelligently. You have more flexibility as the parameters list can be changed anytime. This technique is used extensively through out Prototype, Scriptaculous, and other frameworks such as jQuery, or ExtJs.
JsPaint.UI.Canvas = function( options ) {
var defaultOptions = {
canvasId: 'canvas_container'
,width: 100
,height: 200
,menuItems: [ "File", "Edit", "View", "Help" ]
};
/* merging default options with the passed in options */
this.canvasId = options ? ( options.canvasId || defaultOptions.canvasId ) : defaultOptions.canvasId;
this.width = options ? ( options.width || defaultOptions.width ): defaultOptions.width;
this.height = options ? ( options.height || defaultOptions.height ): defaultOptions.height;
this.menuItems = options ? ( options.menuItems || defaultOptions.menuItems ) : defaultOptions.menuItems;
};
I use a shortcut with the ternary operator ? : and the “or” operator || to quickly merge the options with the defaultOptions.
Tip 7: Using or operator || for Coalescing nullable values
If you need to choose the first not-null value from a list, you can use the || (’or’) operator. Besides its usage as a logical boolean operator, || can be used as a fall-through-if-null operator, return the first non-null value.
var width = options.width || defaultOptions.width || 100;
It will check for the options.width, if null, defaultOptions.width will be used, then finally 100 if both the previous variables are null.
In SQL Server and MySQL, you can use the function COALESCE( csv_list_of_variables ) to do the same, e.g. getting the first non-null value;
Tip 8: Using ternary operator to reduce the amount of if-else in your code
I love the ternary operator. It may look cryptic, but after a while, your code will be shortened quite a bit while still remains readable with proper formatting. Nested ternary operations can be hard to read so it’s a matter of balancing between coding shorthand and code readability.
Tip 9: Function Arguments, enhanced with Prototype
Do you see how ugly our previous code to get the values from the arguments? If you are using Prototype, you can shorten all the ugly manual merging with a one liner
var JsPaint.UI.Canvas = function( options ) {
var defaultOptions = {
canvasId: 'canvas_container'
,width: 100
,height: 200
,menuItems: [ "File", "Edit", "View", "Help" ]
};
/* merge defaultOptions with options */
options = Object.extend( defaultOptions, options );
this.canvasId = options.canvasId;
this.width = options.width;
this.height = options.height;
this.menuItems = options.menuItems;
};
Done! Object.extend() merges all the options together so you can have fun writing other productive code.
Tip 10: Optimize Loops
If you need to iterate through a large array, you should squeeze every bit of speed by caching the length. There’s always a trade-off between speed and storage, and if you want speed, you must trade RAM for it. The Prototype’s API page for Array has an excellent example:
// Custom loop with cached length property: maximum full-loop performance on very large arrays!
for (var index = 0, len = myArray.length; index < len; ++index) {
var item = myArray[index];
// Your code working on item here...
}
Tip 11: Object Inheritance made easy with Prototype
Since Prototype version 1.6, you now have full support for inheritance for your Object-Oriented JavaScript application. JavaScript has the support for inheritance, albeit not built-in, but it has never been easy to work with. Before, I had to write hacks and came up with something monstrous, ugly, and kludgy in the end. With Prototype, you get full support for inheritance for virtually free using Object.extend() and Class.create().
To ready more about inheritance, consult the Prototype’s Tutorial page on Inheritance
Tip 12: Leveraging Prototype’s Internal Methods
I highly recommend you to read through Prototype’s source code. The file contains more than 4000 lines of high-quality JavaScript code and you will learn a lot from doing so. An example is Event.KEY_* attributes (~ line 3700), containing the numerical code for various keys so you don’t have to look them up or re-define them again. Another example is the Prototype.emptyFunction attribute, which is an empty function. I often use emptyFunction in my base classes to make it clear that these methods are virtual methods, to be overridden by the sub-classes.
Example:
JsPaint.Shape.ShapeBase = Class.create( {
initialize: function() {}
/* only the object knows how to draw itself */
,draw: Prototype.emptyFunction
/* only the object knows how to refresh the screen */
,refresh: Prototype.emptyFunction
} );
JsPaint.Shape.Rectangle = Class.create( JsPaint.Shape.ShapeBase, {
initialize: function( $super, width, height, x, y ) {
/* calling parent's constructor */
$super();
}
,draw: function( $super ) {
/* drawing logic here */
}
,refresh: function( $super ) {
/* refresh logic here */
}
} );
JsPaint.Shape.Square = Class.create( JsPaint.Shape.Rectangle, {
initialize: function( $super, side, x, y ) {
/* calling Rectangle's constructor */
$super( side, side, x, y);
}
/* draw() and refresh() stay the same */
} );
Tip 13: Implementing Enumerable methods for your custom collections
For my application, I need to implement a generic sorted list that must behaves similar to a regular array while being optimized for speed. I set out to write a hybrid data structure that uses binary sorting to keep the array always in-ordered. I’ll publish the code in a separate post, but the interesting idea I want to share is the implementation of methods from the Enumerable interface, which helps working with the custom sorted list a lot more enjoyable.
From Prototype’s API page for Enumerable, the methods are
- all
- any
- collect
- detect
- each
- eachSlice
- entries
- find
- findAll
- grep
- inGroupsOf
- include
- inject
- invoke
- map
- max
- member
- min
- partition
- pluck
- reject
- select
- size
- sortBy
- toArray
- zip
I did not implement many these methods for my collection (I didn’t need them all), but my favorite ones are each(), first(), last() (these 2 are not listed, but they are still my favorites). Enumerable is a great time saver, implement it if you can.
Tip 14: A better Enumerable#each() implementation
The current implementation for Enumberable#each() in Prototype is not as nice and it’s cumbersome to break out from the loop. I’m greedy and I want everything a native for-loop can offer: item, its index, ability to break and continue, plus the piece of mind of not worrying about the index-out-of-range issue.
Here is my so-called better each() implementation:
each: function( iterator ) {
for( var i = 0, len = this.collection.length; i < len; ++i ) {
if( typeof( temp = iterator( this.collection[i], i ) ) != 'undefined' && !temp )
break;
}
}
The trick is to only break when the iterator explicitly returns false.
Example Usage:
var list = new SortedList ();
/* populate the list here */
/* iterating ... */
list.each( function( item, index ) {
/* process here */
item.process();
/* skipping ...*/
if( continueConditionIsTrue )
return; /* this will returned 'undefined' back to the iterator */
/* break out early */
if( breakOutConditionIsTrue )
return false;
} );
Tip 15: Passing function reference: The Strategy Pattern
Once you realize how flexible JavaScript is, you begin to have fun with it. Passing function reference is a neat trick that can be very useful. If you look at the each()’s implementation above, you see that the iterator function is being passed in as an argument variable. The iterator is than invoked directly via that argument variable just as you would normally execute a function. Neato!
I used this trick in my SortedList implementation and love it. Since the items stored in the list are custom objects that do not implement IComparable interface (that you can’t do item.compareTo( otherItem) ), however, to sort them, I need to be able to compare 2 object together directly. I implemented a comparator that contains (or encapsulate) the business logic for the object comparison. Basically the SortedList doesn’t care on how the objects are constructed, as long as the comparator can give the correct comparison results, the list is guaranteed to work
Here is the constructor for my SortedList. I used Prototype Class.create() to create a new class and the initialize() method is the constructor.
/* I use Prototype */
var SortedList = Class.create( {
initialize: function( options ) {
this.collection = [];
var defaultOptions = {
comparator: function(a,b) { return ( a < b ) ? -1 : ( a == b ) ? 0 : 1; }
,jsonifier: function(a) { return a.toJSON() }
};
this.comparator = options ? options.comparator || defaultOptions.comparator : defaultOptions.comparator;
this.jsonifier = options ? options.jsonifier || defaultOptions.jsonifier : defaultOptions.jsonifier;
}
} );
I have a default comparator which can be used for basic data types (int, float, double, etc.), or I can pass in a custom comparator in the options argument to override the default one. The strategy for the comparison can be swapped out anytime and still the rest of the implementation remains unchanged.
The indexOf(item) function can be then implemented as follow:
indexOf: function( item ) {
if( this.comparator( this.collection[ 0 ], item ) == 0 )
return 0;
else if( this.comparator( this.collection[ this.collection.length - 1 ], item ) == 0 )
return this.collection.length - 1;
for( var i = 0, len = this.collection.length; i < len; ++i )
{
if( this.comparator( this.collection[ i ], item ) == 0 )
return i;
}
return -1; // not found;
}
Tip 16: Closure
Closure is powerful. Without this feature, JavaScript won’t be as flexible and robust as it is now. There are lots of good tutorials on closure so I won’t be cover it again in depth. I’ll provide a code sample instead. Yup, show, don’t just tell. The simplest example is to access the “this” keyword in a setTimeout or setInterval context.
Let’s have a StopWatch class that keeps track of its internal run time and update its display accordingly. Here is a very simple implementation that I cooked up in 5 minutes
var StopWatch = Class.create({
initialize: function() {
/* init these varialbe */
this.runTime = 0;
this.tickDuration = 100; /* tick every 100 ms */
this.timer = null;
}
,start: function() {
/* exit if the stop watch already running */
if( this.timer ) return;
var me = this;
this.timer = setInterval( function() { me.tick() }, this.tickDuration );
}
,tick: function() {
this.runTime += this.tickDuration;
this.updateDisplay();
}
,stop: function() {
clearInterval( this.timer );
}
,updateDisplay: function() {
/* update the time display here */;
}
});
/* Now run the stop watches */
var exeriseStopWatch = new StopWatch();
exeriseStopWatch.start(); /* you'll see the time ticking here */
var cookingStopWatch = new StopWatch();
cookingStopWatch.start(); /* you'll see the time ticking here */
exeriseStopWatch.stop();
cookingStopWatch.stop();
In the start() method, we declare a “me” variable to hold the reference to the current object and if you remember, we can pass function references as variables within JavaScript. Since the function defined within setInterval is still in the local scope of the start() method, we have access to the “me” variable — which again point back to “this” StopWatch.
Tip 17: Closure with Prototype Cool-aid
If you use Prototype, you can have a much nicer syntax for closure and accessing the “this” scope. Let’s rewrite the start() function of our WtopWatch class.
,start: function() {
/* exit if the stop watch already running */
if( this.timer ) return;
this.timer = setInterval( this.tick.bind( this ), this.tickDuration );
}
Prototype extends the native Function object within JavaScript with some neat wrappers, bind() is one of them. The explanation for function.bind() :
Wraps the function in another, locking its execution scope to an object specified by thisObj.
Your code suddenly becomes much clearer and it states the intention of what it does! Prototype’s developers get the principle from the Ruby language: optimizing the programmer’s fun and productivity. These utility methods such as bind() and bindAsEventListener() just make writing JavaScript a lot more enjoyable and straightforward.
Final Words
This is by far the longest post I have ever written in a single day. I am excited to learn more about JavaScript, and I am more than glad to share my experience with everyone. I hope you can get out a few good things from this post and apply in your everyday work.
Let me know if you find any bugs and typo. Disclaimer: the code samples in this post have not been tested so point it out if you can.

