Understanding dojo.declare, dojo.require, and dojo.provide

Dojo is a great toolkit, which gives us many helpful utilities. On of the things that I really like about it is how it provides a more understandable layer of organization and packaging above what JavaScript normally provides.

Some of this syntax is very Java-like. Your classes are organized into hierarchies, similar to java - and you are encouraged to follow a similar reverse-DNS naming convention. Groups of classes are organized into packages - and those packages are laid out, on disk, as a directory structure.

However, there are some differences between Dojo syntax and Java syntax, and quite a few questions will arise if you confuse the two. Mainly, this is due to the constraints placed on us by needing to conform to valid Javascript code - and many of these confusions arise around using the commands dojo.declare, dojo.require, and dojo.provide.

For example, in Java, you declare a class like this:

package com.toonetown.app;
public class Thing1 {
	// Code for com.toonetown.app.Thing1 Here
}

This would be placed in a file in a path of com/toonetown/app/Thing1.java

A corresponding Dojo class would be written like this:

dojo.provide("com.toonetown.app.Thing1");
dojo.declare("com.toonetown.app.Thing1", null, {
	// Code for com.toonetown.app.Thing1 Here
});

This would be placed in a file in a path of com/toonetown/app/Thing1.js

If you want to inherit from another class, you would do the following in Java (in a file com/toonetown/app/Thing2.java):

package com.toonetown.app;
import com.toonetown.app.Thing1;
public class Thing2 extends Thing2 {
	// Code for com.toonetown.app.Thing2 Here
}

And in Dojo (in com/toonetown/app/Thing2.js):

dojo.provide("com.toonetown.app.Thing2");
dojo.require("com.toonetown.app.Thing1");
dojo.declare("com.toonetown.app.Thing2", com.toonetown.app.Thing1, {
	// Code for com.toonetown.app.Thing2 Here
});

Now, let’s discuss the various parts.

dojo.provide

dojo.provide is similar to Java’s package keyword. It is used to declare the structure of the files. In Java, the compiler will use this information to do its thing. In Dojo, either the loader or the build system will use this information to pull the correct file and load it into the javascript execution environment.

In Java, the combination of the package keyword and the file name are used to obtain the fully-qualified name of the class. In Dojo, you must specify the full name (which is why the word provide is used, instead of package).

One thing to note - dojo.provide takes a string as its argument - since, at the time the loader parses and runs that function, the given class does not yet exist.

dojo.require

dojo.require is similar to Java’s import keyword. It tells the loader or the build system what additional files to pull in. These files will be pulled in, and any code in them executed, *before* the rest of your file gets executed - thus ensuring that any classes that are dojo.declare-ed in them will be available to you.

Similar to dojo.provide, dojo.require takes a string as its argument - again, the class you are requiring may or may not exist at that point.

The thing to remember is that dojo.require and dojo.provide are pairs. You must require exactly what you provide. Also, what you require and provide corresponds DIRECTLY to the file name (and path) of the file you want to load.

dojo.declare

dojo.declare is similar to Java’s class keyword. It tells the execution environment to create an object with the given identifier.

The first parameter is a string (since the class you are declaring doesn’t yet exist). The second parameter is *not* a string - rather, it is the class that you wish to extend from (or an array of classes, if you like). The third parameter is an object that contains the code you want in your new class.

Best Practice

It is important to note, that while best practices dictate that you dojo.declare a class name that matches what you dojo.provide-ed, that is not a requirement. You could very easily do the following:

dojo.provide("com.toonetown.app.Thing3");
dojo.require("com.toonetown.app.Thing2");
dojo.declare("com.toonetown.app.MyThing", com.toonetown.app.Thing2, {
	// Code for com.toonetown.app.MyThing Here
});

This would need to go in a file called com/toonetown/app/Thing3.js - because that’s what matches the dojo.provide function. If you wanted to include this class in another one, you would need to use dojo.require("com.toonetown.app.Thing3") - again…it needs to match the file name.

Once you have this class included in another file, however, you would refer to the class as com.toonetown.app.MyThing - since that is what it has been dojo.declare-ed as.

This flexibility is nice, when you are creating and declaring things like private classes and static objects - but it is something that you need to be aware of when you are setting up your class structure.

Tags: