3370

AngularJS 中的Service,Provider​​ 和有什么区别?Factory

4

30 回答 30

2891

从 AngularJS 邮件列表中,我得到了一个很棒的线程,它解释了服务、工厂、提供者以及它们的注入用法。编译答案:

服务

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

工厂

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

提供者

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

factory负责创造价值的功能也是如此。请注意,工厂函数可以请求其他依赖项。

但是如果你想要更多的面向对象并且有一个名为 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');
}

作为旁注,servicefactoryvalue都来自提供者。

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;
  });
};
于 2013-03-27T17:59:46.853 回答
818

JS 小提琴演示

“Hello world”示例与factory/ service/ provider

var myApp = angular.module('myApp', []);

//service style, probably the simplest one
myApp.service('helloWorldFromService', function() {
    this.sayHello = function() {
        return "Hello, World!";
    };
});

//factory style, more involved but more sophisticated
myApp.factory('helloWorldFromFactory', function() {
    return {
        sayHello: function() {
            return "Hello, World!";
        }
    };
});
    
//provider style, full blown, configurable version     
myApp.provider('helloWorld', function() {

    this.name = 'Default';

    this.$get = function() {
        var name = this.name;
        return {
            sayHello: function() {
                return "Hello, " + name + "!";
            }
        }
    };

    this.setName = function(name) {
        this.name = name;
    };
});

//hey, we can configure a provider!            
myApp.config(function(helloWorldProvider){
    helloWorldProvider.setName('World');
});
        

function MyCtrl($scope, helloWorld, helloWorldFromFactory, helloWorldFromService) {
    
    $scope.hellos = [
        helloWorld.sayHello(),
        helloWorldFromFactory.sayHello(),
        helloWorldFromService.sayHello()];
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="myApp">
<div ng-controller="MyCtrl">
    {{hellos}}
</div>
</body>

于 2013-07-30T10:20:42.027 回答
656

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)当您使用Service时,AngularJS 会在后台使用 '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;
  }
});



3) 提供者是您可以传递给 .config() 函数的唯一服务。如果您想在服务对象可用之前为其提供模块范围的配置,请使用提供程序。

app.controller(‘myProvider’, function($scope, myProvider){
  $scope.artist = myProvider.getArtist();
  $scope.data.thingFromConfig = myProvider.thingOnConfig;
});

app.provider(‘myProvider’, function(){
 //Only the next two lines are available in the app.config()
 this._artist = ‘’;
 this.thingFromConfig = ‘’;
  this.$get = function(){
    var that = this;
    return {
      getArtist: function(){
        return that._artist;
      },
      thingOnConfig: that.thingFromConfig
    }
  }
});

app.config(function(myProviderProvider){
  myProviderProvider.thingFromConfig = ‘This was set in config’;
});



非 TL;DR

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

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

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

现在让我们在回调函数中添加一些“私有”变量。这些将无法从控制器直接访问,但我们最终会在“服务”上设置一些 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 + ‘&amp;callback=JSON_CALLBACK’;
    return _finalUrl
  }

  return service;
});

在这里,您会注意到我们没有将这些变量/函数附加到“服务”。我们只是创建它们以便以后使用或修改它们。

  • baseUrl 是 iTunes API 需要的基本 URL
  • _artist 是我们要查找的艺术家
  • _finalUrl 是我们将调用 iTunes 的最终且完全构建的 URL
  • makeUrl 是一个函数,它将创建并返回我们的 iTunes 友好 URL。

现在我们的辅助/私有变量和函数已经到位,让我们为“服务”对象添加一些属性。我们在“服务”上放置的任何内容都可以直接在我们将“myFactory”传入的任何控制器中使用。

我们将创建简单地返回或设置艺术家的 setArtist 和 getArtist 方法。我们还将创建一个方法,该方法将使用我们创建的 URL 调用 iTunes API。此方法将返回一个承诺,一旦数据从 iTunes API 返回,该承诺将履行。如果您没有太多在 AngularJS 中使用 Promise 的经验,我强烈建议您深入了解它们。

下面的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”服务。然后,我们使用来自“myFactory”的数据在我们的 $scope 对象上设置属性。上面唯一棘手的代码是如果您以前从未处理过承诺。因为 callItunes 返回了一个承诺,所以我们可以使用 .then() 方法,并且只有在我们的承诺通过 iTunes 数据实现时才设置 $scope.data.artistData。你会注意到我们的控制器非常“瘦”(这是一个很好的编码实践)。我们所有的逻辑和持久数据都位于我们的服务中,而不是我们的控制器中。

2) 服务
在处理创建服务时,最重要的事情可能是它是用“new”关键字实例化的。对于您的 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 below line creates an object(obj) 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 中的真正作用,在 AngularJS 中创建服务应该更容易理解。

创建服务时要了解的最重要的事情是知道服务是使用“新”关键字实例化的。将这些知识与我们上面的示例相结合,您现在应该认识到您将直接将属性和方法附加到“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);
      })
  }
});

就像我之前提到的,一旦你真正理解了“new”的作用,Services 几乎与 AngularJS 中的工厂相同。

3) 提供者

关于 Providers 最重要的一点是,它们是您可以传递到应用程序的 app.config 部分的唯一服务。如果您需要在应用程序中的其他任何地方都可用之前更改服务对象的某些部分,那么这一点非常重要。尽管与服务/工厂非常相似,但我们将讨论一些差异。

首先,我们以与 Service 和 Factory 类似的方式设置 Provider。下面的变量是我们的“私有”和辅助函数。

