太精彩,不得不全文引用。

到这个层次,可能才敢说自己懂了吧。。。

http://syaning.com/2015/07/21/dive-into-angular-5/

在使用AngularJS的时候,可能需要创建各种各样的服务,这个时候,需要告诉AngularJS如何创建这些服务,这便是Provider。在实际使用的时候,会有providerfactoryservicevalueconstant,事实上,它们都是Provider,从factoryconstant,只不过是对provider的一步步封装。相关源码都在函数createInjector中。


1. provider

源码如下:

  1. function provider(name, provider_) {
  2. assertNotHasOwnProperty(name, 'service');
  3. if (isFunction(provider_) || isArray(provider_)) {
  4. provider_ = providerInjector.instantiate(provider_);
  5. }
  6. if (!provider_.$get) {
  7. throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name);
  8. }
  9. return providerCache[name + providerSuffix] = provider_;
  10. }

首先,一个Provider的名字不可以是hasOwnProperty,然后,如果provider_是函数或者数组的话,调用providerInjector.instantiate将其实例化。之后进行判断,如果provider_没有$get属性,则报错。然后将provider_缓存在providerCache中。

下面来分析对参数provider_的约束。

(1) 如果provider_是函数,那么会调用provider_ = providerInjector.instantiate(provider_)对其再次赋值,相当于以provider_作为构造函数来创建实例。因此在构造函数中,必须要有对$get的操作,因此如下形式都是可以的:

  1. app.provider('greeting', function() {
  2. this.$get = function() {
  3. return {
  4. sayHello: function() {
  5. console.log('hello world');
  6. }
  7. };
  8. };
  9. });
  10. // or
  11. app.provider('greeting', function() {
  12. return {
  13. $get: function() {
  14. return {
  15. sayHello: function() {
  16. console.log('hello world');
  17. }
  18. };
  19. }
  20. };
  21. });

(2) 如果provider_是数组,同样的接下来会调用provider_ = providerInjector.instantiate(provider_),所以数组必须是[deps, fn]的形式,其中deps为依赖,fn与(1)中函数的限制相同。例如:

  1. app.provider('greeting', ['$injector', function($injector) {
  2. this.$get = function() {
  3. return {
  4. sayHello: function() {
  5. console.log('hello world', $injector);
  6. }
  7. };
  8. };
  9. }]);

(3)如果provider_是一个对象字面量,则它必须要有$get属性,例如:

  1. app.provider('greeting', {
  2. $get: function() {
  3. return {
  4. sayHello: function() {
  5. console.log('hello world');
  6. }
  7. };
  8. }
  9. });

接下来讨论对$get的约束。在定义instanceInjector的时候,代码如下:

  1. instanceInjector = (instanceCache.$injector =
  2. createInternalInjector(instanceCache, function(serviceName, caller) {
  3. var provider = providerInjector.get(serviceName + providerSuffix, caller);
  4. return instanceInjector.invoke(provider.$get, provider, undefined, serviceName);
  5. }));

由于会被invoke调用,因此$get的值必须为一个函数或者[deps, fn]形式的数组。而函数的返回值,则可以为原始数据类型、对象、函数等等,并无限制。

总之,在调用provider(name, provider_)的时候,会将(name + 'Provider', {$get:function(){/* ... */}})键值对缓存在providerCache中,在注入的时候,则会调用$get函数,将其返回值进行注入,并缓存在instanceCache中。

2. factory

源码如下:

  1. function factory(name, factoryFn, enforce) {
  2. return provider(name, {
  3. $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
  4. });
  5. }

可以看到,factory事实上是调用了provider方法,而第二个参数factoryFn,事实上也就是上面的$get的值,因此应当符合对$get的约束。

因此上面的例子可以这样写:

  1. app.factory('greeting', function() {
  2. return {
  3. sayHello: function() {
  4. console.log('hello world');
  5. }
  6. };
  7. });

注意到factory的源码中函数有第三个参数enforce,也就是说是否强制返回值,如果函数没有返回值(包括显式返回值为undefined)且第三个参数不为false的时候,就回报错,相关源码如下:

  1. function enforceReturnValue(name, factory) {
  2. return function enforcedReturnValue() {
  3. var result = instanceInjector.invoke(factory, this);
  4. if (isUndefined(result)) {
  5. throw $injectorMinErr('undef', "Provider '{0}' must return a value from $get factory method.", name);
  6. }
  7. return result;
  8. };
  9. }

然而在使用的过程中,会发现,即使显示声明了enforcefalse,还是会报错,例如:

  1. app.factory('greeting', function() {
  2. console.log('hello world');
  3. }, false);
  4. // Error: [$injector:undef] Provider 'greeting' must return a value from $get factory method.

这主要是由supportObject引起的,因为providerCache.$provide.factory的值实际上是supportObject(factory),而supportObject源码如下:

  1. function supportObject(delegate) {
  2. return function(key, value) {
  3. if (isObject(key)) {
  4. forEach(key, reverseParams(delegate));
  5. } else {
  6. return delegate(key, value);
  7. }
  8. };
  9. }

