深入浅出AngularJs中的Service

学习AngularJ是有一个多月了,感觉它确实是一个不错的前端框架。用起来确实舒服多了。如果你有后台开发的思维,并精通Javascripts的话,那理解起来就更是轻松多了。控制器,指令,路由,服务,依赖注入这些在用服务器端程序语言进行开发时,在架构分层中那都是些耳熟能详的概念。AngularJs的核心:双向绑定。在WPF,Silverlight中MVVM模式中那也是很不错的东西。在前端框架Knockout MVVM中,也有经典的展现。所以,感觉Web发展的趋势最终会是前端后端的设计语言是一样的,至少思想是一样的。向Javascript的EM6版本中的设计,就在像Java的设计思想靠近。扯远了,回归正题。下面引入两篇文章助你理解Angular中的Service的概念。这是个很强大的东西。但是,化繁为简,我们慢慢来理解。说明两篇文章均是老外写的文章,由高手翻译而成,个人感觉很不错,引用在此。感谢原作者。

第一篇:

Angular中有几种不同类型的services。每一种都有自己的独特用法。

需要记住的非常重要的一点是service总是一个单体,无论是哪种类型的service。

注释:单体是一种设计模式,它限制了每一个类仅能够实例化为一个对象。无论我们在什么地方注入我们的service,将永远使用同一个实例。

Constant

例子:

app.constant('fooConfig',{
    config1: true,
    config2: "Default config2"
});   

Constant是一个非常有用的service,它经常被用来在指令中提供默认配置。因此如果你正在创建一个指令,并且你想要在给指令传递可选参数的同时进行一个默认配置,一个Constant就是一个好办法。

作为一个constant,我们放入其中的值将不会改变。Contant service 基本上回事一个基本类型的值或者是一个对象。

Value

例子:

app.value('fooConfig',{
    config1: true,
    config2: "Default config2 but it can change"
});   

一个value service有点像是一个constant但是它是可以被改变的。它也经常被用在一个指令上面,来进行配置。一个value service有点像是一个factory service的缩小版,它经常用来保存值但是我们不能在其中对值进行计算。

我们可以使用angular对象的extend方法来改变一个value service:

app = angular.module("app", []);

app.controller('MainCtrl', function($scope, fooConfig) {
  $scope.fooConfig = fooConfig;
  angular.extend(fooConfig, {config3: "I have been extended"});
});

app.value('fooConfig', {
  config1: true,
  config2: "Default config2 but it can changes"
});   

Factory

例子:

app.factory('foo', function() {
  var thisIsPrivate = "Private";
  function getPrivate() {
    return thisIsPrivate;
  }

  return {
    variable: "This is public",
    getPrivate: getPrivate
  };
});

// or..

app.factory('bar', function(a) {
  return a * 2;
});    

Factory service是最普遍使用的service。它同样也非常容易理解。

一个Factory是一个能够返回任何数据类型的service。对于你如何创建它并没有什么可选项,你仅仅需要在其中返回一些东西即可。

正如前面所说的,所有的service类型都是单体,因此如果我们在一个地方修改了foo.variable,其他的地方也会相应的发生改变。

Service

例子:

app.service('foo', function() {
  var thisIsPrivate = "Private";
  this.variable = "This is public";
  this.getPrivate = function() {
    return thisIsPrivate;
  };
});   

Service service 和 factory差不多。它们之间的区别在于service会接收一个构造器,因此当你第一次使用它的时候,它将会自动运行new Foo()来实例化一个对象。一定要记住如果你在其他的地方也使用了这个service,它将返回同一个对象。

事实上,上面的代码和下面的代码等价:

app.factory('foo2', function() {
  return new Foobar();
});


function Foobar() {
  var thisIsPrivate = "Private";
  this.variable = "This is public";
  this.getPrivate = function() {
    return thisIsPrivate;
  };
}   

Foobar是一个类,我们在首次使用它的时候在我们的factory中将它实例化然后将它返回。和service一样,Foobar将只会实例化一次然后下次当我们再次使用factory时它将返回同一个实例。

如果我们已经有了一个类,并且我们想将它用在service中,我们只需要编写如下的代码:

app.service('foo3',Foobar);   

Provider

Provider是factory的加强版。事实上,上一个例子中的factory代码等价于下面的provider代码:

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

  return {

    $get: function() {
      var thisIsPrivate = "Private";
      function getPrivate() {
        return thisIsPrivate;
      }

      return {
        variable: "This is public",
        getPrivate: getPrivate
      };
    }

  };

});   

一个provider中应当由一个$get函数,其中的内容就是我们想要注入我们应用中的部分,因此当我们将foo注入一个控制器时,我们实际上注入的是$get函数。

