前端解讀面向切面編程(AOP)

前言

面向對象(OOP)做爲經典的設計範式,對於咱們來講可謂無人不知,還記得咱們入行起始時那句經典的總結嗎-萬事萬物皆對象
是的,基於OOP思想封裝、繼承、多態的特色,咱們會天然而然的遵循模塊化、組件化的思惟來設計開發應用,以到達易維護、可擴展、高複用的目的。
既然OOP這麼多優勢,那麼常常被你們提起的面向切面編程(AOP)是什麼回事呢,下面咱們就一塊兒來看一下。javascript

AOP定義

第一步仍是要知道aop是什麼,先個來自維基百科的解釋:java

面向側面的程序設計(aspect-oriented programming,AOP,又譯做面向方面的程序設計、觀點導向編程、剖面導向程序設計)是計算機科學中的一個術語,指一種程序設計範型。
側面的概念源於對面向對象的程序設計的改進,但並不僅限於此,它還能夠用來改進傳統的函數。
react

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

tip

確實有點那麼不太清晰,有點亂。不過在亂以前,咱們能夠選能理解的部分先看一下:github

  • 側面(也就是切面) 用來描述分散在對象、類或函數中的橫切關注點
    重點在這,分散在對象中的橫切關注點,能夠猜一下是什麼,應該就是不一樣對象之間公用的部分
  • 側面的概念源於對面向對象的程序設計的改進,它還能夠用來改進傳統的函數. AOP 顯然不是OOP的替代品,是OOP的一種補充。
  • 從主關注點中分離出橫切關注點是面向側面的程序設計的核心概念。
    具體到業務項目中來講,主關注點就是業務邏輯了。針對特定領域問題代碼的調用,就是AOP要關注的部分

簡而言之,AOP是針對業務處理過程當中的切面(即非業務邏輯部分,例如錯誤處理,埋點,日誌等)進行提取.
它所面對的是處理過程當中的某個步驟或階段,以得到邏輯過程當中各部分之間低耦合性的隔離效果(目的是下降耦合)。
具體到實現來講就是經過動態的方式將非主關注點部分插入到主關注點(通常是業務邏輯中)編程

說了這麼多,可能不太明白,仍是一塊兒看代碼吧。app

埋點場景

很廣泛的這麼個場景,須要點擊按鈕以後進行信息上報。 假設咱們有這麼個logger的工具,能夠進行上報:異步

const logger = console.log
//引入便可使用
logger('按鈕被點擊了')
複製代碼

那麼,咱們直接擼起來吧:模塊化

const doSomething = ()=>{
    console.log('doSomething')
} 
let clickHandler = ()=>{
   logger('doSomething以前')
   // n行代碼 
   doSomething() 
   logger('doSomething以後')
   //n 行代碼
}
複製代碼

看起來也沒什麼的,簡單粗暴。
若是有30個按鈕,每一個業務邏輯不一樣,都須要埋這個點(假設打點信息一致)。
咱們30個函數裏面,都要手動寫這個方法的話,這也太坑爹了吧。
主要是與業務代碼嚴重耦合,哪天不當心動了點其餘內容,手抖誤刪了,就gg了。
後續維護的時候,簡直噩夢。
仔細看一下,這不就是符合AOP的使用前提嗎,那麼試試AOP吧。函數

關注點劃分

根據前面提的,能夠劃分下關注點。

主關注點 側關注點
業務邏輯(doSomething) 埋點信息 logger

前面提到AOP關注的是步驟具體到例子來講其實就是插入logger的步驟。
插入時機無非時業務邏輯執行以前或者以後的階段。
具體實現起來也不那麼困難

實現思路

具體到js來講,因爲語言自己的特性,天生就具備運行時動態插入邏輯的能力。
重點在於在原函數上增長其餘功能並不改變函數自己。

畢竟函數能夠接受一切形式的參數,固然函數也不例外了。
當傳入一個函數的時候,咱們要對其操做的餘地就很大了,
保存原函數,而後利用後續參數加上call或apply,就能夠達到咱們的目的。
此外爲了給函數都增長一個屬性,咱們在原型上操做就好了。

經典before或者after的實現

網上太多相似實現了,直接看代碼好了:

// action 即爲咱們的側關注點,即logger
Function.prototype.after = function (action) {
    //保留當前函數,這裏this指向運行函數即clickHandler
    var func = this;
    // return 被包裝過的函數,這裏就能夠執行其餘功能了。
    // 而且該方法掛在Function.prototype上,
    // 被返回的函數依然具備after屬性,能夠鏈式調用
    return function () {
        // 原函數執行,這裏不考慮異步
        var result = func.apply(this, arguments);
        // 執行以後的操做
        action.apply(this,arguments);
        // 將執行結果返回
        return result;
    };
};
// before 實現相似,只不過執行順序差異而已
Function.prototype.before = function (action) {
    var func = this;
    return function () {
        action.apply(this,arguments);
        return func.apply(this, arguments);
    };
};
複製代碼

那麼咱們使用AOP改造以後的代碼就以下了:

const doSomething = ()=>{
    console.log('doSomething')
} 
let clickHandler = ()=>{
   // n行代碼 
   doSomething() 
   //n 行代碼
}
clickHandler = clickHandler.before(()=>{
     logger('doSomething以前')
}).after(()=>{
     logger('doSomething以後')
})
clickHandler() // 執行結果和預期一致
複製代碼

到這裏就實現了面向切面編程,咱們的業務邏輯裏面只管業務自己,側關注點經過這種方式來動態引入,與主邏輯解耦,更加純淨、易於維護。

結束語

到這裏,簡單的AOP就介紹完成了。利用這種模式結合咱們js自己的特性,能夠嘗試更多的可能。 例如咱們react中常見的HOC、es7的裝飾者模式、HOF等,不少時候不得不感嘆大牛們思想的精髓,會讓咱們有種頓悟的感受。本文拋磚引玉,共同窗習啦,對本身是總結和提升,更但願能幫助到須要的小夥伴。更多文章請移步個人博客

參考文章

AllyTeam - 用AOP改善javascript代碼
深刻淺出 Javascript Decorators 和 AOP 編程

相關文章
相關標籤/搜索