Thinking--AOP思想在前端中的应用
原创
©著作权归作者所有:来自51CTO博客作者此时奋飛的原创作品,请联系作者获取转载授权,否则将追究法律责任
Thinking系列,旨在利用10分钟的时间传达一种可落地的编程思想。
AOP
AOP(Aspect Oriented Programming),面向切面编程。其从主关注点中分离出横切关注点是面向侧面的程序设计的核心概念。分离关注点使得解决特定领域问题的代码从业务逻辑中独立出来。
具体到 Javascript 来说,由于语言本身的特性,天生就具有运行时动态插入逻辑的能力。重点在于在原函数上增加其他功能并不改变函数本身。
Spring中的Advice:前置通知(Before Advice)、后置通知(After Advice)、返回通知(After Return Advice)、环绕通知(Around Advice)、抛出异常后通知(After Throwing Advice)
javascript实现
Function.prototype.after = function (action) {
// 保留当前函数,这里this指向运行函数
var func = this
// return 被包装过的函数,这里就可以执行其他功能了
// 并且该方法挂在 Function.prototype 上,
// 被返回的函数依然具有 after 属性,可以链式调用
return function () {
// 原函数执行,这里不考虑异步
var result = func.apply(this, arguments)
// 执行之后的操作
action.apply(this, arguments)
// 将执行结果返回
return result
}
}
Function.prototype.before = function (action) {
var func = this
return function () {
action.apply(this, arguments)
return func.apply(this, arguments)
}
}
let beforeFn = function () {console.log('beforeFn')}
let afterFn = function () {console.log('afterFn')}
let fn = function () {console.log('fn')}
fn.before(beforeFn).after(afterFn)() // beforeFn fn afterFn
问题陈述
两个模块(A、B),由于两个模块相似度很高,且其中一个模块(A)是历史模块(已开发完成);所以我们采用 extends 方式,来扩展开发新的模块(B)。
历史 A 模块
<el-button @click="submit">查询</el-button>
<script>
export default {
name: 'A',
methods: {
submit () {
/* 主逻辑业务 */
}
}
}
</script>
现在,新模块(B)对于查询按钮的处理,需要发生变更,处理业务的同时需要发送相关日志。
对 A 模块进行改造,已适应新的方式
<el-button @click="submit">查询</el-button>
<script>
export default {
name: 'A',
methods: {
/* 将原有的逻辑业务代码进行抽离,便于新模块改造和沿用 */
submit () {
this.submitReq()
},
submitReq () {
/* 主逻辑业务 */
}
}
}
新模块(B),覆盖默认submit
方法,进行日志处理,下面采用常规的静态代理模式
export default {
name: 'B',
extends: 'A',
methods: {
/* 覆盖原有submit方法,增加日志采集处理 */
submit () {
// 这个来自于 A 模块
this.submitReq()
this.recordLogReg()
},
recordLogReg () {}
}
}
解决方案
export default {
name: 'B',
extends: 'A',
methods: {
/* 覆盖原有submit方法,增加日志采集处理 */
submit: this.submitReq.after(this.recordLogReg)(),
recordLogReg () {
/* 采集日志相关 */
}
}
}
每个函数变成了独立单元,可以随意组合,无需互相关联!
如果采用 class 写法,类的装饰器 decorator 也可以达到类似效果。