Appearance
19. AOP 在 JS 中的实现
定义
定义:AOP( Aspect-oriented programming)是面向切面编程,和 OOP、FP一样,都是一种编程思想,将一些与函数的核心功能无关的代码抽离出来,并能够添加一层通知机制,对分离出来的代码进行统一管理。
作用:很好的遵循 单一职责原则,该思想使得我们能够将与代码核心业务逻辑关系不那么密切的功能(如日志功能)添加至程序中,同时又不降低业务代码的可读性,提高模块化程度。
实现
实现思路:我们要实现的效果大致如下所示:
js
// 主业务函数
function main() {
console.log('执行主业务函数')
}
/**
* 对 main 进行切面的相关处理
*/
main()
/**
* 输出:
* 切入之前
* 执行主业务函数
* 切入之后
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
在知道 AOP 之前,我们大概率会这么做:
js
function main() {
console.log('切入之前')
console.log('我是主业务函数')
console.log('切入之后')
}
1
2
3
4
5
2
3
4
5
或者将函数抽离出来:
js
function before() {
console.log('切入之前')
}
function after() {
console.log('切入之前')
}
function main() {
before()
console.log('执行主业务函数')
after()
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
但是上述方法都不能将 before
和 after
这些与主业务功能无密切关系的代码分离出来,因为 before
和 after
可能会用到 main
函数的处理结果,所以又不能直接写成下面这样:
js
before()
main()
after()
1
2
3
2
3
这个时候 AOP 就排上用场了,使用 JS 的原型、闭包和高阶函数,我们可以很方便的实现 AOP。
js
Function.prototype.before = function (beforeFunc) {
let that = this // 保存主业务函数的 this
return function () {
beforeFunc() // 切入主函数之前
that.apply(this, arguments) // arguments 是主函数中的
}
}
Function.prototype.after = function (afterFunc) {
let that = this // 保存主业务函数的 this
return function () {
that.apply(this, arguments) // arguments 是主函数中的
afterFunc.apply() // 切入主函数之后
}
}
function add(...args) {
// 主函数:数字求和
let res = args.reduce((pre, cur) => pre + cur, 0)
console.log('数字之和:' + res)
return res
}
function beforeFunc() {
// 抽离出来的函数
console.log('切入前')
}
function afterFunc() {
// 抽离出来的函数
console.log('切入后')
}
// 由于 before、after 返回的还是函数,所以可以链式执行
const main = add.before(beforeFunc).after(afterFunc)
main(1, 2)
/**
* 打印:
* 切入前
* 数字之和:3
* 切入后
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
改进
直接在 Function
原型上定义方法,一定程度上污染了原型,我们可以使用函数重新改写该方法。
js
function before(fn, beforeFunc) {
return function () {
beforeFunc()
fn.apply(this, arguments)
}
}
function after(fn, afterFunc) {
return function () {
fn.apply(this, arguments)
afterFunc()
}
}
function add(...args) {
let res = args.reduce((pre, cur) => pre + cur, 0)
console.log('数字之和为:' + res)
return res
}
function beforeFunc() {
console.log('切入之前')
}
function afterFunc() {
console.log('切入之后')
}
add = after(before(add, beforeFunc), afterFunc)
add(1, 2, 3)
/**
* 打印:
* 切入之前
* 数字之和为:6
* 切入之后
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38