你相信么,只需一个函数5行JS代码即可在Javascript中实现完整的AOP功能

你相信么,只需一个函数5行JS代码即可在Javascript中实现完整的AOP功能,

你相信么,在JavaScript只需一个函数5行代码即可实现完整的面向方面AOP编程功能。这5行代码的功能包括:

  • 无限层次的函数无害拦截
  • 函数执行前拦截
    • 检查函数的参数值
    • 重新设定函数的参数值
  • 函数执行后拦截
    • 检查函数执行后的返回结果
    • 重新设定函数的返回结果

虽然动态函数式语言的效率是一个存在的问题,但是对于它的高度灵活性,简直让人令人惊叹不已,剧赞。
这个小小的函数源自于和爱明兄的一次讨论:在javascript中不修改源代码的形式扩充新功能的完美方式。
至于为啥要不直接修改别人源代码库,你想,当你辛苦修改后,人家发布了一个新版本,你是不是又要重新
修改一次?如果修改的地方少还好,多了的话,嘿嘿!所以,不是你的东西,最好不要动,除非你确定要自
己完全维护所有的代码(如果你实在太闲)。

为了给开发人员一个榜样,俺不得不写一个patch样例,最开始俺写的patch方式是这样滴:

FCKEditingArea.prototype.FckMediaStart = FCKEditingArea.prototype.Start;
FCKEditingArea.prototype.Start =  function( html, secondCall )
{
  var sHeadExtra = ‘<link href="‘ + FCKConfig.PluginsPath + ‘media/css/fck_media.css" rel="stylesheet" type="text/css" _fcktemp="true" />‘ ;
  html = html.replace( FCKRegexLib.HeadCloser, sHeadExtra + ‘$&‘ ) ;
  return this.FckMediaStart(html, secondCall);
}

俺觉得这种补丁方式是不太让我满意的

爱民兄提到他喜欢这样patch方式:

FCKEditingArea.prototype.Start = function( func ) { 
  return function( html ) { 
    html = html.replace( FCKRegexLib.HeadCloser, sHeadExtra + ‘$&‘ ) ; 
    return func.apply(this, arguments); 
  } 
}(FCKEditingArea.prototype.Start);

这种方式的确不存在漏洞了,perfect,但是却把我彻底弄懵了,看得我头大啊!琢磨了好久才算明白过来。
不过从管理角度考虑,如果程序中到处都是这样的方式,那么将大大降低程序的可读性,增加维护代码的成本
(ps,要开发维护这样的代码,你不得不请JS高级程序员才行)。
为了限制匿名函数的滥用导致的可读性下降,俺写了一个Inject()函数的雏形(将匿名函数的使用限制在其中),
经过爱民兄修改,然后又是讨论,然后我们又修改,如此反复,达到这个最终版本(也许还不是,谁知道呢)。

好了,荣誉归于爱民,臭鸡蛋归于俺,闲话少说,看看代码吧,不算上注释,这个Inject函数正好5行。如果你清楚
的知道匿名函数的特点,那么这个函数,你就不难看懂,否则你就只能管用了。用法在注释的例子里,够简单吧。

/*
  @desc  inject the function
  @param aOrgFunc     the original function to be injected.
  @param aBeforeExec  this is called before the execution of the aOrgFunc.
                      you must return the arguments if you wanna modify the value of the aOrgFunc‘s arguments .
  @param aAtferExec   this is called after the execution of the aOrgFunc.
                      you must add a result argument at the last argument of the aAtferExec function if you wanna 
                      get the result value of the aOrgFunc.
                      you must return the result if you wanna modify the result value of the aOrgFunc .

@Usage  Obj.prototype.aMethod = Inject(Obj.prototype.aMethod, aFunctionBeforeExec[, aFunctionAtferExec]);
  @author  Aimingoo&Riceball

eg:
  var doTest = function (a) {return a};
  function beforeTest(a) { alert(‘before exec: a=‘+a); a += 3; return arguments;};
  function afterTest(a, result) { alert(‘after exec: a=‘+a+‘; result=‘+result); return result+5;};
  
  doTest = Inject(doTest, beforeTest, afterTest);
  
  alert (doTest(2));
  the result should be 10.

*/
function Inject( aOrgFunc, aBeforeExec, aAtferExec ) {
  return function() {
    if (typeof(aBeforeExec) == ‘function‘) arguments = aBeforeExec.apply(this, arguments) || arguments;
    //convert arguments object to array
    var Result, args = [].slice.call(arguments); 
    args.push(aOrgFunc.apply(this, args));
    if (typeof(aAtferExec) == ‘function‘) Result = aAtferExec.apply(this, args);
    return (typeof(Result) != ‘undefined‘)?Result:args.pop();
  }
}

使用新的Inject方式的代码如下:

