AngularJS深入(5)——provider
太精彩,不得不全文引用。
到这个层次,可能才敢说自己懂了吧。。。
http://syaning.com/2015/07/21/dive-into-angular-5/
在使用AngularJS的时候,可能需要创建各种各样的服务,这个时候,需要告诉AngularJS如何创建这些服务,这便是Provider。在实际使用的时候,会有provider
,factory
,service
,value
,constant
,事实上,它们都是Provider,从factory
到constant
,只不过是对provider
的一步步封装。相关源码都在函数createInjector
中。
1. 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_;
}
首先,一个Provider的名字不可以是hasOwnProperty
,然后,如果provider_
是函数或者数组的话,调用providerInjector.instantiate
将其实例化。之后进行判断,如果provider_
没有$get
属性,则报错。然后将provider_
缓存在providerCache
中。
下面来分析对参数provider_
的约束。
(1) 如果provider_
是函数,那么会调用provider_ = providerInjector.instantiate(provider_)
对其再次赋值,相当于以provider_
作为构造函数来创建实例。因此在构造函数中,必须要有对$get
的操作,因此如下形式都是可以的:
app.provider('greeting', function() {
this.$get = function() {
return {
sayHello: function() {
console.log('hello world');
}
};
};
});
// or
app.provider('greeting', function() {
return {
$get: function() {
return {
sayHello: function() {
console.log('hello world');
}
};
}
};
});
(2) 如果provider_
是数组,同样的接下来会调用provider_ = providerInjector.instantiate(provider_)
,所以数组必须是[deps, fn]
的形式,其中deps
为依赖,fn
与(1)中函数的限制相同。例如:
app.provider('greeting', ['$injector', function($injector) {
this.$get = function() {
return {
sayHello: function() {
console.log('hello world', $injector);
}
};
};
}]);
(3)如果provider_
是一个对象字面量,则它必须要有$get
属性,例如:
app.provider('greeting', {
$get: function() {
return {
sayHello: function() {
console.log('hello world');
}
};
}
});
接下来讨论对$get
的约束。在定义instanceInjector
的时候,代码如下:
instanceInjector = (instanceCache.$injector =
createInternalInjector(instanceCache, function(serviceName, caller) {
var provider = providerInjector.get(serviceName + providerSuffix, caller);
return instanceInjector.invoke(provider.$get, provider, undefined, serviceName);
}));
由于会被invoke
调用,因此$get
的值必须为一个函数或者[deps, fn]
形式的数组。而函数的返回值,则可以为原始数据类型、对象、函数等等,并无限制。
总之,在调用provider(name, provider_)
的时候,会将(name + 'Provider', {$get:function(){/* ... */}})
键值对缓存在providerCache
中,在注入的时候,则会调用$get
函数,将其返回值进行注入,并缓存在instanceCache
中。
2. factory
源码如下:
function factory(name, factoryFn, enforce) {
return provider(name, {
$get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
});
}
可以看到,factory
事实上是调用了provider
方法,而第二个参数factoryFn
,事实上也就是上面的$get
的值,因此应当符合对$get
的约束。
因此上面的例子可以这样写:
app.factory('greeting', function() {
return {
sayHello: function() {
console.log('hello world');
}
};
});
注意到factory
的源码中函数有第三个参数enforce
,也就是说是否强制返回值,如果函数没有返回值(包括显式返回值为undefined
)且第三个参数不为false
的时候,就回报错,相关源码如下:
function enforceReturnValue(name, factory) {
return function enforcedReturnValue() {
var result = instanceInjector.invoke(factory, this);
if (isUndefined(result)) {
throw $injectorMinErr('undef', "Provider '{0}' must return a value from $get factory method.", name);
}
return result;
};
}
然而在使用的过程中,会发现,即使显示声明了enforce
为false
,还是会报错,例如:
app.factory('greeting', function() {
console.log('hello world');
}, false);
// Error: [$injector:undef] Provider 'greeting' must return a value from $get factory method.
这主要是由supportObject
引起的,因为providerCache.$provide.factory
的值实际上是supportObject(factory)
,而supportObject
源码如下:
function supportObject(delegate) {
return function(key, value) {
if (isObject(key)) {
forEach(key, reverseParams(delegate));
} else {
return delegate(key, value);
}
};
}
可以发现,经过supportObject
处理后,函数的有效参数值最多只有两个了,因此第三个参数false
也就被忽略掉了。
3. service
源码如下:
function service(name, constructor) {
return factory(name, ['$injector', function($injector) {
return $injector.instantiate(constructor);
}]);
}
可以看到,实质上是调用了factory
,第二个参数constructor
是作为构造函数,最终返回的是该构造函数表示的类的一个实例。由于会调用$injector.instantiate(constructor)
,因此constructor
必须是一个函数或者[deps, fn]
的形式。
因此上面的例子可以写成:
app.service('greeting', function() {
this.sayHello = function() {
console.log('hello world');
};
});
// or
app.service('greeting', function() {
return {
sayHello: function() {
console.log('hello world');
}
};
});
4. value
源码如下:
function value(name, val) {
return factory(name, valueFn(val), false);
}
可以看到,也是调用了factory
。其中valueFn
的作用是将一个值包装为函数,源码如下:
function valueFn(value) {
return function() {
return value;
};
}
5. constant
源码如下:
function constant(name, value) {
assertNotHasOwnProperty(name, 'constant');
providerCache[name] = value;
instanceCache[name] = value;
}
可以看到,该方法只是对providerCache
和instanceCache
进行了属性设定。
6. value & constant
(1)value
设定的值是可以改变的,而constant
设定的值是不可变的,例如:
app.value('greeting', 'hello value')
.value('greeting', 'hello world ')
.controller('ctrl', ['greeting', function(greeting) {
console.log(greeting); // hello world
}]);
////////////////////
app.constant('greeting', 'hello constant')
.constant('greeting', 'hello world ')
.controller('ctrl', ['greeting', function(greeting) {
console.log(greeting); // hello constant
}]);
实现机制在函数setUpModuleLoader
中。部分相关源码如下:
var moduleInstance = {
_invokeQueue: invokeQueue,
value: invokeLater('$provide', 'value'),
constant: invokeLater('$provide', 'constant', 'unshift'),
// ... ...
};
function invokeLater(provider, method, insertMethod, queue) {
if (!queue) queue = invokeQueue;
return function() {
queue[insertMethod || 'push']([provider, method, arguments]);
return moduleInstance;
};
}
在调用value
的时候,用的是数组的push
方法;而调用constant
的时候,用的是数组的unshift
方法。在加载模块的时候,会依次执行_invokeQueue
中的内容。对于通过value
添加的值,会按照声明的顺序进行设定,因此后面的值会覆盖掉前面的值;而对于通过constant
添加的值,会按照声明的逆序进行设定,因此最后得到的值为第一次设定的值,从而就实现了常量的效果。
(2)在config
中,value
不可以被注入,而constant
可以被注入,例如:
app.constant('greeting', 'hello world')
.config(function(greeting) {
console.log(greeting); // hello world
});
////////////////////
app.value('greeting', 'hello world')
.config(function(greeting) {
console.log(greeting); // Error: [$injector:unpr] Unknown provider: greeting
});
这是因为在使用value
的时候,实际上是将greetingProvider
换存在了providerCache
中;而使用constant
的时候,则是将greeting
换存在了providerCache
中。然后在执行模块的_configBlocks
的时候,会在providerCache
中查找注入的依赖greeting
,因此对于value
来说,自然是找不到了。
7. provider, factory & service
这三者的关系非常密切,看如下例子:
function Greeting() {
this.sayHello = function() {
console.log('hello world');
};
}
app.provider('greetingFromProvider', function() {
this.$get = function() {
return new Greeting();
}
})
.factory('greetingFromFactory', function() {
return new Greeting();
})
.service('greetingFromService', Greeting)
.controller('ctrl', ['greetingFromProvider', 'greetingFromFactory', 'greetingFromService',
function(gp, gf, gs) {
gp.sayHello();
gf.sayHello();
gs.sayHello();
}
]);
可以简单理解为service
接收的参数是构造函数,factory
接收的参数是工厂函数,而该工厂函数是作为provider
中的$get
属性而存在的。但是provider
比factory
更加灵活可配置,因此可以理解为可配置的工厂函数。例如:
app.provider('greeting', function() {
var name = 'world';
this.$get = function() {
return {
sayHello: function() {
console.log('hello ' + name);
}
};
};
this.setName = function(newName) {
name = newName;
};
})
.config(function(greetingProvider) {
greetingProvider.setName('alex');
})
.controller('ctrl', function(greeting) {
greeting.sayHello(); // hello alex
});
AngularJS深入(5)——provider的更多相关文章
- AngularJS中的Provider们:Service和Factory等的区别
引言 看了很多文章可能还是不太说得出AngularJS中的几个创建供应商(provider)的方法(factory(),service(),provider())到底有啥区别,啥时候该用啥,之前一直傻 ...
- angularjs factory,service,provider 自定义服务的不同
angularjs框架学了有一段时间了,感觉很好用.可以把angularjs的app理解成php的class,controller是控制器,而内置服务和自定义服务就可以理解成models了.angul ...
- angularJS——自定义服务provider之$get
可以认为provider有三个部分: 第一部分是私有变量和私有函数,这些变量和函数会在以后被修改. 第二部分是在app.config函数里可以访问的变量和函数,所以,他们可以在其他地方使用之前被修改. ...
- [AngularJS + cryptoJS + Gravatar] Provider vs factory
Configurable Bits Need a Provider We want to be able to configure the characterLength before Tweetab ...
- AngularJS Factory Service Provider
先看看http://www.cnblogs.com/mbydzyr/p/3460501.html http://www.oschina.net/translate/angularjs-factory- ...
- AngularJS 中 Provider 的用法及区别
在一个分层良好的 Angular 应用中,Controller 这一层应该很薄.也就是说,应用里大部分的业务逻辑和持久化数据都应该放在 Service 里. 为此,理解 AngularJS 中的几个 ...
- AngularJS学习之依赖注入
1.什么是依赖注入:简称DI,是一种软件设计模式,在这种模式下,一个或更多的依赖(或服务)被注入(或者通过引用传递)到一个独立的对象(或客户端)中,然后成为了该客户端状态的一部分. 该模式分离了客户端 ...
- AngularJS进阶学习
参考:http://***/class/54f3ba65e564e50cfccbad4b 1. AJAX:Asynchronous JavaScript and XML(异步的 JavaScript ...
- AngularJS(15)-依赖注入
AngularJS 依赖注入 什么是依赖注入 wiki 上的解释是:依赖注入(Dependency Injection,简称DI)是一种软件设计模式,在这种模式下,一个或更多的依赖(或服务)被注入(或 ...
随机推荐
- mysql引擎区别
MySQL数据库引擎取决于MySQL在安装的时候是如何被编译的.要添加一个新的引擎,就必须重新编译MYSQL.在缺省情况下,MYSQL支持三个引擎:ISAM.MYISAM和HEAP.另外两种类型INN ...
- CSS3 动画animation
关键帧 什么是关键帧.一如上面对Flash原理的描述一样,我们知道动画其实由许多静态画面组成,第一个这样的静态画面可以表述为一帧.其中关键帧是在动画过程中体现了物理明显变化的那些帧. 比如之前的例子中 ...
- XSS 自动化检测 Fiddler Watcher & x5s & ccXSScan 初识
一.标题:XSS 自动化检测 Fiddler Watcher & x5s & ccXSScan 初识 automated XSS testing assistant 二.引言 ...
- python购物&常用字符处理方法
python 一个购物车的例子 1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 '''购物车''' 4 5 goods = [ 6 7 {&quo ...
- IOS开发/iphone开发多线程
有时候可能有很多功能要同时实现,例如每隔多长时间就会检测程序网络连接,又或者有时候需要从服务器下载一个不小的文件,如果用单线程几乎是不可想的事情,程序将会卡的无法使用,用到多线程和不用多线程,给用户的 ...
- shell 删除文件下的* (copy).jpg备份文件
shell编程中, 在for, while循环中为什么不用(), {} 不是没有; 而是因为(), {}做了其他用途: (): 执行命令组, 注意这个命令组是新开一个子shell中执行, 因此,括号 ...
- 1009: josephus问题
1009: josephus问题 Time Limit: 1 Sec Memory Limit: 64 MBSubmit: 549 Solved: 227 Description josephus ...
- cocos2dx的内存管理机制
首先我们必须说一下c++中变量的内存空间的分配问题,我们在c++中写一个类,可以在栈上分配内存空间也可以使用new在堆上分配内存空间,如果类对象是在栈上分配的内存空间,这个内存空间的管理就不是我们的事 ...
- 淘宝(阿里百川)手机客户端开发日记第六篇 Service详解(二)
DEMO1:当我们点击启动服务时和点击停止服务的时候,观察服务的运行状态,布局由于简单,只是两个普通的Button按钮,在此我只上截图. java代码部分 第一步:我们需要实现一个服务类,继承自ser ...
- poj3259 bellman——ford Wormholes解绝负权问题
Wormholes Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 35103 Accepted: 12805 Descr ...