Aug
24
2010

Writing Modular JavaScript with Dojo

We start this post with a quotation from our dear old friend, JavaScript Hater.

JavaScript?

Yeah, it’s great. I just love writing a big ol’ pile of Spaghetti Code.

Photo Source Used under Creative Commons Attribution 2.0 Generic License

Now, before we rush out and buy this T-shirt for him, let’s recognize that Hater has a point. There is no programming language that allows you to go from Zero to Incomprehensible quite like JavaScript (Ok fine, we’ll throw in Perl too). In academic terms, Hater is referring to a lack of cohesion. *Blows dust off of Software Engineering book from my RIT days*.

co•he•sion

-noun

1. A measure of the extent to which related aspects of a system are kept together in the same module, and unrelated aspects are kept out.

Is it possible to write highly cohesive code with JavaScript? Absolutely. And when you use Dojo, it’s incredibly easy. All you need is dojo.provide() and dojo.require().

To demonstrate, let’s engage in one of my favorite Enterprise past-times, presenting an over-engineered example to illustrate a simple point! We’re going to model Phone objects and provide an API to indentify the Phone based on its properties. To show you that I take over-engineering seriously, I’ll use a UML diagram.

For those of you that don’t have a background in UML, I’ll explain what is going on here. We have a example.smartphone.Phone class with two fields; numberOfGs, which is a number, and hasTheWiFis, which is a boolean. The class has one method, identifyYourself(). This object has a dependency on example.smartphone.util. The example.smartphone.util object in turn has a dependency on example.smartphone.types, which contains string constants describing the phones in our system.

Bringing modularity to this system is straightforward. We will create a file for each of the objects, like so:

Hold on, JavaScript Hater has something to say about my file structure…

Umm, Hello? Dan, these are JavaScript files that we are talking about here. At the end of the day, each of those files is going to be included in our page with a script element, and we definitely want to keep the number of script elements to a minimum. And what about those dependencies?! Not only we will have to manage lots of script elements, we’ll have to keep track of the order them in the page….and what if we add new files with other dependencies? See?! This is why I hate JavaScript. Who cares about cohesion?! If we must use JavaScript, let’s just throw all that code into one file and be done with it!

As much as I hate to admit it, Hater, again, has a point. Managing JavaScript file dependencies has traditionally been a pain. JavaScript doesn’t have built-in dependency management like some of our favorite statically typed languages. This isn’t a dire situation though; we just need a system to manage this madness. Enter dojo.provide() and dojo.require(), true partners in the fight against incohesive JavaScript.

Dojo Resources and Modules

In Dojo parlance, individual JavaScript files are known as resources and the folder structures that contain related resources are called modules. In our example, Phone.js, types.js, and util.js are resources that live in a module called example.smartphone.

Let’s take a look at types.js. Here it is in its entirety.

dojo.provide("example.smartphone.types");

example.smartphone.types.IPHONE_4 = "iPhone 4";
example.smartphone.types.HTC_EVO = "HTC Evo";
example.smartphone.types.OTHER = "Whoah. I think this may be Gordon Gekko's phone in 'Wall Street'.";

On line 1 we add a call to dojo.provide. A call to dojo.provide will be the first line in all of our resources. dojo.provide is very similar to Java’s package system. The argument to dojo.provide is a dot-separated string that describes the underlying file structure of the resource. In this case, our resource is located at example/smartphone/types.js. (This path is relative to the parent folder of the dojo.js file. That is, the example folder needs to be a peer with dojo, dijit, and dojox. When you place your modules here, Dojo will find them automatically. You can put modules in other locations, but must utilize dojo.registerModulePath or set the modulePaths argument on the djConfig variable. For simplicity, we’ll assume the example folder is a peer to dojo, dijit, and dojox).

Anywhere in consuming code where we want to use this resource, we use dojo.require:

dojo.require("example.smartphone.types");

It’s as simple as that. It’s so simple that most people don’t even begin to wonder how it works. ‘Yeah, Dojo does some magic to load that file, cool.’ But we are not ‘most people’. At Enterprise Dojo, we recognize that Dojo is not, in fact, magic. It’s JavaScript. We are JavaScript Scholars here, and we aren’t comfortable with dismissing API as magic. In addition, understanding how dojo.require() and dojo.provide() work truly helps in using them effectively. So we’ll take a little detour to dive into it.

A Deep Dive into dojo.require and dojo.provide

