《悟透JavaScript》之 甘露模型(2)
作者:李战 来源:软件真谛 时间:2008-06-09 14:03:00
类定义中名为Create的函数是特别对待的,因为这就是构造函数。如果没有定义Create构造函数,Class()函数也会创建一个默认构造函数。事实上,这个构造函数就代表了类。除此之外,我们还为其定义了一个Base属性,以方便追溯继承关系。
在本例中的根类object的原型中,我们定义了一个base方法。有了这个方法之后,在类定义的构造函数中,就可以使用“this.base()”这样的方式来调用基类的构造函数。这种调用基类的方式和C#的base及Java的super就非常相似了。
不过,Class()函数中还是有个小问题,那就是不支持toString()方法的覆写。也就是说,如果我们为一个类定义了自己的toString()方法,调用Class()函数来生成类时,toString()方法会丢失。
原来toString()方法被JavaScript规定为不可枚举的内置方法。除此之外还有还有toLocaleString(), valueOf(), hasOwnProperty(), isPrototypeOf(), propertyIsEnumerable()等,都是不能枚举的内置方法。这样在Class()函数中的那个for(…in…)语句就不能遍历到这些属性,导致问题的产生。
因此,这个Class()语法甘露还不能支持不可枚举内置方法的覆写。这不能不说是一个小小的遗憾。当然,遇到需要完全覆写这些内置方法的情况并不多。顶多偶尔会有toString(), toLocaleString(), valueOf()这三个方法的覆写,其他几个几乎不会有覆写的情况。
除此之外,object根类的base()方法也还有个小问题。这个方法用到了函数的caller属性,以此判断构造函数的层次。而Opera浏览器不支持函数的caller属性,因此base方法不适合于Opera浏览器,这不能不说是另一个更大的遗憾。
如果在甘露模型中留有这样的问题,想必观音姐姐也会遗憾。我们还需要继续努力,别让观音姐姐失望啊。
其实,要解决不能覆写非枚举属性的问题也并非难事。既然这些属性是特殊的,我们就可以对其进行特殊处理。我们可以在复制完可枚举的属性之后,加上类似下面的特殊判断处理语句:
if(aDefine.toString != Object.prototype.toString)
aPrototype.toString = aDefine.toString;
因为,如果覆写了toString方法,那么它就肯定不等于原生的toString方法,这时就可复制toString方法。当然,我们也可以不通过比较,而是用hasOwnProperty来判断是否覆写了toString方法:
if(aDefine.hasOwnProperty("toString"))
aPrototype.toString = aDefine.toString;
使用哪种判断方式可以任选。我们建议采用直接比较方式,这样可以避免万一遇到覆写Object.prototype.hasOwnProperty的情况,也不至于出问题。当然,这也似乎有点太钻牛角尖了。
在实际的应用中,我们建议根据具体应用情况来决定是否需要支持特殊属性的覆写。如果在应用中根本不会覆写这些特殊属性,就无需加上这样的特殊处理。如果是打造专业的AJAX类库,最多支持toString(), toLocaleString(), valueOf()这三个方法的覆写就可以了。千万不要玩画蛇添足的游戏。
最头痛的是对base()函数的重写,也就要兼容不支持caller属性的Opera浏览器。虽然Opera浏览器目前只占很小的市场范围,但也算有名的四大浏览器之列。如果甘露模型不支持Opera浏览器,显然无法彰显观音菩萨的法力,也更不好意思说自己是观音老师的弟子。