Classy JavaScript with dojo.declare

The dojo.declare method is the foundation of class creation within the Dojo Toolkit. dojo.declare allows for multiple inheritance to allow developers to create flexible code and avoid writing the same code routines. Dojo, Dijit, and Dojox modules all use dojo.declare; in this tutorial, you'll learn why you should too.

  • Difficulty: Beginner
  • Dojo Version: 1.6

Getting Started

There are some key concepts to review before diving into creating classes with dojo.declare.

dojo.provide

The dojo.provide statement makes the resources contained within the file available. These resources may be a series of classes, functions, or a collection of dojo.require statements pulling in other resources. dojo.provide plays a key role in letting the other widgets and resources within page know the presence of a given class.

dojo.require

dojo.require's role in class creation is providing dependencies to new class. A resource must be loaded for a new class to inherit from it.

The roles of dojo.provide and dojo.require will become clearer within this tutorial.

Basic Dojo Class Creation with Dojo

The dojo.declare method is baked into the base Dojo object, so there are no additional dependencies to load. dojo.declare accepts three arguments: className, superClass, and properties.

ClassName

The className argument represents the name of the class, including the namespace, to be created. Named classes are placed within the global scope. The className can also represent the inheritance chain via the namespace.

Named Class
// Create a new class named "mynamespace.myClass"
dojo.declare("mynamespace.myClass",null,{

	// Custom properties and methods here

});

A class named mynamespace.myClass is now globally available within the application.

"Anonymous" Class
// Create a scoped, anonymous class
var myClass = dojo.declare(null,{

	// Custom properties and methods here

});

The myClass is now only available within its given scope.

SuperClass(es)

The SuperClass argument can be null, one existing class, or an array of existing classes. If a new class inherits from more than one class, the first class in the list will be the base prototype, the rest will be considered "mixins".

Class with No Inheritance
// Create a new class named "mynamespace.myClass"
dojo.declare("mynamespace.myClass",null,{

	// Custom properties and methods here

});

null signifies that this class has no classes to inherit from.

Class Inheriting from Another Class
// Create a new class named "mynamespace.myClass"
dojo.declare("mynamespace.myClass",mynamespace.myParentClass,{

	// mynamespace.myClass now has all of mynamespace.myParentClass's properties and methods
	// These properties and methods override parent's

});

The new mynamespace.myClass will inherit mynamespace.myParentClass's properties and methods. A parent class' method or property can be overridden by adding its key with a new definition within the third argument, which will be explained shortly.

Class with Multiple Inheritance
// Create a new class named "mynamespace.myClass"
dojo.declare("mynamespace.myClass",[
	mynamespace.myParentClass,
	mynamespace.myOtherClass,
	mynamespace.myMixinClass
],
{

	// mynamespace.myClass now has all of the properties and methods from:
	// mynamespace.myParentClass, mynamespace.myOtherClass, and mynamespace.myMixinClass

});

An array of classes signifies multiple inheritance. Properties and methods are inherited from left to right. The first class in the array serves as the base prototype, then the subsequent classes are mixins to that class.

If a property or method is specified in more than one inherited class, the property or method from the last inherited class is used.

Properties and Methods Object

The last argument of dojo.declare is an object containing methods and properties for the class prototype. Properties and methods provided via this argument will override their same namesake if inherited classes have the same properties.

Custom properties and Methods
// Class with custom properties and methods
dojo.declare("mynamespace.Class",[mynamespace.MyParentClass],{
	// Any property
	myProperty1: 12,
	// Another
	myOtherProperty: "Hello",
	// A method
	myMethod: function() {

		// Perform any functionality here

		return result;
	}
})

Example: Basic Class Creation and Inheritance

The following code creates the dijit.form.CurrencyTextBox widget found within Dijit:

dojo.declare(
	"dijit.form.CurrencyTextBox",
	dijit.form.NumberTextBox,
	{

		currency: "",

		baseClass: "dijitTextBox dijitCurrencyTextBox",

		regExpGen: function(constraints){
			// if focused, accept either currency data or NumberTextBox format
			return '(' + (this._focused? this.inherited(arguments, [ dojo.mixin({}, constraints, this.editOptions) ]) + '|' : '')
				+ dojo.currency.regexp(constraints) + ')';
		},

		// Override NumberTextBox._formatter to deal with currencies, ex: converts "123.45" to "$123.45"
		_formatter: dojo.currency.format,

		_parser: dojo.currency.parse,

		parse: function(/*String*/ value, /*Object*/ constraints){
			// summary:
			// 		Parses string value as a Currency, according to the constraints object
			// tags:
			// 		protected extension
			var v = this.inherited(arguments);
			if(isNaN(v) && /\d+/.test(value)){ // currency parse failed, but it could be because they are using NumberTextBox format so try its parse
				v = dojo.hitch(dojo.mixin({}, this, { _parser: dijit.form.NumberTextBox.prototype._parser }), "inherited")(arguments);
			}
			return v;
		},

		_setConstraintsAttr: function(/*Object*/ constraints){
			if(!constraints.currency && this.currency){
				constraints.currency = this.currency;
			}
			this.inherited(arguments, [ dojo.currency._mixInDefaults(dojo.mixin(constraints, { exponent: false })) ]); // get places
		}
	}
);

