Chapter 10. Simulated Classes and Inheritance

Even though JavaScript loosely approximates classes with Function objects and offers a prototype-based inheritance mechanism that's a little different from what you may be used to if your background is a language like Java or C++, Dojo does a fine job of building upon native JavaScript constructs to simulate classes and class-based inheritance. This chapter dives into dojo.declare, the toolkit's vehicle for wiring up classes and inheritance and, in doing so, paves the way for a solid understanding of the Dijit infrastructure, which is coming up in Part II.

JavaScript Is Not Java

Before we get into the core discussion of simulating inheritance hierarchies and classes with Dojo, you must first grok that JavaScript is not Java, nor does Dojo try to fight against JavaScript's style and reimplement portions of the JavaScript language under the hood and force a square peg in a round hole—and this is a very good thing! JavaScript is an incredibly dynamic, weakly typed language, while Java is a more strongly typed language with real classes and class-based inheritance hierarchies that are defined at compile time. JavaScript has prototypal inheritance that can be used to simulate classes and is purely interpreted.

By embracing JavaScript for what it is and leveraging its language features in the most complementary way possible, Dojo benefits from enhancements to the language as it evolves, avoids the maintenance that comes hand-in-hand with massive layers of boilerplate. In the end, this provides you with a streamlined, agile implementation that can keep up with the "release early, release often" philosophy of the modern technological era.

With the notable exception of the form dijits that are introduced in Chapter 13, you won't see a lot of object-oriented design in Dojo because it's not the Dojo way. The Dojo way is to embrace prototypal inheritance and Base constructs like mixin and extend that take advantage of JavaScript's strengths. At the same time, Dojo does try to be pragmatic, and some topics do lend themselves to object-oriented design and are quite awkward to model without it. Actually, the very reason that this chapter is presented as the last of Part I is because Part II introduces Dijit, which is a topic that lends itself quite well to a class-based design.

As you read this chapter, keep this preamble in the back of your mind, because if you come from a strong background in a class-based object-oriented programming language like Java or C++, the temptation will be to superimpose that paradigm onto Dojo and to try to turn everything into an inheritance relationship, which isn't a very natural thing to do for reasons of style or performance. While this chapter does demonstrate the ability to use Dojo for the purpose of simulating deep hierarchies of objects, the assumption is that you'll use discretion when doing so.

One Problem, Many Solutions

In case you're locked into the inheritance paradigm, this section demonstrates multiple approaches to achieving similar results for a simple problem: modeling a circle object. In addition to being a good exercise in demonstrating some out-of-the-box thinking, this section also applies some of the language tools you learned about in Chapter 2.

Typical JavaScript Inheritance

JavaScript programmers have been simulating classes with Function objects for quite a while—sometimes at the expense of abusing the language, other times effectively to solve a particular problem. The inherent nature of a JavaScript Function object is the very mechanism that provides the footing for simulating classes. Namely, it acts as a constructor function that is used in conjunction with the new operator to create object instances, and it provides the template for those object instances that are created.

To illustrate, Example 10-1 provides a short code snippet that approximates a simple Shape class in JavaScript. Note that by convention, classes usually begin with a capital letter.

For just an added touch of simplicity, the examples in this chapter do not use namespaces for qualifying objects. In general, however, you will want to use namespaces, and we'll pick back up using namespaces in Chapter 12.

Example 10-1. A typical JavaScript class
// Define a class
function Shape(centerX, centerY, color)
{
  this.centerX = centerX;
  this.centerY = centerY;
  this.color = color;
};

// Create an instance
s = new Shape(10, 20, "blue");

Once the JavaScript interpreter executes the function definition, a Shape object exists in memory and acts as the prototypal object for creating object instances whenever its constructor function is invoked with the new operator.

For completeness, note that you could have defined the Shape object using Base's extend function in a slightly more compact fashion:

// Create a Function object
function Shape(  ) {}

// Extend its prototype with some reasonable defaults
dojo.extend(Shape, {
    centerX : 0,
    centerY : 0,
    color : ""
});

Unfortunately, you could only have fun with this class for about three seconds because you'd start to get really bored and want to model something a little more concrete—like a specific kind of shape. While you could approximate a new class such as a circle entirely from scratch, a more maintainable approach would be to have a circle class inherit from the shape class that's already defined because all circles are shapes. Besides, you already have a perfectly good Shape class lying around, so why not use it?

Example 10-2 demonstrates one approach to accomplishing this inheritance relationship in JavaScript.

Example 10-2. Typical JavaScript inheritance
// Define a subclass
function Circle(centerX, centerY, color, radius)
{
  // Ensure the subclass properties are added to the superclass by first
  //assigning the subclass a reference to the constructor function and then
  //invoking the constructor function inside of the superclass.
  this.base = Shape;
  this.base(centerX, centerY, color);

  // Assign the remaining custom property to the subclass
  this.radius = radius;
};

// Explicitly chain the subclass's prototype to a superclass so that any
new properties
//that are dynamically added to the superclass are reflected in subclasses
Circle.prototype = new Shape;

// Create an instance
c = new Circle(10, 20, "blue", 2);

//The circle IS-A shape

While you may have found that to be an interesting exercise, it probably wasn't as short and sweet as you might have first thought, and it probably wasn't terribly central to that really cool web application you've been trying to finish up.

Mixin Pattern

For the sake of demonstrating an alternate paradigm to the typical inheritance groupthink, consider Example 10-3's approach of using mixins to model shapes and circles in a different way. It's especially noteworthy to make the connection that mixins heavily leverage duck typing and has-a relationships. Recall that the concept of ducktyping is based upon the idea that if something quacks like a duck and acts like a duck, then it may as well be a duck. In our circumstance, the concept translates to the idea that if an object has the properties you'd expect of a shape or circle, that's good enough to call it as much. In other words, it doesn't matter what the object really is as long as it has the right properties.

Example 10-3. Mixing in as an alternative to inheritance
//Create a plain old Object to model a shape
var shape = {}

//Mix in whatever you need to make it "look like a shape and quack like a shape"
dojo.mixin(shape, {
    centerX : 10,
    centerY : 20,
    color : "blue"
});
//later on you need something else. No problem, mix it right in
dojo.mixin(shape, {
    radius : 2
});

//Now the shape HAS-A radius

For the record, this mixin example is not intended to be an exact drop-in replacement for the previous example that used prototypal inheritance; rather, this mixin example is intended to demonstrate that there are various ways of approaching a problem.

Delegation Pattern

As yet another approach to modeling a relationship between a shape and a circle, consider the pattern of delegation, shown in Example 10-4. Whereas the mixin pattern actually copies properties into a single object instance, the delegation pattern passes on responsibility for some set of properties to another object that already has them.

Example 10-4. Delegation as an alternative to inheritance
//Create a plain old Object
var shape = {}

//Mix in what you need for this instance
dojo.mixin(shape, {
    centerX : 10,
    centerY : 20,
    color : "blue"
});

//delegate circle's responsibility for centerX, centerY, and color to shape
//mix in the radius directly
circle = dojo.delegate(shape, {
    radius : 2
});

The key takeaways from this revision are that the radius property defined in the object literal is mixed into the circle, but the remaining shape properties are not. Instead, the circle delegates to the shape whenever it is asked for a property that it does not have itself. To sum it all up:

  • Requests for radius are provided directly by circle because radius got mixed in.

  • Requests for centerX, centerY, and color are delegated to the shape because they don't exist on the circle itself (loosely speaking).

  • A request for any other property returns undefined by definition because it doesn't exist in either the circle or the shape.

Although the working example is so simple that the mixin pattern makes more sense to use, the delegation pattern certainly has plenty of uses, especially in situations in which you have large number of objects that all share a particular subset of things that are in common.

Simulating Classes with Dojo

Now that you've had a moment to ponder some of the various inheritance possibilities, it's time to introduce the toolkit's fundamental construct for declaring classes and simulating rich inheritance hierarchies. Dojo keeps it simple by tucking away all of the implementation details involved with class declarations and inheritance behind an elegant little function in Base called dojo.declare. This function is easy to remember because you're loosely declaring a class with it. Table 10-1 shows the brief API.

Table 10-1. dojo.declare API

Name

Comment

dojo.declare (/*String*/ className,

/*Function|Function[]*/ superclass,

/*Object*/ props)

Provides a compact way of declaring a constructor function. The className provides the name of the constructor function that is created, superclass is either a single Function object ancestor or an Array of Function object ancestors that are mixed in, and props provides an object whose properties are copied into the constructor function's prototype.

As you might suspect, declare builds upon the patterns provided by functions like extend, mixin, and delegate to provide an even richer abstraction than any one of those patterns could offer individually.

Example 10-5 illustrates how you could use dojo.declare to accomplish an inheritance hierarchy between a shape and circle. For now, consider this example as just an isolated bit of motivation. We'll discuss the finer points momentarily.

