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 invokeQueue[insertMethod || "push"]([provider, method, arguments]), 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(" <- "));
                    return cache[serviceName]
                }
                try {
                    return path.unshift(serviceName), cache[serviceName] = INSTANTIATING, cache[serviceName] = factory(serviceName)
                } finally {
                    path.shift()
                }
            }

            function invoke(fn, self, locals) {
                var length, i, key, args = [],
                    $inject = annotate(fn);
                for (i = 0, length = $inject.length; length > 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(" <- "))
            }),
            instanceCache = {}, instanceInjector = instanceCache.$injector = createInternalInjector(instanceCache, function(servicename) {
                var provider = providerInjector.get(servicename + providerSuffix);
                return instanceInjector.invoke(provider.$get, provider)
            });
        return forEach(loadModules(modulesToLoad), function(fn) {
            instanceInjector.invoke(fn || noop)
        }), instanceInjector
    }

上面说的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(" <- "));
                    return cache[serviceName]
                }
                try {
                    return path.unshift(serviceName), cache[serviceName] = INSTANTIATING, cache[serviceName] = factory(serviceName)
                } finally {
                    path.shift()
                }
            }

            function invoke(fn, self, locals) {
                var length, i, key, args = [],
                    $inject = annotate(fn);
                for (i = 0, length = $inject.length; length > 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(" <- "))
            }),
            instanceCache = {}, instanceInjector = instanceCache.$injector = createInternalInjector(instanceCache, function(servicename) {
                var provider = providerInjector.get(servicename + providerSuffix);
                return instanceInjector.invoke(provider.$get, provider)
            });
        return forEach(loadModules(modulesToLoad), function(fn) {
            instanceInjector.invoke(fn || noop)
        }), instanceInjector
    }

首先这个函数内部包含有创建项目服务的几个关键方法,比如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),一直觉的instanceInjectorproviderInjector这两个变量的定义非常让人迷糊,嘿嘿,估计是google的人写代码非常节省空间吧,这两个变量都是内部DI实例,区别在于第二个参数,当要真正的实例化的时候,第二个参数负责真正的初始化providerCache里保证的provider,其实就是执行它的$get方法,然后把值保存到instanceCache中,以便保证单例使用.

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

这是instanceInjector变量第二个参数的函数体,先在providerCache里查找,然后把provider的$get方法传给instanceInjectorinvoke,这个会真正的生成实例.

最后说下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方面的心得,有错误希望大家提出来,一起改进改进.

时间: 2024-10-31 17:07:00

Angularjs 源码分析1的相关文章

angularjs源码分析之:angularjs执行流程

angularjs用了快一个月了,最难的不是代码本身,而是学会怎么用angular的思路思考问题.其中涉及到很多概念,比如:directive,controller,service,compile,link,scope,isolate scope,双向绑定,mvvm等.最近准备把这些都慢慢搞懂,分析源码并贴到博客园,如有分析不对的地方,还望各位包容并指正. 先上个大图,有个大概印象,注:angularjs的版本为:1.2.1,通过bower install angularjs安装的. 几个重要方

Angularjs 源码分析4

angularjs之$compile 今天主要说说ng里的$compile,这是一个非常关键的服务,页面上的双向绑定,各个监听基本上都是在这里执行的. 源码部分还是引用angular1.2.4,链接在这里下载 compile的源头 ng里最开始引用$compile的地方就是把所有系统内建的指令添加到$CompileProvider里,由于代码太长,只写些关键部分的 $provide.provider('$compile', $CompileProvider). directive({ a: ht

Angularjs 源码分析2

本文主要分析RootScopeProvider和ParseProvider RootScopeProvider简介 今天这个rootscope可是angularjs里面比较活跃的一个provider,大家可以理解为一个模型M或者VM,它主要负责与控制器或者指令进行数据交互. 今天使用的源码跟上次分析的一样也是1.2.X系列,只不过这次用的是未压缩合并版的,方便大家阅读,可以在这里下载 从$get属性说起 说起这个$get属性,是每个系统provider都有的,主要是先保存要实例化的函数体,等待i

Angularjs 源码分析3

本文接着上一篇讲 回顾 上次说到了rootScope里的$watch方法中的解析监控表达式,即而引出了对parse的分析,今天我们接着这里继续挖代码. $watch续 先上一块$watch代码 $watch: function(watchExp, listener, objectEquality) { var scope = this, get = compileToFn(watchExp, 'watch'), array = scope.$$watchers, watcher = { fn:

angularjs 源码解析之injector_AngularJS

简介 injector是用来做参数自动注入的,例如 function fn ($http, $scope, aService) { } ng在运行时会把$http, $scope, aService 自动作为参数传入进行执行. 其实很容易想明白,injector做了两件事 缓存那些service,以后作为参数注入 分析参数列表,找到需要的参数注入 下面源码分析如何实现上面两件事情. 结构 createInjector -> createInternalInjector  return: inst

angularjs 源码解析之scope_AngularJS

简介 在ng的生态中scope处于一个核心的地位,ng对外宣称的双向绑定的底层其实就是scope实现的,本章主要对scope的watch机制.继承性以及事件的实现作下分析. 监听 1. $watch 1.1 使用 // $watch: function(watchExp, listener, objectEquality) var unwatch = $scope.$watch('aa', function () {}, isEqual); 使用过angular的会经常这上面这样的代码,俗称"手

WebWork2源码分析

web Author: zhuam   昨晚一口气看完了夏昕写的<<Webwork2_Guide>>,虽然文档资料很简洁,但仍不失为一本好的WebWork2书籍,看的出作者的经验和能力都是非常的老道,在此向作者的开源精神致敬,并在此引用夏昕的那句话So many open source projects, Why not Open your Documents?   今天下载了最新的WebWork2版本, 开始了源码分析,这份文档只能算是我的个人笔记,也没时间细细校对,且个人能力有

JUnir源码分析(一)

一.引子 JUnit源码是我仔细阅读过的第一个开源项目源码.阅读高手写的代码能学到一些好的编程风格和实现思路,这是提高自己编程水平行之有效的方法,因此早就想看看这些赫赫有名的框架是怎么回事了.今天就拿最简单的JUnit下手,也算开始自己的源码分析之路.   JUnit作为最著名的单元测试框架,由两位业界有名人士协力完成,已经经历了多次版本升级(了解JUnit基础.JUnit实践).JUnit总体来说短小而精悍,有不少值得我们借鉴的经验在里面:但是也有一些不足存在,当然这对于任何程序来说都是难免的

java io学习(三) 管道的简介,源码分析和示例

管道(PipedOutputStream和PipedInputStream)的简介,源码分析和示例 本章,我们对java 管道进行学习. java 管道介绍 在java中,PipedOutputStream和PipedInputStream分别是管道输出流和管道输入流. 它们的作用是让多线程可以通过管道进行线程间的通讯.在使用管道通信时,必须将PipedOutputStream和PipedInputStream配套使用. 使用管道通信时,大致的流程是:我们在线程A中向PipedOutputStr