AngularJS简介

angularjs 是google出品的一款MVVM前端框架,包含一个精简的类jquery库,创新的开发了以指令的方式来组件化前端开发,可以去它的官网看看,请戳这里

再贴上一个本文源码分析对应的angularjs源码合并版本1.2.4,精简版的,除掉了所有的注释, 请戳这里

从启动开始说起

定位到4939行,这里是angularjs开始执行初始化的地方,见代码

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

bindJQuery方法是检查是否引用jquery,没有的话jqlite就用本身自带的,否则切换到jquery中去.这个好理解

publishExternalAPI这个方法是绑定一些公共的方法到angular下面,这些是可以在网站中访问到的,像forEach,copy等公共方法,还有一个重要的任务就是初始化angular核心的模块,publishExternalAPI在465行,现在我们来分析里面的一些重要的代码

  1. function publishExternalAPI(angular) {
  2. extend(angular, {
  3. bootstrap: bootstrap,
  4. copy: copy,
  5. extend: extend,
  6. equals: equals,
  7. element: jqLite,
  8. forEach: forEach,
  9. injector: createInjector,
  10. noop: noop,
  11. bind: bind,
  12. toJson: toJson,
  13. fromJson: fromJson,
  14. identity: identity,
  15. isUndefined: isUndefined,
  16. isDefined: isDefined,
  17. isString: isString,
  18. isFunction: isFunction,
  19. isObject: isObject,
  20. isNumber: isNumber,
  21. isElement: isElement,
  22. isArray: isArray,
  23. version: version,
  24. isDate: isDate,
  25. lowercase: lowercase,
  26. uppercase: uppercase,
  27. callbacks: {
  28. counter: 0
  29. },
  30. $$minErr: minErr,
  31. $$csp: csp
  32. }), angularModule = setupModuleLoader(window);
  33. try {
  34. angularModule("ngLocale")
  35. } catch (e) {
  36. angularModule("ngLocale", []).provider("$locale", $LocaleProvider)
  37. }
  38. angularModule("ng", ["ngLocale"], ["$provide",
  39. function($provide) {
  40. $provide.provider("$compile", $CompileProvider).directive({
  41. a: htmlAnchorDirective,
  42. input: inputDirective,
  43. textarea: inputDirective,
  44. form: formDirective,
  45. script: scriptDirective,
  46. select: selectDirective,
  47. style: styleDirective,
  48. option: optionDirective,
  49. ngBind: ngBindDirective,
  50. ngBindHtml: ngBindHtmlDirective,
  51. ngBindTemplate: ngBindTemplateDirective,
  52. ngClass: ngClassDirective,
  53. ngClassEven: ngClassEvenDirective,
  54. ngClassOdd: ngClassOddDirective,
  55. ngCloak: ngCloakDirective,
  56. ngController: ngControllerDirective,
  57. ngForm: ngFormDirective,
  58. ngHide: ngHideDirective,
  59. ngIf: ngIfDirective,
  60. ngInclude: ngIncludeDirective,
  61. ngInit: ngInitDirective,
  62. ngNonBindable: ngNonBindableDirective,
  63. ngPluralize: ngPluralizeDirective,
  64. ngRepeat: ngRepeatDirective,
  65. ngShow: ngShowDirective,
  66. ngStyle: ngStyleDirective,
  67. ngSwitch: ngSwitchDirective,
  68. ngSwitchWhen: ngSwitchWhenDirective,
  69. ngSwitchDefault: ngSwitchDefaultDirective,
  70. ngOptions: ngOptionsDirective,
  71. ngTransclude: ngTranscludeDirective,
  72. ngModel: ngModelDirective,
  73. ngList: ngListDirective,
  74. ngChange: ngChangeDirective,
  75. required: requiredDirective,
  76. ngRequired: requiredDirective,
  77. ngValue: ngValueDirective
  78. }).directive(ngAttributeAliasDirectives).directive(ngEventDirectives), $provide.provider({
  79. $anchorScroll: $AnchorScrollProvider,
  80. $animate: $AnimateProvider,
  81. $browser: $BrowserProvider,
  82. $cacheFactory: $CacheFactoryProvider,
  83. $controller: $ControllerProvider,
  84. $document: $DocumentProvider,
  85. $exceptionHandler: $ExceptionHandlerProvider,
  86. $filter: $FilterProvider,
  87. $interpolate: $InterpolateProvider,
  88. $interval: $IntervalProvider,
  89. $http: $HttpProvider,
  90. $httpBackend: $HttpBackendProvider,
  91. $location: $LocationProvider,
  92. $log: $LogProvider,
  93. $parse: $ParseProvider,
  94. $rootScope: $RootScopeProvider,
  95. $q: $QProvider,
  96. $sce: $SceProvider,
  97. $sceDelegate: $SceDelegateProvider,
  98. $sniffer: $SnifferProvider,
  99. $templateCache: $TemplateCacheProvider,
  100. $timeout: $TimeoutProvider,
  101. $window: $WindowProvider
  102. })
  103. }
  104. ])
  105. }