Example 10-5. Simulating class-based inheritance with dojo.declare
// "Declare" a Shape
dojo.declare(
  "Shape", //The class name
  null, //No ancestors, so null placeholds
  {
    centerX : 0, // Attributes
    centerY : 0,
    color : "",

    // The constructor function that gets called via "new Shape"
    constructor: (centerX, centerY, color)
    {
      this.centerX = centerX;
      this.centerY = centerY;
      this.color = color;
    }
  }
);

// At this point, you could create an object instance through:
// var s = new Shape(10, 20, "blue");

// "Declare" a Circle
dojo.declare(
  "Circle", //The class name
  Shape, // The ancestor
  {
    radius : 0,

    // The constructor function that gets called via "new Circle"
    constructor: (centerX, centerY, color, radius)
    {
      // Shape's constructor is called automatically
      // with these same params. Note that it simply ignores
      // the radius param since it only used the first 3 named args
      this.radius = radius; //assign the Circle-specific argument
    }
  }
);

// Params to the JavaScript constructor function get passed through
// to dojo.declare's constructor
c = new Circle(10,20,"blue",2);

Hopefully you find dojo.declare to be readable, maintainable, and self-explanatory. Depending on how you lay out the whitespace and linebreaks, it even resembles "familiar" class-based programming languages. The only thing that may have caught you off guard is that Shape 's constructor is called with the same parameters that are passed into Circle 's constructor. Still, this poses no problem because Shape 's constructor accepts only three named parameters, silently ignoring any additional ones. (We'll come back to this in a moment.)

Talking about JavaScript constructor functions that are used with the new operator to create JavaScript objects as well as the special constructor function that appears in dojo.declare 's third parameter can be confusing. To keep these two concepts straight, the parameter that appears in dojo.declare 's third parameter constructor will always be typeset with the code font as constructor, while JavaScript constructor functions will appear in the normal font.

The Basic Class Creation Pattern

The dojo.declare function provides a basic pattern for handling classes that is important to understand because Dijit expands upon it to deliver a flexible creation pattern that effectively automates the various tasks entailed in creating a widget. Chapter 12 focuses on this topic almost exclusively.

Although this chapter focuses on the constructor function because it is by far the most commonly used method, the following pattern shows that there are two other functions that dojo.declare provides: preamble, which is kicked off before constructor, and postscript, which is kicked off after it:

preamble(/*Object*/ params, /*DOMNode*/node)
     //precursor to constructor

constructor(/*Object*/ params, /*DOMNode*/node)
    // fire any superclass constructors
    // fire off any mixin constrctors
    // fire off the local class constructor, if provided

postscript(/*Object*/ params, /*DOMNode*/node)
    // predominant use is to kick off the creation of a widget

To verify for yourself, you might run the code in Example 10-6.

Example 10-6. Basic dojo.declare creation pattern
dojo.addOnLoad(function(  ) {
    dojo.declare("Foo", null, {

        preamble: function(  ) {
            console.log("preamble", arguments);
        },

        constructor : function(  ) {
            console.log("constructor", arguments);
        },

        postscript : function(  ) {
            console.log("postscript", arguments);
        }

});

    var foo = new Foo(100); //calls through to preamble, constructor, and postscript
});

The constructor is where most of the action happens for most class-based models, but preamble and postscript have their uses as well. preamble is primarily used to manipulate arguments for superclasses. While the arguments that you pass into the JavaScript constructor function—new Foo(100) in this case—get passed into Foo 's preamble, constructor, and postscript, this need not necessarily be the case when you have an inheritance hierarchy. We'll revisit this topic again in the "Advanced Argument Mangling" sidebar later in this chapter, after inheritance gets formally introduced in the next section. postscript is primarily used to kick off the creation of a widget. Chapter 12 is devoted almost entirely to the widget lifecycle.

A Single Inheritance Example

Let's dig a bit deeper with more in-depth examples that show some of dojo.declare 's power. This first example is heavily commented and kicks things off with a slightly more advanced inheritance example highlighting an important nuance of using dojo.declare 's internal constructor method:

<html>
    <head>
        <title>Fun with Inheritance!</title>

        <script
            type="text/javascript"
            src="http://o.aolcdn.com/dojo/1.1/dojo/dojo.xd.js">
        </script>

        <script type="text/javascript">
          dojo.addOnLoad(function() {

              //Plain old JavaScript Function object defined here.
              function Point(x,y)  {}
              dojo.extend(Point, {
                  x : 0,
                  y : 0,
                  toString : function(  ) {return "x=",this.x," y=",this.y;}
              });

              dojo.declare(
                  "Shape",
                  null,
                {
                    //Clearly define members first thing, but initialize them all in
                    //the Dojo constructor. Never initialize a Function object here
                    //in this associative array unless you want it to be shared by
                    //*all* instances of the class, which is generally not the case.

                    //A common convention is to use a leading underscore to denote
                    //"private" members

                    _color: "",
                    _owners: null,

                    //Dojo provides a specific constructor for classes. This is it.
                    //Note that this constructor will be executed with the very same
                    //arguments that are passed into Circle's constructor
                    //function -- even though we make no direct call to this
                    //superclass constructor.

                    constructor: function(color)
                    {
                        this._color = color;
                        this._owners = [0]; //See comment below about initializing
                        //objects

                        console.log("Created a shape with color",
                          this._color, "owned by", this._owners);
                    },

                    getColor : function(  ) {return this._color;},
                    addOwner : function(oid) {this._owners.push(oid);},
                    getOwners : function(  ) {return this._owners;}

                    //Don't leave trailing commas after the last element. Not all
                    //browsers are forgiving (or provide meaningful error messages).
                    //Tattoo this comment on the back of your hand.
                }

            );

            //Important Convention:
            //For single inheritance chains, list the superclass's args first in the
            //subclass's constructor, followed by any subclass specific arguments.

            //The subclass's constructor gets called with the full argument chain, so
            //it gets set up properly there, and assuming you purposefully do not
            //manipulate the superclass's arguments in the subclass's constructor,
            //everything works fine.

            //Remember that the first argument to dojo.declare is a string and the
            //second is a Function object.
            dojo.declare(
                "Circle",
                Shape,
                {
                    _radius: 0,
                    _area: 0,
                    _point: null,

                    constructor : function(color,x,y,radius)
                    {
                        this._radius = radius;
                        this._point = new Point(x,y);
                        this._area = Math.PI*radius*radius;

                        //Note that the inherited member _color is already defined
                        //and ready to use here!
                        console.log("Circle's inherited color is " + this._color);
                    },

                    getArea: function(  ) {return this._area;},
                    getCenter : function(  ) {return this._point;}
                }
            );

            console.log(Circle.prototype);

            console.log("Circle 1, coming up...");
            c1 = new Circle("red", 1,1,100);
            console.log(c1.getCenter(  ));
            console.log(c1.getArea(  ));
            console.log(c1.getOwners(  ));
            c1.addOwner(23);
            console.log(c1.getOwners(  ));

            console.log("Circle 2, coming up...");
            c2 = new Circle("yellow", 10,10,20);
            console.log(c2.getCenter(  ));
            console.log(c2.getArea(  ));
            console.log(c2.getOwners(  ));
        });
        </script>
    </head>
    <body>
    </body>
</html>

Trailing commas will most likely hose you outside of Firefox, so take extra-special care not to accidentally leave them hanging around. Some programming languages like Python allow trailing commas; if you frequently program in one of those languages, take added caution.

You should notice the output shown in Figure 10-1 in the Firebug console when you run this example.

Firebug output from
Figure 10-1. Firebug output from Example 10-6

An important takeaway is that a Function object exists in memory as soon as the dojo.declare statement has finished executing, an honest-to-goodness Function object exists behind the scenes, and its prototype contains everything that was specified in the third parameter of the dojo.declare function. This object serves as the prototypical object for all objects created in the future. This subtlety can be tricky business if you're not fully cognizant of it, and that's the topic of the next section.

A common gotcha with prototype-based inheritance

As you know, a Point has absolutely nothing to do with Dojo. It's a plain old JavaScript Function object. As such, however, you must not initialize it inline with other properties inside of Shape 's associative array. If you do initialize it inline, it will behave somewhat like a static member that is shared amongst all future Shape objects that are created—and this can lead to truly bizarre behavior if you're not looking out for it.

The issue arises because behind the scenes declare mixes all of the properties into the Object 's prototype and prototype properties are shared amongst all instances. For immutable types like numbers or strings, changing the property results in a local change. For mutable types like Object and Array, however, changing the property in one location promulgates it. The issue can be reduced as illustrated in the snippet of code in Example 10-7.

Example 10-7. Prototype properties are shared amongst all instances
function Foo(  ) {}
Foo.prototype.bar = [100];

//create two Foo instances
foo1 = new Foo;
foo2 = new Foo;

console.log(foo1.bar); // [100]
console.log(foo2.bar); // [100]

