• angular核心原理解析2:注入器的创建和使用


    上一课没有讲到创建注入器的方法createInjector。

    此方法,会创建两种不同的注入器:第一种叫做providerInjector,第二种叫做instanceInjector。providerInjector是用来创建provider的,instanceInjector是用来创建一个对象实例的。

    我们可以在js代码中直接使用注入器:

    var myModule = angular.module("myModule", []);

    myModule.factory("person", function(){    //定义了一个服务person

      return {

        name:"chaojidan"

      }

    });

    myModule.controller("myController", ["$scope", "$injector",     //myController依赖于服务person,但是它不声明依赖于person,而是通过注入器$injector来获得服务person的实例对象。

      function($scope, $injector){

        $injector.invoke(function(person){   //通过注入器$injector的invoke方法,把服务person的实例注入到函数function中。

          console.log(person.name);

        });

      }

    ])

    注入器$injector的annotate方法的作用:它主要分析函数的参数签名。比如:

    $injector.annotate(function(arg1,arg2){})会得到[arg1, arg2]。

    在前面的操作中,我们经常使用函数参数声明的方式来注入一个服务(实例对象),其实angular就是通过annotate方法得到参数的名字,然后通过注入器实例化这些对象,最后注入到函数中。

    function annotate(fn) {
            var $inject,
                fnText,
                argDecl,
                last;
    
            if (typeof fn == 'function') {
                if (!($inject = fn.$inject)) {
                    $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);
                            });
                        });
                    }
                    fn.$inject = $inject;
                }
            } else if (isArray(fn)) {
                last = fn.length - 1;
                assertArgFn(fn[last], 'fn');
                $inject = fn.slice(0, last);
            } else {
                assertArgFn(fn, 'fn', true);
            }
            return $inject;
        }

    它是怎么获得函数的参数声明的呢?其实它是通过函数的toString方法得到整个函数的描述,然后通过正则表达式得到函数的参数。

    在angular中,所有的provider都可以用来进行注入。我们创建provider有以下几种方式:

    provider/factory/service/constant/value。

    我们创建好provider之后,注入到哪里去呢?我们有以下几种方式来注入provider:

    controller/directive/filter/service/factory等。

    举个例子:

    var myModule = angular.module("myModule", []);

    myModule.provider("helloAngular", function(){      //通过provider方法创建一个服务提供者helloAngular

      return {

        $get : function(){    //provider方法来定义服务提供者的话,必须定义$get方法。

          var name = "chaojidan";

          function getName(){

            return name;

          }

          return {

            getName: getName

          }

        }

      }

    });

    myModule.controller("myController", ["$scope", "helloAngular" ,     //通过controller方法注入helloAngular服务,也就是注入一个helloAngular服务的实例对象

      function($scope, helloAngular){

        $scope.name = helloAngular.getName();     //使用helloAngular服务的实例对象

      }

    ])

    第二个例子:

    var myModule = angular.module("myModule", []);

    myModule.factory("helloAngular", function(){      //通过factory方法创建一个服务提供者helloAngular

      var name = "chaojidan";

      function getName(){

        return name;

      }

      return {

        getName:getName

      }

    });

    myModule.controller("myController", ["$scope", "helloAngular" ,     //通过controller方法注入helloAngular服务,也就是注入一个helloAngular服务的实例对象

      function($scope, helloAngular){

        $scope.name = helloAngular.getName();     //使用helloAngular服务的实例对象

      }

    ])

    第三个例子:

    var myModule = angular.module("myModule", []);

    myModule.service("helloAngular", function(){      //通过service方法创建一个服务提供者helloAngular

      this.name = "chaojidan";

      this.getName = function(){

        return this.name;

      }

    });

    myModule.controller("myController", ["$scope", "helloAngular" ,     //通过controller方法注入helloAngular服务,也就是注入一个helloAngular服务的实例对象

      function($scope, helloAngular){

        $scope.name = helloAngular.getName();     //使用helloAngular服务的实例对象

      }

    ])

    其实从angular源码可以知道,创建provider的这几种方式:provider/factory/service/constant/value,其中,

    provider方法是基础,其他都是调用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);
               }
               return providerCache[name + providerSuffix] = provider_;
    }
    
    function factory(name, factoryFn) { 

        return provider(name, { $get: factoryFn });
    }
    function service(name, constructor) { return factory(name, ['$injector', function($injector) { return $injector.instantiate(constructor); }]); } function value(name, val) {

      return factory(name, valueFn(val));
    }
    function constant(name, value) { assertNotHasOwnProperty(name, 'constant'); providerCache[name] = value; instanceCache[name] = value; }

    createInjector方法里面,其实是通过createInternalInjector方法来创建注入器的。

    function createInternalInjector(cache, factory) {
            function getService(serviceName) {     //注入器可以用来获取一个服务的实例
                    if (cache.hasOwnProperty(serviceName)) {
                        if (cache[serviceName] === INSTANTIATING) {
                            throw $injectorMinErr('cdep', 'Circular dependency found: {0}', path.join(' <- '));
                        }
                        return cache[serviceName];
                    } else {
                        try {
                            path.unshift(serviceName);
                            cache[serviceName] = INSTANTIATING;
                            return cache[serviceName] = factory(serviceName);
                        } catch (err) {
                            if (cache[serviceName] === INSTANTIATING) {
                                delete cache[serviceName];
                            }
                            throw err;
                        } finally {
                            path.shift();
                        }
                    }
            }
    
            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];
                        if (typeof key !== 'string') {
                            throw $injectorMinErr('itkn',
                                'Incorrect injection token! Expected service name as string, got {0}', key);
                        }
                        args.push(
                            locals && locals.hasOwnProperty(key)
                                ? locals[key]
                                : getService(key)
                        );
                    }
                    if (!fn.$inject) {
                        // this means that we must be an array.
                        fn = fn[length];
                    }
    
                    // http://jsperf.com/angularjs-invoke-apply-vs-switch
                    // #5388
                    return fn.apply(self, args);
          }
    
         function instantiate(Type, locals) {   //可以用来实例化一个对象
                    var Constructor = function() {},
                        instance, returnedValue;
    
                    // Check if Type is annotated and use just the given function at n-1 as parameter
                    // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
                    Constructor.prototype = (isArray(Type) ? Type[Type.length - 1] : Type).prototype;
                    instance = new Constructor();
                    returnedValue = invoke(Type, instance, locals);
    
                    return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance;
        }
    
        return {    //返回的对象,其实就是注入器
                    invoke: invoke,
                    instantiate: instantiate,
                    get: getService,
                    annotate: annotate,    //可以用来分析一个函数的签名
                    has: function(name) {
                        return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
                    }
        };
    }

    注入器总共有invoke,instantiate,get,annotate,has五个方法,其中,annotate用来分析一个函数的签名,也就是函数的参数,invoke用来调用一个函数,get用来获得一个服务的实例对象,instantiate用来实例化一个对象。

    angularJS在初始化启动时,注册了一些内置的provider。在publishExternalAPI方法中:

    $provide.provider({
                        $anchorScroll: $AnchorScrollProvider,
                        $animate: $AnimateProvider,
                        $browser: $BrowserProvider,
                        $cacheFactory: $CacheFactoryProvider,
                        $controller: $ControllerProvider,
                        $document: $DocumentProvider,
                        $exceptionHandler: $ExceptionHandlerProvider,
                        $filter: $FilterProvider,
                        $interpolate: $InterpolateProvider,
                        $interval: $IntervalProvider,
                        $http: $HttpProvider,
                        $httpBackend: $HttpBackendProvider,
                        $location: $LocationProvider,
                        $log: $LogProvider,
                        $parse: $ParseProvider,
                        $rootScope: $RootScopeProvider,
                        $q: $QProvider,
                        $sce: $SceProvider,
                        $sceDelegate: $SceDelegateProvider,
                        $sniffer: $SnifferProvider,
                        $templateCache: $TemplateCacheProvider,
                        $timeout: $TimeoutProvider,
                        $window: $WindowProvider
    });

    我们以$controller: $ControllerProvider,为例子,来看下内置的provider是如何定义的?

        function $ControllerProvider() {
            var controllers = {},
                CNTRL_REG = /^(S+)(s+ass+(w+))?$/;
            this.register = function(name, constructor) {    
                assertNotHasOwnProperty(name, 'controller');
                if (isObject(name)) {
                    extend(controllers, name);
                } else {
                    controllers[name] = constructor;
                }
            };
            this.$get = ['$injector', '$window', function($injector, $window) {    //provider必须有$get方法
                return function(expression, locals) {
                    var instance, match, constructor, identifier;
                    if(isString(expression)) {
                        match = expression.match(CNTRL_REG),
                            constructor = match[1],
                            identifier = match[3];
                        expression = controllers.hasOwnProperty(constructor)
                            ? controllers[constructor]
                            : getter(locals.$scope, constructor, true) || getter($window, constructor, true);
                        assertArgFn(expression, constructor, true);
                    }
                    instance = $injector.instantiate(expression, locals);    //当你想去拿控制器,也就是controller实例时,实际上是注入器帮你实例化的。
                    if (identifier) {
                        if (!(locals && typeof locals.$scope == 'object')) {
                            throw minErr('$controller')('noscp',
                                "Cannot export controller '{0}' as '{1}'! No $scope object provided via `locals`.",
                                constructor || expression.name, identifier);
                        }
                        locals.$scope[identifier] = instance;
                    }
                    return instance;
                };
            }];
        }

    当你的应用中,需要控制器实例对象时,也就是需要controller这个服务时,实际上是注入器在ControllerProvider(控制器服务提供者)中实例化一个控制器实例对象,然后给应用的(注入进去)。

    加油!

  • 相关阅读:
    pytorch和tensorflow
    创建用户
    linux 软件安装
    python 常见错误集锦
    Anaconda常规用法
    两个电脑之间文件快穿-基于用一个局域网和python的使用
    python-pip使用整理
    时间序列 -prophet
    SQL习题集锦
    取色器RGB转换htlm
  • 原文地址:https://www.cnblogs.com/chaojidan/p/4268918.html
Copyright © 2020-2023  润新知