JS效率個(gè)人經(jīng)驗(yàn)談(8-15更新),加入range技巧
更新時(shí)間:2007年01月09日 00:00:00 作者:
首先,要謝謝CSDN hbhbhbhbhb1021(天外水火(我要多努力))和cuixiping(無心)的提醒。我會(huì)抽空把IE專有的方法如:insertAdjacentHTML的速度也給測(cè)出來看看是否合適大量數(shù)據(jù)時(shí)IE下,不用innerHTML的速度。
這里的主要測(cè)試不是指生成數(shù)據(jù)時(shí)的速度,指的是匹配速度 ,例如
我這里的匹配速度
我測(cè)的10000條數(shù)據(jù),有效數(shù)據(jù)為1000-1100條,輸出復(fù)雜的HTML,速度為360ms左右,方法為 正則匹配Match(有循環(huán))
希望貼出您的測(cè)試數(shù)據(jù)。
行innerHTML和insertAdjacentHTML速度的測(cè)試,比均結(jié)果相差不會(huì)大于20ms(平均速度),在IE中insertAdjacentHTML速度還是很快的,在Mozilla下是得不償失的。
可以點(diǎn)擊這里進(jìn)行簡(jiǎn)單的匹配測(cè)試
點(diǎn)擊這里進(jìn)行innerHTML和insertAdjacentHTML速度的測(cè)試,可以兼容Mozilla的
寫這篇文章,其間我也是刪刪減減的,所以語句也不怎么通順,看的朋友也就辛苦了一些了。
本文主要是出于有朋友使用我原來寫的autocomplete的JS控件。當(dāng)數(shù)據(jù)量大的時(shí)候,會(huì)出現(xiàn)效率極其慢的情況,我在這段時(shí)間做出的一些測(cè)試也及一些經(jīng)驗(yàn),與大家分享,如果有錯(cuò)的地方,還請(qǐng)指出。
經(jīng)過測(cè)試,我們會(huì)發(fā)現(xiàn)如下的情況或者說的結(jié)論,如果您的測(cè)試結(jié)果與我的不符,請(qǐng)說明原因,以便相互學(xué)習(xí)。
1)當(dāng)一個(gè)較大的HTML字符串給到obj.innerHTML時(shí)會(huì)出現(xiàn)麻煩。也就是說當(dāng)一個(gè)較大的字符串在賦予一個(gè)Element的innerHTML時(shí),這個(gè)過程將可能是我們無法容忍的。(而事實(shí)上這并非JS的錯(cuò),而確實(shí)是String數(shù)據(jù)量太多)
2)用拼合字符串的方法可以使效率提高,在字符串較大時(shí),2)的情況仍然出現(xiàn)。超過一定的數(shù)量,速度會(huì)明顯慢下來。
3)正則匹配的方法會(huì)比平常遍歷的方法要高效一些。
4)在執(zhí)行過程中,綁定事件的時(shí)間會(huì)花費(fèi)更多一些。測(cè)試在1w條數(shù)據(jù)情況下,大約是匹配以及生成HTML數(shù)據(jù)的30倍,也就是說生成數(shù)據(jù)總花費(fèi)100ms,而綁定事件則需要3000ms。
5)總體來說。IE的速度要比Mozilla要慢(我用的是Firefox1.5做的測(cè)試)。
6)大數(shù)據(jù)量時(shí),不要用DOM生成Element。
7)非JS內(nèi)置方法,也許會(huì)引發(fā)很多時(shí)間過多重復(fù)的勞動(dòng)而且可能事得其反。建議盡可能利會(huì)內(nèi)置方法。
總結(jié)問題:
一、在把字符串給到innerHTML上。
二、循環(huán)綁定事件所花費(fèi)時(shí)間。
三、生成我們需要的DIV所花時(shí)間。
四、不同的瀏覽器問題。
下面對(duì)癥下藥:
問題一
我們可以做的沒有其它的,只有盡可能少的HTML字符串,比如最基本的一個(gè)DIV,可以這樣寫
<div style="height:20px; font:9pt Verdana;"></div>也可以這樣寫<div class="c1"></div>,第二種就比第一種速度明顯要快的。如果還不行的話,請(qǐng)看下面這個(gè)方法對(duì)你是否合適
在做程序的時(shí)候突然想起來51js上PK tree,一位版主所寫的一棵樹,1百萬的一個(gè)節(jié)點(diǎn),動(dòng)態(tài)載入。只需要不到1秒。毫無疑問,肯定是取巧了,因?yàn)橹灰簧鷗ree的html就是一個(gè)很大的數(shù)量。這個(gè)樹的特別的地方就是生成樹時(shí),并非把1百萬的節(jié)點(diǎn)都一次生成innerHTML,而是只生成在視角范圍內(nèi)的節(jié)點(diǎn),當(dāng)滾動(dòng)條向下滾的時(shí)候,才動(dòng)態(tài)的再生成樹節(jié)點(diǎn)。這個(gè)方法至少我覺得思想很開闊,很有價(jià)值。
我們所知道,mySQL數(shù)據(jù)庫里取數(shù)據(jù)可以這么取。SELECT * FROM table limit 0,100,意思是只取數(shù)據(jù)庫中的0-100條數(shù)據(jù)。說到這里可能有些朋友也想到了,在JS中,我們可以利用這個(gè)方法來取數(shù)據(jù),將一個(gè)數(shù)組看作是一個(gè)表。只是單純的數(shù)據(jù)表,非二維表。如圖
利用這一些,我們可以把數(shù)據(jù)有效的值先取出來。如圖:
想想看。假如我們?nèi)∫粋€(gè)數(shù)組,下標(biāo)為10000,設(shè)生成一個(gè)autocomplete的節(jié)點(diǎn)HTML長(zhǎng)度20(已經(jīng)非常小了"<div class="out">item</div>)。
匹配數(shù)據(jù)已知:有3000條數(shù)據(jù)
輸節(jié)字節(jié)數(shù)為:3000(asc碼)也就是3000*20=60000字節(jié)
而用limit方法,輸出為:10*20=200字節(jié)。
很明顯的差距!
之后我們便可以分步求解,即當(dāng)滾動(dòng)條出現(xiàn),或者按下down(方向鍵)再動(dòng)態(tài)的生成innerHTML。
8-13更新:測(cè)了一下,用自己寫的limit的速度,和自帶的Array.slice的速度比了一下,速度差不多,而且有的時(shí)候還比slice的速度還要快一些。
Array.prototype.limit = function(l, h) {
var _a = this; var ret = [];
l = l<0?0:l; h = h>_a.length?_a.length:h;
for (var i=0; i<_a.length; i++) {
if (i>=l && i<=h) ret[ret.length] = _a[i];
if (i>h) break;
}; return ret;
}
有興趣的朋友也可以自己測(cè)一下,貼出數(shù)據(jù),看看哪個(gè)效率更好。
問題二、
為什么我們還要循環(huán)來綁定事件呢?
還是由于問題一。
假設(shè)這樣寫
1)
<div id="container">
<div onclick="handlerClick()">never-online</div>
</div>
還可以這樣寫
2)
<div id="container">
<div>never-online</div>
</div>
document.getElementById("container").childNodes[0].onclick=function(){handlerClick()};
這樣也可以省掉一些字符串,從而節(jié)省字符串資源。但又需要把container的子元素再遍歷,所以也會(huì)花費(fèi)時(shí)間,用第一種方法還是第二種?我建議還是用第一種,但最好把字符串減到最低,如:
<div id="container">
<div onclick="_c()">never-online</div>
</div>
大數(shù)據(jù)量情況下,還是越少字符越好,雖然代碼不怎么美觀。
問題三、
生成DIV時(shí)我們可以這樣生成
var div = document.createElement("DIV");
div.onclick=function(){};
//TODO
也可以這樣用字符串
var sHtml = "<div onclick=foo()>val</div>";
當(dāng)數(shù)量小時(shí),第一種速度會(huì)比第二種快。但當(dāng)達(dá)到一個(gè)數(shù)量級(jí)時(shí),第二種要明顯比第一種快??傮w來說第二種較好。因?yàn)榈诙N還可以更靈活,比如利用join,還有正則匹配。
問題四、
這個(gè)問題也不容忽視的。每個(gè)browser有不同的特點(diǎn),速度執(zhí)行也有不同,我個(gè)人覺得,這點(diǎn)和JS上優(yōu)化效率上是一樣的。
盡可能的利用瀏覽器本身的內(nèi)置方法,這樣大多數(shù)情況下也可以把效率提高。
那么如何能夠把腳本的效率提高起來呢?
1)用match匹配,一個(gè)aCache數(shù)組。循環(huán)match.length,并給aCache,之后用join(""),再給到innerHTML(此方法仍然需要循環(huán),而且需額外的一個(gè)數(shù)組做臨時(shí)數(shù)據(jù)存儲(chǔ))
2)無需循環(huán),但必須在生成數(shù)據(jù)時(shí)也額外生成指定字符串。(此方法也需要額外的空間做臨時(shí)數(shù)據(jù))如圖:
3)寧可多次判斷,也不重復(fù)進(jìn)行一次重新匹配。e.g:
input控件中第一次取到的值為:1,第二次按下的值為12
如果進(jìn)行判斷的話,可以事件存儲(chǔ)一個(gè)值,也就是前一次按下的值。如上面的值1。第二次按下時(shí)沒有退格,即再在前面的值中加一個(gè)字符2,那么我們將在前面1中匹配出的數(shù)據(jù)中進(jìn)行匹配。這樣可以大大的減輕循環(huán)的次數(shù)。
4)利用問題一中所寫的limit,將數(shù)據(jù)動(dòng)態(tài)取出。這些能夠很好的解決HTML字符串過大的問題,但此方法控制不當(dāng)?shù)脑?,也?huì)事得其反。
5) 利用range技巧來加入HTMLStr,也就是說,當(dāng)一個(gè)HTML字符串太大時(shí),再用innerHTML+=anotherHTMLStr,這樣的方法,也是會(huì)讓速度太慢,在IE中,我們可以用obj.insertAdjacentHTML("beforeEnd", anotherHTMLStr)這樣的方法來插入HTML,這個(gè)方法經(jīng)過測(cè)試,比較穩(wěn)定。利用limit再加上insertAdjacentHTML,會(huì)使得插入的HTML代碼速度更穩(wěn)定,而在Mozilla中,則要利用range的技巧來達(dá)到此目的,如下:
這里的主要測(cè)試不是指生成數(shù)據(jù)時(shí)的速度,指的是匹配速度 ,例如
我這里的匹配速度
我測(cè)的10000條數(shù)據(jù),有效數(shù)據(jù)為1000-1100條,輸出復(fù)雜的HTML,速度為360ms左右,方法為 正則匹配Match(有循環(huán))
希望貼出您的測(cè)試數(shù)據(jù)。
行innerHTML和insertAdjacentHTML速度的測(cè)試,比均結(jié)果相差不會(huì)大于20ms(平均速度),在IE中insertAdjacentHTML速度還是很快的,在Mozilla下是得不償失的。


