什麼是AOP?編程
AOP(面向切面編程)的主要做用是把一些跟核心業務邏輯模塊無關的功能抽離出來,這些跟業務邏輯無關的功能一般包括日誌統計、安全控制、異常處理等。把這些功能抽離出來以後, 再經過「動態織入」的方式摻入業務邏輯模塊中。安全
AOP能給咱們帶來什麼好處?app
AOP的好處首先是能夠保持業務邏輯模塊的純淨和高內聚性,其次是能夠很方便地複用日誌統計等功能模塊。 函數
JavaScript實現AOP的思路?this
一般,在 JavaScript 中實現 AOP,都是指把一個函數「動態織入」到另一個函數之中,具體的實現技術有不少,下面我用擴展 Function.prototype 來作到這一點。請看下面代碼:spa
Function.prototype.before = function (beforefn) { var _self = this; //保存原函數引用 return function () { //返回包含了原函數和新函數的"代理函數" beforefn.apply(this, arguments); //執行新函數,修正this return _self.apply(this, arguments); //執行原函數 } }; Function.prototype.after = function (afterfn) { var _self = this; return function () { var ret = _self.apply(this, arguments); afterfn.apply(this, arguments); return ret; } }; var func = function () { console.log("2") } func = func.before(function () { console.log("1"); }).after(function () { console.log("3"); } ) func();
執行結果以下:prototype
我把負責打印數字1和打印數字3的兩個函數經過AOP的方式動態植入func函數。經過執行上面的代碼,咱們看到控制檯順利地返回了執行結果一、二、3。代理
這種使用AOP的方式來給函數添加職責,也是JavaScript語言中的一種很是特別的巧妙的裝飾者模式實現,下面咱們來試試Function.prototype.before的威力,請看下面代碼:日誌
Function.prototype.before = function (beforefn) { var __self = this; // 保存原函數的引用 return function () { // 返回包含了原函數和新函數的"代理"函數 beforefn.apply(this, arguments); // 執行新函數,且保證 this 不被劫持,新函數接受的參數 // 也會被原封不動地傳入原函數,新函數在原函數以前執行 return __self.apply(this, arguments); // 執行原函數並返回原函數的執行結果, 2 // 而且保證 this 不被劫持 } } Function.prototype.after = function (afterfn) { var __self = this; return function () { var ret = __self.apply(this, arguments); afterfn.apply(this, arguments); return ret; } }; document.getElementById = document.getElementById.before(function(){ alert (1); }); var button = document.getElementById( 'button' );
執行結果:code
咱們給document.getElementById()作了一些裝飾,之後咱們每次調用這個方法以前都會先執行alert("1")這條語句,可是請注意咱們這條語句並非寫在了document.getElementById()這個方法的源碼中,而只是在他的外部給他加了裝飾,這樣帶來好處就是咱們能夠在不改變原方法的源碼的狀況下爲他添加一些新的行爲。國際慣例,舉個栗子:
個人同事寫了一個函數能夠輸出當前時間,而我如今的需求是輸出當前天氣以後再輸出當前時間,下面有兩種解決思路:
(1)傳統解決辦法: 拿同事的函數過來,找到他輸出時間的代碼,在這些代碼以前加入輸出當前天氣的代碼
(2)裝飾者模式解決辦法:拿同事的函數過來,不用看他的源碼,直接給他的函數裝飾一下,裝飾的東西也就是輸出當前天氣的代碼。
兩種方法都解決了問題,可是他們的出發點是徹底不一樣的:
(1)方法是改造原函數的內部,咱們就須要去理解源代碼,而後作修改。
(2)方法是給原函數添加了一層外套,咱們根本不用管本來函數的內部實現。
如今又有了新的需求:在輸出當前時間以前,先輸出當前溫度
(1)方法,咱們在第一個需求已經把同事的代碼改的面目全非了,如今又要從新理解函數內部,並加以修改(刪除輸出當前天氣的代碼,而後加入輸出當前溫度的代碼)。
(2)方法,同事本來的函數是沒有變的,咱們如今給同事的函數換一件套(輸出當前溫度)就能夠了。