angular之流入器与上下文的实现
angular之注入器与上下文的实现
- 认真看把
-
//这里分三大部分, //分别是 provider(providerCache 和 instanceCache 与 providerInjector 和 instanceInjector) 是angualr 最核心的几个组件之一。 //loadModules //createInternalInjector function createInjector(modulesToLoad) { var INSTANTIATING = {}, providerSuffix = 'Provider', path = [], loadedModules = new HashMap([], true), //providerCache 基本保存所有组件,这里初始化了$provide组件,在初始化的时候,会加入很多组件 providerCache = { $provide: { provider: supportObject(provider), factory: supportObject(factory), service: supportObject(service), value: supportObject(value), constant: supportObject(constant), decorator: decorator } }, //负责除了decorator组件之外所有组件注入 providerInjector = (providerCache.$injector = createInternalInjector(providerCache, function() { throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- ')); })), //基本没用,只保存了。constant组件的信息 instanceCache = {}, //负责decorator组件的注入,得到还是在providerCache 得到 instanceInjector = (instanceCache.$injector = createInternalInjector(instanceCache, function(servicename) { //注意,这里的代码与 getseriver方法的使用, //等于 instanceInjector.getServer还是从providerInjector.get得到 var provider = providerInjector.get(servicename + providerSuffix); return instanceInjector.invoke(provider.$get, provider); })); forEach(loadModules(modulesToLoad), function(fn) { instanceInjector.invoke(fn || noop); }); return instanceInjector; //////////////////////////////////// // $provider 组件包括factory,provider,service,value,constant,decorator,从这里就可以看到这些组件的不同之处。 //////////////////////////////////// function supportObject(delegate) { return function(key, value) { if (isObject(key)) { forEach(key, reverseParams(delegate)); } else { return delegate(key, value); } }; } function provider(name, provider_) { assertNotHasOwnProperty(name, 'service'); if (isFunction(provider_) || isArray(provider_)) { provider_ = providerInjector.instantiate(provider_); } if (!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 }); }; } //////////////////////////////////// // Module Loading //////////////////////////////////// function loadModules(modulesToLoad) { var runBlocks = [], moduleFn, invokeQueue, i, ii; forEach(modulesToLoad, function(module) { if (loadedModules.get(module)) return;//如何依赖的组件已经初始化过,就不初始化了。 loadedModules.put(module, true);//加入缓存 try { if (isString(module)) { moduleFn = angularModule(module);//通过module名得到module的相关数据,等于angular.module(“”) //loadModules(moduleFn.requires) 先加载依赖,同是把依赖的 run 方法 加入到runBlocks 中。 runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks); //循环执行 module中的基础方法 ,在初始化,会加载很多provider, for (invokeQueue = moduleFn._invokeQueue, i = 0, ii = invokeQueue.length; i < ii; i++) { var invokeArgs = invokeQueue[i], provider = providerInjector.get(invokeArgs[0]); provider[invokeArgs[1]].apply(provider, invokeArgs[2]); } } else if (isFunction(module)) { runBlocks.push(providerInjector.invoke(module)); } else if (isArray(module)) { runBlocks.push(providerInjector.invoke(module)); } else { assertArgFn(module, 'module'); } } catch (e) { if (isArray(module)) { module = module[module.length - 1]; } if (e.message && e.stack && e.stack.indexOf(e.message) == -1) { // Safari & FF's stack traces don't contain error.message content // unlike those of Chrome and IE // So if stack doesn't contain message, we create a new string that contains both. // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here. /* jshint -W022 */ e = e.message + '\n' + e.stack; } throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}", module, e.stack || e.message || e); } }); return runBlocks; } //////////////////////////////////// // internal Injector 注入器的实现 ,,缓存对象,与执行工厂,会实例化两个依赖注入对象,可以说白了,基本就等于一个。 //////////////////////////////////// function createInternalInjector(cache, factory) { function getService(serviceName) { if (cache.hasOwnProperty(serviceName)) { if (cache[serviceName] === INSTANTIATING) { throw $injectorMinErr('cdep', 'Circular dependency found: {0}', serviceName + ' <- ' + path.join(' <- ')); } return cache[serviceName]; } else { try { path.unshift(serviceName); cache[serviceName] = INSTANTIATING; return cache[serviceName] = factory(serviceName); } catch (err) { if (cache[serviceName] === INSTANTIATING) { delete cache[serviceName]; } throw err; } finally { path.shift(); } } } //fn 执行的方法,locals方法需要的实参,self方法执行的上下文,也就是对象,也就是调用方法的this参数 function invoke(fn, self, locals) { var args = [], $inject = annotate(fn), length, i, key; for (i = 0, length = $inject.length; i < length; i++) { key = $inject[i]; if (typeof key !== 'string') { throw $injectorMinErr('itkn', 'Incorrect injection token! Expected service name as string, got {0}', key); } //注意这里,如果参数在locals里面没有,就在 service里面得到。为什么有locals // 比如 作用域不是服务,节点与属性也不是服务,只能从locals得到。 args.push( locals && locals.hasOwnProperty(key) ? locals[key] : getService(key) ); } if (isArray(fn)) { fn = fn[length];//注意这里,fn如果是数组那么最后一个才是执行的方法 } // http://jsperf.com/angularjs-invoke-apply-vs-switch // #5388 return fn.apply(self, args); } //这个方法 使用与invoke的区别就是使用了原型链而已,javascript这个语言就是麻烦。 function instantiate(Type, locals) { var Constructor = function() {}, instance, returnedValue; // Check if Type is annotated and use just the given function at n-1 as parameter // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]); Constructor.prototype = (isArray(Type) ? Type[Type.length - 1] : Type).prototype; instance = new Constructor(); returnedValue = invoke(Type, instance, locals);//注意这里调用了 invoke 方法 return 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); } }; } }