我看到angular.factory()和angular.service()都用于声明服务;但是,我在官方文档中的任何地方cannot find angular.service
。
这两种方法有什么区别?应该用什么(假设他们做不同的事情)?
答案 0 :(得分:1263)
angular.service('myService', myServiceFunction);
angular.factory('myFactory', myFactoryFunction);
我无法绕过这个概念,直到我这样说:
服务:您撰写的功能将新 -ed:
myInjectedService <---- new myServiceFunction()
工厂:您编写的函数(构造函数)将调用:
myInjectedFactory <--- myFactoryFunction()
你用它做什么取决于你,但有一些有用的模式......
function myServiceFunction() {
this.awesomeApi = function(optional) {
// calculate some stuff
return awesomeListOfValues;
}
}
---------------------------------------------------------------------------------
// Injected in your controller
$scope.awesome = myInjectedService.awesomeApi();
function myFactoryFunction() {
var aPrivateVariable = "yay";
function hello() {
return "hello mars " + aPrivateVariable;
}
// expose a public API
return {
hello: hello
};
}
---------------------------------------------------------------------------------
// Injected in your controller
$scope.hello = myInjectedFactory.hello();
function myFactoryFunction() {
return function() {
var a = 2;
this.a2 = function() {
return a*2;
};
};
}
---------------------------------------------------------------------------------
// Injected in your controller
var myShinyNewObject = new myInjectedFactory();
$scope.four = myShinyNewObject.a2();
你可以用两者完成同样的事情。但是,在某些情况下, factory 可以让您更灵活地使用更简单的语法创建注入表。这是因为myInjectedService必须始终是一个对象,myInjectedFactory可以是一个对象,一个函数引用或任何值。例如,如果您编写了一个服务来创建构造函数(如上面的上一个示例所示),则必须实例化它:
var myShinyNewObject = new myInjectedService.myFunction()
这可能比这更不可取:
var myShinyNewObject = new myInjectedFactory();
(但是你应该首先考虑使用这种类型的模式,因为控制器中的 new -ing对象会创建难以模拟测试的难以跟踪的依赖关系。让服务管理一组对象,而不是使用new()
wily-nilly。)
另请注意,在这两种情况下,棱角都可以帮助您管理单身人士。无论您注入服务或功能的位置或次数,您都将获得对同一对象或功能的相同引用。 (除了工厂只返回一个数字或字符串之类的值。在这种情况下,你总是得到相同的值,但不是引用。)
答案 1 :(得分:317)
简单地说..
// Service
service = (a, b) => {
a.lastName = b;
return a;
};
// Factory
factory = (a, b) => Object.assign({}, a, { lastName: b });
const fullName = { firstName: 'john' };
// Service
const lastNameService = (a, b) => {
a.lastName = b;
return a;
};
console.log(lastNameService(fullName, 'doe'));
// Factory
const lastNameFactory = (a, b) =>
Object.assign({}, a, { lastName: b })
console.log(lastNameFactory(fullName, 'doe'));
答案 2 :(得分:247)
以下是主要区别:
语法:module.service( 'serviceName', function );
结果:当将serviceName声明为可注入参数时,将向您提供传递给module.service
的函数实例。
用法:对于共享实用程序函数非常有用,只需将( )
附加到注入的函数引用即可调用。也可以使用injectedArg.call( this )
或类似方式运行。
语法:module.factory( 'factoryName', function );
结果:当将factoryName声明为可注入参数时,将为您提供通过调用传递给module.factory
的函数引用返回的值。
用法:可用于返回'class'函数,然后可以将其设置为创建实例。
这是example using services and factory。详细了解AngularJS Service vs Factory。
您还可以在stackoverflow AngularJS documentation上查看confused about service vs factory及类似问题。
答案 3 :(得分:135)
TL; DR
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)当您使用服务时,Angular会使用'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;
}
});
非TL; DR
1)工厂
工厂是最流行的创建和配置服务的方式。 DR说的确没有比TL更多的东西。您只需创建一个对象,向其添加属性,然后返回该对象。然后,当您将工厂传递到控制器时,该对象上的这些属性现在将通过您的工厂在该控制器中可用。下面是一个更广泛的例子。
app.factory('myFactory', function(){
var service = {};
return service;
});
当我们将'myFactory'传递给我们的控制器时,我们可以使用我们附加到'service'的任何属性。
现在让我们在回调函数中添加一些“私有”变量。这些不能直接从控制器访问,但我们最终会在'service'上设置一些getter / setter方法,以便在需要时能够改变这些'private'变量。
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'对象添加一些属性。无论我们提供什么“服务”,我们都可以直接使用我们通过“myFactory”的控制器。
我们将创建setArtist和getArtist方法,只返回或设置艺术家。我们还将创建一个方法,使用我们创建的URL调用iTunes API。一旦数据从iTunes API返回,此方法将返回一个承诺。如果你在Angular中没有使用promises的经验,我强烈建议你深入研究它们。
setArtist 下方接受艺术家并允许您设置艺术家。 getArtist 返回艺术家callItunes首先调用makeUrl()以构建我们将使用$ http请求的URL。然后它设置一个promise对象,使用我们的最终url发出$ http请求,然后因为$ http返回一个promise,我们可以在我们的请求之后调用.success或.error。然后我们用iTunes数据解决我们的承诺,或者我们拒绝它并发出一条消息“有一个错误”。
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'服务。然后,我们在$ scope对象上设置属性来自'myFactory'的数据。上面唯一棘手的代码是,如果你以前从未处理过承诺。因为callItunes正在返回一个promise,所以我们可以使用.then()方法,只有在我们的承诺与iTunes数据一起完成后才设置$ scope.data.artistData。你会注意到我们的控制器非常“薄”。我们所有的逻辑和持久数据都位于我们的服务中,而不是我们的控制器中。
2)服务
在处理创建服务时,最重要的事情可能是它使用'new'关键字进行实例化。对于JavaScript 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函数,然后调用它。等一下,我在这里看不到 - 我只看到名字和年龄,让我查看原型。是的,看起来像是在原型上,让我称之为。“
下面是您如何思考'new'关键字在JavaScript中实际执行的操作的代码。它基本上是上一段的代码示例。我已经把'解释器视图'或解释器看到笔记中的代码的方式。
var Person = function(name, age){
//The line below this creates an obj object 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;
}
现在了解'new'关键字在JavaScript中的实际功能,在Angular中创建服务应该更容易理解。
创建服务时要了解的最重要的事情是知道服务是使用'new'关键字实例化的。将这些知识与上面的示例相结合,您现在应该认识到您将直接将属性和方法附加到'this',然后从服务本身返回。我们来看看这个实际情况。
与我们最初对Factory示例所做的不同,我们不需要创建一个对象然后返回该对象,因为像之前多次提到的那样,我们使用了'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);
})
}
});
就像我之前提到的,一旦你真正理解了'新'的含义,服务几乎与Angular的工厂相同。
答案 4 :(得分:35)
服务和工厂彼此相似。两者都会产生一个可以注入其他对象的单例对象,因此通常可以互换使用。
它们旨在在语义上用于实现不同的设计模式。
服务模式是指您的应用程序被分解为逻辑上一致的功能单元。一个示例可能是API访问器或一组业务逻辑。
这在Angular中尤其重要,因为Angular模型通常只是从服务器中提取的JSON对象,因此我们需要在某处放置我们的业务逻辑。
例如,这是一个Github服务。它知道如何与Github交谈。它知道网址和方法。我们可以将它注入控制器,它将生成并返回一个promise。
(function() {
var base = "https://api.github.com";
angular.module('github', [])
.service('githubService', function( $http ) {
this.getEvents: function() {
var url = [
base,
'/events',
'?callback=JSON_CALLBACK'
].join('');
return $http.jsonp(url);
}
});
)();
另一方面,工厂旨在实施工厂模式。我们使用工厂函数生成对象的工厂模式。通常我们可以使用它来构建模型。这是一个返回Author构造函数的工厂:
angular.module('user', [])
.factory('User', function($resource) {
var url = 'http://simple-api.herokuapp.com/api/v1/authors/:id'
return $resource(url);
})
我们会像这样使用它:
angular.module('app', ['user'])
.controller('authorController', function($scope, User) {
$scope.user = new User();
})
请注意,工厂也会返回单身人士。
因为工厂只返回一个对象,所以它可以返回你喜欢的任何类型的对象,包括构造函数,如上所示。
另一个技术差异在于服务和工厂的组成方式。将新建服务功能以生成对象。将调用工厂函数并返回对象。
这意味着在服务中,我们会附加到&#34;这个&#34;在构造函数的上下文中,它将指向正在构造的对象。
为了说明这一点,这里是使用服务和工厂创建的同一个简单对象:
angular.module('app', [])
.service('helloService', function() {
this.sayHello = function() {
return "Hello!";
}
})
.factory('helloFactory', function() {
return {
sayHello: function() {
return "Hello!";
}
}
});
答案 5 :(得分:26)
这里的所有答案似乎都围绕着服务和工厂,这是有效的,因为那是被问到的。但同样重要的是要记住,其他几个包括provider()
,value()
和constant()
。
要记住的关键是每一个都是另一个的特例。链中的每个特殊情况允许您使用更少的代码执行相同的操作。每个人都有一些额外的限制。
决定何时使用,您只需看到哪一个允许您在更少的代码中执行您想要的操作。这是一张图片,说明它们有多相似:
有关完整的逐步细分和快速参考何时使用,您可以访问我从中获取此图像的博客文章:
答案 6 :(得分:23)
<强>建筑强>
对于工厂,Angular将调用该函数来获取结果。这是缓存和注入的结果。
//factory
var obj = fn();
return obj;
使用服务,Angular将通过调用 new 来调用构造函数。构造的函数被缓存并注入。
//service
var obj = new fn();
return obj;
<强>实施强>
工厂通常返回一个对象文字,因为返回值 注入控制器,运行块,指令等的内容
app.factory('fn', function(){
var foo = 0;
var bar = 0;
function setFoo(val) {
foo = val;
}
function setBar (val){
bar = val;
}
return {
setFoo: setFoo,
serBar: setBar
}
});
服务功能通常不会返回任何内容。相反,它们执行初始化和公开函数。功能也可以参考这个&#39;因为它是使用&#39; new&#39;
构建的app.service('fn', function () {
var foo = 0;
var bar = 0;
this.setFoo = function (val) {
foo = val;
}
this.setBar = function (val){
bar = val;
}
});
<强>结论强>
在使用工厂或服务时,它们都非常相似。它们被注入控制器,指令,运行块等,并以几乎相同的方式在客户端代码中使用。它们也都是单例 - 意味着在注入服务/工厂的所有地方之间共享相同的实例。
那你更喜欢哪个?任何一个 - 他们是如此相似,以至于差异是微不足道的。如果您选择其中一个,请注意它们的构造方式,以便您可以正确实现它们。
答案 7 :(得分:5)
我花了一些时间试图找出差异。
我认为工厂函数使用模块模式和服务函数使用标准的java脚本构造函数模式。
答案 8 :(得分:2)
工厂模式更灵活,因为它可以返回函数和值以及对象。
服务模式恕我直言中没有太多意义,因为它所做的一切与工厂一样容易。例外情况可能是:
可以说,服务模式是从语法角度创建新对象的略微更好的方式,但实例化的成本也更高。其他人已经指出angular使用“new”来创建服务,但这并不完全正确 - 它无法做到这一点,因为每个服务构造函数都有不同数量的参数。实际上角度的作用是在内部使用工厂模式来包装构造函数。然后它为模拟 javascript的“new”运算符做了一些聪明的jiggery pokery,用可变数量的可注入参数调用你的构造函数 - 但如果你只是直接使用工厂模式,你可以省略这一步,因此非常有效地提高了代码的效率。