可以发现,经过supportObject处理后,函数的有效参数值最多只有两个了,因此第三个参数false也就被忽略掉了。

3. service

源码如下:

  1. function service(name, constructor) {
  2. return factory(name, ['$injector', function($injector) {
  3. return $injector.instantiate(constructor);
  4. }]);
  5. }

可以看到,实质上是调用了factory,第二个参数constructor是作为构造函数,最终返回的是该构造函数表示的类的一个实例。由于会调用$injector.instantiate(constructor),因此constructor必须是一个函数或者[deps, fn]的形式。

因此上面的例子可以写成:

  1. app.service('greeting', function() {
  2. this.sayHello = function() {
  3. console.log('hello world');
  4. };
  5. });
  6. // or
  7. app.service('greeting', function() {
  8. return {
  9. sayHello: function() {
  10. console.log('hello world');
  11. }
  12. };
  13. });

4. value

源码如下:

  1. function value(name, val) {
  2. return factory(name, valueFn(val), false);
  3. }

可以看到,也是调用了factory。其中valueFn的作用是将一个值包装为函数,源码如下:

  1. function valueFn(value) {
  2. return function() {
  3. return value;
  4. };
  5. }

5. constant

源码如下:

  1. function constant(name, value) {
  2. assertNotHasOwnProperty(name, 'constant');
  3. providerCache[name] = value;
  4. instanceCache[name] = value;
  5. }

可以看到,该方法只是对providerCacheinstanceCache进行了属性设定。

6. value & constant

(1)value设定的值是可以改变的,而constant设定的值是不可变的,例如:

  1. app.value('greeting', 'hello value')
  2. .value('greeting', 'hello world ')
  3. .controller('ctrl', ['greeting', function(greeting) {
  4. console.log(greeting); // hello world
  5. }]);
  6. ////////////////////
  7. app.constant('greeting', 'hello constant')
  8. .constant('greeting', 'hello world ')
  9. .controller('ctrl', ['greeting', function(greeting) {
  10. console.log(greeting); // hello constant
  11. }]);

实现机制在函数setUpModuleLoader中。部分相关源码如下:

  1. var moduleInstance = {
  2. _invokeQueue: invokeQueue,
  3. value: invokeLater('$provide', 'value'),
  4. constant: invokeLater('$provide', 'constant', 'unshift'),
  5. // ... ...
  6. };
  7. function invokeLater(provider, method, insertMethod, queue) {
  8. if (!queue) queue = invokeQueue;
  9. return function() {
  10. queue[insertMethod || 'push']([provider, method, arguments]);
  11. return moduleInstance;
  12. };
  13. }

在调用value的时候,用的是数组的push方法;而调用constant的时候,用的是数组的unshift方法。在加载模块的时候,会依次执行_invokeQueue中的内容。对于通过value添加的值,会按照声明的顺序进行设定,因此后面的值会覆盖掉前面的值;而对于通过constant添加的值,会按照声明的逆序进行设定,因此最后得到的值为第一次设定的值,从而就实现了常量的效果。

(2)config中,value不可以被注入,而constant可以被注入,例如:

  1. app.constant('greeting', 'hello world')
  2. .config(function(greeting) {
  3. console.log(greeting); // hello world
  4. });
  5. ////////////////////
  6. app.value('greeting', 'hello world')
  7. .config(function(greeting) {
  8. console.log(greeting); // Error: [$injector:unpr] Unknown provider: greeting
  9. });

这是因为在使用value的时候,实际上是将greetingProvider换存在了providerCache中;而使用constant的时候,则是将greeting换存在了providerCache中。然后在执行模块的_configBlocks的时候,会在providerCache中查找注入的依赖greeting,因此对于value来说,自然是找不到了。

7. provider, factory & service

这三者的关系非常密切,看如下例子:

  1. function Greeting() {
  2. this.sayHello = function() {
  3. console.log('hello world');
  4. };
  5. }
  6. app.provider('greetingFromProvider', function() {
  7. this.$get = function() {
  8. return new Greeting();
  9. }
  10. })
  11. .factory('greetingFromFactory', function() {
  12. return new Greeting();
  13. })
  14. .service('greetingFromService', Greeting)
  15. .controller('ctrl', ['greetingFromProvider', 'greetingFromFactory', 'greetingFromService',
  16. function(gp, gf, gs) {
  17. gp.sayHello();
  18. gf.sayHello();
  19. gs.sayHello();
  20. }
  21. ]);