There are several great tutorials out there on this topic. You should read them all, but I’ll give a quick overview of what’s happening here. When dojo.require(“example.smartphone.types”); runs, it loads and executes the JavaScript file at example\smartphone\types.js. The first line it executes is dojo.provide(“example.smartphone.types”);. This line creates a namespace that is modeled after the file structure of the module. Simply put, a namespace is a global variable with nested sub-variables. In this case, dojo.provide creates a global variable called example, adds a sub-variable to it called smartphone, and then adds a sub-variable to smartphone called types.

This screen-shot from the DOM tab of Firebug directly after calling dojo.provide(“example.smartphone.types”) should help you understand:

Now that we have this nice object hierarchy, we can add our constants to it using regular JavaScript notation. See highlighted lines below:

dojo.provide("example.smartphone.types");

example.smartphone.types.IPHONE_4 = "iPhone 4";
example.smartphone.types.HTC_EVO = "HTC Evo";
example.smartphone.types.OTHER = "Whoah. I think this may be Gordon Gekko's phone in 'Wall Street'.";

Here is what it looks like in memory:

Similarly, here is the code for util.js:

dojo.provide("example.smartphone.util");

dojo.require("example.smartphone.types");

example.smartphone.util.calculatePhoneType = function(phone) {
	if(phone.numberOfGs == 3) {
		return example.smartphone.types.IPHONE_4;
	} else if(phone.numberOfGs == 4) {
		return example.smartphone.types.HTC_EVO;
	} else {
		return example.smartphone.types.OTHER;
	}
};

The call to dojo.provide on line 1 adds a util variable to our existing example.smartphone namespace. On line 5 we add a function called calculatePhoneType to the util object. Show them what I mean, Firebug:

Adding dojo.declare Into the Mix

Lastly, let’s take a look at example.smartphone.Phone. Our two previous examples were singletons. There is no need to have more than one instance of util or types in our system. Phone is different. It needs to be a Class so that we can instantiate multiple instances of it. For that we will use Dojo’s mechanism of simulating Classes, dojo.declare.

The arguments to dojo.declare are the Class’s name, followed by its super types, and finally an object that contains its properties. Here’s how we will use it to declare a Phone class. This is the entire contents of example/smartphone/Phone.js:

dojo.provide("example.smartphone.Phone");

dojo.require("example.smartphone.util");

dojo.declare("example.smartphone.Phone", [], {
	numberOfGs : null,
	hasTheWiFis : null,
	constructor : function(numGs, wiFis) {
		this.numberOfGs = numGs;
		this.hasTheWiFis = wiFis;
	},
	identifyYourSelf : function() {
		return example.smartphone.util.calculatePhoneType(this);
	}
});

Walking through this code, the dojo.provide in line 1 sets up our namespace, adding a simple object named ‘Phone’ onto the example.smartphone namespace. Line 3 specifies our dependency on example.smartphone.util, and Line 5 does the job of declaring our Phone class. Notice how our class name is the same as what we passed to dojo.provide. When the dojo.declare runs, the Phone object in memory is replaced by a constructor function that we use to instantiate Phone objects. These Firebug captures illustrate what I mean:

Right after dojo.provide(‘example.smartphone.Phone’):

After dojo.declare(‘example.smartphone.Phone’):

This is a little confusing. You may be wondering what would have happened if we chose to name our Class something other than example.smartphone.Phone. Or what if we had more than one dojo.declare in our resource? The answer is that dojo.declare will indiscriminately create new namespaces for any of these scenarios. If we added dojo.declare(“some.other.namespace”, …) to the end of Phone.js, we’d end up with this:

The practice of dojo.declare’ing a class with the same name as the resource in which it is declared is simply a pattern for neatly organizing our code, and is used extensively by Dojo internally. I haven’t even scratched the surface of dojo.declare. It enables inheritance and all sorts of other Object Oriented goodness. Read the details here.

Wrapping Things Up

We now have every piece of our system implemented, all neatly packaged, maintaining the highest level of cohesion. Professor Lutz would be so proud. And we owe it all to dojo.require and dojo.provide. Nice work guys.

Photo Source Used under Creative Commons Attribution 2.0 Generic License

Finally, let’s write a little program to test out our code:

dojo.require("example.smartphone.Phone");

var phone1 = new example.smartphone.Phone(3, true);
var phone2 = new example.smartphone.Phone(4, true);
var phone3 = new example.smartphone.Phone(2, false);

console.log("Phone 1 is: " + phone1.identifyYourSelf());
console.log("Phone 2 is: " + phone2.identifyYourSelf());
console.log("Phone 3 is: " + phone3.identifyYourSelf());

