搜尋
首頁web前端js教程web效能優化之javascript效能調優_javascript技巧

JavaScript 是比較完善的前端開發語言,在現今的 web 開發中應用非常廣泛,尤其是對 Web 2.0 的應用。隨著 Web 2.0 越來越流行的今天,我們會發現:在我們的 web 應用專案中,會有大量的 JavaScript 程式碼,而且以後會越來越多。 JavaScript 作為一個解釋執行的語言,以及它的單線程機制,決定了效能問題是 JavaScript 的軟肋,也是 web 軟體工程師在寫 JavaScript 需要高度重視的問題,尤其是針對 Web 2.0 的應用。絕大多數 web 軟體工程師都或多或少的遇到所開發的 Web 2.0 應用的效能欠佳的問題,其主要原因就是 JavaScript 效能不足,瀏覽器負荷過重。但是,解決這種解釋執行並且單執行緒運作語言的效能問題也並非易事。這篇文章會著重介紹一些關於開發中JavaScript 效能調優的技巧和最佳實踐,同樣也會涉及到關於JavaScript 操作DOM 節點的效能調優的一些方法.

簡介
Web 開發中經常會遇到效能的問題,尤其是針對當今的Web2.0 應用。 JavaScript 是當今使用最廣泛的Web 開發語言,Web 應用的效能問題很大一部分都是由程式設計師寫的JavaScript 腳本效能不佳所造成的,裡麵包括了JavaScript 語言本身的效能問題,以及其與DOM 交互時的性能問題。本文主要來探討如何盡可能多的避免這類問題,從而最大限度的提高 Web 應用的效能。

JavaScript 效能調優
JavaScript 語言由於它的單執行緒和解釋執行的兩個特點,決定了它本身有很多地方有效能問題,所以可改進的地方有不少。

eval 的問題
比較下述程式碼:
清單1. eval 的問題

複製程式碼 程式碼如下:

var reference = {}, props = “p1”;
eval(“reference.” props “=5”)
var reference = {}, props = “p1”;
reference[props] = 5

有「eval」的程式碼比沒有「eval」的程式碼慢上100 倍以上。
主要原因是:JavaScript 程式碼在執行前會進行類似「預先編譯」的操作:首先會建立一個目前執行環境下的活動對象,並將那些以var 申明的變數設為活動對象的屬性,但是此時這些變數的賦值都是undefined,並將那些以function 定義的函數也加入為活動物件的屬性,而且它們的值正是函數的定義。但是,如果你使用了“eval”,則“eval”中的程式碼(實際上為字串)無法預先識別其上下文,無法事先解析和最佳化,即無法進行預編譯的操作。所以,其性能也會大幅降低。

Function 的用法
比較下述代碼:
清單2. function 的用法
程式碼如下:


var func1 = new Function(“return arguments[0] arguments[1]”);
func11 (10, 20);
var func2 = function(){ return arguments[0] arguments[1] };
func2(10, 20);


這裡類似之前提到的「eval」方法,這裡「func1」的效率會比「func2」的效率差很多,所以建議使用第二種方式。
函數的作用域鏈(scope chain):

JavaScript 程式碼解釋執行,在進入函數內部時,它會預先分析當前的變量,並將這些變數歸入不同的層級(level),一般情況下:
局部變數放入層級1(淺),全域變數放入層級2(深)。如果進入「with」或「try – catch」程式碼區塊,則會增加新的層級,即將「with」或「catch」裡的變數放入最淺層(層 1),並將先前的層級依序加深。
參考如下代碼: 清單3. 函數作用域鏈
代碼如下:


var myObj = … ..
… ..
function process(){
var images = document.getElementsByTagName("img"), wid = document.getElementsByTagName("input"),
combination = [];
for(var i = 0; i combination.push(combine(images[i] , widget[2*i]));
}
myObj.container.property1 = combination[0];
myObj.container.property2 = combination[combination.length-1];
}

