国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 編程 > JavaScript > 正文

Angular.JS學(xué)習(xí)之依賴注入$injector詳析

2019-11-20 08:42:07
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

前言

在依賴注入(IoC)之前,我們?cè)诔绦蛑行枰獎(jiǎng)?chuàng)建一個(gè)對(duì)象很簡(jiǎn)單也很直接,就是在代碼中new Object即可,有我們自己負(fù)責(zé)創(chuàng)建、維護(hù)、修改和刪除,也就是說(shuō),我們控制了對(duì)象的整個(gè)生命周期,直到對(duì)象沒(méi)有被引用,被回收。誠(chéng)然,當(dāng)創(chuàng)建或者維護(hù)的對(duì)象數(shù)量較少時(shí),這種做法無(wú)可厚非,但是當(dāng)一個(gè)大項(xiàng)目中需要?jiǎng)?chuàng)建大數(shù)量級(jí)的對(duì)象時(shí),僅僅依靠程序員來(lái)進(jìn)行維護(hù)所有對(duì)象,這是難以做到的,特別是如果想在程序的整個(gè)生命周期內(nèi)復(fù)用一些對(duì)象,我們需要自己寫一個(gè)緩存模塊對(duì)所有對(duì)象進(jìn)行緩存,這加大了復(fù)雜度。當(dāng)然,IoC的好處并不僅限于此,它也降低了對(duì)依賴的耦合度,不必在代碼中進(jìn)行引用或者傳參即可操作依賴。

在js中,我們可以這樣引入依賴

1、使用全局變量引用

2、在需要的地方通過(guò)函數(shù)參數(shù)傳遞

使用全局變量的壞處自不必說(shuō),污染了全局的名字空間,而通過(guò)函參傳遞引用,也可以通過(guò)兩種方法實(shí)現(xiàn):

1、閉包傳遞

2、后臺(tái)解析出依賴對(duì)象,并通過(guò)Function.prototype.call進(jìn)行傳參

而在AngularJS中,依賴注入是通過(guò)后者實(shí)現(xiàn)的,接下來(lái)的幾節(jié)將會(huì)介紹IoC模塊的具體實(shí)現(xiàn)。

獲取依賴

var FN_ARGS = /^function/s*[^/(]*/(/s*([^/)]*)/)/m;var FN_ARG_SPLIT = /,/; // 獲取服務(wù)名var FN_ARG = /^/s*(_?)(/S+?)/1/s*$/;var STRIP_COMMENTS = /((////.*$)|(///*[/s/S]*?/*//))/mg;var $injectorMinErr = minErr('$injector');function anonFn(fn) { // For anonymous functions, showing at the very least the function signature can help in // debugging. var fnText = fn.toString().replace(STRIP_COMMENTS, ''),  args = fnText.match(FN_ARGS); if (args) { return 'function(' + (args[1] || '').replace(/[/s/r/n]+/, ' ') + ')'; } return 'fn';}function annotate(fn, strictDi, name) { var $inject,  fnText,  argDecl,  last; if (typeof fn === 'function') { if (!($inject = fn.$inject)) {  $inject = [];  if (fn.length) {  if (strictDi) {   if (!isString(name) || !name) {   name = fn.name || anonFn(fn);   }   throw $injectorMinErr('strictdi',   '{0} is not using explicit annotation and cannot be invoked in strict mode', name);  }  fnText = fn.toString().replace(STRIP_COMMENTS, '');  argDecl = fnText.match(FN_ARGS);  forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) {   arg.replace(FN_ARG, function(all, underscore, name) {   $inject.push(name);   });  });  }  fn.$inject = $inject; } } else if (isArray(fn)) { last = fn.length - 1; assertArgFn(fn[last], 'fn'); $inject = fn.slice(0, last); } else { assertArgFn(fn, 'fn', true); } return $inject;}

