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+as\s+(\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(控制器服务提供者)中实例化一个控制器实例对象,然后给应用的(注入进去)。

加油!

时间: 2024-11-03 09:36:18

angular核心原理解析2:注入器的创建和使用的相关文章

angular核心原理解析1:angular自启动过程

angularJS的源代码整体上来说是一个自执行函数,在angularJS加载完成后,就会自动执行了. angular源代码中: angular = window.angular || (window.angular = {}) 定义一个全局的angular空对象. 然后: bindJQuery(); //绑定jQuery publishExternalAPI(angular); //扩展angular对象的方法和属性 jqLite(document).ready(function() { an

angular核心原理解析3:指令的执行过程

指令的执行过程分析. 我们知道指令的执行分两个阶段,一个是compile,一个是link. 我们可以在指令中自定义compile和link. 首先,我们来讲解如何自定义link函数 举个例子: <!doctype html> <html ng-app="myModule"> <head> </head> <body> <hello></hello> </body> <script sr

Promise核心原理解析

作者: HerryLo 本文永久有效链接: https://github.com/AttemptWeb...... Promises对象被用于表示一个异步操作的最终完成 (或失败), 及其结果值.主要是为了解决异步操作的问题. #Promise对象的状态 一个 Promise对象有以下三种状态: pending: 初始状态,既不是成功,也不是失败状态. fulfilled(resolved): 意味着操作成功完成. rejected: 意味着操作失败. Promise对象内部运行的一个变化, 变

GeoHash核心原理解析 - OPEN 开发经验库

阅读目录 引子 一.感性认识GeoHash 二.GeoHash算法的步骤 三.GeoHash Base32编码长度与精度 三.GeoHash算法 四.使用注意点 引子 机机是个好动又好学的孩子,平日里就喜欢拿着手机地图点点按按来查询一些好玩的东西.某一天机机到北海公园游玩,肚肚饿了,于是乎打开手机地图,搜索北海公园附近的餐馆,并选了其中一家用餐. 饭饱之后机机开始反思了,地图后台如何根据自己所在位置查询来查询附近餐馆的呢?苦思冥想了半天,机机想出了个方法:计算所在位置P与北京所有餐馆 的距离,然

GeoHash核心原理解析

引子 机机是个好动又好学的孩子,平日里就喜欢拿着手机地图点点按按来查询一些好玩的东西.某一天机机到北海公园游玩,肚肚饿了,于是乎打开手机地图,搜索北海公园附近的餐馆,并选了其中一家用餐. 饭饱之后机机开始反思了,地图后台如何根据自己所在位置查询来查询附近餐馆的呢?苦思冥想了半天,机机想出了个方法:计算所在位置P与北京所有餐馆的距离,然后返回距离<=1000米的餐馆.小得意了一会儿,机机发现北京的餐馆何其多啊,这样计算不得了,于是想了,既然知道经纬度了,那它应该知道自己在西城区,那应该计算所在位置

GeoHash原理解析

GeoHash 核心原理解析       引子 一提到索引,大家脑子里马上浮现出B树索引,因为大量的数据库(如MySQL.oracle.PostgreSQL等)都在使用B树.B树索引本质上是对索引字段进行排序,然后通过类似二分查找的方法进行快速查找,即它要求索引的字段是可排序的,一般而言,可排序的是一维字段,比如时间.年龄.薪水等等.但是对于空间上的一个点(二维,包括经度和纬度),如何排序呢?又如何索引呢?解决的方法很多,下文介绍一种方法来解决这一问题.   思想:如果能通过某种方法将二维的点数

深入解析Koa之核心原理

这篇文章主要介绍了玩转Koa之核心原理分析,本文从封装创建应用程序函数.扩展res和req.中间件实现原理.异常处理的等这几个方面来介绍,写的十分的全面细致,具有一定的参考价值,对此有需要的朋友可以参考学习下.如有不足之处,欢迎批评指正. Koa作为下一代Web开发框架,不仅让我们体验到了async/await语法带来同步方式书写异步代码的酸爽,而且本身简洁的特点,更加利于开发者结合业务本身进行扩展. 本文从以下几个方面解读Koa源码: 封装创建应用程序函数 扩展res和req 中间件实现原理

【转】Angular运行原理揭秘 Part 1

当你用AngularJS写的应用越多, 你会越发的觉得它相当神奇. 之前我用AngularJS实现了相当多酷炫的效果, 所以我决定去看看它的源码, 我想这样也许我能知道它的原理. 下面是我从源码中找到的一些可以了解AngularJS那些高级(和隐藏)功能如何实现的代码. 1) 依赖注入的实现原理 依赖注入(DI)让我们可以不用自己实例化就能创建依赖对象的方法. 简单的来说, 依赖是以注入的方式传递的. 在Web应用中, Angular让我们可以通过DI来创建像Controllers和Direct

Spring Boot启动原理解析

Spring Boot启动原理解析http://www.cnblogs.com/moonandstar08/p/6550758.html 前言 前面几章我们见识了SpringBoot为我们做的自动配置,确实方便快捷,但是对于新手来说,如果不大懂SpringBoot内部启动原理,以后难免会吃亏.所以这次博主就跟你们一起一步步揭开SpringBoot的神秘面纱,让它不在神秘. 正文 我们开发任何一个Spring Boot项目,都会用到如下的启动类 从上面代码可以看出,Annotation定义(@Sp