面向切面编程
- 面向切面编程在java中提出这类概念
- 但是在js没有束缚和约定,只需要按编程思想来实现原理
- 在js中使用function或class实现面向切面编程
面向切面概念
- AOP (Aspect Oriented Programming) 面向切面编程
- 主要实现目的是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或者阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果
- 类比
- 用刀切西瓜,西瓜红壤面就是切面,可以两驱切面的圆周和面积
- 无侵入式的干扰
- 只需要两篇,比如开头和结尾截取, 而非随意切,横切,竖切
- 处理业务某个中间的部分,提取和隔离,不对具体业务干扰
- 比如在业务中穿插了一些代码,比如网页性能检测,用js埋点
- 不在里面埋点,在整体业务上加东西,在函数之前或之后执行,如果哪天有热插拔的需求,把函数抽走,也不影响原来的业务
- 埋点函数和原来业务不会影响和混淆
代码实现
-
比如, 统计当前所有函数谁耗时最长,最性能优化
-
1 )侵入式演示: 每个函数前后加代码
function test() { console.time() alert(2) console.timeEnd() }
-
2 ) 在原型链上添加函数优化,先忽略这个 console.time/timeEnd 统计函数
-
2.1 只添加before函数到原型链上
function test() { alert(2) } Function.prototype.before = function (fn) { fn() // 执行前置任务 this.apply(this, arguments) // 执行自身 } // 演示 test.before(function() { alert(1) })
-
2.2 只添加after函数到原型链上
function test() { alert(2) } Function.prototype.after = function (fn) { // 先执行 this本身,再执行回调 this.apply(this, arguments) // 执行自身 fn() // 执行前置任务 } // 演示 test.after(function() { alert(3) })
-
2.3 将before和after函数添加到原型链上
function test() { alert(2) } Function.prototype.before = function (fn) { fn() // 执行前置任务 // return this.apply(this, arguments) // 执行自身 // 这里可以return 用于其他 this.apply(this, arguments) // 执行自身 } Function.prototype.after = function (fn) { // 先执行 this本身,再执行回调 this.apply(this, arguments) fn() // 执行后置任务 } // 演示 test.before(function() { alert(1) }) test.after(function() { alert(3) })
- 这时候默认函数被执行了2遍,需要优化
-
-
5 ) 继续优化重复执行的默认函数,将this只在before中执行
function test() { alert(2) } Function.prototype.before = function (fn) { fn() // 执行前置任务 // return this.apply(this, arguments) // 执行自身 // 这里可以return 用于其他 this.apply(this, arguments) // 执行自身 } Function.prototype.after = function (fn) { // 先执行 this本身,再执行回调 // this.apply(this, arguments) // 执行自身 fn() // 执行后置任务 } // 演示 test.before(function() { alert(1) }) test.after(function() { alert(3) })
- 以上是原型链中添加, 这完全没问题,但是写了两次
-
6 ) 支持链式调用版本优化
function test() { alert(2) } Function.prototype.before = function (fn) { var _self = this return function () { fn.apply(this, arguments) _self.apply(this, arguments) } } Function.prototype.after = function (fn) { var _self = this return function () { _self.apply(this, arguments) fn.apply(this, arguments) } }
- 基于以上代码,如果这样测试, 测试1:
// 演示 test.before(function() { alert(1) }).after(function() { alert(3) })()
- 它的输出顺序是
after before 1 2 before over 3 after over
- 它的输出顺序是
- 如果这样测试, 测试2
test.after(() => { alert(3) }).before(() => { alert(1) })()
- 它的输出顺序是
before 1 after 2 3 after over before over
- 它的输出顺序是
- 基于以上代码,如果这样测试, 测试1:
- 注意的是 this 指针的引用,使用function而非箭头函数
- 以上示例,不管先调用before还是after
- 都会先执行before中的fn
- 之后是默认函数
- 最后才是after的fn
- 输出顺序都是: 1 2 3
7 ) 链式调用,并支持异常断开
function test() {
alert(2)
// return false // 注意这里可以打开,尝试
return 'test'
}
Function.prototype.before = function (fn) {
var _self = this
return function () {
// 下面的 if 写法比较麻烦,但这样写算比较清晰吧
if (fn.apply(this, arguments) === false) {
return false
}
return _self.apply(this, arguments)
}
}
Function.prototype.after = function (fn) {
var _self = this
return function () {
var result = _self.apply(this, arguments)
if (result === false) return false
fn.apply(this, arguments)
return result // 这里注意
}
}
// 演示
test.before(function() {
alert(1)
}).after(function() {
alert(3)
})()
- 注意以上 === 判断中,关于 false 和 undefined区别
- undefined 意味着成功,只有主动 false 时,才可以
- 还可以换一种写法,比如 return true时,继续,其他都拒绝执行
- 每一层都可以 return , 可return成任意值,只有false才会阻断