AngularJs 的依赖注入

本文详细介绍了AngularJS中的依赖注入概念及其三种主要的使用方式:推断式注入、标记式注入和行内式注入。同时,还展示了如何利用$injector API进行依赖注入的管理和调试。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

AngularJs 的依赖注入

1-1 依赖注入介绍
  1. 依赖注入的介绍

    “依赖”:当一个对象在创建的时候必须依赖另外一个对象。Exp. Var a = newA(); a依赖A。

    “注入”:生命依赖关系之后,angular通过injector注入器所依赖的对象进行“注入”操作。

    1. 依赖注入的原理

      每个Angular应用都存在一个injector注入器来处理依赖关系,注入器就是一个负责帮你查找和创建依赖的服务定位器。注入器对象具有get函数,可以获得任何被定义过的服务实例。

    2. <!DOCTYPE html>
      <html lang="en">
      <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>依赖注入</title>
        <script src="components/angular/angular.min.js"></script>
        <style>
            .body{
                font-size: 5em;
            }
            .show{
                border: solid 1px black;
            }
        </style>
      </head>
      <body ng-app="a4_1">
        <div ng-controller="c4_1">
            <div class="{{cls}}">{{show}}</div>
            <button ng-click="onClick()">click here</button>
        </div>
        <script>
            var a4_1 = angular.module('a4_1',[]);
                a4_1.config(function($controllerProvider){
                    $controllerProvider.register('c4_1',['$scope',function($scope){
                        $scope.cls = '';
                        $scope.onClick = function(){
                            $scope.cls = 'show';
                            $scope.show = 'click here to show the information';
                        }
                    }]);
                });
        </script>
      </body>
      </html>
    3. 代码解析

      1. 创建模块之后我们并没有调用模块的控制器函数controller,而是调用config函数进行服务注册,这是因为在实际的代码执行过程,下面的代码执行后的功能是相同的。

        a4_1.controller('c4_1',['$scope',function($scope){
        //控制器代码
        }]);
        a4_1.config(function($controllerProvider){
        $controllerProvider.register('c4_1',['$scope',function($scope){
        //控制器代码
        }
        }]);
        })
      2. 在angular中执行的本质是第二段代码,在Angular中可以通过模块中的config函数来声明需要注入的依赖对象,而声明的方式就是通过provider的服务。但在Angular内部controller控制器是通过controllerProvider服务创建的。因此用户在创建一个控制的时候是在config中调用controllerProvider服务的register方法,来完成一个控制器的创建,控制器创建之后,则再调用injector注入器完成各个依赖对象的注入。

1-2依赖注入的实例
  1. 代码

    <!DOCTYPE html>
    <html lang="en">
    <head>
       <meta charset="UTF-8">
       <meta name="viewport" content="width=device-width, initial-scale=1.0">
       <meta http-equiv="X-UA-Compatible" content="ie=edge">
       <title>Document</title>
       <script src="components/angular/angular.min.js"></script>
       <style>
           .show{
               border: solid 1px black;
           }
       </style>
    </head>
    <body ng-app="a4_2">
       <div ng-controller="c4_2">
           <div>
               <div class="{{cls}}">{{text}}</div>
               <button ng-click="onClick(1)">morning</button>
               <button ng-click="onClick(2)">morning up</button>
               <button ng-click="onClick(3)">afternoon</button>
               <button ng-click="onClick(4)">evening</button>
           </div>
       </div>
       <script>
           var a4_2 = angular.module('a4_2',[]);
           a4_2.config(function($provide){
               $provide.provider('show_1',function(){
                   this.$get = function(){
                       return {
                           val : function(name){
                               return name;
                           }
                       }
                   }
               });
           });
           a4_2.config(function($provide){
               $provide.factory('show_2',function(){
                   return {
                       val:function(name){
                           return name;
                       }
                   };
               });
           });
           a4_2.config(function($provide){
               $provide.value('show_3',function(name){
                   return name;
               });
           });
           a4_2.config(function($provide){
               $provide.service('show_4',function(){
                   var self = {};
                   self.val = function(name){
                           return name;
                       }
                   return self;
               })
           });
           a4_2.controller('c4_2',function($scope,show_1,show_2,show_3,show_4){
               $scope.cls = '';
               $scope.onClick = function(t){
                   $scope.cls = 'show';
                   switch (t) {
                       case 1:
                           $scope.text = show_1.val('good morning');
                           break;
                       case 2:
                           $scope.text = show_2.val('good morning up');
                           break;
                       case 3:
                           $scope.text = show_3('good afternoon');
                           break;
                       case 4:
                           $scope.text = show_4.val('good evening');
                           break;
                       default:
                           break;
                   }
               }
           });
       </script>
    </body>
    </html>
  2. 知识点

    在定义模版中的控制器层代码时,将这些定义好的变量全部作为依赖注入变量来使用。当页面在解析这段代码时,angular将启动$provide,并返回多个与服务一一对应的实例,通过这些实例来分别处理这些注入变量对应的函数功能,但对于开发者来说,只要注入变量就可以。

