AngularJS:何时使用服务而不是工厂


296

请在这里忍受我。我知道还有其他答案,例如: AngularJS:服务vs提供程序vs工厂

但是我仍然不知道您何时在工厂使用服务。

据我所知,工厂通常用于创建可被多个控制器调用的“通用”功能:创建通用控制器功能

Angular文档似乎更喜欢工厂而不是服务。他们甚至在使用工厂时也提到“服务”,这更加令人困惑!http://docs.angularjs.org/guide/dev_guide.services.creating_services

那么什么时候使用一项服务呢?

有什么事情是只能通过服务完成的?

幕后有什么不同吗?性能/内存差异?

这是一个例子。除了声明方法外,它们看起来是相同的,我不知道为什么要对彼此进行比较。http://jsfiddle.net/uEpkE/

更新:从托马斯的答案看来,这似乎暗示服务是针对使用私有方法的更简单逻辑而工厂是针对更复杂的逻辑,因此我更新了下面的小提琴代码,似乎两者都能够支持私有功能?

myApp.factory('fooFactory', function() {
    var fooVar;
    var addHi = function(foo){ fooVar = 'Hi '+foo; }

    return {
        setFoobar: function(foo){
            addHi(foo);
        },
        getFoobar:function(){
            return fooVar;
        }
    };
});
myApp.service('fooService', function() {
    var fooVar;
    var addHi = function(foo){ fooVar = 'Hi '+foo;}

    this.setFoobar = function(foo){
        addHi(foo);
    }
    this.getFoobar = function(){
        return fooVar;
    }
});

function MyCtrl($scope, fooService, fooFactory) {
    fooFactory.setFoobar("fooFactory");
    fooService.setFoobar("fooService");
    //foobars = "Hi fooFactory, Hi fooService"
    $scope.foobars = [
        fooFactory.getFoobar(),
        fooService.getFoobar()
    ];
}

当然,服务支持私有,但是如果您正确地阅读了我的文章,那纯粹是代码风格:我们还可以利用新的词法作用域来模拟“私有”变量。它是“模拟”
Thomas Pons 2013年


Answers:


280

说明

您在这里得到了不同的东西:

第一:

  • 如果您使用服务,您将获得一个函数实例(“ this”关键字)。
  • 如果使用工厂,则将通过调用函数引用(工厂中的return语句)获得 返回的值

参考: angular.service与angular.factory

第二:

请记住,AngularJS中的所有提供程序(值,常量,服务,工厂)都是单例!

第三:

使用其中一种(服务或工厂)与代码风格有关。但是,AngularJS中的常用方法是使用factory

为什么呢

因为 “工厂方法是将对象放入AngularJS依赖项注入系统的最常用方法。它非常灵活并且可以包含复杂的创建逻辑。由于工厂是常规函数,因此我们还可以利用新的词法作用域来模拟”私有“变量。这非常有用,因为我们可以隐藏给定服务的实现细节。”

参考http//www.amazon.com/Mastering-Web-Application-Development-AngularJS/dp/1782161821)。


用法

Service:对于共享实用程序功能很有用,只需将其附加()到注入的函数引用中,即可调用这些实用程序功能。也可以与之injectedArg.call(this)类似地运行。

工厂:对于返回一个“类”函数很有用,然后可以对其进行新的创建实例。

因此,当您的服务中有复杂的逻辑并且不想暴露这种复杂性,请使用工厂

在其他情况下,如果要返回服务实例,请使用service

但是随着时间的推移,您会发​​现在我认为的80%的情况下,您将使用工厂。

有关更多详细信息:http : //blog.manishchhabra.com/2013/09/angularjs-service-vs-factory-with-example/


更新:

优秀的文章在这里:http : //iffycan.blogspot.com.ar/2013/05/angular-service-or-factory.html

“如果希望像普通函数一样调用函数,请使用 factory。如果希望使用new运算符实例化函数,请使用service。如果您不知道它们之间的区别,请使用factory。”


更新:

AngularJS小组会做他的工作并给出解释:http : //docs.angularjs.org/guide/providers

从此页面:

“ Factory和Service是最常用的配方。它们之间的唯一区别是Service配方对于自定义类型的对象更有效,而Factory可以生成JavaScript原语和函数。”


7
Re First:我到处都读过,但是我不理解它的实际含义。我想从您的回答来看,“在大多数情况下”没有实际区别吗?谢谢你的推荐书!
user1941747

如果您的服务真的很复杂并且您需要私有方法并且对象使用工厂,这很简单
Thomas Pons 2013年

1
我注意到您添加了“如果要返回服务的实例,请使用service”。我的后续问题是您何时想返回服务实例?我正在尝试在这里找到特定的用例。
user1941747

12
“由于工厂是常规函数,所以我们还可以利用新的词法作用域来模拟“私有”变量。” -这不是具体到工厂,你可以做同样的服务..
pootzko

似乎Google团队更喜欢服务而不是工厂,这使事情变得更加混乱!google-styleguide.googlecode.com/svn/trunk/...
xzhang

111

allernhwkim最初在链接到他的博客的问题上发布了答案,但主持人删除了该问题。这是我发现的唯一一则帖子,不仅告诉您如何使用服务,提供者和工厂做同样的事情,而且还告诉您可以与提供者一起做什么,而您不能与工厂一起使用,没有服务的工厂。

直接从他的博客:

app.service('CarService', function() {
   this.dealer="Bad";
    this.numCylinder = 4;
});

app.factory('CarFactory', function() {
    return function(numCylinder) {
      this.dealer="Bad";
        this.numCylinder = numCylinder
    };
});

app.provider('CarProvider', function() {
    this.dealerName = 'Bad';
    this.$get = function() {
        return function(numCylinder) {
            this.numCylinder = numCylinder;
            this.dealer = this.dealerName;
        }
    };
    this.setDealerName = function(str) {
      this.dealerName = str;
    }      
});

这显示了CarService如何始终生产具有4个气缸的汽车,而您不能为单个汽车更改它。而CarFactory返回一个函数,以便您可以new CarFactory在控制器中执行此操作,并传入该汽车专用的多个汽缸。您不能这样做,new CarService因为CarService是一个对象而不是一个函数。

工厂不能像这样工作的原因:

app.factory('CarFactory', function(numCylinder) {
      this.dealer="Bad";
      this.numCylinder = numCylinder
});

并自动返回一个函数供您实例化,是因为那样您将无法执行此操作(将内容添加到原型/ etc中):

app.factory('CarFactory', function() {
    function Car(numCylinder) {
        this.dealer="Bad";
        this.numCylinder = numCylinder
    };
    Car.prototype.breakCylinder = function() {
        this.numCylinder -= 1;
    };
    return Car;
});

看看它实际上是一家生产汽车的工厂。

他的博客的结论相当不错:

结论,

---------------------------------------------------  
| Provider| Singleton| Instantiable | Configurable|
---------------------------------------------------  
| Factory | Yes      | Yes          | No          |
---------------------------------------------------  
| Service | Yes      | No           | No          |
---------------------------------------------------  
| Provider| Yes      | Yes          | Yes         |       
---------------------------------------------------  
  1. 当您只需要一个简单的对象(例如哈希),例如{foo; 1,bar:2}时,可以使用Service。虽然很容易编写代码,但是无法实例化它。

  2. 在需要实例化对象(例如,新的Customer(),新的Comment()等)时,请使用Factory。

  3. 需要配置时使用提供程序。即测试网址,质量检查网址,生产网址。

如果发现自己只是在工厂中返回对象,则可能应该使用service。

不要这样做:

app.factory('CarFactory', function() {
    return {
        numCylinder: 4
    };
});

使用服务代替:

app.service('CarService', function() {
    this.numCylinder = 4;
});

11
对我很有帮助。+1为比较表
Vu Anh 2014年

5
如果您使用一个参数numCylinder定义服务功能,则它将具有与工厂方法相同的灵活性
Ovi,

去阅读博文,并浪费时间尝试弄清楚角度,如果您在阅读博文后知道javascript,您将完全理解这两者之间的区别。
ncubica 2015年

