When writing JavaScript code, I often use module pattern. A function constructs an object to be returned and used by the outside world. This could be immediately invoked function expression, or CommonJs module.
1 | var calc = (function initCalc() { |
Using function expression initCalc helps to keep functions add and sub private
and inaccessible to the outside world.
We separate individual features (functions add and sub) from constructing the public API
(returning an object with add and sub method properties).
The module reveals certain API to the world; this is called
revealing module pattern.
The explicit export can be taken one step further to write portable javascript code using
universal module definition.
Let us think about the exported (returned object). It has full access to the variables
defined inside the initCalc closure. I always try to follow simple rule to make the API
easier to use
Avoid using
thisin the returned API object whenever possible.
Suppose we decide that subtraction (sub method) should be implemented using addition and decide to reuse the add
method. We could go about it in two ways.
1 - Use this keyword
1 | var calc = (function initCalc() { |
This method has huge shortcoming: the function calc.sub is only valid if the sub method is
called in the context of the calc object. In other words, unless we call calc.sub it will not work.
Thus we cannot use it as a callback
1 | process.nextTick(calc.sub, 2, 3); |
If we want to pass sub around, we need to bind it to the calc object
1 | var sub = calc.sub.bind(calc); |
If we do partial application, we must bind calc too
1 | var subFrom5 = calc.sub.bind(calc, 5); |
2 - Use closure variables
Let us change code slightly to make much friendlier API
1 | var calc = (function initCalc() { |
Now we can freely pass calc methods around - they no longer need to be bound.
1 | process.nextTick(calc.sub, 2, 3); // silent -1 |
We can do partial application without worrying about the context
1 | var subFrom5 = calc.sub.bind(null, 5); |
Conclusion
When constructing the public API object, it pays to think how to make it simpler to use.
Using this keyword requires each function to be invoked as a method, or be bound to the
API object. Avoiding this and using closure variables directly makes the API just a
collection of composable functions - a simple and useful module pattern.
Update - an interesting solution by Peter Downs
Peter Downs @peterldowns showed me an interesting 3rd solution
to this problem. He used a functional expression called with new keyword to attach exported methods
to this object returned implicitly. See gist
1 | var calc = new function calcModule() { |