JavaScript bietet keine Speicherverwaltungsoperationen. Stattdessen wird der Speicher von der JavaScript-VM durch einen Speicherrückgewinnungsprozess namens ?Garbage Collection“ verwaltet. [Verwandte Empfehlungen: Javascript-Lerntutorial]Woher wissen wir, dass die Speicherbereinigung ordnungsgem?? funktionieren kann, da wir sie nicht erzwingen k?nnen? Wie viel wissen wir darüber?
Die Skriptausführung wird w?hrend dieses Vorgangs angehalten- Es gibt Speicher für nicht zug?ngliche Ressourcen frei
- Es ist nicht deterministisch
- Es überprüft nicht den gesamten Speicher auf einmal, sondern wird in mehreren Zyklen ausgeführt
- Es ist nicht deterministisch pr?diktiv , aber es wird bei Bedarf ausgeführt.
- Bedeutet das, dass Sie sich über Probleme mit der Ressourcen- und Speicherzuweisung keine Sorgen machen müssen? Natürlich nicht. Wenn wir nicht aufpassen, kann es zu Speicherlecks kommen.
Ein Speicherverlust ist ein Block zugewiesenen Speichers, den die Software nicht zurückgewinnen kann.
Javascript bietet einen Garbage Collector, aber das bedeutet nicht, dass wir Speicherlecks vermeiden k?nnen. Um für die Garbage Collection in Frage zu kommen, darf das Objekt nicht an anderer Stelle referenziert werden. Wenn Sie Verweise auf ungenutzte Ressourcen enthalten, verhindert dies, dass diese Ressourcen zurückgefordert werden. Dies wird als ?unbewusste Erinnerungserhaltung“ bezeichnet.
Speicherverlust kann dazu führen, dass der Garbage Collector h?ufiger ausgeführt wird. Da dieser Vorgang die Ausführung des Skripts verhindert, kann es sein, dass unser Programm h?ngen bleibt. Wenn eine solche Verz?gerung auftritt, werden w?hlerische Benutzer definitiv bemerken, dass das Produkt für l?ngere Zeit nicht offline sein wird, wenn sie damit nicht zufrieden sind. Noch schlimmer ist, dass die gesamte Anwendung abstürzen kann, was gg ist. Wie kann man Speicherlecks verhindern? Die Hauptsache ist, dass wir vermeiden, unn?tige Ressourcen zu behalten. Schauen wir uns einige h?ufige Szenarien an.
1. Timer-überwachungDie Methode setInterval()
ruft wiederholt eine Funktion auf oder führt ein Codefragment aus, mit einer festen Zeitverz?gerung zwischen jedem Aufruf. Es gibt eine Intervall-ID
zurück, die das Intervall eindeutig identifiziert, sodass Sie es sp?ter durch Aufruf von clearInterval()
l?schen k?nnen. Wir erstellen eine Komponente, die eine Rückruffunktion aufruft, um anzuzeigen, dass sie nach x
Schleifen abgeschlossen ist. In diesem Beispiel verwende ich React, aber das funktioniert mit jedem FE-Framework.
import?React,?{?useRef?}?from?'react'; const?Timer?=?({?cicles,?onFinish?})?=>?{ ????const?currentCicles?=?useRef(0); ????setInterval(()?=>?{ ????????if?(currentCicles.current?>=?cicles)?{ ????????????onFinish(); ????????????return; ????????} ????????currentCicles.current++; ????},?500); ????return?( ????????<p>Loading?...</p> ????); } export?default?Timer;
Auf den ersten Blick scheint es kein Problem zu geben. Keine Sorge, erstellen wir eine weitere Komponente, die diesen Timer ausl?st, und analysieren wir ihre Speicherleistung. setInterval()
方法重復(fù)調(diào)用函數(shù)或執(zhí)行代碼片段,每次調(diào)用之間有固定的時間延遲。它返回一個時間間隔ID
,該ID
唯一地標(biāo)識時間間隔,因此您可以稍后通過調(diào)用 clearInterval()
來刪除它。
我們創(chuàng)建一個組件,它調(diào)用一個回調(diào)函數(shù)來表示它在x
個循環(huán)之后完成了。我在這個例子中使用React,但這適用于任何FE框架。
import?React,?{?useState?}?from?'react'; import?styles?from?'../styles/Home.module.css' import?Timer?from?'../components/Timer'; export?default?function?Home()?{ ????const?[showTimer,?setShowTimer]?=?useState(); ????const?onFinish?=?()?=>?setShowTimer(false); ????return?( ??????<p> ??????????{showTimer???( ??????????????<timer></timer> ??????????):?( ??????????????<button>?setShowTimer(true)}> ????????????????Retry ??????????????</button> ??????????)} ??????</p> ????) }
一看,好像沒啥問題。不急,我們再創(chuàng)建一個觸發(fā)這個定時器的組件,并分析其內(nèi)存性能。
useEffect(()?=>?{ ????const?intervalId?=?setInterval(()?=>?{ ????????if?(currentCicles.current?>=?cicles)?{ ????????????onFinish(); ????????????return; ????????} ????????currentCicles.current++; ????},?500); ????return?()?=>?clearInterval(intervalId); },?[])
在 Retry
按鈕上單擊幾次后,這是使用Chrome Dev Tools獲取內(nèi)存使用的結(jié)果:
當(dāng)我們點擊重試按鈕時,可以看到分配的內(nèi)存越來越多。這說明之前分配的內(nèi)存沒有被釋放。計時器仍然在運行而不是被替換。
怎么解決這個問題?setInterval
的返回值是一個間隔 ID,我們可以用它來取消這個間隔。在這種特殊情況下,我們可以在組件卸載后調(diào)用 clearInterval
。
import?{?useEffect?}?from?'react'; export?const?useTimeout?=?(refreshCycle?=?100,?callback)?=>?{ ????useEffect(()?=>?{ ????????if?(refreshCycle??{ ????????????callback(); ????????},?refreshCycle); ????????return?()?=>?clearInterval(intervalId); ????},?[refreshCycle,?setInterval,?clearInterval]); }; export?default?useTimeout;
有時,在編寫代碼時,很難發(fā)現(xiàn)這個問題,最好的方式,還是要把組件抽象化。
這里使用的是React,我們可以把所有這些邏輯都包裝在一個自定義的 Hook 中。
const?handleTimeout?=?()?=>?...; useTimeout(100,?handleTimeout);
現(xiàn)在需要使用setInterval
時,都可以這樣做:
function?homeShortcuts({?key})?{ ????if?(key?===?'E')?{ ????????console.log('edit?widget') ????} } //?用戶在主頁上登陸,我們執(zhí)行 document.addEventListener('keyup',?homeShortcuts);? //?用戶做一些事情,然后導(dǎo)航到設(shè)置 function?settingsShortcuts({?key})?{ ????if?(key?===?'E')?{ ????????console.log('edit?setting') ????} } //?用戶在主頁上登陸,我們執(zhí)行 document.addEventListener('keyup',?settingsShortcuts);
現(xiàn)在你可以使用這個useTimeout Hook
,而不必?fù)?dān)心內(nèi)存被泄露,這也是抽象化的好處。
2.事件監(jiān)聽
Web API提供了大量的事件監(jiān)聽器。在前面,我們討論了setTimeout
?,F(xiàn)在來看看 addEventListener
。
在這個事例中,我們創(chuàng)建一個鍵盤快捷鍵功能。由于我們在不同的頁面上有不同的功能,所以將創(chuàng)建不同的快捷鍵功能
document.removeEventListener(‘keyup’,?homeShortcuts);
看起來還是很好,除了在執(zhí)行第二個 addEventListener
時沒有清理之前的 keyup
。這段代碼不是替換我們的 keyup
監(jiān)聽器,而是將添加另一個 callback
。這意味著,當(dāng)一個鍵被按下時,它將觸發(fā)兩個函數(shù)。
要清除之前的回調(diào),我們需要使用 removeEventListener
function?homeShortcuts({?key})?{ ????if?(key?===?'E')?{ ????????console.log('edit?widget') ????} } //?user?lands?on?home?and?we?execute document.addEventListener('keyup',?homeShortcuts);? //?user?does?some?stuff?and?navigates?to?settings function?settingsShortcuts({?key})?{ ????if?(key?===?'E')?{ ????????console.log('edit?setting') ????} } //?user?lands?on?home?and?we?execute document.removeEventListener('keyup',?homeShortcuts);? document.addEventListener('keyup',?settingsShortcuts);Nach ein paar Klicks auf die Schaltfl?che
Wiederholen
ist dies das Ergebnis der Verwendung von Chrome Dev Tools zum Ermitteln der Speichernutzung:

Wie kann dieses Problem gel?st werden? Der Rückgabewert von setInterval
ist eine Intervall-ID, mit der wir das Intervall abbrechen k?nnen. In diesem speziellen Fall k?nnen wir clearInterval
aufrufen, nachdem die Komponente entladen wurde. const?ref?=?...
const?visible?=?(visible)?=>?{
??console.log(`It?is?${visible}`);
}
useEffect(()?=>?{
????if?(!ref)?{
????????return;
????}
????observer.current?=?new?IntersectionObserver(
????????(entries)?=>?{
????????????if?(!entries[0].isIntersecting)?{
????????????????visible(true);
????????????}?else?{
????????????????visbile(false);
????????????}
????????},
????????{?rootMargin:?`-${header.height}px`?},
????);
????observer.current.observe(ref);
},?[ref]);
Manchmal ist es schwierig, dieses Problem beim Schreiben von Code zu finden. Der beste Weg ist, die Komponenten zu abstrahieren. React wird hier verwendet, und wir k?nnen diese gesamte Logik in einen benutzerdefinierten Hook einbinden.
const?ref?=?... const?visible?=?(visible)?=>?{ ??console.log(`It?is?${visible}`); } useEffect(()?=>?{ ????if?(!ref)?{ ????????return; ????} ????observer.current?=?new?IntersectionObserver( ????????(entries)?=>?{ ????????????if?(!entries[0].isIntersecting)?{ ????????????????visible(true); ????????????}?else?{ ????????????????visbile(false); ????????????} ????????}, ????????{?rootMargin:?`-${header.height}px`?}, ????); ????observer.current.observe(ref); ????return?()?=>?observer.current?.disconnect(); },?[ref]);Jetzt k?nnen Sie Folgendes tun, wann immer Sie
setInterval
verwenden müssen: ??function?addElement(element)?{ ????if?(!this.stack)?{ ????????this.stack?=?{ ????????????elements:?[] ????????} ????} ????this.stack.elements.push(element); }??Jetzt k?nnen Sie diesen
useTimeout Hook
verwenden, ohne sich Sorgen machen zu müssen, dass Speicher verloren geht Abstraktionsvorteile. ??????2. Ereignis-Listener??????Die Web-API bietet eine gro?e Anzahl von Ereignis-Listenern. Zuvor haben wir setTimeout
besprochen. Schauen wir uns nun addEventListener
an. ????In diesem Beispiel erstellen wir eine Tastenkombinationsfunktion. Da wir unterschiedliche Funktionen auf verschiedenen Seiten haben, werden unterschiedliche Tastenkombinationsfunktionen erstellt ??var?a?=?'example?1';?//?作用域限定在創(chuàng)建var的地方 b?=?'example?2';?//?添加到Window對象中?? Sieht immer noch gut aus, au?er dass der vorherige
addEventListener
>keyupausgeführt wird >. Anstatt unseren keyup
-Listener zu ersetzen, fügt dieser Code einen weiteren callback
hinzu. Das bedeutet, dass beim Drücken einer Taste zwei Funktionen ausgel?st werden. ????Um den vorherigen Rückruf zu l?schen, müssen wir removeEventListener
verwenden: ??"use?strict"??Den obigen Code umgestalten: ??
Uncaught?ReferenceError:?b?is?not?defined?? Erfahrungsgem?? müssen Sie bei der Verwendung von Tools aus globalen Objekten sehr vorsichtig sein. ??????3.Observers????????Observers?? ist eine Browser-Web-API-Funktion, die viele Entwickler nicht kennen. Dies ist hilfreich, wenn Sie nach ?nderungen in der Sichtbarkeit oder Gr??e von HTML-Elementen suchen m?chten. ??
IntersectionObserver
接口 (從屬于Intersection Observer API) 提供了一種異步觀察目標(biāo)元素與其祖先元素或頂級文檔視窗(viewport
)交叉狀態(tài)的方法。祖先元素與視窗(viewport
)被稱為根(root
)。
盡管它很強大,但我們也要謹(jǐn)慎的使用它。一旦完成了對對象的觀察,就要記得在不用的時候取消它。
看看代碼:
const?ref?=?... const?visible?=?(visible)?=>?{ ??console.log(`It?is?${visible}`); } useEffect(()?=>?{ ????if?(!ref)?{ ????????return; ????} ????observer.current?=?new?IntersectionObserver( ????????(entries)?=>?{ ????????????if?(!entries[0].isIntersecting)?{ ????????????????visible(true); ????????????}?else?{ ????????????????visbile(false); ????????????} ????????}, ????????{?rootMargin:?`-${header.height}px`?}, ????); ????observer.current.observe(ref); },?[ref]);
上面的代碼看起來不錯。然而,一旦組件被卸載,觀察者會發(fā)生什么?它不會被清除,那內(nèi)存可就泄漏了。我們怎么解決這個問題呢?只需要使用 disconnect
方法:
const?ref?=?... const?visible?=?(visible)?=>?{ ??console.log(`It?is?${visible}`); } useEffect(()?=>?{ ????if?(!ref)?{ ????????return; ????} ????observer.current?=?new?IntersectionObserver( ????????(entries)?=>?{ ????????????if?(!entries[0].isIntersecting)?{ ????????????????visible(true); ????????????}?else?{ ????????????????visbile(false); ????????????} ????????}, ????????{?rootMargin:?`-${header.height}px`?}, ????); ????observer.current.observe(ref); ????return?()?=>?observer.current?.disconnect(); },?[ref]);
4. Window Object
向 Window 添加對象是一個常見的錯誤。在某些場景中,可能很難找到它,特別是在使用 Window Execution上下文中的this
關(guān)鍵字。看看下面的例子:
function?addElement(element)?{ ????if?(!this.stack)?{ ????????this.stack?=?{ ????????????elements:?[] ????????} ????} ????this.stack.elements.push(element); }
它看起來無害,但這取決于你從哪個上下文調(diào)用addElement
。如果你從Window Context調(diào)用addElement,那就會越堆越多。
另一個問題可能是錯誤地定義了一個全局變量:
var?a?=?'example?1';?//?作用域限定在創(chuàng)建var的地方 b?=?'example?2';?//?添加到Window對象中
要防止這種問題可以使用嚴(yán)格模式:
"use?strict"
通過使用嚴(yán)格模式,向JavaScript編譯器暗示,你想保護(hù)自己免受這些行為的影響。當(dāng)你需要時,你仍然可以使用Window。不過,你必須以明確的方式使用它。
嚴(yán)格模式是如何影響我們前面的例子:
- 對于
addElement
函數(shù),當(dāng)從全局作用域調(diào)用時,this
是未定義的 - 如果沒有在一個變量上指定
const | let | var
,你會得到以下錯誤:
Uncaught?ReferenceError:?b?is?not?defined
5. 持有DOM引用
DOM節(jié)點也不能避免內(nèi)存泄漏。我們需要注意不要保存它們的引用。否則,垃圾回收器將無法清理它們,因為它們?nèi)匀皇强稍L問的。
用一小段代碼演示一下:
const?elements?=?[]; const?list?=?document.getElementById('list'); function?addElement()?{ ????//?clean?nodes ????list.innerHTML?=?''; ????const?pElement=?document.createElement('p'); ????const?element?=?document.createTextNode(`adding?element?${elements.length}`); ????pElement.appendChild(element); ????list.appendChild(pElement); ????elements.push(pElement); } document.getElementById('addElement').onclick?=?addElement;
注意,addElement
函數(shù)清除列表 p
,并將一個新元素作為子元素添加到它中。這個新創(chuàng)建的元素被添加到 elements
數(shù)組中。
下一次執(zhí)行 addElement
時,該元素將從列表 p
中刪除,但是它不適合進(jìn)行垃圾收集,因為它存儲在 elements
數(shù)組中。
我們在執(zhí)行幾次之后監(jiān)視函數(shù):
在上面的截圖中看到節(jié)點是如何被泄露的。那怎么解決這個問題?清除 elements
?數(shù)組將使它們有資格進(jìn)行垃圾收集。
總結(jié)
在這篇文章中,我們已經(jīng)看到了最常見的內(nèi)存泄露方式。很明顯,JavaScript本身并沒有泄漏內(nèi)存。相反,它是由開發(fā)者方面無意的內(nèi)存保持造成的。只要代碼是整潔的,而且我們不忘自己清理,就不會發(fā)生泄漏。
了解內(nèi)存和垃圾回收在JavaScript中是如何工作的是必須的。一些開發(fā)者得到了錯誤的意識,認(rèn)為由于它是自動的,所以他們不需要擔(dān)心這個問題。
作者: Jose Granja?
原文:https://betterprogramming.pub/5-common-javascript-memory-mistakes-c8553972e4c2
(學(xué)習(xí)視頻分享:web前端)
Das obige ist der detaillierte Inhalt von5 h?ufige JavaScript-Speicherfehler. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Hei?e KI -Werkzeuge

Undress AI Tool
Ausziehbilder kostenlos

Undresser.AI Undress
KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover
Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Clothoff.io
KI-Kleiderentferner

Video Face Swap
Tauschen Sie Gesichter in jedem Video mühelos mit unserem v?llig kostenlosen KI-Gesichtstausch-Tool aus!

Hei?er Artikel

Hei?e Werkzeuge

Notepad++7.3.1
Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version
Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1
Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6
Visuelle Webentwicklungstools

SublimeText3 Mac-Version
Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

PHP und Vue: eine perfekte Kombination von Front-End-Entwicklungstools In der heutigen Zeit der rasanten Entwicklung des Internets ist die Front-End-Entwicklung immer wichtiger geworden. Da Benutzer immer h?here Anforderungen an das Erlebnis von Websites und Anwendungen stellen, müssen Frontend-Entwickler effizientere und flexiblere Tools verwenden, um reaktionsf?hige und interaktive Schnittstellen zu erstellen. Als zwei wichtige Technologien im Bereich der Front-End-Entwicklung k?nnen PHP und Vue.js in Kombination als perfekte Waffe bezeichnet werden. In diesem Artikel geht es um die Kombination von PHP und Vue sowie um detaillierte Codebeispiele, die den Lesern helfen sollen, diese beiden besser zu verstehen und anzuwenden

Als schnelle und effiziente Programmiersprache erfreut sich Go im Bereich der Backend-Entwicklung gro?er Beliebtheit. Allerdings assoziieren nur wenige Menschen die Go-Sprache mit der Front-End-Entwicklung. Tats?chlich kann die Verwendung der Go-Sprache für die Front-End-Entwicklung nicht nur die Effizienz verbessern, sondern Entwicklern auch neue Horizonte er?ffnen. In diesem Artikel wird die M?glichkeit der Verwendung der Go-Sprache für die Front-End-Entwicklung untersucht und spezifische Codebeispiele bereitgestellt, um den Lesern ein besseres Verst?ndnis dieses Bereichs zu erleichtern. In der traditionellen Frontend-Entwicklung werden h?ufig JavaScript, HTML und CSS zum Erstellen von Benutzeroberfl?chen verwendet

Django ist ein in Python geschriebenes Webanwendungs-Framework, das Wert auf schnelle Entwicklung und saubere Methoden legt. Obwohl Django ein Web-Framework ist, müssen Sie zur Beantwortung der Frage, ob Django ein Front-End oder ein Back-End ist, ein tiefes Verst?ndnis der Konzepte von Front-End und Back-End haben. Das Front-End bezieht sich auf die Schnittstelle, mit der Benutzer direkt interagieren, und das Back-End bezieht sich auf serverseitige Programme. Sie interagieren mit Daten über das HTTP-Protokoll. Wenn das Front-End und das Back-End getrennt sind, k?nnen die Front-End- und Back-End-Programme unabh?ngig voneinander entwickelt werden, um Gesch?ftslogik bzw. interaktive Effekte sowie den Datenaustausch zu implementieren.

In Front-End-Entwicklungsinterviews decken h?ufige Fragen ein breites Themenspektrum ab, darunter HTML/CSS-Grundlagen, JavaScript-Grundlagen, Frameworks und Bibliotheken, Projekterfahrung, Algorithmen und Datenstrukturen, Leistungsoptimierung, dom?nenübergreifende Anfragen, Front-End-Engineering, Designmuster sowie neue Technologien und Trends. Interviewerfragen sollen die technischen F?higkeiten, die Projekterfahrung und das Verst?ndnis des Kandidaten für Branchentrends beurteilen. Daher sollten Kandidaten in diesen Bereichen umfassend vorbereitet sein, um ihre F?higkeiten und Fachkenntnisse unter Beweis zu stellen.

Django: Ein magisches Framework, das sowohl Front-End- als auch Back-End-Entwicklung bew?ltigen kann! Django ist ein effizientes und skalierbares Webanwendungs-Framework. Es unterstützt mehrere Webentwicklungsmodelle, einschlie?lich MVC und MTV, und kann problemlos hochwertige Webanwendungen entwickeln. Django unterstützt nicht nur die Back-End-Entwicklung, sondern kann auch schnell Front-End-Schnittstellen erstellen und durch die Vorlagensprache eine flexible Ansichtsanzeige erreichen. Django kombiniert Front-End-Entwicklung und Back-End-Entwicklung zu einer nahtlosen Integration, sodass sich Entwickler nicht auf das Lernen spezialisieren müssen

Kombination von Golang und Front-End-Technologie: Um zu untersuchen, welche Rolle Golang im Front-End-Bereich spielt, sind spezifische Codebeispiele erforderlich. Mit der rasanten Entwicklung des Internets und mobiler Anwendungen ist die Front-End-Technologie immer wichtiger geworden. Auch in diesem Bereich kann Golang als leistungsstarke Back-End-Programmiersprache eine wichtige Rolle spielen. In diesem Artikel wird untersucht, wie Golang mit Front-End-Technologie kombiniert wird, und sein Potenzial im Front-End-Bereich anhand spezifischer Codebeispiele demonstriert. Die Rolle von Golang im Front-End-Bereich ist effizient, pr?gnant und leicht zu erlernen

Zu den Hauptfunktionen von React geh?ren komponentiertes Denken, Staatsmanagement und virtuelles DOM. 1) Die Idee der Komponentierung erm?glicht es, die Benutzeroberfl?che in wiederverwendbare Teile aufzuteilen, um die Lesbarkeit und Wartbarkeit der Code zu verbessern. 2) Das staatliche Management verwaltet dynamische Daten durch Status und Requisiten und ?ndert sich ausl?sen UI -Updates. 3) Aktualisieren Sie die Benutzeroberfl?che virtuelle DOM -Optimierungsleistung durch die Berechnung des Mindestbetriebs der DOM -Replik im Speicher.

Der Unterschied: Das Frontend ist für das Design und die Interaktion der Benutzeroberfl?che verantwortlich, einschlie?lich Webseiten, Bildern und anderen Inhalten, die Benutzer im Browser sehen. Das Backend ist für die Datenverarbeitung und die Durchführung logischer Vorg?nge verantwortlich, einschlie?lich serverseitiger Skripte, Datenbanken und APIs.
