在编写js的时候,我们有时会遇到针对某种场景做处理,比如在方法开始的时候校验参数,执行方法前检查权限,或是删除前给出确认提示等等。这些校验方法、权限检测、确认提示,规则可能都是相同的,在每个方法前去调用,显得麻烦,而且不利于统一管理,于是我们想到了面向切面编程(AOP)。
1. 简单AOP实现
简单的AOP实现,就是在原函数执行的前后,增加运行before和after两个增强方法,用这个新函数替换原函数,为此,我编写了一个类似于构造器的函数(后文就称它为构造器),代码如下:
// originFun为原函数,before和after为增强方法 function constructor(originFun, before, after){ function _class(){ before.apply(this, arguments); originFun.apply(this, arguments); after.apply(this, arguments); } return _class; }
使用时,用构造器生成的新函数替换原函数即可,测试代码如下:
// 加法运算,作为测试的原函数 function calcAdd(a,b){ console.log(a + "+" + b + "=" + (a + b)); return a+b; } // AOP增强 calcAdd = constructor(calcAdd, function(){console.log("我在原方法前执行")}, function(){console.log("我在原方法后执行")}); // 调用方法进行测试 calcAdd(2, 3);
打印在控制台的测试结果:
我在原方法前执行 2+3=5 我在原方法后执行
2. AOP工厂
在某些场景下,使用的增强方法是相同的,每次将增强方法作为参数传递有点麻烦,于是我做了一个工厂方法,把增强方法作为参数,这样就可以生成不同的构造器,在不同场景调用不同的构造器即可:
// AOP工厂 var aopFactory = function(before, after){ // 构造方法,在原方法前后增加执行方法 function constructor(originFun){ function _class(){ var result; proxy.before(arguments); result = originFun.apply(this, arguments); proxy.after(arguments); return result; } return _class; } var proxy = { // 添加被代理方法,参数a为被代理方法,参数b为目标对象 add : function(a, b){ var funName; // 判断参数a类型,可以为方法或方法名 if(typeof a == "function"){ funName = a.name; }else if(typeof a == "string"){ funName = a; }else{ return; } // 不传对象时默认为window对象 b = b || window; if(typeof b == "object" && b[funName]){ // 替换原方法 b[funName] = constructor(b[funName]); } }, // 默认before为空方法 before : function(){}, // 默认after为空方法 after : function(){} } // 注入特定的前后处理方法 if(typeof before == "function"){ proxy.before = before; } if(typeof after == "function"){ proxy.after = after; } return proxy; }
测试代码如下:
var printProxy, checkProxy; // 打印参数 function printArguments(){ var i, length; for(i=0, length=arguments.length; i<length; i++){ console.info("param" + (i + 1) + " = " + arguments[i]); } } // 验证参数是否为数字 function checkNumber(){ var i, length; for(i=0, length=arguments.length; i<length; i++){ if(typeof arguments[i] != "number") console.error(arguments[i] + "不是数字"); } } // 将printArguments方法作为前置通知,生成打印参数的构造器 printProxy = aopFactory(printArguments); // 将checkNumber方法作为前置通知,生成验证参数是否为数字的构造器 checkProxy = aopFactory(checkNumber); // 加法 function calcAdd(a,b){ console.log(a + "+" + b + "=" + (a + b)); return a+b; } // 减法 function calcMinus(a,b){ console.log(a + "-" + b + "=" + (a - b)); return a-b; } // 乘法 function calcMultiply(a,b){ console.log(a + "*" + b + "=" + (a * b)); return a*b; } // 除法 function calcDivide(a,b){ console.log(a + "/" + b + "=" + (a / b)); return a/b; } // 为加减法生成验证参数是否为数字的代理方法 checkProxy.add(calcAdd); checkProxy.add(calcMinus); // 为乘除法生成打印参数的代理方法 printProxy.add(calcMultiply); printProxy.add(calcDivide); // 测试 calcAdd("4", 5); calcMinus(6, "a"); calcMultiply(4, 5); calcDivide(6, 3);