可以简单理解为service接收的参数是构造函数,factory接收的参数是工厂函数,而该工厂函数是作为provider中的$get属性而存在的。但是providerfactory更加灵活可配置,因此可以理解为可配置的工厂函数。例如:

  1. app.provider('greeting', function() {
  2. var name = 'world';
  3. this.$get = function() {
  4. return {
  5. sayHello: function() {
  6. console.log('hello ' + name);
  7. }
  8. };
  9. };
  10. this.setName = function(newName) {
  11. name = newName;
  12. };
  13. })
  14. .config(function(greetingProvider) {
  15. greetingProvider.setName('alex');
  16. })
  17. .controller('ctrl', function(greeting) {
  18. greeting.sayHello(); // hello alex
  19. });

AngularJS深入(5)——provider的更多相关文章

  1. AngularJS中的Provider们:Service和Factory等的区别

    引言 看了很多文章可能还是不太说得出AngularJS中的几个创建供应商(provider)的方法(factory(),service(),provider())到底有啥区别,啥时候该用啥,之前一直傻 ...

  2. angularjs factory,service,provider 自定义服务的不同

    angularjs框架学了有一段时间了,感觉很好用.可以把angularjs的app理解成php的class,controller是控制器,而内置服务和自定义服务就可以理解成models了.angul ...

  3. angularJS——自定义服务provider之$get

    可以认为provider有三个部分: 第一部分是私有变量和私有函数,这些变量和函数会在以后被修改. 第二部分是在app.config函数里可以访问的变量和函数,所以,他们可以在其他地方使用之前被修改. ...

  4. [AngularJS + cryptoJS + Gravatar] Provider vs factory

    Configurable Bits Need a Provider We want to be able to configure the characterLength before Tweetab ...

  5. AngularJS Factory Service Provider

    先看看http://www.cnblogs.com/mbydzyr/p/3460501.html http://www.oschina.net/translate/angularjs-factory- ...

  6. AngularJS 中 Provider 的用法及区别

    在一个分层良好的 Angular 应用中,Controller 这一层应该很薄.也就是说,应用里大部分的业务逻辑和持久化数据都应该放在 Service 里. 为此,理解 AngularJS 中的几个 ...

  7. AngularJS学习之依赖注入

    1.什么是依赖注入:简称DI,是一种软件设计模式,在这种模式下,一个或更多的依赖(或服务)被注入(或者通过引用传递)到一个独立的对象(或客户端)中,然后成为了该客户端状态的一部分. 该模式分离了客户端 ...

  8. AngularJS进阶学习

    参考:http://***/class/54f3ba65e564e50cfccbad4b 1. AJAX:Asynchronous JavaScript and XML(异步的 JavaScript ...

  9. AngularJS(15)-依赖注入

    AngularJS 依赖注入 什么是依赖注入 wiki 上的解释是:依赖注入(Dependency Injection,简称DI)是一种软件设计模式,在这种模式下,一个或更多的依赖(或服务)被注入(或 ...

随机推荐

  1. Kali实现局域网ARP欺骗和ARP攻击

    原文地址: http://netsecurity.51cto.com/art/201303/386031.htm http://xiao106347.blog.163.com/blog/static/ ...

  2. 快速搭建ssh(最终版)

    一个月又忘了.还是做个笔记.自己看下就能想起来... 1.在MyEclipse中新建web Project 2.首先整合struts

  3. C# 检测操作系统是否空闲,实现系统空闲后做一些操作

    public class CheckComputerFreeState { /// <summary> /// 创建结构体用于返回捕获时间 /// </summary> [St ...

  4. JavaScript的Eval与JSON.parse的区别

    JavaScript的Eval与JSON.parse的区别 json的定义以及用法: JSON(JavaScript Object Notation)是一种轻量级的数据格式,采用完全独立于语言的文本格 ...

  5. 小白科普之JavaScript的数组

    一.与其他语言数据的比较    相同点:有序列表    不同点:js的数组的每一项可以保存任何类型的数据:数组的大小是可以动态调整的 二.数组创建的两种方法 1)  var colors = new ...

  6. SGU 180 Inversions(离散化 + 线段树求逆序对)

    题目链接:http://acm.sgu.ru/problem.php?contest=0&problem=180 解题报告:一个裸的求逆序对的题,离散化+线段树,也可以用离散化+树状数组.因为 ...

  7. js检测是否安装了flash插件

    function flashChecker() { var hasFlash = 0; //是否安装了flash var flashVersion = 0; //flash版本 var isIE = ...

  8. 百度图片爬虫-python版

               self.browser=imitate_browser.BrowserBase()            self.chance=0            self.chanc ...

  9. nginx(三)初步搭建nginx虚拟主机

    上面就是nginx基于域名.ip访问的配置,掌握住格式,就很好配置了. 一.基于域名的虚拟主机的配置:1.我们在此复习一下DNS的配置:[root@mgmserver /]# hostnamemgms ...

  10. RAID阵列的初始化与管理

    如果我们创建RAID阵列的目的是新部署一台服务器,我们建议所有新创建的RAID阵列都应该做初始化操作,这样,硬盘上原有的用户数据将被清除,以便进行后续的系统,软件安装. 转自: http://zh.c ...