zoukankan      html  css  js  c++  java
  • angular 依赖注入

    依赖注入

    angular的依赖注入模式通过自动提前查找依赖以及为依赖提供目标,以此将依赖资源注入到需要它们的地方。

    依赖注入服务可以使Web应用良好构建(比如分离表现层、 数据和控制三者的部件),并且松耦合(一个部件自己不需要解决部件之间的依赖问题,它们都被DI子系统所处理)。

    使用场景

    可以看到angular中的依赖注入服务使用非常频繁,注入的服务在下文称为provider。 如:

    var app = angular.module('myApp', []);
    app.factory('greeter', function() {
        return function(msg) {
            alert(msg) 
        }
    });
    app.value('testValue', '123');
    //内置provider:$scope, 自定义的provider: greeter, testValue
    app.controller('MyController', function($scope, greeter, testValue) {
        greeter('hello');
    });

    注入器(injector)

    注入器用来解析、创建依赖服务,并通过invoke方法将依赖注入到需要的函数对象中。

    解析provider标记

    angular是如何根据参数的名称来找到相应的provider, injector对象中的annotate函数实现了这一功能

    angular.injector().annotate(function($ABC, $myScope) {});
    //output: ["$ABC", "$myScope"]
     
    //采用正则去掉comment和空白字符,然后通过匹配functionBody种的function参数,获取需要的字符
    var annotate = function() {
        var FN_ARGS = /^functions*[^(]*(s*([^)]*))/m;
        var FN_ARG_SPLIT = /,/;
        var FN_ARG = /^s*(_?)(S+?)1s*$/;
        var STRIP_COMMENTS = /((//.*$)|(/*[sS]*?*/))/mg;
        $inject = [];
        if (fn.length) {
            fnText = fn.toString().replace(STRIP_COMMENTS, '');
            argDecl = fnText.match(FN_ARGS);
            forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) {
                arg.replace(FN_ARG, function(all, underscore, name) {
                                 $inject.push(name);
                    });
            });
        }
        return $inject;
    };

    invoke注入

    这一步根据解析到的$inject数组,依次拿到对应provider.$get方法执行的结果,push到args数组。 同时让args作为被注入fn的参数调用fn。达到服务提供者和使用者的分离。

    function invoke(fn, self, locals) {
         var args = [],
             $inject = annotate(fn),
             length, i,
             key;
      
         for (i = 0, length = $inject.length; i < length; i++) {
             key = $inject[i];
             args.push(getService(key));
         }
         if (isArray(fn)) {
             fn = fn[length];
         }
         return fn.apply(self, args);
     }

    invoke中调用的getService函数保证了所有的Provider为单例

    function getService(serviceName) {
          if (cache.hasOwnProperty(serviceName)) {
               return cache[serviceName];
          } else {
              var provider = providerCache[name + providerSuffix];
              invoke(provider.$get, provider)
              return cache[serviceName] = invoke(provider.$get, provider);
          }          
    } 

    Provider

    在angular的应用中,依赖注入的设计使得使用provider的成本大大降低。同时也是衔接MVC架构的基础, 你可以根据系统设计灵活分层, provider会让app中的service、model、controller很简单的联系起来。 
    Provider分为两种,一种是内置的,另外是为app module自定制的。

    内置Provider

    用一个简单的provider为例来分析:

    function $LocationProvider() {
      this.hashPrefix = function(prefix) {  
      };
      this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement',
          function($rootScope, $browser, $sniffer, $rootElement) {
            var $location;
            $location.$$replace = false;
            return $location;
        }];
    }

    初始化的时候,如果是函数或者数组就通过新建一个constructor对象,使得该对象的prototype指向原来provider的prototype,同时拷贝构造函数中的属性。

    function instantiate(provider) {
          var Constructor = function() {},
              instance, returnedValue;
    
          Constructor.prototype = provider.prototype;
          instance = new Constructor();
          returnedValue = ((typeof provider === 'array') ? provider[length-1] : provider).apply(Constructor)
          return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance;
    }
    

    最后将生成的construcor对象放到providerCache中, 完成内置provider的初始化, 完整的代码如下:

    function provider(name, provider_) {
        assertNotHasOwnProperty(name, 'service');
        if (isFunction(provider_) || isArray(provider_)) {
          provider_ = providerInjector.instantiate(provider_);
        }
        if (!provider_.$get) {
          throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name);
        }
      //如果非函数或者对象,直接将provider放到cache中 return providerCache[name + providerSuffix] = provider_; }

    以下是provider内置的一些provider,由于篇幅有限,列举了一些, 可以在angular module中通过名称注入到需要的地方。 更多请参考angular源码。

    var angularModule = setupModuleLoader(window);
    angularModule('ng', ['ngLocale'], ['$provide',
        function ngModule($provide) {
            $provide.provider({
                $$sanitizeUri: $$SanitizeUriProvider
            });
            $provide.provider('$compile', $CompileProvider).
            directive({
                ngSwitch: ngSwitchDirective,
                ngSwitchWhen: ngSwitchWhenDirective,
                ngSwitchDefault: ngSwitchDefaultDirective,
                ngOptions: ngOptionsDirective,
                ngTransclude: ngTranscludeDirective,
                ngModel: ngModelDirective,
                ngList: ngListDirective,
                ngChange: ngChangeDirective,
                required: requiredDirective,
                ngRequired: requiredDirective,
                ngValue: ngValueDirective
                ...
            }).
            directive({
                ngInclude: ngIncludeFillContentDirective
            }).
            directive(ngAttributeAliasDirectives).
            directive(ngEventDirectives);
            $provide.provider({
                $controller: $ControllerProvider,
                $document: $DocumentProvider,
                $filter: $FilterProvider,
                $interval: $IntervalProvider,
                $http: $HttpProvider,
                $location: $LocationProvider,
                $log: $LogProvider,
                $parse: $ParseProvider,
                $rootScope: $RootScopeProvider,
                $window: $WindowProvider,
                ...
            });
        }
    ]);

    自定制Provider

    angular可以通过以下6种方式实现自定制Provider注入:

    1. decorator
    2. constant 
    3. value
    4. service
    5. factory
    6. provider

    如:

    <script type="text/javascript"> 
        var app = angular.module('myApp', []); 
        app.value('test', 'hello world'); 
        app.constant('test1', "hello world test"); 
         
        app.provider(function ($provide) { 
            $provide.decorator('test', function ($delegate) { 
                return $delegate + 'again'; 
            }); 
        });
        app.factory('myService', function () {  
            console.log('my service init');
        }); 
        app.service('myService1', function() { 
            console.log('my service1 init'); 
        });  
        app.controller('my controller', function($scope, test, test1, myService, myService1) { 
           ...
        } 
    </script>

    分析一下代码:

    //将provider直接保存到providerCache中
    function provider(name, provider_) {
        assertNotHasOwnProperty(name, 'service');
        if (isFunction(provider_) || isArray(provider_)) {
            provider_ = providerInjector.instantiate(provider_);
        }
        if (!provider_.$get) {
            throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name);
        }
        return providerCache[name + providerSuffix] = provider_;
    }
    //由于所有的provider默认都必须实现$get方法,所以采用工厂方法对factoryFn进行封装,再保存到providerCache中
    function factory(name, factoryFn) {
        return provider(name, {
            $get: factoryFn
        });
    }
    //调用工厂方法, 处理当constructor返回非对象的时候, 新建对象返回,并复用constructor的prototype,拷贝constructor的实例属性
    function service(name, constructor) {
        return factory(name, ['$injector', function($injector) {
                return $injector.instantiate(constructor);
            }
        ]);
    }
    //调用工厂方法
    function value(name, val) {
        return factory(name, function(){ return val; });
    }
    //不可修改的静态属性,也可以直接作为provider提供
    function constant(name, value) {
        providerCache[name] = value;
        instanceCache[name] = value;
    }
    //改写provider的$get, 并将原来provider的返回值作为参数调用装饰函数。
    function decorator(serviceName, decorFn) {
        var origProvider = providerInjector.get(serviceName + providerSuffix),
            orig$get = origProvider.$get;
       
            origProvider.$get = function() {
                var origInstance = instanceInjector.invoke(orig$get, origProvider); 
                return instanceInjector.invoke(decorFn, null, {
                    $delegate: origInstance
                });
            }; 
    }

    参考

    • https://github.com/angular/angular.js
  • 相关阅读:
    windbg常用命令
    Windbg双机调试环境配置(Windows7/Windows XP+VirtualBox/VMware+WDK7600)
    SVN使用说明文档
    JavaScript-浏览器兼容之客户端检测
    JavaScript-执行环境
    JavaScript-函数
    JavaScript-静态私有变量
    JavaScript-构造函数模式
    JavaScript 自执行函数剖析
    easyui如何在datagrid 每行增加超链接
  • 原文地址:https://www.cnblogs.com/mininice/p/3985834.html
Copyright © 2011-2022 走看看