在设计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"));
var greeting = function(world){ return "hello " + world +"!"; }; alert(greeting("world"));
运行代码
我们把它塞进更深一层的作用域,非bind函数。
var wrap= function(fn){
return function(){
return fn.apply(null,arguments);
};
};
var wrap= function(fn){ return function(){ return fn.apply(null,arguments); }; }; var greeting = function(world){ return "hello " + world +"!"; }; alert(greeting("world")); alert(wrap(greeting)("world"))
运行代码
但这只是延迟了它的执行时间而已。上面函数中的null,也可以换成window。
var wrap= function(fn){
return function(){
return fn.apply(window,arguments);
};
};
var wrap= function(fn){ return function(){ return fn.apply(window,arguments); }; }; var greeting = function(world){ return "hello " + world +"!"; }; alert(greeting("world")); alert(wrap(greeting)("world"))
运行代码
因为总要人去调用函数的,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;
};
这样就可以如下神奇效果:
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; }; var html = function(str){ //原函数数 return ""+str+"
"; } alert(html("段落")); var p = wrapper(html);//第一次包装 alert(p("段落")) p.wrap(function(str){//第二次包装 return "运行代码
可以看出,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;
}