巧用 AOP 面向切面编程,实现 JavaScript 设计模式

401 阅读2分钟

是什么

面向切面编程(AOP)是Java常用编程思想,它的作用是在某个函数上进行切割,可以在函数执行 前 / 中 / 后 添加其它逻辑代码。
AOP编程的好处是遵循单一原则,不会修改原函数内部的代码就可以改变原函数的逻辑。
AOP装饰函数的技巧在实际开发中非常有用。无论是业务代码还是框架层面,我们都可以把行为依照职责分成粒度更细的函数,随后通过装饰把它们合并到一起,这有助于我们编写一个松耦合和高复用性的系统。

原型链实现 AOP

    /**
     * 切入前代码
     * @param {Function} fn        
     * @param {Function} beforeFn   
     * @returns 
    */
    const before = function (fn, beforeFn) {
        return function () {
            beforeFn.apply(this, arguments);
            return fn.apply(this, arguments);
        }
    }
    /**
     * 切入后代码
     * @param {Function} fn         
     * @param {Function} beforeFn 
     * @returns 
     */
    const after = function (fn, afterFn) {
        return function () {
            let agent = fn.apply(this, arguments);
            afterFn.apply(this, arguments);
            return agent;
        }
    }


    let logic = function () {
        console.log('业务代码')
    }
    logic = before(logic, function () {
        console.log('切入前代码')
    })
    logic = after(logic, function () {
        console.log('切入后代码')
    })

    logic();
    // 切入前代码
    // 业务代码
    // 切入后代码

实现职责链模式

AOP实现职责链即简单又巧妙,这种把函数叠在一起的方式,同时也叠加了函数的作用域。
最大优点解耦了请求发送者和N个接收者之间复杂关系。

假设:一个电商网站,对缴纳定金的用户有不同的优惠政策。已经支付过 500 元定金的用户会收到 100 元的优惠券,支付 200 元定金的用户可以收到 50 元的优惠券,没有支付定金的用户则为普通的购买模式。没有优惠券,且在库存有限的情况下不一定保证能买到。

   let order500 = function (orderType, pay, stock) {
        if (orderType === 1 && pay) {
            console.log('500元定金,得到100优惠券');
        } else {
            return 'nextSuccessor';
        }
    }

    let order200 = function (orderType, pay, stock) {
        if (orderType === 2 && pay) {
            console.log('200元定金,得到50优惠券');
        } else {
            return 'nextSuccessor';
        }
    };

    let orderNormal = function (orderType, pay, stock) {
        if (stock > 0) {
            console.log('普通购买,无优惠券');
        } else {
            console.log('手机库存不足');
        }
    };

    Function.prototype.after = function (fn) {
        let _this = this

        return function () {
            let ret = _this.apply(this, arguments)
            if (ret === 'nextSuccessor') return fn.apply(this, arguments)
            return ret
        }
    }

    let order = order500.after(order200).after(orderNormal)
    order(1, true, 500); // 500元定金,得到100优惠券
    order(2, true, 500); // 200元定金,得到50优惠券
    order(3, true, 500); // 普通购买,无优惠券

实现装饰器模式

假设:页面中有一个登录 button 点击这个 button 会弹出登录浮层,与此同时要进行数据上报,来统计有多少个用户点击了这个登录 button。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title></title>
</head>

<body>
    <button tag="login" id="button">点击</button>
</body>
<script>
    Function.prototype._after = function (afterFn) {
        var _self = this;

        return function () {
            var ret = _self.apply(this, arguments);
            afterFn.apply(this, arguments);
            return ret;
        }
    };
    let showLogin = function () {
        console.log('打开登录浮层')
    }
    let log = function () {
        console.log(`上报标签为${this.getAttribute('tag')}`)
    }
    showLogin = showLogin._after(log)
    document.getElementById('button').onclick = showLogin

    // 打开登录浮层
    // 上报标签为login
</script>
</html>

参考文章:
js实现AOP,面向切面编程