Ruby's Louvre

每天学习一点点算法

导航

javascript wrapper函数

在设计javascript的继承体系时,有一个重要需求,方法链。通俗地说,说是在方法中调用父类的同名方法。类似java的this.super().method()。如何把父类的同名方法包装到子类中呢?这就要用到wrapper函数。之所以叫wrapper,而不是wrap,因为它比wrap更加wrapper。比如像Ext那种深度继承的结构中,如果父类没有找祖父,祖父没有找曾祖父,沿着原型链层层上溯,以获取它所需要的功能。此外,wrapper函数在jQuery也有应用,好像分为三种wrapAll,wrapinner,wrap,专门用来对付IE的table或其他DOM。可能还有其他用处,不管了,先看如何实现它。

一个普通的函数

 
    var greeting = function(world){
      return "hello " + world +"!";
    };
    alert(greeting("world"));

我们把它塞进更深一层的作用域,非bind函数。

 
    var wrap= function(fn){
      return function(){
        return fn.apply(null,arguments);
      };
    };

但这只是延迟了它的执行时间而已。上面函数中的null,也可以换成window。

 
    var wrap= function(fn){
      return function(){
        return fn.apply(window,arguments);
      };
    };

因为总要人去调用函数的,null没有此能力,就由window上。现在我们就是要在这个位置上做文章,把换成this。如果没有进一步的改进,这里的this还是window的替身。下面就开始复杂了,先分解一下写法,降低阅读难度,就像jQuery那样把它掰成三部分:

 
    var wrapper= function(fn){//这里改一下名。
      var temp =  function(){
        return fn.apply(this,arguments);
      };
      return temp;
    };
 
    //fn为原函数
    //temp为改装了的函数
    //wrapper为包装工厂,只运作一次
    //wrap为改装了的函数的属性,它的参数和wrapper一样为函数,但能运行无数次,
    //把原改装了的函数内置为新增函数的内部函数。
    var wrapper= function(fn){
      var temp =  function(){
        return fn.apply(this,arguments);
      };
      temp.wrap = function(callback){
        var prev = fn;
        fn = function(){
          return callback.apply(prev,arguments);
        };
      };
      return temp;
    };

这样就可以如下神奇效果:

可以看出,this总是为原来的同名函数(p),也就是说,我们可以称之为父方法,这样也super关键字或者相关的代替品也不用了,轻松调用原来覆盖了父类的方法。因此jQuery同学单是用它来处理DOM真是大材小用。Prototype同学就做得不错了,不过代码写得比较艰涩:

 
  wrap: function(wrapper) {
    var __method = this;
    return function() {
      return wrapper.apply(this, [__method.bind(this)].concat($A(arguments)));
    }
  },

它的方法链设计:

 
    for (var i = 0, length = properties.length; i < length; i++) {
      var property = properties[i], value = source[property];
      if (ancestor && Object.isFunction(value) &&
          value.argumentNames().first() == "$super") {
        var method = value;
        value = (function(m) {
          return function() { return ancestor[m].apply(this, arguments) };
        })(property).wrap(method);

        value.valueOf = method.valueOf.bind(method);
        value.toString = method.toString.bind(method);
      }
      this.prototype[property] = value;
    }

相关文档:http://www.prototypejs.org/learn/class-inheritance

posted on 2009-11-14 16:15  司徒正美  阅读(7224)  评论(3编辑  收藏  举报