這裡我們可以看到,“images”,“widget”,“combination”屬於局部變量,在層 1。 “document”,“myObj”屬於全域變量,在層 2。
變數所​​在的層越淺,存取(讀取或修改)速度越快,層越深,存取速度越慢。所以這裡對“images”,“widget”,“combination”的訪問速度比“document”,“myObj”要快一些。所以建議盡量使用局部變量,可見如下程式碼:
清單4. 使用局部變數
複製程式碼



複製程式碼



複製程式碼



複製程式碼


程式碼如下:


var myObj = … ..
… .. function process(){ var doc = document;
var images = doc. getElementsByTagName("img"),
widget = doc.getElementsByTagName("input"), combination = []; for(var i = 0; i combination.push(combine(images[i], widget[2*i])); } myObj.container.property1 = combination[0]; myObj.container.property2 = combination[ combination.length-1];
}


我們用局部變數“doc”取代全域變數“document”,這樣可以改善效能,尤其是對於大量使用全域變數的函數裡面。
再看如下代碼:

清單5. 慎用with





複製代碼


代碼如下:

var myObj = … ..
… ..
function process(){ var doc = document; var images = doc.getElementsByName(" img"),
widget = doc.getElementsByTagName("input"),
combination = []; for(var i = 0; i combination. push(combine(images[i], widget[2*i])); } with (myObj.container) { property1 = combination[0]; property2 = combination[combination .length-1];
}
}


加上「with」關鍵字,我們讓程式碼更加簡潔清晰了,但是這樣做效能會受影響。如同先前所說的,當我們進入「with」程式碼區塊時,「combination」便從原來的層 1 變到了層 2,這樣,效率會大打折扣。所以比較一下,還是用原來的程式碼:

清單6.改進。程式碼如下:


var myObj = … ..
… ..
function process(){
var doc = document;
var images = doc.getElementsByName(doc. "img"),
widget = doc.getElementsByTagName("input"),
combination = [];
for(var i = 0; i combination .push(combine(images[i], widget[2*i]));
} myObj.container.property1 = combination[0]; myObj.container.property2 = combination[combination. length-1];
}


複製程式碼


程式碼如下:


var myObj = … ..
… ..
function process(){
var doc = document;
var images = doc.getElementsByTagName("img"),
widget = doc.getElementsByTagName("input"),
combination = [];
for(var i = 0; i combination.push(combine(images[i], widget[2*i]));
}
var ctn = myObj.container;
ctn.property1 = combination[0]; ctn.property2 = combination[combination.length-1]; }


我們用局部變數來取代「myObj」的第2 層的「container ”對象。如果有大量的這種對物件深層屬性的訪問,可以參考以上方式提高效能。
字串(String)相關


複製程式碼
代碼如下: str = “str1”str2” str = “str1”str2” str = “str1”str2”
這是我們拼接字串常用的方式,但是這種方式會有一些臨時變數的建立和銷毀,影響效能,所以建議使用如下方式拼接:
清單9. 字串陣列方式拼接
複製程式碼 程式碼如下:

var str str_ >str_array.push(“str1”);
str_array.push(“str2”);
str = str_array.join(“”);

這裡我們利用陣列(array )的「join」方法實作字串的拼接,尤其是程式的舊版的Internet Explore(IE6)上運行時,會有非常明顯的效能上的改進。
當然,最新的瀏覽器(如火狐Firefox3 ,IE8 等等)對字串的拼接做了最佳化,我們也可以這樣寫:

清單10. 字串快速拼接
複製程式碼 程式碼如下:
str =“str1”
str =“str”

新的瀏覽器對「 =」做了最佳化,效能略快於陣列的「join」方法。在不久的將來更新版本瀏覽器可能會對「 」也會進行最佳化,所以那時我們可以直接寫入:str = “str1” “str2”。


隱式型別轉換 參考以下程式碼:

清單11. 隱式型別轉換




複製程式碼

