面向切面编程


2019/9/26 笔记 编程范式

# 概念

面向切面

  • Aspect Oriented Programming (AOP)
  • 目的:针对业务处理过程中的切面进行提取
  • 效果:面对处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果
  • 场景:需要对一个网页进行性能检测
    • 侵入式:在需要记录的时间点进行埋点注入,根据各节点的记录的时间来描述网页性能
    • 无侵入式:注入一段SDK,包装对浏览器各时间节点的记录,统一汇总节点时间

# 实践

现有一业务需求,要求监控同步函数执行的前后都设置回调函数

// 同步函数
function test () {
    console.log(2);
    // 业务逻辑...
    return 'test的执行结果';
}

# 侵入式的写法

没什么思考的,写就完了

function test (before, after) {
    before();

    console.log(2);
    // 业务逻辑...

    after();

    return 'test的执行结果';
}

function before () {
    console.log(1);
}

function before () {
    console.log(3);
}

输入 :
1
2
3

但这非常 可惜 !!

  • 每个函数都得写一遍,效率低下
  • 为了监控的需求而去修改了原来的业务代码,耦合性高
  • 可读性非常低,接手你项目的人一脸懵逼业务逻辑就业务逻辑呗,你加啥 before after ,过家家呢?

# 面向切面的写法

首先我们尝试在原型链上动动手脚

function test () {
    console.log(2);
    // 业务逻辑...
    return 'test的执行结果';
}

Function.prototype.before = function (fn) {
    const _this = this;
    fn();
    _this.call(_this, arguments);
}

Function.prototype.after = function (fn) {
    const _this = this;
    _this.call(_this, arguments);
    fn();
}

test.before(function () {
    console.log(1);
})
test.after(function () {
    console.log(3);
})

输入 :
1
2
2
3

好像有点效果,但是我们发现 test 主函数被执行了两次,这肯定不可行

我们再动动手脚

function test () {
    console.log(2);
    // 业务逻辑...
    return 'test的执行结果';
}

Function.prototype.before = function (fn) {
    // 这里的 this 不一定是之前的 test ,也有可能是通过 after 包装过后抛出的函数
    const _this = this;
    // 这里直接抛出一个待执行的函数
    return function () {
        // 这里的 this 指向的是 window
        fn.apply(this);
        _this.apply(_this, arguments);
    }
}

Function.prototype.after = function (fn) {
    // 这里的 this 不一定是之前的 test ,也有可能是通过 before 包装过后抛出的函数
    const _this = this;
    return function () {
        _this.apply(_this, arguments);
        fn.apply(this);
    }
}

test.before(function () {
    console.log(1);
}).after(function () {
    console.log(3);
})();

输入 :
1
2
3

通过这样面向切面的方式,我们既完成的需求也保证了原函数的完整性

还可以试着加入流程控制机制:

  • before 的执行结果为 false 的时候,控制 test 以及 after 不再继续执行
  • 在 after 的回调中拿到 test 执行的结果
function test () {
    console.log(2);
    // 业务逻辑...
    return 'test的执行结果';
}

Function.prototype.before = function (fn) {
    // 这里的 this 不一定是之前的 test ,也有可能是通过 after 包装过后抛出的函数
    const _this = this;
    // 这里直接抛出一个待执行的函数
    return function () {
        // 当 before 的回调结果为 false 的时候,不再继续执行
        if (fn.apply(this) === false) return void 0;
        // 执行后续回调并返回结果
        return _this.apply(_this, arguments);
    }
}

Function.prototype.after = function (fn) {
    // 这里的 this 不一定是之前的 test ,也有可能是通过 before 包装过后抛出的函数
    const _this = this;

    return function () {
        // 拿到 test 的执行结果 res
        const res = _this.apply(_this, arguments);
        // 将结果传递给 after 的回调
        fn.apply(this, [ res ]);
    }
}

test.before(function () {
    console.log(1);
    return true;
}).after(function (res) {
    console.log(3);
    // 打印 test 的执行结果
    console.log(res);
})();

输入 :
1
2
3
test的执行结果

Last Updated: 12/2/2019, 9:23:57 AM