既然factory如此简单,那我们为什么还要使用provider呢?因为我们可以在config阶段配置一个provider。因此我们可以编写下面的代码:

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

  var thisIsPrivate = "Private";

  return {

    setPrivate: function(newVal) {
      thisIsPrivate = newVal;
    },

    $get: function() {
      function getPrivate() {
        return thisIsPrivate;
      }

      return {
        variable: "This is public",
        getPrivate: getPrivate
      };
    }

  };

});

app.config(function(fooProvider) {
  fooProvider.setPrivate('New value from config');
});   

在这里我们将thisIsPrivate移到了我们的$get函数的外面,然后我们创建了一个setPrivate来在一个config函数中修改thisIsPrivate。为什么我们需要这样做?这难道不比在factory中添加setter要容易吗?除此之外,还有另外一个原因。

我们想要注入一个特定的对象但是我们想要提供一种方式来根据我们的需求进行一些配置。例如:一个service包含了一个使用jsonp的资源,我们想要配置具体使用的URL,或者我们想要使用一个第三方的service比如restangular来允许我们根据我们的需求来进行配置。

要注意到我们在config函数中放入的是nameProvider而不是name。在这里,我们实际上还是对name进行配置。

看到这里我们其实已经意识到了我们已经在应用中进行过一些配置了,像是$routeProvider以及$locationProvider,两者分别用来配置我们的路由了html5模式。

Decorator

那么现在已经决定要使用前面的 foo service,但是其中还是缺少一个你想要的greet函数。你可以修改factory吗?答案是不行!但是你可以装饰它:

app.config(function($provide){
    $provide.decorator('foo',function($delegate){
        $delegate.greet = function(){
            return "Hello, I am a new function of 'foo'";
        }
    });
});   

$provide是Angular用来在内部创建我们的service的东西。如果我们想要使用它的话可以手动来使用它或者仅仅使用在我们的模块中提供的函数(我们需要使用$provide来进行装饰)。$provide有一个函数,decorator,它让我们可以装饰我们的service。它接收我们想要装饰的service的名字并且在回调函数中接收一个$delegate来代表我们实际上的service实例。

在这里我们可以做一切我们想要的事情来装饰我们的service。在上面的例子中,我们为我们原来的service添加了一个greet函数。接着我们返回了修改后的service。

经过修改以后,现在我们的factory中已经有了一个叫做greet的函数。

装饰一个service的能力是非常实用的,尤其是当我们想要使用第三方的service时,此时我们不需要将代码复制到我们的项目中,而只需要进行一些修改即可。

注意:constant service不能被装饰。

创建一个实例

我们的services都是单体但是我们可以创建一个单体factory来创建新的实例。在你深入之前,记住Angular中的服务都是单体并且我们不想改变这一点。但是,在极少数的情况下你需要生成一个新的实例,你可以像下面这样做:

//我们的类   

function Person(json){
    angular.extend(this,json);
}

Person.prototype = {
    update: function(){
        //更新内容   
        this.name = "Dave";
        this.country = "Canada";
    }
};   

Person.getById = function(id){
    //由id来获取一个Person的信息   
    return new Person({
        name: "Jesus",
        country: "Spain"
        });
};

//我们的factory   
app.factory('personService',function(){
    return {
        getById: Person.getById
    };
});   

在这里我们创建了一个Person对象,它接收一些json数据来初始化对象。然后我们在我们的原型(原型中的函数可以被Person的实例所用)中创建了一个函数,并且在Person上直接创建了一个函数(就像是类函数一样)。

因此现在我们拥有了一个类函数,它将基于我们提供的id来创建一个新的Person对象,并且每一个对象都可以自我更新。现在我们仅仅需要创建一个能够使用它的service。

当每次我们调用personService.getById时,我们都在创建一个新的Person对象,因此你可以在不同的控制器中使用这个service,即便当factory是一个单体,它也能生成新的对象。

总结

Service是Angular中最酷的特性之一。我们可以使用很多方法来创造它们,我们仅仅需要找到符合我们需求的方法然后实现它。


本文译自understaning service types,原文地址http://angular-tips.com/blog/2013/08/understanding-service-types/


这篇讲解的真的很好。给100个赞。


第二篇:加深你的理解


翻译自Angular service or factory?(貌似要)

若觉得太长了,可直接跳到底部的总结。

在很多AngularJS的教程和文档中,作者时而使用service

,时而使用 factory ,但从来不解释 为什么 。更别说其实你还能用 value constant 了。

我们来看看在什么情况下你应该使用哪个。首先,我们也应该理解providers的工作方式:

provider

这是provieder方法的源码:

[javascript]  view plain copy
  1. function provider(name, provider_) {  
  2.     if (isFunction(provider_) || isArray(provider_)) {  
  3.         provider_ = providerInjector.instantiate(provider_);  
  4.     }  
  5.     if (!provider_.$get) {  
  6.         throw Error('Provider ' + name + ' must define $get factory method.');  
  7.     }  
  8.     return providerCache[name + providerSuffix] = provider_;  
  9. }  

