The Premise of the Module Pattern:
- A class is actually defined as a function (will be aka “class” from this point)
- The parameters you decide for this “class” are in fact the parameters for the constructor
- The local variables and functions you create inside your “class” turn into private members
- The return method for your “class” (remember it’s still a function) returns an object containing your public methods and variables
- These methods have access to your “private” members, and you can feel free to use the accessor/mutator method pattern just like any other ‘ole OO language
Why the Module Pattern?
- As hum-jumbled as this solution is, it is still clearer to an OO programmer to model true encapsulation
- Public parts can touch the private parts, but the world can’t touch the class’ private parts (stop laughing)
- Unfortunately, this pattern doesn’t allow private members to access public members, so you are limited (but not by too much since there are few occasions where this is needed)
- You can piece together objects that follow the JavaBean convention
Ok, So Show Me Some Stuff That’s Useful
The example we picked is the simplified Person object. We’ll explain the squirrelly “options” code you see at the top of the object later, but for now let’s define our requirements. It would be useful if we could make the Person object:
- Created easily and logically (the Constructor). The YUI example uses some kind of funky init() method that is called after object creation; however, we’ like the typical Constructor pattern to be followed.
- Have a name and age that is accessible by the JavaBean convention (accessors/mutators)
- Have the ability to calculate the age in dog years (don’t ask me why, but it is interesting)
The Simple Person Object
To start out, let’s just create the scaffold for the Person ‘Module’:
// Creating the class
var Person = function( p_options ){
// Begin Private Section
// Begin Public Section
return {
};
};
In line 02 of our scaffold we created a ‘Person’ variable and we assigned it to a function with one parameter, “p_options”.
This is important as it will serve our as our constructor and parameters. Now technically, we can have multiple parameters to the “constructor” for things like name, age, etc, but we will show you something more interesting in a moment.
Since it is a function that we created, it needs to have a return statement. This return statement will return an Anonymous Object (‘{}’), which will contain our public members and API. That way, when we create a ‘new Person()’ object and store it to a variable, that variable only has access to the API from Person’s return statement (line 06). Let’s add a name and age to our Person; we’ll also add a method to calculate the age in dog years as well:
// Creating the class
var Person = function( p_options ){
// Private name
var m_name = options.name;
// Private age
var m_age = options.age;
// Private method
var calcAgeInDogYears = function(){
return m_age / 7;
};
// Begin Public Section
return {
};
};
Ignoring the initialization of the two variables for the moment, we just created the two members m_age and m_name and the method calcAgeInDogYears(). Since these items are not in the return statement for Person, it will not be returned as part of the public API for Person, which makes these items part of private API. However, the public API does have access to these private API members and methods, so we can create a simple JavaBean pattern, so let’s add some accessors and mutators (and something to return the calculated age in dog years) to the public API:
// Creating the class
var Person = function( p_options ){
// Private name
var m_name = options.name;
// Private age
var m_age = options.age;
// Private method
var calcAgeInDogYears = function(){
return m_age / 7;
};
// Begin Public Section
return {
// Name acceesor
getName: function(){
return m_name;
},
// Name mutator
setName: function(name){
m_name = name;
},
// Age accessor
getAge: function(){
return m_age;
},
// Age mutator
setAge: function(age){
m_age = age;
},
// Get person's age in dog years
getAgeInDogYears: function(){
return calcAgeInDogYears();
}
};
};
Now what’s squirrelly about the method definition for the public API is that it does have to follow the JavaScript object convention (not JSON): a comma-separated list of items that contain name + “: ” + assignment_type. Therefore, methods would follow ‘methodName: function( params ){}‘ and members would be ‘exampleCount: 0‘.
Let’s Fix the Constructor Parameters and the Private Member Initialization
Now, this could done as simply as follows (without much thought to optional parameters or null’s):
// Creating the class
var Person = function( p_name, p_age ){
// Private name
var m_name = p_name;
// Private age
var m_age = p_age;
...
However, we are limited; you must pass both parameters, and the private members can not have default values. The Mootools team have come up with an interesting solution to this problem: the Options class. This example is not about explaining how their Options class is implemented, but just how it works; however, if you’d like to see their implementation, you can do so here.
The premise of the Options class is to parse a comma-separated “param: param_value” JavaScript object and assign values to their equivalent “middle-man parameters“. You can then take these “middle-man parameters” and use them elsewhere.
This sounds awfully confusing so let’s just try an example. Let’s start with those “middle-man parameters“:
// Creating the class
var Person = function( p_options ){
// Private options used for construction
var options = {
name: null,
age: 0
};
// Private name
var m_name = options.name;
// Private age
var m_age = options.age;
// Private method
var calcAgeInDogYears = function(){
return m_age / 7;
};
// Begin public section
return {
// Name acceesor
getName: function(){
return m_name;
},
// Name mutator
setName: function(name){
m_name = name;
},
// Age accessor
getAge: function(){
return m_age;
},
// Age mutator
setAge: function(age){
m_age = age;
},
// Get person's age in dog years
getAgeInDogYears: function(){
return calcAgeInDogYears();
}
};
};
As you can see in lines 03 – 07, we have defined a private ‘options‘ JS object that has the “name” and “age” attributes, which have been initialized to null and 0 respectively. Now, we have default values for our private members. Yay! However, we still need a way to convert the Constructor p_options to our private member options. Well, we can just parse through the p_options array, and if we see a parameter name we recognize, we can store it into our options object. So let’s parse the p_options (don’t forget to make sure it’s not null and not undefined!):
// Creating the class
var Person = function( p_options ){
// Private options used for construction
var options = {
name: null,
age: 0
};
/**
* Set the options if provided any.
* This options pattern is from Mootools, and thier library
* will do it for you for free!
*/
if ( p_options != null && p_options != undefined
&& p_options != 'undefined' ){
for ( var opt in options ) {
if ( p_options[ opt ] != null
&& p_options[ opt ] != undefined
&& p_options[ opt ] != 'undefined' ){
options[ opt ] = p_options[ opt ];
}
}
}
// Private name
var m_name = options.name;
// Private age
var m_age = options.age;
// Private method
var calcAgeInDogYears = function(){
return m_age / 7;
};
// Begin public section
return {
// Name acceesor
getName: function(){
return m_name;
},
// Name mutator
setName: function(name){
m_name = name;
},
// Age accessor
getAge: function(){
return m_age;
},
// Age mutator
setAge: function(age){
m_age = age;
},
// Get person's age in dog years
getAgeInDogYears: function(){
return calcAgeInDogYears();
}
};
};
BAM! We just kicked it up a notch. Now we don’t care what the user passes along to us in our constructor; it can be junk, nulls, undefined, wrong parameters, etc, and the right options will be mapped correctly to our options with simple perfect results each and every time (anyone hear the sawing of the Miracle Blade by Chef Tony?). The meat and bones of lines 13 – 22 can be boiled down to this:
for ( var opt in options ) {
options[ opt ] = p_options[ opt ];
}
Using this pseudo-iterator approach we can find all of the options that we care about.
Since We Have Been Unit Testing All Along…, We Know That This All Works Right?
Ok, maybe we haven’t been unit testing all along, but we can start now that it’s done… Let’s build some simple test cases to test our Person object:
// Testing 'options' pattern and constructor regular
var dave = new Person({ name: "David Smith", age: 24 });
// Testing 1 parameter version (should use default for age)
var john = new Person({ name: "John Doe" });
// Should use defaults for name and age
var fred = new Person();
// Testing accessors
// Expected: normal
alert("Name: " + dave.getName() + "nAge: " + dave.getAge());
// Expected normal name + 0 age
alert("Name: " + john.getName() + "nAge: " + john.getAge());
// Expected: null name and 0 age
alert("Name: " + fred.getName() + "nAge: " + fred.getAge());
// Testing mutators
fred.setName( "Fred Flinstone" );
fred.setAge( 45 );
// Expected: name = yabba dabba do + age = 45
alert("Name: " + fred.getName() + "Age: " + fred.getAge());
// Testing public calls to private methods
alert("Name: " + fred.getName() + "Age In Dog Years: "
+ fred.getAgeInDogYears());
Lights, Camera, Action
Time to download…
Must… make… JavaScript… look… like… a… language… I… know…
@Caligula Welcome back
. I know you don’t like some of the generalizations that I make, but honestly, I really do appreciate that you took the time to read it. Seriously! Thanks!
Nice article! I have a notice: the name mutator should be called setName, not getName.
@Dmitry. Thanks! Fixed. Yeah, I had some translation problems with pasting the stuff into wordpress
Well written article, and nice use of closures.
I tend not to bother with OOP in Javascript, as it lends itself much more cleanly to other styles of coding. To me it doesn’t seem to benefit from it as much as big server-side apps do. Instead I use a more functional/procedural style with lots of anonymous functions and objects.
Nice to have the technique available though.
Disadvantages of this method:
– you access private and public member differently so when you want to change visibility, you have to make changes in every place where member is used.
– you cannot access private member in method added later to the object.
Adventage is that by using this technique (returning new object instead of this from constructor, you can easily implement inheritance):
function Person() { // inheritance var me = new Object(); // private var priv = 5; // public me.a = 1; me.b = 2; me.getPriv = function() { return priv; } return me; } function Man() { // inheritance var me = new Person(); // define base class // private var manPrivate = 42; // define private var // public me.c = 'test'; // define public var me.d = function() { // define public method return this.a manPrivate this.getPriv(); } return me; }I like the use of closures, but I don’t like this for most of my JS. I find that this is often unnecessary. Why not just call myPerson.name or myPerson.name = “Adam”? I mean, if you do want to write something that has more limitations on your getters and setters, then that’s fine; however, the example you gave doesn’t do that.
This is also slower than just setting the properties and being done with it, not only from execution, but now download time.
Nice article, I use this to code my stuff. I love it when developers who do not use javascript as their primary language crap all over it when someone goes out of their way to improve it. It simply validates that this kind of thinking is on the right track.
@Pope. A reason for this closure is to:
It’s like the quandary of structs and classes… Why don’t we just use structs for everything? Why not use C-like conventions for everything?
Sure there are performance implications, but it comes down to the old hardware speed versus software speed. Could you imagine how awesome cellular phones would be if they kept the same hardware and coded everything with assembly and made it as efficient as possible? It would be hell for the developers, but the end result would be awesome! However, that goal is unreal and unreasonable; there is a reason why JVM’s were stuck on those phones; Java is an easier language to program in than assembly.
The same goes for browsers; JavaScript is the now “JVM-on-cellular-phones” equivalent to browsers as far as the efficiency problem.
As far as download time, you’ve nailed me there; the only thing I can say is make sure you have zip output compression turned on and your stuff has been YUI compressed or JS minified.
@TheUIGuy. Thanks!
So to your point A, that’s where I strongly disagree. You’re writing stuff for non-JavaScript “experts”, but what if your non-JS expert is a ruby programmer? Then the get/set is more foreign for them.
Now I don’t think Ruby programmers are victim to your structs/classes argument since you don’t use get/set properties* to access variables and I don’t think JavaScript programmers are victims either.
Ultimately, you need to work with people that understand the language and not cripple it for those who aren’t familiar with it. The closure technique I consider advanced; however not understanding how to set variables means your programmer doesn’t even know how to use this language at all…so why cater to the latter?
If you do want to write code like Java, perhaps GWT is for you and your group of Java programmers.
* :attr_reader, :attr_writer, and :attr_accessor create get/set in the form of @def my_property@ and @def my_property=(arg)@
I wouldn’t worry about ensuring that other kinds of developers can read it – thats not, imo, a goal of this type of coding, nor its it really where the effort should be spent.
The goal is to improve communication amongst engineers of the same type – to not only improve the coding structures for yourself, which this format provides, but to move from the procedural global-class setting which is so common, to a hierarchical oop-esque framework.
Instead of people focusing on the notion that this is trying to emulate a language, they should be thankful that this is even possible – given the JSON format which has been around for many years, just never truly utilized by the mainstream. Why thankful? Because any structure, concept or paradigm that leads to better coding is better for everyone.
@TheUIGuy: I am thankful that this is possible and I use this pattern all the time to hide my private variables and keep from some bloated global scope. My comments aren’t about how this pattern is bad, just perhaps the use of this example isn’t the best demonstration for this.
Nice article. Thanks for writing it up. I found it very useful. Small thing – my age in dog years is my human age * 7 (not /).
Hi there,
Sorry if this is the most basic JS question you’ve ever heard but how does the line “options[ opt ] = p_options[ opt ];” know when to assign the value of p_options[ opt ] to options[ opt ]?
For example,
If your code is { name: “David Smith”, age: 24 } then everything is fine, but if I change the code to { named: “David Smith”, ages: 24 } (notice I’ve purposely mistyped each property name) then the above line “options[ opt ] = p_options[ opt ];” doesn’t fire and the ‘Person’ Object refers to it’s default values.
How does it know that the property values are the same or not? As far as I can tell the code “options[ opt ] = p_options[ opt ];” doesn’t evaluate to true because it is not in an if statement and all it does is assign the value from p_options[ opt ] to the property options[ opt ].
I can’t find anywhere in the code where it checks whether p_options[ opt ] actually matches options[ opt ] before assigning a value to it?
Any help appreciated as that has confused me
Thanks!
M.
@Mark. Good questions.
Let me explain the code first…
// Make sure the parameter p_options object passed // in exists... if ( p_options != null && p_options != undefined && p_options != 'undefined' ){ // Loop through each option in the members options // and store the option key in "opt" (i.e. "name") for ( var opt in options ) { // For each class member option, make sure // before we assign a value to it that the source // value (parameter p_option) exists. if ( p_options[ opt ] != null && p_options[ opt ] != undefined && p_options[ opt ] != 'undefined' ){ // We would have only gotten here if // p_option[ opt ] existed (via the previous // "if statement"). Assign the value from the // p_options to the member option. options[ opt ] = p_options[ opt ]; } } }For example, say that the member
option == { name: null, age: 0 }and parameterp_options == { name: "Bob", age_mage: 21 }.Now, we know that age_mage isn’t a member option but we’ll see how it flows anyways.
The first iteration through the “for” loop will have
opt == "name"(because we are looping through the *member* options…). The “if” statement checks if “name” exists in the parameter options, and that it has a non-null value (we actually might want to edit that to allow nulls?). Well, since p_options[ "name" ] == “Bob”, it will assign “Bob” to options[ "name" ]; therefore, the member options has changed from the default value.The second iteration through the “for” loop will have opt == “age”. The “if” statement will then again check if p_options[ "age" ] exists and has a non-null value. Well, p_options doesn’t have an “age” property, so p_options[ "age" ] returns ‘undefined’ or something of the sort; it fails the “if” condition and the assignment operation never gets executed and the default value for the member option property is retained.
What does this all mean? You could have hundreds of useless parameters that you passed in with p_options, and we won’t ever read them because we are only looking for properties that exist in the member options. Meaning, “age_mage” never even gets read.
– We don’t care if p_options[ opt ] == options[ opt ]. We just care if “opt” exists in p_options or not.
Hope this explains things. Let me know!
Hi there,
Thanks for clarifying, now when I re-look over the code I can clearly see what’s happening (can’t believe I couldn’t see how it worked originally as it’s very obvious, must have been a ‘special’ day for me, lol)
Thanks again, great article and I’ll be using this Module Pattern a lot!
Kind regards,
Mark
Your opening paragraph starts “The module pattern originally developed by Douglas Crockford”, but do you have any evidence for this assertion? The YUI blog describes Douglas Crokford telling some of his stall about the construct but makes no claims about his originating (or developing) it.
The module pattern has been in regular (if perhaps not common) use since at least early 2003. In the absence of any evidence of an earlier publication, I was probably the first to propose the structure that became the module pattern. For example, in:-
- and the small group of individuals who then “originally developed” those ideas into what later came as interesting news to the YUI developers did not include Douglas Crockford.
I will have another go at posting that example URL as it appears to have been skipped the first time.
http://groups.google.com/group/comp.lang.javascript/msg/9f58bd11bd67d937
This is an extremely clear elucidation of the module pattern. I will be referencing it in all my code for anyone who comes along later who might be unfamiliar with this pattern. Thanks!