程式碼如下:
var str = “12345678”, arr = [];
for(var i = 0; i arr.push( str.charAt(i)); } 這裡我們在每個循環時都會呼叫字串的“ charAt”方法,但由於我們是將常數“12345678”賦值給“str”,所以“str”這裡事實上並不是一個字串對象,當它每次調用“charAt”函數時,都會臨時構造值為“ 12345678”的字串對象,然後呼叫“charAt”方法,最後再釋放這個字串臨時對象。我們可以做一些改進: 清單12. 避免隱式類型轉換




複製代碼

代碼如下:

var str = new Stirng(“12345678”), arr = []; for(var i = 0; i arr.push( str.charAt(i));
}


這樣一來,變數「str」作為一個字串對象,就不會有這種隱式型別轉換的過程了,這樣一來,效率會顯著提高。
字串匹配
JavaScript 有 RegExp 對象,支援對字串的正規表示式匹配。是一個很好的工具,但是它的性能並不是非常理想。相反,字串物件(String)本身的一些基本方法的效率是非常高的,例如“substring”,“indexOf”,“charAt”等等,在我們需要用正則表達式匹配字串時,可以考慮一下: 是否能夠透過字串物件本身支援的基本方法解決問題。 是否可以透過「substring」來縮小需要用正規表示式的範圍。 這些方式都能夠有效的提高程序的效率。 關於正規表示式對象,還有一點要注意,參考如下程式碼: 清單13. 正規表示式




複製程式碼

程式碼如下:

for(var i = 0; i if(str_array[i].match (/^s*extras/)){
……………………




複製程式碼


程式碼如下:
var sExpr = /^s*extras/; for(var i = 0; i if(str_array[i].match(sExpr )){ …………………… } }
這樣就不會有臨時物件了。
setTimeout 和setInterval
「setTimeout」和「setInterval」這兩個函數可以接受字串變量,但是會帶來和之前談到的「eval」類似的效能問題,所以建議還是直接傳入函數對象本身。

利用提前退出
參考如下兩段代碼:
清單15. 利用提前退出



複製程式碼


程式碼如下:


// 程式碼1
var name = … .;
var source = … ;
if(source.match(/ …… /)){
……………………………
}
// 程式碼2
var name = … .;
var source = ... ; if(name.indexOf( … ) &&source.match(/ …… /)){ ……………………………
}


代碼2 多了一個對“name.indexOf( … )”的判斷,這使得程式每次走到這段時會先執行“indexOf”的判斷,再執行後面的“match” ,在「indexOf」比「match」更有效率的前提下,這樣做會減少「match」的執行次數,從而一定程度的提高效率。
----------------------------------------------- ---------------------------------

DOM 操作效能調優

JavaScript 的開發離不開DOM 的操作,所以對DOM 操作的效能調優在Web 開發中也是非常重要的。 Repaint 和 Reflow Repaint 也叫 Redraw,它指的是一種不會影響目前 DOM 的結構和佈局的一種重繪動作。如下動作會產生Repaint 動作:
不可見到可見(visibility 樣式屬性)
顏色或圖片變化(background, border-color, color 樣式屬性)
不改變頁面元素大小,形狀和位置,但改變其外觀的變化
Reflow 比起Repaint 來講就是一種更顯著的改變了。它主要發生在 DOM 樹被操作的時候,任何改變 DOM 的結構和佈局都會產生 Reflow。但當一個元素的 Reflow 操作發生時,它的所有父元素和子元素都會放生 Reflow,最後 Reflow 必然會導致 Repaint 的產生。舉例說明,以下動作會產生Repaint 動作:

瀏覽器視窗的變化
DOM 節點的新增刪除操作



複製程式碼



複製程式碼


複製程式碼
複製程式碼複製程式碼

複製程式碼



複製程式碼

var pDiv = document.createElement(“div”);
document.body.appendChild(pDiv);----- reflow
var cDiv1 = document.createElement(“ div”);
var cDiv2 = document.createElement(“div”); pDiv.appendChild(cDiv1);----- reflow pDiv.appendChild(cDiv2);----- reflow
這是我們常接觸的程式碼了,但這段程式碼會產生3 次reflow。再看以下程式碼: 清單17. 減少reflow

複製程式碼



複製程式碼



複製程式碼>

var pDiv = document.createElement(“div”);
var cDiv1 = document.createElement(“div”); var cDiv2 = document.createElement(“div”); var cDiv2 = document.createElement(“div”); 🎜>pDiv.appendChild(cDiv1); pDiv.appendChild(cDiv2); document.body.appendChild(pDiv);----- reflow 這裡便只有一次reflow,所以我們推薦這種DOM 節點操作的方式。 關於上述較少Reflow 操作的解決方案,還有一個可以參考的模式:清單18. 利用display 減少reflow 複製程式碼 程式碼如下: var pDiv = document.getElementById(“parent”); pDiv.style.display = “none”-🎜>pDiv.style.display = “none”-🎜>pDiv.style.display = “none”-🎜>pDiv.style.display = “none”-🎜>pDiv.style.display = “none”-🎜>pDiv.style.display = "none"- ---- reflow pDiv.appendChild(cDiv1); pDiv.appendChild(cDiv2); pDiv.appendChild(cDiv3); pDiv.appendChild(appendChild(cDiv3); pDiv.appendChild(cDiv4); appendChild(cDiv5); pDiv.style.width = “100px”; pDiv.style.height = “100px”; pDiv.style.display = “block”----- reflow
先隱藏pDiv,再顯示,這樣,隱藏和顯示之間的操作便不會產生任何的Reflow,提高了效率

特殊測量屬性和方法
DOM 元素裡面有一些特殊的測量屬性的存取和方法的調用,也會觸發Reflow,比較典型的就是「offsetWidth」屬性和「getComputedStyle」方法。
圖1. 特殊測量屬性和方法
web效能優化之javascript效能調優_javascript技巧 
這些測量屬性和方法大致有這些:
複製代碼 代碼如下:

offsetLeft
offsetTop
offsetHeight
HeoffsetWidth
scrollTop/Left/Width/Left/Width/Rft
getComputedStyle()
currentStyle(in IE))

這些屬性和方法的存取和調用,都會觸發Reflow 的產生,我們應該盡量減少對這些屬性和方法的訪問和調用,參考如下代碼:

清單19. 特殊測量屬性
複製代碼 代碼如下:
var pe = document.getElementById(“pos_element”);
var result = document.getElementById(“result_element”);
var pOffent.getElementById(“result_element”);
var pOffsetWidth = pepe. result.children[0].style.width = pOffsetWidth;
result.children[1].style.width = pOffsetWidth;
result.children[2].style.width = pOffsetWidth;

…………其他修改…………
這裡我們可以用臨時變數將「offsetWidth」的值快取起來,這樣就不用每次存取「offsetWidth」屬性。這種方式在循環裡面非常適用,可以大大提高效能。


樣式相關
我們一定常常看到如下的程式碼:
清單20. 樣式相關

複製程式碼 程式碼如下:
var sElement = document.getElementById(“pos_element”);
sElement.style.b =' 1px solid red '
sElement.style.backgroundColor = ' silver '
sElement.style.padding = ' 2px 3px '
sElement.style.marginLeft = ' 5px '





但可以看到,這裡的每一個樣式的改變,都會產生Reflow。需要減少這種情況的發生,我們可以這樣做:

複製程式碼


程式碼如下:


.class1 {
border: ' 1px solid red '
background-color: ' silver '
padding: ' 2px 3px '
margin-left: ' 5px '
} document.getElementById(“pos_element”).className = 'class1' ;


🎜>用class 取代style,可以將原有的所有Reflow 或Repaint 的次數都縮減到一個。


解決方案2

清單22. cssText 解決方案



> 程式碼如下: var sElement = document.getElementById(“pos_element”); var newStyle = ' border: 1px solid red; ' background-color; silver; '
' padding: 2px 3px; ' “margin-left: 5px;” sElement.style.cssText = newStyle; 一次設定所有樣式,也是減少Reflow提高性能的方法。 XPath 一個頁面上往往包含 1000 多頁面元素,在定位具體元素的時候,往往需要一定的時間。如果用 id 或 name 定位可能效率不會太慢,如果用元素的一些其他屬性(例如 className 等等)定位,可能效率有不理想了。有的可能只能透過遍歷所有元素(getElementsByTagName)然後過濾才能找到對應元素,這就更加低效了,這裡我們推薦使用 XPath 查找元素,這是許多瀏覽器本身支援的功能。 清單 23. XPath 解決方案 複製程式碼 程式碼

if(document.evaluate){
var tblHeaders = document.evaluate(“//body/div/table//th”);
var result = tblHeaders.iterateNext();
while(result) {
result.style.border = “1px dotted blue”;
result ………………
result = xpathResult.iterateNext();
}
}
} else{ //getElementsByTagName() ……
// 處理瀏覽器不支援XPath 的情況
………………………………
}


瀏覽器XPath 的搜尋引擎會優化搜尋效率,大幅縮短結果回傳時間。
HTMLCollection 物件

這是一類特殊的對象,它們有點像數組,但不完全是數組。下述方法的回傳值一般都是HTMLCollection 物件:
document.images, document.forms
getElementsByTagName()
getElementsByClassName()
這些HTMLCollection 物件並不是固定的值,而是固定的值,而是固定的值動態的結果。它們是一些比較特殊的查詢的回傳值,在下列情況下,它們會重新執行先前的查詢而得到新的回傳值(查詢結果),雖然多數情況下會和前一次或幾次的回傳值都一樣:
Length 屬性

具體的某個成員
所以,HTMLCollection 物件對這些屬性和成員的訪問,比起數組來慢很多。當然也有例外,Opera 和 Safari 對這種情況就處理的很好,不會有太大效能問題。
參考如下代碼: 清單24. HTMLConnection 對象
代碼如下:


var items = [“test1”, “test2”, “test3”, ……………… ];
for(var i = 0; i ………………………………
}
var items = document.getElementsByTagName(“div”);
for(var i = 0; i …………………………………… .
}


上述兩端代碼,下面的效率比起上面一段要慢很多,因為每個循環都會有「items.length」的觸發,也會導致「document.getElementsByTagName(..)」方法的再次調用,這便是效率便會大幅度下降的原因。我們可以這樣解決: 清單25. HTMLConnection 對象解決方案
代碼如下:


var items = document.getElementsByTagName(“div”);
var len = items.length
for(var i = 0; i …………………………………… .
}


這樣一來,效率基本上與普通數組一樣。
動態建立script 標籤
載入並執行一段JavaScript 腳本是需要一定時間的,在我們的程式中,有時候有些JavaScript 腳本被載入後基本上沒有被使用過(例如:腳本裡的函數從來沒有被調用等等)。載入這些腳本只會佔用 CPU 時間和增加記憶體消耗,降低 Web 應用的效能。所以推薦動態的載入 JavaScript 腳本文件,尤其是那些內容較多,消耗資源較大的腳本文件。 清單26. 建立script 標籤
程式碼
複製程式碼


程式碼

if(needXHR){
document.write(“
if(dojo.isIE){
document.write(“

--------- -------------------------------------------------- --------------------- 結束語 這篇文章介紹了Web 開發中關於效能方面需要注意的一些小細節,從JavaScript 本身著手,介紹了JavaScript 中需要避免的一些函數的使用和程式設計規則,例如eval 的弊端,function scope chain 以及String 的用法等等,也分享了一些比較推薦的做法,並擴展到JavaScript對DOM 操作的效能調優,例如利用Repaint 和Reflow 的機制,如何使用特殊測量屬性,樣式相關的效能調優以及HTMLCollection 物件的原理和使用小技巧。這些小細節我們可以在開發過程中盡量注意一下,以盡可能多的提升我們 Web 應用的效能。
陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
JavaScript:探索網絡語言的多功能性JavaScript:探索網絡語言的多功能性Apr 11, 2025 am 12:01 AM

JavaScript是現代Web開發的核心語言,因其多樣性和靈活性而廣泛應用。 1)前端開發:通過DOM操作和現代框架(如React、Vue.js、Angular)構建動態網頁和單頁面應用。 2)服務器端開發:Node.js利用非阻塞I/O模型處理高並發和實時應用。 3)移動和桌面應用開發:通過ReactNative和Electron實現跨平台開發,提高開發效率。

JavaScript的演變:當前的趨勢和未來前景JavaScript的演變:當前的趨勢和未來前景Apr 10, 2025 am 09:33 AM

JavaScript的最新趨勢包括TypeScript的崛起、現代框架和庫的流行以及WebAssembly的應用。未來前景涵蓋更強大的類型系統、服務器端JavaScript的發展、人工智能和機器學習的擴展以及物聯網和邊緣計算的潛力。

神秘的JavaScript:它的作用以及為什麼重要神秘的JavaScript:它的作用以及為什麼重要Apr 09, 2025 am 12:07 AM

JavaScript是現代Web開發的基石,它的主要功能包括事件驅動編程、動態內容生成和異步編程。 1)事件驅動編程允許網頁根據用戶操作動態變化。 2)動態內容生成使得頁面內容可以根據條件調整。 3)異步編程確保用戶界面不被阻塞。 JavaScript廣泛應用於網頁交互、單頁面應用和服務器端開發,極大地提升了用戶體驗和跨平台開發的靈活性。

