1. 程式人生 > >AngularJS 原始碼分析1

AngularJS 原始碼分析1

AngularJS簡介

angularjs 是google出品的一款MVVM前端框架,包含一個精簡的類jquery庫,創新的開發了以指令的方式來元件化前端開發,可以去它的官網看看,請戳這裡

再貼上一個本文原始碼分析對應的angularjs原始碼合併版本1.2.4,精簡版的,除掉了所有的註釋, 請戳這裡

從啟動開始說起

定位到4939行,這裡是angularjs開始執行初始化的地方,見程式碼

bindJQuery(), publishExternalAPI(angular), jqLite(document).ready(function() {
        angularInit(document, bootstrap)
    })

bindJQuery方法是檢查是否引用jquery,沒有的話jqlite就用本身自帶的,否則切換到jquery中去.這個好理解

publishExternalAPI這個方法是繫結一些公共的方法到angular下面,這些是可以在網站中訪問到的,像forEach,copy等公共方法,還有一個重要的任務就是初始化angular核心的模組,publishExternalAPI在465行,現在我們來分析裡面的一些重要的程式碼

function publishExternalAPI(angular) {
        extend(angular, {
            bootstrap: bootstrap,
            copy: copy,
            extend: extend,
            equals: equals,
            element: jqLite,
            forEach: forEach,
            injector: createInjector,
            noop: noop,
            bind: bind,
            toJson: toJson,
            fromJson: fromJson,
            identity: identity,
            isUndefined: isUndefined,
            isDefined: isDefined,
            isString: isString,
            isFunction: isFunction,
            isObject: isObject,
            isNumber: isNumber,
            isElement: isElement,
            isArray: isArray,
            version: version,
            isDate: isDate,
            lowercase: lowercase,
            uppercase: uppercase,
            callbacks: {
                counter: 0
            },
            $$minErr: minErr,
            $$csp: csp
        }), angularModule = setupModuleLoader(window);
        try {
            angularModule("ngLocale")
        } catch (e) {
            angularModule("ngLocale", []).provider("$locale", $LocaleProvider)
        }
        angularModule("ng", ["ngLocale"], ["$provide",
            function($provide) {
                $provide.provider("$compile", $CompileProvider).directive({
                    a: htmlAnchorDirective,
                    input: inputDirective,
                    textarea: inputDirective,
                    form: formDirective,
                    script: scriptDirective,
                    select: selectDirective,
                    style: styleDirective,
                    option: optionDirective,
                    ngBind: ngBindDirective,
                    ngBindHtml: ngBindHtmlDirective,
                    ngBindTemplate: ngBindTemplateDirective,
                    ngClass: ngClassDirective,
                    ngClassEven: ngClassEvenDirective,
                    ngClassOdd: ngClassOddDirective,
                    ngCloak: ngCloakDirective,
                    ngController: ngControllerDirective,
                    ngForm: ngFormDirective,
                    ngHide: ngHideDirective,
                    ngIf: ngIfDirective,
                    ngInclude: ngIncludeDirective,
                    ngInit: ngInitDirective,
                    ngNonBindable: ngNonBindableDirective,
                    ngPluralize: ngPluralizeDirective,
                    ngRepeat: ngRepeatDirective,
                    ngShow: ngShowDirective,
                    ngStyle: ngStyleDirective,
                    ngSwitch: ngSwitchDirective,
                    ngSwitchWhen: ngSwitchWhenDirective,
                    ngSwitchDefault: ngSwitchDefaultDirective,
                    ngOptions: ngOptionsDirective,
                    ngTransclude: ngTranscludeDirective,
                    ngModel: ngModelDirective,
                    ngList: ngListDirective,
                    ngChange: ngChangeDirective,
                    required: requiredDirective,
                    ngRequired: requiredDirective,
                    ngValue: ngValueDirective
                }).directive(ngAttributeAliasDirectives).directive(ngEventDirectives), $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
                })
            }
        ])
    }

方法體中的setupModuleLoader方法是一個模組載入器,這也是一個關鍵方法, 主要作用是建立和獲取模組,程式碼見417行.

