您的位置:首页 > 其它

换种思维,简单解决

2010-01-10 13:42 134 查看
还是简单的问题:基“类”方法调用。下面的代码给出了一个十分极端的例子(使用了megamijs):递归的树状调用基“类”不同名方法,下面的是标准输出。

(说明:trace相当于C# Console.WriteLine,derive派生子构造器,method定义方法,__mgBase调用基“类”方法,指定名字。)

var A = Object.derive().method('g', function() {
trace('a#g')
});
var B = A.derive().method('h', function() {
trace(' >> in b#h');
this.__mgBase('g');
this.__mgBase('g');
trace(' end b#h')
});
var C = B.derive().method('f', function() {
trace(' >> in c#f');
this.__mgBase('h');
this.__mgBase('g');
trace(' end c#f')
});

var x = new C;
x.g = function() {
trace(' >> in own');
this.__mgBase('f');
this.__mgBase('h');
trace('own')
}
x.g();


>> in own
>> in c#f
>> in b#h
a#g
a#g
end b#h
a#g
end c#f
>> in b#h
a#g
a#g
end b#h
own


传统的方法是构造“基类链”,然后沿着积累链一级一级向上找——Qomo就是这么做的,而且效果很好。但是这样的缺点也很明显,每一次调用都要查找链,效率是一个大问题。

我们注意到每次__mgBase调用的时候,传入的this都是同一个,所以搜索到的__mgBase就也是同一个,于是就出现了“搜链”的状况——假如每次搜索基类时修改一下__mgBase,让它不一样呢?

想法很疯狂!那么就试试吧!

var mgBaseGen = function(bcl, original) {
return function(nname) {
var c = bcl,
rv;
while (!c.prototype.hasOwnProperty(nname)) c = c.baseConstructor;
this.__mgBase = mgBaseGen(c.baseConstructor, this.__mgBase);
try {
rv = c.prototype[nname].apply(this, Array.prototype.slice.call(arguments, 1));
} finally {
this.__mgBase = original;
};
return rv;
};
};


这个神奇的函数干的事情就是生成__mgBase供复写,它传入两个参数——搜索方法的“基类”和基类方法调用之后恢复的原有__mgBase。当__mgBase被调用的时候,它先是搜索基类对应的方法,然后把this的__mgBase复写,最后调用基类方法。等调用完后再把__mgBase改回来,然后return。try-finally的作用是无论基类方法调用的时候产生怎样的错误,都会保证把__mgBase改回来。(就像C#中经常出现的一样,不是吗?)Function的baseConstructor是一个由megamijs维护的属性,它代表基类(例子中,B的baseConstructor === A)。

但是,最早的__mgBase总得有吧?呵呵,先看一个工具方法(不做解释):

var containsOwnValue = function(ob, v) {
for (var each in ob) if (ob.hasOwnProperty(each) && ob[each] === v) return true;
return false;
}


接着是第二段好戏——当当当~~:

clz.prototype.__mgBase = function(name) {
var c = clz, rv, cler = arguments.callee.caller;
if (!containsOwnValue(this, cler)) {
while (!containsOwnValue(c.prototype, cler)) c = c.baseConstructor;
c = c.baseConstructor;
};
var original = this.__mgBase;
this.__mgBase = mgBaseGen(c, original);
try {
rv = this.__mgBase.apply(this, arguments);
} finally {
this.__mgBase = original;
};
return rv;
};


这是Function.prototype.inherits里面的代码,第3-6行检测调用__mgBase的函数到底是对象“自己拥有”(hasOwnProperty)的方法还是原型上的。第六行之后可以保证c的原型上一定没有arguments.callee.caller。然后就是传统的复写,调用(注意这里和上面mgBaseGen生成的函数间的区别)。

总的来说,这种方案几乎可以完美的实现调用基类方法,但它的效率仍然不是很高(主要在第一步查原型使用了反射)。因此,我还是建议,珍惜生命,远离base。

(注:下源码可以svn这里:
svn co https://megamijs.svn.sourceforge.net/svnroot/megamijs megamijs
,主文件megami.js有上面的全部代码)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