Python還是JavaScript更好?Python還是JavaScript更好?Apr 06, 2025 am 12:14 AM

Python更适合数据科学和机器学习,JavaScript更适合前端和全栈开发。1.Python以简洁语法和丰富库生态著称,适用于数据分析和Web开发。2.JavaScript是前端开发核心,Node.js支持服务器端编程,适用于全栈开发。

如何安裝JavaScript?如何安裝JavaScript?Apr 05, 2025 am 12:16 AM

JavaScript不需要安裝,因為它已內置於現代瀏覽器中。你只需文本編輯器和瀏覽器即可開始使用。 1)在瀏覽器環境中,通過標籤嵌入HTML文件中運行。 2)在Node.js環境中,下載並安裝Node.js後,通過命令行運行JavaScript文件。

在Quartz中如何在任務開始前發送通知?在Quartz中如何在任務開始前發送通知?Apr 04, 2025 pm 09:24 PM

如何在Quartz中提前發送任務通知在使用Quartz定時器進行任務調度時,任務的執行時間是由cron表達式設定的。現�...

在JavaScript中,如何在構造函數中獲取原型鏈上函數的參數?在JavaScript中,如何在構造函數中獲取原型鏈上函數的參數?Apr 04, 2025 pm 09:21 PM

在JavaScript中如何獲取原型鏈上函數的參數在JavaScript編程中,理解和操作原型鏈上的函數參數是常見且重要的任�...

微信小程序webview中Vue.js動態style位移失效是什麼原因?微信小程序webview中Vue.js動態style位移失效是什麼原因?Apr 04, 2025 pm 09:18 PM

在微信小程序web-view中使用Vue.js動態style位移失效的原因分析在使用Vue.js...

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
3 週前By尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解鎖Myrise中的所有內容
3 週前By尊渡假赌尊渡假赌尊渡假赌

熱工具

Atom編輯器mac版下載

Atom編輯器mac版下載

最受歡迎的的開源編輯器

SAP NetWeaver Server Adapter for Eclipse

SAP NetWeaver Server Adapter for Eclipse

將Eclipse與SAP NetWeaver應用伺服器整合。

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

VSCode Windows 64位元 下載

VSCode Windows 64位元 下載

微軟推出的免費、功能強大的一款IDE編輯器

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

強大的PHP整合開發環境