上一课没有讲到创建注入器的方法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(控制器服务提供者)中实例化一个控制器实例对象,然后给应用的(注入进去)。

加油!

angular核心原理解析2:注入器的创建和使用的更多相关文章

  1. angular核心原理解析1:angular自启动过程

    angularJS的源代码整体上来说是一个自执行函数,在angularJS加载完成后,就会自动执行了. angular源代码中: angular = window.angular || (window ...

  2. angular核心原理解析3:指令的执行过程

    指令的执行过程分析. 我们知道指令的执行分两个阶段,一个是compile,一个是link. 我们可以在指令中自定义compile和link. 首先,我们来讲解如何自定义link函数 举个例子: < ...

  3. 【算法】(查找你附近的人) GeoHash核心原理解析及代码实现

    本文地址 原文地址 分享提纲: 0. 引子 1. 感性认识GeoHash 2. GeoHash算法的步骤 3. GeoHash Base32编码长度与精度 4. GeoHash算法 5. 使用注意点( ...

  4. Java并发包JUC核心原理解析

    CS-LogN思维导图:记录CS基础 面试题 开源地址:https://github.com/FISHers6/CS-LogN JUC 分类 线程管理 线程池相关类 Executor.Executor ...

  5. 「进阶篇」Vue Router 核心原理解析

    前言 此篇为进阶篇,希望读者有 Vue.js,Vue Router 的使用经验,并对 Vue.js 核心原理有简单了解: 不会大篇幅手撕源码,会贴最核心的源码,对应的官方仓库源码地址会放到超上,可以配 ...

  6. ibatis 核心原理解析!

    关注下方公众号,可以在公众号后台回复“博客园”,免费获得作者 Java 知识体系/面试必看资料. 最近查找一个生产问题的原因,需要深入研究 ibatis 框架的源码.虽然最后证明问题的原因与 ibat ...

  7. ibatis 核心原理解析

    最近查找一个生产问题的原因,需要深入研究 ibatis 框架的源码.虽然最后证明问题的原因与 ibatis 无关,但是这个过程加深了对 ibatis 框架原理的理解. 这篇文章主要就来讲讲 ibati ...

  8. Promise核心原理解析

    作者: HerryLo 本文永久有效链接: https://github.com/AttemptWeb...... Promises对象被用于表示一个异步操作的最终完成 (或失败), 及其结果值.主要 ...

  9. NameServer 核心原理解析

    在之前的文章中,已经把 Broker.Producer 和 Conusmer 的部分源码和核心的机制介绍的差不多了,但是其实 RocketMQ 中还有一个比较关键但是我们平时很容易忽略的组件--Nam ...

随机推荐

  1. NBU 还原windows ORACLE数据库(EC)

    rman target / startup nomount; run{ allocate channel ch00 type 'SBT_TAPE'; send 'nb_ora_serv=nbumast ...

  2. errorlevel 续1

    -------siwuxie095             常用 errorlevel 返回值:     backup 0 备份成功 1 未找到备份文件 2 文件共享冲突阻止备份完成 3 用户用 ct ...

  3. c语言define和typedef区别和使用

    define完全可以理解替换,typedef代表别名.听着差不多的意思,那2者区别在哪? 先来个简单例子查看基本使用. //define和typedef区别 #define DB double //替 ...

  4. sublime相关资源

    Sublime Text 全程指南 http://zh.lucida.me/blog/sublime-text-complete-guide/ Sublime官网Package安装 https://p ...

  5. Mysql--关于数值字段的比较问题

    今天在进行数据库查询的过程中,因为需要比较一条记录中两个字段的大小问题 select * from cyber_download_rate where measure_time between '20 ...

  6. RocketMQ 加载配置文件

    BrokerStartup.java // 指定配置文件 if (commandLine.hasOption('c')) { String file = commandLine.getOptionVa ...

  7. Google Tango初学者教程

    Getting Started with the Tango Java API In this tutorial, we'll go through setting up your build env ...

  8. struts2 动态Action

    1.java 2.struts.xml struts2.5,默认关闭动态Action,着色的是开启和使用动态action 3.JSP 小结:访问时,用!后跟方法名的方法,方法返回值----->r ...

  9. Appium之打开应用时提示框处理

    当打开一个应用时,会有一个无关紧要的提示框,如果要继续操作,需要先关闭提示框,如下图(如新用户福利提示): 此时,如果你直接用Appium inspector或者Android uiautomator ...

  10. scala中Nil用法

    http://www.runoob.com/scala/scala-lists.html 即Nil是空List 双冒号是追加进入 package com.yjsj.spark object scala ...