angularJS加载进来后,会有一个立即执行函数调用,在源代码的最下面是angular初始化的地方。代码展示:

bindJQuery();

publishExternalAPI(angular);

jqLite(document).ready(function() {
     angularInit(document, bootstrap);
});

bindJQuery方法的作用是:检查你的主页面是否引用了jquery,没有的话,就用angular本身自带的JQLite,否则使用你引用的jquery。

    function bindJQuery() {
        jQuery = window.jQuery;     //如果在主页面加载了jQuery库,这里的jQuery就会存在
        if (jQuery) {
            jqLite = jQuery;
       ......
        } else {
            jqLite = JQLite;    //如果没有引用jQuery库,就使用自带的JQLite。
        }
        angular.element = jqLite;      //把结果赋给angular.element上
    }

publishExternalAPI方法的作用是:首先绑定一些方法到angular对象上,然后就是初始化angular核心的模块。

function publishExternalAPI(angular){
        extend(angular, {              //绑定方法到angular对象上
              'bootstrap': bootstrap,

        'extend': extend,

        'element': jqLite,

        'injector': createInjector,
         ......
        });
        angularModule = setupModuleLoader(window);      // 此方法会把angular对象绑定到window上,然后把一个函数绑定到angular的module属性上,最后返回这个函数,这个函数是一个模块加载器,主要作用是创建和获取模块。这里的angularModule函数就是angular.module函数。
        try {
            angularModule('ngLocale');
        } catch (e) {
            angularModule('ngLocale', []).provider('$locale', $LocaleProvider);    //创建一个名为ngLocale的模块,并在这个模块上定义一个名为$locale的$LocaleProvider服务提供者。这里的provider方法,是把方法中的参数都存到invokeQueue数组中,以便在后面调用,从setupModuleLoader方法中很容易知道。
        }
        angularModule('ng', ['ngLocale'], ['$provide',      //创建一个名为ng的模块,这个模块依赖于ngLocale模块。
            function ngModule($provide) {
                $provide.provider({
                    $$sanitizeUri: $$SanitizeUriProvider
                });
                $provide.provider('$compile', $CompileProvider).     //ng模块中,定义一个名为$compile的$CompileProvider服务提供者
                    directive({
                        a: htmlAnchorDirective,
                        input: inputDirective,
                        textarea: inputDirective,
                        form: formDirective,
                        option: optionDirective,
                        ngBind: ngBindDirective,
                        ngClass: ngClassDirective,
                        ngController: ngControllerDirective,
                        ngForm: ngFormDirective,
                        ngHide: ngHideDirective,
                        ngIf: ngIfDirective,
                        ngInit: ngInitDirective,
                        ngRepeat: ngRepeatDirective,
                        ngShow: ngShowDirective,
                        ngOptions: ngOptionsDirective,
                        ngTransclude: ngTranscludeDirective,
                        ngModel: ngModelDirective,
                        ngList: ngListDirective,
                        ngChange: ngChangeDirective,
                        required: requiredDirective,
                        ngRequired: requiredDirective,
                        ngValue: ngValueDirective
                    });
                $provide.provider({          //在ng模块中,定义一系列的服务提供者
                    $animate: $AnimateProvider,
                    $controller: $ControllerProvider,
                    $filter: $FilterProvider,
                    $http: $HttpProvider,
                    $location: $LocationProvider,
                    $parse: $ParseProvider,
                    $rootScope: $RootScopeProvider,
                    $window: $WindowProvider
                });
            }
        ]);
    }

