AngularJS中的服务、提供商和工厂之间有什么区别?


当前回答

下面是我为AngularjS中的对象工厂设计的一些烧烤代码模板。我用汽车/汽车工厂作为例子来说明。在控制器中生成简单的实现代码。

     <script>
        angular.module('app', [])
            .factory('CarFactory', function() {

                /**
                 * BroilerPlate Object Instance Factory Definition / Example
                 */
                this.Car = function() {

                    // initialize instance properties
                    angular.extend(this, {
                        color           : null,
                        numberOfDoors   : null,
                        hasFancyRadio   : null,
                        hasLeatherSeats : null
                    });

                    // generic setter (with optional default value)
                    this.set = function(key, value, defaultValue, allowUndefined) {

                        // by default,
                        if (typeof allowUndefined === 'undefined') {
                            // we don't allow setter to accept "undefined" as a value
                            allowUndefined = false;
                        }
                        // if we do not allow undefined values, and..
                        if (!allowUndefined) {
                            // if an undefined value was passed in
                            if (value === undefined) {
                                // and a default value was specified
                                if (defaultValue !== undefined) {
                                    // use the specified default value
                                    value = defaultValue;
                                } else {
                                    // otherwise use the class.prototype.defaults value
                                    value = this.defaults[key];
                                } // end if/else
                            } // end if
                        } // end if

                        // update 
                        this[key] = value;

                        // return reference to this object (fluent)
                        return this;

                    }; // end this.set()

                }; // end this.Car class definition

                // instance properties default values
                this.Car.prototype.defaults = {
                    color: 'yellow',
                    numberOfDoors: 2,
                    hasLeatherSeats: null,
                    hasFancyRadio: false
                };

                // instance factory method / constructor
                this.Car.prototype.instance = function(params) {
                    return new 
                        this.constructor()
                                .set('color',           params.color)
                                .set('numberOfDoors',   params.numberOfDoors)
                                .set('hasFancyRadio',   params.hasFancyRadio)
                                .set('hasLeatherSeats', params.hasLeatherSeats)
                    ;
                };

                return new this.Car();

            }) // end Factory Definition
            .controller('testCtrl', function($scope, CarFactory) {

                window.testCtrl = $scope;

                // first car, is red, uses class default for:
                // numberOfDoors, and hasLeatherSeats
                $scope.car1     = CarFactory
                                    .instance({
                                        color: 'red'
                                    })
                                ;

                // second car, is blue, has 3 doors, 
                // uses class default for hasLeatherSeats
                $scope.car2     = CarFactory
                                    .instance({
                                        color: 'blue',
                                        numberOfDoors: 3
                                    })
                                ;
                // third car, has 4 doors, uses class default for 
                // color and hasLeatherSeats
                $scope.car3     = CarFactory
                                    .instance({
                                        numberOfDoors: 4
                                    })
                                ;
                // sets an undefined variable for 'hasFancyRadio',
                // explicitly defines "true" as default when value is undefined
                $scope.hasFancyRadio = undefined;
                $scope.car3.set('hasFancyRadio', $scope.hasFancyRadio, true);

                // fourth car, purple, 4 doors,
                // uses class default for hasLeatherSeats
                $scope.car4     = CarFactory
                                    .instance({
                                        color: 'purple',
                                        numberOfDoors: 4
                                    });
                // and then explicitly sets hasLeatherSeats to undefined
                $scope.hasLeatherSeats = undefined;
                $scope.car4.set('hasLeatherSeats', $scope.hasLeatherSeats, undefined, true);

                // in console, type window.testCtrl to see the resulting objects

            });
    </script>

这里有一个更简单的例子。我使用的是一些第三方库,它们期望“Position”对象显示纬度和经度,但通过不同的对象财产。我不想破解供应商代码,所以我调整了我传递的“位置”对象。

    angular.module('app')
.factory('PositionFactory', function() {

    /**
     * BroilerPlate Object Instance Factory Definition / Example
     */
    this.Position = function() {

        // initialize instance properties 
        // (multiple properties to satisfy multiple external interface contracts)
        angular.extend(this, {
            lat         : null,
            lon         : null,
            latitude    : null,
            longitude   : null,
            coords: {
                latitude: null,
                longitude: null
            }
        });

        this.setLatitude = function(latitude) {
            this.latitude           = latitude;
            this.lat                = latitude;
            this.coords.latitude    = latitude;
            return this;
        };
        this.setLongitude = function(longitude) {
            this.longitude          = longitude;
            this.lon                = longitude;
            this.coords.longitude   = longitude;
            return this;
        };

    }; // end class definition

    // instance factory method / constructor
    this.Position.prototype.instance = function(params) {
        return new 
            this.constructor()
                    .setLatitude(params.latitude)
                    .setLongitude(params.longitude)
        ;
    };

    return new this.Position();

}) // end Factory Definition