方法体中的setupModuleLoader方法是一个模块加载器,这也是一个关键方法, 主要作用是创建和获取模块,代码见417行.

  1. function setupModuleLoader(window) {
  2.     function ensure(obj, name, factory) {
  3.         return obj[name] || (obj[name] = factory())
  4.     }
  5.     var $injectorMinErr = minErr("$injector"),
  6.         ngMinErr = minErr("ng");
  7.     return ensure(ensure(window, "angular", Object), "module", function() {
  8.         var modules = {};
  9.         return function(name, requires, configFn) {
  10.             var assertNotHasOwnProperty = function(name, context) {
  11.                 if ("hasOwnProperty" === name) throw ngMinErr("badname", "hasOwnProperty is not a valid {0} name", context)
  12.             };
  13.             return assertNotHasOwnProperty(name, "module"), requires && modules.hasOwnProperty(name) && (modules[name] = null), ensure(modules, name, function() {
  14.                 function invokeLater(provider, method, insertMethod) {
  15.                     return function() {
  16.                         return invokeQueue[insertMethod || "push"]([provider, method, arguments]), moduleInstance
  17.                     }
  18.                 }
  19.                 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);
  20.                 var invokeQueue = [],
  21.                     runBlocks = [],
  22.                     config = invokeLater("$injector", "invoke"),
  23.                     moduleInstance = {
  24.                         _invokeQueue: invokeQueue,
  25.                         _runBlocks: runBlocks,
  26.                         requires: requires,
  27.                         name: name,
  28.                         provider: invokeLater("$provide", "provider"),
  29.                         factory: invokeLater("$provide", "factory"),
  30.                         service: invokeLater("$provide", "service"),
  31.                         value: invokeLater("$provide", "value"),
  32.                         constant: invokeLater("$provide", "constant", "unshift"),
  33.                         animation: invokeLater("$animateProvider", "register"),
  34.                         filter: invokeLater("$filterProvider", "register"),
  35.                         controller: invokeLater("$controllerProvider", "register"),
  36.                         directive: invokeLater("$compileProvider", "directive"),
  37.                         config: config,
  38.                         run: function(block) {
  39.                             return runBlocks.push(block), this
  40.                         }
  41.                     };
  42.                 return configFn && config(configFn), moduleInstance
  43.             })
  44.         }
  45.     })
  46. }
  47.  

上面publishExternalAPI 方法中的angularModule = setupModuleLoader(window);是在window下面创建全局的angular对象,并且返回一个高阶函数,赋值给了angular.module属性,所以一般我们创建模块都是用angular.module方法.这里的angularModule其实就是相当于angular.module