name 是字符串. provider_可以是这三种东西之一:

  1. 函数(function)

    如果传入的是一个函数,那么这个函数会被 dependency injection调用,并返回一个有$get方法的对象

  2. 数组(array)

    数组被视为使用Inline Annotation的函数, 它也必须返回一个有$get方法的对象。

  3. 对象(object)

    如果传入的是个对象,那么它应该是有$get方法的对象。

无论provider的第二个参数是什么,你最终都会个到一个拥有$get方法的对象。以这段代码为例:

[javascript]  view plain copy
  1. // You can run this  
  2.   
  3. // Create a module  
  4. var hippo = angular.module('hippo', []);  
  5.   
  6. // Register an object provider  
  7. hippo.provider('awesome', {  
  8.     $get: function() {  
  9.         return 'awesome data';  
  10.     }  
  11. });  
  12.   
  13. // Get the injector (this happens behind the scenes in angular apps)  
  14. var injector = angular.injector(['hippo''ng']);  
  15.   
  16. // Call a function with dependency injection  
  17. injector.invoke(function(awesome) {  
  18.     console.log('awesome == ' + awesome);  
  19. });  

一旦你理解了provider,你就会发现factoryservicevalueconstant只是生成provider的快捷方式

factory

这是源代码:

[javascript]  view plain copy
  1. function factory(name, factoryFn) {  
  2.     return provider(name, { $get: factoryFn });  
  3. }  

所以你可以简单的把一个名叫awesome的provider写成这个样子:

[javascript]  view plain copy
  1. hippo.factory('awesome'function() {  
  2.     return 'awesome data';  
  3. })  

service

这是源代码:

[javascript]  view plain copy
  1. function service(name, constructor) {  
  2.     return factory(name, ['$injector'function($injector) {  
  3.         return $injector.instantiate(constructor);  
  4.     }]);  
  5. }  

所以这可以让你构建一个factory,用它可以初始化一个“类(class)”,比如说:

[javascript]  view plain copy
  1. var gandalf = angular.module('gandalf', []);  
  2.   
  3. function Gandalf() {  
  4.     this.color = 'grey';  
  5. }  
  6. Gandalf.prototype.comeBack = function() {  
  7.     this.color = 'white';  
  8. }  
  9.   
  10. gandalf.service('gandalfService', Gandalf);  
  11.   
  12. var injector = angular.injector(['gandalf''ng']);  
  13.   
  14. injector.invoke(function(gandalfService) {  
  15.     console.log(gandalfService.color);  
  16.     gandalfService.comeBack()  
  17.     console.log(gandalfService.color);  
  18. });  

以上的代码会生成一个Gandalf(译注:即甘道夫,魔戒里的白胡子老头),但是请记住,使用同一个service返回的实例都是同一个实例(这是件好事)。

value

这是源代码:

[javascript]  view plain copy
  1. function value(name, value) {  
  2.     return factory(name, valueFn(value));  
  3. }  

value可以让你把awesome的缩短成这样:

[javascript]  view plain copy
  1. hippo.value('awesome''awesome data');  

constant

这是源代码:

[javascript]  view plain copy
  1. function constant(name, value) {  
  2.     providerCache[name] = value;  
  3.     instanceCache[name] = value;  
  4. }  

constantvalue不同,constant在config的时候是可达的(accessible),如下例:

[javascript]  view plain copy
  1. var joe = angular.module('joe', []);  
  2.   
  3. joe.constant('bobTheConstant''a value');  
  4. joe.value('samTheValue''a different value');  
  5.   
  6. joe.config(function(bobTheConstant) {  
  7.     console.log(bobTheConstant);  
  8. });  
  9.   
  10. joe.config(function(samTheValue) {  
  11.     console.log(samTheValue);  
  12. });  
  13.   
  14. // This will fail with "Error: Unknown provider: samTheValue from joe"  
  15. var injector = angular.injector(['joe''ng']);  

在Modules文档的Module Loading & Dependencies部分可以获得更多信息。

总结

如果你想像调用普通函数那样调用你的函数,用factory,如果你想用new操作符来实例化你的函数,那么使用service。如果你不知道这其中的区别在那里,那就用factory

以下是AngularJS源代码中,每个函数(很棒的)注释文档:

  1. factory

    A short hand for configuring services if only $get method is required.

  2. service

    A short hand for registering service of given class.

  3. value

    A short hand for configuring services if the $get method is a constant.

  4. constant

    A constant value, but unlike {@link AUTO.$provide#value value} it can be injected into configuration function (other modules) and it is not interceptable by {@link AUTO.$provide#decorator decorator}.


你可能感兴趣的:(前端AngularJs,angularjs,service)