关于《你不知道的Javacript》中模块依赖加载器的理解

394 阅读2分钟

在《你不知道的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是个母模块,下面有两个子模块,分别实现了自己的方法。

  1. 然后解释一下母模块,MyModules提供了两个方法,define可以给用户定义自己的模块、get获取已有的模块。重点解释一下define。define有三个参数:name :要定义模块的名字,deps:模块所要依赖的,impl「接口」:实现的过程;
  2. 然后直接看foo字模块的定义: MyModules.define("foo", ["bar"], function(bar) { … }这里比较模糊的是第二个参数 ["bar”] 有什么作用。在foo 的实现中,需要调用bar的方法hello,因此需要把模块bar传入,但是,bar是在哪里实现的?在MyModules实现的,我们需要提供一个参数来告诉MyModules帮我们把bar注入到foo中,这就是define函数第二个参数的用法。
  3. 回到define的实现代码中:for (var i=0; i<deps.length; i++) { deps[i] = modules[deps[i]];} 还记得第二个参数的作用吗? ['bar'] 告知MyModules我所要依赖的子模块,但这个参数是个数组,并且里面的元素'bar'是字符串,所以这段代码的功能是,遍历deps,并把字符串换成模块,也就是['bar'] 变成了[MyModules.bar]
  4. 回到最重要的一段代码:modules[name] = impl.apply(impl,deps),这个的用法是把[MyModules.bar] 注入到 第三个参数impl : function(bar) { … } 的实现中,然后this仍然指向自身,并把该函数赋值给modules。

总结:这种实现既保证了各个子模块的封闭性,又不缺乏可扩展性,很值得学习