与其只是在表格中填写“是”和“否”而不做任何解释,我将更详细地介绍一下。
[注意,完成后添加:这最终......比我预期的要长一些。底部有一个 tl;dr,但我希望这能提供信息。]
[这个答案也已添加到 AngularJS wiki 中:了解依赖注入]
提供者($provide)
The $provide
service 负责告诉 Angular 如何创建新的可注入事物;这些东西被称为services。服务是由称为提供者,这就是您使用时创建的内容$provide
。定义提供者是通过provider
方法上的$provide
服务,您可以获取$provide
通过要求将服务注入到应用程序的config
功能。一个例子可能是这样的:
app.config(function($provide) {
$provide.provider('greeting', function() {
this.$get = function() {
return function(name) {
alert("Hello, " + name);
};
};
});
});
这里我们为一个名为的服务定义了一个新的提供者greeting
;我们可以注入一个名为greeting
到任何可注入的函数中(比如控制器,稍后会详细介绍),Angular 将调用提供者的$get
函数以返回服务的新实例。在这种情况下,将被注入的是一个函数,它接受一个name
参数和alert
是一条基于名称的消息。我们可以这样使用它:
app.controller('MainController', function($scope, greeting) {
$scope.onClick = function() {
greeting('Ford Prefect');
};
});
现在这就是窍门。 factory
, service
, and value
都是定义提供者各个部分的快捷方式——也就是说,它们提供了一种定义提供者的方法,而无需键入所有内容。例如,你可以这样写完全相同的提供商像这样:
app.config(function($provide) {
$provide.factory('greeting', function() {
return function(name) {
alert("Hello, " + name);
};
});
});
理解这一点很重要,所以我会重新表述:在幕后,AngularJS 正在调用完全相同的代码我们上面写的($provide.provider
版本)for我们。从字面上看,这两个版本 100% 没有区别。value
工作方式是一样的——如果我们从我们的$get
函数(又名我们的factory
函数)总是完全相同,我们可以使用以下代码编写更少的代码value
。例如,因为我们总是返回相同的函数greeting
服务,我们可以使用value
也定义它:
app.config(function($provide) {
$provide.value('greeting', function(name) {
alert("Hello, " + name);
});
});
同样,这与我们用来定义此函数的其他两种方法 100% 相同——这只是节省一些输入的一种方法。
现在你可能注意到这很烦人app.config(function($provide) { ... })
我一直在用的东西。自从定义新的提供商(通过any上面给定的方法)是如此常见,AngularJS 公开了$provider
直接在模块对象上使用方法,以节省更多的输入:
var myMod = angular.module('myModule', []);
myMod.provider("greeting", ...);
myMod.factory("greeting", ...);
myMod.value("greeting", ...);
这些都与更详细的做同样的事情app.config(...)
我们之前使用的版本。
到目前为止我跳过的一种注射剂是constant
。现在,很容易说它的工作原理就像value
。稍后我们会看到其中有一个区别。
回顾, all这些代码正在执行exact一样:
myMod.provider('greeting', function() {
this.$get = function() {
return function(name) {
alert("Hello, " + name);
};
};
});
myMod.factory('greeting', function() {
return function(name) {
alert("Hello, " + name);
};
});
myMod.value('greeting', function(name) {
alert("Hello, " + name);
});
注射器($injector)
注入器负责使用我们提供的代码实际创建我们的服务实例$provide
(没有双关语的意思)。每当您编写一个接受注入参数的函数时,您都会看到注入器在工作。每个 AngularJS 应用程序都有一个$injector
当应用程序首次启动时创建;你可以通过注射来获得它$injector
进入任何可注入函数(是的,$injector
知道如何自我注入!)
一旦你有$injector
,您可以通过调用来获取已定义服务的实例get
上面有服务的名称。例如,
var greeting = $injector.get('greeting');
greeting('Ford Prefect');
注入器还负责将服务注入到函数中;例如,您可以使用注入器神奇地将服务注入到您拥有的任何函数中invoke
method;
var myFunction = function(greeting) {
greeting('Ford Prefect');
};
$injector.invoke(myFunction);
值得注意的是,注入器只会创建服务的实例once。然后它会缓存提供者通过服务名称返回的任何内容;下次您请求服务时,您实际上会得到完全相同的对象。
因此,为了回答你的问题,你可以将服务注入任何被调用的函数$injector.invoke
。这包括
- 控制器定义函数
- 指令定义函数
- 过滤器定义函数
- the
$get
提供者的方法(又名factory
定义函数)
Since constant
s and value
s 总是返回一个静态值,它们不是通过注入器调用的,因此你不能向它们注入任何东西。
配置提供者
您可能想知道为什么有人会费心去建立一个成熟的提供商provide
方法如果factory
, value
等等就容易多了。答案是提供商允许大量配置。我们已经提到,当您通过提供程序(或 Angular 为您提供的任何快捷方式)创建服务时,您将创建一个新的提供程序来定义该服务的构建方式。我什么didn't值得一提的是,这些提供者可以被注入config
您的应用程序的各个部分,以便您可以与它们交互!
首先,Angular 分两个阶段运行您的应用程序——config
and run
阶段。这config
正如我们所看到的,您可以根据需要设置任何提供程序。这也是设置指令、控制器、过滤器等的地方。这run
正如您可能猜到的那样,Angular 实际编译 DOM 并启动应用程序的阶段。
您可以使用以下命令添加要在这些阶段运行的其他代码myMod.config
and myMod.run
函数——每个函数都在该特定阶段运行一个函数。正如我们在第一节中看到的,这些函数是可注入的——我们注入了内置函数$provide
我们第一个代码示例中的服务。不过,值得注意的是在此期间config
阶段,只能注入提供者(以下服务除外)AUTO
模块 -$provide
and $injector
).
例如,以下是不允许:
myMod.config(function(greeting) {
// WON'T WORK -- greeting is an *instance* of a service.
// Only providers for services can be injected in config blocks.
});
你什么do可以访问任何提供者对于您所做的服务:
myMod.config(function(greetingProvider) {
// a-ok!
});
有一个重要的例外:constant
由于无法更改,因此允许注入到内部config
块(这就是它们与value
s)。仅通过名称即可访问它们(无Provider
必要的后缀)。
每当您为服务定义提供者时,该提供者都会被命名serviceProvider
, where service
是服务的名称。现在我们可以利用提供商的力量来做一些更复杂的事情!
myMod.provider('greeting', function() {
var text = 'Hello, ';
this.setText = function(value) {
text = value;
};
this.$get = function() {
return function(name) {
alert(text + name);
};
};
});
myMod.config(function(greetingProvider) {
greetingProvider.setText("Howdy there, ");
});
myMod.run(function(greeting) {
greeting('Ford Prefect');
});
现在我们的提供者有一个函数叫做setText
我们可以用它来定制我们的alert
;我们可以通过以下方式访问该提供商config
阻止调用此方法并自定义服务。当我们最终运行我们的应用程序时,我们可以获取greeting
服务,试一试,我们的定制生效了。
由于这是一个更复杂的示例,因此这里有一个工作演示:http://jsfiddle.net/BinaryMuse/9GjYg/
控制器($controller)
控制器函数可以被注入,但控制器本身不能被注入到其他东西中。这是因为控制器不是通过提供者创建的。相反,有一个内置的 Angular 服务,称为$controller
负责设置您的控制器。你打电话时myMod.controller(...)
,你实际上正在访问该服务的提供者,就像上一节一样。
例如,当您定义这样的控制器时:
myMod.controller('MainController', function($scope) {
// ...
});
你实际上在做的是这样的:
myMod.config(function($controllerProvider) {
$controllerProvider.register('MainController', function($scope) {
// ...
});
});
稍后,当 Angular 需要创建控制器的实例时,它会使用$controller
服务(反过来又使用$injector
调用您的控制器函数,以便它也注入其依赖项)。
过滤器和指令
filter
and directive
工作方式与controller
; filter
使用名为的服务$filter
及其提供者$filterProvider
, while directive
使用名为的服务$compile
及其提供者$compileProvider
。一些链接:
- $过滤器:https://docs.angularjs.org/api/ng/service/$filter
- $filterProvider:https://docs.angularjs.org/api/ng/provider/$filterProvider
- $编译:https://docs.angularjs.org/api/ng/service/$compile
- $compileProvider:https://docs.angularjs.org/api/ng/provider/$compileProvider
根据其他示例,myMod.filter
and myMod.directive
是配置这些服务的快捷方式。
因此,总而言之,任何调用的函数$injector.invoke
可以注入。从您的图表来看,这包括(但不限于):
- 控制器
- 指示
- factory
- filter
- 提供者
$get
(将提供者定义为对象时)
- 提供程序函数(将提供程序定义为构造函数时)
- service
提供商创建新服务可以注入到东西中。这包括:
- constant
- factory
- provider
- service
- value
也就是说,内置服务如$controller
and $filter
can被注射,你可以use这些服务来获取您使用这些方法定义的新过滤器和控制器(即使您定义的事物本身无法注入事物中)。
除此之外,任何注入器调用的函数都可以注入任何提供者提供的服务——没有任何限制(除了config
and run
此处列出的差异)。