Creating custom libraries
Even though the function-based nature of JavaScript makes it
deceptively easy to get started, most developers who are new to the
language simply write global functions directly in the web page. This
practice, however, is seriously flawed because naming conflicts will
inevitably arise between functions in libraries. Furthermore, writing
reams of functions in the global namespace is simply unmaintainable.
This section examines several approaches for creating custom libraries that are efficient and maintainable.
Understanding the singleton pattern
The singleton pattern creates a single instance of an object that
encapsulates code within it. The singleton pattern is a straightforward
implementation of an object designed to encapsulate code and keep it
out of the global namespace. As an example, consider the following code
that sets up a custom namespace and then defines a singleton:
"use strict";
var Wingtip = window.Wingtip || {};
Wingtip.Customer = {
name: "Brian Cox",
speak: function() { alert("My name is " + this.name); }
};
Within the Customer
object, each member is added by declaring a publicly accessible key,
followed by the definition of a function or object as the value. Note
the use of the this keyword within the speak function to reference the name member object. Calling code might interact with the publically accessible members as shown in the following code.
Wingtip.Customer.speak();
The singleton pattern does a nice job of encapsulating code into the Customer
object outside of the global namespace. Additionally, the calling code
is straightforward, readable, and maintainable. The entire Customer definition could subsequently be packaged into a separate file (for example, wingtip.customer.js)
and reused across several apps. The obvious disadvantage of this
pattern is that you can only have one customer. In a typical SharePoint
app, you are going to need to create many customer instances.
Understanding the module pattern
The module
pattern and its variants use a function instead of an object as the
basis for encapsulation. The advantage of the module pattern is that it
can support private members, public members, and multiple instances;
the exact support is based on the pattern variant that you use.
The standard module pattern uses a self-invoking function as the
container. The standard module pattern can be regarded as an improved
version of the singleton pattern because it still only supports one
instance. Example 2 shows an example of the module pattern.
Example 2. The module pattern
"use strict";
var Wingtip = window.Wingtip || {};
Wingtip.Customer = function () {
//private members
var name = "Brian Cox",
talk = function() {alert("My name is " + name);};
//public interface
return {
fullname: name,
speak: talk
}
}();
In Example 2, notice that the function definition is followed by a set of parentheses. It is these parentheses that make the function self-invoking.
The return value is an object whose key-value pairs reference the
private members, which effectively creates a public interface for the
library. The following code shows how the module is called:
alert(Wingtip.Customer.fullname);
Wingtip.Customer.speak();
Note that the return value doesn’t have to actually provide a
key-value pair for every one of the private members. When the return
value reveals only a subset of the members, the pattern is said to be a
variant of the module pattern known as the revealing module pattern. The revealing module pattern allows for the definition of private members that are inaccessible through the public interface. Example 3 shows an example that utilizes get and set functions to access the name member.
Example 3. The revealing module pattern
"use strict";
var Wingtip = window.Wingtip || {};
Wingtip.Customer = function () {
//private members
var name,
setname = function(n) { name = n; },
getname = function() { return name; },
talk = function() {alert("My name is " + name);};
//public interface
return {
set_name: setname,
get_name: getname,
speak: talk
}
}();
If the parentheses are removed from the function, it is no longer self-invoking. To make use of the module, you must create an instance referenced by a new variable. Using this variant of the module
pattern, you can create multiple customer instances for use, which
should feel very familiar to C# developers. The following code shows
how to create an instance if the module were not self-invoking:
var customer1 = new Wingtip.Customer();
customer1.set_name("Brian Cox");
customer1.speak();
Understanding the prototype pattern
Unlike previous patterns, the prototype pattern does not rely on
closures to achieve its functionality. Instead, it relies on the
inheritance of the prototype chain. The prototype provides a means of
defining members in a single place for use by many instances. Every
object in JavaScript has a prototype property with which you can expand
to include new members. This sets up a very interesting pattern that
you can utilize to define a prototype that can be used to create
instances later. If you’re a C# developer, this feels a lot like
defining a class from which instances are created. The following code
shows an example of the prototype pattern:
"use strict";
var Wingtip = window.Wingtip || {};
Wingtip.Customer = function (n) {
this.name = n;
};
Wingtip.Customer.prototype.speak = function() {
alert("My name is " + this.name);
}
The prototype pattern begins with the definition of a function. This
function often accepts initialization parameters, which are stored in
variables defined within the prototype by using the this
keyword. The initial function definition acts as the constructor for
new instances, which means that the variables defined within are also
defined for each instance as part of the prototype.
The prototype associated with a function can easily be extended by
referencing the prototype property and adding a new member. In the
example, a speak function
is added to the prototype. As an alternative, you can also define the
prototype as an object containing many functions, as shown in the
following code:
"use strict";
var Wingtip = window.Wingtip || {};
Wingtip.Customer = function (n) {
this.name = n
};
Wingtip.Customer.prototype = {
get_name: function() { return this.name; },
set_name: function(n) { this.name = n; },
speak: function() { alert("My name is " + this.name); }
};
The prototype
pattern can also be combined with the module pattern by simply defining
a self-invoking function in the prototype. Additionally, defining
members separately is not required; you could simply define all members
in the constructor as shown in the code that follows. The bottom line
is that hybrid patterns are possible by combining several concepts
together.
"use strict";
var Wingtip = window.Wingtip || {};
Wingtip.Customer = function (n) {
this.name = n;
this.speak = function() { alert("My name is " + this.name); };
};
Once the prototype is defined, you can create instances by using the new
keyword. Each instance inherits the definition of the function
prototype. The code that follows shows how to create an instance and
invoke a function. The resulting code has a decidedly object-oriented
feel that should make C# programmers comfortable.
var customer1 = new Wingtip.Customer("Brian Cox");
customer1.speak();