什么是AOP(面向切面编程)
通过预编译方式和运行其动态代理实现在不修改源代码的情况下给程序动态统一添加某种特定功能的一种技术。AOP实际是GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,提高代码的灵活性和可扩展性,AOP可以说也是这种目标的一种实现。
其核心概念是从主关注点中分离出横切关注点的面向侧面的设计理念,将业务逻辑中将特定的代码独立出来, 业务代码中不在含有特定的代码,业务逻辑和特定代码的关系通过侧面来封装,维护。
主要几个点:
- 侧面(切面)用来描述分散在类,函数或对象的横切关注点,比如不动对象的公用部分
- 侧面(切面)概念来源于对OOP(面向对象编程)的改进,还可以改进传统的函数,AOP不是OOP的替代而是对其的一个补充
- 从主关注点中分离出横切关注点是面向侧面的程序设计的核心概念
主要场景
日志记录,性能统计,安全控制,事务处理,异常处理等等。举一个例子: 例如埋点场景:
const logger = console.log
//引入即可使用
logger('按钮被点击了')
function doSomething(){
// 业务逻辑
}
const hadnlerCLICK = () => {
logger(打点逻辑");
doSomething()
logger("打点逻辑")
}
如果有30个打点按钮,业务逻辑都不一样,打点信息都一样,那我们就需要写30个这样的函数,并且与业务耦合,后期维护也很麻烦。
关注点划分
主关注点 | 侧关注点 |
---|---|
业务逻辑(doSomething) | 埋点信息(logger) |
实现思路
具体到js来说,由于语言本身的特性,天生就具有运行时动态插入逻辑的能力。 重点在于在原函数上增加其他功能并不改变函数本身。
毕竟函数可以接受一切形式的参数,当然函数也不例外了。 当传入一个函数的时候,我们要对其操作的余地就很大了, 保存原函数,然后利用后续参数加上call或apply,就可以达到我们的目的。 此外为了给函数都增加一个属性,我们在原型上操作就行了。
Function.prototype.befored = function (fn) {
let that = this;
return function () {
fn.apply(this, arguments);
that.apply(this, arguments);
}
}
Function.prototype.after = function (fn) {
let that = this;
return function () {
that.apply(this, arguments);
fn.apply(this, arguments);
}
}
const doSomething = ()=>{
console.log('doSomething')
}
let clickHandler = ()=>{
// n行代码
doSomething()
//n 行代码
}
clickHandler = clickHandler.before(()=>{
logger('doSomething之前')
}).after(()=>{
logger('doSomething之后')
})
clickHandler() // 执行结果和预期一致
总结
AOP结合js的特性,可以尝试多种可能,结合业务,选择合适的编程方式,例如我们react中常见的HOC、es7的装饰者模式、HOF等