app.provider('myProvider', function(){
   var baseUrl = 'https://itunes.apple.com/search?term=';
  var _artist = '';
  var _finalUrl = '';

  //Going to set this property on the config function below.
  this.thingFromConfig = ‘’;

  var makeUrl = function(){
    _artist = _artist.split(' ').join('+');
    _finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK'
    return _finalUrl;
  }
}

*再次,如果上述代码的任何部分令人困惑,请查看工厂部分,我会在其中详细解释它的作用。

您可以将 Providers 视为具有三个部分。第一部分是稍后将修改/设置的“私有”变量/函数(如上所示)。第二部分是将在您的 app.config 函数中可用的变量/函数,因此可以在它们在其他任何地方可用之前进行更改(也如上所示)。重要的是要注意这些变量需要附加到“this”关键字。在我们的示例中,只有“thingFromConfig”可以在 app.config 中进行更改。第三部分(如下所示)是当您将“myProvider”服务传递到特定控制器时控制器中可用的所有变量/函数。

使用 Provider 创建服务时,控制器中唯一可用的属性/方法是那些从 $get() 函数返回的属性/方法。下面的代码将 $get 放在“this”上(我们知道最终会从该函数返回)。现在,该 $get 函数返回我们希望在控制器中可用的所有方法/属性。这是一个代码示例。

this.$get = function($http, $q){
    return {
      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: function(artist){
        _artist = artist;
      },
      getArtist: function(){
        return _artist;
      },
      thingOnConfig: this.thingFromConfig
    }
  }

现在完整的 Provider 代码如下所示

app.provider('myProvider', function(){
  var baseUrl = 'https://itunes.apple.com/search?term=';
  var _artist = '';
  var _finalUrl = '';

  //Going to set this property on the config function below
  this.thingFromConfig = '';

  var makeUrl = function(){
    _artist = _artist.split(' ').join('+');
    _finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK'
    return _finalUrl;
  }

  this.$get = function($http, $q){
    return {
      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: function(artist){
        _artist = artist;
      },
      getArtist: function(){
        return _artist;
      },
      thingOnConfig: this.thingFromConfig
    }
  }
});

现在就像在我们的工厂和服务中一样,setArtist、getArtist 和 callItunes 将在我们将 myProvider 传递到的任何控制器中可用。这是 myProvider 控制器(与我们的工厂/服务控制器几乎完全相同)。

app.controller('myProviderCtrl', function($scope, myProvider){
  $scope.data = {};
  $scope.updateArtist = function(){
    myProvider.setArtist($scope.data.artist);
  };

  $scope.submitArtist = function(){
    myProvider.callItunes()
      .then(function(data){
        $scope.data.artistData = data;
      }, function(data){
        alert(data);
      })
  }

  $scope.data.thingFromConfig = myProvider.thingOnConfig;
});

如前所述,使用 Provider 创建服务的重点是能够在最终对象传递给应用程序的其余部分之前通过 app.config 函数更改一些变量。让我们看一个例子。

app.config(function(myProviderProvider){
  //Providers are the only service you can pass into app.config
  myProviderProvider.thingFromConfig = 'This sentence was set in app.config. Providers are the only service that can be passed into config. Check out the code to see how it works';
});

现在你可以看到 'thingFromConfig' 在我们的提供程序中是如何作为空字符串的,但是当它出现在 DOM 中时,它将是 'This sentence was set...'。

于 2014-05-15T15:53:00.907 回答
513

所有服务都是单例;它们在每个应用程序中被实例化一次。它们可以是任何类型,无论是原始类型、对象字面量、函数,甚至是自定义类型的实例。

valuefactoryservice和方法都是提供者constantprovider他们教注入器如何实例化服务。

最冗长但也是最全面的一个是 Provider 配方。其余四种配方类型——值、工厂、服务和常量——只是提供者配方之上的语法糖

  • Value Recipe是最简单的情况,您自己实例化 Service 并将实例化的值提供给注入器。
  • Factory recipe为 Injector 提供了一个工厂函数,它在需要实例化服务时调用该函数。调用时,工厂函数创建并返回服务实例。服务的依赖项作为函数的参数注入。所以使用这个配方增加了以下能力:
    • 能够使用其他服务(有依赖关系)
    • 服务初始化
    • 延迟/延迟初始化
  • Service recipe与Factory recipe 几乎相同,但这里 Injector使用 new 运算符而不是工厂函数调用构造函数。
  • Provider 配方通常是矫枉过正的。它允许您配置工厂的创建,从而增加了一层间接性。

    仅当您希望为应用程序范围的配置公开 API 时才应使用 Provider 配方,该 API 必须在应用程序启动之前进行。这通常只对可重用服务感兴趣,这些服务的行为可能需要在应用程序之间略有不同。

  • Constant 配方就像 Value 配方一样,只是它允许您定义在配置阶段可用的服务。比使用价值配方创建的服务更快。与 Values 不同,它们不能使用decorator.
请参阅提供程序文档

于 2013-12-24T13:15:49.647 回答
224

了解 AngularJS 工厂、服务和提供者

所有这些都用于共享可重用的单例对象。它有助于在您的应用程序/各种组件/模块之间共享可重用的代码。

从文档服务/工厂

  • 惰性实例化——Angular仅在应用程序组件依赖于它时实例化服务/工厂。
  • 单例——依赖于服务的每个组件都获得对服务工厂生成的单个实例的引用。

工厂

工厂是一个函数,您可以在其中在创建对象之前操作/添加逻辑,然后返回新创建的对象。

app.factory('MyFactory', function() {
    var serviceObj = {};
    //creating an object with methods/functions or variables
    serviceObj.myFunction = function() {
        //TO DO:
    };
    //return that object
    return serviceObj;
});

用法

它可以只是一个类的函数集合。因此,当您将它注入到您的控制器/工厂/指令函数中时,它可以在不同的控制器中实例化。每个应用程序仅实例化一次。

服务

只需在查看服务时考虑数组原型。服务是使用“new”关键字实例化新对象的函数。您可以使用this关键字向服务对象添加属性和功能。与工厂不同,它不返回任何东西(它返回一个包含方法/属性的对象)。

app.service('MyService', function() {
    //directly binding events to this context
    this.myServiceFunction = function() {
        //TO DO:
    };
});

用法

当您需要在整个应用程序中共享单个对象时使用它。例如,经过身份验证的用户详细信息、可共享的方法/数据、实用程序功能等。

提供者

提供者用于创建可配置的服务对象。您可以从配置功能配置服务设置。它使用$get()函数返回一个值。该$get函数在运行阶段以角度执行。

app.provider('configurableService', function() {
    var name = '';
    //this method can be be available at configuration time inside app.config.
    this.setName = function(newName) {
        name = newName;
    };
    this.$get = function() {
        var getName = function() {
             return name;
        };
        return {
            getName: getName //exposed object to where it gets injected.
        };
    };
});

用法

当您需要在使服务对象可用之前为其提供模块式配置时,例如。假设您想根据您的环境设置您的 API URL,例如devstageprod

笔记

只有提供者将在 Angular 的配置阶段可用,而服务和工厂则不可用。

希望这已经澄清了您对Factory、Service 和 Provider的理解。

于 2015-02-01T12:58:09.617 回答
190

对我来说,当我意识到它们都以相同的方式工作时,我得到了启示:运行一次,存储它们获得的值,然后在通过依赖注入引用时咳出相同的存储值

假设我们有:

app.factory('a', fn);
app.service('b', fn);
app.provider('c', fn);

三者的区别在于:

  1. a的储值来自于跑步fn
  2. b的储值来自newing fn
  3. c的存储值来自首先通过newing获取一个实例fn,然后运行$get该实例的一个方法。

这意味着在 AngularJS 中有类似缓存对象的东西,每次注入的值只分配一次,当它们第一次被注入时,以及在哪里:

cache.a = fn()
cache.b = new fn()
cache.c = (new fn()).$get()

这就是我们this在服务中使用并在提供者中定义 a的原因this.$get

于 2014-11-14T06:25:16.053 回答
137

服务 vs 供应商 vs 工厂:

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

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

什么是服务: 在 AngularJS 中,服务只不过是一个可以存储一些有用方法或属性的单例 JavaScript 对象。这个单例对象是基于 ngApp(Angular 应用程序)创建的,它在当前应用程序中的所有控制器之间共享。当 Angularjs 实例化一个服务对象时,它会为这个服务对象注册一个唯一的服务名称。所以每次我们需要服务实例时,Angular 都会在注册表中搜索这个服务名称,并返回对服务对象的引用。这样我们就可以在服务对象上调用方法、访问属性等。您可能会质疑是否也可以将属性、方法放在控制器的作用域对象上!那么为什么需要服务对象呢?答案是:服务在多个控制器范围之间共享。如果您将一些属性/方法放在控制器的作用域对象中,它将仅对当前作用域可用。

因此,如果存在三个控制器范围,分别是控制器 A、控制器 B 和控制器 C,它们都将共享同一个服务实例。

<div ng-controller='controllerA'>
    <!-- controllerA scope -->
</div>
<div ng-controller='controllerB'>
    <!-- controllerB scope -->
</div>
<div ng-controller='controllerC'>
    <!-- controllerC scope -->
</div>

如何创建服务?

AngularJS 提供了不同的方法来注册服务。这里我们将重点介绍三个方法 factory(..),service(..),provider(..);

使用此链接获取代码参考

工厂功能:

我们可以定义一个工厂函数如下。

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.Provider对象是使用我们在provider函数中定义的构造函数创建的。

var serviceProvider = new serviceProviderConstructor();

2.我们传入app.config()的函数,被执行。这称为配置阶段,在这里我们有机会自定义我们的服务。

configureService(serviceProvider);

3.最后调用serviceProvider的$get方法创建服务实例。

serviceInstance = serviceProvider.$get()

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

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

工作演示

概括:


工厂使用返回服务实例的工厂函数。 服务实例 = fnFactory();

服务使用构造函数,Angular 使用“new”关键字调用此构造函数来创建服务实例。 serviceInstance = new fnServiceConstructor();

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

于 2014-07-22T11:39:14.920 回答
88

正如这里的几个人正确指出的那样,工厂、提供者、服务,甚至价值和常数都是同一事物的版本。您可以将更一般的内容provider分解为所有内容。像这样:

在此处输入图像描述

这是这张图片来自的文章:

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

于 2015-11-19T13:36:14.010 回答
64

工厂

你给 AngularJS 一个函数,AngularJS 会在工厂被请求时缓存并注入返回值。

例子:

app.factory('factory', function() {
    var name = '';
    // Return value **is** the object that will be injected
    return {
        name: name;
    }
})

用法:

app.controller('ctrl', function($scope, factory) {
     $scope.name = factory.name;
});

服务

你给 AngularJS 一个函数,AngularJS 会调用new来实例化它。当请求服务时,AngularJS 创建的实例将被缓存和注入。由于new用于实例化服务,因此关键字this有效并引用实例。

例子:

app.service('service', function() {
     var name = '';
     this.setName = function(newName) {
         name = newName;
     }
     this.getName = function() {
         return name;
     }
});

用法:

app.controller('ctrl', function($scope, service) {
   $scope.name = service.getName();
});

提供者

你给 AngularJS 一个函数,AngularJS 会调用它的$get函数。它是$get函数的返回值,在请求服务时将被缓存和注入。

Providers 允许你在AngularJS 调用方法来获取注入之前配置提供程序。$get

例子:

app.provider('provider', function() {
     var name = '';
     this.setName = function(newName) {
          name = newName;
     }
     this.$get = function() {
         return {
            name: name
         }
     }
})

用法(作为控制器中的注射剂)

app.controller('ctrl', function($scope, provider) {
    $scope.name = provider.name;
});

用法(在$get调用之前配置提供程序以创建可注入)

app.config(function(providerProvider) {
    providerProvider.setName('John');
});
于 2014-08-02T05:37:39.273 回答
57

在与提供者玩耍时,我注意到了一些有趣的事情。

对于供应商而言,注射剂的可见性与对于服务和工厂的可见性不同。如果你声明一个 AngularJS“常量”(例如,myApp.constant('a', 'Robert');),你可以将它注入到服务、工厂和提供者中。

但是如果你声明一个 AngularJS 的“值”(例如,myApp.value('b', {name: 'Jones'});),你可以将它注入到服务和工厂中,但不能注入到提供者创建函数中。但是,您可以将其注入$get您为提供者定义的函数中。这在 AngularJS 文档中有所提及,但很容易忽略。您可以在 %provide 页面上的值和常量方法部分找到它。

http://jsfiddle.net/R2Frv/1/

<div ng-app="MyAppName">
    <div ng-controller="MyCtrl">
        <p>from Service: {{servGreet}}</p>
        <p>from Provider: {{provGreet}}</p>
    </div>
</div>
<script>
    var myApp = angular.module('MyAppName', []);

    myApp.constant('a', 'Robert');
    myApp.value('b', {name: 'Jones'});

    myApp.service('greetService', function(a,b) {
        this.greeter = 'Hi there, ' + a + ' ' + b.name;
    });

    myApp.provider('greetProvider', function(a) {
        this.firstName = a;
        this.$get = function(b) {
            this.lastName = b.name;
            this.fullName = this.firstName + ' ' + this.lastName;
            return this;
        };
    });

    function MyCtrl($scope, greetService, greetProvider) {
        $scope.servGreet = greetService.greeter;
        $scope.provGreet = greetProvider.fullName;
    }
</script>
于 2013-05-19T19:53:03.497 回答
46

这对新手来说是非常令人困惑的部分,我试图用简单的话来澄清它

AngularJS Service:用于与控制器中的服务引用共享实用程序功能。服务本质上是单例的,因此对于一项服务,仅在浏览器中创建一个实例,并且在整个页面中使用相同的引用。

在服务中,我们使用对象创建函数名称作为属性。

AngularJS Factory:Factory的目的也与 Service 相同,但是在这种情况下,我们创建一个新对象并添加函数作为该对象的属性,最后我们返回该对象。

AngularJS Provider:这个目的还是一样的,但是 Provider 给出了它的 $get 函数的输出。

定义和使用服务、工厂和提供者在http://www.dotnetfunda.com/articles/show/3156/difference-between-angularjs-service-factory-and-provider进行了解释

于 2015-10-09T03:43:21.683 回答
35

对我来说,理解差异的最好和最简单的方法是:

var service, factory;
service = factory = function(injection) {}

AngularJS 如何实例化特定组件(简化):

// service
var angularService = new service(injection);

// factory
var angularFactory = factory(injection);

所以,对于服务来说,成为AngularJS组件的是服务声明函数所代表的类的对象实例。对于工厂来说,就是工厂声明函数返回的结果。工厂的行为可能与服务相同:

var factoryAsService = function(injection) {
  return new function(injection) {
    // Service content
  }
}

最简单的思维方式如下:

  • 服务是一个单例对象实例。如果您想为代码提供单例对象,请使用服务。
  • 工厂是一个阶级。如果您想为您的代码提供自定义类,请使用工厂(不能通过服务完成,因为它们已经被实例化了)。

工厂“类”示例在评论中提供,以及提供者差异。

于 2014-04-30T11:20:20.343 回答
34

我对此事的澄清:

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

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

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

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

下面我的理解很简单。

工厂: 您只需在工厂内部创建一个对象并返回它。

服务:

你只有一个标准函数,它使用这个关键字来定义一个函数。

提供者:

您定义了一个$get对象,它可用于获取返回数据的对象。

于 2014-06-05T07:18:39.173 回答
26

来自Angular 文档的摘要:

  • 有五种配方类型定义了如何创建对象:ValueFactoryServiceProviderConstant
  • 工厂服务是最常用的配方。它们之间的唯一区别是Service配方更适用于自定义类型的对象,而Factory可以生成 JavaScript 原语和函数。
  • Provider recipe 是核心的recipe 类型,所有其他的只是它的语法糖。
  • Provider是最复杂的配方类型。除非您正在构建需要全局配置的可重用代码,否则您不需要它。

在此处输入图像描述


SO的最佳答案:

https://stackoverflow.com/a/26924234/165673 (<-- 好) https://stackoverflow.com/a/27263882/165673
https://stackoverflow.com/a/16566144/165673

于 2016-04-27T22:32:50.683 回答
22

所有好的答案已经。我想在ServiceFactory上再补充几点。以及服务/工厂之间的区别。人们还可能有以下问题:

  1. 我应该使用服务还是工厂?有什么不同?
  2. 他们做同样的事情还是有同样的行为?

让我们从 Service 和 factory 的区别开始:

  1. 两者都是单例:每当 Angular 第一次发现这些作为依赖项时,它都会创建一个服务/工厂的单个实例。创建实例后,将永远使用同一个实例。

  2. 可用于对具有行为的对象进行建模:它们都可以具有方法、内部状态变量等。尽管您编写该代码的方式会有所不同。

服务:

服务是一个构造函数,Angular 会通过调用 new 来实例化它yourServiceName()。这意味着几件事。

  1. 函数和实例变量将是this.
  2. 您不需要返回值。当 Angular 调用new yourServiceName() 时,它会接收到this包含您放置在其上的所有属性的对象。

示例:

angular.service('MyService', function() {
  this.aServiceVariable = "Ved Prakash"
  this.aServiceMethod = function() {
    return //code
  };
});

当 Angular 将此MyService服务注入依赖于它的控制器时,该控制器将获得一个MyService它可以调用函数的对象,例如 MyService.aServiceMethod ()。

小心this

由于构造的服务是一个对象,它里面的方法在被调用时可以引用这个:

angular.service('ScoreKeeper', function($http) {
  this.score = 0;

  this.getScore = function() {
    return this.score;
  };

  this.setScore = function(newScore) {
    this.score = newScore;
  };

  this.addOne = function() {
    this.score++;
  };
});

您可能很想调用ScoreKeeper.setScore一个承诺链,例如,如果您通过从服务器获取分数来初始化分数:$http.get('/score').then(ScoreKeeper.setScore).这样做的问题是ScoreKeeper.setScore它将被this绑定到调用null并且您会得到错误。更好的方法是$http.get('/score').then(ScoreKeeper.setScore.bind(ScoreKeeper))。无论你是否选择在你的服务方法中使用它,都要小心你如何调用它们。

从 a 返回一个值Service

由于 JavaScript 构造函数的工作方式,如果您(i.e., an Object)constructor函数返回一个复杂值,调用者将获得该 Object 而不是 this 实例。

这意味着您基本上可以从下面复制粘贴工厂示例,替换factoryservice,它会起作用:

angular.service('MyService', function($http) {
  var api = {};

  api.aServiceMethod= function() {
    return $http.get('/users');
  };
  return api;
});

所以当 Angular 用 new MyService() 构造你的服务时,它会得到那个 api 对象而不是 MyService 实例。

这是任何复杂值(对象、函数)的行为,但不适用于原始类型。

工厂:

工厂是一个简单的旧函数,它返回一个值。返回值是注入到依赖于工厂的东西中的东西。Angular 中一个典型的工厂模式是返回一个具有函数作为属性的对象,如下所示:

angular.factory('MyFactory', function($http) {
  var api = {};

  api.aFactoryMethod= function() {
    return $http.get('/users');
  };

  return api;
});

工厂依赖的注入值是工厂的返回值,它不一定是对象。它可能是一个函数

上述1和2问题的答案:

在大多数情况下,只要坚持使用工厂来处理所有事情。他们的行为更容易理解。是否返回值没有选择,而且如果你做错了也不会引入错误。

不过,当我谈论将它们作为依赖项注入时,我仍然将它们称为“服务”。

Service/Factory 的行为非常相似,有些人会说任何一个都可以。这有点正确,但我发现遵循 John Papa 风格指南的建议并坚持使用工厂更容易。**

于 2017-09-27T12:21:19.200 回答
17

另一个说明是工厂可以创建函数/基元,而服务不能。查看这个基于 Epokk 的 jsFiddle:http : //jsfiddle.net/skeller88/PxdSP/1351/

工厂返回一个可以调用的函数:

myApp.factory('helloWorldFromFactory', function() {
  return function() {
    return "Hello, World!";
  };
});

工厂还可以返回一个带有可调用方法的对象:

myApp.factory('helloWorldFromFactory', function() {
  return {
    sayHello: function() {
      return "Hello, World!";
    }
  };
});

该服务返回一个带有可以调用的方法的对象:

myApp.service('helloWorldFromService', function() {
  this.sayHello = function() {
     return "Hello, World!";
  };
});

有关更多详细信息,请参阅我写的关于差异的帖子:http: //www.shanemkeller.com/tldr-services-vs-factories-in-angular/

于 2014-04-30T17:20:23.413 回答
17

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

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

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

Service vs Factory部分已覆盖: https ://www.youtube.com/watch?v=BLzNCkPn3ao

服务实际上与关键字有关new,据我们所知,它做了 4 件事:

  1. 创建全新的对象
  2. 将其链接到其prototype对象
  3. 连接contextthis
  4. 并返回this

工厂是关于工厂模式的 - 包含返回类似服务的对象的函数。

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

这个简单/简短的视频:还涵盖了提供者https ://www.youtube.com/watch?v=HvTZbQ_hUZY (在那里你可以看到它们是如何从工厂到提供者的)

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

于 2015-06-23T00:04:37.310 回答
14

在阅读了所有这些帖子之后,它给我带来了更多的困惑..但仍然都是有价值的信息..最后我找到了下表,它可以提供简单比较的信息

  • 注入器使用配方创建两种类型的对象:服务和特殊用途对象
  • 有五种配方类型定义了如何创建对象:Value、Factory、Service、Provider 和 Constant。
  • 工厂和服务是最常用的配方。它们之间的唯一区别是 Service 配方更适用于自定义类型的对象,而 Factory 可以生成 JavaScript 原语和函数。
  • Provider recipe 是核心的 recipe 类型,所有其他的只是它的语法糖。
  • Provider 是最复杂的配方类型。除非您正在构建需要全局配置的可重用代码,否则您不需要它。
  • 除控制器外的所有特殊用途对象均通过工厂配方定义。

在此处输入图像描述

对于初学者来说:-这可能不正确的用例,但在高层次上,这就是这三个的用例。

  1. 如果您想在 Angular 模块中使用配置功能,则应将其创建为提供者

angular.module('myApp').config(function($testProvider){
$testProvider.someFunction();
})

  1. Ajax 调用或第三方集成需要服务
  2. 对于数据操作,将其创建为工厂

对于基本场景,工厂和服务的行为相同。

于 2017-05-11T09:32:07.317 回答
13

这是我为 AngularjS 中的对象工厂提供的一些代码模板。我以 Car/CarFactory 为例进行说明。在控制器中制作简单的实现代码。

     <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>

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

    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

;

于 2015-03-30T15:26:46.270 回答
12

使用此页面和文档作为参考(自上次查看以来似乎已经有了很大改进),我整理了以下真实(-ish)世界演示,它使用了 5 种提供程序中的 4 种;价值,恒定,工厂和成熟的供应商。

HTML:

<div ng-controller="mainCtrl as main">
    <h1>{{main.title}}*</h1>
    <h2>{{main.strapline}}</h2>
    <p>Earn {{main.earn}} per click</p>
    <p>You've earned {{main.earned}} by clicking!</p>
    <button ng-click="main.handleClick()">Click me to earn</button>
    <small>* Not actual money</small>
</div>

应用程序

var app = angular.module('angularProviders', []);

// A CONSTANT is not going to change
app.constant('range', 100);

// A VALUE could change, but probably / typically doesn't
app.value('title', 'Earn money by clicking');
app.value('strapline', 'Adventures in ng Providers');

// A simple FACTORY allows us to compute a value @ runtime.
// Furthermore, it can have other dependencies injected into it such
// as our range constant.
app.factory('random', function randomFactory(range) {
    // Get a random number within the range defined in our CONSTANT
    return Math.random() * range;
});

// A PROVIDER, must return a custom type which implements the functionality 
// provided by our service (see what I did there?).
// Here we define the constructor for the custom type the PROVIDER below will 
// instantiate and return.
var Money = function(locale) {

    // Depending on locale string set during config phase, we'll
    // use different symbols and positioning for any values we 
    // need to display as currency
    this.settings = {
        uk: {
            front: true,
            currency: '£',
            thousand: ',',
            decimal: '.'
        },
        eu: {
            front: false,
            currency: '€',
            thousand: '.',
            decimal: ','
        }
    };

    this.locale = locale;
};

// Return a monetary value with currency symbol and placement, and decimal 
// and thousand delimiters according to the locale set in the config phase.
Money.prototype.convertValue = function(value) {

    var settings = this.settings[this.locale],
        decimalIndex, converted;

    converted = this.addThousandSeparator(value.toFixed(2), settings.thousand);

    decimalIndex = converted.length - 3;

    converted = converted.substr(0, decimalIndex) +
        settings.decimal +
        converted.substr(decimalIndex + 1);    

    converted = settings.front ?
            settings.currency + converted : 
            converted + settings.currency; 

    return converted;   
};

// Add supplied thousand separator to supplied value
Money.prototype.addThousandSeparator = function(value, symbol) {
   return value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, symbol);
};

