一篇记录了自己思考的读书笔记。
博客:Murphy 的博客
知乎:@Murphy
面向侧面的程序设计(aspect-oriented programming,AOP,又译作面向方面的程序设计、观点导向编程、剖面导向程式设计)是计算机科学中的一个术语,指一种程序设计范型。该范型以一种称为侧面(aspect,又译作方面)的语言构造为基础,侧面是一种新的模块化机制,用来描述分散在对象、类或函数中的横切关注点(crosscutting concern)。
从编程的角度看,在运行时,动态地将代码切入到类的指定方法、指定位置的编程思想就是面向切面编程。
AOP 的主要作用是把一些跟核心业务逻辑模块无关的功能抽离出来,这些跟业务逻辑无关的功能通常包括日志统计、安全控制、异常处理等。这样做的好处首先是可以保持业务逻辑模块的纯净和高内聚性,其次是可以很方便地复用日志统计等功能模块。
从主关注点中分离出横切关注点是面向侧面的程序设计(aspect-oriented programming)的核心概念。分离关注点使得解决特定领域问题的代码从业务逻辑中独立出来,业务逻辑的代码中不再含有针对特定领域问题代码的调用,业务逻辑同特定领域问题的关系通过侧面来封装、维护,这样原本分散在在整个应用程序中的变动就可以很好的管理起来。
在 Java 语言中,可以通过反射和动态代理实现 AOP 技术,而在 JavaScript 中,有一种非常巧妙的方法实现。我们先来看一下想要达到的效果:
原来的函数长这样(可以把它看作核心业务逻辑):
var func = function() {
console.log(2);
}复制代码
现在呢,想改造一下这个函数,把某些希望在把这个函数之前执行和之后执行的函数“动态织入”到核心函数中,像这样:
func = func.before(function() {
// 在 func 执行之前执行的函数
}).after(function() {
// 在 func 执行之后执行的函数
})复制代码
然后再执行
func();复制代码
时,能够依次执行 before()
传入的函数,func()
原来的内容,最后是 after()
传入的函数。怎么做到呢?
首先,函数能够链式执行下去意味着任何函数(或者其原型链上)都有对 before
和 after
的定义,因此可以扩充函数的构造函数(Function
) 的原型(prototype
)。然后为了能按照我们希望的顺序执行,需要调整 func()
本身的内容在执行过程中的顺序。我们知道在 JavaScript 中,除了直接执行一个函数外,还可以使用 call
或者 apply
执行,比如:
var foo = function(str) {
console.log(str);
}
foo("hello"); // hello
foo.call(this, "hello"); // hello
foo.apply(this, ["hello"]); // hello复制代码
所以呢,是不是可以这样子,为了在执行核心函数 func
之前动态织入 before
中的函数,可以先保存对 func
的引用,并在之后返回一个规定好执行顺序的装饰后的函数。像这样:
Function.prototype.before = function(beforefn) {
var _self = this; // 保存对原函数的引用
return function() {
beforefn.apply(this, arguments);
_self.apply(this, arguments);
}
}复制代码
注意这句 _self = this
的作用,因为我们调用 before
时会作为核心函数的方法调用,所以第一个 this
会是核心函数本身;而在返回的函数中,this
就无所谓了,它指向的是全局变量,在浏览器中的话就是 window
。
来验证一下:
func = func.before(function() {
console.log(1);
})
func(); // 1 2复制代码
Success √
所以 after
的写法也类似:
Function.prototype.after = function(afterfn) {
var _self = this;
return function() {
_self.apply(this, arguments);
afterfn.apply(this, arguments);
}
}复制代码
还是一样测试一下:
func = func.after(function() {
console.log(3);
})
func(); // 2 3复制代码
也可以链式调用起来:
func = func.before(function() {
console.log(1);
}).after(function() {
console.log(3);
})
func(); // 1 2 3复制代码
这种使用 AOP 的方式来给函数动态的添加职责,也是 JavaScript 语言中一种特别和巧妙的装饰者模式实现。
参考: