读书人

[设计形式]JavaScript钩子机制的应用场

发布时间: 2013-10-30 12:56:21 作者: rapoo

[设计模式]JavaScript钩子机制的应用场景与实现

当我们面对比较复杂的前端项目时,我们经常会采用模块化的方式来对JavaScript代码进行解耦,以方便对代码的管理和维护,以下是一个简单的立即执行函数实现的模块化例子:


  1. var Common = (function(){
  2. var func = function(){
  3. // 全局公用方法
  4. }
  5. return {
  6. func : func
  7. }
  8. })();
  9. var ModuleA = (function(){
  10. var _count = 1;
  11. var init = function(){
  12. // 独立模块逻辑
  13. }
  14. var getCount = function(){
  15. return _count;
  16. }
  17. return {
  18. init : init,
  19. getCount : getCount
  20. }
  21. })();


模块只对外暴露外部需要的接口,而外部模块不需要关心其内部的运行逻辑,只需要知道调用接口的方式和返回结果,这样就实现了模块的“低耦合,高内聚”。


看起来很美好,可是当项目的逻辑变的越来越复杂时,比如A模块中某个针对全局公用的逻辑,可能在B模块的某种特定情况下需要进行一些额外的逻辑操作,该怎么办呢?


  1. var Common = (function(){
  2. var func = function(){
  3. // 全局公用方法
  4. if(typeof ModuleA != 'undefined' && ModuleA.getCount() > 0){
  5. // 模块A需要进行的额外逻辑操作
  6. }
  7. }
  8. return {
  9. func : func
  10. }
  11. })();
  12. var ModuleA = (function(){
  13. var _count = 1;
  14. var init = function(){
  15. // 独立模块逻辑
  16. }
  17. var getCount = function(){
  18. return _count;
  19. }
  20. return {
  21. init : init,
  22. getCount : getCount
  23. }
  24. })();


不知道当你看到Common.func中间的那一坨东西的时候,会不会突然怒吼一声:“卧槽,尼玛怎么这么恶心!”= =。。


明明是A模块的逻辑,却恶心地出现在了公用模块里,如果这样的特殊逻辑多起来之后,Common模块会不会变成这样?


  1. var Common = (function(){
  2. var func = function(){
  3. // 全局公用方法
  4. if(typeof ModuleA != 'undefined' && ModuleA.getCount() > 0){
  5. // 模块A需要进行的额外逻辑操作
  6. }
  7. if(typeof ModuleB != 'undefined' && ModuleB.getWhat() != 'something'){
  8. // 模块B需要进行的额外逻辑操作
  9. }
  10. // ...
  11. if(typeof ModuleZ != 'undefined' && ModuleB.isWhat() !== false){
  12. // 模块Z需要进行的额外逻辑操作
  13. }
  14. }
  15. return {
  16. func : func
  17. }
  18. })();


天哪,简直无法忍受。。


如果。。如果有这么一个钩子(Hook),可以把额外的逻辑代码挂在Common.func上,而Common.func执行的时候顺便把钩子上挂着的代码也执行了,那该多好啊。。这样的话既可以实现特殊的额外操作,又可以保持模块的低耦合和高内聚:


  1. var Common = (function(){
  2. var func = function(){
  3. // 执行挂在这个方法的钩子上的所有额外逻辑代码
  4. Hook.doActions();
  5. // 全局公用方法
  6. }
  7. return {
  8. func : func
  9. }
  10. })();
  11. var ModuleA = (function(){
  12. var _count = 1;
  13. var init = function(){
  14. // 用钩子把额外的逻辑挂到Common.func上
  15. Hook.addAction(Common.func, function(){
  16. if(_count > 0){
  17. // 增加的额外逻辑操作
  18. console.log('看看执行到没?');
  19. }
  20. });
  21. // 独立模块逻辑
  22. }
  23. var getCount = function(){
  24. return _count;
  25. }
  26. return {
  27. init : init,
  28. getCount : getCount
  29. }
  30. })();


没有不可能。借鉴了一下WordPress的Hook机制,一个基于JavaScript钩子机制模块就实现了。