1-3 依赖注入标记
  1. 推断式注入

    1. 介绍

      在没有明确声明下面,Angular会认定参数名称就是依赖注入的函数名,并在内部调用函数对象的toString()方法,获取对应的参数列表。

    2. 代码

      <!DOCTYPE html>
      <html lang="en">
      <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>推断注入</title>
        <script src="components/angular/angular.min.js"></script>
      </head>
      <body ng-app="a4_3">
        <div ng-controller="c4_3">
            <input id="btnAlert" type="button" value="alert the infor" ng-click="onClick('alert sucecss')">
        </div>
        <script>
            var a4_3 = angular.module('a4_3',[])
                .factory('$show',function($window){
                    return{
                        show : function(text){
                            $window.alert(text);
                        }
                    }
                })
                .controller('c4_3',function($scope,$show){
                    $scope.onClick = function(msg){
                        $show.show(msg);
                    }
                })
        </script>
      </body>
      </html>
    3. 解释

      在编写应用控制器代码时,由于在注入服务过程,没有使用[]或进行标记式的注入生命,因此注入器inject根据参数的名称来推断服务和控制器的关系。

      angular自动通过annotate函数自动提取实例化参数时传递来的列表,并最终通过注入器将这些列表注入控制器中。需要说明的是:这种注入方式不需要关注注入时参数的先后顺序,angular会根据依赖的程度自动处理,由于angular需要根据参数列表分析注入服务,因此,这种注入的方式不能处理压缩或者混淆后的代码,只能处理源码。

  2. 标记式注入

    1. 代码:

      <!DOCTYPE html>
      <html lang="en">
      
      <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>推断注入</title>
        <script src="components/angular/angular.min.js"></script>
      </head>
      
      <body ng-app="a4_3">
        <div ng-controller="c4_3">
            <div>{{text}}</div>
            <input id="btnAlert" type="button" value="alert the infor" ng-click="onShow('alert sucecss')">
            <input id="btnAlert" type="button" value="write the infor" ng-click="onWrite('write sucecss')">
        </div>
        <script>
            var c4_3 =function($scope,$show,$write){
                $scope.onShow = function(msg){
                    $show.show(msg);
                }
                $scope.onWrite = function(msg){
                    $scope.text = $write.write(msg);
                }
            }
            c4_3.$inject = ['$scope','$show','$write'];
            var a4_3 = angular.module('a4_3', [])
                .factory('$show', function ($window) {
                    return {
                        show: function (text) {
                            $window.alert(text);
                        }
                    }
                })
                .factory('$write', function () {
                    return {
                        write: function (text) {
                            return text;
                        }
                    }
                })
                .controller('c4_3', c4_3)
        </script>
      </body>
      
      </html>
    2. 解释

      控制函数通过调用$inject属性,向函数中注入了3个服务,注入服务名和顺序必须与函数在构造时的参数名和顺序完全一致,否则,将出现错误异常。

  3. 行内式注入

    1. 介绍

      行内注入指的是在构建一个Angular对象时,允许开发人员将一个字符串数组作为对象的参数,而不仅仅是一个函数,在这个数组中,除最后一个必须是函数体外,其余代表的都是注入对象的服务,而他们的名称和顺序与最后一个函数的参数是一一对应的。

    2. 代码

      <!DOCTYPE html>
      <html lang="en">
      <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>行内注入</title>
        <script src="components/angular/angular.min.js"></script>
      </head>
      <body ng-app="a4_3">
        <div ng-controller="c4_3">
            <input id="btnAlert" type="button" value="alert the infor" ng-click="onClick('alert sucecss')">
        </div>
        <script>
            var a4_3 = angular.module('a4_3',[])
                .factory('$show',function($window){
                    return{
                        show : function(text){
                            $window.alert(text);
                        }
                    }
                })
                .controller('c4_3',['$scope','$show',function($scope,$show){
                    $scope.onClick = function(msg){
                        $show.show(msg+'1');
                    }
                }])
        </script>
      </body>
      </html>
    3. 解释

      在注入的函数体中声明与服务名一一对应的参数,以用于函数体内部的调用,由于这种方式仍然是分析并处理注入字符数组中的内容,因此,即使是压缩或混淆后的代码,这种方式可以使用。

