JavaScript Library Techniques, Tips and Tricks
You need only to look as far as your preferred JavaScript library to get an indication of how much JavaScript has evolved over the years. Most libraries implement some powerful features, and the programming techniques that govern these libraries now closely resemble OO constructs found in server-side languages.
As you mature in your JavaScript knowledge, you may find yourself not only wanting to use, but also wanting to understand the core functionality inherent to each library. Deconstructing the source, you will discover common threads running through each line of code. If you can learn to imitate and manipulate these patterns to your benefit, then you will find that you have reached an important milestone.
The following are a few tips and tricks that are gleaned from libraries in order to help you get started.
Namespacing and anonymous, self-executing functions
Namespacing in JavaScript is synonymous with namespaces, or packages, in server-side languages. Instead of mimicking the same behavior by placing several JavaScript files in different folders, library designers will often sub-divide a single object into several smaller, meaningful objects. For instance, consider some of the namespaces in the Yahoo! YUI (called modules), such as Anim, Dom, and Element. Similar namespaces can be found in Prototype, such as Ajax, Event, and Enumerable.
Although the technique for accessing methods and properties available in each library namespace can differ drastically, the concepts are similar. Most notably is the use of anonymous, self-invoking (or self-executing) functions. Take a look at the simplest example of namespacing below.
This namespace is effortless to implement, is the easiest to understand, and does not use a self-executing function. It is often referred to as object literal notation, or a nested object literal. If you never roll out your own library to other developers, and have no need for private methods and properties, then this is probably the most straightforward approach. The next example is a little more complicated.
Instead of using an object literal for the global namespace, I am using a self-executing function, and then storing (or pointing to) this object with the variable named JSANT
. This approach allows you the additional benefit of private methods and properties, which are only accessible from your public methods and properties. This is often referred to as the Modular Design Pattern.
The pattern is possible due to the fact that a function that self-executes, only executes once. Whatever is returned that one time, is now stored as a part of the namespace through your global variable. If you have also tried to master closures, well, this is your chance to shine. When an inner function like JSANT.ajax.someMethod()
accesses a variable within the outer JSANT function (like myPrivateProperty
) after it executes, then you have a closure.
The final, and most complicated namespacing technique, is the use of the dollar sign ($) as a substitute for your global namespace. The following code is derived from the jQuery JavaScript library, and it has several benefits. The first of which, is the same as the second example — private methods and properties. The second benefit, is the ability to use the dollar sign not only as a namespace, but as a selector for selecting elements in the DOM. The final benefit, is compatibility with other libraries that also use the dollar sign selector syntax.
If I were to walk through the previous code verbally, it would sound something like this:
- Create a self-executing anonymous function that contains all of your code, and protects your library from conflicting with variables already defined in the document.
- Create a variable called JSANT, point it to a function, and assign this to the window object.
- Pass JSANT one parameter, which will end up being the syntax for your selector.
- If an instance of the JSANT object already exists, then initialize it.
- If an instance of the JSANT object does not exist, then create it recursively.
- If the dollar selector is not being used by another library (making it undefined), assign it to the window object, and then point it to the JSANT object. The $ and JSANT can now be used interchangeably.
- Define the prototype of JSANT, and all additional namespaces, methods and properties.
As best as I understand it, and have read it explained elsewhere, JSANT must be declared with prototype. This is because the instanceof
follows the prototype chain to see if your object inherits from the prototype of the constructor function that is the class you are asking instanceof. If someone from the jQuery team would like to venture a better explanation in layman’s terms, it might be more helpful.
Constructor type checking
Many developers are plagued by the fact that although objects in JavaScript have types, checking for those types can be a difficult proposition. When I first discovered constructor type checking, it was certainly an aha! moment. The previously less acceptable method for checking an object’s type, was to use the typeof
keyword. Unfortunately, for custom objects and Arrays, JavaScript always returns “object” as the type. Although accurate, it is certainly not specific enough. To get more exact type definitions, use instead the name property on the constructor for an object.
Variable arguments, function overloading, and the callback function
Polymorphism in JavaScript is a difficult OO concept to emulate. Unlike Java, there really is no such thing as overloading a function. You cannot define multiple functions with the same name, pass them different numbers and types of arguments, and expect the JavaScript engine to find the correct function based upon those arguments. However, there is an alternative that comes close, and that is the use of variable arguments.
A function can gain access to the arguments that were passed into it using the arguments keyword. You do not even need to define variable names to match up with the arguments, as in the first example below. However, arguments is not an array, so in order to treat it as such, you will need to call (or convert) it into a real Array object.
If you are the only developer using the function, and know this list of variable arguments will serve a single purpose, then you can reap the benefits. However, if you want to provide more flexibility, and roll the function out as a utility to other developers, then you will probably need to consider the second example.
This approach might at first glance appear less flexible, but it allows you to treat your function as if it were being overloaded. Each conditional block can share local variables and perform a different task depending on the number of arguments. This also sets some expectations for other developers.
The final example utilizes variable arguments to help process a set of key/value pairs in a hashmap, and allows other developers the ability to specify an optional callback function. This is an extremely flexible, yet very defined feature that is present in several JavaScript libraries. Returning a callback function allows you to notify users after an action is complete, perform updates in the background, and it is generally a very useful tool.
Shorthand
The final technique that I will cover that is used in JavaScript libraries today is shorthand. One of the positive aspects of the language is that the syntax is flexible, which allows several developers to code to their comfort level in a single script. This flexibility can cause some confusion and verboseness if no conventions have been agreed upon, but it can also lead to the creative use of shorthand. The following are three academic examples that demonstrate some of this shorthand.
Chainability
One topic that I did not cover in this tutorial is chainability — also known as object chaining, or DOM element chaining. This is a very powerful feature in many libraries that deserves special attention. Object chaining allows you to perform an action on an element in the DOM, return it as an object, and then perform another action on that object without having to specify the element again. You could call this a preview of things to come!