换种思维,简单解决
2010-01-10 13:42
134 查看
还是简单的问题:基“类”方法调用。下面的代码给出了一个十分极端的例子(使用了megamijs):递归的树状调用基“类”不同名方法,下面的是标准输出。
(说明:trace相当于C# Console.WriteLine,derive派生子构造器,method定义方法,__mgBase调用基“类”方法,指定名字。)
传统的方法是构造“基类链”,然后沿着积累链一级一级向上找——Qomo就是这么做的,而且效果很好。但是这样的缺点也很明显,每一次调用都要查找链,效率是一个大问题。
我们注意到每次__mgBase调用的时候,传入的this都是同一个,所以搜索到的__mgBase就也是同一个,于是就出现了“搜链”的状况——假如每次搜索基类时修改一下__mgBase,让它不一样呢?
想法很疯狂!那么就试试吧!
这个神奇的函数干的事情就是生成__mgBase供复写,它传入两个参数——搜索方法的“基类”和基类方法调用之后恢复的原有__mgBase。当__mgBase被调用的时候,它先是搜索基类对应的方法,然后把this的__mgBase复写,最后调用基类方法。等调用完后再把__mgBase改回来,然后return。try-finally的作用是无论基类方法调用的时候产生怎样的错误,都会保证把__mgBase改回来。(就像C#中经常出现的一样,不是吗?)Function的baseConstructor是一个由megamijs维护的属性,它代表基类(例子中,B的baseConstructor === A)。
但是,最早的__mgBase总得有吧?呵呵,先看一个工具方法(不做解释):
接着是第二段好戏——当当当~~:
这是Function.prototype.inherits里面的代码,第3-6行检测调用__mgBase的函数到底是对象“自己拥有”(hasOwnProperty)的方法还是原型上的。第六行之后可以保证c的原型上一定没有arguments.callee.caller。然后就是传统的复写,调用(注意这里和上面mgBaseGen生成的函数间的区别)。
总的来说,这种方案几乎可以完美的实现调用基类方法,但它的效率仍然不是很高(主要在第一步查原型使用了反射)。因此,我还是建议,珍惜生命,远离base。
(注:下源码可以svn这里:
(说明: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有上面的全部代码)
相关文章推荐
- 心得: 把具体的事物概念话, 再通过概念划的思维解决事物, 得出结果化的时候再进行具体化, 看似简单实则不易啊
- Arcgis for Android解决定位结果与地图偏移的简单处理思维
- Arcgis for Android解决定位结果与地图偏移的简单处理思维
- 解决gedit中文乱码问题,采用命令行输入相关命令,简单实用的解决方法
- 为神马精确Sprite的碰撞形状不通过简单的放大Sprite的尺寸来解决?
- 代码首要的目标应该是“解决问题”(包括“没有 bug”),其次的目标才是“简单优雅”。
- unity。。。完美简单解决靠鼠标在屏幕上的移动来控制相机水平,垂直旋转的c#脚本
- 织梦Dedecms5.7整站调用购物车订单数量简单解决办法
- 【swift3.0】【解决手势冲突问题】【简单的一个方法】
- fuse程序挂载时nonempty问题的简单理解和解决
- 【狼人杀plus全记录】没有公网IP照样完美解决微信小程序本地测试问题,超简单方法!
- 简单记录一个spring mvc 400的错误的解决
- 简单内存泄漏检测方法,解决Detected memory leaks!问题
- 为WebApi项目添加注册功能:问题与解决的思维流程
- 解决IE6、IE7、Firefox兼容最简单的CSS Hack
- 1248 寒冰王座 换个思维来解决这个背包题目
- Android Studio run app 无法自启动的简单解决办法
- ubuntu中txt中文文本乱码最简单解决方法
- 最简单的代码解决IE6与其它浏览的兼容性
- ECSHOP忘记管理员密码怎么办?简单快速恢复密码方法 解决方法