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),一直覺的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
作者: feenan
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。