Output from Firebug’s console:

Did you notice how we only had to call dojo.require on example.smartphone.Phone and not any of the other objects in our system? That’s not a typo. Because our objects specify their dependencies internally via calls to dojo.require, the dependent resources are loaded automatically. Now that is how you MANAGE DEPENDENCIES!!! Sorry, I get excited about this stuff.

That may have seemed like a lot of work to get something simple going, and admittedly the benefits to structuring your code this way are not glaringly obvious from this example. But when your real-world JavaScript application grows, and it will, you may have hundreds of resources with seriously complex dependencies. In these types of systems, cohesion and dependency management are not only helpful, they are absolutely essential.

You may also have performance concerns due to the costly time that it takes to load multiple JavaScript files. This is addressed by the Dojo Build System, the details of which are a post for another day.

If you’d like to play around with the source code presented in this post, download this zip file.

Related Posts

About the Author: Dan Lee

Dan Lee is a Software Developer and Dojo enthusiast. He is the creator and co-developer of the ReminderFox Firefox add-on and the addictive iOS game, Rhymo. Find him on Twitter here. Any opinions expressed on this blog are his own, and not of his employer.

8 Comments + Add Comment

  • Rebecca Murphey recently wrote a very similar post: http://blog.rebeccamurphey.com/scaffolding-a-buildable-dojo-application touches on what you mentioned plus provides a forkable github repository for scaffolding a basic application with build steps included.

    Also, with regard do declaring with no inheritance: I would pass `null` as opposed to an empty array. The presence of the array adds a negligible cost to determine the parameter (as opposed to the falsy `null`) … I might even go so far as to suggest using “plain” js constructor format, and use declare() if you need classical inheritance. eg:

    dojo.provide(“my.Thinger”); // module

    // `my` is already an empty obj because of provide()
    my.Thinger = function(args){
    dojo.mixin(this, args);
    }
    dojo.extend(my.Thinger, {
    /* props */
    });
    // teh subclass
    dojo.declare(“my.ThingerSubclass”, my.Thinger, { /* props */ });

    Otherwise great stuff! Keep on rockin’

    • Good point about the []. I use the empty array to remind myself that the super-types argument is an array of types. But I see from the doc that it can accept a single object as well. So, ‘null’ probably is the best option there when there are no super-types.

      In regards to not using dojo.declare, sorry, that’s just not going to happen :) I’m a True Believer in JavaScript, but my programming roots are in strong, statically typed languages, just like a lot of my Enterprise peeps. The charms of formally declaring a ‘Class’ make dojo.declare far too attractive for us not to use!

  • Great stuff. Auto-gen of objects and sub-objects seems really slick. Keep the articles coming. Solid thought-bombs therein. :)

  • Interesting stuff, Dan! A question though, from someone who is unfamiliar with Dojo – while this does reduce the amount of script tags on your page, I assume dojo still has to fetch each script file required individually? Doesn’t that result in a potentially huge amount of http requests on page load as the amount of files grow?

    Also, how does this work together with tools for combining js files into one big file (such as Squishit)?

    • Hi Thomas! Your question is a good one, I can tell that you are not a stranger to the world of web site performance improvement :)

      When it is time to build a performant, deploy-ready version of your web-app, that is when you turn to the Dojo Build System. http://www.dojotoolkit.org/reference-guide/build/index.html

      Using our example here, we could use the Dojo Build System to create a single ‘layer’ .js file that contained the contents of Phones.js, types.js, and util.js. This layer file is a minified, 1-line version of our resources. We would then include this layer file in our page using a regular ol’ script element. Once we have the layer file included, the dojo.require’s become no-ops because the very first thing that dojo.require does is check to see if the required resource is already in memory. If it is, it quickly returns.

      The Build System understands dojo.require, so it is smart enough to pack all of that JavaScript in the right order. Cool, right?

      • Excellent! I’ll have to check that out. Thanks.

        I think I may just have to learn Dojo, if nothing else to get some new perspectives on my javascript development. :)

        • If you learn best by reading books, as I do, I highly recommend ‘Dojo: The Definitive Guide’. It is a tad out-of-date and is missing some of the latest goodies that are in Dojo, but it serves as an *excellent* introduction to what is possible with the Dojo Toolkit. Go get it!

  • Could this topic (“Writing modular JavaScript with DOJO”) be explained any simpler than this? I seriously doubt! Kudos Dan!!

Leave a comment