FCKMediaProcessor.EditingArea_StartBefore = function ( html, secondCall )
{
  var sHeadExtra = ‘<link href="‘ + FCKConfig.PluginsPath + ‘media/css/fck_media.css" rel="stylesheet" type="text/css" _fcktemp="true" />‘ ;
  html = html.replace( FCKRegexLib.HeadCloser, sHeadExtra + ‘$&‘ ) ;
  //在执行前修改了参数值,所以返回修改后的参数
  return arguments;
}
FCKEditingArea.prototype.Start =  Inject(FCKEditingArea.prototype.Start, FCKMediaProcessor.EditingArea_StartBefore);

ok,下面将继续增强 Inject的功能,将会把浪子提到的控制原函数是否执行加上。
本增强版本和上面的Inject函数的版本使用上略有差别,使用规范如下:

  • 执行前调用(BeforeExec)
    • 如果修改了参数值,必须这样返回修改的参数: return new Arguments(arguments);
    • 如果没有返回值或者返回undefined那么正常执行,返回其它值表明不执行原函数,该值作为替代的原函数返回值。
  • 执行后调用(AfterExec)
    • 如果希望取得函数的返回值,必须在原参数表后面增加一个参数: result
    • 如果希望知道原函数是否被执行,那么必须在 result参数的后面再增加一个参数: isDenied 该值为真表明没有执行原函数
    • 如果希望修改函数的返回值,那么你只要简单的把修改后的值返回即可。

示例:假设我们要拦截一个 doTest 函数,修改它的参数值a,在原参数值上+1,并在它的函数返回值上也+1:

//定义测试函数 doTest
var doTest = function (a) {
  alert(‘dotest 运行中‘);
  return a;
};

function beforeTest(a) { 
  alert(‘执行前参数: a=‘+a); 
  a += 1; 
  return new Arguments(arguments);
}

function afterTest(a, result, isDenied) { 
//这里不会体现出参数a的改变,如果原函数改变了参数a。因为在js中所有参数都是值参。
  alert(‘执行后参数: a=‘+a+‘; result=‘+result+‘;isDenied=‘+isDenied);
  return result+1;
}
  
//注入该doTest函数
doTest = Inject(doTest, beforeTest, afterTest);

//执行 doTest并显示结果
alert (doTest(2));

和爱民兄谈论后,修订后的新的Inject函数如下,这下功能全了,具体代码也不过十多行:

/*
  @desc  inject the function
  @param aOrgFunc     the original function to be injected.
  @param aBeforeExec  this is called before the execution of the aOrgFunc.
                      you must return the arguments(new Arguments(arguments)) if you wanna modify the arguments value of the aOrgFunc.
                      it will stop the execution of the aOrgFunc if you return a value not an Arguments object nor a undefined value 
  @param aAtferExec   this is called after the execution of the aOrgFunc.
                      you must add a result argument at the last argument of the aAtferExec function if you wanna get the result value of the aOrgFunc.
                      you must add a isDenied argument following the result argument if you wanna know whether the aOrgFunc is executed.
                      you must return the result if you wanna modify the result value of the aOrgFunc .

@Usage  Obj.prototype.Method = Inject(Obj.prototype.Method, aFunctionBeforeExec[, aFunctionAtferExec]);
  @version 1.1
  @author  Aimingoo&Riceball
  @history
    V1.0 -- fiest released.
    V1.1 -- 
      Supports to denie the aOrgFunc execution in aBeforeExec.
      Supports around in the aAtferExec, the aAtferExec be always executed even though denie the aOrgFunc execution in aBeforeExec.
        + isDenied argument to the aAtferExec function. notice the aAtferExec whether the aOrgFunc is executed

eg:
  var doTest = function (a) {return a};
  function beforeTest(a) { alert(‘before exec: a=‘+a); a += 3; return new Arguments(arguments);};
  function afterTest(a, result, isDenied) { alert(‘after exec: a=‘+a+‘; result=‘+result+‘;isDenied=‘+isDenied); return result+5;};
  
  doTest = Inject(doTest, beforeTest, afterTest);
  
  alert (doTest(2));
  the result should be 10.

*/
function Arguments(args) {
  //convert arguments object to array
  this.value = [].slice.call(args);
}
function Inject( aOrgFunc, aBeforeExec, aAtferExec ) {
  return function() {
    var Result, isDenied=false, args=[].slice.call(arguments);
    if (typeof(aBeforeExec) == ‘function‘) {
      Result = aBeforeExec.apply(this, args);
      if (Result instanceof Arguments) //(Result.constructor === Arguments)
        args = Result.value;
      else if (isDenied = Result !== undefined)
        args.push(Result)
    }

!isDenied && args.push(aOrgFunc.apply(this, args)); //if (!isDenied) args.push(aOrgFunc.apply(this, args));

if (typeof(aAtferExec) == ‘function‘)
      Result = aAtferExec.apply(this, args.concat(isDenied));
    else 
      Result = undefined;

return (Result !== undefined ? Result : args.pop());
  }
}