当然,一个完整的钩子机制需要考虑的并不像上面说的那么简单,具体的实现大家请看代码,或者懒得看的可以自己尝试实现,我就不在赘述了:


  1. /**
  2. * 钩子模块(动作是对方法的逻辑进行补充,过滤器是对方法的返回值进行处理)
  3. *
  4. * @author Lanfei
  5. * @date 2013.10.28
  6. *
  7. * function handler(num1, num2){
  8. * Hook.doActions();
  9. * var value = num1 + num2;
  10. * return Hook.applyFilters(value);
  11. * }
  12. * console.log('before hook:');
  13. * console.log(handler(1, 2));
  14. * function action(num1, num2){
  15. * console.log('the numbers are ' + num1 + ' and ' + num2 + '.');
  16. * }
  17. * function filter(value){
  18. * return 'the result is ' + value + '.';
  19. * }
  20. * Hook.addAction(handler, action);
  21. * Hook.addFilter(handler, filter);
  22. * console.log('after hook: ');
  23. * console.log(handler(1, 2));
  24. *
  25. */
  26. var Hook = (function(){
  27. var addAction = function(method, action, priority){
  28. _initHook(method);
  29. var actions = method['__hooks__'].actions;
  30. actions.push({
  31. action : action,
  32. priority : priority || 10
  33. });
  34. actions.sort(_compare);
  35. }
  36. var doActions = function(){
  37. var method = Hook.doActions.caller;
  38. _initHook(method);
  39. var actions = method['__hooks__'].actions;
  40. if(arguments.length == 0){
  41. arguments = method.arguments;
  42. }
  43. for(var i in actions){
  44. if(actions[i].action.apply(method, arguments) === false){
  45. return false;
  46. }
  47. }
  48. }
  49. var hasAction = function(method, action){
  50. _initHook(method);
  51. var actions = method['__hooks__'].actions;
  52. if(actions.length > 0 && action !== undefined){
  53. for(var i in actions){
  54. if(actions[i].action == action){
  55. return true;
  56. }
  57. }
  58. return false;
  59. }else{
  60. return actions.length > 0;
  61. }
  62. }
  63. var removeAction = function(method, action){
  64. _initHook(method);
  65. var actions = method['__hooks__'].actions;
  66. if(actions.length > 0){
  67. if(action !== undefined){
  68. for(var i in actions){
  69. if(actions[i].action == action){
  70. delete actions[i];
  71. return;
  72. }
  73. }
  74. }else{
  75. method['__hooks__'].actions = [];
  76. }
  77. }
  78. }
  79. var addFilter = function(method, filter, priority){
  80. _initHook(method);
  81. var filters = method['__hooks__'].filters;
  82. filters.push({
  83. filter : filter,
  84. priority : priority || 10
  85. });
  86. filters.sort(_compare);
  87. }
  88. var applyFilters = function(value){
  89. var method = Hook.applyFilters.caller;
  90. _initHook(method);
  91. var filters = method['__hooks__'].filters;
  92. for(var i in filters){
  93. value = filters[i].filter.call(method, value);
  94. }
  95. return value;
  96. }
  97. var hasFilter = function(method, filter){
  98. _initHook(method);
  99. var filters = method['__hooks__'].filters;
  100. if(filters.length > 0 && filter !== undefined){
  101. for(var i in filters){
  102. if(filters[i].filter == filter){
  103. return true;
  104. }
  105. }
  106. return false;
  107. }else{
  108. return filters.length > 0;
  109. }
  110. }
  111. var removeFilter = function(method, filter){
  112. _initHook(method);
  113. var filters = method['__hooks__'].filters;
  114. if(filters.length > 0){
  115. if(filter !== undefined){
  116. for(var i in filters){
  117. if(filters[i].filter == filter){
  118. delete filters[i];
  119. return;
  120. }
  121. }
  122. }else{
  123. method['__hooks__'].filters = [];
  124. }
  125. }
  126. }
  127. var _compare = function(hook1, hook2){
  128. return hook1.priority < hook2.priority;
  129. }
  130. var _initHook = function(method){
  131. if(! method['__hooks__']){
  132. method['__hooks__'] = {
  133. actions : [],
  134. filters : []
  135. };
  136. }
  137. }
  138. return {
  139. addAction : addAction,
  140. doActions : doActions,
  141. hasAction : hasAction,
  142. removeAction : removeAction,
  143. addFilter : addFilter,
  144. applyFilters : applyFilters,
  145. hasFilter : hasFilter,
  146. removeFilter : removeFilter
  147. };
  148. })();


PS:发现我写博客不怎么喜欢用图片,不知道大家看起来会不会不太喜欢= =。。



=======================签 名 档=======================
原文地址(我的博客):http://www.clanfei.com/2013/10/1730.html
欢迎访问交流,至于我为什么要多弄一个博客,因为我热爱前端,热爱网页,我更希望有一个更加自由、真正属于我自己的小站,或许并不是那么有名气,但至少能够让我为了它而加倍努力。。
=======================签 名 档=======================


读书人网 >软件开发

热点推荐