angular.servicevsangular.factory

我已经看到angular.factory()angular.service()用于声明服务;然而,我找不到angular.service在官方留档的任何地方。

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

344601 次浏览

以下是主要区别:

服务

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

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

用法:对于共享效用函数很有用,只需将( )附加到注入的函数引用即可调用。也可以与injectedArg.call( this )或类似的一起运行。

工厂

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

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

用法:对于返回阶级函数可能很有用,然后可以对其进行new'ed以创建实例。

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

您还可以在stackoverflow对服务与工厂的困惑上检查AngularJS留档和类似的问题。

简单地说…

const user = {firstName: 'john'};
// Factoryconst addLastNameFactory = (user, lastName) => ({...user,lastName,});
console.log(addLastNameFactory(user, 'doe'));
// Serviceconst addLastNameService = (user, lastName) => {user.lastName = lastName; // BAD! Mutationreturn user;};
console.log(addLastNameService(user, 'doe'));

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

我很难理解这个概念,直到我这样对自己说:

服务:你写的函数将是新的-ed:

  myInjectedService  <----  new myServiceFunction()

工厂:您编写的函数(构造函数)将是援引

  myInjectedFactory  <---  myFactoryFunction()

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

比如写一个服务函数来暴露一个公共API:

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

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

function myFactoryFunction() {var aPrivateVariable = "yay";
function hello() {return "hello mars " + aPrivateVariable;}  
// expose a public APIreturn {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 controllervar myShinyNewObject = new myInjectedFactory();$scope.four = myShinyNewObject.a2();

用哪一个?…

你可以用两者来完成同样的事情。然而,在某些情况下,工厂给你更多的灵活性来创建具有更简单语法的可注入对象。这是因为虽然myInjecttedService必须始终是一个对象,但myInjecttedFactory可以是一个对象、一个函数引用或任何值。例如,如果你编写一个服务来创建一个构造函数(就像上面的最后一个例子一样),它必须像这样实例化:

var myShinyNewObject = new myInjectedService.myFunction()

可以说,这比这更不可取:

var myShinyNewObject = new myInjectedFactory();

(但是你首先应该警惕使用这种类型的模式,因为控制器中的新的-ing对象会产生难以跟踪的依赖关系,这些依赖关系很难模拟测试。让服务为你管理一组对象比使用new()狡猾。)


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

还要记住,在这两种情况下,角都在帮助你管理单例。无论你在哪里或注入多少次服务或函数,你都会得到对同一个对象或函数的相同引用。(除非工厂只是返回一个值,比如数字或字符串。在这种情况下,你总是会得到相同的值,但不是引用。)

太长别读

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



不要太长别读

1)工厂
工厂是创建和配置服务的最流行的方式。没有什么比太长别读说的更多的了。你只需创建一个对象,向其添加属性,然后返回相同的对象。然后当你将工厂传递给你的控制器时,对象上的这些属性现在将通过你的工厂在该控制器中可用。下面是一个更广泛的例子。

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

现在,当我们将“myFactory”传递给我们的控制器时,我们附加到“service”的任何属性都将可用。

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

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”。我们只是创建它们以便以后使用或修改它们。

  • BasUrl是iTunesAPI需要的基本URL
  • _artist就是我们要找的艺术家
  • _finalUrl是我们将调用的最终且完全构建的URLiTunesmakeUrl是一个函数,它将创建并返回我们的URLiTunes友好的URL。

现在我们的辅助/私有变量和函数已经就位,让我们向“service”对象添加一些属性。无论我们放在“service”上的是什么,我们都可以直接在我们传递“myFactory”到的任何控制器中使用。

我们将创建setArtist和getArtist方法来简单地返回或设置艺术家。我们还将创建一个使用我们创建的URL调用iTunesAPI的方法。这个方法将返回一个Promise,一旦数据从iTunesAPI返回就会实现。如果你没有在Angular中使用Promise的经验,我强烈建议你深入研究它们。

Itune es首先调用makeUrl()以构建我们将用于$超文本传输协议请求的URL。然后它设置了一个Promise对象,用我们的最终URL发出$超文本传输协议请求,然后因为$超文本传输协议返回了Promise,我们可以在请求后调用.成功或. 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和call Itune es)。

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。因为call Itune es返回的是一个Promise,我们能够使用. thi()方法,并且只有在iTunes数据实现我们的Promise后才设置$scope.data.artistData。你会注意到我们的控制器非常'薄'。我们所有的逻辑和持久数据都位于我们的服务中,而不是我们的控制器中。

