面向切面編程
- 面向切面編程在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 = thisreturn function () {fn.apply(this, arguments)_self.apply(this, arguments)} }Function.prototype.after = function (fn) {var _self = thisreturn 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 = thisreturn function () {if (fn.apply(this, arguments) === false) {return false}return _self.apply(this, arguments)}
}Function.prototype.after = function (fn) {var _self = thisreturn function () {var result = _self.apply(this, arguments)if (result === false) return falsefn.apply(this, arguments)return result // 這里注意}
}// 演示
test.before(function() {alert(1)
}).after(function() {alert(3)
})()
- 注意以上 === 判斷中,關于 false 和 undefined區別
- undefined 意味著成功,只有主動 false 時,才可以
- 還可以換一種寫法,比如 return true時,繼續,其他都拒絕執行
- 每一層都可以 return , 可return成任意值,只有false才會阻斷