JavaScript - - By James Edwards

JavaScript Design Patterns Deconstructed

All sophisticated design patterns throw up the same fundamental question — is there a concrete advantage to using it in the first place? Without understanding the benefits, it could be very easy to think that it’s just a pointlessly verbose and over-engineered solution, or that it’s only following some kind of fashion.

But there are real and important benefits to using this kind of pattern, and it isn’t an attempt to make JavaScript “more like” any other language, or to mimic classical inheritance. It is an attempt to make the most of the inheritance and scoping features JavaScript inherently has, and to provide robust solutions to the environmental problems that are unique to JavaScript development.

Encapsulated Closures

Wrapping up scripts in single enclosures makes for vastly better encapsulation.

All JavaScript authors have to face the issue of scripts conflicting with one another. Any definition in the global scope will override any previous definition, or built-in object, with the same name. Ultimately your script may break another, or be broken by another. But of course an author doesn’t necessarily know what other objects will exist at the same time as theirs, so the only way to manage it is to avoid putting anything in the global scope.

In practice, it’s impossible to avoid that entirely, but what we can do is wrap our scripts into single enclosures, and thereby reduce the number of objects the script has to place into the global scope to one. All scripts benefit from a clean environment, so all scripts should maintain one; this is not just good practice for library and third-party tool developers, it’s a good idea for all scripting.

Named or Anonymous? Public or Private?

Anonymous closures are inaccessible from outside themselves. Is that a benefit? Why wrap up scripts in anonymous closures if it just means that other scripts can’t access them?

That’s exactly why! So that other scripts can’t access them. The important use-case here is the development of third-party tools — which provide external functionality, but don’t wish to expose their internal mechanics. Some scripts just run background tasks, and don’t require any input at all; so if that’s the case, there’s no point providing any input — the entire script can be wrapped in an anonymous enclosure, and then there’s zero chance of global conflict.

But usually a script provides some public functionality, and this is when it’s helpful to have a split between public and private data. A robust codebase should not allow itself to be broken by user-error, such as a vital property being modified to an unexpected value; but if all the data is public, the user can easily do this.

It’s a mistake I’ve made myself in the past, of providing an API to a script that allowed the user to break internal values, simply because those values were recorded as public properties. If they had been recorded as private variables, they would have been safe, because the user doesn’t have the ability to modify private variables from outside the enclosing scope.

If you’re writing scripts solely for yourself, for your own application or site, then you could argue that such considerations are moot. If you control 100% of the codebase, then its internal interactions are all under your control, too, and you can resolve something like a name conflict simply by changing one of the names. Nevertheless, I tend to work with this kind of pattern even when it’s not strictly necessary, because I find it easier to manage.

THIS or That?

The enclosing scope of any function can be referred to as this, so when we define a named or anonymous enclosure, this refers to that enclosure at the top level; and it continues to refer to that enclosure from within its public methods.

But within private functions, this refers to the immediate enclosing scope (the private function), not the top-level enclosing scope. So if we want to be able to refer to the top-level scope, we have to create a variable which refers to it from anywhere. That’s the purpose of "THIS":

function MyScript(){} (function() { var THIS = this; function defined(x) { alert(this); //points to defined() alert(THIS); //points to MyScript() } }).apply(MyScript);

It could be called anything that’s not otherwise reserved. Some people call it "that" or "self"; I’ve even tried using non-English words like "la" or "das." But eventually I settled on the upper-case "THIS" because it’s long-standing convention in many languages to declares constants in all upper-case, and this seemed to fit the bill.

I use the same convention whenever I need to define private constants: that is, private values that won’t change in the lifetime of the script’s execution. Browser variables are a good example, when used:

var OLDER_WEBKIT = /applewebkit/([0-4]|[5][0-2])/i.test(navigator.userAgent), KONQUEROR = navigator.vendor == 'KDE'; 

Ideally we would use const rather than var to declare them, as true constants use less memory, but that isn’t supported in Internet Explorer.

Privileged Values

The last thing I want to look at is the benefit of using privileged values. All they really are is private values which can be re-defined using a public method.

 var options = { x : 123, y : 'abc' }; this.define = function(key, value) { if(defined(options[key])) { options[key] = value; } }; 

The point of doing this is to exert more control of when and how these values can be defined. The defining function can, for example, contain detailed validation which limits certain members to a range of predefined values, or which only allows them to be defined at a certain time or in a certain order.

I used this to good effect in my CSSUtilities library, where it provides a means of defining global configuration (for example, asynchronous execution mode, which can only be true or false). The combination of private variables with a public definition method provides control over those definitions, since the user is unable to re-define them independently of the definition function, and therefore can’t define them invalidly.

Thumbnail credit: superkimbo