Preface
Actually, the Module Pattern here is the Module Pattern proposed by Douglas, exactly the same. But the author analyzed the module management of mainstream JS libraries (frameworks), which is a highlight.
I. Module Pattern
JS doesn't have the concept of class, naturally there's no class-based encapsulation. The biggest advantage of class might be module (namespace) management and access control, which JS doesn't have. But using JS function scope characteristics, access control (encapsulation) can be implemented. Code that speaks:
var module = (function() {
// Private attributes
var attr1 = 1;
function fun1() {
// ...
}
// Return anonymous object, of course, can also return non-anonymous object
return {
// Public attributes
attr2: attr1,
fun2: fun1
}
})(); // IIFE, Immediately Invoked Function Expression
From outside, only public attributes attr2 and fun2 can be accessed. Private attributes attr1 and fun1 are restricted from access (undefined). This seems like good encapsulation. But the problem is module is a singleton, cannot be reused. If you want to reuse, need to make a small change:
function Module() {
// Private attributes
var attr1 = 1;
function fun1() {
// ...
}
// Public attributes
this.attr2 = attr1;
this.fun2 = fun1;
}
var module1 = new Module();
var module2 = new Module();
From the perspective of access control (encapsulation), both methods work. The disadvantage of the first method is the singleton limitation, also exists problems like unable to pass parameters during initialization. The second method supports reuse, but also has overhead of multiple instances. Specific usage should be determined according to scenarios.
P.S. For more information about Douglas's Module Pattern, please see AnYuQingYang: JS Learning Notes 3_Function Expressions
II. Module Pattern Variations
1. Introducing Mixins
(function(m, arg){
// ...
})(Module, arg);
This actually has nothing to do with the Module Pattern. The key point is not the "alias" emphasized by the author, but an important method in "module file-ization". AMD and CMD both have single module corresponding to single file. This method provides a way to expand modules located in other files. ("Introducing Mixins"? Just pretend you didn't see it...)
The above method has a problem: it requires modules to be loaded in strict order (Module object definition must be loaded first). Can use the following method to remove this restriction:
(function(m, arg){
// ...
})(Module || {}, arg);
This method can achieve asynchronous module loading, but modules depending on other modules are definitely not suitable.
2. Augmentation
(function() {
// Create
var obj = {};
// Augment
obj.attr = 1;
// Return
return obj;
})();
Nothing special. Worth mentioning is Douglas's Module Pattern idea: Create -> Augment -> Return. As for "Augmentation", I don't know what the author wants to say either...
III. Module Management of JS Libraries (Frameworks)
1. Dojo Modules
obj.setObject('module.submod.submod', (function() {
// Private attributes
// var
// function
return {
// Public attributes
// attr: val
};
})();
Very special module definition method, setObject(str, obj). Actually, there can be more convenient methods:
String.prototype.ns = function (obj) {
return setObject(this.toString(), obj);
};
Sometimes fully utilizing the flexibility provided by JS is very convenient (no need to worry too much about problems brought by extending prototypes. If ns might conflict, what about regns, regNS...)
2. ExtJS Modules
Ext.namespace('module');
module.submod = (function() {
// Private attributes
// var
// function
return {
// Public attributes
// attr: val
};
})();
Very similar to basic Module Pattern, easy to implement.
3. YUI Modules
Y.namespace('module.submod') = (function() {
// Private attributes
// var
// function
return {
// Public attributes
// attr: val
};
})();
Implementation methods are all similar. This implementation supports chain calling: Y.namespace('module.submod').attr = val;
4. JQuery Modules
function regLib(mod) {
$(function() {
if (mod.init) {
mod.init();
}
});
return mod;
}
var module = regLib((function() {
// Private attributes
// var
// function
return {
// Public attributes
// attr: val
init: function() {
// ...
}
};
})());
Using regLib function to automatically place mod.init() in DOMready event handler when loading new modules. Not really a feature. Actually, the plugin mechanism provided by JQuery is module management. Plugin mechanism is not used here, but provides a method to manage custom modules in non-plugin way in JQuery. JQuery plugin mechanism is as follows:
;(function ($) {
// Private attributes
// var
// function
$.fn.myPlugin = {
// Public attributes
// attr: val
version: 1.0
};
})(jQuery);
No essential difference, all are patterns.
IV. Revealing Module Pattern
The only difference from the Module Pattern mentioned earlier is requiring public attributes to be references of private attributes. Actually, the first example code already did this. Just because of this point, making it a new pattern might not be worth it. So this content is squeezed here.
Module internals can be handled very flexibly. Can choose to directly expose anonymous function references, private attribute references, or primitive values. No need to give up this flexibility to satisfy some "Revealing Module Pattern".
V. Advanced Module Pattern
P.S. This part is supplemented by the author. Fortunately, there's not much content in the book, giving the author a chance to find this article: In-depth Understanding of JavaScript Module Pattern
1. Shallow Copy
All implementation methods mentioned above directly extend module objects. If you want to create an independent module based on the original module object, need shallow/deep copy. For example:
var MODULE_TWO = (function (old) {
var my = {},
key;
for (key in old) {
if (old.hasOwnProperty(key)) {
my[key] = old[key];
}
}
var super_moduleMethod = old.moduleMethod;
my.moduleMethod = function () {
// override method on the clone, access to super through super_moduleMethod
};
return my;
}(MODULE));
The above example is shallow copy. Reference type attributes are still shared. To be completely independent, must perform deep copy.
2. Cross-File Private State
This is a very special method, similar to "functionalization idea" mentioned in [AnYuQingYang: "JavaScript: The Good Parts" - Functionalization](/articles/《javascript 语言精粹》之函数化/). On this basis, access control is added. Example code is as follows:
var MODULE = (function (my) {
var _private = my._private = my._private || {},
// Before module loads, enable access to _private, to allow expansion parts to operate on private content
_unseal = my._unseal = my._unseal || function () {
my._private = _private;
my._seal = _seal;
my._unseal = _unseal;
},
// After module loads, call to remove access permission to _private
_seal = my._seal = my._seal || function () {
delete my._private;
delete my._seal;
delete my._unseal;
};
// permanent access to _private, _seal, and _unseal
return my;
}(MODULE || {}));
When a module is divided into several files, call _unseal before module loads to get access permission to private attributes. After module loading completes, call _seal to delete access permission. _private is the my container in the functionalization part of "JavaScript: The Good Parts".
References
-
"JavaScript Design Patterns"
-
In-depth Understanding of JavaScript Module Pattern: Mentioned several advanced module patterns, very good
-
[AnYuQingYang: "JavaScript: The Good Parts" - Functionalization](/articles/《javascript 语言精粹》之函数化/)
No comments yet. Be the first to share your thoughts.