angular.module在创建模块的时候,传递一个参数的时候,是获取模块;传递一个以上的是创建新模块;该方法返回的是一个moduleInstance对象,它的任务就是来创建控制器,服务,指令,以及配置方法,全局运行方法,而且是链式调用,因为每个方法都会返回moduleInstance,看这里

  1. function invokeLater(provider, method, insertMethod) {
  2. return function() {
  3. return invokeQueue[insertMethod || "push"]([provider, method, arguments]), moduleInstance
  4. }
  5. }

此处的return invokeQueue[insertMethod || "push"]([provider, method, arguments]), moduleInstance,逗号表达式是返回最后一个值

再来一个angular.module在项目中运用的代码

  1. angular.module('demoApp', [])
  2. .factory()
  3. .controller()
  4. .directive()
  5. .config()
  6. .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行

  1. function createInjector(modulesToLoad) {
  2. function supportObject(delegate) {
  3. return function(key, value) {
  4. return isObject(key) ? (forEach(key, reverseParams(delegate)), void 0) : delegate(key, value)
  5. }
  6. }
  7.     function provider(name, provider_) {
  8.         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);
  9.         return providerCache[name + providerSuffix] = provider_
  10.     }
  11.     function factory(name, factoryFn) {
  12.         return provider(name, {
  13.             $get: factoryFn
  14.         })
  15.     }
  16.     function service(name, constructor) {
  17.         return factory(name, ["$injector",
  18.             function($injector) {
  19.                 return $injector.instantiate(constructor)
  20.             }
  21.         ])
  22.     }
  23.     function value(name, val) {
  24.         return factory(name, valueFn(val))
  25.     }
  26.     function constant(name, value) {
  27.         assertNotHasOwnProperty(name, "constant"), providerCache[name] = value, instanceCache[name] = value
  28.     }
  29.     function decorator(serviceName, decorFn) {
  30.         var origProvider = providerInjector.get(serviceName + providerSuffix),
  31.             orig$get = origProvider.$get;
  32.         origProvider.$get = function() {
  33.             var origInstance = instanceInjector.invoke(orig$get, origProvider);
  34.             return instanceInjector.invoke(decorFn, null, {
  35.                 $delegate: origInstance
  36.             })
  37.         }
  38.     }
  39.     function loadModules(modulesToLoad) {
  40.         var moduleFn, invokeQueue, i, ii, runBlocks = [];
  41.         return forEach(modulesToLoad, function(module) {
  42.             if (!loadedModules.get(module)) {
  43.                 loadedModules.put(module, !0);
  44.                 try {
  45.                     if (isString(module))
  46.                         for (moduleFn = angularModule(module), runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks), invokeQueue = moduleFn._invokeQueue, i = 0, ii = invokeQueue.length; ii > i; i++) {
  47.                             var invokeArgs = invokeQueue[i],
  48.                                 provider = providerInjector.get(invokeArgs[0]);
  49.                             provider[invokeArgs[1]].apply(provider, invokeArgs[2])
  50.                         } else isFunction(module) ? runBlocks.push(providerInjector.invoke(module)) : isArray(module) ? runBlocks.push(providerInjector.invoke(module)) : assertArgFn(module, "module")
  51.                 } catch (e) {
  52.                     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)
  53.                 }
  54.             }
  55.         }),runBlocks
  56.     }
  57.     function createInternalInjector(cache, factory) {
  58.         function getService(serviceName) {
  59.             if (cache.hasOwnProperty(serviceName)) {
  60.                 if (cache[serviceName] === INSTANTIATING) throw $injectorMinErr("cdep", "Circular dependency found: {0}", path.join(" <- "));
  61.                 return cache[serviceName]
  62.             }
  63.             try {
  64.                 return path.unshift(serviceName), cache[serviceName] = INSTANTIATING, cache[serviceName] = factory(serviceName)
  65.             } finally {
  66.                 path.shift()
  67.             }
  68.         }
  69.         function invoke(fn, self, locals) {
  70.             var length, i, key, args = [],
  71.                 $inject = annotate(fn);
  72.             for (i = 0, length = $inject.length; length > i; i++) {
  73.                 if (key = $inject[i], "string" != typeof key) throw $injectorMinErr("itkn", "Incorrect injection token! Expected service name as string, got {0}", key);
  74.                 args.push(locals && locals.hasOwnProperty(key) ? locals[key] : getService(key))
  75.             }
  76.             switch (fn.$inject || (fn = fn[length]), self ? -1 : args.length) {
  77.                 case 0:
  78.                     return fn();
  79.                 case 1:
  80.                     return fn(args[0]);
  81.                 case 2:
  82.                     return fn(args[0], args[1]);
  83.                 case 3:
  84.                     return fn(args[0], args[1], args[2]);
  85.                 case 4:
  86.                     return fn(args[0], args[1], args[2], args[3]);
  87.                 case 5:
  88.                     return fn(args[0], args[1], args[2], args[3], args[4]);
  89.                 case 6:
  90.                     return fn(args[0], args[1], args[2], args[3], args[4], args[5]);
  91.                 case 7:
  92.                     return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
  93.                 case 8:
  94.                     return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]);
  95.                 case 9:
  96.                     return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]);
  97.                 case 10:
  98.                     return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9]);
  99.                 default:
  100.                     return fn.apply(self, args)
  101.             }
  102.         }
  103.         function instantiate(Type, locals) {
  104.             var instance, returnedValue, Constructor = function() {};
  105.             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
  106.         }
  107.         return {
  108.             invoke: invoke,
  109.             instantiate: instantiate,
  110.             get: getService,
  111.             annotate: annotate,
  112.             has: function(name) {
  113.                 return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name)
  114.             }
  115.         }
  116.     }
  117.     var INSTANTIATING = {}, providerSuffix = "Provider",
  118.         path = [],
  119.         loadedModules = new HashMap,
  120.         providerCache = {
  121.             $provide: {
  122.                 provider: supportObject(provider),
  123.                 factory: supportObject(factory),
  124.                 service: supportObject(service),
  125.                 value: supportObject(value),
  126.                 constant: supportObject(constant),
  127.                 decorator: decorator
  128.             }
  129.         }, providerInjector = providerCache.$injector = createInternalInjector(providerCache, function() {
  130.             throw $injectorMinErr("unpr", "Unknown provider: {0}", path.join(" <- "))
  131.         }),
  132.         instanceCache = {}, instanceInjector = instanceCache.$injector = createInternalInjector(instanceCache, function(servicename) {
  133.             var provider = providerInjector.get(servicename + providerSuffix);
  134.             return instanceInjector.invoke(provider.$get, provider)
  135.         });
  136.     return forEach(loadModules(modulesToLoad), function(fn) {
  137.         instanceInjector.invoke(fn || noop)
  138.     }), instanceInjector
  139. }
  140.  

