經過AOP解耦Javascript中的緊耦合代碼

    AOP你們都知道,Javascript對於AOP的使用也有一些很好的地方.這幾天正好在改別人代碼他在javascript用了AOP進行編程,正好仔細說說怎麼玩的這個.javascript

 AOP

    單獨AOP的概念最先接觸是在Java中,面向切片編程SSH三大框架中的spring對它有深刻的使用(固然,我根本沒來得及深呢就投入偉大的前端事業了).
css

    AOP如何面向切片編程的呢?
前端

    舉一個很典型的例子,其實AOP有不少應用,這裏感受最典型的,在javascript中的就是check或者log,在操做以前的check以及操做以後的log.
java

    好比有一個操做,是要發送一個請求保存一些數據:jquery

// 點擊按鈕保存
function btnClick(obj){
  // 數據進行必定的初始化
  var saveObject = dateFormat(obj);
  // 在數據進行保存以前,先check一下是否合法
  beforeSaveCheck(saveObject);
  // 真正的保存操做
  save(saveObject);
  // 保存以後存儲一下日誌
  saveLog(saveObject);
}

    能夠看到,在真正的保存操做以前和以後會有一個check和log的過程,而極可能的是在每個相似操做的函數中,都須要這兩個步驟.若是每個對應的操做都加上這兩個函數,你認爲科學嗎?對於代碼的維護以及將來的擴展都會帶來很大程度上的不方便.git

    AOP在javascript中就能夠很好地解決相似的問題,用一種更加有好的方式.讓咱們來暢想一下這種更好的方式,它會將很是零散雜亂,可是公共的部分組合在一塊兒,而且實現高複用,低耦合.若是說面向對象的思想是在程度的程度設計上進行操刀,那麼面向切片就是對方法的進一步升級和改造.github

    那麼爲AOP作一個淺顯的總結就是在不修改原代碼的狀況下,動態添加功能的一種技術.
spring

  若是不使用AOP呢?

    上面爲AOP說了不少好話,可是光說好可能不會讓你們瞭解它的好.咱們再來看若是不使用它,通常狀況下怎麼處理一些問題.好比jquery的$.css()方法,能夠設置樣式.如今咱們但願定義幾個特殊的方法,好比:
編程

$('#id').css('font-size','big');
$('#id').css('font-size','normal');
$('#id').css('font-size','small');
// 不須要再輸入具體的字體大小值,big表明18px,normal表明14px,small表明8px

    若是使用粗暴的手法,能夠直接讓原有代碼支持咱們新的功能:
app

(function($){
 
    // 把原方法暫存起來:
    var _oldcss = $.fn.css;
 
    // 重寫原方法:
    $.fn.css = function(prop,value){
        if (/^font-?size$/i.test(prop) && value.toLowerCase() === 'big') {
           return _oldcss.call(this,prop,'18px');
        } else {
           return _oldcss.apply(this,arguments);
        }
    };
})(jQuery);

    上面的代碼雖然實現了,不過徹底讓人hold不住.爲何?第一修改了原來的代碼,尤爲是這種fix庫的代碼很不爽,尤爲若是你代碼潔癖比較重.另外也違反了'開放-封閉'的原則,沒有對擴展開發,對修改封閉.

    話歸上題,仍是來看看用AOP如何實現check和log的.

  AOP的基本實現

    來看一個簡單地AOP實現:

   var AOP = {
    around: function (pointcut,advice,namespaces) {
      if (namespaces === undefined || namespaces.length ===0 ) {
        namespaces = [(function(){return this;}).call()];
      }
      for (var i in namespaces) {
       var ns = namespaces[i];
       for (var member in ns) {
        if (typeof ns[member] === 'function' && member.match(pointcut)) {
          (function(fn,fnName,ns){
            ns[fnName] = function(){
              return advice.call(ns,{fn:fn,fnName:fnName,arguments:arguments});
            }
          })(ns[member],member,ns)
        }
       };
      };
    },
    next: function(f){
      return f.fn.apply(this,f.arguments);
    }
   }

   function hello(name){
    console.log('aop hello ' + name);
   }

   hello('world');

   AOP.around('hello.*',function(f){
    console.log('before hello world');
    AOP.next(f);
    console.log('after hello world');
   });

   hello('world2');

    輸出:

aop hello world 
before hello world
aop hello world2
after hello world

    這是around的實現,基本的還有before和after的實現:

Aop.before = function(pointcut, advice, namespaces) {
  Aop.around(pointcut,
             function(f) {
               advice.apply(this, f.arguments);
               return Aop.next(f)
             },
             namespaces);
};

Aop.after = function(pointcut, advice, namespaces) {
  Aop.around(pointcut,
             function(f) {
               var ret = Aop.next(f);
               advice.apply(this, f.arguments);
               return ret;
             },
             namespaces);
};

    更好的代碼能夠看Github上meld這個庫,很方便的就可使用AOP:https://github.com/cujojs/meld/blob/master/meld.js.

相關文章
相關標籤/搜索