// This statement modifies the prototype, which is shared by all object instances...
foo1.bar.push(200);

//...so both instances reflect the change.
console.log(foo1.bar); // [100,200]
console.log(foo2.bar); // [100,200]

To guard against ever even thinking about making the mistake of inadvertently initializing a nonprimitive data type inline, perform all of your initialization—even initialization for primitive types—inside of the standard Dojo constructor, and maintain a consistent style. To keep your class as readable as possible, it's still a great idea to list all of the class properties inline and provide additional comments where it enhances understanding.

To illustrate the potentially disastrous effect on the working example, make the following changes indicated in bold to your Shape class and take a look at the console output in Firebug:

//...snip...

dojo.declare("Shape", null,

 {
    _color: null,
    //_owners: null,
    _owners: [0],   //this change makes the _owners member
                    //behave much like a static!

    constructor : function(color) {
    this._color = color;
                    //this._owners = [0];


                    console.log("Created a shape with color ",this._colora
                        " owned by ", this._owners);
                },

                getColor : function(  ) {return this._color;},
                addOwner : function(oid) {this._owners.push(oid);},
                getOwners : function(  ) {return this._owners;}
            }

        );

//...snip...

After you make this change and refresh the page in Firefox, you'll see the output shown in Figure 10-2 in the Firebug Console.

Firebug output
Figure 10-2. Firebug output

Calling an inherited method

In class-based object-oriented programming, a common pattern is to override a superclass method in a subclass and then call the inherited superclass method before performing any custom implementation in the subclass. Though not always the case, it's common that the superclass's baseline implementation still needs to run and that the subclass is offering existing implementation on top of that baseline. Any class created via dojo.declare has access to a special inherited method that, when called, invokes the corresponding superclass method to override. (Note that the constructor chain is called automatically without needing to use inherited.)

Example 10-8 illustrates this pattern.

Example 10-8. Calling overridden superclass methods from a subclass
dojo.addOnLoad(function(  ) {
   dojo.declare("Foo", null, {

       constructor : function(  ) {
           console.log("Foo constructor", arguments);
       },

       custom : function(  ) {
           console.log("Foo custom", arguments);
       }

   });

   dojo.declare("Bar", Foo, {

       constructor : function(  ) {
           console.log("Bar constructor", arguments);
       },

       custom : function(  ) {
           //automatically call Foo's 'custom' method and pass in the same arguments,
           //though you could juggle them if need be
           this.inherited(arguments);
           //without this call, Foo.custom would never get called
           console.log("Bar custom", arguments);
       }

   });

   var bar = new Bar(100);
   bar.custom(4,8,15,16,23,42);
});

And here's the corresponding Firebug output on the console:

Foo constructor [100]
Bar constructor [100]
Foo custom [4, 8, 15, 16, 23, 42]
Bar custom [4, 8, 15, 16, 23, 42]

Multiply Inheriting with Mixins

The previous section introduced how Dojo simulates class-based inheritance and pointed out some critical issues involving JavaScript's finer points that are central to developing with Dojo. Although the primary example demonstrated single inheritance in which a Shape superclass provided the basis for a Circle subclass, Dojo also provides a limited form of multiple inheritance.

The process of defining inheritance relationships by weaving together Function objects in this way is referred to as prototype chaining because it's through JavaScript's Object.prototype property that the hierarchy is defined. (Recall that Example 10-2 illustrated this concept within the boilerplate of manually defining an inheritance relationship between a shape and a circle.)

Dojo simulates class-based inheritance by building upon the concept of prototype chaining to establish the hierarchy for single inheritance contexts. However, employing multiple-inheritance relationships is a little bit different because JavaScript limits Function objects to having only one built-in prototype property.

As you might imagine, there are a number of approaches that could be used to circumvent this issue. The approach that Dojo uses is to leverage prototype chaining so that you define a single prototypical ancestor that is the basis for prototype chaining—but at the same time, allowing you to provide other mixins that get injected into the prototypal ancestor. In other words, a class can have only one prototype, but the Function objects that the class creates can get "stamped" with as many constructor functions as you want to throw at it. Granted, the prototypes of those constructor functions won't be taken into account later in the life of the object, but they can be leveraged in very powerful ways nonetheless. Think of these mixins as "interfaces that actually do stuff" or "interface + implementation."

In multiple-inheritance relationships, the ancestors are provided to dojo.declare inside of a list. The first element of the list is known as the prototypical ancestor, while the latter is commonly a mixin ancestor, or more concisely, a "mixin."

Here's what multiple inheritance looks like with Dojo. The only thing that's different is that the third parameter to dojo.declare is a list instead of a Function object. The first element in that list is the prototypical ancestor, while the other is a mixin:

<html>
    <head>
        <title>Fun with Multiple Inheritance!</title>

        <script
            type="text/javascript"
            src="http://o.aolcdn.com/dojo/1.1/dojo/dojo.xd.js">
        </script>
        <script type="text/javascript">
            dojo.addOnLoad(function(  ) {
                //A perfectly good Dojo class with a reasonable constructor and no
                //direct ancestors.
                dojo.declare("Tiger", null, {
                    _name: null,
                    _species: null,

                    constructor : function(name)
                    {
                        this._name = name;
                        this._species = "tiger";
                        console.log("Created ",this._name +,"the ",this._species);
                    }
                });

                //Another perfectly good Dojo class with a reasonable constructor
                //and no direct ancestors.
                dojo.declare("Lion", null, {
                    _name: null,
                    _species: null,

                    constructor: function(name)  {
                        this._name = name;
                        this._species = "lion";
                        console.log("Created ",this._name +," the ",this._species);
                    }
                });

                //A Dojo class with more than one ancestor. The first ancestor is the
                //prototypical ancestor, while the second (and any subsequent
                //functions) are mixins. Take special note that each of the
                //superclass constructors execute before this subclass's constructor
                //executes -- and there's really no way to get around that.
                dojo.declare("Liger", [Tiger, Lion], {
                    _name: null,
                    _species: null,

                    constructor : function(name) {
                        this._name = name;
                        this._species = "liger";
                        console.log("Created ",this._name, " the ", this._species);
                    }
                });

                lucy = new Liger("Lucy");
                console.log(lucy);
            });
        </script>
    </head>
    <body>
    </body>
</html>

If you open the previous example and look at the console output in Firebug shown in Figure 10-3, you'll see that both a Tiger and Lion are created before a Liger is created. Just like the previous example with shapes, you do get your subclass, but not until after the necessary superclasses have been created, complete with constructor methods running and all.

Although you do eventually get your Liger, it's not until after the necessary superclasses have been created and properly initialized
Figure 10-3. Although you do eventually get your Liger, it's not until after the necessary superclasses have been created and properly initialized

Multiple Inheritance Oddities

In the earlier example involving shapes, there was no particular need to be concerned with the argument list from a Circle getting passed up to a Shape because a Circle built directly upon a Shape. Furthermore, it made good sense and was even convenient to include Shape 's constructor argument as the first argument of Circle 's constructor. In this past example with lions, tigers, and ligers, the constructor s are all single argument functions that do the same thing, so there's no real issue there, either.

But wait—what if Tiger and Lion each had custom constructor s? For example, Tiger 's constructor might specify arguments corresponding to the name and number of stripes, while Lion 's constructor might specify the name and mane length. How would you define a Liger 's constructor to handle a situation like that? The very same arguments that are passed into Liger 's constructor will be passed into Tiger 's constructor as well as Lion 's constructor, and that just doesn't make any sense.

In this particular instance—when two or more superclasses each require their own custom parameters—your best bet, if you have the option, is to pass in an associative array of named parameters and use these in your constructor instead of relying on the arguments list directly. Passing in custom parameters to superclasses in a multiple-inheritance relationship is not well-supported as of Dojo 1.1, although discussion for this kind of impedance matching is under consideration for a future release.

In general, a convenient pattern is to design multiple-inheritance relationships such that superclasses don't have constructors that require any arguments. The advantage of this approach is that purposefully defining superclasses without arguments allows the subclass to receive and process as many custom arguments as it needs, while ensuring that any superclasses up the inheritance chain won't be affected by them. After all, because they don't use them in any way, they can't be affected.

Summary

After learning the content in this chapter, you should:

  • Understand how to use dojo.declare to simulate classes in Dojo

  • Be able to implement single- and multiple-inheritance relationships in Dojo

  • Be aware of the dangers involved in initializing JavaScript Object s outside of a Dojo class's constructor

  • Know that Function objects are the mechanism used to approximate classes in JavaScript; remember, there aren't "real" classes in JavaScript

  • Understand some of the distinctions between prototype-based inheritance and class-based inheritance

  • Have a general understanding of how Dojo leverages JavaScript's prototype-based inheritance behind the scenes to simulate class-based inheritance

Part II is next, in which we'll cover Dijit and Util.