在《你不知道的Javacript上》中第5.5章中,有这么一段模块依赖加载器核心代码,我把它记下来并加上自己的理解
var MyModules = (function Manager() {
var modules = {}; 
function define(name, deps, impl) {
for (var i = 0; i < deps.length; i++) { 
deps[i] = modules[deps[i]]; 
} 
modules[name] = impl.apply(impl, deps); 
} 
function get(name) {
return modules[name]; 
}
return {
define: define, 
get: get
}; 
}) ();
MyModules.define("bar", [], function () {
function hello(who) {
return "Let me introduce: " + who; 
} 
return {
hello: hello
}; 
}); 

MyModules.define("foo", ["bar"], function (bar) {
var hungry = "hippo"; 
function awesome() {
console.log(bar.hello(hungry).toUpperCase()); 
} 
return {
awesome: awesome
}; 
}); 
var bar = MyModules.get("bar"); 
var foo = MyModules.get("foo"); 

console.log(
bar.hello("hippo") 
); // Let me introduce: hippo
foo.awesome(); // LET ME INTRODUCE: HIPPO
MyModules
|_ _ bar
| |_ _ hello(who)
| _ _ foo
|_ _awesome()
以上代码运行后如结构图所示:MyModules是个母模块,下面有两个子模块,分别实现了自己的方法。
- 然后解释一下母模块,MyModules提供了两个方法,define可以给用户定义自己的模块、get获取已有的模块。重点解释一下define。define有三个参数:name :要定义模块的名字,deps:模块所要依赖的,impl「接口」:实现的过程;
- 然后直接看foo字模块的定义:
MyModules.define("foo", ["bar"], function(bar) { … }这里比较模糊的是第二个参数 ["bar”] 有什么作用。在foo 的实现中,需要调用bar的方法hello,因此需要把模块bar传入,但是,bar是在哪里实现的?在MyModules实现的,我们需要提供一个参数来告诉MyModules帮我们把bar注入到foo中,这就是define函数第二个参数的用法。 - 回到define的实现代码中:
for (var i=0; i<deps.length; i++) { deps[i] = modules[deps[i]];}还记得第二个参数的作用吗? ['bar'] 告知MyModules我所要依赖的子模块,但这个参数是个数组,并且里面的元素'bar'是字符串,所以这段代码的功能是,遍历deps,并把字符串换成模块,也就是['bar'] 变成了[MyModules.bar] - 回到最重要的一段代码:
modules[name] = impl.apply(impl,deps),这个的用法是把[MyModules.bar] 注入到 第三个参数impl : function(bar) { … } 的实现中,然后this仍然指向自身,并把该函数赋值给modules。
总结:这种实现既保证了各个子模块的封闭性,又不缺乏可扩展性,很值得学习