深入瞭解JavaScript的記憶體與效能問題

2022-03-31 13:00:52
本篇文章給大家帶來了關於的相關知識,其中主要介紹了JavaScript的記憶體與效能問題,包括瞭如何解決類似按鈕過多的問題、刪除事件處理程式等等,希望對大家有幫助。

相關推薦:

一、何為JavaScript記憶體與效能

因為事件處理程式在現代web應用中可以實現互動,所以很多開發者都會錯誤地在頁面中大量使用它們,在JavaScript中,頁面中事件處理程式的數量與頁面整體效能直接相關。原因有很多,比如①每個函數都是物件,都要佔用記憶體空間,物件越多,效能越差;②為指定事件處理程式所需存取DOM的次數會先造成整個頁面互動的延遲。

二、談談關於innerHTML的效能問題?

1、使用innerHTML的反面教材

for(let value of values){
	ul.innerHTML += '<li>${value}</li>';}

這段程式碼效率低,因為每次迭代都要設定一次innerHTML,不僅如此,每次迴圈都要先讀取innerHTML,也就是說一次迴圈要存取兩次innerHTML。

2、如何解

let itemsHtml = "";for(let value of values){
	itemsHtml  += '<li>${value}</li>';}ul.innerHTML = itemsHtml;

這樣修改之後,效率就高多了,只會對innerHTML進行一次賦值,下面程式碼也可以搞定:
ul.innerHTML = values.map(value => '<li>${value}</li>').join(' ');

三、如何解決類似按鈕過多問題?

過多事件處理程式的解決方案是使用事件委託。事件委託利用事件冒泡,可以只使用一個事件處理程式來管理一種型別的事件。例如,click事件冒泡到document。這意味著可以為整個頁面指定一個onclick事件處理程式,而不是為每個可點選元素分別指定事件處理程式。

<ul id="myGirls">
	<li id="girl1">比比東</li>
	<li id="girl2">雲韻</li>
	<li id="girl3">美杜莎</li></ul>

這裡包含三個列表項,在被點選時應該執行某個操作,通常的方式是指定三個事件處理程式:

let item1 = document.getElementById("girl1");let item2 = document.getElementById("girl2");let item3 = document.getElementById("girl3");item1.addEventListener("click",(event) => {
	console.log("我是比比東!");})item2.addEventListener("click",(event) => {
	console.log("我是雲韻!");})item3.addEventListener("click",(event) => {
	console.log("我是美杜莎!");})

相同程式碼太多,程式碼過於醜陋了。
使用事件委託,只要給多有元素的共同的祖先節點新增一個事件處理程式,就可以解決醜陋!

let list = document.getElementById("myGirls");list.addEventListener("click",(event) => {
	let target = event.target;
	switch(target.id){
		case "girl1":
			console.log("我是比比東!");
			break;
		case "girl2":
			console.log("我是雲韻!");
			break;
		case "girl3":
			console.log("我是美杜莎!");
			break;
	}})

四、事件委託的優點有哪些?

  • document物件隨時可用,任何時候都可以為它新增一個事件處理程式(不用等待DOMContentLoaded或load事件),通過它處理頁面中所有某種型別的事件。這意味著只要頁面渲染出可點選的元素,就可以無延遲的起作用。

  • 節省花在設定頁面事件程式上的事件。

  • 減少整個頁面所需的記憶體,提升整體效能。

五、刪除事件處理程式

把事件處理程式指定給元素後,在瀏覽器程式碼和負責頁面互動的JavaScript程式碼之間就建立了聯絡。這種聯絡簡歷越多,頁面效能就越差。除了通過事件委託來限制這種連線之外,還應該及時刪除不用的事件處理程式。很多web應用效能不佳都是由於無用的事件處理程式長駐記憶體導致的。
導致這個問題的原因有兩個:

1、刪除帶有事件處理程式的元素

比如通過的DOM方法removeChild()或replaceChild()刪除節點。最常見的還是使用innerHTML整體替換頁面的某一部分。這時候,被innerHTML刪除的元素上如果有事件處理程式,也不會被垃圾收集程式正常清理。
所以,如果在得知某個元素會被刪除之前,應手動刪除它的事件處理程式,比如btn.onclick = null;//刪除事件處理程式,事件委託也有助於解決這個問題,如果得知某個元素要被innerHTML替代的時候,就不要給該元素新增事件處理程式了,將其新增到更高層級的節點上即可。

2、頁面解除安裝也會導致記憶體中殘留參照的問題

如果在頁面解除安裝後事件處理程式沒有被清理,則它們仍然會殘留在記憶體中。之後,瀏覽器每次載入和解除安裝頁面(比如通過前進、後退或重新整理),記憶體中殘留物件的數量都會增加,這是因為事件處理程式不會被回收。
一般來說,最好在onunload事件處理程式中趁頁面尚未解除安裝先刪除所有事件處理程式。這時候也能體現出事件委託的優勢,因為事件處理程式少,所以容易記住刪除哪些。

六、如何解決迴圈中動態新增p,造成的死迴圈問題?

表示式

let ps = document.getElementsByTagName("p");for(let i = 0;i<ps.length;++i){
	let p = document.createElement("p");
	document.body.appendChild(p);}

表示式

let ps = document.getElementsByTagName("p");for(let i = 0,len=ps.length;i<len;++i){
	let p = document.createElement("p");
	document.body.appendChild(p);}

表示式①中第一行取得了包含檔案中所有<p>元素的HTMLCollection。因為這個集合是實時的,所以任何時候只要向頁面中新增一個新的<p>元素,再查詢這個集合就會多一項。因為瀏覽器不希望儲存每次建立的集合,所以就會在每次存取時更新集合。每次迴圈都會求值i < ps.length,這意味著要獲取所有<p>元素的查詢。因為迴圈體中建立並向檔案中新增一個新的<p>元素,所以每次迴圈ps.length的值也會遞增。因為兩個值都會遞增,所以i永遠不會等於ps.length,因此表示式①會造成死迴圈。
而表示式②中,又初始化了一個儲存集合長度的變數len,因為len儲存著迴圈開始集合的長度,而這個值不會隨集合增大動態增長(for迴圈中初始化變數處只會初始化一次),所以就可以避免表示式①中出現的無窮迴圈問題。
如果不想初始化一個變數,也可以使用反向迭代:

表示式

let ps = document.getElementsByTagName("p");for(let i = ps.length-1;i>=0;--i){
	let p = document.createElement("p");
	document.body.appendChild(p);}

七、JavaScript思維導圖

在這裡插入圖片描述

相關推薦:

以上就是深入瞭解JavaScript的記憶體與效能問題的詳細內容,更多請關注TW511.COM其它相關文章!