dojo/aspect源码解析
dojo/aspect模塊是dojo框架中對(duì)于AOP的實(shí)現(xiàn)。關(guān)于AOP的詳細(xì)解釋請(qǐng)讀者另行查看其它資料,這里簡(jiǎn)單復(fù)習(xí)一下AOP中的基本概念:
生成代理對(duì)象的過(guò)程可以按照下圖理解:
dojo/aspect模塊代碼主要分為兩部分:
- advise方法,通過(guò)使用閉包跟鏈?zhǔn)侥P蛠?lái)構(gòu)造“通知”鏈。 "use strict";var undefined, nextId = 0;function advise(dispatcher, type, advice, receiveArguments){var previous = dispatcher[type];var around = type == "around";var signal;if(around){var advised = advice(function(){return previous.advice(this, arguments);});signal = {remove: function(){if(advised){advised = dispatcher = advice = null;}},advice: function(target, args){return advised ?advised.apply(target, args) : // called the advised functionprevious.advice(target, args); // cancelled, skip to next one }};}else{// create the remove handlersignal = {remove: function(){if(signal.advice){var previous = signal.previous;var next = signal.next;if(!next && !previous){delete dispatcher[type];}else{if(previous){previous.next = next;}else{dispatcher[type] = next;}if(next){next.previous = previous;}}// remove the advice to signal that this signal has been removeddispatcher = advice = signal.advice = null;}},id: nextId++,advice: advice,receiveArguments: receiveArguments};}if(previous && !around){if(type == "after"){// add the listener to the end of the list// note that we had to change this loop a little bit to workaround a bizarre IE10 JIT bugwhile(previous.next && (previous = previous.next)){}previous.next = signal;signal.previous = previous;}else if(type == "before"){// add to beginningdispatcher[type] = signal;signal.next = previous;previous.previous = signal;}}else{// around or first one just replacesdispatcher[type] = signal;}return signal;} View Code
- aspect方法,這個(gè)函數(shù)返回一個(gè)閉包。閉包的作用是將“通知”方法織入到目標(biāo)函數(shù)中,java中運(yùn)行時(shí)通過(guò)反射的方式來(lái)織入,而js中通過(guò)動(dòng)態(tài)更改目標(biāo)函數(shù)來(lái)實(shí)現(xiàn)織入過(guò)程,這時(shí)調(diào)用該方法可以使切面函數(shù)與業(yè)務(wù)邏輯同時(shí)進(jìn)行。 function aspect(type){return function(target, methodName, advice, receiveArguments){var existing = target[methodName], dispatcher;if(!existing || existing.target != target){// no dispatcher in placetarget[methodName] = dispatcher = function(){var executionId = nextId;// before advicevar args = arguments;var before = dispatcher.before;while(before){args = before.advice.apply(this, args) || args;before = before.next;}// around adviceif(dispatcher.around){var results = dispatcher.around.advice(this, args);}// after advicevar after = dispatcher.after;while(after && after.id < executionId){if(after.receiveArguments){var newResults = after.advice.apply(this, args);// change the return value only if a new value was returnedresults = newResults === undefined ? results : newResults;}else{results = after.advice.call(this, results, args);}after = after.next;}return results;};if(existing){dispatcher.around = {advice: function(target, args){return existing.apply(target, args);}};}dispatcher.target = target;}var results = advise((dispatcher || existing), type, advice, receiveArguments);advice = null;return results;};} View Code
?
注意:dojo的處理過(guò)程中并不生成代理對(duì)象,而是直接更改原有的對(duì)象的方法。
關(guān)于aspect.after方法(before方法與其類似)的解釋請(qǐng)看這篇文章:Javascript事件機(jī)制兼容性解決方案;aspect.around的由來(lái)在這篇文章Javascript aop(面向切面編程)之a(chǎn)round(環(huán)繞)里有其一步步的演化過(guò)程。
本文給出aspect模塊調(diào)用后的示意圖:
before與after函數(shù):
around函數(shù):
var advised = advice(function(){return previous.advice(this, arguments);});signal = {remove: function(){if(advised){advised = dispatcher = advice = null;}},advice: function(target, args){return advised ? //一旦調(diào)用remove,adviced變?yōu)榭?#xff0c;便會(huì)跳過(guò)本次環(huán)繞通知,進(jìn)入上一層的advice方法。advised.apply(target, args) : // called the advised functionprevious.advice(target, args); // cancelled, skip to next one }};
可以看到around函數(shù)中借用閉包形成環(huán)繞函數(shù)鏈。這里調(diào)用remove方法后并沒(méi)有像before跟after中將通知方法徹底移除,注冊(cè)過(guò)的環(huán)繞方法仍然會(huì)存在內(nèi)存中,所以這個(gè)方法無(wú)法移除環(huán)繞通知,僅僅是避免了在函數(shù)鏈中執(zhí)行它而已。內(nèi)存無(wú)法釋放,不建議使用太多。
?
總結(jié)
以上是生活随笔為你收集整理的dojo/aspect源码解析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Android -- DragDrop
- 下一篇: 美国域名总量跌至7971万:4月上旬降幅