模块是提供一些特殊服务的功能块。比方本地化模块负责文字本地化,验证模块负责数据验证。一般来说,服务在模块内部,当我们须要某个服务的时候,是先把模块实例化。然后再调用模块的方法。

但Angular模块和我们通常理解的模块不一样。Angular模块仅仅保留服务的声明,服务的实例化是由服务注入器完毕的,实例化之后服务就留在了服务注入器中,和模块没有关系了,这就是为什么我们使用的服务全部来自注入器的原因。

每调用一次angular.boostrap()方法会创建一个新的Angular应用和一个新的服务注入器,因此。每一个应用都相应一个服务注入器。彼此互不冲突。

在angular中,模块能够是对象、方法(假设是数组,数组的最后一个元素必须是方法。前面的元素都是方法按顺序排列的參数名称)。后面讲的模块属性和方法。都属于通过angular.module()定义的模块对象。假设模块是方法。是不须要经过angular.module()定义的,仅仅需写入依赖数组(就是说依赖数组的元素能够是方法)。模块在载入依赖关系的时候直接运行了。

注意:通过angular.module()方法定义的模块是唯一的。假设反复定义就会覆盖前面的定义。

angular模块

angular模块通过angular.module(name,requires, configFn)方法生成:

  • 參数name是模块名称。
  • 參数requires表示依赖模块数组。

    假设不设置requires參数,调用angular.module(name)方法表示获取这个模块;因此,假设确定新模块没有依赖关系,必须设置requires为空数组[];

  • 參数configFn是方法或数组。负责在模块初始化时做一些配置。假设是数组。最后一个元素必须是方法。

方法configFn并非在运行angular.module()的时候马上运行,而是当这个模块被第一次使用时,由注入器调用运行。

同一时候,查看方法configFn中的this就会发现,这个this在浏览器中指向的是window。而不是module。并且,方法configFn仅仅会运行一次,因此同一个angular模块不会反复配置。

參数requires中的字符串表示依赖的模块名称。假设不是字符串。则必须是方法(或数组格式的方法),那么。这种方法就代表了一个模块。

同名模块

已经初始化的angular模块保存在一个叫modules的缓存对象中,key是模块名,value是模块对象。所以。定义一个同名的模块,等于覆盖之前的模块。

服务注入

前面已经讲了,angular模块仅仅保留服务的定义,如今我们再来理解服务是怎样加入注入器的。

在了解服务注入器前。还须要了解还有一个概念,就是服务提供商。在Angular中称为Provider,差点儿全部的服务(除了$injector)都是由服务提供商供应。不管是服务还是服务提供商。他们在Angular中都是唯一的,服务和服务提供商是一个一对一的关系。也正是由于这个关系,我们在使用模块service()、value()等方法时,感觉我们定义的好像仅仅是服务,但事实上Angular背后为这些服务都创建了一一相应的服务提供商。注入器的机制就是当我们须要某个服务的时候,首先依据服务名找到相应的服务提供商。然后由服务提供商创建相应的服务并返回。

所以这就是整个过程:

  1. 模块定义服务、服务提供商。
  2. 注入器依据模块依赖关系载入模块,实例化全部服务提供商;
  3. 应用须要服务,注入器依据服务名寻找服务提供商。服务提供商实例化服务。

以上仅仅是理论。如今从代码层面来看Angular是怎样实现的。

每一个angular模块内置有三个数组。invokeQueue保存怎样注入服务提供商和值的信息;configBlocks保存模块的配置信息。runBlocks保存这个模块的运行信息。

模块被使用的时候,注入器依据invokeQueue中的信息。实例化服务提供商;依据configBlocks中的信息对服务提供商做一些额外的处理;依据runBlocks中提供的信息,调用前面的服务提供商提供的服务运行模块须要完毕的工作。

angular模块提供了非常多方法来填充这三个数组,比方config()、run()等。三个数组的元素也是数组。详细元素格式參考后面的说明。

