面向切面编程
pr1mavera 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的执行结果