So most programming languages in common use today have some concept of namespaces. Whether they're called packages, namespaces, or something else, they're ubiquitous as a design element. JavaScript is no exception, it also has namespace features, based on either function closure (no surprise) via what's known as the module pattern, or object literals. We'll start with the object literal approach; it's important that you understand this method first, as it's used obliquely with the module pattern, which we'll cover a bit later.
In the below examples, I'm using Google Chrome and the browser's integrated development tools. Very similar to Firebug in Firefox. This way, I can use the integrated JavaScript console to both execute arbitrary JavaScript and examine the current interpreter state. I also have access to various output functions via the console object.
Object literals area a way to create object without a preexisting class definition. The basic format consists of key/value pairs surrounded by braces and set to some variable:
var anon_obj = {
key1: value1,
key2: value2,
... ,
keyN: valueN
}
Now, there's some other interesting things about objects and object literals that I should mention before I proceed just to muddy my message a bit. They can also be used associative arrays. For example, I can initialize an anonymous object like this:
var my_hash = {
one: 1,
two: 2,
three: 3
}
Now, if I run the following code sometime after defining my_hash, like so:
console.log(my_hash['one']);
var key ='one';
console.log(my_hash[key]);
I see the value 1 output twice in the console. I can also add key/value pairs to my_hash like this:
my_hash['four'] = 4;
Now, you don't want to use general objects as keys. It seems like you can do this:
var obj_key = new Object();
my_hash[obj_key] = 'arbitrary string';
but if you later do this:
var obj_new_key = new Object();
my_hash[obj_new_key] = 'macaroni';
you'll find that my_hash[obj_key] will also be set to 'macaroni'. Just to make things more interesting, my_hash[2] and my_hash['2'] can be mapped to the same value as well!
Now back to using literals as namespacing constructs. First, we're going to provide a namespace for a group of functions, like so:
var Functions = {
print_name: function() {
console.log('Functions');
},
print_time: function() {
console.log(new Date().getTime());
},
print_date: function() {
console.log(new Date());
}
}
You'd invoke these functions using a syntax like Functions.print_name(). You could also pass this namespace into a function call as an argument like this:
function printer(ns) {
ns.print_name();
ns.print_date();
ns.print_time();
}
printer(Functions);
Remember, the function printer(ns) syntax I used above is in fact a shorthand for var printer = function(ns). Personally, I'm usually on the fence as to which one to use; I like the latter because it makes clear that printer is in fact just a variable name that can receive different assignments after declaration, but the former is shorter and generally more clear, as most folks aren't going to reassign the printer variable to something else.
Object literals are pretty powerful, but they don't really allow us to do much besides assignment. Using the module pattern, we can use function closure to give us the ability to assemble our namespaces and provide namespace constructors as well as private variables - I'll cover this next.
No comments:
Post a Comment