『設計模式』高階函數實現 AOP

一篇記錄了本身思考的讀書筆記。
博客:Murphy 的博客
知乎:@Murphygit

面向側面的程序設計(aspect-oriented programming,AOP,又譯做面向方面的程序設計、觀點導向編程、剖面導向程式設計)是計算機科學中的一個術語,指一種程序設計範型。該範型以一種稱爲側面(aspect,又譯做方面)的語言構造爲基礎,側面是一種新的模塊化機制,用來描述分散在對象、類或函數中的橫切關注點(crosscutting concern)。github

從編程的角度看,在運行時,動態地將代碼切入到類的指定方法、指定位置的編程思想就是面向切面編程編程

AOP 的主要做用是把一些跟核心業務邏輯模塊無關的功能抽離出來,這些跟業務邏輯無關的功能一般包括日誌統計、安全控制、異常處理等。這樣作的好處首先是能夠保持業務邏輯模塊的純淨和高內聚性,其次是能夠很方便地複用日誌統計等功能模塊。設計模式

從主關注點中分離出橫切關注點是面向側面的程序設計(aspect-oriented programming)的核心概念。分離關注點使得解決特定領域問題的代碼從業務邏輯中獨立出來,業務邏輯的代碼中再也不含有針對特定領域問題代碼的調用,業務邏輯同特定領域問題的關係經過側面來封裝、維護,這樣本來分散在在整個應用程序中的變更就能夠很好的管理起來。瀏覽器

在 Java 語言中,能夠經過反射和動態代理實現 AOP 技術,而在 JavaScript 中,有一種很是巧妙的方法實現。咱們先來看一下想要達到的效果:安全

原來的函數長這樣(能夠把它看做核心業務邏輯):app

var func = function() {
      console.log(2);
}複製代碼

如今呢,想改造一下這個函數,把某些但願在把這個函數以前執行和以後執行的函數「動態織入」到核心函數中,像這樣:模塊化

func = func.before(function() {
      // 在 func 執行以前執行的函數
}).after(function() {
      // 在 func 執行以後執行的函數
})複製代碼

而後再執行 函數

func();複製代碼

時,可以依次執行 before() 傳入的函數,func() 原來的內容,最後是 after() 傳入的函數。怎麼作到呢?測試

首先,函數可以鏈式執行下去意味着任何函數(或者其原型鏈上)都有對 beforeafter 的定義,所以能夠擴充函數的構造函數(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 語言中一種特別和巧妙的裝飾者模式實現。

參考:

JavaScript 設計模式與開發實踐

維基百科-面向側面的程序設計

相關文章
相關標籤/搜索