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, “Dijit and Util”.
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, Form Widgets, 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, “Base and Core”
is because Part II, “Dijit and Util” 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, Language and Browser Utilities.
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, “A typical JavaScript class”
provides a short code snippet that approximates a simple Shape class in JavaScript. Note that by
convention, classes usually begin with a capital letter.
Tip
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, Dijit Anatomy and Lifecycle.
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, “Typical JavaScript inheritance” 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 shapeWhile 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, “Mixing in as an alternative to inheritance”'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 radiusFor 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, “Delegation as an alternative to inheritance”. 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
radiusare provided directly bycirclebecauseradiusgot mixed in.Requests for
centerX,centerY, andcolorare delegated to theshapebecause they don't exist on thecircleitself (loosely speaking).A request for any other property returns
undefinedby definition because it doesn't exist in either thecircleor theshape.
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, “dojo.declare API” shows the brief
API.
Table 10.1. dojo.declare API
|
Name |
Comment |
|---|---|
|
Provides a compact way
of declaring a constructor function. The |
Tip
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, “Simulating class-based inheritance with dojo.declare”
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.)
Tip
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, Dijit Anatomy and Lifecycle 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 widgetTo verify for yourself, you might run the code in Example 10.6, “Basic dojo.declare creation pattern”.
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, Dijit Anatomy and Lifecycle 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>Warning
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, “Firebug output from Example 10.6, “Basic dojo.declare creation pattern”” in the Firebug console when you run this example.
Figure 10.1. Firebug output from Example 10.6, “Basic dojo.declare creation pattern”
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, “Prototype properties are shared amongst all
instances”.
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, “Firebug output” in the Firebug Console.
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, “Calling overridden superclass methods from a subclass” 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, “Typical JavaScript inheritance” 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, “Although you do eventually get your Liger, it's not until
after the necessary superclasses have been created and properly
initialized”, 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.
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.declareto simulate classes in DojoBe able to implement single- and multiple-inheritance relationships in Dojo
Be aware of the dangers involved in initializing JavaScript
Objects outside of a Dojo class'sconstructorKnow 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, “Dijit and Util” is next, in which we'll cover Dijit and Util.








Add a comment



Add a comment