A common pattern used in JavaScript is the module pattern. Modules are useful for many things, they allow us to separate our application into smaller parts and work on them individually - this can help keep our code clean and more maintainable.
But modules are more than that, in JavaScript a module is a closure (or an immediately invoked function expression -- IIFE) that can have private, public and protected properties and methods.
So it's like a class, sort of.
JavaScript allows us to define closures, normally when we declare a function in JavaScript:
function doStuff() {
console.log("Doing stuff...");
}
We need to call the function explicitly:
doStuff();
A closure is a little different, instead of us having to invoke the function by explicitly calling it, we can have it be called immediately on declaration. The closure will have it's own internal, private scope:
var doStuff = (function() {
// Code goes here.
})();
We do not need to call any function, the code within the anonymous closure assigned to doStuff is executed immediately.
We can declare private methods and variables within the function:
var doStuff = (function() {
var private_var;
function setPrivateVar(string) {
private_var = string;
}
function showPrivateVar() {
console.log(private_var);
}
setPrivateVar("This is a private string");
showPrivateVar();
})();
Because the function is immediately invoked, we will see "This is a private string" written to the console. The closure function is called immediately on declaration - first, it calls the setPrivateVar() private method which sets the private variable, then calling the showPrivateVar() mprivate method which dumps the private variable to the console.
These private methods can be accessed from within the function, but if we do something like:
var doStuff = (function() {
var private_var;
function setPrivateVar(string) {
private_var = string;
}
function showPrivateVar() {
console.log(private_var);
}
setPrivateVar("This is a private string");
showPrivateVar();
})();
console.log(doStuff.private_var);
We will get a type error:
TypeError: doStuff is undefined.
The reason for this is that the anonymous closure function isn't returning anything, no methods or properties are being made public.
If we change the code so that it returns something - say, an empty object literal:
var doStuff = (function() {
var private_var;
function setPrivateVar(string) {
private_var = string;
}
function showPrivateVar() {
console.log(private_var);
}
setPrivateVar("This is a private string");
showPrivateVar();
return {
};
})();
console.log(doStuff.private_var);
Instead of the type error we got the first time we simply see:
undefined
So it's not telling us that doStuff is undefined, but that the property private_var is undefined -- which is what we want, it is supposed to be private, right?
The two methods are also private, we can call them from within the module no problem:
setPrivateVar("This is a private string");
showPrivateVar();
But not from outside the module, for example, if we try to change the private_var by calling setPrivateVar() from outside the closure:
console.log(doStuff.setPrivateVar("Some random string"));
We will get an error:
TypeError: doStuff.setPrivateVar is not a function
The same will happen if we try to call the showPrivateVar() method outside of the closure.
We can give access to public properties and methods in the returned object literal fairly easily:
var doStuff = (function() {
var private_var = "Private stuff";
// Returned object literal can be accessed via the
// doStuff variable.
return {
"get_public_copy": function() {
return private_var;
}
};
})();
It is important to note that the returned object is part of the close and so has access to properties and methods in that scope -- it can return, or reveal the private private_var property to us. We can access this public method thusly:
console.log(doStuff.get_public_copy());
All we've done is return an object literal that is assigned to doStuff, this provides access to a public method named get_public_copy. The get_public_copy method simply executes an anonymous function that accesses the private property from within the closure and returns it to us.
We can also reveal private methods in a similar way:
var doStuff = (function() {
var private_var = "Private stuff";
var showPrivateVar = function() {
console.log(private_var);
};
return {
"showPrivateVar": showPrivateVar
};
})();
What we now have is a protected method, or a private method being made available via a publicly accessible property.
There is so much more that we can do with the module and revealing module patterns, these are a few basic examples but it's a design pattern you'll come across quite often in JavaScript.