计算机编程的世界其实是一个将简单的部分不断抽象,并将这些抽象组织起来的过程。JavaScript也不例外,在我们使用JavaScript编写应用时,我们是不是都会使用到别人编写的代码,例如一些的开源库或者框架。随着我们项目的增长,我们需要依赖的模块变得越来越多,这个时候,如何有效的组织这些模块成了一个非常重要的问题。依赖注入解决的正是如何有效组织代码依赖模块的问题。你可能在一些框架或者库种听说过“依赖注入”这个词,比如说的前端框架AngularJS,依赖注入是其中一个非常重要的特性。但是,依赖注入根本不是什么新鲜玩意,它在其他的编程语言例如PHP中已经存在已久。同时,依赖注入也没有想象种那样复杂。在本文中,我们将一起来学习JavaScript中的依赖注入的概念,深入浅出的讲解如何编写“依赖注入风格”的代码。
  目标设定
  假设我们现在拥有两个模块。第一个模块的作用是发送Ajax请求,而第二个模块的作用则是用作路由。
  var service = function() {
  return { name: 'Service' };
  }
  var router = function() {
  return { name: 'Router' };
  }
  这时,我们编写了一个函数,它需要使用上面提到的两个模块:
  var doSomething = function(other) {
  var s = service();
  var r = router();
  };
  在这里,为了让我们的代码变得有趣一些,这个参数需要多接收几个参数。当然,我们完全可以使用上面的代码,但是无论从哪个方面来看上面的代码都略显得不那么灵活。要是我们需要使用的模块名称变为ServiceXML或者ServiceJSON该怎么办?或者说如果我们基于测试的目的想要去使用一些假的模块改怎么办。这时,我们不能仅仅去编辑函数本身。因此我们需要做的第一件事情是将依赖的模块作为参数传递给函数,代码如下所示:
  var doSomething = function(service, router, other) {
  var s = service();
  var r = router();
  };
  在上面的代码中,我们完全传递了我们所需要的模块。但是这又带来了一个新的问题。假设我们在代码的哥哥部分都调用了doSomething方法。这时,如果我们需要第三个依赖项该怎么办。这个时候,去编辑所有的函数调用代码并不是一个明智的方法。因此,我们需要一段代码来帮助我们做这件事情。这是依赖注入器试图去解决的问题。现在我们可以来定下我们的目标了:
  我们应该能够去注册依赖项
  依赖注入器应该接收一个函数,然后返回一个能够获取所需资源的函数
  代码不应该复杂,而应该简单友好
  依赖注入器应该保持传递的函数作用域
  传递的函数应该能够接收自定义的参数,而不仅仅是被描述的依赖项
  requirejs/AMD方法
  或许你已经听说过了大名鼎鼎的requirejs,它是一个能够很好的解决依赖注入问题的库:
  define(['service', 'router'], function(service, router) {      
  // ...
  });
  requirejs的思想是首先我们应该去描述所需要的模块,然后编写你自己的函数。其中,参数的顺序很重要。假设我们需要编写一个叫做injector的模块,它能够实现类似的语法。
  var doSomething = injector.resolve(['service', 'router'], function(service, router, other) {
  expect(service().name).to.be('Service');
  expect(router().name).to.be('Router');
  expect(other).to.be('Other');
  });
  doSomething("Other");
  在继续往下之前,需要说明的一点是在doSomething的函数体中我们使用了expect.js这个断言库来确保代码的正确性。这里有一点类似TDD(测试驱动开发)的思想。
  现在我们正式开始编写我们的injector模块。首先它应该是一个单体,以便它能够在我们应用的各个部分都拥有同样的功能。
  var injector = {
  dependencies: {},
  register: function(key, value) {
  this.dependencies[key] = value;
  },
  resolve: function(deps, func, scope) {
  }
  }
  这个对象非常的简单,其中只包含两个函数以及一个用于存储目的的变量。我们需要做的事情是检查deps数组,然后在dependencies变量种寻找答案。剩余的部分,则是使用.apply方法去调用我们传递的func变量:
  resolve: function(deps, func, scope) {
  var args = [];
  for(var i=0; i<deps.length, d=deps[i]; i++) {
  if(this.dependencies[d]) {
  args.push(this.dependencies[d]);
  } else {
  throw new Error('Can't resolve ' + d);
  }
  }
  return function() {
  func.apply(scope || {}, args.concat(Array.prototype.slice.call(arguments, 0)));
  }       
  }
  如果你需要指定一个作用域,上面的代码也能够正常的运行。