讀古今文學網 > 編寫高質量代碼:改善JavaScript程序的188個建議 > 建議112:謹慎使用HTML集合 >

建議112:謹慎使用HTML集合

HTML集合是用於存放DOM節點引用的類數組對象。下列方法的返回值都是一個集合:

❑document.getElementsByName

❑document.getElementsByClassName

❑document.getElementsByTagName_r

下列屬性也屬於HTML集合:

❑document.images:頁面中所有的<img>元素。

❑document.links:所有的<a>元素。

❑document.forms:所有表單。

❑document.forms[0].elements:頁面中第一個表單的所有字段。

這些方法和屬性返回HTMLCollection對象,是一種類似數組的列表。它不是數組,因為它沒有數組的方法,比如push、slice等,但它提供了一個length屬性,與數組一樣可以使用索引訪問列表中的元素。例如,document.images[1]返回集合中的第二個元素。正如DOM標準中所定義的那樣,HTML集合是一個虛擬存在,意味著當底層文檔更新時它將自動更新。

HTML集合實際上在查詢文檔,當更新信息時,每次都要重複執行這種查詢操作。例如,讀取集合中元素的數目,也就是集合的length。這正是執行低效率的原因。


var allps=document.getElementsByTagName_r('p');

for(var i=0;i<allps.length;i++){

document.body.appendChild(document.createElement('p'))

}


上面這段代碼看上去只是簡單地增加了頁面中p元素的數量:遍歷現有p,每次創建一個新的p並附加到body上面。實際上這是個死循環,因為循環終止條件allps.length在每次迭代中都會增加,它反映出底層文檔的當前狀態。

像這樣遍歷HTML集合會導致邏輯錯誤,而且也很慢,因為每次迭代都需要進行查詢,所以不建議用數組的length屬性做循環判斷條件。訪問集合的length比數組的length還要慢,因為這意味著每次都要重新運行查詢過程。在下面的例子中,將一個集合coll複製到數組arr中,然後比較每次迭代所用的時間。


function toArray(coll){

for(var i=0,a=,len=coll.length;i<len;i++){

a[i]=coll[i];

}

return a;

}


設置一個集合,並把它複製到一個數組:


var coll=document.getElementsByTagName_r('p');

var ar=toArray(coll);


比較下列兩個函數:


//比較慢

function loopCollection{

for(var count=0;count<coll.length;count++){

}

}

//比較快

function loopCopiedArray{

for(var count=0;count<arr.length;count++){

}

}


當每次迭代過程訪問集合的length屬性時,將會導致集合器更新,在所有瀏覽器上都會產生明顯的性能損失。優化的辦法很簡單,只要將集合的length屬性緩存到一個變量中,然後在循環判斷條件中使用這個變量。


function loopCacheLengthCollection{

var coll=document.getElementsByTagName_r('p'),

len=coll.length;

for(var count=0;count<len;count++){

}

}


上面函數的運行速度與loopCopiedArray一樣快。

遍歷數組比遍歷集合快,如果先將集合元素複製到數組,訪問它們的屬性將更快。記住這需要一個額外的步驟——遍歷集合,因此,應當評估在特定條件下使用這樣一個數組副本是否有益。