Object-oriented JavaScript follow up part 2: Technical
Following on from part 1 of my follow-up to “Object-oriented JavaScript”, part 2 provides a technical update to some of the theories and examples.
Without further a-do, let’s jump in with the technical stuff:
A quick note on variable declaration
The var
declaration allows you to chain definitions in a comma separated list; what’s more that list can reference variables declared earlier in the same list.
Take this code as an example:
This could alternatively be written like so:
Personally, I prefer the latter method simply because it keeps my var
declarations in one place (where possible) without endless repetition of the var
keyword. However, this is simply a minor note on coding style, rather than something that can improve the operation of your code.
Understanding object-oriented scope
As any experienced JavaScript coder knows, JavaScript is functionally scoped; this means any variables created within an object or function are only available within that object or function.
Simple, right?
Well no, there are actually a couple of complications to this behaviour that you should really understand:
Passing by reference
When passing arguments in JavaScript, it’s not immediately clear how those arguments are being handled internally. In fact, it is entirely dependant on the data-type of those arguments.
When passing arguments of type Number, String, or Boolean, JavaScript will pass them in by value. This means that the value stored in the argument will actually be a copy of the original value:
Passing in an object, however, passes the argument by reference. This means that, within the function, the argument is not a copy, but a reference—or link, if you like—to the original object. This means any members of that object are available within our function, and any changes to the object passed as an argument will be reflected outside of the scope of the function.
Actually, the way JavaScript handles references is another blog article in itself. Watch this space.
Here’s our example again:
This is a subtle difference, but one, I think you’ll agree, that is important to understand.
The this
special operator
The this
operator can be used to obtain a reference to the current context object and allows properties and methods within that execution context to be referenced. The context object can be considered a “hidden” parameter that is passed to any function.
There are four ways the context is made available to a function:
Implicitly with a method
Implicitly with new
Explicitly with Function.call
Explicitly with Function.apply
In general, JavaScript developers tend to rely on implicit context passing rather than explicit. However, there is one situation where this reliance falls down…
A common problem
When registering event handlers against DOM nodes in JavaScript, the this
context no longer references the method’s parent object, but rather the DOM node to which the event handler is registered.
The easiest way around this is to make sure the reference to the context object originally stored in this
is maintained before defining the event handler:
Further to this, according to Douglas Crockford, there is an error in the ECMAScript spec. that causes this
to be set incorrectly for inner functions; something that he discusses in his article Private Members in JavaScript:
Now, the "var that = this
" technique is certainly a bone of contention amongst members of the JavaScript community, but I’m firmly on the fence in the whole discussion. Certainly my second example could be solved with a complex network of Function.call
and Function.apply
instead of that.member
, but my first example would be somewhat more difficult.
Personally, I feel if Doug Crockford—a developer with many years more experience and infinitely more JS knowledge than myself—says it’s ok, then I’m fine with it.
Garbage collection
As JavaScript is a scripting language, it implicitly allocates memory for objects, strings, variables, and so on as it runs. Garbage collection is the process by which the JavaScript engine detects when those pieces of memory are no longer reachable—that is, they could not possibly ever be used again—and reclaims the memory.
The ECMAScript specification (of which JavaScript is an implementation) doesn’t include a definition on how a JS engine should handle garbage collection, so although each JavaScript engine includes garbage collection—memory usage would quickly snowball if they didn’t—they all handle it somewhat differently.
Some engines are better than others; Google’s V8 is exceptionally good, where as the IE JScript engine is notoriously bad (often resulting in memory leaks).
Nested functions and closures
JavaScript allows the nesting of functions, creating nested scope blocks that inherit scope from their parents all the way up to the global scope.
Take this code for example (there are much better ways of doing this; this method is only in the interests of illustration):
In the above example getListItem
is a nested function within appendList
. This means it inherits everything within the scope of appendList
. As a result of this, the inner function is able to make use of the itemPrefix
parameter that is passed to its parent without it needing to be passed in as another parameter on getListItem
.
When JS automatically garbage collects the appendList
function, it will find no external references to the getListItem
function and will garbage collect that as well.
This is a useful feature of JavaScript, and allows for fairly advanced lambda functions (anonymous functions), as well as providing a useful tool for namespacing library functions.
However, if, upon garbage collection, JavaScript finds an external reference to a nested function, it creates a closure of scope; thus maintaining access to all the variables required within the scope of that function.
This is known as a closure.
Common usage of closures
The most common occurrence of a closure in JavaScript is when declaring event callbacks. This is simply due to the fact that the callback function is referenced within the event listener once registered.
However, it’s important to understand that many modern libraries, plugin architectures, and plugins themselves also make use of closures, and understanding how they use them can mean the difference between controlled and uncontrolled memory usage.
Invisible pitfalls are invisible
It’s obvious when you think about it, but all closures have a potentially high memory imprint as they are maintaining much more than just their constituent parts. What’s more, because of their inherent avoidance of garbage collection, they are maintained until they are manually destroyed either by the code, or by a page refresh.
Prototypal inheritance
I did discuss prototypal inheritance in my original Object-Oriented Javascript article, however I just want to go into a little more detail here.
In a prototypal system, objects are supposed to inherit from objects; unlike in a classical inheritance system where classes inherit from classes and instantiate objects. JavaScript has no class (pun intended).
Prototypal inheritance is actually simpler than classical inheritance once you get your head around it. You don’t need to define classification, so your code is smaller and less redundant since objects inherit from other more general objects. It is a model of differential inheritance; i.e. each level of inheritance only adds the differences with its parent.
Unfortunately, JavaScript is a bit confused about its prototypal nature, and wants to fit in with the other scripting languages by pretending to like The Beatles and by making itself more attractive to classically trained programmers by introducing the new
operator.
This means that:
produces an object that inherits from myConstructor.prototype
.
This indirection means that JavaScript actually has a pretty ugly constructor pattern and most new JavaScript developers never really get to grips with the inheritance model at all.
However, all that has changed with the all singing, all dancing advent of JavaScript 1.8.5 (The New Shit™), which introduces the Object.create
method:
Mmm tasty, but since JS 1.8.5 only seems to be implemented in Firefox 4 (still in beta), don’t get all excited just yet.
If you want something similar in your own code, you can implement the following for a similar approach (and still kiss goodbye to ever having to use the new
operator again):
What’s more, this DIY version will allow you access to any superclass’ implementations of a particular method like so:
This code is a mix of the goog.inherits
function—included in the Closure library—and the Object.create
outline by Crockford in his article on prototypal inheritance.
JavaScript design patterns
Taking all this into account, we can start developing some incredibly useful design patterns that make use of prototypal inheritance, closures, and JavaScripts nuances of scope.
In fact, several have already been developed within the JavaScript community:
JavaScript namespacing
To avoid the perils of globally scoped variables and functions, it is advisable to create a single global object for the purposes of namespacing the rest of your code. This is relatively simple to achieve:
This command tests for the existence of a NEF
object at the global scope, and if evaluated to true, returns that object. Otherwise, it creates a new object using the object literal syntax.
You may now assign any further variables or modules to your namespace:
Module pattern
The JavaScript module pattern was first proposed by Doug Crockford as a means of enforcing public and private object members through the use of closures.
My preferred variation on the pattern is such:
This method simple uses a closure to maintain private variables in the scope of the returned pub
object. The trick of this technique is the use of the ()
, right at the end of the code, after the parent function definition, which causes the function to run immediately and return the pub
object.
jQuery plugins
The jQuery plugin architecture also makes use of closures to do something quite similar to the Module Pattern above. However, jQuery plugins are specifically namespaced to the jQuery object itself:
Alternatively, should you wish to only include a single public method, you can avoid jQuery.fn.extend
by instead making use of jQuery.fn
for definition:
In this code we can see that jQuery is passed to an anonymous function, that is run immediately, as the parameter $
. The anonymous function provides a scope block for the plug-in code, thus preventing potential headaches from variable naming clashes across shared code. The plug-in functions themselves are then created as closures so that they have access to the private variables and functions within that outer anonymous function. These plug-in variables are then bound to the passed in jQuery instance $
using jQuery’s own built in extension functions.
This is actually quite a clever use of closures, although it does have the potential for memory leaks and excessive memory usage if variables and references aren’t kept in check. With that in mind, it’s often a very good idea to profile your jQuery plugins to understand what might need tidying up.
Custom events
Most modern JavaScript libraries include a mechanism for defining your own custom events. A custom event is merely a bespoke event, defined in your code, that other event-handler functions may be bound to. This custom event may then be triggered by you at any given point of execution in the code.
The custom event architecture is an implementation of the Observer Pattern, in which an object maintains a list of dependant “observers” which it notifies automatically of any state changes. In JavaScript, the object maintaining the list is our event, and the “observers” are the event handlers.
Using custom events in your own code provides a useful binding for other developers’ code. The event itself is loosely coupled to the event handlers, thus, additional event handlers can be added or removed by third-party code.
For more information on custom events, I’d recommend having a look at the documentation for the library of your choice. Here are few links:
Summary
So that’s the technical addendum to my 2006 post. Hopefully, that should serve to bring the article up to date, and correct a few of my previous errors. As always, comments and corrections are welcome via the comment form below.
Originally I had intended posting this article in two parts, however, it has continued to grow even as I write it. For this reason, the third and final part of this follow-up series will look at what’s on the horizon for JavaScript development, including how GoogleBot deals with JavaScript, the importance of supporting a core experience (where JavaScript is unavailable), server-side JavaScript, and architecting JavaScript applications.