首页 » 编写高质量代码:改善JavaScript程序的188个建议 » 编写高质量代码:改善JavaScript程序的188个建议全文在线阅读

《编写高质量代码:改善JavaScript程序的188个建议》建议115:实现DOM原型继承机制

关灯直达底部

符合DOM标准的浏览器都支持HTMLElement类,DOM文档中所有元素都继承于这个类,而HTMLElement对象又继承于Element类(Element类继承于Node类),这样通过在HTMLElement类的原型对象上定义方法,为HTML DOM文档中所有元素绑定函数和数据。例如:


HTMLElement.prototype.pre=function{//扩展原型方法

var e=this.previousSibling;

while(e&&e.nodeType!=1){

e=e.previousSibling;

}

return e;

}


为HTMLElement类的原型对象定义方法,这样每个HTML DOM元素都会继承这个方法。注意,在函数体内,应该通过关键字this来指向当前元素对象,而不用从参数变量中获取当前元素。在应用这个原型方法时,可以直接把它绑定到元素后面,例如:


window.onload=function{

var e=document.getElementsByTagName(/"p/")[0];

e=e.pre;

alert(e.nodeName);

}


这样通过HTMLElement类的原型对象prototype就可以很方便地扩展每个HTML元素的方法或属性。但IE隐藏了这个类,禁止通过JavaScript脚本来访问它。为了能够兼容IE,可以通过扩展方法解决这个问题。扩展方法如下:


var DOMElement={

extend:function(name,fn){

//添加名称为name的方法fn

if(!document.all)

//IE之外的浏览器都能够访问到HTMLElement这个类

eval(/"HTMLElement.prototype./"+name+/"=fn/");

else{

//在IE中不能访问HTMLElement这个类

//为了达到同样的目的,必须重写下面几个函数

//document.createElement

//document.getElementById

//document.getElementsByTagName

//这几个函数都是获得HTML元素的方法

//修改这些方法,使通过这些方法获得的每个元素拥有名称为name的方法fn

var_createElement=document.createElement;

document.createElement=function(tag){

var_elem=_createElement(tag);

eval(/"_elem./"+name+/"=fn/");//_elem[name]=fn;也可以达到同样的目的

return_elem;

}

var_getElementById=document.getElementById;

document.getElementById=function(id){

var_elem=_getElementById(id);

eval(/"_elem./"+name+/"=fn/");

return_elem;

}

var_getElementsByTagName=document.getElementsByTagName;

document.getElementsByTagName=function(tag){

var_arr=_getElementsByTagName(tag);

for(var_elem=0;_elem<_arr.length;_elem++)

eval(/"_arr[_elem]./"+name+/"=fn/");

return_arr;

}

}

}

};


上面扩展函数的设计思路比较灵巧,实现方法也很简单,下面进行详细分析。

首先,利用Document对象的All对象来判断浏览器的类型,因为只有IE支持All对象集合。

对于非IE浏览器来说,由于它们一般都支持DOM标准模型,因此可以直接使用HTMLElement.prototype来设计原型方法,实现被所有文档元素继承。

对于IE来说,它不能够对原型对象进行设计,用户只能够通过Document对象的getElementById或getElementsByTagName方法来获取文档中的元素,或者通过Document对象的createElement方法创建一个新的元素。那么,只要把元素将要继承的方法绑定到这些方法内部就可以间接实现所有元素都拥有这个方法。因为,获取文档中的元素只能够通过这3种方法中的一种才可以实现。把住这3种方法的关口,即可实现为当前元素捎带一个继承方法的目的。

由于Document对象允许用户重写这些方法,那么可以在重写过程中顺便加入用户指定的方法。为了避免在重写方法过程中破坏原方法的引用,可以先借助变量存储原方法的引用:


var_createElement=document.createElement;


然后重写方法,在重写过程中先执行原方法的代码:


var_elem=_createElement(tag);


接着,通过动态形式,为当前元素加入一个用户的方法:


eval(/"_elem./"+name+/"=fn/");


最后,再返回原方法执行的值,这样既不会破坏原方法的功能,又为当前元素扩展了一个方法。具体应用的方法如下:


DOMElement.extend(/"pre/",function{

var e=this.previousSibling;

while(e&&e.nodeType!=1){

e=e.previousSibling;

}

return e;

})


在应用DOMElement.extend方法时,应确保在获取或创建元素之前执行它,即在调用Document对象的getElementById、getElementsByTagName和createElement方法之前调用DOMElement.extend方法。在页面初始化后为当前元素调用扩展的方法,实际上这个方法与上面示例中HTMLElement类原型方法是完全相同的。


window.onload=function{

var e=document.getElementsByTagName(/"p/")[0];

e=e.pre;

alert(e.nodeName);

}