setupModuleLoader方法的源码:

    function setupModuleLoader(window) {     function ensure(obj, name, factory) {
            return obj[name] || (obj[name] = factory());
        }
        var angular = ensure(window, 'angular', Object);    //设置window.angular等于一个空对象     return ensure(angular, 'module', function() {         //把angular.module设置成这个module函数,并返回这个函数。
            var modules = {};
            return function module(name, requires, configFn) {   //当我们通过var demo1 = angular.module('demoApp', []);创建一个模块时,它返回的是moduleInstance。而这个moduleInstance对象有factory(),controller(),directive(),config(),run()等方法可以调用。
          if (requires && modules.hasOwnProperty(name)) {   //如果有同名的模块已经创建过,就把以前的那个模块删除。这里使用的是一个闭包,因为每次调用angular.module进行模块的创建时,访问的modules对象是在外层的匿名函数中定义的,本来一个函数执行结束后,就会销毁里面的变量,虽然这里匿名函数已经执行结束了,但是由于内部函数module引用了此变量modules,所以即便外层的匿名函数执行结束了,它里面定义的modules变量也不会销毁。通过闭包,我们可以定义一个私有的变量modules,只能通过规定的方法angular.module进行访问,外部无法操作这个私有的变量modules。
                    modules[name] = null;
                }
                return ensure(modules, name, function() {      //modules[demoApp] = moduleInstance,并返回这个moduleInstance。
                    var invokeQueue = [];
                    var runBlocks = [];
                    var config = invokeLater('$injector', 'invoke');
                    var moduleInstance = {    //模块实例的方法
                        requires: requires,
                        provider: invokeLater('$provide', 'provider'),
                        factory: invokeLater('$provide', 'factory'),
                        service: invokeLater('$provide', 'service'),
                        value: invokeLater('$provide', 'value'),
                        constant: invokeLater('$provide', 'constant', 'unshift'),
                        animation: invokeLater('$animateProvider', 'register'),
                        filter: invokeLater('$filterProvider', 'register'),       //当我们通过一个模块实例创建一个过滤器时,调用的是invokeLater方法返回的匿名函数function(){    invokeQueue['push']([$filterProvider, register, arguments]);   return moduleInstance;    }
                        controller: invokeLater('$controllerProvider', 'register'),
                        directive: invokeLater('$compileProvider', 'directive'),
                        config: config,
                        run: function(block) {
                            runBlocks.push(block);
                            return this;
                        }
                    };                        if(configFn){    //当调用angular.module方法传入了三个参数时,就会执行config方法,上面在定义ng模块时,就会传入第三个参数。              config(configFn);    //config方法其实就是invokeLater方法执行后的返回值。这里执行之后,也是对数组invokeQueue进行push操作。当ng模块创建时,invokeQueue = [  [ $injector, invoke, [[$provide, function ngModule(){}]] ]    ]。            }
                    return  moduleInstance;
                    function invokeLater(provider, method, insertMethod) {
                        return function() {
                            invokeQueue[insertMethod || 'push']([provider, method, arguments]);
                            return moduleInstance;
                        };
                    }
                });
            };
        });
    }

我们举个例子说下上面的源码:

var demo1 = angular.module('demoApp', []);
demo1.filter('reverse', function(){     当调用此方法时,其实就是调用invokeQueue["push"]()方法,并且返回此模块实例,以便进行链式操作。比如:demo1.filter().directive()。当调用demo1的filter方法时,这里会执行invokeQueue["push"]([  $filterProvider, register, ["reverse", function(){}] ]),而这只是一个数组的push操作,也就是说,invokeQueue = [  [ $filterProvider, register, ["reverse", function(){}] ]  ];
    return function(input, uppercase) {
      var out = "";
      for (var i = 0; i < input.length; i++) {
        out = input.charAt(i) + out;
      }
      if (uppercase) {
        out = out.toUpperCase();
      }
      return out;
    }
  });

publishExternalAPI方法讲完之后,接下来就是

