Namespaces are one honking great idea — let’s do more of those!
- Tim Peters, The Zen of Python
Almost every modern programming language has the concept of namespaces. JavaScript is not among them. Namespaces allow the developer to separate large blocks of code from each other using a well defined and language enforced naming convention. When it comes to organizing large code bases in an intuitive hierarchical manner (as you might organize the file system on your computer), namespaces are indispensable. As JavaScript starts being used for more advanced applications (think client side form validation versus Google docs) the traditional ways of writing JavaScript begin to fall apart. Although namespaces are not a built-in language feature of JavaScript, it is not that hard to replicate similar functionality that can make your code much more manageable. In this short blog post I will try to describe a few of the ways to do namespacing along with their pros and cons.
One Example – Google
It’s hard to find a small example where namespaces are really useful. That’s because namespaces are primarily helpful for large projects. Google is a great example of a large project and it is no surprise that they use namespacing. Google has several different JavaScript frameworks for services such as search, maps, and rss feeds. From one perspective, all this code belongs together because it is all made by Google and it is all made for accessing Google’s web services using JavaScript. On the other hand, they have very little if anything to do with each other. The API for search is completely different than the API for displaying a map or reading an RSS feed. Namespaces allow you to group code at multiple levels of organization so that code can belong to both the very general “Google” code base, along with the more specific “search” or “maps” code base. When every piece of code is associated with a namespace, you rarely wonder about what project it is for, where it is used, or what it does. All that information is implied by the namespace (although sometimes rather tersely).
What are some ways you can duplicate namespaces in JavaScript?
Simple Naming Conventions
The simplest model that some people consider satisfactory for namespacing is to just use a naming convention.
Suppose we have three functions that are for doing searches, displaying maps, and reading RSS feeds. We might define them with these names:
function search(query){ ... }
function map(geocode){ ... }
function read(rssUrl){ ... }
From the function names alone, it is really hard to tell what they do when we are considering the entire scope of all of Google’s web services. It’s even worse if you consider that these functions could be used along side your own internal functions that have similar names but which do very different things. It would be better to change these function names to:
function GSearch_search(query){ ... }
function GMaps_map(geocode){ ... }
function GRSS_read(rssUrl){ ... }
Now we can look at the function names and see that the G means its part of Google’s web service APIs, and in particular GSearch, GMaps, and GRSS correspond to the search, maps, and RSS feed APIs respectively. Keep in mind that each one of the APIs will have lots of different functions related to their services. For example,
function GMaps_map(geocode){ ... }
function GMaps_getGeocode(address){ ... }
function GMaps_plotMarker(geocode, description, icon){ ... }
...
The naming convention succeeds at communicating which functions belong together, but that is about it. It becomes very tedious to always write out GMaps_ for every function call. It also isn’t hard to imagine another company named “Geozone” coming out with their own maps service where they decided to prefix everything with G for Geozone. And we definitely don’t want to write out GeozoneMaps_ all the time.
Although a naming convention helps us out, we can do better.
Nested Objects
The next level up from using a simple naming convention is to use JavaScript’s simple object model to group functions together. It would look something like this:
var google = {
maps: {
getGeocode: function(address){ ... }
plotMarker: function(geocode, description, icon){ ... }
...
},
search: {
search: function(query){ ... }
findSimilar: function(searchResult){ ... }
...
},
rss: {
read: function(rssUrl){ ... }
subscribe: function(rssUrl, rssReader){ ... }
...
}
};
Depending on how much experience you’ve had with JavaScript, you may be surprised that this is correct syntax. At first this seems like a lot more code than just using the naming convention, but the benefits outweigh the downsides. With this namespacing pattern you would call the functions like this:
google.maps.getGeocode("1600 Pennsylvania Ave. Washington DC");
Now we have the luxury of making longer and more descriptive namespace names (google is much better than just G) without worrying about having to always write out the full namespace for the function. As soon as we get tired of writing google.maps.* everywhere, we can just reference that entire google.maps.* object structure with a shorthand variable like this:
var m = google.maps;
m.getGeocode("1600 Pennsylvania Ave. Washington DC");
Realistically speaking, it is probably not a good idea to stick entire namespace structures into a one character variable name – but it serves as a good example of just how far you can go.
Now the Google maps API is actually way more extensive than what I have outlined here. Every few months, Google comes out with new amazing controls for their maps from simple zooming to alternate map types, miniature navigation, right on down to their street view interface. All the code that handles these map controls should live under the same namespace. You might end up with namespaces that are rather deep:
google.maps.control.streetview.showStreetView("some address");
Now it is really nice to be able to map google.maps.control.streetview to short variable name like streets so you get:
streets.showStreetView("some address");
streets.hideStreetView():
...
This is a lot better than writing GMapsControlStreets_showStreetView() constantly. But there are still problems with using a big fat object declaration to do namespaces. The big nasty factor is that you have to define the entire google.* namespace – maps, search, and RSS – in a single JavaScript file. That is definitely insane especially if you the web developer just want to use Google maps and don’t want to force clients to download all the code for search and RSS too just so you can have pretty namespaces. And just imagine what would happen when Google adds new services to the JavaScript library?
In order to keep developers from going crazy working in one gigantic JavaScript file, different areas of code need to be broken out into different files. As a simple case, we might have one file for each Google web service: maps, search, and RSS (in reality I’m sure they have tens if not hundreds). We would have maps.js, search.js, and rss.js.
To still get our namespace magic we might consider putting in the full namespace object structure in each of the JavaScript files. This solves the problem for someone who just wants to use one of the libraries. They could have HTML like this:
<script type="text/javascript" src="maps.js"></script>
<script type="text/javascript">
google.maps.geocode.getGeocode("1600 Pennsylvania Avenue");
</script>
Unfortunately, if you try to use two libraries on the same page, the namespaces end up colliding and whichever one is loaded last takes precedence.
<script type="text/javascript" src="search.js"></script>
<script type="text/javascript" src="maps.js"></script>
<script type="text/javascript">
google.maps.geocode.getGeocode("1600 Pennsylvania Avenue");
// the below won't work because the namespace structure in maps.js
// redefined the "google" variable from search.js clobbering all the
// search functions.
//google.search.findSimilar("http://someurl.com")
</script>
Ideally we need a way to “merge” code from multiple files into the same namespace. As it turns out, this isn’t actually that difficult.
Safely Constructing Nested Objects
At the top of each JavaScript file, instead of just defining the entire namespace object structure in one fell swoop, we can add to the namespace in stages, at each point making sure not to clobber any existing namespace with the same name. So if we had one JavaScript file, control.js for everything mapped to the google.maps.control namespace, it would look like this:
// setup google.maps namespace safely
var google = google || {};
google.maps = google.maps || {};
// define google.maps.control namespace.
google.maps.control = {
streetview: {
showStreetView: function(address){ ... },
hideStreetView: function(){ ... },
...
},
zoom: {
zoomToLevel: function(level){ ... },
zoomToFit: function(markers){ ... },
...
}
...
};
Now suppose all of the JavaScript files are setup this way. And we are loading several different ones like so:
<script type="text/javascript" src="google.search.js"></script>
<script type="text/javascript" src="google.maps.geocode.js"></script>
<script type="text/javascript" src="google.maps.control.js"></script>
Let’s walk through how the code in these scripts gets executed.
- First search.js is loaded. It has in it the following line:
var google = google || {};
Looking at the google || {} part, since this is the first script to be loaded, there are no existing global variables defined. Thus the variable named google will evaluate to undefined. Normally this would raise a runtime error and the JavaScript would stop processing except that it is part of a boolean statement. in JavaScript, undefined is equivalent to false in boolean expressions, so the next thing that will be evaluated is the {} section. Since {} is equivalent to true in JavaScript, the statement google || {} will evaluate to {}. Remember that or blocks return the first object that evaluates to true, in this case the {}. Now we’ve defined the variable google as a new empty object. The google.search.js file would then go on to attach additional namespaces to the google object. So that once the file is fully loaded we would be able to call
google.search.findSimilar("http://someurl.com");
- Next, the google.maps.geocode.js file gets loaded. This has the same statement:
var google = google || {};
But as we saw in step 1, the google.search.js file already set the google variable to {} and added additional properties to the google object. That means the statement google || {} evaluates to whatever the value of the existing google variable is, since it is equivalent to true in the boolean statement. So in this particular case, the code is effectively equivalent to
var google = google;
which means we are not overriding the entire google namespace defined in google.search.js.
- Next we have a line that looks like:
google.maps = google.maps || {};
This works exactly the same way except that this time, since the google.search.js file did not specify the maps namespace, the expression google.maps || {} evaluates to {} and so we initialize the google.maps namespace.
- By the time google.maps.control.js has loaded, the
google.maps namespace already exists and so we the expression above becomes equivalent to
google.maps = google.maps;
and we have managed to preserver the google.maps.geocode namespace defined in google.maps.geocode.js.
- Finally, we initialize the
google.maps.control namespace in its entirety with the line:
google.maps.control = {
...
};
because we know it is not defined in any other JavaScript file. At the end of this process, once all the JavaScript files are loaded, we have an object structure that looks like:
var google = {
search: {
...
},
maps: {
geocode: {
...
},
control: {
...
}
...
}
};
Namespacing in JavaScript Frameworks
All of this may sound like a lot of work, and the truth is that it is. However, more and more frameworks are starting to use this method for namespacing and have provided helper functions to make it a lot easier. Here are some examples:
Namespacing on the Server
As some additional background on namespacing, here is how some other “modern” programming languages handle it.
- PythonIn Python, namespaces are implicitly constructed from the file system path. If we were writing these
google namespaces in python, we would have a directory tree like so:
google/
__init__.py
search.py
maps/
__init__.py
control.py
geocode.py
...
Then if the google directory were on the python path, you could “import” these sections of the code from python like so:
import google.maps.control
google.maps.control.showStreetView()
or alternatively like so:
from google.maps import control
control.showStreetView()
You can see that we were able to replicate this functionality in JavaScript by reassigning a namespace section to a short variable name.
- JavaIn Java, namespaces are explicitly defined in every .java file. The control.java file would look like this:
package google.maps.control;
public class StreetView {
public static void showStreetView(){ ... }
}
Although the package is defined explicitly in every .java file, the .java files themselves are typically placed in a directory structure just like python. You would then access the namespace from another file like so:
import google.maps.control.StreetView;
public class Foo {
public static void main(String[] args){
StreetView.showStreetView();
}
}
Conclusions
From the Python and Java examples of namespacing, it is pretty clear that namespacing has become a fundamental part of modern programming languages – especially languages designed for building complex and maintainable software (even ActionScript has namespacing these days). Although JavaScript was not originally intended for building complex and powerful software, the demands of the market and the constraints of the browser have forced JavaScript in that direction. If you are a front-end developer that’s new to advanced JavaScript development, I urge you to take the leap now and start using namespaces. Although namespacing might be a conceptual high jump to take at first, you will be much happier in the long run with well organized code. If you are a back-end developer that fears JavaScript due to its lack of formalized patterns, fear not! There are ways to replicate the high level patterns you know and love and the entire web development community will benefit from your involvement in front-end technologies.
If there is anything I didn’t explain well enough, please let me know and I will try to augment that section with more descriptive text.