在编写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);
View Code

相关文章:

  • 2022-12-23
  • 2023-03-23
  • 2021-06-07
  • 2022-12-23
  • 2022-12-23
  • 2021-05-18
  • 2021-12-10
猜你喜欢
  • 2021-05-18
  • 2022-12-23
  • 2021-10-29
  • 2021-06-02
  • 2022-01-16
  • 2022-02-01
相关资源
相似解决方案