annotate函數(shù)通過(guò)對(duì)入?yún)⑦M(jìn)行針對(duì)性分析,若傳遞的是一個(gè)函數(shù),則依賴模塊作為入?yún)鬟f,此時(shí)可通過(guò)序列化函數(shù)進(jìn)行正則匹配,獲取依賴模塊的名稱并存入$inject數(shù)組中返回,另外,通過(guò)函數(shù)入?yún)鬟f依賴的方式在嚴(yán)格模式下執(zhí)行會(huì)拋出異常;第二種依賴傳遞則是通過(guò)數(shù)組的方式,數(shù)組的最后一個(gè)元素是需要使用依賴的函數(shù)。annotate函數(shù)最終返回解析的依賴名稱。

注入器的創(chuàng)建

AngularJS的API也提供了injector部分,通過(guò)injector部分,通過(guò)injector可以使用get,has,instantiate,invoke以及上節(jié)提到的annotate等方法,通過(guò)源碼可以更清晰的理解。

function createInternalInjector(cache, factory) { // 對(duì)服務(wù)注入器 providerInjector而言,只根據(jù)服務(wù)名獲取服務(wù),factory會(huì)拋出異常 function getService(serviceName, caller) {  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, caller);  } catch (err) {   if (cache[serviceName] === INSTANTIATING) {   delete cache[serviceName];   }   throw err;  } finally {   path.shift();  }  } } function invoke(fn, self, locals, serviceName) {  if (typeof locals === 'string') {  serviceName = locals;  locals = null;  }  var args = [],   // 解析并獲取注入服務(wù)列表   $inject = annotate(fn, strictDi, serviceName),   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);  }  // 注入的服務(wù)作為參數(shù)傳入  args.push(   locals && locals.hasOwnProperty(key)   ? locals[key]   : getService(key, serviceName)  );  }  if (isArray(fn)) {  fn = fn[length];  }  // http://jsperf.com/angularjs-invoke-apply-vs-switch  // #5388  return fn.apply(self, args); } function instantiate(Type, locals, serviceName) {  // 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) {}]);  // Object creation: http://jsperf.com/create-constructor/2  var instance = Object.create((isArray(Type) ? Type[Type.length - 1] : Type).prototype);  var returnedValue = invoke(Type, instance, locals, serviceName);  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);  } }; }

createInternalInjector方法創(chuàng)建$injector對(duì)象,傳遞的參數(shù)分別為緩存對(duì)象和工廠函數(shù)。在具體實(shí)現(xiàn)中,AngularJS創(chuàng)建了兩個(gè)injector對(duì)象--providerInjector和instanceInjector(這兩個(gè)對(duì)象的不同主要是createInternalInjector方法傳遞的緩存對(duì)象不同),而通過(guò)angular.injector()導(dǎo)出的就是instanceInjector。對(duì)于providerInjector,主要用來(lái)獲取服務(wù)的提供者,即serviceProvider。而對(duì)于instanceInjector而言,主要用于執(zhí)行從providerInjector獲取的provider對(duì)象的$get方法,生產(chǎn)服務(wù)對(duì)象(依賴),并將服務(wù)對(duì)象傳遞給相應(yīng)的函數(shù),完成IoC。

首先從get方法說(shuō)起,get方法主要獲取指定名稱的服務(wù),通過(guò)angular的injector方法獲取的是instanceInjector,而當(dāng)緩存中沒(méi)有該服務(wù)對(duì)象(依賴)時(shí),我們需要執(zhí)行factory(serviceName, caller)方法,我們看看對(duì)于的factory函數(shù):

instanceInjector = (instanceCache.$injector =   createInternalInjector(instanceCache, function(serviceName, caller) { var provider = providerInjector.get(serviceName + providerSuffix, caller); return instanceInjector.invoke(provider.$get, provider, undefined, serviceName);   }));

紅色部分即為factory函數(shù),它顯示通過(guò)providerInjector獲取相應(yīng)服務(wù)的提供者serviceProvider,然后調(diào)用instanceInjector的invoke方法在serviceProvider上下文執(zhí)行serviceProvider的$get方法,返回服務(wù)對(duì)象并保存在緩存中。這樣,便完成了服務(wù)對(duì)象(依賴)的獲取和緩存。

invoke方法也很簡(jiǎn)單,它的入?yún)⒎謩e問(wèn)fn, self, locals, serviceName,即要執(zhí)行的函數(shù),函數(shù)執(zhí)行的上下文,提供的options選項(xiàng)和服務(wù)名。首先獲取函數(shù)的所有依賴名,通過(guò)annotate方法完成之后,如果options中提供了對(duì)于名稱的依賴,則使用,否則通過(guò)get方法獲取依賴,最后傳入函數(shù),并將函數(shù)的執(zhí)行結(jié)果返回。invoke返回的結(jié)果往往是一個(gè)服務(wù)對(duì)象。

instantiate方法主要根據(jù)提供的構(gòu)造函數(shù)創(chuàng)建一個(gè)示例,用作依賴或提供服務(wù)。值得一提的是并沒(méi)有通過(guò)new關(guān)鍵字創(chuàng)建對(duì)象,而是通過(guò)ECMA5提供的Object.create來(lái)繼承函數(shù)的原型對(duì)象實(shí)現(xiàn),非常巧妙。

has方法則是相繼判斷serviceProvider和service是否存在于緩存中。

至此,$injector對(duì)象創(chuàng)建完畢。

注冊(cè)服務(wù)(依賴)

服務(wù)不可能憑空而來(lái),我們需要自己實(shí)現(xiàn)或者外部引入服務(wù)或依賴。所以,注冊(cè)服務(wù)的模塊也是值得深究的。AngularJS提供了多種注冊(cè)服務(wù)的API,但是我們著重關(guān)注的是provider方法,其他factory,service方法都是基于此進(jìn)行構(gòu)建的。

這些方法(provider,factory等)綁定在providerCache.provide對(duì)象上,而我們通過(guò)angular.module(′app′,[]).provider(...)方式調(diào)用的provider函數(shù),會(huì)在module加載期間將調(diào)用(該調(diào)用抽象成一個(gè)數(shù)組,即[provider,method,arguments])綁定在內(nèi)部的invokeQueue數(shù)組中,最終在providerCache.provide對(duì)象上,而我們通過(guò)angular.module(′app′,[]).provider(...)方式調(diào)用的provider函數(shù),會(huì)在module加載期間將調(diào)用(該調(diào)用抽象成一個(gè)數(shù)組,即[provider,method,arguments])綁定在內(nèi)部的invokeQueue數(shù)組中,最終在providerCache.provide對(duì)象上調(diào)用provider方法,其他的controller,directive等方法類似,不過(guò)是綁定在providerCache.controllerProvider,providerCache.controllerProvider,providerCache.compileProvider對(duì)象上。

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 enforceReturnValue(name, factory) { return function enforcedReturnValue() {  var result = instanceInjector.invoke(factory, this);  if (isUndefined(result)) {  throw $injectorMinErr('undef', "Provider '{0}' must return a value from $get factory method.", name);  }  return result; }; } function factory(name, factoryFn, enforce) { return provider(name, {  $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn }); } function service(name, constructor) { return factory(name, ['$injector', function($injector) {  return $injector.instantiate(constructor); }]); } function value(name, val) { return factory(name, valueFn(val), false); } function constant(name, value) { assertNotHasOwnProperty(name, 'constant'); providerCache[name] = value; instanceCache[name] = value; } // 在服務(wù)實(shí)例化之后進(jìn)行調(diào)用(攔截),攔截函數(shù)中注入實(shí)例化的服務(wù),可以修改,擴(kuò)展替換服務(wù)。 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}); }; }

