angular.service vs angular.factory

我已经看到angular.factory()和angular.service()用于声明服务; 然而,我不能在官方文档中的任何地方find angular.service

这两种方法有什么区别? 哪些应该用于什么(假设他们做不同的事情)?

  angular.service('myService', myServiceFunction); angular.factory('myFactory', myFactoryFunction); 

我一直困扰着这个概念,直到我这样对自己说:

服务 :你写的function将是新的

  myInjectedService <---- new myServiceFunction() 

工厂 :你写的函数 (构造函数)将被调用

  myInjectedFactory <--- myFactoryFunction() 

你做什么取决于你,但有一些有用的模式…

比如写一个服务函数来公开一个公共的API:

 function myServiceFunction() { this.awesomeApi = function(optional) { // calculate some stuff return awesomeListOfValues; } } --------------------------------------------------------------------------------- // Injected in your controller $scope.awesome = myInjectedService.awesomeApi(); 

或者使用工厂函数公开一个公共API:

 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(); 

哪一个使用?…

你可以用两个来完成同样的事情。 但是,在某些情况下, 工厂给你更多的灵活性来创build一个简单的语法注射。 这是因为虽然myInjectedService必须始终是一个对象,但myInjectedFactory可以是对象,函数引用或任何值。 例如,如果您编写了一个服务来创build一个构造函数(如上面的最后一个示例中所示),则必须像这样来实例化:

 var myShinyNewObject = new myInjectedService.myFunction() 

这可以说是不太可取的比这个:

 var myShinyNewObject = new myInjectedFactory(); 

(但是,首先应该谨慎使用这种types的模式,因为控制器中的对象会创build难以模拟testing的难以跟踪的依赖关系。最好是让服务pipe理一组对象你比使用new()狡猾。)。


还有一件事,他们都是单身…

另外请记住,在这两种情况下,angular正在帮助你pipe理一个单身人士。 无论您在何处或多less次注入服务或function,都将获得与同一对象或function相同的参考。 (除了工厂简单地返回一个数字或string的值之外,在这种情况下,您将始终获得相同的值,但不是参考)。

简单的说 ..

 // 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')); 

以下是主要区别:

服务

语法: module.service( 'serviceName', function );

结果:当将serviceName声明为注入参数时,将为您提供传递给module.service 的函数实例

用法:对于通过简单追加( )到注入的函数引用来调用有用的共享实用程序函数可能很有用。 也可以运行injectedArg.call( this )或类似的。

工厂

语法: module.factory( 'factoryName', function );

结果:当将factoryName声明为注入参数时,将提供通过调用传递给module.factory 的函数引用返回

用法:可以用于返回一个“类”函数,然后可以创build新的实例。

这里是使用服务和工厂的例子 。 阅读更多关于AngularJS服务与工厂 。

你也可以查看关于服务vs工厂的关于stackoverflow的AngularJS文档和类似的问题。

TL; DR

1)当你使用工厂时,你创build一个对象,添加属性,然后返回相同的对象。 当你把这个工厂传入你的控制器时,那个对象上的这些属性现在可以通过你的工厂在那个控制器中使用。

 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)工厂
工厂是创build和configuration服务最stream行的方式。 DR说,实际上没有比TL更多的东西。 你只要创build一个对象,给它添加属性,然后返回相同的对象。 然后,当您将工厂传递到您的控制器时,对象上的这些属性现在将通过您的工厂在该控制器中可用。 下面是一个更广泛的例子。

 app.factory('myFactory', function(){ var service = {}; return service; }); 

现在,当我们将“myFactory”传递给我们的控制器时,无论我们附加到“服务”的属性是否可用。

现在让我们在callback函数中添加一些“私有”variables。 这些将不能直接从控制器访问,但是我们最终将在“服务”上设置一些getter / setter方法,以便在需要时能够更改这些“私有”variables。

 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; }); 

在这里你会注意到我们没有将这些variables/函数附加到“服务”上。 我们只是简单地创build它们,以便稍后使用或修改它们。

  • baseUrl是iTunes API所需的基本URL
  • _artist是我们希望查找的艺术家
  • _finalUrl是我们打电话给iTunes的最终完整的URL makeUrl是一个函数,它将创build并返回我们的iTunes友好的URL。