From the snippet above, it's easy to conclude:

  • The class' name is dijit.form.CurrencyTextBox
  • The class inherits from dijit.form.NumberTextBox (and thus NumberTextBox's dependencies)
  • The class sets many custom properties and methods

Let's dig deeper into class creation with Dojo by learning about the constructor method.

The constructor Method

Once of the special class methods is the constructor method. The constructor method is fired upon class instantiation, executed in the scope of the new object. This means that the this keyword references the instance, not the original class. The constructor method also accepts any number of instance-specific arguments.

// Create a new class
dojo.declare("mynamespace.Twitter",null,{
	// The default username
	username: "defaultUser",

	// The constructor
	constructor: function(args) {
		dojo.safeMixin(this,args);
	}
});

Take the following instance creation:

var myInstance = new mynamespace.Twitter();

The username used within this instance will be "defaultUser" since no specific settings were provided to the instance. To leverage the safeMixin method, provide a username parameter:

var myInstance = new mynamespace.Twitter({
	username: "sitepen"
});

Now the instance uses sitepen as the username setting!

dojo.safeMixin is also useful in class creation and inheritance. As the API docs state:

This function is used to mix in properties like dojo._mixin does, but it skips a constructor property and decorates functions like dojo.declare does. It is meant to be used with classes and objects produced with dojo.declare. Functions mixed in with dojo.safeMixin can use this.inherited() like normal methods. This function is used to implement extend() method of a constructor produced with dojo.declare().

dojo.safeMixin is the perfect ally when creating classes with numerous options.

Inheritance

As stated above, inheritance is defined within the second argument of dojo.declare. Classes are mixed-in from left to right with each subsequent class' properties and methods getting priority over the previous if a property has already been defined. Take the following:

// Define class A
dojo.declare("mynamespace.A",null,{
	// A few properties...
	propertyA: "Yes",
	propertyB: 2
});

// Define class B
dojo.declare("mynamespace.B",mynamespace.A,{
	// A few properties...
	propertyA: "Maybe",
	propertyB: 1,
	propertyC: true
});

// Define class C
dojo.declare("mynamespace.C",[mynamespace.A,mynamespace.B],{
	// A few properties...
	propertyA: "No",
	propertyB: 99,
	propertyD: false
});

The result of the inherited class properties is:

// Create an instance
var instance = new mynamespace.C();

// instance.propertyA = "No" // overridden by B, then by C
// instance.propertyB = 99 // overridden by B, then by C
// instance.propertyC = true // kept from B
// instance.propertyD = false // created by C

Setting and overriding simple Number, String, and boolean variables is straight-forward. Be careful when using arrays and objects with dojo.declare to ensure they are properly scoped. Arrays and objects defined in the return are shared by all instances of the object. Arrays and objects defined in the constructor are properties of each object instantiation. Refer to the dojo.declare documentation for additional information.

this.inherited

While completely overriding methods is certainly useful, sometimes the constructor of each class up through the inheritance chain should execute to preserve its original functionality. This is where the this.inherited(arguments) statement comes in handy. The this.inherited(arguments) statement calls the parent class' method of the same name. Consider the following:

// Define class A
dojo.declare("mynamespace.A",null,{
	myMethod: function() {
		console.log("Hello!");
	}
});

// Define class B
dojo.declare("mynamespace.B",mynamespace.A,{
	myMethod: function() {
		// Call A's myMethod
		this.inherited(arguments); // arguments provided to A's myMethod
		console.log("World!");
	}
});

// Create an instance of B
var myB = new mynamespace.B();
myB.myMethod();


// Would output:
//		Hello!
//		World!

The this.inherited method can be called at any time within the child class' code. There will be some cases where you will want to call inherited() in the middle of the child function, or even at the end. That said, you should not call it from within the constructor.

Conclusion

The dojo.declare method is the key to creating modular, reusable classes with the Dojo Toolkit. dojo.declare allows for complex class recreation with multiple inheritance and any number of properties and methods. Better yet is that dojo.declare is simple to learn and will allow developers to avoid repeating code.

dojo.declare Resources

Looking for more detail about dojo.declare and class creation? Check out these great resources:

Error in the tutorial? Can’t find what you are looking for? Let us know!