function setupModuleLoader(window) {

        function ensure(obj, name, factory) {
            return obj[name] || (obj[name] = factory())
        }

        var $injectorMinErr = minErr("$injector"),
            ngMinErr = minErr("ng");

        return ensure(ensure(window, "angular", Object), "module", function() {
            var modules = {};

            return function(name, requires, configFn) {
                var assertNotHasOwnProperty = function(name, context) {
                    if ("hasOwnProperty" === name) throw ngMinErr("badname", "hasOwnProperty is not a valid {0} name", context)
                };
                return assertNotHasOwnProperty(name, "module"), requires && modules.hasOwnProperty(name) && (modules[name] = null), ensure(modules, name, function() {
                    function invokeLater(provider, method, insertMethod) {
                        return function() {
                            return invokeQueue[insertMethod || "push"]([provider, method, arguments]), moduleInstance
                        }
                    }
                    if (!requires) throw $injectorMinErr("nomod", "Module '{0}' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.", name);
                    var invokeQueue = [],
                        runBlocks = [],
                        config = invokeLater("$injector", "invoke"),
                        moduleInstance = {
                            _invokeQueue: invokeQueue,
                            _runBlocks: runBlocks,
                            requires: requires,
                            name: name,
                            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"),
                            controller: invokeLater("$controllerProvider", "register"),
                            directive: invokeLater("$compileProvider", "directive"),
                            config: config,
                            run: function(block) {
                                return runBlocks.push(block), this
                            }
                        };
                    return configFn && config(configFn), moduleInstance
                })
            }
        })
    }

上面publishExternalAPI 方法中的angularModule = setupModuleLoader(window);是在window下面建立全域性的angular物件,並且返回一個高階函式,賦值給了angular.module屬性,所以一般我們建立模組都是用angular.module方法.這裡的angularModule其實就是相當於angular.module

angular.module在建立模組的時候,傳遞一個引數的時候,是獲取模組;傳遞一個以上的是建立新模組;該方法返回的是一個moduleInstance物件,它的任務就是來建立控制器,服務,指令,以及配置方法,全域性執行方法,而且是鏈式呼叫,因為每個方法都會返回moduleInstance,看這裡

function invokeLater(provider, method, insertMethod) {
    return function() {
        return invokeQueue[insertMethod || "push"]([provider, method, arguments]), moduleInstance
    }
}

此處的return invokeQueueinsertMethod || "push", moduleInstance,逗號表示式是返回最後一個值

再來一個angular.module在專案中運用的程式碼

angular.module('demoApp', [])
.factory()
.controller()
.directive()
.config()
.run();

接下來再看publishExternalAPI的程式碼,因為ngLocale預設沒有建立,所以angularModule("ngLocale")這個直接異常,跳到catch裡執行angularModule("ngLocale", []).provider("$locale", $LocaleProvider),記住這裡的provider方法,預設是把它的引數都存到invokeQueue陣列中,以便在後面用到.

接下來開始建立ng模組,它依賴上面的ngLocale模組,注意建立模組的時候傳了第三個引數,當建立模組的時候傳了三個引數,預設第三引數會執行config(configFn),這個方法也是把相應的引數放入invokeQueue陣列中,只不過前兩引數是$injector,invoke,這裡先透露一下,其實所有invokeQueue陣列項中,三個引數的意思:第一個引數呼叫第二個引數,然後傳遞第三個引數,這個後面會講到.

這裡說下ng模組中第三個引數裡的函式體,這裡主要做了兩件事,初始了$compile服務,並且利用compile服務的directive方法,把一些常用的指令都儲存到compile服務中的一個內部陣列中.

這裡先說下$provide.provider,這個在angular裡用的比較多,其實就是提前把定義的provider放入DI函式內的providerCache內,看如下程式碼,在740行