调用队列 – invokeQueue

数组元素为[‘provider’, ‘method’, arguments]。

举例– 加入一个Controller:

angular.module('ngAppDemo',[])

.controller('ngAppDemoController',function($scope) {

      $scope.a= 1;

      $scope.b = 2;

});

这段代码等于:

invokeQueue.push(['$controllerProvider','register', ['ngAppDemoController', function(){}]]);

注入器依据这个信息,就会调用$controllerProvider的register方法注冊一个ngAppDemoController。

配置队列 – configBlocks

元素格式为['$injector', 'invoke', arguments]。

运行队列 – runBlocks

元素要求是方法,或者是数组。数组最后一个元素是方法。

angular模块实例属性和方法

属性

requires

表示模块的依赖数组。即angular.module方法的requires參数。

name

模块的名字。

_invokeQueue、_configBlocks、_runBlocks

分别相应invokeQueue、configBlocks、runBlocks。

方法

模块的下面方法最后全部会返回模块实例本身,形成运行链

animation()

调用这种方法表示这个模块将在$animateProvider中注冊一个动画服务。

原理:invokeQueue尾部插入['$animateProvider', 'register', arguments]。

config()

调用这种方法,表示给这个模块追加一个配置方法。

原理:在configBlocks尾部插入['$injector','invoke', arguments]

constant()

调用这种方法表示这个模块将给默认的$provider注冊一个常量。

原理:在invokeQueue首部插入['$provide', 'constant', arguments]。

controller()

调用这种方法表示模块将在$controllerProvider中注冊一个控制器。

原理:在invokeQueue尾部插入['$controllerProvider', 'register', arguments]。

directive()

调用这种方法表示这个模块将在$compileProvider中注冊一个指令。

原理:在invokeQueue尾部插入['$compileProvider', 'directive', arguments]。

factory()

调用这种方法表示这个模块中将生成一个服务工厂(隐式创建一个了服务提供商)。

原理:在invokeQueue尾部插入['$provide', 'factory', arguments]。

filter()

调用这种方法表示这个模块将在$filterProvider中注冊一个过滤器。

原理:在invokeQueue尾部插入['$filterProvider', 'register', arguments]。

provider()

调用这种方法表示这个模块将加入一个服务提供商。

原理:在invokeQueue尾部插入['$provide', 'provider', arguments]。

run(block)

调用这种方法表示这个模块将运行某个功能块。block能够是方法,也能够是数组。

原理:在invokeQueue尾部插入block。

service()

调用这种方法表示这个模块将注冊一个服务(隐式创建了一个服务提供商)。

原理:在invokeQueue尾部插入['$provide', 'service', arguments]。

value()

调用这种方法表示这个模块将注冊一个变量(隐式创建了一个服务提供商)。

原理:在invokeQueue尾部插入['$provide', 'value', arguments]。

服务注入器(Service Injector) & 服务提供商(Service Provider)

在Angular中,服务可能是对象、方法、或者一个常量值。服务由服务提供商创建。而服务提供商由注入器统一管理。当我们须要某个服务的时候。注入器负责依据服务名寻找相应的服务提供商,然后由服务提供商的$get()生产工厂创建服务实例。因此。服务提供商必须有一个$get()方法,这种方法就是服务创建单例工厂。

背后原理:注入器中的Providers和Services各自通过一个Map对象保存在缓存(分别相应providerCache和instanceCache)中,仅仅只是Providers的key是serviceName + “Provider”,而Services的key是serviceName。

服务提供商-Provider

Provider即服务提供商,必须有一个$get()方法。$get()的返回值是Provider在注入器中实际的服务。

注入器

创建注入器的方法仅仅在bootstrap()方法中被调用过,也就是说,每一个angular应用相应一个注入器。

注入过程

注入器由angular.injector(modulesToLoad, isStrictDi)方法创建。在angular中事实上为createInjector方法。參数modulesToLoad是数组,元素格式为下面之中的一个:

  • ‘module’,模块的名称。
  • [‘service1’, ‘service2’, fn]。
  • fn,方法的返回值必须仍然是方法。

方法angular.injector()的运行过程:

1.        遍历modulesToLoad。依据moduleName寻找或生成相应的模块。

2.        调用模块invokeQueue中的全部方法。这一步的目的是创建必需的服务。

3.        调用模块configBlocks中的全部方法,目的是用已有的服务配置这个模块。

4.        调用注入器的invoke()方法运行模块runBlocks的全部方法。

5.        返回服务注入器实例。

不是全部模块都是对象,假设模块本身是方法或者是数组(最后一个元素必须是方法)。则运行这种方法、或数组的最后一个方法,相当于直接进入了第四步。

注入器与bootstrap的关系

创建注入器的方法在angular.js中仅仅在bootstrap()方法中被调用过。也就是说,每一个angular应用相应一个注入器。

注入器方法

在providerCache中和instanceCache中分别内置有一个$injector对象,分别负责给模块注入服务提供商和为方法注入服务。

一般我们仅仅谈论instanceCache中的$injector对象。由于providerCache和它的$injector是私有的,仅仅在Angular内部代码使用。

比方,运行模块调用队列、配置队列中的方法时注入的是服务提供商,而当调用运行队列中的方法时,注入的是服务。

$injector.invoke(fn, self, locals, serviceName)

运行方法fn。

locals是可选參数,是对象,表示局部变量。

self是fn中的this。

最后一个參数serviceName是可选參数,表示在哪个服务中调用了fn方法,用于错误信息显示,没有处理逻辑。

$injector.instantiate(Type, locals, serviceName)

l  假设參数Type为方法,依据Type的prototype创建一个实例(通过Object.create方法创建),假设Type是数组。使用最后一个元素的prototype。

l  參数Locals是当Type方法的參数出如今locals对象中的时候。取locals[arg]的值又一次作为Type的參数。假设locals中没有。则等价于调用get(arg,serviceName)获取service作为新的參数。

实例化过程能够简单概括为

Type.apply(Object.create(Type.prototype),locals[argName]|| get(argName, serviceName))。

注意:实例化出来的不一定是对象。也可能是方法。

最后一个參数serviceName是可选參数,表示在哪个服务中实例化了Type,用于错误信息显示,没有处理逻辑。

$injector.get(name, caller)

从注入器中获取一个服务实例。

參数name是服务的名称。參数caller也是字符串,表示调用这个服务的是哪个方法,用于错误信息提示。没有处理逻辑。

$injector.annotate(fn[,strictDi][,name] )

返回数组。数组的元素是fn方法须要注入的依赖服务。

在严格模式下,方法的依赖注入必须使用显示的注解加入,也就是说通过fn.$injector能够获取这种方法的依赖注入。

參数name是可选的,用于错误显示,没有处理逻辑。

方法annotate()也能够接受数组,数组的最后一个參数一定是fn,前面的元素则是依赖。

$injector.has(name)

检查该注入器中是否存在指定的服务。

Provider方法

注入器的providerCache中内置有一个$provider对象,这是注入器的默认服务提供商,$provider有六个固定的方法。这几个方法的作用主要是为注入器加入其他服务提供商。

注意:

  • 下面全部方法的name參数不须要以“Provider”结尾,由于provider()方法会默认把这个后缀加上。

  • 下面不论什么一个方法不做同名推断,因此,假设出现同名,后者将覆盖前者。

$provider.provide(name, provider)

參数provider能够是方法或数组,也能够是对象。

l  假设是方法,则是provider的构造函数。

调用注入器的instantiate()方法,生成一个provider实例。并以name为key保存在注入器的providerCache中。

l  假设是数组。最后一个必须是provider的构造函数。前面的就是构造函数的參数名。之后的原理和provider是方法的情形同样。