jqLite(document).ready(function() {    //文档加载完成后,执行angularInit方法,我们经常说的:文档加载完成后,angular才启动的意思就是在这里。
        angularInit(document, bootstrap);
});
    function angularInit(element, bootstrap) {
        var elements = [element],
            appElement,
            module,
            names = ['ng:app', 'ng-app', 'x-ng-app', 'data-ng-app'],   //angular有4种方式在页面上定义angular应用
            NG_APP_CLASS_REGEXP = /\sng[:\-]app(:\s*([\w\d_]+);?)?\s/;
        function append(element) {
            element && elements.push(element);
        }
        forEach(names, function(name) {
            names[name] = true;
            append(document.getElementById(name));
            name = name.replace(':', '\\:');
            if (element.querySelectorAll) {
                forEach(element.querySelectorAll('.' + name), append);
                forEach(element.querySelectorAll('.' + name + '\\:'), append);
                forEach(element.querySelectorAll('[' + name + ']'), append);
            }
        });    //针对4种定义方式,在页面上获取定义为angular应用的元素节点,
        forEach(elements, function(element) {
            if (!appElement) {    //appElement只定义一次
                var className = ' ' + element.className + ' ';
                var match = NG_APP_CLASS_REGEXP.exec(className);
                if (match) {
                    appElement = element;
                    module = (match[2] || '').replace(/\s+/g, ',');
                } else {
                    forEach(element.attributes, function(attr) {
                        if (!appElement && names[attr.name]) {
                            appElement = element;
                            module = attr.value;
                        }
                    });
                }
            }
        });    //定义angular应用的元素,也就是在页面上写有ng-app(有4种方式定义)的元素定义为appElement。
        if (appElement) {
            bootstrap(appElement, module ? [module] : []); //如果页面上有定义angular应用的元素,就启动。
        }
    }
    function bootstrap(element, modules) {
        var doBootstrap = function() {      //定义一个函数
            element = jqLite(element);
            if (element.injector()) {    //如果此元素的angular应用已经启动过了,就抛出错误
                var tag = (element[0] === document) ? 'document' : startingTag(element);
                throw ngMinErr('btstrpd', "App Already Bootstrapped with this Element '{0}'", tag);
            }
            modules = modules || [];
            modules.unshift(['$provide', function($provide) {
                $provide.value('$rootElement', element);
            }]);
            modules.unshift('ng');     //这里,我们假设在页面上定义了ng-app的元素,没有添加任何的多余的东西。因此这里的modules=["ng",["$provide",function($provide){}]]。
            var injector = createInjector(modules);   //这个方法非常重要,它把我们angular应用需要初始化的模块数组传进去后,进行加载,并创建一个注册器实例对象,最后返回这个注册器实例对象。
            injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector', '$animate',  //调用注册器实例对象的invoke方法
                function(scope, element, compile, injector, animate) {
                    scope.$apply(function() {
                        element.data('$injector', injector);
                        compile(element)(scope);
                    });
                }]
            );
            return injector;
        };
        var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
        if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {   //如果window.name不是以NG_DEFER_BOOTSTRAP!开头的话,就进入if语句,执行上面定义的方法。
            return doBootstrap();
        }
        window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
        angular.resumeBootstrap = function(extraModules) {
            forEach(extraModules, function(module) {
                modules.push(module);
            });
            doBootstrap();
        };
    }

上面的invoke方法执行后,会调用compile方法,此方法里面会先生成编译实例,然后通过编译实例去编译应用的元素,编译的核心是生成指令对应的link函数。

加油!

AngularJS源码解析1:angular自启动过程的更多相关文章

  1. AngularJS源码解析4:Parse解析器的详解

    $ParseProvider简介 此服务提供者也是angularjs中用的比较多的,下面我们来详细的说下这个provider. function $ParseProvider() { var cach ...

  2. 解析jQuery中extend方法--源码解析以及递归的过程《二》

    源码解析 在解析代码之前,首先要了解extend函数要解决什么问题,以及传入不同的参数,会达到怎样的效果.extend函数内部处理传入的不同参数,返回处理后的对象. extend函数用来扩展对象,增加 ...

  3. Fabric1.4源码解析:链码实例化过程

    之前说完了链码的安装过程,接下来说一下链码的实例化过程好了,再然后是链码的调用过程.其实这几个过程内容已经很相似了,都是涉及到Proposal,不过整体流程还是要说一下的. 同样,切入点仍然是fabr ...

  4. AngularJS源码解析3:RootScope的创建过程

    RootScopeProvider简介 RootScopeProvider是angularjs里面比较活跃的一个provider.它主要用来生成实例rootScope,它代表angularjs应用的根 ...

  5. 【Spring源码解析】—— 结合SpringMVC过程理解IOC容器初始化

    关于IOC容器的初始化,结合之前SpringMVC的demo,对其过程进行一个相对详细的梳理,主要分为几个部分: 一.IOC的初始化过程,结合代码和debug过程重点说明 1. 为什么要debug? ...

  6. Spring源码解析 – AnnotationConfigApplicationContext容器创建过程

    Spring在BeanFactory基础上提供了一些列具体容器的实现,其中AnnotationConfigApplicationContext是一个用来管理注解bean的容器,从AnnotationC ...

  7. Fabric1.4源码解析: 链码容器启动过程

    想写点东西记录一下最近看的一些Fabric源码,本文使用的是fabric1.4的版本,所以对于其他版本的fabric,内容可能会有所不同. 本文想针对Fabric中链码容器的启动过程进行源码的解析.这 ...

  8. AngularJS源码解析2:注入器的详解

    上一课,没有讲createInjector方法,只是讲了它的主要作用,这一课,详细来讲一下这个方法.此方法,最终返回的注册器实例对象有以下几个方法: invoke, instantiate, get, ...

  9. Spring源码解析-Web容器启动过程

    Web容器启动过程,主要讲解Servlet和Spring容器结合的内容. 流程图如下: Web容器启动的Root Context是有ContextLoaderListener,一般使用spring,都 ...

随机推荐

  1. bzoj3110

    3110: [Zjoi2013]K大数查询 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 5881  Solved: 1958[Submit][Sta ...

  2. 图解:Arcgis Server 安装

    必须保证IIS配置正常,否则arcserver安装不会成功. 选择安装路径,还是尽量不要在有括号的文件夹下. 设置服务名,最好使用默认的. 点击完成后会要求进行服务配置. 在windows serve ...

  3. C#添加测量运行时间

    使用范围 使用模块化开发,每个模块都有初始化功能,初始化功能可能包括:加载配置表,初始化事件,初始化设置 那么如果想测量每个模块的Init时间呢?Net框架已经提供了测量运行的方法 System.Di ...

  4. AC日记——潜伏者 洛谷 P1071 (模拟)

    题目描述 R 国和 S 国正陷入战火之中,双方都互派间谍,潜入对方内部,伺机行动.历尽艰险后,潜伏于 S 国的 R 国间谍小 C 终于摸清了 S 国军用密码的编码规则: 1. S 国军方内部欲发送的原 ...

  5. jstl表达式替换某些字符

    转自:http://www.yiibai.com/jsp/jstl_function_replace.html fn:replace() 函数替换一个字符串与另一个字符串的所有匹配. 语法 fn:re ...

  6. SQL Server强制删除发布

    今日发现SQL Server 中 存在以前(系统还原前)的发布内容,使用鼠标->右键,选择删除,失败.   可使用语句: EXEC SP_REMOVEDBREPLICATION '发布数据库名称 ...

  7. 如何煉成NET架構師

    微软的DotNet 开发绝对是属于那种入门容易提高难的技术.而要能够成为DotNet 架构师没有三年或更长时间的编码积累基本上是不可能的.特别是在大型软件项目中,架构师是项目核心成员,承上启下,因此 ...

  8. 通知/代理/block 三者比对

    通知 :   “一对多”,”多对一” 传值 四个步骤:  1.发送通知2.创建监听者3.接收通知4.移除监听者 使用场景:1- 很多控制器都需要知道一个事件,应该用通知:2 - 相隔多层的两个控制器之 ...

  9. 关于《ASP.NET MVC企业级实战》

    大家好,我的书<ASP.NET MVC企业级实战>已经出版啦,感谢大家过去的关注与支持!前言部分,出版的时候漏了部分内容,我这里将其贴出来. 本书提供源码和教学PPT课件!(源码在书中第3 ...

  10. 90道Python面试题,做对80%直击年薪40w

    转发链接: 1.一行代码实现1--100之和 利用sum()函数求和 2.如何在一个函数内部修改全局变量 函数内部global声明 修改全局变量 3.列出5个python标准库 os:提供了不少与操作 ...