javascript aop

摘要 使用AOP改善代码中紧耦合的部分,更好的解耦逻辑.

    AOP大家都知道,Javascript对于AOP的使用也有一些很好的地方.这几天正好在改别人代码他在javascript用了AOP进行编程,正好仔细说说怎么玩的这个.

 AOP

    单独AOP的概念最早接触是在Java中,面向切片编程SSH三大框架中的spring对它有深入的使用(当然,我根本没来得及深呢就投入伟大的前端事业了).

    AOP如何面向切片编程的呢?

    举一个很典型的例子,其实AOP有很多应用,这里感觉最典型的,在javascript中的就是check或者log,在操作之前的check以及操作之后的log.

    比如有一个操作,是要发送一个请求保存一些数据:

?
1
2
3
4
5
6
7
8
9
10
11
// 点击按钮保存
function  btnClick(obj){
   // 数据进行一定的初始化
   var  saveObject = dateFormat(obj);
   // 在数据进行保存之前,先check一下是否合法
   beforeSaveCheck(saveObject);
   // 真正的保存操作
   save(saveObject);
   // 保存之后存储一下日志
   saveLog(saveObject);
}

    可以看到,在真正的保存操作之前和之后会有一个check和log的过程,而很可能的是在每一个类似操作的函数中,都需要这两个步骤.如果每一个对应的操作都加上这两个函数,你认为科学吗?对于代码的维护以及未来的扩展都会带来很大程度上的不方便.

    AOP在javascript中就可以很好地解决类似的问题,用一种更加有好的方式.让我们来畅想一下这种更好的方式,它会将非常零散杂乱,但是公共的部分组合在一起,并且实现高复用,低耦合.如果说面向对象的思想是在程度的程度设计上进行操刀,那么面向切片就是对方法的进一步升级和改造.

    那么为AOP做一个浅显的总结就是在不修改原代码的情况下,动态添加功能的一种技术.

  如果不使用AOP呢?

    上面为AOP说了很多好话,但是光说好可能不会让大家了解它的好.我们再来看如果不使用它,一般情况下怎么处理一些问题.比如jquery的$.css()方法,可以设置样式.现在我们希望定义几个特殊的方法,比如:

?
1
2
3
4
$( '#id' ).css( 'font-size' , 'big' );
$( '#id' ).css( 'font-size' , 'normal' );
$( '#id' ).css( 'font-size' , 'small' );
// 不需要再输入具体的字体大小值,big代表18px,normal代表14px,small代表8px

    如果使用粗暴的手法,可以直接让原有代码支持我们新的功能:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
( 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实现:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
    var  AOP = {
     around:  function  (pointcut,advice,namespaces) {
       if  (namespaces === undefined || namespaces.length ===0 ) {
         namespaces = [( function (){ return  this ;}).call()];
       }
       for  ( var  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' );

    输出:

?
1
2
3
4
aop hello world 
before hello world
aop hello world2
after hello world

    这是around的实现,基本的还有before和after的实现:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值