4
好惊讶 !您在这里指的是博客,而两者都在说完全相反的话。您说:工厂-可实例化-是博客说:工厂-可实例化-否
Devesh M

1
我同意@Devesh。我认为您已经混合了实例化。在博客文章中:“只有工厂,您无法实现此目标,因为工厂无法实例化”。
马特

20

所有这些提供程序的概念都比最初看起来要简单得多。如果解剖提供者,然后拉出不同部分,就会很清楚。

简单地说,这些供应商的每一个是另一个的专用版本,顺序如下:provider> factory> value/ constant/ service

只要提供者尽其所能,您就可以在链的更下游使用提供者,从而减少编写代码。如果它不能实现您想要的功能,则可以上链,您只需要编写更多代码即可。

此图像说明了我的意思,在此图像中,您将看到提供程序的代码,突出显示的部分向您显示可以使用提供程序的哪些部分来创建工厂,值等。

AngularJS提供者,工厂,服务等都是一回事
(来源:simplygoodcode.com

有关我从中获取图像的博客文章的更多详细信息和示例,请访问:http : //www.simplygoodcode.com/2015/11/the-difference-between-service-provider-and-factory-in-angularjs/


8

工厂和服务都产生单例对象,这些对象可以由提供者进行配置并注入到控制器和运行块中。从被注射者的角度来看,物体是来自工厂还是来自服务部门绝对没有区别。

那么,何时使用工厂,何时使用服务?归结为您的编码偏好,仅此而已。如果您喜欢模块化的JS模式,请前往工厂。如果您喜欢构造函数(“类”)样式,则可以使用该服务。请注意,两种样式都支持私有成员。

该服务的优势可能是从OOP角度来看更直观:创建一个“类”,并与提供者一起在模块中重用相同的代码,并通过提供以下内容来改变实例化对象的行为:配置块中的构造函数使用不同的参数。


您能否提供一个示例,说明在config块中向构造函数提供不同参数的含义?如果只是服务或工厂,如何提供参数。“与提供者一起”是什么意思?能够配置它使我认为我的许多对象应该是提供者与工厂或服务。
timbrown 2015年

2

与服务相比,工厂无所不能或做得更好。反之亦然。工厂似乎更受欢迎。这样做的原因是它在处理私人/公共成员方面很方便。在这方面服务将更加笨拙。在对服务进行编码时,您倾向于通过“ this”关键字将您的对象成员公开,并且可能突然发现那些私有成员对私有方法(即内部函数)不可见。

var Service = function(){

  //public
  this.age = 13;

  //private
  function getAge(){

    return this.age; //private does not see public

  }

  console.log("age: " + getAge());

};

var s = new Service(); //prints 'age: undefined'

Angular使用“ new”关键字为您创建服务,因此实例Angular传递给控制器​​将具有相同的缺点。当然,您可以使用this / that解决问题:

var Service = function(){

  var that = this;

  //public
  this.age = 13;

  //private
  function getAge(){

    return that.age;

  }

  console.log("age: " + getAge());

};

var s = new Service();// prints 'age: 13'  

但是,使用大的Service常数,可能会使代码的可读性差。此外,服务原型不会看到私有成员-只有公开成员可以使用:

var Service = function(){

  var name = "George";

};

Service.prototype.getName = function(){

  return this.name; //will not see a private member

};

var s = new Service();
console.log("name: " + s.getName());//prints 'name: undefined'

总结起来,使用Factory更为方便。由于Factory没有这些缺点。我建议默认使用它。


这个答案有几个问题。首先,这篇文章展示了Javascript的词法作用域概念,而不是AngularJS服务如何工作。其次,调用的上下文myapp.service(...)完全丢失。在new Service()服务功能中或在注入服务的位置应该被调用的位置。在的情况下,第三个清单完全不可能myapp.service ('Service', function() {...})
lanoxx

2

即使他们说所有服务和工厂都是单身人士,我也不同意这一点。我会说工厂不是单例,这就是我的答案。我真的会考虑定义每个组件(服务/工厂)的名称,我的意思是:

一个工厂因为不是单,你可以尽可能多的创造,只要你想,当你注入,所以它就像一个工厂对象。您可以创建您域的实体的工厂,并可以更舒适地使用该对象,这些对象可能像模型的对象。当您检索多个对象时,可以将它们映射到该对象中,并且可以充当DDBB和AngularJs模型之间的另一层。您可以向这些对象添加方法,从而使对象的角度比AngularJs App多一点。

同时,服务是单例的,因此我们只能创建一个类,也许不能创建,但是在注入控制器时只有一个实例,因此服务提供的功能更像是普通服务(剩余调用,功能..)给控制器。

从概念上讲,您可以认为服务可以提供服务,工厂可以创建一个类的多个实例(对象)


0

服务

语法:module.service('serviceName',function); 结果:将serviceName声明为可注入参数时,将提供传递给module.service的实际函数引用。

用法:对于共享实用程序函数很有用,只需将()附加到注入的函数引用上,即可调用这些实用程序函数。也可以使用injectedArg.call(this)或类似方法运行。

工厂工厂

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

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

用法:对于返回“类”函数很有用,然后可以对其进行新的创建实例。

提供者

语法:module.provider('providerName',function);

结果:当将providerName声明为可注入参数时,将通过调用传递给module.provider的函数引用的$ get方法为您提供返回的值。

用法:对于返回一个“类”函数很有用,该函数可以被新建以创建实例,但是在注入之前需要某种配置。对于在项目间可重用的类也许有用?对此仍然有些朦胧。


0

可以同时使用两种方式创建对象还是仅从两者访问函数


您可以从服务创建新对象

app.service('carservice', function() {
    this.model = function(){
        this.name = Math.random(22222);
        this.price = 1000;
        this.colour = 'green';
        this.manufacturer = 'bmw';
    }
});

.controller('carcontroller', function ($scope,carservice) { 
    $scope = new carservice.model();
})

注意 :

  • 服务默认返回对象而不是构造函数。
  • 这就是为什么将构造函数设置为this.model属性的原因。
  • 由于此服务将返回对象,但在该对象内部但将是构造函数,该函数将用于创建新对象;

您可以从工厂创建新对象

app.factory('carfactory', function() {
    var model = function(){
        this.name = Math.random(22222);
        this.price = 1000;
        this.colour = 'green';
        this.manufacturer = 'bmw';
    }
    return model;
});

.controller('carcontroller', function ($scope,carfactory) { 
    $scope = new carfactory();
})

注意 :

  • 默认情况下,factory返回构造函数而不是object。
  • 这就是为什么可以使用构造函数创建新对象的原因。

创建服务以仅访问简单功能

app.service('carservice', function () {
   this.createCar = function () {
       console.log('createCar');
   };
   this.deleteCar = function () {
       console.log('deleteCar');
   };
});

.controller('MyService', function ($scope,carservice) { 
    carservice.createCar()
})

创建工厂以仅访问简单功能

app.factory('carfactory', function () {
    var obj = {} 
        obj.createCar = function () {
            console.log('createCar');
        };
       obj.deleteCar = function () {
       console.log('deleteCar');
    };
});

.controller('MyService', function ($scope,carfactory) { 
    carfactory.createCar()
})

结论:

  • 您可以使用想要创建新对象的方式,也可以只是访问简单功能的方式
  • 不会有任何性能上的影响,一个使用另一个
  • 两者都是单例对象,每个应用程序仅创建一个实例。
  • 传递引用的每个地方只有一个实例。
  • 在角度文档中,工厂称为service服务也称为service

0

工厂和服务是最常用的方法。它们之间的唯一区别是,Service方法对于需要继承层次结构的对象更有效,而Factory可以生成JavaScript原语和函数。

Provider函数是核心方法,其他所有方法都只是语法糖。仅当您正在构建需要全局配置的可重用代码段时,才需要它。

有五种创建服务的方法:值,工厂,服务,提供者和常量。您可以在此处了解有关此角度服务的更多信息,本文将通过实际的演示示例说明所有这些方法。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.