l  假设是对象,说明这个provider已经被实例化了,仅仅需有$get()方法就可以。

$provider.factory(name, factoryFn, enforce)

使用$provider.provide()一般须要定义一个Provider类,假设不想定义Provider类,而是直接定义服务工厂。就能够使用这种方法。

背后原理:首先生成一个匿名对象,这个对象的$get属性就是factoryFn(enforce为false的情况下),然后把这个匿名对象作为$provider.provide()方法的第二个參数。所以。factoryFn事实上依旧是绑定在一个provider上的。

$provider.service(name, constructor)

调用injector.instantiate()方法,利用參数constructor生成service实例,參数name是这个service的名称。

众所周知,service由provider提供,那这种方法是怎么回事?原理:Angular首先依据constructor生成一个factoryFn,然后调用$provider.factory(name, factoryFn)。

所以事实上还是生成了一个provider。

举例:

$provider.service('filter', constructor)

等于创建了一个filter服务实例。并且在providerCache中保存了一个名称为“filterProvider”的服务提供商。

$provider.value(name, value)

这种方法实际调用injector.factory(name,valueFn(value), false)方法实现。所以事实上等于创建一个仅仅提供值的服务提供商。

$provider.constant(name, value)

这种方法直接在providerCache中加入一个属性实现。

$provider.decorate(serviceName, decorFn)

改动旧的服务,改为运行decorFn方法,并把servcieName原来的服务作为一个參数。參数名为$delegate。等价于一个静态代理。

背后原理:首先依据seviceName找到Provider,然后改动provider的$get属性。

服务实例化

全部服务都由Provider管理,除了常量值,其他一般都是还没有创建出来的。

了解了注入器的全部方法。也应该能够看出,仅仅有两个方法:get()和invoke()真实的调用了服务。

事实上,服务的实例化实际是在注入器的get()方法中完毕的。而invoke()仅仅是在须要的时候调用了get()。

angular内置模块

ngLocale - 本地化模块

angular.module('ngLocale',[]).provider('$locale', $LocaleProvider);

结果是invokeQueue.push(['$provide', 'provider',['$locale', $LocaleProvider]]);

ng

angular.module('ng',['ngLocale']).config(['$provide', function(){}]);

结果是configBlocks.push(['$injector', 'invoke',['$provide', function(){}]])。

三个固定模块

每一个使用bootstrap(element, modules, config)生成的应用,注入器中有三个固定的模块:

  • 第一个模块是"ng"。
  • 第二个模块是[‘$provider’,fn],它的作用是把根元素element作为变量保存在$provider中。

  • 第三个模块是[‘$compileProvider’,fn],它的作用是依据config.debugInfoEnabled调用 $conpileProvider.debugInfoEnabled(true)。