上面说的DI其实就是上面的createInjector函数,这个是angularjs管理依赖注入的核心函数,然后再看$provide.provider,其实就是调用内部函数provider

  1. function provider(name, provider_) {
  2. 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);
  3. return providerCache[name + providerSuffix] = provider_
  4. }

注意这里的providerCache,它保存了所有调用provider方法的provider_

好了,好像说的有点远了,我们再回到初始化的代码

说完了publishExternalAPI的代码,我们再了看看angularInit方法,这个方法的作用就是先找到带angular项目标识的元素,然后调用bootstrap方法,我们重点来看看bootstrap方法,见344行

  1. function bootstrap(element, modules) {
  2. var doBootstrap = function() {
  3. if (element = jqLite(element), element.injector()) {
  4. var tag = element[0] === document ? "document" : startingTag(element);
  5. throw ngMinErr("btstrpd", "App Already Bootstrapped with this Element '{0}'", tag)
  6. }
  7. modules = modules || [], modules.unshift(["$provide",
  8. function($provide) {
  9. $provide.value("$rootElement", element)
  10. }
  11. ]), modules.unshift("ng");
  12. var injector = createInjector(modules);
  13. return injector.invoke(["$rootScope", "$rootElement", "$compile", "$injector", "$animate",
  14. function(scope, element, compile, injector) {
  15. scope.$apply(function() {
  16. element.data("$injector", injector), compile(element)(scope)
  17. })
  18. }
  19. ]), injector
  20. }, NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
  21. return window && !NG_DEFER_BOOTSTRAP.test(window.name) ? doBootstrap() : (window.name = window.name.replace(NG_DEFER_BOOTSTRAP, ""), angular.resumeBootstrap = function(extraModules) {
  22. forEach(extraModules, function(module) {
  23. modules.push(module)
  24. }), doBootstrap()
  25. }, void 0)
  26. }