2)服务
也许在创建服务时最重要的一点是它是使用“new”关键字实例化的。对于JavaScript大师来说,这应该会让你对代码的本质有一个很大的提示。对于那些JavaScript背景有限的人,或者那些不太熟悉“new”关键字实际作用的人,让我们回顾一些JavaScript基础知识,这些基础知识最终将帮助我们理解服务的本质。

要真正看到使用new关键字调用函数时发生的变化,让我们创建一个函数并使用new关键字调用它,然后让我们展示解释器在看到new关键字时所做的事情。最终结果将是相同的。

首先,创建我们的Constructor。

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

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

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

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'关键字,因此解释器将创建该对象,将其委托给它的原型,然后为我们返回它,而无需我们做这项工作。

首先,让我们创建我们的'Private'和helper函数。这应该看起来很熟悉,因为我们对工厂做了完全相同的事情。我不会在这里解释每一行的作用,因为我在工厂示例中做了这件事,如果你感到困惑,请重新阅读工厂示例。

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和call Itune es将在我们将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”的作用,服务几乎与Angular中的工厂相同。

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

建筑

使用工厂,Angular将调用函数来获取结果。缓存和注入的是结果。

 //factoryvar obj = fn();return obj;

对于服务,Angular将通过调用新的调用构造函数。构造的函数被缓存和注入。

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

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

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

结论

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

那么你应该更喜欢哪一个呢?任何一个-它们是如此相似,以至于差异是微不足道的。如果你确实选择了一个而不是另一个,只要知道它们是如何构建的,这样你就可以正确地实现它们。

线索就在名字里

服务和工厂彼此相似。两者都将产生一个可以注入到其他对象中的单例对象,因此通常可以互换使用。

它们旨在在语义上用于实现不同的设计模式。

服务用于实现服务模式

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

这在Angular中尤其重要,因为Angular模型通常只是从服务器中提取的JSON对象,因此我们需要在某个地方放置我们的业务逻辑。

例如,这是一个Github服务。它知道如何与Github交谈。它知道URL和方法。我们可以将其注入控制器,它将生成并返回一个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);}});)();

工厂实现工厂模式

另一方面,工厂旨在实现工厂模式。我们使用工厂函数生成对象的工厂模式。通常我们可能会使用它来构建模型。这是一个返回作者构造函数的工厂:

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

请注意,工厂也返回单例。

工厂可以返回构造函数

因为工厂只是返回一个对象,所以它可以返回任何类型的对象,包括构造函数,如上所述。

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

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

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

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

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

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

我花了一些时间试图找出区别。

我认为工厂函数使用模块模式,服务函数使用标准的java脚本构造函数模式。

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

在服务模式中没有太多的意义,因为它所做的一切都可以很容易地与工厂一起完成。例外可能是:

  • 如果您出于某种原因关心实例化服务的声明类型-如果您使用服务模式,您的构造函数将是新服务的类型。
  • 如果你已经有了一个在其他地方使用的构造函数,你也想把它用作服务(尽管如果你想向它注入任何东西,可能没有多大用处!)。

可以说,从语法的角度来看,服务模式是创建新对象的更好的方式,但实例化成本也更高。其他人表示,角使用“new”来创建服务,但事实并非如此——它无法做到这一点,因为每个服务构造函数都有不同数量的参数。角实际上所做的是在内部使用工厂模式来包装你的构造函数。然后,它对模拟 javascript的“new”运算符进行了一些巧妙的jiggery pokery,使用可变数量的可注入参数调用您的构造函数-但如果您直接使用工厂模式,则可以省略此步骤,从而略微提高代码的效率。

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

要记住的关键是每一个都是另一个的特例。链中的每个特例都允许您用更少的代码做同样的事情。每个人都有一些额外的限制。

决定何时使用哪个,你只需要看到哪个允许你在更少的代码中做你想做的事情。这是一张图片,说明了它们有多相似:

在此处输入图片描述

对于完整的一步一步细分和快速参考何时使用每个你可以访问博客文章,我从哪里得到这张照片:

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