天天看点

对AngularJs中依赖注入的理解和使用方法

Angular中的依赖注入

一:什么是依赖注入?

  依赖注入是一种软件设计模式,在这种模式下,一个或更多的依赖被注入到一个独立的对象中,然后成为该对象的一部分。该模式分离了客户端依赖本身行为的创建,使得程序设计变得松耦合,并遵循了依赖反转和单一职责原则。与服务定位器模式形成直接对比的是,它允许客户端了解客户端如何使用该系统找到依赖。

AngularJS 提供很好的依赖注入机制。以下5个核心组件用来作为依赖注入,即他们都可以向控制器中注入值或者创建服务:

1.value

2.factory

3.service

4.provider

5.constant

二:详解每个组建地用法?

  • value():value 是一个简单的 javascript 对象,用于向控制器传递值(配置阶段):
<div ng-app="mainApp" ng-controller="CalcController">
    .........
</div>
<script type="text/javascript">
    // 定义一个模块  
    var mainApp = angular.module("mainApp", []);  
    // 创建 value 对象 "defaultInput" 并传递数据  
    mainApp.value("defaultInput", );  
    // 将 "defaultInput" 注入到控制器  
    mainApp.controller('CalcController', function($scope, defaultInput) {  
        $scope.number = defaultInput;  
        console.log(defaultInput)
    }); 
</script>
           
  • factory():factory返回一个对象,只需要创建一个对象,为它添加属性,然后返回这个对象,在控制器中注入该factory,即可使用它的所有属性。
//4.factory 是一个函数用于返回值。在 service 和 controller 需要时创建。通常我们使用 factory 函数来计算或返回值。
        mainApp.factory('MathService', function() {
            var factory = {};
            factory.multiply = function(a, b) {
                return a * b
            }
                return factory;
        });
           

Angular里面创建服务的一个方式是使用factory()方法,factory()让我们通过返回一个包含服务方法和数据的对象来定义一个服务。在创建的服务方法里我们可以注入其他服务,比如&http;