// PROVIDER is the core recipe type - VALUE, CONSTANT, SERVICE & FACTORY
// are all effectively syntactic sugar built on top of the PROVIDER construct
// One of the advantages of the PROVIDER is that we can configure it before the
// application starts (see config below).
app.provider('money', function MoneyProvider() {

    var locale;

    // Function called by the config to set up the provider
    this.setLocale = function(value) {
        locale = value;   
    };

    // All providers need to implement a $get method which returns
    // an instance of the custom class which constitutes the service
    this.$get = function moneyFactory() {
        return new Money(locale);
    };
});

// We can configure a PROVIDER on application initialisation.
app.config(['moneyProvider', function(moneyProvider) {
    moneyProvider.setLocale('uk');
    //moneyProvider.setLocale('eu'); 
}]);

// The ubiquitous controller
app.controller('mainCtrl', function($scope, title, strapline, random, money) {

    // Plain old VALUE(s)
    this.title = title;
    this.strapline = strapline;

    this.count = 0;

    // Compute values using our money provider    
    this.earn = money.convertValue(random); // random is computed @ runtime
    this.earned = money.convertValue(0);

    this.handleClick = function() { 
        this.count ++;
        this.earned = money.convertValue(random * this.count);
    };
});

工作演示

于 2015-01-30T20:04:12.223 回答
12

