For a long time, I have had a dilemma about encapsulating these scripts within their own namespaces. Using namespaces has many advantages, but may be unfamiliar to some readers of these pages. However, I've now decided that understanding of namespaces is now sufficiently established that anyone consulting these scripts should be able to understand the (only slightly) more involved syntax.
Just to (hopefully) restate the obvious, do remember that while JavaScript is an object-oriented language, it is a prototype-based language rather than a class-based language.*
For people who are not familiar with encapsulating functions within namespaces in JavaScript, I will run through a quick explanation of how namespaces can be used in JavaScript.
I'll use the Base-64 encoding functions as an example.
The simplest, traditional way of defining functions is:
function base64encode(str) { // all the work goes on here... return encode(str); } enc = base64encode('some string');
An alternative to this which is perhaps slightly more in keeping with JavaScript’s nature is to extend the String object:
String.prototype.base64encode = function() { // all the work goes on here... return encode(this); } enc = 'some string'.base64encode();
However, both of these ‘pollute’ the global namespace, and risk having naming conflicts with other functions. This problem is minimised by using appropriate namespaces. In this case, I imaginatively used ‘Base64’:
var Base64 = {}; // Base64 namespace Base64.encode = function(str) { // all the work goes on here... return encode(str); } enc = Base64.encode('some string');
I believe this is sufficiently close to the first version that inexperienced programmers should be able to recognise it.
Of course, this doesn’t guarantee no nameclashes (nothing can completely), but the conflict will only be at ‘class’ level – it is relatively simple to rename the class with a global search-and-replace, if required, or to use other tricks to alias it.
I have broadly adopted this approach for the scripts on this site. In a few cases, I have extended String or Number objects, despite the incursions into the global namespace, just because it is sometimes easier to work with methods operating directly on String on Number objects.
It is probably worth noting that there are various other ways of achieving the same namespace encapsulation. A fairly common approach is to use object literals (as used in JSON):
var Base64 = Base64 ? Base64 : { code: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=', encode: function(str) { // all the work goes on here... return encode(str); }, decode: function(str) { // all the work goes on here... return decode(str); } }
This looks quite familiar to programmers used to class-based languages, but is becoming obscure for less experienced folk.
Perhaps also worth noting is a variation on this which uses an anonymous function to allow the creation of public and private methods and properties:
var Base64 = Base64 ? Base64 : function() { var private = { code: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=' }; var public = { encode: function(str) { // all the work goes on here... return encode(this); }, decode: function(str) { // all the work goes on here... return decode(str); } }; return public; }();
This is quite neat, but it is becoming highly obscure for less experienced programmers, and only worthwhile if you need the protection of private methods and properties!
No single approach is right for all circumstances. I’ve tried to find a middle ground which brings benefits of encapsulation within namespaces without being too obscure to non-programmers.
* For more on prototype- / class-based distinction, see