寫這篇文章,其間我也是刪刪減減的,所以語句也不怎么通順,看的朋友也就辛苦了一些了。
本文主要是出于有朋友使用我原來寫的autocomplete的JS控件。當(dāng)數(shù)據(jù)量大的時(shí)候,會(huì)出現(xiàn)效率極其慢的情況,我在這段時(shí)間做出的一些測(cè)試也及一些經(jīng)驗(yàn),與大家分享,如果有錯(cuò)的地方,還請(qǐng)指出。
經(jīng)過測(cè)試,我們會(huì)發(fā)現(xiàn)如下的情況或者說的結(jié)論,如果您的測(cè)試結(jié)果與我的不符,請(qǐng)說明原因,以便相互學(xué)習(xí)。
1)當(dāng)一個(gè)較大的HTML字符串給到obj.innerHTML時(shí)會(huì)出現(xiàn)麻煩。也就是說當(dāng)一個(gè)較大的字符串在賦予一個(gè)Element的innerHTML時(shí),這個(gè)過程將可能是我們無法容忍的。(而事實(shí)上這并非JS的錯(cuò),而確實(shí)是String數(shù)據(jù)量太多)
2)用拼合字符串的方法可以使效率提高,在字符串較大時(shí),2)的情況仍然出現(xiàn)。超過一定的數(shù)量,速度會(huì)明顯慢下來。
3)正則匹配的方法會(huì)比平常遍歷的方法要高效一些。
4)在執(zhí)行過程中,綁定事件的時(shí)間會(huì)花費(fèi)更多一些。測(cè)試在1w條數(shù)據(jù)情況下,大約是匹配以及生成HTML數(shù)據(jù)的30倍,也就是說生成數(shù)據(jù)總花費(fèi)100ms,而綁定事件則需要3000ms。
5)總體來說。IE的速度要比Mozilla要慢(我用的是Firefox1.5做的測(cè)試)。
6)大數(shù)據(jù)量時(shí),不要用DOM生成Element。
7)非JS內(nèi)置方法,也許會(huì)引發(fā)很多時(shí)間過多重復(fù)的勞動(dòng)而且可能事得其反。建議盡可能利會(huì)內(nèi)置方法。
總結(jié)問題:
一、在把字符串給到innerHTML上。
二、循環(huán)綁定事件所花費(fèi)時(shí)間。
三、生成我們需要的DIV所花時(shí)間。
四、不同的瀏覽器問題。
下面對(duì)癥下藥:
問題一
我們可以做的沒有其它的,只有盡可能少的HTML字符串,比如最基本的一個(gè)DIV,可以這樣寫
<div style="height:20px; font:9pt Verdana;"></div>也可以這樣寫<div class="c1"></div>,第二種就比第一種速度明顯要快的。如果還不行的話,請(qǐng)看下面這個(gè)方法對(duì)你是否合適
在做程序的時(shí)候突然想起來51js上PK tree,一位版主所寫的一棵樹,1百萬的一個(gè)節(jié)點(diǎn),動(dòng)態(tài)載入。只需要不到1秒。毫無疑問,肯定是取巧了,因?yàn)橹灰簧鷗ree的html就是一個(gè)很大的數(shù)量。這個(gè)樹的特別的地方就是生成樹時(shí),并非把1百萬的節(jié)點(diǎn)都一次生成innerHTML,而是只生成在視角范圍內(nèi)的節(jié)點(diǎn),當(dāng)滾動(dòng)條向下滾的時(shí)候,才動(dòng)態(tài)的再生成樹節(jié)點(diǎn)。這個(gè)方法至少我覺得思想很開闊,很有價(jià)值。
我們所知道,mySQL數(shù)據(jù)庫里取數(shù)據(jù)可以這么取。SELECT * FROM table limit 0,100,意思是只取數(shù)據(jù)庫中的0-100條數(shù)據(jù)。說到這里可能有些朋友也想到了,在JS中,我們可以利用這個(gè)方法來取數(shù)據(jù),將一個(gè)數(shù)組看作是一個(gè)表。只是單純的數(shù)據(jù)表,非二維表。如圖
利用這一些,我們可以把數(shù)據(jù)有效的值先取出來。如圖:
想想看。假如我們?nèi)∫粋€(gè)數(shù)組,下標(biāo)為10000,設(shè)生成一個(gè)autocomplete的節(jié)點(diǎn)HTML長(zhǎng)度20(已經(jīng)非常小了"<div class="out">item</div>)。
匹配數(shù)據(jù)已知:有3000條數(shù)據(jù)
輸節(jié)字節(jié)數(shù)為:3000(asc碼)也就是3000*20=60000字節(jié)
而用limit方法,輸出為:10*20=200字節(jié)。
很明顯的差距!
之后我們便可以分步求解,即當(dāng)滾動(dòng)條出現(xiàn),或者按下down(方向鍵)再動(dòng)態(tài)的生成innerHTML。
8-13更新:測(cè)了一下,用自己寫的limit的速度,和自帶的Array.slice的速度比了一下,速度差不多,而且有的時(shí)候還比slice的速度還要快一些。
Array.prototype.limit = function(l, h) {
var _a = this; var ret = [];
l = l<0?0:l; h = h>_a.length?_a.length:h;
for (var i=0; i<_a.length; i++) {
if (i>=l && i<=h) ret[ret.length] = _a[i];
if (i>h) break;
}; return ret;
}
有興趣的朋友也可以自己測(cè)一下,貼出數(shù)據(jù),看看哪個(gè)效率更好。
問題二、
為什么我們還要循環(huán)來綁定事件呢?
還是由于問題一。
假設(shè)這樣寫
1)
<div id="container">
<div onclick="handlerClick()">never-online</div>
</div>
還可以這樣寫
2)
<div id="container">
<div>never-online</div>
</div>
document.getElementById("container").childNodes[0].onclick=function(){handlerClick()};
這樣也可以省掉一些字符串,從而節(jié)省字符串資源。但又需要把container的子元素再遍歷,所以也會(huì)花費(fèi)時(shí)間,用第一種方法還是第二種?我建議還是用第一種,但最好把字符串減到最低,如:
<div id="container">
<div onclick="_c()">never-online</div>
</div>
大數(shù)據(jù)量情況下,還是越少字符越好,雖然代碼不怎么美觀。
問題三、
生成DIV時(shí)我們可以這樣生成
var div = document.createElement("DIV");
div.onclick=function(){};
//TODO
也可以這樣用字符串
var sHtml = "<div onclick=foo()>val</div>";
當(dāng)數(shù)量小時(shí),第一種速度會(huì)比第二種快。但當(dāng)達(dá)到一個(gè)數(shù)量級(jí)時(shí),第二種要明顯比第一種快??傮w來說第二種較好。因?yàn)榈诙N還可以更靈活,比如利用join,還有正則匹配。
問題四、
這個(gè)問題也不容忽視的。每個(gè)browser有不同的特點(diǎn),速度執(zhí)行也有不同,我個(gè)人覺得,這點(diǎn)和JS上優(yōu)化效率上是一樣的。
盡可能的利用瀏覽器本身的內(nèi)置方法,這樣大多數(shù)情況下也可以把效率提高。
那么如何能夠把腳本的效率提高起來呢?
1)用match匹配,一個(gè)aCache數(shù)組。循環(huán)match.length,并給aCache,之后用join(""),再給到innerHTML(此方法仍然需要循環(huán),而且需額外的一個(gè)數(shù)組做臨時(shí)數(shù)據(jù)存儲(chǔ))
2)無需循環(huán),但必須在生成數(shù)據(jù)時(shí)也額外生成指定字符串。(此方法也需要額外的空間做臨時(shí)數(shù)據(jù))如圖:
3)寧可多次判斷,也不重復(fù)進(jìn)行一次重新匹配。e.g:
input控件中第一次取到的值為:1,第二次按下的值為12
如果進(jìn)行判斷的話,可以事件存儲(chǔ)一個(gè)值,也就是前一次按下的值。如上面的值1。第二次按下時(shí)沒有退格,即再在前面的值中加一個(gè)字符2,那么我們將在前面1中匹配出的數(shù)據(jù)中進(jìn)行匹配。這樣可以大大的減輕循環(huán)的次數(shù)。
4)利用問題一中所寫的limit,將數(shù)據(jù)動(dòng)態(tài)取出。這些能夠很好的解決HTML字符串過大的問題,但此方法控制不當(dāng)?shù)脑?,也?huì)事得其反。
5) 利用range技巧來加入HTMLStr,也就是說,當(dāng)一個(gè)HTML字符串太大時(shí),再用innerHTML+=anotherHTMLStr,這樣的方法,也是會(huì)讓速度太慢,在IE中,我們可以用obj.insertAdjacentHTML("beforeEnd", anotherHTMLStr)這樣的方法來插入HTML,這個(gè)方法經(jīng)過測(cè)試,比較穩(wěn)定。利用limit再加上insertAdjacentHTML,會(huì)使得插入的HTML代碼速度更穩(wěn)定,而在Mozilla中,則要利用range的技巧來達(dá)到此目的,如下:
if (browser.isMozilla) {
HTMLElement.prototype.insertAdjacentHTML = function (sWhere, sHTML) {
var df; var r = this.ownerDocument.createRange();
switch (String(sWhere).toLowerCase()) {
case "beforebegin":
r.setStartBefore(this);
df = r.createContextualFragment(sHTML);
this.parentNode.insertBefore(df, this);
break;
case "afterbegin":
r.selectNodeContents(this);
r.collapse(true);
df = r.createContextualFragment(sHTML);
this.insertBefore(df, this.firstChild);
break;
case "beforeend":
r.selectNodeContents(this);
r.collapse(false);
df = r.createContextualFragment(sHTML);
this.appendChild(df);
break;
case "afterend":
r.setStartAfter(this);
df = r.createContextualFragment(sHTML);
this.parentNode.insertBefore(df, this.nextSibling);
break;
}
};
}
后記:效率問題沒有一個(gè)完整的解決方法,只有實(shí)踐中根據(jù)需要而定。因此,上面的方法僅供您參考,如果你也有一些好的方法,可以在評(píng)論中寫下您的經(jīng)驗(yàn),以便交流。
HTMLElement.prototype.insertAdjacentHTML = function (sWhere, sHTML) {
var df; var r = this.ownerDocument.createRange();
switch (String(sWhere).toLowerCase()) {
case "beforebegin":
r.setStartBefore(this);
df = r.createContextualFragment(sHTML);
this.parentNode.insertBefore(df, this);
break;
case "afterbegin":
r.selectNodeContents(this);
r.collapse(true);
df = r.createContextualFragment(sHTML);
this.insertBefore(df, this.firstChild);
break;
case "beforeend":
r.selectNodeContents(this);
r.collapse(false);
df = r.createContextualFragment(sHTML);
this.appendChild(df);
break;
case "afterend":
r.setStartAfter(this);
df = r.createContextualFragment(sHTML);
this.parentNode.insertBefore(df, this.nextSibling);
break;
}
};
}
您可能感興趣的文章:
相關(guān)文章
js獲取元素到文檔區(qū)域document的(橫向、縱向)坐標(biāo)的兩種方法
獲取頁面中元素到文檔區(qū)域document的橫向、縱向坐標(biāo),在js控制元素運(yùn)動(dòng)的過程中,對(duì)于頁面元素坐標(biāo)位置的獲取是經(jīng)常用到的,這里主要總結(jié)下兩種方法2013-05-05JS實(shí)現(xiàn)拖動(dòng)模態(tài)框案例
這篇文章主要為大家詳細(xì)介紹了JS實(shí)現(xiàn)拖動(dòng)模態(tài)框案例,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-07-07window.open 以post方式傳遞參數(shù)示例代碼
這篇文章主要介紹了window.open以post方式傳遞參數(shù)的方法,需要的朋友可以參考下2014-02-02javascript DOM 操作基礎(chǔ)知識(shí)小結(jié)
經(jīng)常用到j(luò)avascript對(duì)dom,喜歡這方便的朋友也很多,要想更好的對(duì)dom進(jìn)行操作,這些基礎(chǔ)一定要知道的。2010-04-04(function(){})()的用法與優(yōu)點(diǎn)
(function(){})()的用法與優(yōu)點(diǎn)...2007-03-03[JS]實(shí)現(xiàn)動(dòng)態(tài)增加框架!未完成
[JS]實(shí)現(xiàn)動(dòng)態(tài)增加框架!未完成...2007-03-03在?localStorage?中上傳和檢索存儲(chǔ)圖像的示例詳解
這篇文章主要介紹了在?localStorage?中上傳和檢索存儲(chǔ)圖像,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-06-06