Injector 注入器 - acelan86/angular GitHub Wiki

该方法用来创建注入器,注入的概念请参照angularjs官网说明

loadModules 加载模块

loadModules在创建注入器时被调用,返回runBlocks数组

  var loadedModules = new HashMap(); //用来记录加载过的模块
  ...
  /**
   * 改方法完成如下事情:
   * 1. 遍历所有需要加载的模块modulesToLoad
   * 2. 如果需要加载的module是一个字符串,执行下面的步骤:
   *     2.1  调用angular.module方法获取或新建一个模块moduleInatance
   *     2.2  执行loadModules(moduleInstance.requires), 将moduleInstace._runBlocks和执行结果concat到runBlocks结果集中
   *     2.3  执行moduleInatance._invokeQueue中记录的所有方法
   *    否则,xxxx
   *    否则,xxxx
   * 3. 将runBlocks返回
  */
   
  function loadModules(modulesToLoad){
    var runBlocks = [];
    forEach(modulesToLoad, function(module) { //#1
      if (loadedModules.get(module)) return;
      loadedModules.put(module, true);
      if (isString(module)) { //#2
        var moduleFn = angularModule(module);  //#2.1
        runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks); #2.2

        try { //#2.3
          for(var invokeQueue = moduleFn._invokeQueue, i = 0, ii = invokeQueue.length; i < ii; i++) {
            var invokeArgs = invokeQueue[i],
                provider = invokeArgs[0] == '$injector'
                    ? providerInjector
                    : providerInjector.get(invokeArgs[0]);

            provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
          }
        } catch (e) {
          if (e.message) e.message += ' from ' + module;
          throw e;
        }
      } else if (isFunction(module)) { //else
        try {
          runBlocks.push(providerInjector.invoke(module));
        } catch (e) {
          if (e.message) e.message += ' from ' + module;
          throw e;
        }
      } else if (isArray(module)) { //else
        try {
          runBlocks.push(providerInjector.invoke(module));
        } catch (e) {
          if (e.message) e.message += ' from ' + String(module[module.length - 1]);
          throw e;
        }
      } else {
        assertArgFn(module, 'module');
      }
    });
    return runBlocks;  //#3
  }

createInjector创建注入器

function createInjector(modulesToLoad) {
  var loadedModules = new HashMap(), //就是一个obj模拟hasMap, 记录已经加载过的模块
      providerCache = {
        $provide: { ... }
      },
      providerInjector = createInternalInjector(provideCache,...), //先不管他们是啥
      instanceCache = {},
      instanceInjector = instanceCache.$injector = createInternalInjector(instanceCache, ...); //先不管他们是啥
  ...

  forEach(
    loadModules(modulesToLoad),
    function(fn) { 
      instanceInjector.invoke(fn || noop);
    }
  );
  ...

  return instanceInjector;
}
function createInternalInjector(cache, factory) {

    function getService(serviceName) {
        //依赖是一个服务,获取该服务
        //这里会用到cache和factory
        //这里做的就是,如果是一个service,那么从cache中(可能是一个providerCache,也可能是一个injectorCache)中获取name为serviceName的service,返回
    }

    //依赖注入的过程
    function invoke(fn, self, locals){
      var args = [],
          $inject = annotate(fn),  //将fn中的依赖注入模块名解析出来,返回$inject, annotate见下面说明
          length, i,
          key;

      //得到$inject中需要依赖注入的实际值(可能是locals或者是一个service)存放在args中,作为调用实参
      for(i = 0, length = $inject.length; i < length; i++) {
        key = $inject[i];
        args.push(
          locals && locals.hasOwnProperty(key)
          ? locals[key]
          : getService(key)
        );
      }

      //找不到fn.$inject说明传入的fn是一个['$xxx', function ($xxx) {}]这样的依赖声明格式
      if (!fn.$inject) {
        fn = fn[length];  //最后一个元素为真正要执行的fn
      }
      //源码中一坨switch简单看成:
      return fn.apply(self, args); //这里就是真正的依赖注入,将依赖与执行函数的形参绑定,完成注入
    }

    //这方法还没细看干嘛的,感觉就是invoke的一个包装而已。
    function instantiate(Type, locals) {
      ...
      return isObject(returnedValue) ? returnedValue : instance;
    }

    return {
      invoke: invoke,
      instantiate: instantiate,
      get: getService,
      annotate: annotate
    };
  }

annotate注解方法

此方法用来解析依赖注入的不同写法, 最终返回注入的依赖名称

//方法1
function foo($scope, $service) {
   ...todo something
}

//方法2, function的形参可以随意名字
['$scope', '$sercice', function ($scope, $service) {
    ..todo something;
}]

//方法3
function foo($scope, $service) {
  ..todo something;
}
foo.$inject = ['$scope', '$service'];

实现如下:

var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m; //匹配function (xxx, xxx)中的'xxx,xxx'这样的依赖注入名称字符串
var FN_ARG_SPLIT = /,/;    //你懂的
var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;  //匹配参数名
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; //匹配代码中的注释

function annotate(fn) {
  var $inject,
      fnText,
      argDecl,
      last;

  if (typeof fn == 'function') {
    if (!($inject = fn.$inject)) {
      $inject = [];
      fnText = fn.toString().replace(STRIP_COMMENTS, ''); //去除注释
      argDecl = fnText.match(FN_ARGS);
      forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg){
        arg.replace(FN_ARG, function(all, underscore, name){
          $inject.push(name);
        });
      });
      fn.$inject = $inject; //这里对没有$inject属性的fn进行赋值,保证一个function类型的fn一定有保持依赖名字的属性
    }
  } else if (isArray(fn)) {
    last = fn.length - 1;
    assertArgFn(fn[last], 'fn');
    $inject = fn.slice(0, last);
  } else {
    assertArgFn(fn, 'fn', true);
  }
  return $inject;
}