现在我们的助手/私有variables和函数已经到位,让我们添加一些属性到“服务”对象。 无论我们把“服务”放在哪里,我们都可以直接在我们传递“myFactory”的控制器中使用。

我们将创buildsetArtist并获取艺术家方法,简单地返回或设置艺术家。 我们还将创build一个方法,用我们创build的URL调用iTunes API。 这个方法将返回一个数据从iTunes API返回的诺言。 如果您在Angular中没有太多的使用承诺的经验,我强烈build议您深入了解它们。

setArtist下面接受一位艺术家,并允许您设置艺术家。 getArtist返回艺术家callItunes首先调用makeUrl()以构build我们将用于$ http请求的URL。 然后它设置一个promise对象,用最后一个url创build一个$ 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”服务。 然后,我们在来自“myFactory”数据的$ scope对象上设置属性。 上面唯一棘手的代码是,如果你从来没有处理过承诺。 由于callItunes正在返回一个承诺,我们可以使用.then()方法,只要设置$ scope.data.artistData一旦我们的承诺与iTunes数据完成。 你会注意到我们的控制器非常“薄”。 我们所有的逻辑和持久性数据都位于我们的服务中,而不是在我们的控制器中。

2)服务
在处理创build服务时最重要的事情可能是使用“new”关键字实例化。 对于你的JavaScript专家来说,这应该给你一个很大的提示,以代码的性质。 对于那些对JavaScript有限背景或对“新”关键字实际上不太熟悉的人,我们来回顾一下JavaScript的基础知识,这些基础知识最终将帮助我们理解服务的本质。

要真正看到用'new'关键字调用一个函数时发生的变化,我们来创build一个函数并用'new'关键字调用它,然后让我们看看解释器在看到'new'关键字时会做些什么。 最终结果将是相同的。

首先让我们创build我们的构造函数。

 var Person = function(name, age){ this.name = name; this.age = age; } 

这是一个典型的JavaScript构造函数。 现在每当我们使用'new'关键字调用Person函数,'this'将被绑定到新创build的对象。

现在让我们在Person的原型上添加一个方法,这样它就可以在我们Person类的每个实例上使用。

 Person.prototype.sayName = function(){ alert('My name is ' + this.name); } 

现在,因为我们把sayName函数放在原型上,Person的每个实例都可以调用sayName函数来提醒实例的名字。

现在我们有Person构造函数和我们的sayName函数在它的原型上,让我们实际创build一个Person的实例,然后调用sayName函数。

 var tyler = new Person('Tyler', 23); tyler.sayName(); //alerts 'My name is Tyler' 

所以一起创build一个Person构造函数的代码,添加一个函数到它的原型,创build一个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()时,解释器会说:“好吧,我要看看刚刚创build的'tyler'对象,findsayName函数,然后调用它。 等一下,我在这里看不到 – 我看到的只是名字和年龄,让我检查原型。 是的,看起来好像是在原型上,让我来称呼它。“

以下代码是关于如何考虑“新”关键字在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; } 

现在掌握JavaScript中“new”关键字的用法,在Angular中创buildService应该更容易理解。

在创build服务时要理解的最重要的事情是知道服务是用“新”关键字实例化的。 结合这些知识与上面的例子,你现在应该认识到,你将直接附加你的属性和方法到'this',然后将从服务本身返回。 让我们来看看这个在行动。

与我们最初使用Factory示例不同的是,我们不需要创build对象,然后返回该对象,因为像之前多次提到过的那样,我们使用“new”关键字,因此解释器将创build该对象,并将其委托给它的原型,然后返回给我们,我们不必做这项工作。

首先,让我们创build我们的“私人”和帮手function。 这应该看起来很熟悉,因为我们和我们的工厂完全一样。 我不会解释每一行在这里做什么,因为我在工厂的例子中做了这个,如果你感到困惑,重新读一下这个工厂的例子。

 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控制器(几乎和我们的工厂控制器一样)。

 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的工厂一样。

线索是在名字

服务和工厂是相似的。 两者都会产生一个可以注入其他对象的单例对象,所以经常可以互换使用。

它们旨在用语义来实现不同的devise模式。

服务是为了实现一个服务模式

服务模式是将应用程序分解为逻辑上一致的function单元的模式。 一个例子可能是一个API访问器,或一组业务逻辑。

