The goal of this blog entry is to describe how you can use the Windows JavaScript (WinJS) library to create namespaces. In particular, you learn how to use the WinJS.Namespace.define() and WinJS.Namespace.defineWithParent() methods. You also learn how to hide private methods by using the module pattern.
Why Do We Need Namespaces?
Before we do anything else, we should start by answering the question: Why do we need namespaces? What function do they serve? Do they just add needless complexity to our Metro applications?
After all, plenty of JavaScript libraries do just fine without introducing support for namespaces. For example, jQuery has no support for namespaces and jQuery is the most popular JavaScript library in the universe. If jQuery can do without namespaces, why do we need to worry about namespaces at all?
Namespaces perform two functions in a programming language. First, namespaces prevent naming collisions. In other words, namespaces enable you to create more than one object with the same name without conflict.
For example, imagine that two companies – company A and company B – both want to make a JavaScript shopping cart control and both companies want to name the control ShoppingCart. By creating a CompanyA namespace and CompanyB namespace, both companies can create a ShoppingCart control: a CompanyA.ShoppingCart and a CompanyB.ShoppingCart control.
The second function of a namespace is organization. Namespaces are used to group related functionality even when the functionality is defined in different physical files. For example, I know that all of the methods in the WinJS library related to working with classes can be found in the WinJS.Class namespace. Namespaces make it easier to understand the functionality available in a library.
If you are building a simple JavaScript application then you won’t have much reason to care about namespaces. If you need to use multiple libraries written by different people then namespaces become very important.
Using WinJS.Namespace.define()
In the WinJS library, the most basic method of creating a namespace is to use the WinJS.Namespace.define() method. This method enables you to declare a namespace (of arbitrary depth).
The WinJS.Namespace.define() method has the following parameters:
· name – A string representing the name of the new namespace. You can add nested namespace by using dot notation
· members – An optional collection of objects to add to the new namespace
For example, the following code sample declares two new namespaces named CompanyA and CompanyB.Controls. Both namespaces contain a ShoppingCart object which has a checkout() method:
// Create CompanyA namespace with ShoppingCart
WinJS.Namespace.define("CompanyA");
CompanyA.ShoppingCart = {
checkout: function (){ return "Checking out from A"; }
};
// Create CompanyB.Controls namespace with ShoppingCart
WinJS.Namespace.define(
"CompanyB.Controls",
{
ShoppingCart: {
checkout: function(){ return "Checking out from B"; }
}
}
);
// Call CompanyA ShoppingCart checkout method
console.log(CompanyA.ShoppingCart.checkout()); // Writes "Checking out from A"
// Call CompanyB.Controls checkout method
console.log(CompanyB.Controls.ShoppingCart.checkout()); // Writes "Checking out from B"
In the code above, the CompanyA namespace is created by calling WinJS.Namespace.define(“CompanyA”). Next, the ShoppingCart is added to this namespace. The namespace is defined and an object is added to the namespace in separate lines of code.
A different approach is taken in the case of the CompanyB.Controls namespace. The namespace is created and the ShoppingCart object is added to the namespace with the following single line of code:
WinJS.Namespace.define(
"CompanyB.Controls",
{
ShoppingCart: {
checkout: function(){ return "Checking out from B"; }
}
}
);
Notice that CompanyB.Controls is a nested namespace. The top level namespace CompanyB contains the namespace Controls. You can declare a nested namespace using dot notation and the WinJS library handles the details of creating one namespace within the other.
After the namespaces have been defined, you can use either of the two shopping cart controls. You call CompanyA.ShoppingCart.checkout() or you can call CompanyB.Controls.ShoppingCart.checkout().
Using WinJS.Namespace.defineWithParent()
The WinJS.Namespace.defineWithParent() method is similar to the WinJS.Namespace.define() method. Both methods enable you to define a new namespace. The difference is that the defineWithParent() method enables you to add a new namespace to an existing namespace.
The WinJS.Namespace.defineWithParent() method has the following parameters:
· parentNamespace – An object which represents a parent namespace
· name – A string representing the new namespace to add to the parent namespace
· members – An optional collection of objects to add to the new namespace
The following code sample demonstrates how you can create a root namespace named CompanyA and add a Controls child namespace to the CompanyA parent namespace:
WinJS.Namespace.define("CompanyA");
WinJS.Namespace.defineWithParent(CompanyA, "Controls",
{
ShoppingCart: {
checkout: function () { return "Checking out"; }
}
}
);
console.log(CompanyA.Controls.ShoppingCart.checkout()); // Writes "Checking out"
One significant advantage of using the defineWithParent() method over the define() method is the defineWithParent() method is strongly-typed. In other words, you use an object to represent the base namespace instead of a string. If you misspell the name of the object (CompnyA) then you get a runtime error.
Using the Module Pattern
When you are building a JavaScript library, you want to be able to create both public and private methods. Some methods, the public methods, are intended to be used by consumers of your JavaScript library. The public methods act as your library’s public API.
Other methods, the private methods, are not intended for public consumption. Instead, these methods are internal methods required to get the library to function. You don’t want people calling these internal methods because you might need to change them in the future.
JavaScript does not support access modifiers. You can’t mark an object or method as public or private. Anyone gets to call any method and anyone gets to interact with any object.
The only mechanism for encapsulating (hiding) methods and objects in JavaScript is to take advantage of functions. In JavaScript, a function determines variable scope. A JavaScript variable either has global scope – it is available everywhere – or it has function scope – it is available only within a function. If you want to hide an object or method then you need to place it within a function.
For example, the following code contains a function named doSomething() which contains a nested function named doSomethingElse():
function doSomething() {
console.log("doSomething");
function doSomethingElse() {
console.log("doSomethingElse");
}
}
doSomething(); // Writes "doSomething"
doSomethingElse(); // Throws ReferenceError
You can call doSomethingElse() only within the doSomething() function. The doSomethingElse() function is encapsulated in the doSomething() function.
The WinJS library takes advantage of function encapsulation to hide all of its internal methods. All of the WinJS methods are defined within self-executing anonymous functions. Everything is hidden by default. Public methods are exposed by explicitly adding the public methods to namespaces defined in the global scope.
Imagine, for example, that I want a small library of utility methods. I want to create a method for calculating sales tax and a method for calculating the expected ship date of a product. The following library encapsulates the implementation of my library in a self-executing anonymous function:
(function (global) {
// Public method which calculates tax
function calculateTax(price) {
return calculateFederalTax(price) + calculateStateTax(price);
}
// Private method for calculating state tax
function calculateStateTax(price) {
return price * 0.08;
}
// Private method for calculating federal tax
function calculateFederalTax(price) {
return price * 0.02;
}
// Public method which returns the expected ship date
function calculateShipDate(currentDate) {
currentDate.setDate(currentDate.getDate() + 4);
return currentDate;
}
// Export public methods
WinJS.Namespace.define("CompanyA.Utilities",
{
calculateTax: calculateTax,
calculateShipDate: calculateShipDate
}
);
})(this);
// Show expected ship date
var shipDate = CompanyA.Utilities.calculateShipDate(new Date());
console.log(shipDate);
// Show price + tax
var price = 12.33;
var tax = CompanyA.Utilities.calculateTax(price);
console.log(price + tax);
In the code above, the self-executing anonymous function contains four functions: calculateTax(), calculateStateTax(), calculateFederalTax(), and calculateShipDate(). The following statement is used to expose only the calcuateTax() and the calculateShipDate() functions:
// Export public methods
WinJS.Namespace.define("CompanyA.Utilities",
{
calculateTax: calculateTax,
calculateShipDate: calculateShipDate
}
);
Because the calculateTax() and calcuateShipDate() functions are added to the CompanyA.Utilities namespace, you can call these two methods outside of the self-executing function. These are the public methods of your library which form the public API.
The calculateStateTax() and calculateFederalTax() methods, on the other hand, are forever hidden within the black hole of the self-executing function. These methods are encapsulated and can never be called outside of scope of the self-executing function. These are the internal methods of your library.
Summary
The goal of this blog entry was to describe why and how you use namespaces with the WinJS library. You learned how to define namespaces using both the WinJS.Namespace.define() and WinJS.Namespace.defineWithParent() methods. We also discussed how to hide private members and expose public members using the module pattern.