在上面的代码中,Array.prototype.slice.call(arguments, 0)的作用是将arguments变量转换为一个真正的数组。到目前为止,我们的代码可以完美的通过测试。但是这里的问题是我们必须要将需要的模块写两次,而且不能够随意排列顺序。额外的参数总是排在所有的依赖项之后。
  反射(reflection)方法
  根据维基百科中的解释,反射(reflection)指的是程序可以在运行过程中,一个对象可以修改自己的结构和行为。在JavaScript中,简单来说是阅读一个对象的源码并且分析源码的能力。还是回到我们的doSomething方法,如果你调用doSomething.toString()方法,你可以获得下面的字符串:
  "function (service, router, other) {
  var s = service();
  var r = router();
  }"
  这样一来,只要使用这个方法,我们可以轻松的获取到我们想要的参数,以及更重要的一点是他们的名字。这也是AngularJS实现依赖注入所使用的方法。在AngularJS的代码中,我们可以看到下面的正则表达式:
  /^functions*[^(]*(s*([^)]*))/m
  我们可以将resolve方法修改成如下所示的代码:
  resolve: function() {
  var func, deps, scope, args = [], self = this;
  func = arguments[0];
  deps = func.toString().match(/^functions*[^(]*(s*([^)]*))/m)[1].replace(/ /g, '').split(',');
  scope = arguments[1] || {};
  return function() {
  var a = Array.prototype.slice.call(arguments, 0);
  for(var i=0; i<deps.length; i++) {
  var d = deps[i];
  args.push(self.dependencies[d] && d != '' ? self.dependencies[d] : a.shift());
  }
  func.apply(scope || {}, args);
  }       
  }
  我们使用上面的正则表达式去匹配我们定义的函数,我们可以获取到下面的结果:
  ["function (service, router, other)", "service, router, other"]
  此时,我们只需要第二项。但是一旦我们去除了多余的空格并以,来切分字符串以后,我们得到了deps数组。下面的代码是我们进行修改的部分:
  var a = Array.prototype.slice.call(arguments, 0);
  ...
  args.push(self.dependencies[d] && d != '' ? self.dependencies[d] : a.shift());
  在上面的代码中,我们遍历了依赖项目,如果其中有缺失的项目,如果依赖项目中有缺失的部分,我们从arguments对象中获取。如果一个数组是空数组,那么使用shift方法将只会返回undefined,而不会抛出一个错误。到目前为止,新版本的injector看起来如下所示:
  var doSomething = injector.resolve(function(service, other, router) {
  expect(service().name).to.be('Service');
  expect(router().name).to.be('Router');
  expect(other).to.be('Other');
  });
  doSomething("Other");
  在上面的代码中,我们可以随意混淆依赖项的顺序。
  但是,没有什么是完美的。反射方法的依赖注入存在一个非常严重的问题。当代码简化时,会发生错误。这是因为在代码简化的过程中,参数的名称发生了变化,这将导致依赖项无法解析。例如:
  var doSomething=function(e,t,n){var r=e();var i=t()}
  因此我们需要下面的解决方案,像AngularJS中那样:
  var doSomething = injector.resolve(['service', 'router', function(service, router) {
  }]);
  这和一开始看到的AMD的解决方案很类似,于是我们可以将上面两种方法整合起来,终代码如下所示:
  var injector = {
  dependencies: {},
  register: function(key, value) {
  this.dependencies[key] = value;
  },
  resolve: function() {
  var func, deps, scope, args = [], self = this;
  if(typeof arguments[0] === 'string') {
  func = arguments[1];
  deps = arguments[0].replace(/ /g, '').split(',');
  scope = arguments[2] || {};
  } else {
  func = arguments[0];
  deps = func.toString().match(/^functions*[^(]*(s*([^)]*))/m)[1].replace(/ /g, '').split(',');
  scope = arguments[1] || {};
  }
  return function() {
  var a = Array.prototype.slice.call(arguments, 0);
  for(var i=0; i<deps.length; i++) {
  var d = deps[i];
  args.push(self.dependencies[d] && d != '' ? self.dependencies[d] : a.shift());
  }
  func.apply(scope || {}, args);
  }       
  }
  }
  这一个版本的resolve方法可以接受两个或者三个参数。下面是一段测试代码:
  var doSomething = injector.resolve('router,,service', function(a, b, c) {
  expect(a().name).to.be('Router');
  expect(b).to.be('Other');
  expect(c().name).to.be('Service');
  });
  doSomething("Other");
  你可能注意到了两个逗号之间什么都没有,这并不是错误。这个空缺是留给Other这个参数的。这是我们控制参数顺序的方法。
  结语
  在上面的内容中,我们介绍了几种JavaScript中依赖注入的方法,希望本文能够帮助你开始使用依赖注入这个技巧,并且写出依赖注入风格的代码。