仔细看上面的代码,有一句比较关键var injector = createInjector(modules);,把要初始化的模块传进DI中,并返回一个依赖对象,这里的modules参数包含一个ng模块,一个定义$rootElement值的模块,一个业务对应的模块

现在我们重点分析createInjector的代码,为了方便查阅,把上面的di构造函数重新贴一下

  1. function createInjector(modulesToLoad) {
  2. function supportObject(delegate) {
  3. return function(key, value) {
  4. return isObject(key) ? (forEach(key, reverseParams(delegate)), void 0) : delegate(key, value)
  5. }
  6. }
  7.     function provider(name, provider_) {
  8.         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);
  9.         return providerCache[name + providerSuffix] = provider_
  10.     }
  11.     function factory(name, factoryFn) {
  12.         return provider(name, {
  13.             $get: factoryFn
  14.         })
  15.     }
  16.     function service(name, constructor) {
  17.         return factory(name, ["$injector",
  18.             function($injector) {
  19.                 return $injector.instantiate(constructor)
  20.             }
  21.         ])
  22.     }
  23.     function value(name, val) {
  24.         return factory(name, valueFn(val))
  25.     }
  26.     function constant(name, value) {
  27.         assertNotHasOwnProperty(name, "constant"), providerCache[name] = value, instanceCache[name] = value
  28.     }
  29.     function decorator(serviceName, decorFn) {
  30.         var origProvider = providerInjector.get(serviceName + providerSuffix),
  31.             orig$get = origProvider.$get;
  32.         origProvider.$get = function() {
  33.             var origInstance = instanceInjector.invoke(orig$get, origProvider);
  34.             return instanceInjector.invoke(decorFn, null, {
  35.                 $delegate: origInstance
  36.             })
  37.         }
  38.     }
  39.     function loadModules(modulesToLoad) {
  40.         var moduleFn, invokeQueue, i, ii, runBlocks = [];
  41.         return forEach(modulesToLoad, function(module) {
  42.             if (!loadedModules.get(module)) {
  43.                 loadedModules.put(module, !0);
  44.                 try {
  45.                     if (isString(module))
  46.                         for (moduleFn = angularModule(module), runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks), invokeQueue = moduleFn._invokeQueue, i = 0, ii = invokeQueue.length; ii > i; i++) {
  47.                             var invokeArgs = invokeQueue[i],
  48.                                 provider = providerInjector.get(invokeArgs[0]);
  49.                             provider[invokeArgs[1]].apply(provider, invokeArgs[2])
  50.                         } else isFunction(module) ? runBlocks.push(providerInjector.invoke(module)) : isArray(module) ? runBlocks.push(providerInjector.invoke(module)) : assertArgFn(module, "module")
  51.                 } catch (e) {
  52.                     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)
  53.                 }
  54.             }
  55.         }),runBlocks
  56.     }
  57.     function createInternalInjector(cache, factory) {
  58.         function getService(serviceName) {
  59.             if (cache.hasOwnProperty(serviceName)) {
  60.                 if (cache[serviceName] === INSTANTIATING) throw $injectorMinErr("cdep", "Circular dependency found: {0}", path.join(" <- "));
  61.                 return cache[serviceName]
  62.             }
  63.             try {
  64.                 return path.unshift(serviceName), cache[serviceName] = INSTANTIATING, cache[serviceName] = factory(serviceName)
  65.             } finally {
  66.                 path.shift()
  67.             }
  68.         }
  69.         function invoke(fn, self, locals) {
  70.             var length, i, key, args = [],
  71.                 $inject = annotate(fn);
  72.             for (i = 0, length = $inject.length; length > i; i++) {
  73.                 if (key = $inject[i], "string" != typeof key) throw $injectorMinErr("itkn", "Incorrect injection token! Expected service name as string, got {0}", key);
  74.                 args.push(locals && locals.hasOwnProperty(key) ? locals[key] : getService(key))
  75.             }
  76.             switch (fn.$inject || (fn = fn[length]), self ? -1 : args.length) {
  77.                 case 0:
  78.                     return fn();
  79.                 case 1:
  80.                     return fn(args[0]);
  81.                 case 2:
  82.                     return fn(args[0], args[1]);
  83.                 case 3:
  84.                     return fn(args[0], args[1], args[2]);
  85.                 case 4:
  86.                     return fn(args[0], args[1], args[2], args[3]);
  87.                 case 5:
  88.                     return fn(args[0], args[1], args[2], args[3], args[4]);
  89.                 case 6:
  90.                     return fn(args[0], args[1], args[2], args[3], args[4], args[5]);
  91.                 case 7:
  92.                     return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
  93.                 case 8:
  94.                     return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]);
  95.                 case 9:
  96.                     return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]);
  97.                 case 10:
  98.                     return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9]);
  99.                 default:
  100.                     return fn.apply(self, args)
  101.             }
  102.         }
  103.         function instantiate(Type, locals) {
  104.             var instance, returnedValue, Constructor = function() {};
  105.             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
  106.         }
  107.         return {
  108.             invoke: invoke,
  109.             instantiate: instantiate,
  110.             get: getService,
  111.             annotate: annotate,
  112.             has: function(name) {
  113.                 return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name)
  114.             }
  115.         }
  116.     }
  117.     var INSTANTIATING = {}, providerSuffix = "Provider",
  118.         path = [],
  119.         loadedModules = new HashMap,
  120.         providerCache = {
  121.             $provide: {
  122.                 provider: supportObject(provider),
  123.                 factory: supportObject(factory),
  124.                 service: supportObject(service),
  125.                 value: supportObject(value),
  126.                 constant: supportObject(constant),
  127.                 decorator: decorator
  128.             }
  129.         }, providerInjector = providerCache.$injector = createInternalInjector(providerCache, function() {
  130.             throw $injectorMinErr("unpr", "Unknown provider: {0}", path.join(" <- "))
  131.         }),
  132.         instanceCache = {}, instanceInjector = instanceCache.$injector = createInternalInjector(instanceCache, function(servicename) {
  133.             var provider = providerInjector.get(servicename + providerSuffix);
  134.             return instanceInjector.invoke(provider.$get, provider)
  135.         });
  136.     return forEach(loadModules(modulesToLoad), function(fn) {
  137.         instanceInjector.invoke(fn || noop)
  138.     }), instanceInjector
  139. }
  140.  

