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(控制器服务提供者)中实例化一个控制器实例对象,然后给应用的(注入进去)。
加油!
posted on 2015-02-10 17:23 chaojidan 阅读(1782) 评论(0) 编辑 收藏 举报