换种思维,简单解决

还是简单的问题:基“类”方法调用。下面的代码给出了一个十分极端的例子(使用了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有上面的全部代码)

你可能感兴趣的:(思维)