function createInjector(modulesToLoad) {
        function supportObject(delegate) {
            return function(key, value) {
                return isObject(key) ? (forEach(key, reverseParams(delegate)), void 0) : delegate(key, value)
            }
        }

        function provider(name, provider_) {
            if (assertNotHasOwnProperty(name, "service"), (isFunction(provider_) || isArray(provider_)) && (provider_ = providerInjector.instantiate(provider_)), !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
        }

        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
                })
            }
        }

        function loadModules(modulesToLoad) {
            var moduleFn, invokeQueue, i, ii, runBlocks = [];
            return forEach(modulesToLoad, function(module) {
                if (!loadedModules.get(module)) {
                    loadedModules.put(module, !0);
                    try {
                        if (isString(module))
                            for (moduleFn = angularModule(module), runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks), invokeQueue = moduleFn._invokeQueue, i = 0, ii = invokeQueue.length; ii > i; i++) {
                                var invokeArgs = invokeQueue[i],
                                    provider = providerInjector.get(invokeArgs[0]);
                                provider[invokeArgs[1]].apply(provider, invokeArgs[2])
                            } else isFunction(module) ? runBlocks.push(providerInjector.invoke(module)) : isArray(module) ? runBlocks.push(providerInjector.invoke(module)) : assertArgFn(module, "module")
                    } catch (e) {
                        throw isArray(module) && (module = module[module.length - 1]), e.message && e.stack && -1 == e.stack.indexOf(e.message) && (e = e.message + "\n" + e.stack), $injectorMinErr("modulerr", "Failed to instantiate module {0} due to:\n{1}", module, e.stack || e.message || e)
                    }
                }
            }),runBlocks
        }

        function createInternalInjector(cache, factory) {
            function getService(serviceName) {
                if (cache.hasOwnProperty(serviceName)) {
                    if (cache[serviceName] === INSTANTIATING) throw $injectorMinErr("cdep", "Circular dependency found: {0}", path.join("  i; i++) {
                    if (key = $inject[i], "string" != typeof key) throw $injectorMinErr("itkn", "Incorrect injection token! Expected service name as string, got {0}", key);
                    args.push(locals && locals.hasOwnProperty(key) ? locals[key] : getService(key))
                }
                switch (fn.$inject || (fn = fn[length]), self ? -1 : args.length) {
                    case 0:
                        return fn();
                    case 1:
                        return fn(args[0]);
                    case 2:
                        return fn(args[0], args[1]);
                    case 3:
                        return fn(args[0], args[1], args[2]);
                    case 4:
                        return fn(args[0], args[1], args[2], args[3]);
                    case 5:
                        return fn(args[0], args[1], args[2], args[3], args[4]);
                    case 6:
                        return fn(args[0], args[1], args[2], args[3], args[4], args[5]);
                    case 7:
                        return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
                    case 8:
                        return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]);
                    case 9:
                        return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]);
                    case 10:
                        return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9]);
                    default:
                        return fn.apply(self, args)
                }
            }

            function instantiate(Type, locals) {
                var instance, returnedValue, Constructor = function() {};
                return Constructor.prototype = (isArray(Type) ? Type[Type.length - 1] : Type).prototype, instance = new Constructor, returnedValue = invoke(Type, instance, locals), 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)
                }
            }
        }
        var INSTANTIATING = {}, providerSuffix = "Provider",
            path = [],
            loadedModules = new HashMap,
            providerCache = {
                $provide: {
                    provider: supportObject(provider),
                    factory: supportObject(factory),
                    service: supportObject(service),
                    value: supportObject(value),
                    constant: supportObject(constant),
                    decorator: decorator
                }
            }, providerInjector = providerCache.$injector = createInternalInjector(providerCache, function() {
                throw $injectorMinErr("unpr", "Unknown provider: {0}", path.join(" 

上面說的DI其實就是上面的createInjector函式,這個是angularjs管理依賴注入的核心函式,然後再看$provide.provider,其實就是呼叫內部函式provider

function provider(name, provider_) {
    if (assertNotHasOwnProperty(name, "service"), (isFunction(provider_) || isArray(provider_)) && (provider_ = providerInjector.instantiate(provider_)), !provider_.$get) throw $injectorMinErr("pget", "Provider '{0}' must define $get factory method.", name);
    return providerCache[name + providerSuffix] = provider_
}

注意這裡的providerCache,它儲存了所有呼叫provider方法的provider_

好了,好像說的有點遠了,我們再回到初始化的程式碼

說完了publishExternalAPI的程式碼,我們再了看看angularInit方法,這個方法的作用就是先找到帶angular專案標識的元素,然後呼叫bootstrap方法,我們重點來看看bootstrap方法,見344行

function bootstrap(element, modules) {
        var doBootstrap = function() {
            if (element = jqLite(element), element.injector()) {
                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");
            var injector = createInjector(modules);
            return injector.invoke(["$rootScope", "$rootElement", "$compile", "$injector", "$animate",
                function(scope, element, compile, injector) {
                    scope.$apply(function() {
                        element.data("$injector", injector), compile(element)(scope)
                    })
                }
            ]), injector
        }, NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
        return window && !NG_DEFER_BOOTSTRAP.test(window.name) ? doBootstrap() : (window.name = window.name.replace(NG_DEFER_BOOTSTRAP, ""), angular.resumeBootstrap = function(extraModules) {
            forEach(extraModules, function(module) {
                modules.push(module)
            }), doBootstrap()
        }, void 0)
    }

仔細看上面的程式碼,有一句比較關鍵var injector = createInjector(modules);,把要初始化的模組傳進DI中,並返回一個依賴物件,這裡的modules引數包含一個ng模組,一個定義$rootElement值的模組,一個業務對應的模組

現在我們重點分析createInjector的程式碼,為了方便查閱,把上面的di建構函式重新貼一下

function createInjector(modulesToLoad) {
        function supportObject(delegate) {
            return function(key, value) {
                return isObject(key) ? (forEach(key, reverseParams(delegate)), void 0) : delegate(key, value)
            }
        }

        function provider(name, provider_) {
            if (assertNotHasOwnProperty(name, "service"), (isFunction(provider_) || isArray(provider_)) && (provider_ = providerInjector.instantiate(provider_)), !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
        }

        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
                })
            }
        }

        function loadModules(modulesToLoad) {
            var moduleFn, invokeQueue, i, ii, runBlocks = [];
            return forEach(modulesToLoad, function(module) {
                if (!loadedModules.get(module)) {
                    loadedModules.put(module, !0);
                    try {
                        if (isString(module))
                            for (moduleFn = angularModule(module), runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks), invokeQueue = moduleFn._invokeQueue, i = 0, ii = invokeQueue.length; ii > i; i++) {
                                var invokeArgs = invokeQueue[i],
                                    provider = providerInjector.get(invokeArgs[0]);
                                provider[invokeArgs[1]].apply(provider, invokeArgs[2])
                            } else isFunction(module) ? runBlocks.push(providerInjector.invoke(module)) : isArray(module) ? runBlocks.push(providerInjector.invoke(module)) : assertArgFn(module, "module")
                    } catch (e) {
                        throw isArray(module) && (module = module[module.length - 1]), e.message && e.stack && -1 == e.stack.indexOf(e.message) && (e = e.message + "\n" + e.stack), $injectorMinErr("modulerr", "Failed to instantiate module {0} due to:\n{1}", module, e.stack || e.message || e)
                    }
                }
            }),runBlocks
        }

        function createInternalInjector(cache, factory) {
            function getService(serviceName) {
                if (cache.hasOwnProperty(serviceName)) {
                    if (cache[serviceName] === INSTANTIATING) throw $injectorMinErr("cdep", "Circular dependency found: {0}", path.join("  i; i++) {
                    if (key = $inject[i], "string" != typeof key) throw $injectorMinErr("itkn", "Incorrect injection token! Expected service name as string, got {0}", key);
                    args.push(locals && locals.hasOwnProperty(key) ? locals[key] : getService(key))
                }
                switch (fn.$inject || (fn = fn[length]), self ? -1 : args.length) {
                    case 0:
                        return fn();
                    case 1:
                        return fn(args[0]);
                    case 2:
                        return fn(args[0], args[1]);
                    case 3:
                        return fn(args[0], args[1], args[2]);
                    case 4:
                        return fn(args[0], args[1], args[2], args[3]);
                    case 5:
                        return fn(args[0], args[1], args[2], args[3], args[4]);
                    case 6:
                        return fn(args[0], args[1], args[2], args[3], args[4], args[5]);
                    case 7:
                        return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
                    case 8:
                        return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]);
                    case 9:
                        return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]);
                    case 10:
                        return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9]);
                    default:
                        return fn.apply(self, args)
                }
            }

            function instantiate(Type, locals) {
                var instance, returnedValue, Constructor = function() {};
                return Constructor.prototype = (isArray(Type) ? Type[Type.length - 1] : Type).prototype, instance = new Constructor, returnedValue = invoke(Type, instance, locals), 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)
                }
            }
        }
        var INSTANTIATING = {}, providerSuffix = "Provider",
            path = [],
            loadedModules = new HashMap,
            providerCache = {
                $provide: {
                    provider: supportObject(provider),
                    factory: supportObject(factory),
                    service: supportObject(service),
                    value: supportObject(value),
                    constant: supportObject(constant),
                    decorator: decorator
                }
            }, providerInjector = providerCache.$injector = createInternalInjector(providerCache, function() {
                throw $injectorMinErr("unpr", "Unknown provider: {0}", path.join(" 

首先這個函式內部包含有建立專案服務的幾個關鍵方法,比如provider,service,value,factory,其實內部呼叫的都是provider方法,而且所有的provider都必須包含一個$get屬性,只不過沒有$get屬性的,內部實現都會主動增加一個$get屬性,除了這些建立provider的方法外,還有一個內部核心的注入類,這個主要用來建立真正的例項用,並處理相關的依賴建立

這裡有幾個內部變數值得關注,providerCache這個會儲存一個$provide物件,主要用來對外提供建立服務的方法,然後這個變數會儲存所有已經註冊的provider實倒,包含$get方法的,只是沒有例項化;providerInjector變數是傳遞了providercache變數的內部di例項;instanceCache這個會儲存所有已經例項化的provider;instanceInjector是用來真正例項化一個provider的.本身是一個內部di例項.

這裡重點說下loadModules方法,因為angularjs就是依靠這個方法來載入所有的模組,以及模組依賴的provider

function loadModules(modulesToLoad) {
            var moduleFn, invokeQueue, i, ii, runBlocks = [];
            return forEach(modulesToLoad, function(module) {
                if (!loadedModules.get(module)) {
                    loadedModules.put(module, !0);
                    try {
                        if (isString(module))
                            for (moduleFn = angularModule(module), runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks), invokeQueue = moduleFn._invokeQueue, i = 0, ii = invokeQueue.length; ii > i; i++) {
                                var invokeArgs = invokeQueue[i],
                                    provider = providerInjector.get(invokeArgs[0]);
                                provider[invokeArgs[1]].apply(provider, invokeArgs[2])
                            } else isFunction(module) ? runBlocks.push(providerInjector.invoke(module)) : isArray(module) ? runBlocks.push(providerInjector.invoke(module)) : assertArgFn(module, "module")
                    } catch (e) {
                        throw isArray(module) && (module = module[module.length - 1]), e.message && e.stack && -1 == e.stack.indexOf(e.message) && (e = e.message + "\n" + e.stack), $injectorMinErr("modulerr", "Failed to instantiate module {0} due to:\n{1}", module, e.stack || e.message || e)
                    }
                }
            }),runBlocks
        }

這個方法的引數是一個數組,裡面是的資料是在doBootstrap裡定義的,上面有講

這個方法依次載入模組數組裡對應的provider,這裡用到了上面提到的_invokeQueue陣列,裡面定義儲存很多provider資訊,注意這裡的constant型別的provider會直接建立例項,跟別的provider不一樣.

var invokeArgs = invokeQueue[i],
    provider = providerInjector.get(invokeArgs[0]);
    provider[invokeArgs[1]].apply(provider, invokeArgs[2])

這裡就是利用儲存的三個引數來依次利用第一個引數呼叫第二個引數,然後傳遞第三個引數

loadModules方法最後返回一個執行塊程式碼,所以一般專案裡的run方法會在模組載入完以及config方法呼叫完之後執行.

 return forEach(loadModules(modulesToLoad), function(fn) {
            instanceInjector.invoke(fn || noop)
        }), instanceInjector

注意這裡run方法程式碼在這裡執行instanceInjector.invoke(fn || noop),一直覺的instanceInjector和providerInjector這兩個變數的定義非常讓人迷糊,嘿嘿,估計是google的人寫程式碼非常節省空間吧,這兩個變數都是內部DI例項,區別在於第二個引數,當要真正的例項化的時候,第二個引數負責真正的初始化providerCache裡保證的provider,其實就是執行它的$get方法,然後把值儲存到instanceCache中,以便保證單例使用.

var provider = providerInjector.get(servicename + providerSuffix);
return instanceInjector.invoke(provider.$get, provider)

這是instanceInjector變數第二個引數的函式體,先在providerCache裡查詢,然後把provider的$get方法傳給instanceInjector的invoke,這個會真正的生成例項.

最後說下invoke的程式碼,這裡會頻繁用一個工具方法annotate,這個是獲取一個函式的入參,並以陣列形式返回,invoke會自動的檢查要執行的函式的入參,假如已經生成例項的,則傳給函式,否則先生成依賴的例項,最後執行函式

最後當所有的模組載入完成,並且run程式碼塊也執行完成之後,接下來就是編譯頁面程式碼,給指令生成相應的link函數了

injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector', '$animate',
       function(scope, element, compile, injector, animate) {
        scope.$apply(function() {
          element.data('$injector', injector);
          compile(element)(scope);
        });
      }]
    );

這個會生成編譯例項,通過編譯例項去編譯專案起始頁,編譯的核心是生成指令對應的link函式,有點類似後端的編譯,先詞法分析,用lex,然後語法分析,用parse,最後連結,生成link函式

總結

本篇主要是分析了angularjs的模組載入以及依賴注入這部分原始碼,以後還會分析編譯以及作用域相關的原始碼,本文只是自己一點angularjs方面的心得,有錯誤希望大家提出來,一起改進改進.

作者宣告

本來不想補充這個的,但是看到本園裡把我這個文章給轉走了,盡然還寫上防止別人轉載,真是個joker

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連線,否則保留追究法律責任的權利。

相關推薦

AngularJS 原始碼分析1

AngularJS簡介 angularjs 是google出品的一款MVVM前端框架,包含一個精簡的類jquery庫,創新的開發了以指令的方式來元件化前端開發,可以去它的官網看看,請戳這裡 再貼上一個本文原始碼分析對應的angularjs原始碼合併版本1.2.4,精簡版的,除掉了所有的註釋, 請戳這裡 從啟動

Netty Pipeline原始碼分析(1)

原文連結:wangwei.one/posts/netty… 前面,我們分析了Netty EventLoop的 建立 與 啟動 原理,接下里我們來分析Netty中另外兩個重要元件—— ChannelHandler 與 Pipeline。Netty中I/O事件的傳播機制均由它負責,下面我們來看看它是如

vue原始碼分析1-new Vue做了哪些操作

首先我們可以看到vue的原始碼在github上有,大家可以克隆下來。 git地址 我們主要看src下的內容。 1.現在我們來分析下 new Vue都做了哪些操作 var app = new Vue({ el: '#app', mounted:{ console.log(t

redis原始碼分析1------dict的實現

1. 總體結構 redis的dict就是hash表,使用鏈式結構來解決key值衝突,典型的資料結構 結構體的定義如下: typedef struct dictEntry { void *key; union { void *val; uint64_t

Netty原始碼分析:1.4伺服器啟動流程

第一章節是主要是伺服器啟動的程式碼分析。章節目錄有: |———1.1初始化NioEventLoopGroup |———1.2初始化NioEventLoop |———1.3初始化NioServerSocketChannel |———1.4伺服器啟動流程 為什麼先從初始化開

Netty原始碼分析:1.3初始化NioServerSocketChannel

第一章節是主要是伺服器啟動的程式碼分析。章節目錄有: |———1.1初始化NioEventLoopGroup |———1.2初始化NioEventLoop |———1.3初始化NioServerSocketChannel |———1.4伺服器啟動流程 為什麼先從初始化開

Netty原始碼分析:1.2初始化NioEventLoop

第一章節是主要是伺服器啟動的程式碼分析。章節目錄有: |———1.1初始化NioEventLoopGroup |———1.2初始化NioEventLoop |———1.3初始化NioServerSocketChannel |———1.4伺服器啟動流程 為什麼先從初始化開

Netty原始碼分析:1.1初始化NioEventLoopGroup

第一章節是主要是伺服器啟動的程式碼分析。章節目錄有: |———1.1初始化NioEventLoopGroup |———1.2初始化NioEventLoop |———1.3初始化NioServerSocketChannel |———1.4伺服器啟動流程 為什麼先從初始化開

《2.uboot和系統移植-第5部分-2.5.uboot原始碼分析1-啟動第一階段》

《2.uboot和系統移植-第5部分-2.5.uboot原始碼分析1-啟動第一階段》 第一部分、章節目錄 2.5.1.start.S引入 2.5.2.start.S解析1 2.5.3.start.S解析2 2.5.4.start.S解析3 2.5.5.start.S解析4 2.5.6.s

MyBatis原始碼分析-1-基礎支援層-反射模組-Reflector/ReflectorFactory

本文主要介紹MyBatis的反射模組是如何實現的。 MyBatis 反射的核心類Reflector,下面我先說明它的建構函式和成員變數。具體方法下面詳解。 org.apache.ibatis.reflection.Reflector public class Reflector {

rxjs 原始碼分析1-(fromEvent)

前言 Rxjs是使用 Observables 的響應式程式設計的庫,它使編寫非同步或基於回撥的程式碼更容易。我們現在針對Rxjs 6 來進行原始碼分析,分析其實現的基本原理, 我們可以根據中文文件來學習Rxjs 的基本使用,但是這個文件是Rxjs 5 的版本。其最基本的使用區別如下,Rxjs 6的操作符都放

谷歌瀏覽器的原始碼分析 1

隨著網路技術的發展,越來越多應用都已經離不開網路,特別像人類大腦一樣的知識庫的搜尋引擎,更加是離不開功能強大的雲端計算。不過,即便雲端計算非常強大,但它還不能直接地把結果呈現給使用者,這樣就需要一個客戶端來呈現出來,這個客戶端就是瀏覽器。現在越來越多人上網,他們每一次上網,都離不開瀏覽的使用,這已經是一

Shiro原始碼分析(1)

簡介 SecurityManager:安全管理器,Shiro最核心元件。Shiro通過SecurityManager來管理內部元件例項,並通過它來提供安全管理的各種服務。 Authenticator:認證器,認證AuthenticationToken是否有

3.21以太貓原始碼分析1

概述: Cryptokitties,眾所周知的迷戀貓的遊戲,是基於以太坊平臺執行的。使用者在遊戲中可以養大、買賣並繁育“電子寵物”小貓,每隻小貓和繁衍的後代都是獨一無二的。由於它是第一款真正意義上的區塊

以太坊原始碼分析(1)go-ethereum的設計思路及模組組織形式

# go-ethereum原始碼解析因為go ethereum是最被廣泛使用的以太坊客戶端, 所以後續的原始碼分析都從github上面的這份程式碼進行分析。 然後我使用的是windows 10 64位的環境。### 搭建go ethereum除錯環境首先下載go安裝包進行安裝,因為GO的網站被牆,所以從下面

[9]【ffmpeg原始碼分析 1】av_register_all()和avcodec_register_all()

日期:2016.10.18 作者:isshe github:github.com/isshe 郵箱:[email protected] 前言 接下來打

SpringMVC原始碼分析1:SpringMVC概述

轉載自:https://blog.csdn.net/a724888/article/details/76014532 Web MVC簡介 1.1、Web開發中的請求-響應模型: 在Web世界裡,具體步驟如下: 1、  Web瀏覽器(如IE)發起請求,如訪問http:/

Leveldb資料Compaction原始碼分析(1)

Leveldb資料Compaction原始碼分析(1) 這一節來講Leveldb的資料壓縮過程,上一節講了Leveldb的資料尋找過程,文章地址為:但是最後在講Leveldb中的Leveln的層級尋找時,我想應該是有沒有看懂的,直接二分法找到sstable,然後載入快取就能找到檔案,

Egg 學習筆記-原始碼分析1

1. Egg在呼叫controller/service資料夾下的模組時,不需要require,如何實現的? 在原生Node/Koa中,當我們需要呼叫其他模組時,需要require, 非常繁瑣。(java體系都是auto import) 但在Egg中,我們可以

coreutils4.5.1 uniq.c原始碼分析1

uniq.c這個檔案其實沒讀懂,不過從程式中發現了幾個支點,下次再細細品。 第一。交換兩行的寫法。 #define SWAP_LINES(A, B)            \   do