JavaScript 的一些效率問題

前幾天在測試 each 和 for 迴圈的效率時,意外的一直得到 each 效率比較好的奇怪現象,搞了兩天才找到原因。

each 這種方法效率會比 for 迴圈還要低主要是因為它是把要做的事情用 function 傳進去,多了一個 function call 和一層 function scope,要對變數作存取時會多了到不同層 scope 尋找的差,所以理論上它會比 for 迴圈還要慢。除此之外,DOM 本身就很慢了,當然 DOM NodeList 的操作和存取也不會快到哪去,所以像 Sizzle 引擎就會把 DOM NodeList 轉成陣列再傳回來,而我測試 each 和 for 兩者的效率時,也就是這個部分產生了非預想的結果,根據測試結果 Google V8 和 Firefox 3.6 的 Tracemonkey 這兩個會編譯 JS 的引擎,第二次對同樣內容的 DOM collection 轉陣列的動作時會比第一次還要快,而且大約是兩倍快,測試的基準是第一次用 getElementsByTagName 抓 <span> ,第二次用 jQuery,內部也是一樣用 getElementsByTagName ,並且小修改過 DOM 結構後再做一樣的事情也是會比第一次還快,並且,不管是用 slice 還是 for 迴圈一個元素一個元素推到陣列裡面都沒差很多,一開始因為測試都是對同樣的標籤作處理,結果先測的方法就佔了劣勢,就像是美食漫畫一樣,先上菜的都會輸一樣,不過我對編譯器的怎樣做最佳化的方法不熟,所以這兩個引擎是怎麼辦到的就不清楚了。

除了這點之外,還有一些不算小發現的小發現,第一個就是 Firefox 3.6 還是好慢,詳細數據我忘了,不過和 V8、Safari 比起來差距還是不小,然後就是 IE 超.級.慢!!第二是 each 真的比較慢,不過在現在 JS 引擎普遍暴力的情況下差距其實不明顯(不過 NodeList 和陣列的差距還是有)。第三是前面已經講過的 Sizzle 回傳的是陣列而不是 NodeList ,雖然實際上想要自己組合 NodeList 似乎也是不可能的。最後是 jQeury 可以用$("span")[0]這種寫法來直接存取 DOM 節點,不過它並不是陣列,要轉成純陣列可以用$("span").get(),只是測試過後發現沒有比較快,因為還要重新轉一次陣列,這裡損失的時間也不會比直接存取來的少。