[Lesen empfehlen] Die anderen 19 Teile des Zyklus Wir alle wissen, dass der JavaScript-Code für Webprojekte sehr groß werden kann. Und je größer der Code, desto länger lädt der Browser ihn. Das Problem liegt hier aber nicht nur in der Zeit der Datenübertragung über das Netzwerk. Nachdem das Programm geladen wurde, muss es noch analysiert, in Bytecode kompiliert und schließlich ausgeführt werden. Heute machen wir Sie auf eine Übersetzung von Teil 14 der JavaScript-Ökosystemreihe aufmerksam. Wir werden nämlich über das Parsen von JS-Code sprechen, wie abstrakte Syntaxbäume erstellt werden und wie ein Programmierer diese Prozesse beeinflussen kann, um die Geschwindigkeit ihrer Anwendungen zu erhöhen.

Wie sind Programmiersprachen?
Bevor wir uns mit abstrakten Syntaxbäumen befassen, wollen wir uns mit der Funktionsweise von Programmiersprachen befassen. Unabhängig davon, welche Sprache Sie verwenden, müssen Sie immer bestimmte Programme verwenden, die den Quellcode in etwas konvertieren, das bestimmte Befehle für die Maschinen enthält. Entweder Interpreter oder Compiler fungieren als solche Programme. Es spielt keine Rolle, ob Sie in einer interpretierten Sprache (JavaScript, Python, Ruby) oder kompiliert (C #, Java, Rust) schreiben. Ihr Code, bei dem es sich um einfachen Text handelt, durchläuft immer die Analysephase, dh, einfacher Text wird in eine Datenstruktur umgewandelt wird als Abstract Syntax Tree (AST) bezeichnet.
Abstrakte Syntaxbäume bieten nicht nur eine strukturierte Darstellung des Quellcodes, sondern spielen auch eine entscheidende Rolle bei der semantischen Analyse, bei der der Compiler die Richtigkeit von Softwarekonstrukten und die korrekte Verwendung ihrer Elemente überprüft. Nach dem Bilden des AST und dem Durchführen von Überprüfungen wird diese Struktur verwendet, um Bytecode oder Maschinencode zu generieren.
Verwenden abstrakter Syntaxbäume
Abstrakte Syntaxbäume werden nicht nur in Interpreten und Compilern verwendet. Sie sind in der Welt der Computer in vielen anderen Bereichen nützlich. Eine der häufigsten Anwendungen ist die statische Code-Analyse. Statische Analysatoren führen den an sie übergebenen Code nicht aus. Trotzdem müssen sie die Struktur der Programme verstehen.
Angenommen, Sie möchten ein Tool entwickeln, das häufig vorkommende Strukturen in Ihrem Code findet. Die Berichte eines solchen Tools helfen beim Refactoring und reduzieren die Codeduplizierung. Dies kann unter Verwendung des üblichen Zeichenfolgenvergleichs erfolgen, aber dieser Ansatz ist sehr primitiv und seine Fähigkeiten sind begrenzt. Wenn Sie ein ähnliches Tool erstellen möchten, müssen Sie keinen eigenen Parser für JavaScript schreiben. Es gibt viele Open-Source-Implementierungen solcher Programme, die vollständig mit der ECMAScript-Spezifikation kompatibel sind. Zum Beispiel - Esprima und Eichel. Es gibt auch Tools, die bei der Arbeit mit Parsern helfen können, nämlich bei der Arbeit mit abstrakten Syntaxbäumen.
Darüber hinaus werden abstrakte Syntaxbäume häufig bei der Entwicklung von Transpilern verwendet. Angenommen, Sie möchten einen Transpiler entwickeln, der Python-Code in JavaScript-Code konvertiert. Ein ähnliches Projekt kann auf der Idee basieren, dass ein Transpiler verwendet wird, um einen abstrakten Syntaxbaum basierend auf Python-Code zu erstellen, der wiederum in JavaScript-Code konvertiert wird. Wahrscheinlich werden Sie sich hier fragen, wie das möglich ist. Die Sache ist, dass abstrakte Syntaxbäume nur eine alternative Art sind, Code in einer Programmiersprache darzustellen. Bevor der Code in AST konvertiert wird, sieht er beim Schreiben wie normaler Text aus, der bestimmten Regeln folgt, die die Sprache bilden. Nach dem Parsen wird dieser Code zu einer Baumstruktur, die dieselben Informationen wie der Quellcode des Programms enthält. Dadurch ist es möglich, nicht nur den Übergang vom Quellcode zum AST durchzuführen, sondern auch die inverse Transformation, wodurch der abstrakte Syntaxbaum in eine Textdarstellung des Programmcodes umgewandelt wird.
JavaScript analysieren
Lassen Sie uns darüber sprechen, wie abstrakte Syntaxbäume erstellt werden. Betrachten Sie als Beispiel eine einfache JavaScript-Funktion:
function foo(x) { if (x > 10) { var a = 2; return a * x; } return x + 10; }
Der Parser erstellt einen abstrakten Syntaxbaum, der in der folgenden Abbildung schematisch dargestellt ist.
Abstrakter SyntaxbaumBitte beachten Sie, dass dies eine vereinfachte Darstellung der Ergebnisse des Parsers ist. Ein echter abstrakter Syntaxbaum sieht viel komplizierter aus. In diesem Fall ist es unser Hauptziel, eine Vorstellung davon zu bekommen, in was sich der Quellcode zunächst verwandelt, bevor er ausgeführt wird. Wenn Sie sich ansehen möchten, wie ein echter abstrakter Syntaxbaum aussieht, verwenden Sie die
AST Explorer- Website. Um einen AST für ein bestimmtes Fragment von JS-Code zu generieren, reicht es aus, ihn in das entsprechende Feld auf der Seite zu platzieren.
Vielleicht haben Sie hier eine Frage, warum der Programmierer wissen muss, wie der JS-Parser funktioniert. Am Ende ist das Parsen und Ausführen von Code eine Browseraufgabe. In gewisser Weise hast du recht. Die folgende Abbildung zeigt die Zeit, die einige bekannte Webprojekte benötigen, um verschiedene Schritte bei der Ausführung von JS-Code auszuführen.
Schauen Sie sich diese Zeichnung genauer an, vielleicht sehen Sie dort etwas Interessantes.
Zeitaufwand für die Ausführung von JS-CodeSehen Sie? Wenn nicht, schauen Sie noch einmal. Tatsächlich sprechen wir über die Tatsache, dass Browser durchschnittlich 15 bis 20% der Zeit damit verbringen, JS-Code zu analysieren. Und dies sind keine bedingten Daten. Hier finden Sie statistische Informationen zur Arbeit realer Webprojekte, die JavaScript auf die eine oder andere Weise verwenden. Vielleicht scheint Ihnen die Zahl von 15% nicht so groß zu sein, aber glauben Sie mir, das ist viel. Eine typische einseitige Anwendung lädt ungefähr 0,4 MB JavaScript-Code, und der Browser benötigt ungefähr 370 ms, um diesen Code zu analysieren. Auch hier kann man sagen, dass es keinen Grund zur Sorge gibt. Und ja, das allein ist nicht viel. Vergessen Sie jedoch nicht, dass dies nur die Zeit ist, die erforderlich ist, um den Code zu analysieren und in einen AST umzuwandeln. Dies beinhaltet nicht die Zeit, die zum Ausführen des Codes benötigt wird, oder die Zeit, die zum Lösen anderer Aufgaben benötigt wird, die mit dem Laden der Seite einhergehen, z. B. die Aufgaben zum Verarbeiten von HTML und CSS und zum
Rendern der Seite . Darüber hinaus sprechen wir nur über Desktop-Browser. Bei mobilen Systemen ist das noch schlimmer. Insbesondere kann die Analysezeit für denselben Code auf Mobilgeräten 2-5 Mal länger sein als auf dem Desktop. Schauen Sie sich die folgende Abbildung an.
Analysezeit von 1 MB JS-Code auf verschiedenen GerätenHier ist die Zeit, die erforderlich ist, um 1 MB JS-Code auf verschiedenen Mobil- und Desktopgeräten zu analysieren.
Darüber hinaus werden Webanwendungen immer komplexer und immer mehr Aufgaben werden auf die Client-Seite übertragen. All dies zielt darauf ab, die Benutzererfahrung bei der Arbeit mit Websites zu verbessern, um diese Gefühle denen näher zu bringen, die Benutzer bei der Interaktion mit herkömmlichen Anwendungen erleben. Es ist leicht herauszufinden, wie stark sich dies auf Webprojekte auswirkt. Öffnen Sie dazu einfach die Entwicklertools im Browser, besuchen Sie eine moderne Website und sehen Sie, wie viel Zeit für das Parsen des Codes, das Kompilieren und alles andere aufgewendet wird, was im Browser passiert, wenn Sie die Seite für die Arbeit vorbereiten.
Website-Analyse mit Entwicklertools in einem BrowserLeider verfügen mobile Browser nicht über solche Tools. Dies bedeutet jedoch nicht, dass mobile Versionen von Websites nicht analysiert werden können. Hier
helfen uns Tools wie
DeviceTiming . Mit DeviceTiming können Sie die Zeit messen, die zum Parsen und Ausführen von Skripten in verwalteten Umgebungen benötigt wird. Dies funktioniert aufgrund der Platzierung lokaler Skripte in der durch den Hilfscode gebildeten Umgebung, was dazu führt, dass wir jedes Mal, wenn die Seite von verschiedenen Geräten geladen wird, die Zeit für das Parsen und die Codeausführung lokal messen können.
Analyseoptimierung und JS-Engines
JS-Engines tun viele nützliche Dinge, um unnötige Arbeit zu vermeiden und Codeverarbeitungsprozesse zu optimieren. Hier sind einige Beispiele.
Die V8-Engine unterstützt Streaming-Skripte und Code-Caching. In diesem Fall wird unter Streaming die Tatsache verstanden, dass das System asynchron geladene Skripte analysiert und Skripts, deren Ausführung verzögert ist, in einem separaten Thread ab dem Moment beginnen, in dem der Code geladen wird. Dies führt dazu, dass das Parsen fast gleichzeitig mit dem Abschluss des Ladens des Skripts endet, wodurch sich die für die Vorbereitung der Seiten für die Arbeit erforderliche Zeit um etwa 10% verringert.
JavaScript-Code wird normalerweise bei jedem Besuch einer Seite in Bytecode kompiliert. Dieser Bytecode geht jedoch verloren, nachdem der Benutzer zu einer anderen Seite navigiert hat. Dies liegt an der Tatsache, dass der kompilierte Code zum Zeitpunkt der Kompilierung stark vom Status und Kontext des Systems abhängt. Um die Situation zu verbessern, hat Chrome 42 die Unterstützung für das Zwischenspeichern von Bytecodes eingeführt. Dank dieser Innovation wird der kompilierte Code lokal gespeichert. Wenn der Benutzer zu der bereits besuchten Seite zurückkehrt, müssen keine Skripte heruntergeladen, analysiert und kompiliert werden, um ihn für die Arbeit vorzubereiten. Dies spart Chrome etwa 40% der Zeit beim Parsen und Kompilieren. Darüber hinaus führt dies bei mobilen Geräten zu einer Einsparung von Batteriestrom.
Die
Carakan- Engine, die im Opera-Browser verwendet wurde und lange Zeit durch V8 ersetzt wurde, könnte die Kompilierungsergebnisse bereits verarbeiteter Skripte wiederverwenden. Es war nicht erforderlich, dass diese Skripte mit derselben Seite verbunden oder sogar aus derselben Domäne geladen wurden. Diese Caching-Technik ist in der Tat sehr effektiv und ermöglicht es Ihnen, den Kompilierungsschritt vollständig abzubrechen. Sie stützt sich auf typische Benutzerverhaltensszenarien und darauf, wie Menschen mit Webressourcen arbeiten. Wenn der Benutzer während der Arbeit mit einer Webanwendung einer bestimmten Abfolge von Aktionen folgt, wird derselbe Code geladen.
Der von FireFox verwendete
SpiderMonkey- Interpreter speichert nicht alles hintereinander zwischen. Es unterstützt ein Überwachungssystem, das die Anzahl der Aufrufe eines bestimmten Skripts zählt. Basierend auf diesen Indikatoren werden Abschnitte des Codes bestimmt, die optimiert werden müssen, dh diejenigen, die die maximale Last haben.
Natürlich können einige Browserentwickler entscheiden, dass ihre Produkte überhaupt kein Caching benötigen.
Masei Stachovyak , ein führender Entwickler des Safari-Browsers, sagt, dass Safari nicht am Zwischenspeichern von kompiliertem Bytecode beteiligt ist. Die Möglichkeit des Caching wurde in Betracht gezogen, wurde jedoch noch nicht implementiert, da die Codegenerierung weniger als 2% der gesamten Programmausführungszeit in Anspruch nimmt.
Diese Optimierungen wirken sich nicht direkt auf das Parsen des Quellcodes in JS aus. Im Laufe ihrer Anwendung wird alles getan, um diesen Schritt in bestimmten Fällen vollständig zu überspringen. Egal wie schnell das Parsen ist, es dauert immer noch einige Zeit, und das völlige Fehlen des Parsens ist vielleicht das Beispiel für eine perfekte Optimierung.
Reduzieren Sie die Vorbereitungszeit für Webanwendungen
Wie wir oben herausgefunden haben, wäre es schön, den Bedarf an Parsing-Skripten zu minimieren, aber Sie können es nicht vollständig entfernen. Lassen Sie uns also darüber sprechen, wie Sie die Zeit reduzieren können, die für die Vorbereitung von Webanwendungen für die Arbeit erforderlich ist. Tatsächlich kann dafür viel getan werden. Sie können beispielsweise die Menge des in der Anwendung enthaltenen JS-Codes minimieren. Ein kleiner Code, der eine Seite für die Arbeit vorbereitet, kann schneller analysiert werden, und die Ausführung dauert höchstwahrscheinlich weniger lange als ein umfangreicherer Code.
Um die Codemenge zu reduzieren, können Sie das Laden auf der Seite nur so organisieren, wie es wirklich benötigt wird, und nicht mit einem großen Code, der absolut alles enthält, was für das gesamte Webprojekt benötigt wird. So fördert beispielsweise das
PRPL- Muster einen solchen Ansatz zum Laden von Code. Alternativ können Sie die Abhängigkeiten überprüfen und feststellen, ob sie redundant sind, sodass dies nur zu einem ungerechtfertigten Wachstum der Codebasis führt. Tatsächlich haben wir hier ein großes Thema angesprochen, das eines gesonderten Materials würdig ist. Zurück zum Parsen.
In diesem Material werden daher Techniken erläutert, mit denen ein Webentwickler einem Parser helfen kann, seine Arbeit schneller zu erledigen. Solche Techniken existieren. Moderne JS-Parser verwenden heuristische Algorithmen, um zu bestimmen, ob ein bestimmter Code so schnell wie möglich ausgeführt werden muss oder ob er später ausgeführt werden muss. Basierend auf diesen Vorhersagen analysiert der Parser entweder das Codefragment vollständig unter Verwendung des eifrigen Parsing-Algorithmus oder verwendet den Lazy-Parsing-Algorithmus. Mit einer vollständigen Analyse verstehen Sie die Funktionen, die Sie so schnell wie möglich kompilieren müssen. Während dieses Prozesses werden drei Hauptaufgaben gelöst: Erstellen eines AST, Erstellen einer Hierarchie von Sichtbarkeitsbereichen und Auffinden von Syntaxfehlern. Die Lazy-Analyse wird dagegen nur für Funktionen verwendet, die noch nicht kompiliert werden müssen. Dadurch wird kein AST erstellt und es wird nicht nach Fehlern gesucht. Mit diesem Ansatz wird nur eine Hierarchie von Sichtbarkeitsbereichen erstellt, was im Vergleich zu Verarbeitungsfunktionen, die so schnell wie möglich ausgeführt werden müssen, etwa die Hälfte der Zeit spart.
In der Tat ist das Konzept nicht neu. Selbst veraltete Browser wie IE9 unterstützen solche Optimierungsansätze, obwohl moderne Systeme natürlich weit fortgeschritten sind.
Lassen Sie uns ein Beispiel untersuchen, das die Funktionsweise dieser Mechanismen veranschaulicht. Angenommen, wir haben den folgenden JS-Code:
function foo() { function bar(x) { return x + 10; } function baz(x, y) { return x + y; } console.log(baz(100, 200)); }
Wie im vorherigen Beispiel fällt der Code in den Parser, der seine Analyse durchführt und den AST bildet. Infolgedessen stellt der Parser einen Code dar, der aus den folgenden Hauptteilen besteht (wir werden die
foo
Funktion nicht beachten):
- Deklarieren einer Balkenfunktion, die ein Argument (
x
) akzeptiert. Diese Funktion verfügt über einen Rückgabebefehl und gibt das Ergebnis des Hinzufügens von x
und 10 zurück. - Deklarieren einer
baz
Funktion, die zwei Argumente ( x
und y
) baz
. Sie hat auch einen Rückgabebefehl, sie gibt das Ergebnis des Hinzufügens von x
und y
. - Aufruf der
baz
Funktion mit zwei Argumenten - 100 und 200. - Aufruf der Funktion
console.log
mit einem Argument, dem Wert, der von der zuvor aufgerufenen Funktion zurückgegeben wird.
So sieht es aus.
Das Ergebnis des Parsens des Beispielcodes ohne Anwendung der OptimierungSprechen wir darüber, was hier los ist. Der Parser sieht die Deklaration der
console.log
, die Deklaration der
baz
Funktion, den Aufruf der
baz
Funktion und den Aufruf der
console.log
Funktion. Beim Parsen dieses Codes stößt der Parser offensichtlich auf eine Aufgabe, deren Ausführung die Ergebnisse dieses Programms nicht beeinflusst. Es geht darum, die Funktionsleiste zu analysieren. Warum ist die Analyse dieser Funktion nicht praktikabel? Die Sache ist, dass die
bar
, zumindest im dargestellten Codefragment, niemals aufgerufen wird. Dieses einfache Beispiel mag weit hergeholt erscheinen, aber viele reale Anwendungen haben eine große Anzahl von Funktionen, die niemals aufgerufen werden.
In einer solchen Situation können wir, anstatt die Balkenfunktion zu analysieren, einfach aufzeichnen, dass sie deklariert ist, aber nirgendwo verwendet wird. Gleichzeitig wird die eigentliche Analyse dieser Funktion durchgeführt, wenn sie unmittelbar vor ihrer Ausführung erforderlich wird. Wenn Sie eine verzögerte Analyse durchführen, müssen Sie natürlich den Hauptteil der Funktion erkennen und ihre Deklaration aufzeichnen, aber hier endet die Arbeit. Für eine solche Funktion ist es nicht erforderlich, einen abstrakten Syntaxbaum zu bilden, da das System keine Informationen darüber hat, dass diese Funktion ausgeführt werden soll. Darüber hinaus wird kein Heapspeicher zugewiesen, was normalerweise erhebliche Systemressourcen erfordert. Kurz gesagt, die Weigerung, unnötige Funktionen zu analysieren, führt zu einer signifikanten Steigerung der Codeleistung.
Infolgedessen bildet der reale Parser im vorherigen Beispiel eine Struktur, die dem folgenden Schema ähnelt.
Ergebnis des Parsens von Beispielcode mit OptimierungBeachten Sie, dass der Parser die Deklaration der Funktionsleiste notiert hat, sich jedoch nicht mit deren weiterer Analyse befasst hat. Das System hat keine Anstrengungen unternommen, um den Funktionscode zu analysieren. In diesem Fall war der Hauptteil der Funktion ein Befehl, das Ergebnis einfacher Berechnungen zurückzugeben. In den meisten realen Anwendungen kann der Funktionscode jedoch viel länger und komplexer sein und viele Rückgabebefehle, Bedingungen, Schleifen, Variablendeklarationsbefehle und verschachtelte Funktionen enthalten. All dies zu analysieren, sofern solche Funktionen niemals aufgerufen werden, ist Zeitverschwendung.
Das oben beschriebene Konzept ist nicht kompliziert, aber seine praktische Umsetzung ist keine leichte Aufgabe. Hier haben wir ein sehr einfaches Beispiel untersucht, und tatsächlich ist es bei der Entscheidung, ob ein bestimmter Code in einem Programm benötigt wird, erforderlich, Funktionen und Schleifen sowie bedingte Operatoren und Objekte zu analysieren. Im Allgemeinen können wir sagen, dass der Parser absolut alles im Programm verarbeiten und analysieren muss.
Hier ist zum Beispiel ein sehr verbreitetes Muster für die Implementierung von Modulen in JavaScript:
var myModule = (function() {
Die meisten modernen JS-Parser erkennen dieses Muster, für sie ist es ein Signal, dass der im Modul befindliche Code vollständig analysiert werden muss.
Aber was ist, wenn Parser immer faul analysiert haben? Dies ist leider keine gute Idee. Tatsache ist, dass bei diesem Ansatz, wenn Code so schnell wie möglich ausgeführt werden muss, das System langsamer wird. Der Parser führt einen faulen Parsing-Durchgang durch. Danach beginnt er sofort, vollständig zu analysieren, was so schnell wie möglich zu tun ist. Dies führt zu einer Verlangsamung von etwa 50% im Vergleich zum Ansatz, bei dem der Parser sofort beginnt, den wichtigsten Code vollständig zu analysieren.
Codeoptimierung unter Berücksichtigung der Merkmale der Analyse
Nachdem wir ein wenig darüber herausgefunden haben, was in den Parsern vor sich geht, ist es an der Zeit, darüber nachzudenken, was getan werden kann, um ihnen zu helfen. Wir können Code schreiben, damit das Parsen von Funktionen zum gewünschten Zeitpunkt durchgeführt wird. Es gibt ein Muster, das die meisten Parser verstehen. Es drückt sich darin aus, dass Funktionen in Klammern stehen. Ein solches Design sagt dem Parser fast immer, dass die Funktion sofort zerlegt werden muss. Wenn der Parser eine öffnende Klammer erkennt, unmittelbar danach die Funktionsdeklaration folgt, beginnt er sofort mit dem Parsen der Funktion. Wir können dem Parser helfen, indem wir diese Technik anwenden, wenn wir Funktionen beschreiben, die so schnell wie möglich ausgeführt werden müssen.
Angenommen, wir haben eine Funktion
foo
:
function foo(x) { return x * 10; }
Da dieses Codefragment keinen expliziten Hinweis darauf enthält, dass diese Funktion sofort ausgeführt werden soll, führt der Browser nur die verzögerte Analyse durch. Wir sind jedoch zuversichtlich, dass wir diese Funktion sehr bald benötigen werden, damit wir zum nächsten Trick greifen können.
Speichern Sie zunächst die Funktion in einer Variablen:
var foo = function foo(x) { return x * 10; };
Bitte beachten Sie, dass wir den ursprünglichen Funktionsnamen zwischen dem
function
und der öffnenden Klammer belassen haben. Es kann nicht gesagt werden, dass dies absolut notwendig ist, aber es wird empfohlen, genau das zu tun, denn wenn eine Ausnahme ausgelöst wird, während die Funktion ausgeführt wird, können Sie den Namen der Funktion in den Stack-Trace-Daten sehen, nicht
<anonymous>
.
Nach der obigen Änderung verwendet der Parser weiterhin die verzögerte Analyse. Um dies zu ändern, reicht ein kleines Detail aus. Die Funktion muss in Klammern stehen:
var foo = (function foo(x) { return x * 10; });
Wenn der Parser nun eine öffnende Klammer vor dem
function
findet, beginnt er sofort mit dem Parsen dieser Funktion.
Es ist möglicherweise nicht einfach, solche Optimierungen manuell durchzuführen, da Sie dazu wissen müssen, in welchen Fällen der Parser eine verzögerte Analyse durchführt und in welchen Fällen die vollständige. Dazu müssen Sie außerdem entscheiden, ob eine bestimmte Funktion so schnell wie möglich einsatzbereit sein muss oder nicht.
Programmierer werden diese zusätzliche Arbeit sicherlich nicht übernehmen wollen. Außerdem ist der auf diese Weise verarbeitete Code schwieriger zu lesen und zu verstehen, was nicht weniger wichtig ist als alles, was bereits gesagt wurde. In dieser Situation helfen uns spezielle Softwarepakete wie Optimize.js. Ihr Hauptziel ist es, die anfängliche Startzeit für JS-Quellcode zu optimieren. Sie führen eine statische Code-Analyse durch und ändern sie so, dass die Funktionen, die so schnell wie möglich ausgeführt werden müssen, in Klammern eingeschlossen sind. Dies führt dazu, dass der Browser sie sofort analysiert und für die Ausführung vorbereitet.
Nehmen wir also an, wir programmieren, ohne wirklich an irgendetwas zu denken, und wir haben das folgende Codefragment:
(function() { console.log('Hello, World!'); })();
Es sieht ganz normal aus, funktioniert wie erwartet und wird schnell ausgeführt, da der Parser die öffnende Klammer vor dem
function
. So weit so gut. , , , :
!function(){console.log('Hello, World!')}();
, , . , - .
, , . , , , . , , , . , , . Optimize.js. Optimize.js, :
!(function(){console.log('Hello, World!')})();
, . , . , , , — .
, JS- — , . ? , , , , . , , , , JS- , . , , , -, . - . , , . , , , , . , JS- , , V8 , , . .
, -:
- . .
- , .
- , , , JS-. , , .
- DeviceTiming , .
- Optimize.js , , .
Zusammenfassung
, ,
SessionStack , , -, . , . — . , — , -, , , .
Liebe Leser! - JavaScript-?