Heim >Web-Frontend >js-Tutorial >Wie funktioniert JavaScript unter der Haube?
Dieser Beitrag zielt wirklich darauf ab, auf einfache Weise zu erklären, wie JavaScript unter der Haube funktioniert, sodass auch ein neuer Programmierer in der Lage ist, das Konzept zu verstehen und zu visualisieren, was beim Codieren von JavaScript passiert.
Zu Beginn möchte ich mich auf mindestens drei Fragen konzentrieren, die es mir erleichtern, Schwierigkeiten zu überwinden und die Logik dahinter zu verinnerlichen?
Diese Fragen werden wahrscheinlich auch bei Vorstellungsgesprächen für Webentwickler gestellt, bei denen JavaScript Folgendes impliziert:
1. Wie funktioniert JavaScript?
2. Erklären Sie den Unterschied zwischen synchron und asynchron?
3. Oder erläutern Sie diese Aussage: JavaScript ist eine Single-Threaded-Sprache, die nicht blockierend sein kann?
Wirklich, es ist nicht notwendig zu wissen, wie JavaScript intern funktioniert, um ein Programm zu schreiben, aber es ist wichtig und entscheidend zu lernen, um zu verstehen, was dahinter passiert, und um zu spüren, was man schreibt, daher einige der vielen Entwickler, die über viele Jahre verfügen Im Träger ist es mir egal.
Lassen Sie uns zunächst wissen, Was ist ein Programm? Ein Programm ist einfach eine Reihe von Anweisungen, die einem Computer sagen, was er tun und wie er die Aufgabe ausführen soll. Das Programm muss den Speicher zuweisen, andernfalls können wir keine Variablen haben oder gar keine Dateien auf unseren Computern speichern. Das Programm sollte auch eine dedizierte Aufgabe analysieren (lesen) und ausführen, und alles geschieht in einem Speicher.
Jetzt verfügt JavaScript über eine Engine namens JavaScript-Engine, die jeder Browser implementiert. In Chrome heißt es beispielsweise V8, in Mozilla Firefox: Spider Monkey, im Safari-Browser: JavaScript Core Webkit.
Das folgende Bild zeigt den V8-Motor von Google Chrome
Was passiert in der JavaScript Engine?
JavaScript-Engine wie V8 in Chrome liest von uns geschriebene JavaScript-Codes und wandelt sie in maschinenausführbare Anweisungen für den Browser um. Die obige Abbildung zeigt Teile der JavaScript-Engine, die aus zwei Teilen besteht, nämlich einem Speicherheap **und **einem Aufrufstapel.
Es ist auch wichtig zu beachten, dass die Speicherzuweisung im Speicherheap erfolgt, während das Parsen (Lesen) und die Ausführung im Aufrufstapel stattfinden. Darüber hinaus ist es der Speicherheap, der Ihnen sagt, wo Sie sich im Programm befinden.
Sehen wir uns die Speicherzuordnung im Speicherheap mit JS-Codes (JavaScript) an
const a = 4; // now we allocated a memory. JS engine is going to remember // that a has a value of 4. const Obj = {a, b, c }; // In memory, variable 'Obj' holds the object {a, b,c} // The same as on array. the engine will remember values of the array const Array = [1,2,3,4,5]
Was ist also ein Problem mit dem oben genannten Code, nachdem er global deklariert wurde?
Es gibt etwas namens Memory Leak. Wie oben erwähnt, erfolgt die Variablendeklaration im Speicher-Heap und die zuzuordnende Größe ist begrenzt. Wenn Sie weiterhin globale Variablen deklarieren, bei denen es sich um sehr große Arrays anstelle von Zahlen handelt und die sogar ungenutzt sind, füllt dies den Speicher und führt zum Speicherleck. Sie werden hören, dass globale Variablen schlecht sind, denn wenn wir vergessen, sie zu bereinigen, füllen wir diesen Speicherhaufen und der Browser wird irgendwann nicht mehr funktionieren.
Was ist mit dem Call Stack?
Wenn wir uns erinnern, ist es der Call Stack, der Skripte liest und ausführt. Verwenden wir Codes, um es klarer zu machen.
const a = 4; // now we allocated a memory. JS engine is going to remember // that a has a value of 4. const Obj = {a, b, c }; // In memory, variable 'Obj' holds the object {a, b,c} // The same as on array. the engine will remember values of the array const Array = [1,2,3,4,5]
Mit den oben genannten Codes liest der Call Sack die erste Zeile console.log(“x”); und in den Aufrufstapel gestellt wird, erkennt die JavaScript-Engine, dass das console.log hinzugefügt wurde, fügt es dann in den Aufrufstapel ein, führt es aus und gibt x aus. Anschließend wird das erste console.log nach Abschluss der Ausführung entfernt und im zweiten console.log(„y“) platziert und zum Aufrufstapel hinzugefügt. Führen Sie y aus und entfernen Sie das zweite console.log. Ruft schließlich console.log(„z“) mit demselben Prozess ab.
Dies ist die einfachste Demo. Was wäre, wenn es sich um ein etwas komplexeres Beispiel handelt? Geben wir ein typisches Beispiel:
// Example Call Stak console.log("x"); console.log("y"); console.log("z"); // Result in browser // x // y // z
Was ist nun laut Aufrufstapel im obigen Code passiert? Mal sehen, wie der obige Codeblock ausgeführt wird:
//CALL STACK
Funktion example1() wird zuerst ausgeführt, dann kommt Funktion example2() oben auf den Aufrufstapel und wird ausgeführt, wodurch Nummer 7 als Ausgabe ausgegeben wird, nachdem überprüft wurde, ob dies der Fall ist andere Codes zum Ausführen. Danach wird mit dem Entfernen aus dem Aufrufstapel begonnen, beginnend mit console.log(‘7’), example2(), example1() und der Aufrufstapel ist jetzt leer.
> Erinnern wir uns an diese Aussage? JavaScript ist eine Single-Threaded-Sprache, die nicht blockierend sein kann.
Single-Threaded bedeutet, dass es nur einen Aufrufstapel hat. Es kann jeweils nur eine Aufgabe ausgeführt werden, und es ist wichtig zu betonen, dass der Anrufstapel wie ein Stapel „First In Last Out“ ist.
Andere Sprachen können viele Aufrufstapel haben, was als Multi-Threaded bezeichnet wird, was vorteilhafter sein könnte, wenn mehrere Aufrufstapel vorhanden sind, damit wir nicht ständig auf Aufgaben warten müssen.
> Aber warum wurde JavaScript eigentlich für Single-Threading konzipiert?
Um diese Frage zu beantworten: Normalerweise kann das Ausführen von Code in einem einzelnen Thread recht einfach sein, da in einer Multithread-Umgebung keine komplizierten Szenarien auftreten. Sie müssen sich tatsächlich um eines kümmern. Bei Multithreading kann es zu Problemen wie Deadlocks kommen. Mit dieser Theorie können wir leicht erkennen, was eine synchrone Programmierung bedeutet.
Synchronisierte Programmierung bedeutet einfach: Die erste Codezeile wird ausgeführt, die zweite folgt und die dritte wird ausgeführt usw. …
Um es genauer auszudrücken bedeutet dies, dass console.log(„y“) nicht ausgeführt werden kann, bis console.log(“x“) abgeschlossen ist und console.log („z“) beginnt erst, wenn beide ersten beiden beendet sind, da es sich um einen Call Stack handelt.
Es ist wahrscheinlich, dass Programmierer die Website stackoverflow.com nutzen. was bedeutet der Name? Also. Mal sehen:
Wie es zu einem Stapelüberlauf kommt
Das obige Bild zeigt, wie ein Speicherverlust auftreten kann und wie der Speicherheap einer JavaScript-Engine überlaufen kann. Hier empfängt der Aufrufstapel viele Eingaben, die größer sind als seine Größe, und es kommt zu Überläufen.
Es ist möglich, den Stapelüberlauf mit Hilfe des Codes zu demonstrieren:
const a = 4; // now we allocated a memory. JS engine is going to remember // that a has a value of 4. const Obj = {a, b, c }; // In memory, variable 'Obj' holds the object {a, b,c} // The same as on array. the engine will remember values of the array const Array = [1,2,3,4,5]
Beachten Sie, dass JavaScript ein Single-Thread ist und immer nur eine Anweisung gleichzeitig ausgeführt wird. Hier ist jetzt ein Problem: Was passiert, wenn console.log(„y“) im folgenden Codeblock eine große Aufgabe hat, deren Ausführung länger dauern wird? zum Beispiel ausgeführt, indem ein Array durchlaufen wird, das Tausende oder Millionen von Elementen enthält? was würde da passieren?
// Example Call Stak console.log("x"); console.log("y"); console.log("z"); // Result in browser // x // y // z
Die erste Zeile wird ausgeführt und es wird davon ausgegangen, dass die zweite Zeile eine gewaltige Aufgabe zu erfüllen hat. Daher wird die dritte Zeile lange auf die Ausführung warten. Im obigen Beispiel bedeutet das nicht viel, aber stellen wir uns eine große Website vor, die umfangreiche Vorgänge ausführt, und der Benutzer könnte nichts tun. Die Website bleibt stehen, bis die Aufgabe erledigt ist und der Benutzer dort wartet. Das ist eine schlechte Erfahrung, wenn es um die Leistung geht.
Nun, wenn wir bei synchronen Aufgaben eine Funktion haben, die viel Zeit in Anspruch nimmt, wird sie die Linie aufhalten. Es hört sich also so an, als ob wir etwas brauchen, das nicht blockiert. Denken Sie an die Aussage, die ich oben erwähnt habe: JavaScript ist eine Single-Threaded-Sprache, die nicht blockierend sein kann.
Im Idealfall warten wir in JavaScript nicht auf Dinge, die Zeit brauchen. Wie können wir dieses Problem umgehen?
Zur Rettung gibt es Asynchrone Programmierung. Also, was ist das?
Stellen Sie sich Asynchronität wie ein Verhalten vor. Die synchrone Ausführung ist großartig, weil sie vorhersehbar ist. Bei der Synchronisierung wissen wir, was zuerst passiert, was als nächstes passiert usw., aber es kann langsam werden.
Wenn wir Dinge wie Bildverarbeitung oder Anfragen über das Netzwerk wie API-Aufrufe ausführen müssen, verwenden wir etwas mehr als nur synchrone Aufgaben, nämlich Asynchron.
Sehen wir uns an, wie wir asynchrone Programmierung mit Codes durchführen können:
const a = 4; // now we allocated a memory. JS engine is going to remember // that a has a value of 4. const Obj = {a, b, c }; // In memory, variable 'Obj' holds the object {a, b,c} // The same as on array. the engine will remember values of the array const Array = [1,2,3,4,5]
Basierend auf den obigen Codes scheint es, als hätten wir die zweite Zeile übersprungen und die dritte ausgeführt und 3 Sekunden gewartet, um das Ergebnis auszugeben. das ist ein asynchrones Geschehen.
Um dies und die Ereignisse zu verstehen, verwenden wir die folgende Abbildung.
JavaScript-Laufzeitumgebung
Damit JavaScript ausgeführt werden kann, benötigen wir mehr als Memory Heap und Call Stack. Wir benötigen etwas namens JavaScript Run-Time, das Teil des Browsers ist. Es ist in Browsern enthalten. Oben auf der Engine gibt es etwas, das Web-APIs, Rückrufwarteschlange und Ereignisschleife genannt wird, wie in der Abbildung gezeigt.
Lassen Sie uns jetzt den Code diskutieren, in dem wir die Funktion setTimeout verwenden.
// Example Call Stak console.log("x"); console.log("y"); console.log("z"); // Result in browser // x // y // z
Die setTimeout-Funktion ist Teil der Web-API und nicht Teil von JavaScript, sondern wird uns von Browsern zur Verfügung gestellt, damit wir asynchrone Programmierung durchführen können. Lassen Sie uns also zur Klarstellung weitere Details bereitstellen.
CALL STACK: Das console.log(„x“) geht in den Callstack, wird ausgeführt und wir console.log an den Browser. Danach wird setTimeout(() =>{console.log(“y”);},3000); geht in den Aufrufstapel, weil die erste Aufgabe abgeschlossen ist, und gehe dann zur zweiten.
Hier gibt es nun eine Sache: Beim Lesen des Codes erkennt der Aufrufstapel, dass eine setTimeout-Funktion festgelegt wurde und nicht Teil von JavaScript, sondern Teil der Web-API ist ( Siehe die Abbildung JavaScript Run-Time Environment (JavaScript-Laufzeitumgebung) und hat besondere Eigenschaften. Was passiert, ist, dass setTimeout die WEB-API auslöst und da die Web-API benachrichtigt wird, wird die Funktion aus dem Aufrufstapel entfernt.
Jetzt startet die Web-API einen Timer von drei Sekunden, da sie weiß, dass sie die Aufgabe in 3 Sekunden erledigen muss. Denken Sie daran: Da der Aufrufstapel leer ist, fährt die JavaScript-Engine mit Zeile 3 fort, nämlich console.log(“z“); und führen Sie es aus. Deshalb haben wir das Ergebnis x,z erhalten, aber wir haben in der Web-API eine Zeitüberschreitung von drei Sekunden festgelegt. Dann, nach drei Sekunden, wenn das Zeitlimit abgelaufen ist, wird setTimeout ausgeführt und sieht, was sich darin befindet, und es ist fertig. Sobald dies erledigt ist, erkennt die Web-API, dass sie über eine callback()-Funktion von setTimeout verfügt, fügt sie zur CALLBACK-Warteschlange hinzu und ist bereit, sie auszuführen.
Wir kommen zum letzten Teil, nämlich** EVENT LOOP*. Dieser überprüft ständig, ob der Aufrufstapel leer ist. Wenn es leer ist und derzeit nichts in der JavaScript-Engine ausgeführt wird, überprüft es die Rückrufwarteschlange und findet dann unsere **callback()*-Funktion mit console.log(„z“) Platzieren Sie es im CALL STACK und es wird ausgeführt. Sobald es fertig ist, wird es aus dem Aufrufstapel entfernt. Jetzt ist alles leer und man erhält das Ergebnis x z y.
Fazit: In diesem Beitrag haben wir viele Informationen darüber gesehen, was unter der Haube passiert, um die JavaScript-Logik sowohl synchron als auch asynchron ausgeführte Aufgaben vollständig zu verstehen.
Hoffentlich hilft dies neuen und fortgeschrittenen JavaScript-Programmierern dabei, Spaß am Codieren in JavaScript-bezogenen Frameworks wie ReactJS oder AngularJS zu haben, denn dies ist die Grundlage für das Verständnis fortgeschrittener Logik.
> Viel Spaß beim Codieren
Referenzen
https://www.freecodecamp.org/news/how-javascript-works-behind-the-scenes.
https://www.simplilearn.com/tutorials/javascript-tutorial/callback-function-in-javascript#
Das obige ist der detaillierte Inhalt vonWie funktioniert JavaScript unter der Haube?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!