转载

angular核心原理解析2:注入器的创建和使用

上一课没有讲到创建注入器的方法createInjector。

此方法,会创建两种不同的注入器:第一种叫做providerInjector,第二种叫做instanceInjector。providerInjector是用来创建provider的,instanceInjector是用来创建一个对象实例的。

我们可以在js代码中直接使用注入器:

var myModule = angular.module("myModule", []);

myModule.factory("person", function(){    //定义了一个服务person

return {

name:"chaojidan"

}

});

myModule.controller("myController", ["$scope", "$injector",     //myController依赖于服务person,但是它不声明依赖于person,而是通过注入器$injector来获得服务person的实例对象。

function($scope, $injector){

$injector.invoke(function(person){   //通过注入器$injector的invoke方法,把服务person的实例注入到函数function中。

console.log(person.name);

});

}

])

注入器$injector的annotate方法的作用:它主要分析函数的参数签名。比如:

$injector.annotate(function(arg1,arg2){})会得到[arg1, arg2]。

在前面的操作中,我们经常使用函数参数声明的方式来注入一个服务(实例对象),其实angular就是通过annotate方法得到参数的名字,然后通过注入器实例化这些对象,最后注入到函数中。

function annotate(fn) {  var $inject,   fnText,   argDecl,   last;  if (typeof fn == 'function') {   if (!($inject = fn.$inject)) {    $inject = [];    if (fn.length) {     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;   }  } else if (isArray(fn)) {   last = fn.length - 1;   assertArgFn(fn[last], 'fn');   $inject = fn.slice(0, last);  } else {   assertArgFn(fn, 'fn', true);  }  return $inject; } 

它是怎么获得函数的参数声明的呢?其实它是通过函数的toString方法得到整个函数的描述,然后通过正则表达式得到函数的参数。

在angular中,所有的provider都可以用来进行注入。我们创建provider有以下几种方式:

provider/factory/service/constant/value。

我们创建好provider之后,注入到哪里去呢?我们有以下几种方式来注入provider:

controller/directive/filter/service/factory等。

举个例子:

var myModule = angular.module("myModule", []);

myModule.provider("helloAngular", function(){      //通过provider方法创建一个服务提供者helloAngular

return {

$get : function(){    //provider方法来定义服务提供者的话,必须定义$get方法。

var name = "chaojidan";

function getName(){

return name;

}

return {

getName: getName

}

}

}

});

myModule.controller("myController", ["$scope", "helloAngular" ,     //通过controller方法注入helloAngular服务,也就是注入一个helloAngular服务的实例对象

function($scope, helloAngular){

$scope.name = helloAngular.getName();     //使用helloAngular服务的实例对象

}

])

第二个例子:

var myModule = angular.module("myModule", []);

myModule.factory("helloAngular", function(){      //通过factory方法创建一个服务提供者helloAngular

var name = "chaojidan";

function getName(){

return name;

}

return {

getName:getName

}

});

myModule.controller("myController", ["$scope", "helloAngular" ,     //通过controller方法注入helloAngular服务,也就是注入一个helloAngular服务的实例对象

function($scope, helloAngular){

$scope.name = helloAngular.getName();     //使用helloAngular服务的实例对象

}

])

第三个例子:

var myModule = angular.module("myModule", []);

myModule.service("helloAngular", function(){      //通过service方法创建一个服务提供者helloAngular

this.name = "chaojidan";

this.getName = function(){

return this.name;

}

});

myModule.controller("myController", ["$scope", "helloAngular" ,     //通过controller方法注入helloAngular服务,也就是注入一个helloAngular服务的实例对象

function($scope, helloAngular){

$scope.name = helloAngular.getName();     //使用helloAngular服务的实例对象

}

])

其实从angular源码可以知道,创建provider的这几种方式:provider/factory/service/constant/value,其中,

provider方法是基础,其他都是调用provider方法实现的,只是参数不同。从左到右,灵活性越差。

function provider(name, provider_) {      assertNotHasOwnProperty(name, 'service');      if (isFunction(provider_) || isArray(provider_)) {     provider_ = providerInjector.instantiate(provider_);      }      if (!provider_.$get) {       throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name);      }      return providerCache[name + providerSuffix] = provider_; } function factory(name, factoryFn) { 

return provider(name, { $get: factoryFn });
}
function service(name, constructor) { return factory(name, ['$injector', function($injector) { return $injector.instantiate(constructor); }]); } function value(name, val) {

return factory(name, valueFn(val));
}
function constant(name, value) { assertNotHasOwnProperty(name, 'constant'); providerCache[name] = value; instanceCache[name] = value; }

createInjector方法里面,其实是通过createInternalInjector方法来创建注入器的。

function createInternalInjector(cache, factory) {   function getService(serviceName) {  //注入器可以用来获取一个服务的实例     if (cache.hasOwnProperty(serviceName)) {      if (cache[serviceName] === INSTANTIATING) {       throw $injectorMinErr('cdep', 'Circular dependency found: {0}', path.join(' <- '));      }      return cache[serviceName];     } else {      try {       path.unshift(serviceName);       cache[serviceName] = INSTANTIATING;       return cache[serviceName] = factory(serviceName);      } catch (err) {       if (cache[serviceName] === INSTANTIATING) {        delete cache[serviceName];       }       throw err;      } finally {       path.shift();      }     }   }   function invoke(fn, self, locals){ //可以用来调用一个方法     var args = [],      $inject = annotate(fn),      length, i,      key;     for(i = 0, length = $inject.length; i < length; i++) {      key = $inject[i];      if (typeof key !== 'string') {       throw $injectorMinErr('itkn',        'Incorrect injection token! Expected service name as string, got {0}', key);      }      args.push(       locals && locals.hasOwnProperty(key)        ? locals[key]        : getService(key)      );     }     if (!fn.$inject) {      // this means that we must be an array.      fn = fn[length];     }     // http://jsperf.com/angularjs-invoke-apply-vs-switch     // #5388     return fn.apply(self, args);    }   function instantiate(Type, locals) {   //可以用来实例化一个对象     var Constructor = function() {},      instance, returnedValue;     // Check if Type is annotated and use just the given function at n-1 as parameter     // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);     Constructor.prototype = (isArray(Type) ? Type[Type.length - 1] : Type).prototype;     instance = new Constructor();     returnedValue = invoke(Type, instance, locals);     return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance;  }  return { //返回的对象,其实就是注入器     invoke: invoke,     instantiate: instantiate,     get: getService,     annotate: annotate, //可以用来分析一个函数的签名     has: function(name) {      return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);     }  }; } 

注入器总共有invoke,instantiate,get,annotate,has五个方法,其中,annotate用来分析一个函数的签名,也就是函数的参数,invoke用来调用一个函数,get用来获得一个服务的实例对象,instantiate用来实例化一个对象。

angularJS在初始化启动时,注册了一些内置的provider。在publishExternalAPI方法中:

$provide.provider({  $anchorScroll: $AnchorScrollProvider,  $animate: $AnimateProvider,  $browser: $BrowserProvider,  $cacheFactory: $CacheFactoryProvider,  $controller: $ControllerProvider,  $document: $DocumentProvider,  $exceptionHandler: $ExceptionHandlerProvider,  $filter: $FilterProvider,  $interpolate: $InterpolateProvider,  $interval: $IntervalProvider,  $http: $HttpProvider,  $httpBackend: $HttpBackendProvider,  $location: $LocationProvider,  $log: $LogProvider,  $parse: $ParseProvider,  $rootScope: $RootScopeProvider,  $q: $QProvider,  $sce: $SceProvider,  $sceDelegate: $SceDelegateProvider,  $sniffer: $SnifferProvider,  $templateCache: $TemplateCacheProvider,  $timeout: $TimeoutProvider,  $window: $WindowProvider }); 

我们以$controller: $ControllerProvider,为例子,来看下内置的provider是如何定义的?

function $ControllerProvider() {  var controllers = {},   CNTRL_REG = /^(/S+)(/s+as/s+(/w+))?$/;  this.register = function(name, constructor) {    assertNotHasOwnProperty(name, 'controller');   if (isObject(name)) {    extend(controllers, name);   } else {    controllers[name] = constructor;   }  };  this.$get = ['$injector', '$window', function($injector, $window) { //provider必须有$get方法   return function(expression, locals) {    var instance, match, constructor, identifier;    if(isString(expression)) {     match = expression.match(CNTRL_REG),      constructor = match[1],      identifier = match[3];     expression = controllers.hasOwnProperty(constructor)      ? controllers[constructor]      : getter(locals.$scope, constructor, true) || getter($window, constructor, true);     assertArgFn(expression, constructor, true);    }    instance = $injector.instantiate(expression, locals); //当你想去拿控制器,也就是controller实例时,实际上是注入器帮你实例化的。    if (identifier) {     if (!(locals && typeof locals.$scope == 'object')) {      throw minErr('$controller')('noscp',       "Cannot export controller '{0}' as '{1}'! No $scope object provided via `locals`.",       constructor || expression.name, identifier);     }     locals.$scope[identifier] = instance;    }    return instance;   };  }]; } 

当你的应用中,需要控制器实例对象时,也就是需要controller这个服务时,实际上是注入器在ControllerProvider(控制器服务提供者)中实例化一个控制器实例对象,然后给应用的(注入进去)。

加油!

正文到此结束
Loading...