angular.module('myApp.services').factory('User', function($http) {     
                var backendUrl = "http://localhost:3000";
                var service = {                                             
                    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;
            });
           
  • service() service()通过构造函数的方式让我们创建service,我们当然可以使用原型模式替代javaScript原始的对象来定义service。service 类似于一个构造器, 通过 new 关键字实例化对象,将一些属性和方法直接添加到 this 上,在创建 service 对象时, this 会被作为返回值返回,和factory()方法一样我们也可以在函数的定义里面看到服务的注入。
app.service('hexafy', function() {
    this.myFunc = function (x) {
        return x.toString();
    }
});
app.controller('myCtrl', function($scope, hexafy) {
  $scope.hex = hexafy.myFunc();
});
           
  • provider() 所有的服务工厂都是由 provide服务创建的, provide服务负责在运行时初始化这些提供者,提供者是具有 get()方法的对象, injector通过调用 get方法创建服务实例。 provider提供了数个不同的API用于创建服务。每个方法都有各自的特殊用途。例如:
// 使用 provider 创建一个 MathService服务,用于定义一个方法用于计算两数乘积
mainApp.config(function($provide) {
   $provide.provider('MathService', function() {
      this.$get = function() {
         var obj= {};  
         obj.multiply = function(a, b) {
            return a * b; 
         }
         return obj;
      };
   });
});
           

如果不理解,请耐心往下看:

  从技术上说,当我们假定传入的函数就是$get()时,factory()函数就是用provider()方法注册服务的简略形式。(实际上Provider 中提供了一个 factory 方法 get(),它用于返回 value/service/factory。)

下面两种方法的作用完全一样,并且会创建同一个服务。

angular.module('myApp').factory('myService',function(){
    var obj  = {'username' : 'bangbang'};
    return obj;
})
           

这与上面工厂的用法等价

angular.module('myApp').provider('myService',{
    $get : function(){
        var obj = {'username' : 'bangbang'};
        return obj;
    }
})
           

是否可以一直使用factory()方法来代替provider()呢?

答案取决于是否需要用AngularJs的.config()函数来对.provider()方法返回的服务进行额外的扩展配置。同其他创建服务的方法不同,config()方法可以被注入特殊的参数。比如,我们希望在应用启动前配置girhubService的URL:

var app = angular.module('myApp',[]);   
        //使用provider注册该服务
        app.provider('githubService',function(){
            //默认的,私有状态
            var githubUrl = 'https://github.com';
            return {
                setGithubUrl  : function(url){
                    //通过.config改变默认属性
                    if(url){
                        githubUrl = url;
                    }
                },
                $get : function($http){
                    self = this;
                    return $http({method : 'get', url:'datas.json'});
                }
            }
        })

        app.controller('myCtrl',function($scope,githubService){
            console.log(githubService)
        })
           

  通过使用provider()方法,可以在多个应用使用同一个服务时候获得更强的扩展,特别是在不同应用或开源社区之间共享服务时。

  在上面例子中,provider()方法在服务githubService后生成了一个新的提供者,githubServiceProvider即(服务名+Provider),该提供者即gitbubServiceProvider可以被注入到config()函数中。

angular.modele('myApp',[]).config(function(gitbubServiceProvider){
    githubServiceProvider.setGithubUrl("[email protected]");
})
           

如果希望在config()函数中可以对服务进行配置,必须用provider()来定义服务。

provider()方法为服务注册提供者,可以接受两个参数:

第一个参数:name(字符串):name参数在providerCache中是注册的名字,name+provider会成为服务的提供者。同时name也是服务实例的名字。例如:如果我们定义了githubService,那它的提供者是githubServiceProvider。

第二个参数:aProvider(对象/函数/数组):aProvider可以是多种形式,

  • 如果aProvider是函数,那么它会通过依赖注入被调用,并且负责通过$get方法返回一对象;
  • 如果aProvider是数组,他会被当做一个带有行内依赖注入声明的函数来处理。数组的最后的一个元素应该是函数,可以返回带有一个$get方法的对象;
  • 如果aProvider是对象它应该带有$get方法。

    provider()函数返回一个已经注册的提供者实例。

    直接使用provider()API是最原始的创建服务的方法:

//在模块对象上直接创建provider的例子
        var app = angular.module('myApp',[]);
        app.provider("myService",{
            favoriteColor : null,
            setFavoriteColor : function(newColor){
                this.favoriteColor = newColor;
            },
            //$get函数可以接受injectables
            $get : function($http){
                return{
                    'name' : 'bangbang',
                    getFavoriteColor : function(){
                        return this.favoriteColor || 'unknown';
                    }
                }
            }
        })
        app.controller('myCtrl',function($scope,myService){
            console.log(myService)
            myService.getFavoriteColor('white');
        })
           

用这个方法创建服务,必须返回一个定义有$get()函数的对象,否则会导致错误。provider()是非常强大的,可以让我们在不同的应用中共享服务。

  • constant() constant可以将一个已经存在的变量值注册为服务,并将其注入到应用的其他部分当中。例如,假设我们需要给后端服务一个apiKey,可以用constant()将其作为常量保存下来。constant()函数接受两个参数:name(字符串),需要注册常量的名字,value(常量)需要注册常量的值或者对象。实例如下:
var app = angular.module('myApp',[]);
        app.constant('names',['bangbang','qiqi','niuniu']).controller('myCtrl',function($scope,names){
            console.log(names); //["bangbang", "qiqi", "niuniu"]
        })
           
  • 何时使用value()和constant()

      value()方法和constant()方法之间最重要的区别是,常量可以注入到配置函数中,但是值不行。通常情况下,可以通过value()来注册服务对象或函数,用constant()来配置数据。

var app = angular.module('myApp',[]);
        app.constant('number','22').config(function(number){
            //在这里number将被赋值为22
            console.log(number)
        }).value('name','bangbang').config(function(name){
            console.log(name);      //这将抛出一个错误,因为,在config()函数内部无法访问这个值
        })
           

参考文章和书籍:

  • Angular Js权威教程
  • Angular Js菜鸟教程
  • AngularJS中的Provider们:Service和Factory等的区别
  • 跟我学AngularJs:Service、Factory、Provider依赖注入使用与区别
  • AngularJS中service,factory,provider的区别
  • 关于AngularJS中的DI

继续阅读