AngularJS:服务 vs 提供程序 vs 工厂

AngularJS 中的ServiceProviderFactory有什么区别?

答案

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

但这太罗 y 了。 provider.service('greeter', Greeter);可以写为一个简短的方法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 派生的。

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

JS 小提琴演示

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

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>

TL; DR

1)使用Factory 时,您将创建一个对象,向其添加属性,然后返回该对象。当您将此工厂传递到控制器中时,对象上的那些属性现在将通过您的工厂在该控制器中可用。

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 + ‘&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 中使用诺言没有太多经验,我强烈建议您对它们进行深入研究。

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 中创建服务应该更容易理解。

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

});

现在就像在我们的工厂中一样,无论将 myService 传递给哪个控制器,都可以使用 setArtist,getArtist 和 callItunes。这是 myService 控制器(与我们的工厂控制器几乎完全相同)。

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

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

就像我之前提到的,一旦您真正了解了 “新” 功能,服务就几乎与 AngularJS 中的工厂相同。

3)提供者

关于提供程序,要记住的最大事情是,它们是您可以传递到应用程序的 app.config 部分中的唯一服务。如果您需要先更改服务对象的某些部分,然后再在应用程序中的其他任何地方使用它,则此功能至关重要。尽管与服务 / 工厂非常相似,但是我们将讨论一些区别。

首先,我们以与服务和工厂类似的方式设置提供商。下面的变量是我们的 “私有” 和助手功能。

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

* 如果上面的代码中的任何部分令人困惑,请查看 “工厂” 部分,我在其中解释了它们的全部细节。

您可以将提供者分为三个部分。第一部分是 “私有” 变量 / 函数,稍后将对其进行修改 / 设置(如上所示)。第二部分是变量 / 函数,这些变量 / 函数将在您的 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
    }
  }

现在完整的提供程序代码如下所示

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

现在就像在我们的工厂和服务中一样,无论将 myProvider 传递给哪个控制器,都可以使用 setArtist,getArtist 和 callItunes。这是 myProvider 控制器(与我们的 factory / Service 控制器几乎完全相同)。

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 中时,它将是 '此句子已设置...'。

所有服务都是单身人士 ; 每个应用实例化一次。它们可以是任何类型 ,无论是原语,对象文字,函数,还是自定义类型的实例。

valuefactoryserviceconstantprovider方法都是提供程序。他们教喷油器如何实例化服务。

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

  • 值配方是最简单的情况,您可以自己实例化 “服务” 并将实例化的值提供给注射器。
  • Factory 配方为 Injector 提供了一个需要实例化服务的工厂函数。调用时, 工厂函数将创建并返回服务实例。服务的依赖项作为函数的参数注入。因此,使用此配方可添加以下功能:
    • 能够使用其他服务(具有依赖性)
    • 服务初始化
    • 延迟 / 延迟初始化
  • Service 配方与 Factory 配方几乎相同,但是在这里 Injector 使用 new 运算符而不是 factory 函数调用构造函数。
  • 提供者的配方通常是多余的 。通过允许您配置工厂的创建,它又增加了一层间接。

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

  • 常量配方类似于值配方,不同之处在于它允许您定义配置阶段可用的服务。比使用 “价值” 配方创建的服务要早。与值不同,它们不能使用decorator
请参阅提供商文档

了解 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.
        };
    };
});

用法

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

注意

在角度配置阶段仅提供者可用,而服务和工厂则不可用。

希望这能清除您对Factory,Service 和 Provider 的了解。

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

说我们有:

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

两者之间的区别在于:

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

这意味着在 AngularJS 中有一个类似于缓存对象的东西,每次注入的值仅在第一次注入时才分配一次,其中:

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

这就是为什么我们在服务中使用this并在提供程序中定义this.$get的原因。

服务 vs 提供者 vs 工厂:

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

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

什么是服务:在 AngularJS 中, 服务不过是一个单例 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(..),service(..),provider(..);

使用此链接作为代码参考

工厂功能:

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

factory('serviceName',function fnFactory(){ return serviceInstance;})

AngularJS 提供了'factory('serviceName',fnFactory)'方法,该方法带有两个参数,serviceName 和一个 JavaScript 函数。 Angular 通过调用如下函数fnFactory()创建服务实例。

var serviceInstace = fnFactory();

传递的函数可以定义一个对象并返回该对象。 AngularJS 只是将该对象引用存储到作为第一个参数传递的变量中。从 fnFactory 返回的任何内容都将绑定到 serviceInstance。除了返回 object 之外,我们还可以返回函数,值等,无论我们将返回什么,它将对服务实例可用。

例:

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

使用 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 函数,此 providerConstructor 函数定义了工厂函数$ get 。 Angular 调用 $ get()创建服务对象。提供程序语法具有在实例化服务对象之前对其进行配置的另一个优点。 serviceInstance = $ get();

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

在此处输入图片说明

这是该图像的来源文章:

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

您为 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函数的返回值将在请求服务时进行缓存和注入。

提供程序允许您 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');
});

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

供应商的注射剂可见性与服务和工厂的可见性不同。如果您声明 AngularJS 为 “常量”(例如, myApp.constant('a', 'Robert'); ),则可以将其注入服务,工厂和提供程序。

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

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>