Manipulating Object Context

Although the global window object provides the outermost layer of context for a web application, there may be times when you need to swap out the default context for another one. For example, you may want to persist the exact state of a session when the user exits an application, or you might have a custom execution environment that's already been preconfigured for a particular circumstance. Instead of having code that manually iterates over sets of conditions to configure the environment each time, you might opt to use Base's window facilities to swap out the existing context for another one.

The following function allows you to change out the dojo.global object and dojo.doc at will. Note that while dojo.doc is simply a reference to the window.document by default, it does provide a uniform mechanism for identifying the context's current document, which again can be quite useful for situations in which managed object contexts are involved. dojo.body() is a shortcut for obtaining the body of a document.

Warning

The body element is not explicitly defined for a strict XHTML document and some other documents you may encounter.

At a minimum, you should be aware of the following three functions from Base for manipulating context:

dojo.doc //Returns Document
dojo.body(  ) //Returns DomNode
dojo.setContext(/*Object*/globalObject, /*Document*/globalDocument)

Finally, in the spirit of flexibility, Base also provides two functions that allow you to evaluate a function in the context of either a different dojo.global environment or a different dojo.doc than the one that currently exists:

dojo.withGlobal(/*Object*/globalObject, /*Function*/callback, /*Object*/thisObject,
/*Array*/callbackArgs)
dojo.withDoc(/*Object*/documentObject, /*Function*/callback, /*Object*/thisObject,
/*Array*/callbackArgs)

It should be noted that using a Dojo function to operate extensively in another document or window is not a well-tested usage of the toolkit, so you may encounter support issues if going down that route. Standard usage normally entails loading Dojo into every document where you plan to use it. For lightweight operations, however, the context functions discussed in this section should work fine.

Partially Applying Parameters

Base's partial function allows you to partially apply parameters to a function as they become available and perform final execution of the function at a later time. Or, you might just need to apply all of the parameters at once and then pass around a function reference that can be executed—which is a little less messy than passing around the function and the parameters all at the same time and a pattern that is commonly used throughout the toolkit. Here's the API for partial:

dojo.partial(*/Function|String*/func /*, arg1, ..., argN*/) //Returns Any

To illustrate, here's a simple example of partial being used to partially apply parameters to a function that adds a series of numbers:

function addThree(x,y,z) { console.log(x+y+z);}

//apply two args now
f = dojo.partial(addThree, 100,10);

//apply the last one later
f = dojo.partial(f, 1);

//now evaluate
f(  ); //111

Hitching an Object to a Specific Context

Base's hitch function is quite similar to partial in that it allows you partially apply parameters to a function, but it also has the interesting twist that it also allows you to permanently bind (or hitch) a function to a specific execution context, regardless of whatever the final execution context becomes. This can be especially handy for situations in which you have callback functions and will never fully know what the final execution context (and thus, this ) will be. Here's the API:

dojo.hitch(/*Object*/scope, /*Function||String*/method /*, arg1, ... , argN*/)
//Returns Any

And to illustrate, here's a simple example that rewires an Object method:

var foo = {
    name : "Foo",
    greet : function(  ) {
        console.log("Hi, I'm", this.name);
    }
}

var bar = {
    name : "Bar",
    greet : function(  ) {
        console.log("Hi, I'm", this.name);
    }
}

foo.greet(  ); //Hi, I'm Foo
bar.greet(  ); //Hi, I'm Bar

/* Bind bar's greet method to another context */
bar.greet = dojo.hitch(foo, "greet");

/ * Bar is now an impersonator */
bar.greet(  ); // Hi, I'm Foo

To be clear, because the greet function explicitly references a context with this, the following code would not have successfully rewired the greet method:

bar.greet = foo.greet;
bar.greet(  );

Tip

You might find it interesting to know that with respect to implementation, hitch provides the basis for partial and calling hitch with null as the scope is the functional equivalent of calling partial.

The section "Hitching Up Callbacks" in Chapter 4 provides an example of using hitch to manage the context for data that is used within an asynchronous callback function—one of its most common use cases because the callback function has a different this context than the containing Object.

Delegation and Inheritance

Delegation is a programming pattern that entails one object relying on another object to perform an action, instead of implementing that action itself. Delegation is at the very heart of JavaScript as a prototype-based language because it is the pattern through which object properties are resolved in the prototype chain. Although delegation is at the very crux of JavaScript's inheritance implementation, which relies on the prototype chain being resolved at runtime, delegation as a pattern is very different from inheritance in true class-based programming languages like Java and C++, which often (but not always) resolve class hierarchies at compile time instead of runtime. In that regard, it is especially noteworthy that as a runtime feature, delegation necessarily relies on dynamic binding as a language feature.

Dojo's delegate function wraps up the details of dispatching delegation of an Object 's function through the following API:

dojo.delegate(/*Object*/delegate, properties) //Returns Object

Building on the previous example, the following blurb demonstrates how you might use delegation to get an Object that dispatches responsibility for a function to its delegate:

function Foo(  ) {
    this.talk = function(  ) {console.log("Hello, my name is", this.name);}
}

// Get a Function object back that has the name property
// but dispatches, or delegates, responsiblity for the talk function
// to the instance of Foo that is passed in.
var bar = dojo.delegate(new Foo, {name : "Bar"});

// The talk method is resolved through the Foo delegate
bar.talk(  );

Chapter 10 is devoted to the inheritance pattern facilitated by the toolkit's dojo.declare function, which can be used to simulate class hierarchies with JavaScript; the chapter also includes additional discussion on various approaches to accomplishing inheritance patterns.

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.