angularJS加載進來後,會有一個當即執行函數調用,在源代碼的最下面是angular初始化的地方。代碼展現:html
bindJQuery(); publishExternalAPI(angular); jqLite(document).ready(function() { angularInit(document, bootstrap); });
bindJQuery方法的做用是:檢查你的主頁面是否引用了jquery,沒有的話,就用angular自己自帶的JQLite,不然使用你引用的jquery。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核心的模塊。bootstrap
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方法講完以後,接下來就是app
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函數。ide
加油!函數