AngularJS模块具体解释的更多相关文章

  1. AngularJS 模块& 表单

    模块定义了一个应用程序. 模块是应用程序中不同部分的容器. 模块是应用控制器的容器. 控制器通常属于一个模块. 应用("myApp") 带有控制器 ("myCtrl&qu ...

  2. AngularJS 模块

    模块定义了一个应用程序. 模块是应用程序中不同部分的容器. 模块是应用控制器的容器. 控制器通常属于一个模块. 你可以通过 AngularJS 的 angular.module 函数来创建模块: &l ...

  3. 4.了解AngularJS模块和依赖注入

    1.模块和依赖注入概述 1.了解模块 AngularJS模块是一种容器,把代码隔离并组织成简洁,整齐,可复用的块. 模块本身不提供直接的功能:包含其他提供功能的对象的实例:控制器,过滤器,服务,动画 ...

  4. AngularJS模块的详解

    AngularJS模块的详解 在讲angularjs的模块之前,我们先介绍一下angular的一些知识点: AngularJS是纯客户端技术,完全用Javascript编写的.它使用的是网页开发的常规 ...

  5. AngularJS学习之旅—AngularJS 模块(十五)

    一.AngularJS 模块 模块定义了一个应用程序. 模块是应用程序中不同部分的容器. 模块是应用控制器的容器. 控制器通常属于一个模块. 1.创建模块 通过 AngularJS 的 angular ...

  6. AngularJS标准Web业务流程开发框架—1.AngularJS模块以及启动分析

    前言: AngularJS中提到模块是自定义的模块标准,提到这不得不说AngularJS是框架中的老大哥,思想相当的前卫..在这框架满天横行的时代,AngularJS有些思想至今未被超越,当然仁者见仁 ...

  7. 在文件中的AngularJS模块

    <!DOCTYPE html><html><head><meta http-equiv="Content-Type" content=&q ...

  8. 在body中的AngularJS模块

    <!DOCTYPE html><html><head><meta http-equiv="Content-Type" content=&q ...

  9. 【13】AngularJS 模块

    AngularJS 模块 模块定义了一个应用程序.(魔芋:也就是说一个ng-app代表一个应用程序,也就是一个模块,module) 模块是应用程序中不同部分的容器. 模块是应用控制器的容器. 控制器通 ...

随机推荐

  1. POJ3480 John 博弈论 anti-nim anti-SG

    http://poj.org/problem?id=3480 anti-nim其实是anti-SG的一种,就像nim是sg的一种一样.(或者说sg是nim推广?) 看名字就是规则和nim相反,取到最后 ...

  2. BZOJ 2467 [中山市选2010]生成树(组合数学)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=2467 [题目大意] 有一种图形叫做五角形圈.一个五角形圈的中心有1个由n个顶点和n条边 ...

  3. 【高精度】POJ1001-Exponentiation

    整个题库的第二题,原本都没有屑于去做,突发奇想抱着秒杀的心态去写了代码,却硬生生地吃了4个WA.. [思路]先去除掉小数点,进行最基本的高精度乘法运算,再在运算得到的结果中添加小数点输出. [前铺]让 ...

  4. (原创)Stanford Machine Learning (by Andrew NG) --- (week 10) Large Scale Machine Learning & Application Example

    本栏目来源于Andrew NG老师讲解的Machine Learning课程,主要介绍大规模机器学习以及其应用.包括随机梯度下降法.维批量梯度下降法.梯度下降法的收敛.在线学习.map reduce以 ...

  5. mybatis批量update,返回行数为-1

    mybatis批量更新返回结果为-1,是由于mybatis的defaultExExecutorType引起的,    它有三个执行器:SIMPLE 就是普通的执行器:REUSE 执行器会重用预处理语句 ...

  6. ES6的let和const命令

    刚开始学习es6,心里有点方,因为看了前言,感觉要用什么bebal来翻译成es5的代码,才能在各个平台上兼容运行,还有node各种运行环境. 不过自己也去百度了一些,发现还是有一丢丢的困难. 言归正传 ...

  7. NOI 二分算法练习

    1.NOI 二分法求函数的零点 总时间限制:  1000ms 内存限制:  65536kB 描述 有函数: f(x) = x5 - 15 * x4+ 85 * x3- 225 * x2+ 274 * ...

  8. Ubuntu中APache+mod_pyhon

    安装apache 1.sudo apt-get install Apache2 Apxs(Apache extension tool既apache扩展模块的工具)的安装: 1.sudo apt-get ...

  9. 关于applicationContext.xml cannot be opened because it does not exist的解决

    初学Spring在用Resource rs=new ClassPathResource("applicationContext.xml");时老是遇到这个错误.后来发现用 Appl ...

  10. ORACLE查询表最近更改数据的方法

    修改项目时,涉及到了Oracle中许多表的修改(包括:增加.删除字段,修改注释等).由于开始没有进行记录,造成在上测试机时,忘记了具体修改过哪些表了.后来在网上查找了一些资料,例如: 1.select ...