$injector常用API

  1. has和get方法

    1. injector.has(name)

      injector 为获取的$injector对象,name为需要查找的服务名称。执行上述代码,返回一个布尔值,true为找到名称对应的服务,false表示没有找到。

    2. Injector.get(name)

      injector 为获取的$injector对象,name为需要查找的服务名称。执行上述代码后,将直接返回一个服务实例。

  2. 示例

    <!DOCTYPE html>
    <html lang="en">
    <head>
       <meta charset="UTF-8">
       <meta name="viewport" content="width=device-width, initial-scale=1.0">
       <meta http-equiv="X-UA-Compatible" content="ie=edge">
       <title>Document</title>
       <script src="components/angular/angular.min.js"></script>
    </head>
    </head>
    <body ng-app="a4_6">
       <div ng-controller="c4_6">
    
       </div>
       <script>
           var a4_6 = angular.module('a4_6',[])
           .factory('$custom',function(){
               return {
                   print:function(msg){
                       console.log(msg);
                   }
               }
           });
           var injector = angular.injector(['a4_6','ng']);  
           var has = injector.has('$custom');
           console.log(has);
           if(has){
               console.log('has方法实现');
               var custom = injector.get('$custom');
               custom.print('get方法实现');
           }
           a4_6.controller('c4_6',['$scope','$custom',function($scope,$custom){
               $custom.print('controller调用');
           }]);
       </script>
    </body>
    </html>

    解析:

    ​ var injector = angular.injector([‘a4_6’,’ng’]); 这句代码是为了注册服务。

  3. invoke方法

    1. 介绍

      invoke 是一个比较强大的API,他最为常用的场景就是执行一个自定义函数。除此之外,在执行函数的时候,还能传递变量给函数自身,调用格式如下:

      injector.invoke(fn,[self],[locals])

      Injector 是获取的$injector对象,参数fn为需要执行的函数名称,可选参数self 是一个对象,表示用于函数的this变量,可选项参数locals也是一个对象,他也能为函数中的变量名的传递提供方法支持。

    2. 代码

      <!DOCTYPE html>
      <html lang="en">
      <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>Document</title>
        <script src="components/angular/angular.min.js"></script>
      </head>
      </head>
      <body ng-app="a4_7">
        <div ng-controller="c4_7">
      
        </div>
        <script>
            var a4_7 = angular.module('a4_7',[])
            .factory('$custom',function(){
                return {
                    print:function(msg){
                        console.log(msg);
                    }
                }
            });
            var injector = angular.injector(['a4_7','ng']);  
            var has = injector.has('$custom');
            console.log(has);
            //非显示指定注入项
            var invokTest = function($custom){
                $custom.print('invok success');
            };
            if(has){
                console.log('has方法实现');
                // injector.invoke(invokTest);
                //invoke 显示指定注入项,AngularJS采用依赖项数数组方法解决代码压缩混淆注入的问题
                injector.invoke(['$custom',function($custom){
                    this.text = 'invok success1';
                    $custom.print(this.text);
                }],self);
            }
            a4_7.controller('c4_7',['$scope','$custom',function($scope,$custom){
                $custom.print('controller调用');
            }]);
        </script>
      </body>
      </html>

    解析

    1. 使用注入器的invoke()方法,可以直接调用一个用户自定义的函数体,并通过函数参数注入进来的对象.

      angular.injector(['ng'])
      .invoke(function($http){
         // do sth. with $http
      });

      也可以使用注入器的get()方法,获得指定名称的服务实例:

      var my$http = angular.injector(['ng']).get('$http'); 2 // do sth. with my$http  
      
    2. 当采用invoke方式调用组件服务时,AngularJS通过检查函数的参数名确定需要注入什么对象

      angular.injector(['ng'])
      .invoke(function($http){
         // do sth. with $http
      });

      AngularJS执行invoke时,检查函数的参数表,发现并注入$http对象。

      这样有一个问题,就是当我们对JavaScript代码进行压缩处理时,$http可能会被变更成其他名称,这将导致注入失败。

    3. 显示指定注入项

      AngularJS采用依赖项数数组方法解决代码压缩混淆注入的问题:

      angular.injector(['ng'])
      .invoke(["$http","$compile",function($http,$compile){
        // do sth. with $http,$compile
      }]);

      这是传入invoke的是一个数组,数组的最后一项是实际要执行的函数,数组的其他项 则指明需要向该函数注入的对象。invoke将按照数组中的顺序,依次向函数注入依赖对象。也就是说上面代码中绿色部分http,http,compile是可以为任意名称的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值