时间: 2024-08-24 10:05:35

你相信么,只需一个函数5行JS代码即可在Javascript中实现完整的AOP功能的相关文章

【百度地图API】发布静态图API啦!只需一个网址,即可展示定制百度地图!

原文:[百度地图API]发布静态图API啦!只需一个网址,即可展示定制百度地图! 摘要: 百度地图静态图API!您无须执行任何“特殊”操作便可在网页上显示此图片. 不需要 JavaScript.我们只需创建一个网址,并将其放入 <img> 标记中.即可将定制的百度地图图片嵌入到自己的网页中. ------------------------------------------------------------------------------------------------------

shell------打印出文档中只含有一个数字的行

写一个shell脚本,要求打印出文档中只含有一个数字的行: #!/bin/bash for i in `cat /root/passwd` ;do         n=`echo $i |sed 's/[^0-9]//g'|wc -c`         #echo $n         if [ $n -eq 2 ];then                 echo $i         else                 continue         fi done 执行以下此脚本

18 行 JS 代码编一个倒时器

有时候在生活中,你需要一个JavaScript倒计时时钟,而不是一个末日装置设备.不管你是否有一次约会,销售.促销.或者游戏,你可以受益于使用原生JavaScript构建一个时钟,而不是拿到一个现成的插件.虽然有许多很棒的时钟插件,但如果使用原生 JavaScript 实现,那你将得到以下好处: 代码将是轻量级的,因为它没有依赖关系. 你的网站会表现得更好,因为你不需要加载外部脚本和样式表. 你将会有更高的可控性,因为你将按照想要的时钟行为的方式来创建它(而不是找一个趋向你想法的插件). 所以事

只需一个中秋,京东企业购就能变身“独角兽”?

中秋将至,除了传统习俗吃月饼赏月的合家团圆,电商的"企业购"布局也适逢中秋送礼的采购旺季终于找到了自己的用武之地.最近几年,企业级市场的悄然崛起引起不少人的关注,企业网购这个隐藏着巨大前景和无限可能的万亿级市场正在爆发.而于去年更名并上线的京东企业购也一定会在中秋这场大型"首秀"中大干一场. 从BAT到BATJ,从阿里独树一帜到电商双雄,京东真的是凭借618电商一飞冲天的吗?李嘉诚说成功没有偶然,京东由电商延伸生长的各方面业务实力都在步步增强.而随着京东企业级供应链

微信生成二维码 只需一个网址即刻 还有jquery生成二维码

<div class="orderDetails-info"> <img src="http://qr.topscan.com/api.php?text=http://123.net/index.php?s=/Home/Index/yanzheng/mai/{$dange.id}" style="width: 5rem; margin-bottom: 1rem;" > </div> http://qr.tops

vue入门模板——只需一个html

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>vue模板</title> <link rel="stylesheet" type="text/css" href="//unpkg.com/[email protected]/lib/theme-chalk/index.css"

转化json key的命名的一个小东西。js代码,,c_name_id &lt;==&gt; cNameId

发现新公司新老接口返回值的命名规则竟然不一样,瞬间瞎了. 无奈写一个转化的. 程序还有很大的扩展性可以做,有时间继续. function changeName(json,type){ var out={}; if(type == 1){ for( var key in json){ var arr=[]; arr = key.split('_'); for (var i=1;i<arr.length;i++){ var prev = arr[i].substring(0,1).toUpperCa

【JavaScript】Javascript中的函数声明和函数表达式

Javascript有很多有趣的用法,在Google Code Search里能找到不少,举一个例子: <script> ~function() { alert("hello, world."); }(); </script> 试一下就知道这段代码的意思就是声明一个函数,然后立刻执行,因为Javascript中的变量作用域是基于函数的,所以这样可以避免变量污染,但这里的位运算符『~』乍一看让人摸不到头脑,如果去掉它再运行则会报错:SyntaxError. 在阐述

开源|Davinci用户体验:你离数据可视精美大屏只差一个Davinci!

导读:同比和环比是衡量企业某个数据周期性增长速度变化的重要指标,但是一味的看数据,我们很难对增长率的大小做出比较,这个时候就需要可视化工具来帮助我们.小编这次又"编造"了另外一组数据,利用Moonbox写出SQL求同比环比增长率,最后借用Davinci展示出来.具体请看正文~ Davinci介绍:Davinci是一个DVAAS(Data Visualization as a Service)平台解决方案.Davinci面向业务人员/数据工程师/数据分析师/数据科学家,致力于提供一站式数