首先这个函数内部包含有创建项目服务的几个关键方法,比如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

  1. function loadModules(modulesToLoad) {
  2. var moduleFn, invokeQueue, i, ii, runBlocks = [];
  3. return forEach(modulesToLoad, function(module) {
  4. if (!loadedModules.get(module)) {
  5. loadedModules.put(module, !0);
  6. try {
  7. if (isString(module))
  8. for (moduleFn = angularModule(module), runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks), invokeQueue = moduleFn._invokeQueue, i = 0, ii = invokeQueue.length; ii > i; i++) {
  9. var invokeArgs = invokeQueue[i],
  10. provider = providerInjector.get(invokeArgs[0]);
  11. provider[invokeArgs[1]].apply(provider, invokeArgs[2])
  12. } else isFunction(module) ? runBlocks.push(providerInjector.invoke(module)) : isArray(module) ? runBlocks.push(providerInjector.invoke(module)) : assertArgFn(module, "module")
  13. } catch (e) {
  14. 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)
  15. }
  16. }
  17. }),runBlocks
  18. }

这个方法的参数是一个数组,里面是的数据是在doBootstrap里定义的,上面有讲

这个方法依次加载模块数组里对应的provider,这里用到了上面提到的_invokeQueue数组,里面定义保存很多provider信息,注意这里的constant类型的provider会直接创建实例,跟别的provider不一样.

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

