发现一个问题,在FireFox下,如下代码在prototype加载的前提下能运行:
document.getElementById("div1").addClassName("loading").show();
我就奇怪了,这个中间不用Element.extend一下也会有那些扩展的方法,这是怎么回事。关于这个问题,中文资料很少,我查了很久,找到两篇文章,提到这个问题:
根据这两篇文章,我得到一些结论:
一、一切对象都有原型,包括window、document、event等对象。
二、FF下,我们能扩展window、document等对象的原型,可以扩展div元素的原型,等等,但是在IE下不能。所以,直到现在,我才明白,为什么Element.extend第一句代码要这么写:
if (Prototype.BrowserFeatures.SpecificElementExtensions)
return Prototype.K;
如果能对元素的原型进行扩展,则直接返回元素引用本身,不用再把方法复制过来。如果像IE一样的不支持元素的原型进行扩展的话,就要老老实实地把函数复制到引用中。
好了,终于明白Prototype.BrowserFeatures.SpecificElementExtensions的原理了,分析一下它的代码:
BrowserFeatures: {
XPath: !!document.evaluate,
ElementExtensions: !!window.HTMLElement,
SpecificElementExtensions:
document.createElement('div').__proto__ &&
document.createElement('div').__proto__ !==
document.createElement('form').__proto__
},
这儿只分析一下SpecificElementExtensions的代码,这儿用到了比较生僻的知识:__proto__,这儿要说一下,任何一个函数,它都有一个属性:prototype来指向函数的原型,切记,只有函数有,对象没有(在此并不是否认函数不是对象)。那么一个普通对象,它有没有什么属性指向它的构造函数的原型呢,有?__proto__就是。不过,本人实验得知:IE中不支持__proto__属性。至于其他浏览器对于__proto__的支持情况就不清楚了。
再来看上面的代码,它的意思为:如果元素的原型存在,且不同元素的原型是不同的,那么说明,这种浏览器是严格实现W3C中的DOM模型的,可以对元素的原型进行扩展的。
二、Element、Event的真实用意
Element、Event只不过是protype新创建的一个对象?这可能是绝大部分人的看法,Prototype用了它们两个是有原因的。Element是所有元素的原型,Event是事件对象的原型。在IE等等不支持修改元素原型的浏览器里面,它们两个只是个对象而己,但是,在FireFox等浏览器中,它就是所有html元素的原型了,所有元素的属性、方法都从它继承,当然,在Element之下还有每种标签的原型,例如:div的原型是HTMLDivElement。关于元素的原型问题,应当可以在w3c查到,只是,我一直都没有查到。
我刚才想通了这个问题,再看整个ProtoType的代码就通透了。
说一件重要的事,所有原型扩展对在window这个对象下面,例如:Element的其实是window.Element。
三、prototype对DOM扩展的思路
记得写第一篇时,我也要讲这个思路,结果因为没有上面的这番领悟,没讲出个所以然来。
- 创建Element.Methods,然后,把扩展函数全部放里面
- 写Element.Methods中个别函数的特定浏览器版本
- Object.extend(Element, Element.Methods);把Element.Methods中的函数复制到Element下,这样,在FireFox下,原型扩展就完成了,这时,哪怕用document.getElementById所获得的引用也会有扩展函数,根本不需要Element.extend了。
- 如果浏览器不支持原型扩展,这时还有一招,Element.extend,它会手工把所有扩展函数都复制到元素的引用上面。
现在再回过头来看prototype,发现,这个名这取得真好,它几乎所有精神都花在对各种对象的原型的扩展上面了。