这个答案解决了主题/问题

Factory、Service 和 Constant — 如何只是提供程序配方之上的语法糖?

或者

工厂、服务和供应商在内部是如何相似的

基本上发生的事情是

当您将在第二个参数中提供的factory()它设置为提供者并返回它()时,您得到的只是除此之外没有属性/方法(意味着您无法配置它)function$getprovider(name, {$get:factoryFn })provider$getprovider

工厂源代码

function factory(name, factoryFn, enforce) {
    return provider(name, {
      $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
    });
};

service()它返回时,您提供了一个带有function注入的factory() constructor(返回您在服务中提供的构造函数的实例)并返回它

服务源代码

function service(name, constructor) {
    return factory(name, ['$injector', function($injector) {
      return $injector.instantiate(constructor);
    }]);
};

因此,基本上在这两种情况下,您最终都会为您提供的函数设置一个 providers $get ,但是您可以提供除 $get 之外的任何内容,因为您最初可以在 provider() 中为配置块提供

于 2015-04-13T22:55:00.233 回答
11

我知道很多很好的答案,但我必须分享我的使用经验
1.service对于大多数默认情况
2.factory用于创建特定实例的服务

// factory.js ////////////////////////////
(function() {
'use strict';
angular
    .module('myApp.services')
    .factory('xFactory', xFactoryImp);
xFactoryImp.$inject = ['$http'];

function xFactoryImp($http) {
    var fac = function (params) {
        this._params = params; // used for query params
    };

    fac.prototype.nextPage = function () {
        var url = "/_prc";

        $http.get(url, {params: this._params}).success(function(data){ ...
    }
    return fac;
}
})();

// service.js //////////////////////////
(function() {
'use strict';
angular
    .module('myApp.services')
    .service('xService', xServiceImp);
xServiceImp.$inject = ['$http'];

function xServiceImp($http) {  
    this._params = {'model': 'account','mode': 'list'};

    this.nextPage = function () {
        var url = "/_prc";

        $http.get(url, {params: this._params}).success(function(data){ ...
    }       
}
})();

并使用:

controller: ['xFactory', 'xService', function(xFactory, xService){

        // books = new instance of xFactory for query 'book' model
        var books = new xFactory({'model': 'book', 'mode': 'list'});

        // accounts = new instance of xFactory for query 'accounts' model
        var accounts = new xFactory({'model': 'account', 'mode': 'list'});

        // accounts2 = accounts variable
        var accounts2 = xService;
... 
于 2015-04-07T01:25:52.397 回答
10

晚会有点晚了。但我认为这对那些想学习(或清楚)使用工厂、服务和提供者方法开发 Angular JS 自定义服务的人更有帮助。

我看到了这个视频,它清楚地解释了开发 AngularJS 自定义服务的工厂、服务和提供者方法:

https://www.youtube.com/watch?v=oUXku28ex-M

源代码:http ://www.techcbt.com/Post/353/Angular-JS-basics/how-to-develop-angularjs-custom-service

此处发布的代码直接从上述来源复制,以使读者受益。

基于“工厂”的自定义服务的代码如下(同步和异步版本以及调用 http 服务):

var app = angular.module("app", []);
app.controller('emp', ['$scope', 'calcFactory',
  function($scope, calcFactory) {
    $scope.a = 10;
    $scope.b = 20;

    $scope.doSum = function() {
      //$scope.sum = calcFactory.getSum($scope.a, $scope.b); //synchronous
      calcFactory.getSum($scope.a, $scope.b, function(r) { //aynchronous
        $scope.sum = r;
      });
    };

  }
]);

app.factory('calcFactory', ['$http', '$log',
  function($http, $log) {
    $log.log("instantiating calcFactory..");
    var oCalcService = {};

    //oCalcService.getSum = function(a,b){
    //	return parseInt(a) + parseInt(b);
    //};

    //oCalcService.getSum = function(a, b, cb){
    //	var s = parseInt(a) + parseInt(b);
    //	cb(s);
    //};

    oCalcService.getSum = function(a, b, cb) { //using http service

      $http({
        url: 'http://localhost:4467/Sum?a=' + a + '&b=' + b,
        method: 'GET'
      }).then(function(resp) {
        $log.log(resp.data);
        cb(resp.data);
      }, function(resp) {
        $log.error("ERROR occurred");
      });
    };

    return oCalcService;
  }
]);

自定义服务的“服务”方法的代码(这与“工厂”非常相似,但从语法角度来看不同):

var app = angular.module("app", []);
app.controller('emp', ['$scope', 'calcService', function($scope, calcService){
	$scope.a = 10;
	$scope.b = 20;

	$scope.doSum = function(){
		//$scope.sum = calcService.getSum($scope.a, $scope.b);
		
		calcService.getSum($scope.a, $scope.b, function(r){
			$scope.sum = r;
		});		
	};

}]);

app.service('calcService', ['$http', '$log', function($http, $log){
	$log.log("instantiating calcService..");
	
	//this.getSum = function(a,b){
	//	return parseInt(a) + parseInt(b);
	//};

	//this.getSum = function(a, b, cb){
	//	var s = parseInt(a) + parseInt(b);
	//	cb(s);
	//};

	this.getSum = function(a, b, cb){
		$http({
			url: 'http://localhost:4467/Sum?a=' + a + '&b=' + b,
			method: 'GET'
		}).then(function(resp){
			$log.log(resp.data);
			cb(resp.data);
		},function(resp){
			$log.error("ERROR occurred");
		});
	};

}]);

自定义服务的“提供者”方法的代码(如果您想开发可以配置的服务,这是必要的):

var app = angular.module("app", []);
app.controller('emp', ['$scope', 'calcService', function($scope, calcService){
	$scope.a = 10;
	$scope.b = 20;

	$scope.doSum = function(){
		//$scope.sum = calcService.getSum($scope.a, $scope.b);
		
		calcService.getSum($scope.a, $scope.b, function(r){
			$scope.sum = r;
		});		
	};

}]);

app.provider('calcService', function(){

	var baseUrl = '';

	this.config = function(url){
		baseUrl = url;
	};

	this.$get = ['$log', '$http', function($log, $http){
		$log.log("instantiating calcService...")
		var oCalcService = {};

		//oCalcService.getSum = function(a,b){
		//	return parseInt(a) + parseInt(b);
		//};

		//oCalcService.getSum = function(a, b, cb){
		//	var s = parseInt(a) + parseInt(b);
		//	cb(s);	
		//};

		oCalcService.getSum = function(a, b, cb){

			$http({
				url: baseUrl + '/Sum?a=' + a + '&b=' + b,
				method: 'GET'
			}).then(function(resp){
				$log.log(resp.data);
				cb(resp.data);
			},function(resp){
				$log.error("ERROR occurred");
			});
		};		

		return oCalcService;
	}];

});

app.config(['calcServiceProvider', function(calcServiceProvider){
	calcServiceProvider.config("http://localhost:4467");
}]);

最后是与上述任何服务一起使用的 UI:

<html>
<head>
	<title></title>
	<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js" ></script>
	<script type="text/javascript" src="t03.js"></script>
</head>
<body ng-app="app">
	<div ng-controller="emp">
		<div>
			Value of a is {{a}},
			but you can change
			<input type=text ng-model="a" /> <br>

			Value of b is {{b}},
			but you can change
			<input type=text ng-model="b" /> <br>

		</div>
		Sum = {{sum}}<br>
		<button ng-click="doSum()">Calculate</button>
	</div>
</body>
</html>

于 2015-12-28T19:26:13.977 回答
10

只是为了澄清一下,从 AngularJS 源代码中,您可以看到一个服务只是调用了工厂函数,而工厂函数又调用了提供者函数:

function factory(name, factoryFn) { 
    return provider(name, { $get: factoryFn }); 
}

function service(name, constructor) {
    return factory(name, ['$injector', function($injector) {
      return $injector.instantiate(constructor);
    }]);
}
于 2016-01-21T20:44:58.963 回答
9

让我们以简单的方式讨论在 AngularJS 中处理业务逻辑的三种方式:(灵感来自 Yaakov 的 Coursera AngularJS 课程

服务

句法:

应用程序.js

 var app = angular.module('ServiceExample',[]);
 var serviceExampleController =
              app.controller('ServiceExampleController', ServiceExampleController);
 var serviceExample = app.service('NameOfTheService', NameOfTheService);

 ServiceExampleController.$inject = ['NameOfTheService'] //protects from minification of js files

function ServiceExampleController(NameOfTheService){
     serviceExampleController = this;
     serviceExampleController.data = NameOfTheService.getSomeData();
 }

function NameOfTheService(){
     nameOfTheService = this;
     nameOfTheService.data = "Some Data";
     nameOfTheService.getSomeData = function(){
           return nameOfTheService.data;
     }     
}

索引.html

<div ng-controller = "ServiceExampleController as serviceExample">
   {{serviceExample.data}}
</div>

服务特色:

  1. 惰性实例化:如果它没有被注入,它永远不会被实例化。所以要使用它必须将它注入一个模块。
  2. Singleton:如果注入到多个模块中,所有模块都只能访问一个特定的实例。这就是为什么在不同控制器之间共享数据非常方便的原因。

工厂

首先让我们看一下语法:

应用程序.js

var app = angular.module('FactoryExample',[]);
var factoryController = app.controller('FactoryController', FactoryController);
var factoryExampleOne = app.factory('NameOfTheFactoryOne', NameOfTheFactoryOne);
var factoryExampleTwo = app.factory('NameOfTheFactoryTwo', NameOfTheFactoryTwo);

//first implementation where it returns a function
function NameOfTheFactoryOne(){
   var factory = function(){
      return new SomeService();
    }
   return factory;
}

//second implementation where an object literal would be returned
function NameOfTheFactoryTwo(){
   var factory = {
      getSomeService : function(){
          return new SomeService();
       }
    };
   return factory;
}

现在在控制器中使用上述两个:

 var factoryOne = NameOfTheFactoryOne() //since it returns a function
 factoryOne.someMethod();

 var factoryTwo = NameOfTheFactoryTwo.getSomeService(); //accessing the object
 factoryTwo.someMethod();

工厂特点:

  1. 遵循工厂设计模式。工厂是生产新物品或功能的中心场所。
  2. 不仅产生单例,而且产生可定制的服务。
  3. .service()方法是一个总是产生相同类型的服务的工厂,它是一个单例,并且没有任何简单的方法来配置它的行为。该.service()方法通常用作不需要任何配置的快捷方式。

提供者

让我们先看一下语法:

angular.module('ProviderModule', [])
.controller('ProviderModuleController', ProviderModuleController)
.provider('ServiceProvider', ServiceProvider)
.config(Config); //optional

Config.$inject = ['ServiceProvider'];
function Config(ServiceProvider) {
  ServiceProvider.defaults.maxItems = 10; //some default value
}


ProviderModuleController.$inject = ['ServiceProvider'];
function ProviderModuleController(ServiceProvider) {
  //some methods
}

function ServiceProvider() {
  var provider = this;

  provider.defaults = {
    maxItems: 10
  };

  provider.$get = function () {
    var someList = new someListService(provider.defaults.maxItems);

    return someList;
  };
}

}

提供者的特点:

  1. Provider 是在 Angular 中创建服务的最灵活的方法。
  2. 我们不仅可以创建一个可动态配置的工厂,而且在使用工厂时,通过提供者方法,我们可以在整个应用程序的引导时自定义配置工厂一次。
  3. 然后可以通过自定义设置在整个应用程序中使用工厂。换句话说,我们可以在应用程序启动之前配置这个工厂。.service事实上,在 Angular 文档中提到,当我们使用or方法配置我们的服务时,提供者方法实际上是在幕后执行的.factory
  4. $get是一个直接附加到提供程序实例的函数。该函数是工厂函数。换句话说,它就像我们用来提供.factory方法的那个。在该函数中,我们创建自己的服务。这个$get属性,也就是一个函数,使提供者成为提供者AngularJS 期望提供者有一个 $get 属性,它的值是 Angular 将作为工厂函数处理的函数。但是,使整个提供程序设置非常特别的是,我们可以config在服务提供程序内部提供一些对象,并且通常带有默认值,我们可以稍后在步骤中覆盖这些默认值,我们可以在其中配置整个应用程序。
于 2016-12-09T07:27:50.113 回答
7

工厂:您实际上在工厂内部创建一个对象并返回它的工厂。
服务:您只有一个标准功能的服务,它使用 this 关键字来定义功能。
provider:提供者有一个你定义的$get,它可以用来获取返回数据的对象。

于 2015-03-04T11:12:38.417 回答
7

本质上,提供者、工厂和服务都是服务。当您只需要一个 $get() 函数时,工厂是服务的一种特殊情况,允许您用更少的代码编写它。

服务、工厂和供应商之间的主要区别在于它们的复杂性。服务是最简单的形式,工厂更健壮一些,提供者在运行时是可配置的。

以下是何时使用每种方法的摘要:

工厂:您提供的值需要根据其他数据计算。

服务:您正在返回一个带有方法的对象。

提供者:您希望能够在配置阶段配置要在创建之前创建的对象。在应用程序完全初始化之前,主要在应用程序配置中使用 Provider。

于 2016-12-28T11:41:07.937 回答
4

1.服务是在必要时创建的单例对象,并且在应用程序生命周期结束(浏览器关闭时)之前永远不会被清理。当不再需要控制器时,它们会被销毁和清理。

2.创建服务最简单的方法是使用 factory() 方法。factory() 方法允许我们通过返回包含服务功能和服务数据的对象来定义服务。服务定义函数是我们放置可注入服务的地方,例如 $http 和 $q。前任:

angular.module('myApp.services')
.factory('User', function($http) { // injectables go here
var backendUrl = "http://localhost:3000"; var service = {
    // our factory definition
user: {},
setName: function(newName) {
      service.user['name'] = newName;
    },
setEmail: function(newEmail) { service.user['email'] = newEmail;
},
save: function() {
return $http.post(backendUrl + '/users', { user: service.user
}); }
};
return service; });

在我们的应用程序中使用 factory()

在我们的应用程序中使用工厂很容易,因为我们可以在运行时将它简单地注入到我们需要的地方。

angular.module('myApp')
.controller('MainController', function($scope, User) {
  $scope.saveUser = User.save;
});
  1. 另一方面,service() 方法允许我们通过定义构造函数来创建服务。我们可以使用原型对象来定义我们的服务,而不是原始的 javascript 对象。与 factory() 方法类似,我们还将在函数定义中设置可注入对象。
  2. 创建服务的最低级别方法是使用 provide() 方法。这是创建可以使用 .config() 函数配置的服务的唯一方法。与前面的 to 方法不同,我们将在定义的 this.$get() 函数定义中设置注射剂。
于 2016-10-30T23:39:31.173 回答
-3

语法糖就是区别。只需要提供者。或者换句话说,只有提供者是真正的角度,所有其他的都是派生的(以减少代码)。还有一个简单的版本,称为 Value(),它只返回值,不返回计算或函数。甚至 Value 都来源于 provider!

那么为什么会有这样的复杂性,为什么我们不能只使用提供者而忘记其他一切呢?它应该可以帮助我们轻松编写代码并更好地沟通。诙谐的回答是,它变得越复杂,框架的销售就会越好。


  • 可以返回值的提供者 = 值
  • 一个可以实例化并返回的提供者 = Factory (+ Value)
  • 可以实例化+做某事的提供者=服务(+工厂,+价值)
  • 提供者 = 必须包含一个名为 $get (+Factory, + Service, + Value) 的属性

Angular 注入给了我们得出这个结论的第一个提示。

“$injector 用于检索提供者定义的对象实例”不是服务,不是工厂,而是提供者。

更好的答案是:“Angular 服务是由服务工厂创建的。这些服务工厂是由服务提供者创建的函数。服务提供者是构造函数。实例化时,它们必须包含一个属性称为 $get,它拥有服务工厂功能。”

所以主提供者和注入器,一切都会到位:)。当 $get 可以通过从 IServiceProvider 继承在提供程序中实现时,Typescript 会变得很有趣。

于 2016-04-03T19:50:08.663 回答