这里就是利用保存的三个参数来依次利用第一个参数调用第二个参数,然后传递第三个参数

loadModules方法最后返回一个运行块代码,所以一般项目里的run方法会在模块加载完以及config方法调用完之后运行.

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

注意这里run方法代码在这里执行instanceInjector.invoke(fn || noop),一直觉的instanceInjector和providerInjector这两个变量的定义非常让人迷糊,嘿嘿,估计是google的人写代码非常节省空间吧,这两个变量都是内部DI实例,区别在于第二个参数,当要真正的实例化的时候,第二个参数负责真正的初始化providerCache里保证的provider,其实就是执行它的$get方法,然后把值保存到instanceCache中,以便保证单例使用.

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

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

最后说下invoke的代码,这里会频繁用一个工具方法annotate,这个是获取一个函数的入参,并以数组形式返回,invoke会自动的检查要执行的函数的入参,假如已经生成实例的,则传给函数,否则先生成依赖的实例,最后执行函数

最后当所有的模块加载完成,并且run代码块也执行完成之后,接下来就是编译页面代码,给指令生成相应的link函数了

  1. injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector', '$animate',
  2. function(scope, element, compile, injector, animate) {
  3. scope.$apply(function() {
  4. element.data('$injector', injector);
  5. compile(element)(scope);
  6. });
  7. }]
  8. );

这个会生成编译实例,通过编译实例去编译项目起始页,编译的核心是生成指令对应的link函数,有点类似后端的编译,先词法分析,用lex,然后语法分析,用parse,最后链接,生成link函数

总结

本篇主要是分析了angularjs的模块加载以及依赖注入这部分源码,以后还会分析编译以及作用域相关的源码,本文只是自己一点angularjs方面的心得,有错误希望大家提出来,一起改进改进.


作者声明

本来不想补充这个的,但是看到本园里把我这个文章给转走了,尽然还写上防止别人转载,真是个joker

作者: feenan

出处: http://www.cnblogs.com/xuwenmin888

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

