经过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.

相关文章
相关标签/搜索