深入理解Javascript中的循環(huán)優(yōu)化
更新時(shí)間:2013年11月09日 15:01:41 作者:
這篇文章介紹了Javascript中的循環(huán)優(yōu)化,有需要的朋友可以參考一下
循環(huán)是大多數(shù)編程語(yǔ)言都具備的基本功能,JS也不例外,不同之處在于JS是解釋型語(yǔ)言,運(yùn)行于瀏覽器環(huán)境中,客戶端的軟硬件條件會(huì)對(duì)JS執(zhí)行效率產(chǎn)生很大的影響。然而客戶端環(huán)境對(duì)于開(kāi)發(fā)者是未知、多樣的,并且難以改變,所以優(yōu)化代碼質(zhì)量是提高代碼效率的主要途徑。
JS代碼中,循環(huán)是比較容易導(dǎo)致性能問(wèn)題的因素。理解循環(huán)特性進(jìn)而有針對(duì)性地進(jìn)行優(yōu)化也許會(huì)帶來(lái)不錯(cuò)的性能提升。
for、while、do-while循環(huán):
這三種循環(huán)本身的循環(huán)效率相差不多,所以只要根據(jù)適合的應(yīng)用場(chǎng)景選擇即可。
以for循環(huán)為例:
var aValues = ["a", "b", "c", "d"];
for(var i = 0; i < aValues.length; i += 1){
fDoSomethingA(aValues[i]);
fDoSomethingA(aValues[i]);
}
上面例子中每次循環(huán)都要比較i與數(shù)組的長(zhǎng)度,所以每次都要重新讀取數(shù)組長(zhǎng)度,由如果數(shù)組長(zhǎng)度在循環(huán)中是不變的,這樣做就沒(méi)有必要,我們可以使用局部變量代替length的讀取。同理,例子中,aValues[i]由于被讀取兩次以上,我們也可以將它賦值給局部變量:
var aValues = ["a", "b", "c", "d"], nLength = aValues.length;
for(var i = 0, sValue; i < nLength; i += 1){
sValue = aValues[i];
fDoSomethingA(sValue);
fDoSomethingB(sValue);
}
如果循環(huán)的業(yè)務(wù)邏輯對(duì)循環(huán)順序不敏感,可以嘗試倒序循環(huán),即將計(jì)數(shù)器遞減到0。
var aValues = ["a", "b", "c", "d"], nLength = aValues.length;
for(var i = nLength, sValue; i -= 1;){
sValue = aValues[i];
fDoSomethingA(sValue);
fDoSomethingB(sValue);
}
使用這種方式計(jì)數(shù)器默認(rèn)與0進(jìn)行比較,連局部變量比較都省略了,理論上也能提高效率。
for-in循環(huán):
for-in循環(huán)更像在窮舉,他用來(lái)遍歷對(duì)象屬性,我們知道對(duì)象屬性的查找會(huì)一直延續(xù)到原型鏈頂端,這將大大降低循環(huán)效率。for-in循環(huán)的寫(xiě)法上沒(méi)有什么優(yōu)化空間,需要在使用時(shí)遵循一定原則:盡量只在遍歷數(shù)據(jù)型對(duì)象的時(shí)候才使用for-in循環(huán)。
如果遍歷對(duì)象的屬性是明確的,可以使用數(shù)組循環(huán)替代。
例如遍歷一個(gè)聯(lián)系人對(duì)象:
var aContact = ["N", "FN", "EMAIL;PREF", ...];
for(var i = aContact.length; i -= 1;){
fDoSomething(aContact[i]);
}
Duff策略
Duff策略的主要原理是通過(guò)展開(kāi)循環(huán)減少次數(shù)來(lái)提高效率。例如
一個(gè)普通循環(huán):
for(var i = aValues.length; i -= 1){
fDoSomething(aValues[i]);
}
如果aValues.length == N,寫(xiě)成以下這種方式的效率將比循壞來(lái)的高:
fDoSomething(aValues[0]);
fDoSomething(aValues[1]);
fDoSomething(aValues[2]);
fDoSomething(aValues[3]);
...
...
fDoSomething(aValues[N-1]);
但如果N很大,這種寫(xiě)法就不現(xiàn)實(shí),而Duff策略是一種適中的循環(huán)展開(kāi)策略。
近日在網(wǎng)易郵箱通訊錄聯(lián)系人的初始化循環(huán)中加入了Duff策略:
var nLength = aContacts.length,
// 總輪數(shù)
nRounds = Math.floor( nLength / 8),
// 額外余量
nLeft = nLength % 8,
i = 0;
// 先處理余量
if(nLeft){
do{
fFormat(aContacts[i ++]);
}while(-- nLeft)
}
// 每輪執(zhí)行8次格式化
if(nRounds){
do{
fFormat(aContacts[i ++]);
fFormat(aContacts[i ++]);
fFormat(aContacts[i ++]);
fFormat(aContacts[i ++]);
fFormat(aContacts[i ++]);
fFormat(aContacts[i ++]);
fFormat(aContacts[i ++]);
fFormat(aContacts[i ++]);
}while(-- nRounds)
}
如上所示,每輪循環(huán)可以執(zhí)行8個(gè)聯(lián)系人數(shù)據(jù)的格式化操作,還有一輪循環(huán)用于處理余下的聯(lián)系人。由此可見(jiàn),在聯(lián)系人較多的情況下總的循環(huán)次數(shù)大大降低,可以降低循環(huán)的消耗。另外,8是Duff策略提出的最優(yōu)值。
實(shí)際測(cè)試時(shí)發(fā)現(xiàn)在IE下可以帶來(lái)10-20%以上的性能提升,而非IE瀏覽器中幾乎看不到區(qū)別。
結(jié)束語(yǔ):在測(cè)試過(guò)程中發(fā)現(xiàn)非IE瀏覽器下,優(yōu)化后和優(yōu)化前的效率差距并不是很大,甚至可以忽略,這說(shuō)明這些瀏覽器的JS引擎對(duì)
JS代碼中,循環(huán)是比較容易導(dǎo)致性能問(wèn)題的因素。理解循環(huán)特性進(jìn)而有針對(duì)性地進(jìn)行優(yōu)化也許會(huì)帶來(lái)不錯(cuò)的性能提升。
for、while、do-while循環(huán):
這三種循環(huán)本身的循環(huán)效率相差不多,所以只要根據(jù)適合的應(yīng)用場(chǎng)景選擇即可。
以for循環(huán)為例:
復(fù)制代碼 代碼如下:
var aValues = ["a", "b", "c", "d"];
for(var i = 0; i < aValues.length; i += 1){
fDoSomethingA(aValues[i]);
fDoSomethingA(aValues[i]);
}
上面例子中每次循環(huán)都要比較i與數(shù)組的長(zhǎng)度,所以每次都要重新讀取數(shù)組長(zhǎng)度,由如果數(shù)組長(zhǎng)度在循環(huán)中是不變的,這樣做就沒(méi)有必要,我們可以使用局部變量代替length的讀取。同理,例子中,aValues[i]由于被讀取兩次以上,我們也可以將它賦值給局部變量:
復(fù)制代碼 代碼如下:
var aValues = ["a", "b", "c", "d"], nLength = aValues.length;
for(var i = 0, sValue; i < nLength; i += 1){
sValue = aValues[i];
fDoSomethingA(sValue);
fDoSomethingB(sValue);
}
如果循環(huán)的業(yè)務(wù)邏輯對(duì)循環(huán)順序不敏感,可以嘗試倒序循環(huán),即將計(jì)數(shù)器遞減到0。
復(fù)制代碼 代碼如下:
var aValues = ["a", "b", "c", "d"], nLength = aValues.length;
for(var i = nLength, sValue; i -= 1;){
sValue = aValues[i];
fDoSomethingA(sValue);
fDoSomethingB(sValue);
}
使用這種方式計(jì)數(shù)器默認(rèn)與0進(jìn)行比較,連局部變量比較都省略了,理論上也能提高效率。
for-in循環(huán):
for-in循環(huán)更像在窮舉,他用來(lái)遍歷對(duì)象屬性,我們知道對(duì)象屬性的查找會(huì)一直延續(xù)到原型鏈頂端,這將大大降低循環(huán)效率。for-in循環(huán)的寫(xiě)法上沒(méi)有什么優(yōu)化空間,需要在使用時(shí)遵循一定原則:盡量只在遍歷數(shù)據(jù)型對(duì)象的時(shí)候才使用for-in循環(huán)。
如果遍歷對(duì)象的屬性是明確的,可以使用數(shù)組循環(huán)替代。
例如遍歷一個(gè)聯(lián)系人對(duì)象:
復(fù)制代碼 代碼如下:
var aContact = ["N", "FN", "EMAIL;PREF", ...];
for(var i = aContact.length; i -= 1;){
fDoSomething(aContact[i]);
}
Duff策略
Duff策略的主要原理是通過(guò)展開(kāi)循環(huán)減少次數(shù)來(lái)提高效率。例如
一個(gè)普通循環(huán):
復(fù)制代碼 代碼如下:
for(var i = aValues.length; i -= 1){
fDoSomething(aValues[i]);
}
如果aValues.length == N,寫(xiě)成以下這種方式的效率將比循壞來(lái)的高:
復(fù)制代碼 代碼如下:
fDoSomething(aValues[0]);
fDoSomething(aValues[1]);
fDoSomething(aValues[2]);
fDoSomething(aValues[3]);
...
...
fDoSomething(aValues[N-1]);
但如果N很大,這種寫(xiě)法就不現(xiàn)實(shí),而Duff策略是一種適中的循環(huán)展開(kāi)策略。
近日在網(wǎng)易郵箱通訊錄聯(lián)系人的初始化循環(huán)中加入了Duff策略:
復(fù)制代碼 代碼如下:
var nLength = aContacts.length,
// 總輪數(shù)
nRounds = Math.floor( nLength / 8),
// 額外余量
nLeft = nLength % 8,
i = 0;
// 先處理余量
if(nLeft){
do{
fFormat(aContacts[i ++]);
}while(-- nLeft)
}
// 每輪執(zhí)行8次格式化
if(nRounds){
do{
fFormat(aContacts[i ++]);
fFormat(aContacts[i ++]);
fFormat(aContacts[i ++]);
fFormat(aContacts[i ++]);
fFormat(aContacts[i ++]);
fFormat(aContacts[i ++]);
fFormat(aContacts[i ++]);
fFormat(aContacts[i ++]);
}while(-- nRounds)
}
如上所示,每輪循環(huán)可以執(zhí)行8個(gè)聯(lián)系人數(shù)據(jù)的格式化操作,還有一輪循環(huán)用于處理余下的聯(lián)系人。由此可見(jiàn),在聯(lián)系人較多的情況下總的循環(huán)次數(shù)大大降低,可以降低循環(huán)的消耗。另外,8是Duff策略提出的最優(yōu)值。
實(shí)際測(cè)試時(shí)發(fā)現(xiàn)在IE下可以帶來(lái)10-20%以上的性能提升,而非IE瀏覽器中幾乎看不到區(qū)別。
結(jié)束語(yǔ):在測(cè)試過(guò)程中發(fā)現(xiàn)非IE瀏覽器下,優(yōu)化后和優(yōu)化前的效率差距并不是很大,甚至可以忽略,這說(shuō)明這些瀏覽器的JS引擎對(duì)
相關(guān)文章
JavaScript的內(nèi)置對(duì)象Math和字符串詳解
這篇文章主要為大家介紹了JavaScript的內(nèi)置對(duì)象Math和字符串,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助2021-11-11js window.print實(shí)現(xiàn)打印特定控件或內(nèi)容
window.print可以打印網(wǎng)頁(yè),但有時(shí)候我們只希望打印特定控件或內(nèi)容,怎么辦呢?可以把要打印的內(nèi)容放在div中,然后用下面的代碼進(jìn)行打印,希望對(duì)大家有所幫助2013-09-09實(shí)現(xiàn)div滾動(dòng)條默認(rèn)最底部以及默認(rèn)最右邊的示例代碼
下面小編就為大家分享一篇實(shí)現(xiàn)div滾動(dòng)條默認(rèn)最底部以及默認(rèn)最右邊的示例代碼,代碼非常簡(jiǎn)潔,具有很好的參考價(jià)值,希望對(duì)大家有所幫助2017-11-11javascript實(shí)現(xiàn)簡(jiǎn)單搜索功能
這篇文章主要為大家詳細(xì)介紹了javascript實(shí)現(xiàn)簡(jiǎn)單搜索功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-03-03JavaScript代碼實(shí)現(xiàn)禁止右鍵、禁選擇、禁粘貼、禁shift、禁ctrl、禁alt
這篇文章主要介紹了JavaScript代碼實(shí)現(xiàn)禁止右鍵、禁選擇、禁粘貼、禁shift、禁ctrl、禁alt,需要的朋友可以參考下2015-11-11在js文件中寫(xiě)el表達(dá)式取不到值的原因及解決方法
在js文件中寫(xiě)el表達(dá)式取不到值,百度一下,將經(jīng)驗(yàn)總結(jié)如下,有類似情況的朋友可以參考下2013-12-12