AngularJS 源码分析1的更多相关文章

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

    angularjs用了快一个月了,最难的不是代码本身,而是学会怎么用angular的思路思考问题.其中涉及到很多概念,比如:directive,controller,service,compile,l ...

  2. AngularJS 源码分析2

    上一篇地址 本文主要分析RootScopeProvider和ParseProvider RootScopeProvider简介 今天这个rootscope可是angularjs里面比较活跃的一个pro ...

  3. AngularJS 源码分析3

    本文接着上一篇讲 上一篇地址 回顾 上次说到了rootScope里的$watch方法中的解析监控表达式,即而引出了对parse的分析,今天我们接着这里继续挖代码. $watch续 先上一块$watch ...

  4. AngularJS源码分析之依赖注入$injector

    开篇 随着javaEE的spring框架的兴起,依赖注入(IoC)的概念彻底深入人心,它彻底改变了我们的编码模式和思维.在IoC之前,我们在程序中需要创建一个对象很简单也很直接,就是在代码中new O ...

  5. ABP源码分析一:整体项目结构及目录

    ABP是一套非常优秀的web应用程序架构,适合用来搭建集中式架构的web应用程序. 整个Abp的Infrastructure是以Abp这个package为核心模块(core)+15个模块(module ...

  6. MVVM大比拼之AngularJS源码精析

    MVVM大比拼之AngularJS源码精析 简介 AngularJS的学习资源已经非常非常多了,AngularJS基础请直接看官网文档.这里推荐几个深度学习的资料: AngularJS学习笔记 作者: ...

  7. ABP源码分析三十七:ABP.Web.Api Script Proxy API

    ABP提供Script Proxy WebApi为所有的Dynamic WebApi生成访问这些WebApi的JQuery代理,AngularJs代理以及TypeScriptor代理.这些个代理就是j ...

  8. angular源码分析:angular中入境检察官$sce

    一.ng-bing-html指令问题 需求:我需要将一个变量$scope.x = '<a href="http://www.cnblogs.com/web2-developer/&qu ...

  9. angular源码分析:angular中脏活累活承担者之$parse

    我们在上一期中讲 $rootscope时,看到$rootscope是依赖$prase,其实不止是$rootscope,翻看angular的源码随便翻翻就可以发现很多地方是依赖于$parse的.而$pa ...

随机推荐

  1. sql连着function使用

    create function fun002(@thename varchar()) returns int as begin declare @count int select @count=cou ...

  2. Nessus导入Cookie进行Web应用安全扫描

    在不导入Cookie使用Nessus进行扫描的时候,扫描的结果是比较简单的,很多深层的问题无法被扫描出来. 需要我们手动导入Cookie,带着Cookie的状态扫描的结果会更详细更深入,以下是操作步骤 ...

  3. 任务调度框架-Quartz.Net

    使用Quartz.Net依赖于以下3个组件:Common.Logging.dll.Common.Logging.Core.dll.Quartz.dll 简单封装 using Quartz; using ...

  4. DB2 错误信息码

    000 00000 SQL语句成功完成 01xxx SQL语句成功完成,但是有警告 +012 01545 未限定的列名被解释为一个有相互关系的引用 +098 01568 动态SQL语句用分号结束 +1 ...

  5. 基于VC的ACM音频编程接口压缩Wave音频(三)

    (三)音 频 数 据 的 压 缩 下 面 说 明 使 用 CODEC 实 现 音 频 压 缩 的 过 程:假 设 源 信 号 为8K 采 样.16bits PCM 编 码. 单 声 道. 长 度 为1 ...

  6. vb小菜一枚--------早期绑定和后期绑定

    早期绑定和后期绑定 Visual Studio 2005   其他版本   将对象分配给对象变量时,Visual Basic 编译器会执行一个名为 binding 的进程.如果将对象分配给声明为特定对 ...

  7. 对bit、byte、TByte、Char、string、进制的认识

    在学校老师就教1byte = 8bit,一个Byte在内存中占8个房间.每个房间都有门牌号.找到内存中的内容就找门牌号,寻址什么的,虽然在听,但是脑袋里一头雾水,到现在只知道会用就行,但原理也不是那么 ...

  8. bower 问题

    没法写成bower install jquery bootstrap:只能是bower install jquery; bower install bootstrap

  9. webuploader 断点续传

    webuploader 实现 断点续传webuploader是百度开发的上传文件前端控件.可支持html5和flash,因此对浏览器的兼容比较好.因为需要用到ie8,ie8不支持html5,所以必须支 ...

  10. java 调用 C# 类库搞定,三步即可,可以调用任何类及方法,很简单,非常爽啊

    java 调用 C# 类库搞定,三步即可,可以调用任何类及方法,很简单,非常爽啊 java 调用 C# 类库搞定,可以调用任何类及方法,很简单,非常爽啊 总体分三步走: 一.准备一个 C# 类库 (d ...