.controller('testCtrl', function($scope, PositionFactory) {
    $scope.position1 = PositionFactory.instance({latitude: 39, longitude: 42.3123});
    $scope.position2 = PositionFactory.instance({latitude: 39, longitude: 42.3333});
}) // end controller

;

其他回答

我对此事的澄清:

基本上,所有提到的类型(服务、工厂、提供者等)都只是创建和配置全局变量(当然,这些变量对整个应用程序是全局的),就像老式的全局变量一样。

虽然不建议使用全局变量,但这些全局变量的实际用途是通过将变量传递给相关控制器来提供依赖注入。

在为“全局变量”创建值时有许多复杂程度:

常数这定义了一个在整个应用程序中不应修改的实际常量,就像其他语言中的常量一样(JavaScript缺少的)。价值这是一个可修改的值或对象,它充当一些全局变量,甚至可以在创建其他服务或工厂时注入(请参阅有关这些的详细信息)。然而,它必须是一个“文字值”,这意味着必须写出实际值,并且不能使用任何计算或编程逻辑(换句话说,39或myText或{prop:“value”}可以,但2+2不行)。工厂一个更一般的值,可以立即计算。它的工作原理是将一个函数传递给AngularJS,其中包含计算值所需的逻辑,AngularJS执行该函数,并将返回值保存在命名变量中。请注意,可以返回对象(在这种情况下,它的功能类似于服务)或函数(将作为回调函数保存在变量中)。服务服务是工厂的更精简版本,只有当值是对象时才有效,它允许直接在函数中写入任何逻辑(就像它是构造函数一样),以及使用this关键字声明和访问对象财产。供应商与工厂的简化版本的服务不同,提供程序是一种更复杂但更灵活的初始化“全局”变量的方法,最大的灵活性是从app.config中设置值的选项。它的工作方式类似于使用服务和提供者的组合,方法是向提供者传递一个函数,该函数具有使用this关键字声明的财产,该关键字可以从app.config中使用。然后它需要有一个单独的$.get函数,该函数由AngularJS在通过app.config文件设置上述财产后执行,该$.get功能的行为与上述工厂相同,因为它的返回值用于初始化“全局”变量。

工厂:您实际在工厂内部创建对象并将其返回的工厂。service:您只有一个使用this关键字定义函数的标准函数的服务。provider:您定义了一个$get,它可以用来获取返回数据的对象。

服务与供应商与工厂:

我试图保持简单。这都是关于JavaScript的基本概念。

首先,我们来谈谈AngularJS中的服务!

什么是服务:在AngularJS中,Service只是一个单独的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('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()

使用provide语法创建服务的示例代码:

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=新的fnServiceConstructor();

Provider定义了providerConstructor函数,此providerConstructer函数定义了工厂函数$get。Angular调用$get()来创建服务对象。提供程序语法还有一个额外的优点,即在服务对象实例化之前对其进行配置。serviceInstance=$get();

从AngularJS邮件列表中,我得到了一个令人惊叹的线程,它解释了服务与工厂与供应商以及它们的注入用法。编写答案:

服务

语法:module.service('serviceName',function);结果:当将serviceName声明为可注入参数时,将为您提供函数的实例。换句话说,新的FunctionYouPassedToService()。

工厂

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

提供商

语法:module.provider('providerName',function);结果:当将providerName声明为可注入参数时,将提供(newProviderFunction())$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);
}

因此,工厂是一个负责创造价值的职能部门。注意,工厂函数可以要求其他依赖项。

但是,如果你想成为更多的OO,并拥有一个名为Greeter的类呢?

function Greeter(a) {
  this.greet = function() {
    return 'Hello ' + a;
  }
}

然后,要实例化,必须编写

provide.factory('greeter', function(a) {
  return new Greeter(a);
});

然后我们可以像这样在控制器中请求“问候者”

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

顺便说一句,服务、工厂和价值都来自提供者。

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

已经有好的答案了,但我只想分享这个答案。

首先:Provider是创建服务(单例对象)的方法/方法,该服务假设由$injector注入(AngulaJS如何处理IoC模式)。

以及价值、工厂、服务和常量(4种方式)——相对于提供者方式/接收器的语法糖。

服务与工厂部分已涵盖:https://www.youtube.com/watch?v=BLzNCkPn3ao

服务都是关于新关键字的,实际上我们知道它有四个方面:

创建全新对象将其链接到其原型对象将上下文连接到此并返回此

工厂就是工厂模式的全部内容——包含返回类似服务的对象的函数。

使用其他服务的能力(具有依赖关系)服务初始化延迟/延迟初始化

这个简单/简短的视频:还包括提供商:https://www.youtube.com/watch?v=HvTZbQ_hUZY(在那里你可以看到他们是如何从工厂走向供应商的)

在应用程序完全启动/初始化之前,主要在应用程序配置中使用提供程序配方。