provider方法需要兩個(gè)參數(shù),一個(gè)是服務(wù)名(依賴名),另外是工廠方法或者是一個(gè)包含依賴和工廠方法的數(shù)組。首先通過(guò)providerInjector創(chuàng)建工廠方法的一個(gè)實(shí)例,并添加到providerCache中,返回。

factory方法只是將第二個(gè)參數(shù)封裝成了一個(gè)包含$get方法的對(duì)象,即serviceProvider,緩存。并不復(fù)雜。

而service方法則嵌套注入了$injector服務(wù),即instanceInjector,它會(huì)創(chuàng)建構(gòu)造函數(shù)的實(shí)例,作為服務(wù)對(duì)象。

value方法僅僅封裝了一個(gè)provider,其$get方法返回value值。

constant方法則將value的值分別存入providerCache和instanceCache中,并不需要invoke獲取其value值。

而比較特殊且擴(kuò)展性較高的decorator方法,是在serviceProvider的get方法后面添加一個(gè)攔截函數(shù),并通過(guò)傳遞依賴get方法后面添加一個(gè)攔截函數(shù),并通過(guò)傳遞依賴delegate來(lái)獲取原先invoke $get方法返回的服務(wù)對(duì)象。我們可以通過(guò)decorator來(lái)對(duì)服務(wù)進(jìn)行擴(kuò)展,刪除等操作。

流程

最后,在基本的實(shí)現(xiàn)已經(jīng)完成的基礎(chǔ)上,我們走一遍具體的注入流程,更易于我們的深入理解。

angular.module("app",[])  .provider("locationService",function(){  ... }) .controller("WeatherController",function($scope,locationService,$location){  locationService.getWeather()   .then(function(data){    $scope.weather = data;   },function(e){    console.log("error message: "+ e.message)   }); })

我們不關(guān)心具體的代碼實(shí)現(xiàn),僅僅使用上述代碼作為演示。

首先確定AngularJS上下文的范圍,并且獲取依賴模塊(在此處為空);

繼續(xù)注冊(cè)服務(wù)(依賴),將serviceProvider緩存至providerCache中;

聲明控制器;

在此獲取injector示例,通過(guò)執(zhí)行invoke函數(shù),獲取[“injector示例,通過(guò)執(zhí)行invoke函數(shù),獲取[“scope”,”locationService”,”location”]依賴列表,通過(guò)location”]依賴列表,通過(guò)injector的get方法獲取相應(yīng)的依賴對(duì)象。對(duì)于scope和scope和location服務(wù)而言,在AngularJS初始化時(shí)已經(jīng)注入到Angular中,因此可以獲取相應(yīng)的provider對(duì)象,執(zhí)行相關(guān)的方法返回scope和scope和location對(duì)象,而locationService則在provider中進(jìn)行了聲明,因此獲取到locationServiceProvider對(duì)象,通過(guò)調(diào)用instanceInjector.invoke(locationServiceProvider.$get, locationServiceProvider, undefined, “l(fā)ocationService”)返回locationService對(duì)象。

最后將所有的依賴組裝成數(shù)組[scope,locationService,scope,locationService,location]作為參數(shù)傳遞給匿名函數(shù)執(zhí)行。

總結(jié)

至此,依賴注入完成。大家對(duì)依賴注入$injector有沒(méi)有進(jìn)一步的了解呢?以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來(lái)一定的幫助,如果有疑問(wèn)大家可以留言交流。

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 云和县| 伊金霍洛旗| 扶风县| 墨竹工卡县| 南川市| 营口市| 梁河县| 屯门区| 卢氏县| 丹东市| 顺平县| 昌平区| 达尔| 壤塘县| 晋中市| 台东市| 林周县| 泗阳县| 屏东县| 金湖县| 东乡族自治县| 衡南县| 博客| 广昌县| 桂平市| 彩票| 治县。| 汉沽区| 鄯善县| 汶川县| 宁乡县| 林西县| 都匀市| 永丰县| 泗水县| 武川县| 盐山县| 西丰县| 黄浦区| 茶陵县| 响水县|