Better

Ethan的博客,欢迎访问交流

JavaScript切面编程

最近碰到一个需求,有那么一组方法和一个检查机制,只有检查结果通过时才执行对应的代码,否则直接提示,不执行代码。最简单的办法当然是在每个方法执行前加if判断,但是那样的话代码冗余,而且代码看上去实在很愚。

初次尝试

本想使用IIFE遍历对象,针对特定方法添加判断,这样一来就不存在冗余问题。使用的是Angular的factory服务模块。

 return (function (instance) {
        for (var name in instance) {
          var method = instance[name];
          if (typeof method === 'function')) {
            instance[name] = function (name, method) {
              return function () {
                if (check()) {
                  return method.apply(this, arguments);
                } else {
                  // 提示不通过
                }
              }
            }(name, method);
          }
        }
        return instance;
      })({
        funA: funA,
        funB:funB
      });

我一开始看上去觉得没问题的样子,可是忘记考虑到Angular的service是单例模式,这样一来,如果在第一次注入服务的时候,初始化完成时检查就是不通过的或者是通过的,那么就一直是这种状态,有点像惰性函数了,这样一来就存在问题。

AOP

有没有可能通过AOP解决问题呢,毕竟两者的使用场景有点类似,不过AOP关注点在执行顺序上,为业务代码添加额外的功能,在业务之前或之后执行,将和业务无光的代码抽离出来。不管啦,看看JavaScript如何实现AOP呢?简单代码如下:

Function.prototype.before=function(beforefn){
    var __self=this;
    return function(){
        beforefn.apply(this,arguments);
        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;
    }
}

看了这段代码真是恍然大悟,之前一直考虑如何得到方法对象,却没有想到方法也是对象,所有方法都是Function的实例,那么我们可以在Function原型链上扩充方法,这样一来我们声明的方法就会具备这个方法,调用的时候便可以通过this得到方法。有没有很酷!

AOP修改

了解了AOP的原理之后,我们稍加修改便可以达到自己的需求,需要注意的时,我们的检查机制在每次执行都需要检查,而不是只检查一次,因此如下的代码会有问题,虽然通用性强!

 Function.prototype.before = function(check) {
        var __self = this;
        return function() {
          if(check.apply(this,arguments)){
            return __self.apply(this, arguments);
          }else{
            // 提示不通过
          }
      }
}
 return {
        funA: funA,
        funB: funA.before(check),
}

怎么办,那就只能和业务结合起来了。代码如下:

 Function.prototype.before = function() {
        var __self = this;
        return function() {
          if(check()){
            return __self.apply(this, arguments);
          }else{
            // 提示不通过
          }
      }
}
 return {
        funA: funA,
        funB: funA.before(),
}

可能存在的问题

  • 直接在Function原型链上扩展,后期会不会被覆盖呢?
  • 使用apply执行方法和()执行的区别?
    • 显示绑定上下文
    • arguments是一个ArrayLike,直接传递给方法执行表达式会报错


留言