这在Angular中尤为重要,因为Angular模型通常只是从服务器拉取的JSON对象,所以我们需要某处放置我们的业务逻辑。

例如,这里是一个Github服务。 它知道如何与Github交谈。 它知道关于url和方法。 我们可以将其注入到控制器中,它将生成并返回一个承诺。

 (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); } }); )(); 

工厂实行工厂模式

另一方面,工厂是为了实现工厂模式。 在一个工厂模式中,我们使用工厂函数来生成一个对象。 通常我们可以使用它来构build模型。 这是一个返回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(); }) 

请注意,工厂也返回单身人士。

工厂可以返回一个构造函数

因为工厂只是返回一个对象,所以它可以返回任何types的对象,包括一个构造函数,就像我们上面看到的那样。

工厂返回一个对象; 服务是新的

另一个技术区别在于服务和工厂的组成方式。 一个服务函数将会被新的生成对象。 工厂函数将被调用并返回对象。

  • 服务是新的构造函数。
  • 工厂只是简单的调用并返回一个对象。

这意味着在一个服务中,我们追加到“this”,在构造函数的上下文中,它将指向正在build造的对象。

为了说明这一点,下面是使用服务和工厂创build的同一个简单对象:

 angular.module('app', []) .service('helloService', function() { this.sayHello = function() { return "Hello!"; } }) .factory('helloFactory', function() { return { sayHello: function() { return "Hello!"; } } }); 

app.factory('fn',fn)与app.service('fn',fn)

施工

有了工厂,Angular会调用函数来获得结果。 这是caching和注入的结果。

  //factory var obj = fn(); return obj; 

有了服务,Angular将调用new来调用构造函数。 构造的函数被caching和注入。

  //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 } }); 

服务function通常不会返回任何内容。 相反,他们执行初始化和公开function。 函数也可以引用“this”,因为它是使用“new”构造的。

 app.service('fn', function () { var foo = 0; var bar = 0; this.setFoo = function (val) { foo = val; } this.setBar = function (val){ bar = val; } }); 

结论

谈到使用工厂或服务时,他们都非常相似。 它们被注入到控制器,指令,运行块等中,并以几乎相同的方式用于客户端代码中。 他们也都是单身人士 – 这意味着在服务/工厂注入的所有地方之间共享相同的实例。

那你应该选哪个? 任何一个 – 他们是如此相似,差异是微不足道的。 如果您确实select了一个,那么请注意它们是如何构build的,以便您可以正确实施它们。

这里的所有答案似乎都是围绕着服务和工厂,这是有效的,因为那是被问到的。 但是请记住,还有其他几个包括provider()value()constant()

要记住的关键是每一个都是另一个的特例。 链中的每个特殊情况都可以让您用较less的代码完成相同的工作。 每一个也有一些额外的限制。

要决定何时使用哪一个你只看到哪一个让你做你想要的less代码。 这里是一个图像,说明它们有多相似:

在这里输入图像描述

对于完整的一步一步的细分和快速参考何时使用每个你可以访问博客文章,我从这个图像:

http://www.simplygoodcode.com/2015/11/the-difference-between-service-provider-and-factory-in-angularjs/

我花了一些时间试图找出差异。

而我认为工厂函数使用模块模式和服务函数使用标准的Java脚本构造器模式。

工厂模式更加灵活,因为它可以返回函数和值以及对象。

恕我直言,在服务模式没有很多的一点,因为它所做的一切,你可以轻松地做一个工厂。 例外可能是:

  • 如果你关心你的实例化服务的声明types – 如果你使用服务模式,你的构造函数将是新服务的types。
  • 如果你已经有了一个你正在其他地方使用的构造函数,那么你也想把它作为一个服务来使用(尽pipe如果你想注入任何东西的话,可能没什么用处)。

可以说,服务模式是从语法的angular度来创build一个新的对象的稍微好一些的方法,但是实例化的成本也更高。 其他人则表示angular度使用“新”来创build服务,但这不是完全正确的 – 它不能这样做,因为每个服务构造函数都有不同的参数数目。 实际上,angular度是使用内部工厂模式来包装您的构造函数。 然后它做了一些巧妙的jiggery pokery来模拟 javascript的“new”运算符,用可变数量的可注入参数调用你的构造函数 – 但是如果你直接使用工厂模式,你可以省略这一步,这样就可以非常轻微地提高你的效率码。