一篇記錄了自己思考的讀書筆記。
部落格: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 語言中一種特別和巧妙的裝飾者模式實現。
參考: