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."

Tip

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.

Get Dojo: The Definitive Guide now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.