Service
、Provider
和Factory
有什么区别?Service
、Provider
和Factory
有什么区别?从 AngularJS 邮件列表中我得到了一个惊人的帖子,其中解释了服务、工厂和提供者以及它们的注入用法。总结如下:
语法:module.service('serviceName', function);
结果:当将 serviceName 声明为可注入参数时,您将获得该函数的实例(换句话说,new FunctionYouPassedToService()
)。
语法:module.factory('factoryName', function);
结果:当将 factoryName 声明为可注入参数时,您将获得通过调用传递给 module.factory 的函数引用所返回的值。
语法:module.provider('providerName', function);
结果:当将 providerName 声明为可注入参数时,您将获得(new ProviderFunction()).$get()
。在调用 $get 方法之前实例化构造函数 - ProviderFunction
是传递给 module.provider 的函数引用。
提供者的优点是它们可以在模块配置阶段进行配置。
在这里查看提供的代码。
以下是 Misko 的更好解释:
provide.value('a', 123);
function Controller(a) {
expect(a).toEqual(123);
}
在这种情况下,注入器只是简单地返回值本身。但是如果你想计算该值呢?那么就要使用工厂
provide.factory('b', function(a) {
return a*2;
});
function Controller(b) {
expect(b).toEqual(246);
}
因此,factory
是一个负责创建值的函数。请注意,工厂函数可以请求其他依赖项。
但是,如果您想更加面向对象,并拥有一个名为Greeter的类呢?
function Greeter(a) {
this.greet = function() {
return 'Hello ' + a;
}
}
那么要实例化,你需要编写
provide.factory('greeter', function(a) {
return new Greeter(a);
});
那么我们可以像这样在控制器中请求“greeter”:
function Controller(greeter) {
expect(greeter instanceof Greeter).toBe(true);
expect(greeter.greet()).toEqual('Hello 123');
}
但是这样太啰嗦了。一个更简洁的写法是provider.service('greeter', Greeter);
但是如果我们想在注入之前配置Greeter
类呢?那么我们可以这样写:
provide.provider('greeter2', function() {
var salutation = 'Hello';
this.setSalutation = function(s) {
salutation = s;
}
function Greeter(a) {
this.greet = function() {
return salutation + ' ' + a;
}
}
this.$get = function(a) {
return new Greeter(a);
};
});
然后我们可以这样做:
angular.module('abc', []).config(function(greeter2Provider) {
greeter2Provider.setSalutation('Halo');
});
function Controller(greeter2) {
expect(greeter2.greet()).toEqual('Halo 123');
}
顺带一提,service
、factory
和value
都是从provider
派生而来。
provider.service = function(name, Class) {
provider.provide(name, function() {
this.$get = function($injector) {
return $injector.instantiate(Class);
};
});
}
provider.factory = function(name, factory) {
provider.provide(name, function() {
this.$get = function($injector) {
return $injector.invoke(factory);
};
});
}
provider.value = function(name, value) {
provider.factory(name, function() {
return value;
});
};
toEqual
和greeter.Greet
的意义时感到困惑。为什么不使用更真实、更相关的内容? - Kyle Pennellfactory
/service
/provider
的“Hello world”示例:var myApp = angular.module('myApp', []);
//service style, probably the simplest one
myApp.service('helloWorldFromService', function() {
this.sayHello = function() {
return "Hello, World!";
};
});
//factory style, more involved but more sophisticated
myApp.factory('helloWorldFromFactory', function() {
return {
sayHello: function() {
return "Hello, World!";
}
};
});
//provider style, full blown, configurable version
myApp.provider('helloWorld', function() {
this.name = 'Default';
this.$get = function() {
var name = this.name;
return {
sayHello: function() {
return "Hello, " + name + "!";
}
}
};
this.setName = function(name) {
this.name = name;
};
});
//hey, we can configure a provider!
myApp.config(function(helloWorldProvider){
helloWorldProvider.setName('World');
});
function MyCtrl($scope, helloWorld, helloWorldFromFactory, helloWorldFromService) {
$scope.hellos = [
helloWorld.sayHello(),
helloWorldFromFactory.sayHello(),
helloWorldFromService.sayHello()];
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="myApp">
<div ng-controller="MyCtrl">
{{hellos}}
</div>
</body>
$get
函数中会改变上下文吗?- 在该函数中,您不再引用已实例化的提供程序。 - Nate-Wilkinsthis
不会改变上下文,因为调用的是new Provider()
.$get(),其中Provider
是传递给app.provider
的函数。也就是说,$get()
作为构建的Provider
对象的方法被调用,所以this
将指向Provider
,正如示例所示。 - Brandon简而言之:
1) 当你使用一个工厂时,你创建一个对象,向其中添加属性,然后返回该对象。当你将这个工厂传递给你的控制器时,该对象上的属性现在可以通过你的工厂在该控制器中使用。
app.controller(‘myFactoryCtrl’, function($scope, myFactory){
$scope.artist = myFactory.getArtist();
});
app.factory(‘myFactory’, function(){
var _artist = ‘Shakira’;
var service = {};
service.getArtist = function(){
return _artist;
}
return service;
});
2) 使用 Service 时,AngularJS 在幕后使用“new”关键字实例化它。因此,您将向“this”添加属性,服务将返回“this”。当您将服务传递到控制器中时,通过您的服务,“this”上的那些属性现在将在该控制器上可用。
app.controller(‘myServiceCtrl’, function($scope, myService){
$scope.artist = myService.getArtist();
});
app.service(‘myService’, function(){
var _artist = ‘Nelly’;
this.getArtist = function(){
return _artist;
}
});
3) 提供者(Providers)是您可以传递到.config()函数中的唯一服务。当您希望在将服务对象提供给用户之前为整个模块提供配置时,请使用提供者。
app.controller(‘myProvider’, function($scope, myProvider){
$scope.artist = myProvider.getArtist();
$scope.data.thingFromConfig = myProvider.thingOnConfig;
});
app.provider(‘myProvider’, function(){
//Only the next two lines are available in the app.config()
this._artist = ‘’;
this.thingFromConfig = ‘’;
this.$get = function(){
var that = this;
return {
getArtist: function(){
return that._artist;
},
thingOnConfig: that.thingFromConfig
}
}
});
app.config(function(myProviderProvider){
myProviderProvider.thingFromConfig = ‘This was set in config’;
});
非 TL;DR 版本:
1)工厂
工厂是创建和配置服务的最流行方式。除了 TL;DR 中提到的,没有太多需要说明的。你只需创建一个对象,添加属性,然后返回该对象即可。然后将工厂传递到控制器中时,该对象上的这些属性现在可以通过工厂在控制器中使用。下面是一个更详细的示例。
app.factory(‘myFactory’, function(){
var service = {};
return service;
});
现在,我们将任何属性附加到“service”上时,当我们将“myFactory”传递给控制器时,这些属性都将对我们可用。
现在,让我们向回调函数添加一些“私有”变量。这些变量不会直接从控制器中访问,但是我们最终将设置一些getter/setter方法在“service”上,以便在需要时更改这些“私有”变量。
app.factory(‘myFactory’, function($http, $q){
var service = {};
var baseUrl = ‘https://itunes.apple.com/search?term=’;
var _artist = ‘’;
var _finalUrl = ‘’;
var makeUrl = function(){
_artist = _artist.split(‘ ‘).join(‘+’);
_finalUrl = baseUrl + _artist + ‘&callback=JSON_CALLBACK’;
return _finalUrl
}
return service;
});
在这里,您会注意到我们没有将那些变量/函数附加到“service”上。我们只是创建它们以便稍后使用或修改它们。
现在我们的帮助器/私有变量和函数已经就位,让我们向“service”对象添加一些属性。我们放在“service”上的任何东西都可以直接在我们传递“myFactory”的任何控制器中使用。
我们将创建setArtist和getArtist方法,它们仅返回或设置艺术家。我们还将创建一个方法,该方法将使用我们创建的URL调用iTunes API。此方法将返回一个promise,一旦数据从iTunes API返回,该promise将得到满足。如果您在使用AngularJS中的promise方面没有太多经验,我强烈建议深入研究它们。
下面的setArtist接受一个艺术家,并允许您设置艺术家。getArtist返回艺术家。callItunes首先调用makeUrl()以构建我们将与$http请求一起使用的URL。然后它设置了一个promise对象,使用我们的最终url进行了$ http请求,因为$http返回一个promise,所以我们能够在请求之后调用.success或.error。然后,我们使用iTunes数据解析我们的promise,或者使用消息“出现错误”拒绝它。
app.factory('myFactory', function($http, $q){
var service = {};
var baseUrl = 'https://itunes.apple.com/search?term=';
var _artist = '';
var _finalUrl = '';
var makeUrl = function(){
_artist = _artist.split(' ').join('+');
_finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK'
return _finalUrl;
}
service.setArtist = function(artist){
_artist = artist;
}
service.getArtist = function(){
return _artist;
}
service.callItunes = function(){
makeUrl();
var deferred = $q.defer();
$http({
method: 'JSONP',
url: _finalUrl
}).success(function(data){
deferred.resolve(data);
}).error(function(){
deferred.reject('There was an error')
})
return deferred.promise;
}
return service;
});
现在我们的工厂已经完成。我们现在可以将'myFactory'注入到任何控制器中,然后就能调用我们附加到服务对象的方法(setArtist、getArtist和callItunes)。
app.controller('myFactoryCtrl', function($scope, myFactory){
$scope.data = {};
$scope.updateArtist = function(){
myFactory.setArtist($scope.data.artist);
};
$scope.submitArtist = function(){
myFactory.callItunes()
.then(function(data){
$scope.data.artistData = data;
}, function(data){
alert(data);
})
}
});
以上控制器中,我们注入了‘myFactory’服务。然后我们使用来自‘myFactory’的数据在$scope对象上设置属性。如果您以前没有处理过Promise,那么上面唯一棘手的代码就是。由于callItunes返回Promise,我们能够使用.then()方法,并且仅在我们的Promise用iTunes数据实现时设置$scope.data.artistData。您会注意到我们的控制器非常“瘦”(这是一个很好的编码实践)。所有逻辑和持久化数据都位于我们的服务中,而不是我们的控制器中。
2) 服务
在创建服务时需要知道的最重要的事情之一是它是使用“new”关键字实例化的。对于JavaScript专家,这应该给你提供了一个有关代码性质的重要提示。对于那些具有有限JavaScript背景或对“new”关键字实际执行的操作不太熟悉的人,让我们回顾一些JavaScript基础知识,这些知识最终将帮助我们理解服务的性质。
为了真正看到您使用“new”关键字调用函数时所发生的变化,请让我们创建一个函数并使用“new”关键字调用它,然后让我们展示解释器在看到“new”关键字时做了什么。最终结果将是相同的。
首先让我们创建我们的构造函数。
var Person = function(name, age){
this.name = name;
this.age = age;
}
这是一个典型的JavaScript构造函数。现在,每当我们使用“new”关键字调用Person函数时,“this”将绑定到新创建的对象。
现在让我们在Person原型上添加一个方法,以便它可用于我们Person“类”的每个实例。
Person.prototype.sayName = function(){
alert(‘My name is ‘ + this.name);
}
现在,因为我们将sayName函数放在原型上,每个Person的实例都可以调用sayName函数以便提醒该实例的名称。
既然我们已经有了Person构造函数和它原型上的sayName函数,让我们创建一个Person实例并调用sayName函数。
var tyler = new Person(‘Tyler’, 23);
tyler.sayName(); //alerts ‘My name is Tyler’
所以整个创建Person构造函数、将一个函数添加到它的原型中、创建Person实例,然后在它的原型上调用该函数的代码看起来像这样。
var Person = function(name, age){
this.name = name;
this.age = age;
}
Person.prototype.sayName = function(){
alert(‘My name is ‘ + this.name);
}
var tyler = new Person(‘Tyler’, 23);
tyler.sayName(); //alerts ‘My name is Tyler’
现在让我们看看在JavaScript中使用“new”关键字时实际发生了什么。你首先应该注意到的是,在我们的示例中使用“new”后,我们能够像调用对象一样调用“tyler”的方法(sayName)——因为它确实是一个对象。
因此,首先,我们知道我们的Person构造函数正在返回一个对象,无论我们是否在代码中看到它。其次,我们知道,因为我们的sayName函数位于原型上而不是直接位于Person实例上,所以Person函数返回的对象必须在查找失败时委派给其原型。更简单地说,当我们调用tyler.sayName()时,解释器会说:“好的,我将查找刚刚创建的‘tyler’对象上的sayName函数,然后调用它。等等,我在这里没有看到它——我只看到名字和年龄,让我检查一下原型。是的,看起来它在原型上,让我调用它。”
下面是您可以考虑JavaScript中“new”关键字实际上在做什么的代码。它基本上是上面段落的代码示例。我放置了“解释器视图”或解释器看到代码的方式的注释。
var Person = function(name, age){
//The below line creates an object(obj) that will delegate to the person’s prototype on failed lookups.
//var obj = Object.create(Person.prototype);
//The line directly below this sets ‘this’ to the newly created object
//this = obj;
this.name = name;
this.age = age;
//return this;
}
现在了解了JavaScript中'new'关键字的实际作用后,创建AngularJS服务应该更容易理解了。
创建服务时需要注意的最重要的一点是知道服务是使用'new'关键字实例化的。结合上面的示例,你现在应该认识到,你将直接将属性和方法附加到'this'中,然后从服务本身返回它。让我们看看这个过程。
与我们最初在工厂示例中所做的不同,我们不需要创建一个对象,然后返回该对象,因为正如之前多次提到的那样,我们使用了'new'关键字,因此解释器将创建该对象,使其委托给其原型,然后在无需我们进行工作的情况下将其返回给我们。
首先,让我们创建我们的‘私有’和帮助函数。这应该看起来非常熟悉,因为我们在工厂中做了完全相同的事情。我不会在这里解释每行代码的含义,因为我在工厂示例中已经这样做了,如果您感到困惑,请重新阅读工厂示例。
app.service('myService', function($http, $q){
var baseUrl = 'https://itunes.apple.com/search?term=';
var _artist = '';
var _finalUrl = '';
var makeUrl = function(){
_artist = _artist.split(' ').join('+');
_finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK'
return _finalUrl;
}
});
现在,我们将把所有可用于控制器的方法附加到'this'。
app.service('myService', function($http, $q){
var baseUrl = 'https://itunes.apple.com/search?term=';
var _artist = '';
var _finalUrl = '';
var makeUrl = function(){
_artist = _artist.split(' ').join('+');
_finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK'
return _finalUrl;
}
this.setArtist = function(artist){
_artist = artist;
}
this.getArtist = function(){
return _artist;
}
this.callItunes = function(){
makeUrl();
var deferred = $q.defer();
$http({
method: 'JSONP',
url: _finalUrl
}).success(function(data){
deferred.resolve(data);
}).error(function(){
deferred.reject('There was an error')
})
return deferred.promise;
}
});
现在,就像我们的工厂一样,setArtist、getArtist和callItunes将在我们将myService传递到的任何控制器中都可用。以下是myService控制器(与我们的工厂控制器几乎完全相同)。
app.controller('myServiceCtrl', function($scope, myService){
$scope.data = {};
$scope.updateArtist = function(){
myService.setArtist($scope.data.artist);
};
$scope.submitArtist = function(){
myService.callItunes()
.then(function(data){
$scope.data.artistData = data;
}, function(data){
alert(data);
})
}
});
就像我之前提到的一样,一旦你真正理解了“new”的含义,服务(Services)在AngularJS中几乎与工厂(Factories)相同。
3) 提供者(Provider)
关于提供者,最重要的事情是它们是唯一可以传递到应用程序配置(app.config)部分的服务。如果您需要修改服务对象的某个部分,以便在应用程序的其他地方可用之前进行更改,则这非常重要。虽然与服务和工厂非常相似,但有一些区别,我们将进行讨论。
首先,我们以与服务和工厂类似的方式设置提供者。下面的变量是我们的“私有”变量和辅助函数。
app.provider('myProvider', function(){
var baseUrl = 'https://itunes.apple.com/search?term=';
var _artist = '';
var _finalUrl = '';
//Going to set this property on the config function below.
this.thingFromConfig = ‘’;
var makeUrl = function(){
_artist = _artist.split(' ').join('+');
_finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK'
return _finalUrl;
}
}
*如果以上代码的任何部分令人困惑,请查看工厂部分,我会更详细地解释所有内容。
您可以将提供程序视为具有三个部分。第一部分是稍后将被修改/设置的“私有”变量/函数(如上所示)。第二部分是在您的app.config函数中可用并因此可在任何其他地方之前进行更改的变量/函数(也显示在上面)。重要的是注意这些变量需要附加到“this”关键字。在我们的示例中,只有“thingFromConfig”可在app.config中进行更改。第三部分(如下所示)是所有变量/函数,在将“myProvider”服务传递到特定控制器时,这些变量/函数将在该控制器中可用。
使用Provider创建服务时,在您的控制器中可用的属性/方法是从$get()函数返回的那些属性/方法。下面的代码在'this'上放置了$get(我们知道它最终将从该函数返回)。现在,$get函数返回我们希望在控制器中可用的所有方法/属性。这是一个代码示例。
this.$get = function($http, $q){
return {
callItunes: function(){
makeUrl();
var deferred = $q.defer();
$http({
method: 'JSONP',
url: _finalUrl
}).success(function(data){
deferred.resolve(data);
}).error(function(){
deferred.reject('There was an error')
})
return deferred.promise;
},
setArtist: function(artist){
_artist = artist;
},
getArtist: function(){
return _artist;
},
thingOnConfig: this.thingFromConfig
}
}
现在完整的提供程序代码看起来像这样
app.provider('myProvider', function(){
var baseUrl = 'https://itunes.apple.com/search?term=';
var _artist = '';
var _finalUrl = '';
//Going to set this property on the config function below
this.thingFromConfig = '';
var makeUrl = function(){
_artist = _artist.split(' ').join('+');
_finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK'
return _finalUrl;
}
this.$get = function($http, $q){
return {
callItunes: function(){
makeUrl();
var deferred = $q.defer();
$http({
method: 'JSONP',
url: _finalUrl
}).success(function(data){
deferred.resolve(data);
}).error(function(){
deferred.reject('There was an error')
})
return deferred.promise;
},
setArtist: function(artist){
_artist = artist;
},
getArtist: function(){
return _artist;
},
thingOnConfig: this.thingFromConfig
}
}
});
现在,就像在我们的工厂和服务中一样,只要我们将myProvider传递给任何控制器,setArtist、getArtist和callItunes都将可用。这是myProvider控制器(几乎与我们的工厂/服务控制器完全相同)。
app.controller('myProviderCtrl', function($scope, myProvider){
$scope.data = {};
$scope.updateArtist = function(){
myProvider.setArtist($scope.data.artist);
};
$scope.submitArtist = function(){
myProvider.callItunes()
.then(function(data){
$scope.data.artistData = data;
}, function(data){
alert(data);
})
}
$scope.data.thingFromConfig = myProvider.thingOnConfig;
});
正如之前提到的,使用Provider创建服务的整个目的是在将最终对象传递给应用程序的其余部分之前,能够通过app.config函数修改某些变量。让我们看一个例子。
app.config(function(myProviderProvider){
//Providers are the only service you can pass into app.config
myProviderProvider.thingFromConfig = 'This sentence was set in app.config. Providers are the only service that can be passed into config. Check out the code to see how it works';
});
现在你可以看到,'thingFromConfig'在我们的提供程序中是一个空字符串,但当它显示在DOM中时,它将是'This sentence was set...'。
getArtist = function () { return _artist; },
,这不应该是一个冒号吗?(在你的博客上,已经有人修复了这个问题)。 - Rutwick Gangurde所有服务都是单例;它们每个应用程序只实例化一次。可以是任何类型,无论是原始类型、对象字面量、函数,甚至是自定义类型的实例。
value
、factory
、service
、constant
和 provider
方法都是提供者。它们教导注入器如何实例化服务。
最冗长,但也是最全面的是 Provider 配方。 其余四种配方——Value、Factory、Service 和 Constant —— 只是在提供者配方上加了些语法糖。
只有在想要在应用程序启动之前对整个应用程序进行配置的情况下才应使用 Provider 配方。这通常只对行为可能在应用程序之间略有差异的可重用服务感兴趣。
装饰器(decorator)
进行修饰。理解 AngularJS 中 Factory、Service 和 Provider
这些都用于共享可重复使用的单例对象。它有助于在您的应用程序/各个组件/模块之间共享可重用的代码。
来自文档Service/Factory:
- 惰性实例化——当应用程序组件依赖于它时,Angular 才会实例化服务/工厂。
- 单例——每个依赖于服务的组件都获取到由服务工厂生成的单个实例的引用。
工厂是一个函数,在创建对象之前可以进行操纵/添加逻辑,然后返回新创建的对象。
app.factory('MyFactory', function() {
var serviceObj = {};
//creating an object with methods/functions or variables
serviceObj.myFunction = function() {
//TO DO:
};
//return that object
return serviceObj;
});
用法
它可以像类一样只是一系列函数的集合。因此,在将其注入到控制器/工厂/指令函数中时,它可以在不同的控制器中实例化。它每个应用程序只会被实例化一次。
简单地说,当您查看服务时,请考虑数组原型。服务是一个使用“new”关键字实例化新对象的函数。您可以使用this
关键字向服务对象添加属性和函数。与工厂不同,它不返回任何东西(它返回一个包含方法/属性的对象)。
app.service('MyService', function() {
//directly binding events to this context
this.myServiceFunction = function() {
//TO DO:
};
});
用法
在整个应用程序中需要共享单个对象时使用它。例如,已认证的用户详细信息、可共享的方法/数据、实用函数等。
提供者用于创建可配置的服务对象。您可以从 config 函数中配置服务设置。它通过使用 $get()
函数返回一个值,$get
函数会在 Angular 的运行阶段执行。
app.provider('configurableService', function() {
var name = '';
//this method can be be available at configuration time inside app.config.
this.setName = function(newName) {
name = newName;
};
this.$get = function() {
var getName = function() {
return name;
};
return {
getName: getName //exposed object to where it gets injected.
};
};
});
使用方法
当您需要在将服务对象提供给用户之前为其提供模块化配置时,例如,假设您想根据环境设置API URL(如dev
、stage
或prod
)。
注意
只有在Angular的配置阶段,提供程序才可用,而服务和工厂则不可用。
希望这样能够清楚地解释工厂、服务和提供程序给您的理解带来帮助。
only when you want to expose an API for application-wide configuration that must be made before the application starts. This is usually interesting only for reusable services whose behavior might need to vary slightly between applications
,所以听起来好像不可能,对吗? - qix当我意识到它们都是通过运行某个东西一次,存储其取得的值,然后在通过依赖注入引用时,呈现出相同的存储值时,我的启示来了。
假设我们有:
app.factory('a', fn);
app.service('b', fn);
app.provider('c', fn);
三者之间的区别在于:
a
的存储值来自运行fn
。b
的存储值来自用new
实例化fn
。c
的存储值先是通过new
实例化fn
获取一个实例,然后运行该实例的$get
方法。这意味着AngularJS内部有一个类似缓存对象的东西,每个注入的值只会在第一次注入时被分配一次,它们所在的位置是:
cache.a = fn()
cache.b = new fn()
cache.c = (new fn()).$get()
这就是为什么我们在服务中使用this
,并在提供者中定义一个this.$get
的原因。
factory
来完成。
只有在像 CoffeeScript、TypeScript、ES6 等语言中才需要使用 service
,以便使用它们的类语法。
只有当你的模块被多个应用程序使用并具有不同的设置时,才需要使用 provider
并使用 app.config()
。
如果你的服务是一个纯单例或者只能创建某些实例取决于你的实现。 - Andreas Linnert服务 vs 提供商 vs 工厂:
我试图保持简单。这是关于基本的 JavaScript 概念。
首先,让我们谈谈 AngularJS 中的服务!
什么是服务: 在 AngularJS 中,服务只是一个单例 JavaScript 对象,可以存储一些有用的方法或属性。此单例对象基于 ngApp(Angular 应用程序)创建,并在当前应用程序中的所有控制器之间共享。当 AngularJS 实例化服务对象时,它会将此服务对象注册到唯一的服务名称中。因此,每次需要服务实例时,Angular 将搜索该服务名称的注册表,并返回对服务对象的引用。这样,我们就可以在服务对象上调用方法、访问属性等。 你可能会问,你是否也可以将属性、方法放在控制器作用域对象中!那么为什么您需要服务对象?答案是:服务在多个控制器作用域之间共享。如果您将某些属性/方法放在控制器的作用域对象中,则仅可在当前作用域中使用。但是,当您在服务对象上定义方法、属性时,它将全局可用,并且可以通过注入该服务来访问任何控制器作用域中的内容。
因此,如果有三个控制器作用域,例如 controllerA、controllerB 和 controllerC,则所有作用域都将共享同一个服务实例。
<div ng-controller='controllerA'>
<!-- controllerA scope -->
</div>
<div ng-controller='controllerB'>
<!-- controllerB scope -->
</div>
<div ng-controller='controllerC'>
<!-- controllerC scope -->
</div>
如何创建一个服务?
AngularJS提供了不同的方法来注册服务。在这里,我们将集中讨论三种方法:factory(..), service(..), provider(..);
我们可以定义一个工厂函数如下。
factory('serviceName',function fnFactory(){ return serviceInstance;})
AngularJS提供了'factory('serviceName', fnFactory)'方法,它接受两个参数:serviceName和一个JavaScript函数。Angular通过调用fnFactory()函数来创建服务实例,如下所示。
var serviceInstace = fnFactory();
传递的函数可以定义一个对象并返回该对象。AngularJS 简单地将这个对象引用存储到作为第一个参数传递的变量中。从 fnFactory 返回的任何内容都将绑定到 serviceInstance。我们不仅可以返回对象,还可以返回函数、值等等,无论返回什么,都将对服务实例可用。
例如:
var app= angular.module('myApp', []);
//creating service using factory method
app.factory('factoryPattern',function(){
var data={
'firstName':'Tom',
'lastName':' Cruise',
greet: function(){
console.log('hello!' + this.firstName + this.lastName);
}
};
//Now all the properties and methods of data object will be available in our service object
return data;
});
service('serviceName',function fnServiceConstructor(){})
另一种方法是注册服务。唯一的区别在于AngularJS尝试实例化服务对象的方式。这次Angular使用“new”关键字并调用构造函数,类似于以下方式。
var serviceInstance = new fnServiceConstructor();
在构造函数中,我们可以使用“this”关键字向服务对象添加属性/方法。 例如:
//Creating a service using the service method
var app= angular.module('myApp', []);
app.service('servicePattern',function(){
this.firstName ='James';
this.lastName =' Bond';
this.greet = function(){
console.log('My Name is '+ this.firstName + this.lastName);
};
});
Provider(提供者)函数是创建服务的另一种方式。假设我们想要创建一个服务,它可以向用户显示问候语信息,但我们还想提供一种功能,使用户可以设置自己的问候语信息。从技术角度来看,我们想要创建可配置的服务。我们该如何实现这个功能?当然有办法,应用程序可以传递自定义的问候语信息,AngularJS 将其提供给工厂/构造函数来创建我们的服务实例。
在这种情况下,provider() 函数就起到作用了。使用 provider() 函数,我们可以创建可配置的服务。
我们可以按照以下提供者语法来创建可配置的服务:
/*step1:define a service */
app.provider('service',function serviceProviderConstructor(){});
/*step2:configure the service */
app.config(function configureService(serviceProvider){});
1.我们在提供者函数中定义构造函数,并使用它来创建提供者对象。
var serviceProvider = new serviceProviderConstructor();
2.我们在app.config()中传递的函数将被执行。 这称为配置阶段,在此阶段我们有机会自定义我们的服务。
configureService(serviceProvider);
3. 最后,通过调用serviceProvider的$get方法创建服务实例。
serviceInstance = serviceProvider.$get()
var app= angular.module('myApp', []);
app.provider('providerPattern',function providerConstructor(){
//this function works as constructor function for provider
this.firstName = 'Arnold ';
this.lastName = ' Schwarzenegger' ;
this.greetMessage = ' Welcome, This is default Greeting Message' ;
//adding some method which we can call in app.config() function
this.setGreetMsg = function(msg){
if(msg){
this.greetMessage = msg ;
}
};
//We can also add a method which can change firstName and lastName
this.$get = function(){
var firstName = this.firstName;
var lastName = this.lastName ;
var greetMessage = this.greetMessage;
var data={
greet: function(){
console.log('hello, ' + firstName + lastName+'! '+ greetMessage);
}
};
return data ;
};
});
app.config(
function(providerPatternProvider){
providerPatternProvider.setGreetMsg(' How do you do ?');
}
);
概要:
工厂使用一个工厂函数来返回服务实例。serviceInstance = fnFactory();
服务使用构造函数,并使用Angular使用'new'关键字调用此构造函数以创建服务实例。serviceInstance = new fnServiceConstructor();
提供者定义providerConstructor函数,此providerConstructor函数定义一个工厂函数$ get 。 Angular调用$ get()以创建服务对象。提供程序语法具有在实例化服务对象之前配置服务对象的附加优点。serviceInstance = $get();
如几位正确指出的那样,工厂、提供程序、服务甚至是值和常量都是同一事物的不同版本。您可以将更通用的提供程序
分解为所有这些部分。就像下面这样:
这是图片所在的文章:
你向AngularJS提供一个函数,当需要使用工厂时,AngularJS会缓存并注入其返回值。
例如:
app.factory('factory', function() {
var name = '';
// Return value **is** the object that will be injected
return {
name: name;
}
})
使用方法:
app.controller('ctrl', function($scope, factory) {
$scope.name = factory.name;
});
你向AngularJS提供一个函数,AngularJS将调用new来实例化它。由AngularJS创建的实例将被缓存和注入到请求服务时使用。由于使用new实例化服务,因此关键字this是有效的,并且引用该实例。
例如:
app.service('service', function() {
var name = '';
this.setName = function(newName) {
name = newName;
}
this.getName = function() {
return name;
}
});
使用方法:
app.controller('ctrl', function($scope, service) {
$scope.name = service.getName();
});
您向AngularJS提供一个函数,AngularJS将调用其$get
函数。从$get
函数返回的值将被缓存并在请求服务时注入。
提供者允许您在AngularJS调用$get
方法以获取可注入对象之前进行配置。
示例:
app.provider('provider', function() {
var name = '';
this.setName = function(newName) {
name = newName;
}
this.$get = function() {
return {
name: name
}
}
})
用法(作为控制器中的可注入项)
app.controller('ctrl', function($scope, provider) {
$scope.name = provider.name;
});
使用方法(在调用$get
创建可注入对象之前配置提供程序)
app.config(function(providerProvider) {
providerProvider.setName('John');
});
当我尝试处理提供程序时,发现了一些有趣的事情。
对于提供程序而言,可注入对象的可见性与服务和工厂不同。如果声明了 AngularJS 的“常量”(例如:myApp.constant('a', 'Robert');
),则可以将其注入到服务、工厂和提供程序中。
但是,如果声明了 AngularJS 的“值”(例如:myApp.value('b', {name: 'Jones'});
),则可以将其注入到服务和工厂中,但无法注入到创建提供程序的函数中。然而,您可以将其注入到为提供程序定义的 $get
函数中。这在 AngularJS 文档中有提及,但很容易被忽视。您可以在 %provide 页面的 value 和 constant 方法部分找到它。
<div ng-app="MyAppName">
<div ng-controller="MyCtrl">
<p>from Service: {{servGreet}}</p>
<p>from Provider: {{provGreet}}</p>
</div>
</div>
<script>
var myApp = angular.module('MyAppName', []);
myApp.constant('a', 'Robert');
myApp.value('b', {name: 'Jones'});
myApp.service('greetService', function(a,b) {
this.greeter = 'Hi there, ' + a + ' ' + b.name;
});
myApp.provider('greetProvider', function(a) {
this.firstName = a;
this.$get = function(b) {
this.lastName = b.name;
this.fullName = this.firstName + ' ' + this.lastName;
return this;
};
});
function MyCtrl($scope, greetService, greetProvider) {
$scope.servGreet = greetService.greeter;
$scope.provGreet = greetProvider.fullName;
}
</script>
service.factory
成功处理。我不想进一步复杂化这个主题。 - demisxservices
、factories
和providers
的好答案。 - Mistalis