Anhang

Mit Ausnahme von null und undefined hat jeder primitive Datentyp einen prototype, einem entsprechenden Objekt-Wrapper, der Methoden zum Arbeiten mit mit Werten. Wenn eine Methoden- oder Eigenschaftssuche auf einer Primitive aufgerufen wird, JavaScript umschließt das Primitive im Hintergrund und ruft die Methode die Property-Suche für das Wrapper-Objekt durchführt.

Beispiel: Ein Stringliteral hat keine eigenen Methoden, Sie können jedoch die Methode .toUpperCase()-Methode mithilfe des entsprechenden String-Objekts Wrapper:

"this is a string literal".toUpperCase();
> THIS IS A STRING LITERAL

Dies wird als prototypische Übernahme bezeichnet, also Eigenschaften und Methoden übernehmen. aus dem entsprechenden Konstruktor eines Werts.

Number.prototype
> Number { 0 }
>  constructor: function Number()
>  toExponential: function toExponential()
>  toFixed: function toFixed()
>  toLocaleString: function toLocaleString()
>  toPrecision: function toPrecision()
>  toString: function toString()
>  valueOf: function valueOf()
>  <prototype>: Object { }

Mit diesen Konstruktoren können Sie Primitive erstellen, anstatt nur sie nach ihrem Wert anzeigen. Mit dem Konstruktor String wird beispielsweise ein Stringobjekt, kein Stringliteral: ein Objekt, das nicht nur unseren String enthält -Wert, aber alle vererbten Eigenschaften und Methoden des Konstruktors.

const myString = new String( "I'm a string." );

myString
;
> String { "I'm a string." }

typeof myString;
> "object"

myString
.valueOf();
> "I'm a string."

Die resultierenden -Objekte verhalten sich größtenteils wie die Werte, die wir bisher für definieren. Auch wenn z. B. ein Zahlenwert mit der Methode Der Konstruktor new Number ergibt ein Objekt, das alle Methoden und Number des Prototyps mit mathematischen Operatoren wie bei Zahlenliteralen:

const numberOne = new Number(1);
const numberTwo = new Number(2);

numberOne
;
> Number { 1 }

typeof numberOne;
> "object"

numberTwo
;
> Number { 2 }

typeof numberTwo;
> "object"

numberOne
+ numberTwo;
> 3

Sie werden diese Konstruktoren nur sehr selten verwenden, da die integrierten prototypische Vererbung bedeutet, dass sie keinen praktischen Nutzen haben. Wird erstellt... Primitive, die Konstruktoren verwenden, können ebenfalls zu unerwarteten Ergebnissen führen, result ist ein -Objekt und kein einfaches Literal:

let stringLiteral = "String literal."

typeof stringLiteral;
> "string"

let stringObject
= new String( "String object." );

stringObject
> "object"

Dies kann die Verwendung strenger Vergleichsoperatoren erschweren:

const myStringLiteral = "My string";
const myStringObject = new String( "My string" );

myStringLiteral
=== "My string";
> true

myStringObject
=== "My string";
> false

Automatisches Einfügen von Semikolons (ASI)

Beim Parsen eines Skripts können JavaScript-Interpreter die Funktion automatische Semikolon-Einfügung (ASI), um fehlende Instanzen zu korrigieren Semikolons. Wenn der JavaScript-Parser auf ein unzulässiges Token stößt, versucht, vor diesem Token ein Semikolon hinzuzufügen, um den potenziellen Syntaxfehler zu beheben: solange mindestens eine der folgenden Bedingungen zutrifft:

  • Dieses Token ist durch einen Zeilenumbruch vom vorherigen Token getrennt.
  • Das Token ist }.
  • Das vorherige Token ist ) und das eingefügte Semikolon wäre das Ende Semikolon einer do...while-Anweisung.

Weitere Informationen finden Sie in den ASI-Regeln.

Das Weglassen von Semikolons nach den folgenden Anweisungen führt beispielsweise nicht zu einer Syntaxfehler aufgrund von ASI:

const myVariable = 2
myVariable
+ 3
> 5

ASI kann jedoch nicht mehrere Anweisungen in derselben Zeile berücksichtigen. Wenn Sie Schreiben Sie mehr als eine Anweisung in dieselbe Zeile und trennen Sie sie durch Semikolons:

const myVariable = 2 myVariable + 3
> Uncaught SyntaxError: unexpected token: identifier

const myVariable = 2; myVariable + 3;
> 5

ASI ist ein Versuch zur Fehlerkorrektur, keine Art von syntaktischer Flexibilität in JavaScript umwandeln. Achten Sie darauf, dass Sie bei Bedarf Semikolons verwenden, um den richtigen Code zu generieren.

Strenger Modus

Die Standards, die beim Schreiben von JavaScript regeln, haben sich weit über alle Aspekte, die bei der frühen Entwicklung der Sprache berücksichtigt wurden. Jede neue Änderung an Das erwartete JavaScript-Verhalten muss keine Fehler auf älteren Websites verursachen.

ES5 befasst sich mit langjährigen Problemen mit der JavaScript-Semantik, bestehenden Implementierungen unterbrochen, indem sie den „strikten Modus“ eine Möglichkeit, in einen restriktiveren Satz von Sprachregeln entweder für ein ganzes Skript oder auf eine bestimmte Funktion anpassen können. Verwenden Sie das Stringliteral, um den strikten Modus zu aktivieren "use strict", gefolgt von einem Semikolon in der ersten Zeile eines Skripts oder :

"use strict";
function myFunction() {
 
"use strict";
}

Der strikte Modus verhindert, dass bestimmte „unsichere“ oder veraltete Funktionen, wirft explizite Fehler anstelle von häufigen „stumm“ und verbietet die Verwendung von die mit zukünftigen Sprachfunktionen kollidieren könnten. Zum Beispiel früh im Designentscheidungen im Hinblick auf den Variablenumfang die Wahrscheinlichkeit, dass Entwickler fälschlicherweise den globalen Geltungsbereich Deklarieren Sie eine Variable unabhängig vom enthaltenden Kontext, indem Sie den Parameter var Keyword:

(function() {
  mySloppyGlobal
= true;
}());

mySloppyGlobal
;
> true

Moderne JavaScript-Laufzeiten können dieses Verhalten nicht ohne das Risiko eine Website, die darauf basiert, entweder irrtümlich oder mit Absicht. Stattdessen verhindert modernes JavaScript dies, da Entwickler sich für strikte und den strikten Modus standardmäßig nur bei neue Sprachfunktionen, bei denen alte Implementierungen nicht beeinträchtigt werden:

(function() {
   
"use strict";
    mySloppyGlobal
= true;
}());
> Uncaught ReferenceError: assignment to undeclared variable mySloppyGlobal

Sie müssen "use strict" als String-Literal. Ein Vorlagenliteral (use strict) funktioniert nicht. Außerdem müssen Sie "use strict" vor jeglichen im vorgesehenen Kontext ausgeführt werden. Andernfalls wird sie vom Interpreter ignoriert.

(function() {
   
"use strict";
    let myVariable
= "String.";
    console
.log( myVariable );
    sloppyGlobal
= true;
}());
> "String."
> Uncaught ReferenceError: assignment to undeclared variable sloppyGlobal

(function() {
    let myVariable
= "String.";
   
"use strict";
    console
.log( myVariable );
    sloppyGlobal
= true;
}());
> "String." // Because there was code prior to "use strict", this variable still pollutes the global scope

Nach Referenz, nach Wert

Beliebige Variable, einschließlich Eigenschaften eines Objekts, Funktionsparameter und Elemente in einer Array set oder map kann entweder ein Primitiv enthalten Wert oder einen Referenzwert.

Wenn ein primitiver Wert einer Variablen zugewiesen wird, erstellt die Suchmaschine eine Kopie dieses Werts und weist ihn der Variablen zu.

Wenn Sie ein Objekt (Klasseninstanzen, Arrays und Funktionen) einer nicht eine neue Kopie dieses Objekts zu erstellen, sondern enthält Verweis auf die im Speicher gespeicherte Position des Objekts Aus diesem Grund Ein Objekt, auf das eine Variable verweist, ändert nicht nur das Objekt, auf das verwiesen wird, einen Wert, der in dieser Variablen enthalten ist. Wenn Sie z. B. eine neue mit einer Variablen ein, die einen Objektverweis enthält, und verwenden Sie dann die neue zum Hinzufügen einer Eigenschaft zu diesem Objekt, werden die Eigenschaft und ihr Wert mit dem ursprünglichen Objekt:

const myObject = {};
const myObjectReference = myObject;

myObjectReference
.myProperty = true;

myObject
;
> Object { myProperty: true }

Dies ist nicht nur wichtig, um Objekte zu ändern, sondern auch, da bei strikter Gleichheit zwischen Objekten Verweisen Sie auf dasselbe Objekt, um es als true auszuwerten. Sie können nicht auf unterschiedliche Objekte erstellen, auch wenn diese Objekte strukturell identisch sind:

const myObject = {};
const myReferencedObject = myObject;
const myNewObject = {};

myObject
=== myNewObject;
> false

myObject
=== myReferencedObject;
> true

Arbeitsspeicherzuweisung

JavaScript nutzt die automatische Speicherverwaltung, d. h. der Arbeitsspeicher explizit zugewiesen oder freigegeben werden. Während die Details der JavaScript-Engines Speicherverwaltungskonzepte überdenken, Der Umfang dieses Moduls ist hilfreich, Kontext für die Arbeit mit Referenzwerten.

Es gibt zwei „Bereiche“, im Arbeitsspeicher: der „Stack“ und den „Heap“. Der Stack speichert – primitive Werte und Verweise auf Objekte –, da die Funktion fester Speicherplatz, der zum Speichern dieser Daten benötigt wird, ausgeführt wird. Der Heap speichert Objekte, die dynamisch zugewiesenen Speicherplatz benötigen da sich ihre Größe während der Ausführung ändern kann. Arbeitsspeicher wird durch einen Prozess freigegeben namens „automatische Speicherbereinigung“ mit der Objekte ohne Verweise aus zu speichern.

Im Hauptthread

JavaScript ist eine im Grunde genommen Single-Threaded-Sprache mit einem "synchronen" Ausführungsmodell, d. h. es kann nur eine Aufgabe ausführen auf einmal ansehen. Dieser sequenzielle Ausführungskontext wird als Hauptthread bezeichnet.

Der Haupt-Thread wird von anderen Browser-Aufgaben, wie dem Parsen von HTML, das Rendern und erneutes Rendern von Teilen der Seite, das Ausführen von CSS-Animationen von einfachen (z. B. Hervorheben von Text) bis hin zu (z. B. Interaktion mit Formularelementen). Browser-Anbieter fanden heraus, Möglichkeiten zur Optimierung der Aufgaben, die vom Hauptthread ausgeführt werden, aber komplexer sind Scripts können trotzdem zu viele Ressourcen des Hauptthreads nutzen und sich insgesamt auf die Auswirkungen auswirken. Seitenleistung.

Einige Aufgaben können in Hintergrundthreads namens Web Worker mit einigen Einschränkungen:

  • Worker-Threads können nur auf eigenständige JavaScript-Dateien reagieren.
  • Sie haben nur eingeschränkten oder keinen Zugriff auf das Browserfenster und die Benutzeroberfläche.
  • Sie sind eingeschränkt, wie sie mit dem Hauptthread kommunizieren können.

Diese Einschränkungen machen sie ideal für fokussierte, ressourcenintensive Aufgaben, die ansonsten den Hauptthread belegen könnten.

Aufrufstack

Die Datenstruktur, die zum Verwalten von „Ausführungskontexten“ verwendet wird, d. h. der Code, aktiv ausgeführt wird: Dies ist eine Liste namens Aufrufstack (häufig „Stapel“). Wenn ein Skript zum ersten Mal ausgeführt wird, einen „globalen Ausführungskontext“ erstellt, per Push an den Aufrufstack, wobei Anweisungen innerhalb dieses globalen Kontexts, die nacheinander ausgeführt werden, von oben bis unten. Wenn der Interpreter beim Ausführen der Funktion globalen Kontext enthält, wird ein „Funktionsausführungskontext“ für diesen Aufruf in den oben im Stack, pausiert den globalen Ausführungskontext und führt die Funktion Ausführungskontext.

Bei jedem Aufruf einer Funktion lautet der Kontext der Funktionsausführung für diesen Aufruf: an den Anfang des Stacks, direkt über dem aktuellen Ausführungskontext. Der Aufrufstack basiert auf einem „First In, First Out“-Prinzip. Dies bedeutet, dass die meisten Der letzte Funktionsaufruf (der höchste Wert im Stack) wird ausgeführt und fährt fort. bis es geklärt ist. Wenn diese Funktion vollständig ist, wird sie vom Interpreter entfernt. aus dem Aufrufstack und dem Ausführungskontext, der diesen Funktionsaufruf enthält, wird wieder zum höchsten Element im Stapel und setzt die Ausführung fort.

Diese Ausführungskontexte erfassen alle für ihre Ausführung erforderlichen Werte. Sie werden auch die Variablen und Funktionen festgelegt, die im Rahmen des basierend auf dem übergeordneten Kontext erstellt und den Wert der Funktion Schlüsselwort this im Kontext der Funktion.

Ereignisschleife und Callback-Warteschlange

Diese sequenzielle Ausführung bedeutet, dass asynchrone Aufgaben, die Callback-Funktionen enthalten, wie das Abrufen von Daten von einem Server, das Reagieren auf Nutzerinteraktionen, oder auf mit setTimeout oder setInterval eingestellte Timer warten, blockieren auf den Hauptthread zurück, bis diese Aufgabe abgeschlossen ist oder den Vorgang unerwartet Aktueller Ausführungskontext in dem Moment, in dem der Ausführungskontext der Callback-Funktion festgelegt ist werden dem Stapel hinzugefügt. Um dies zu beseitigen, verwaltet JavaScript asynchrone Aufgaben, ein ereignisgesteuertes „Gleichzeitigkeitsmodell“, aus der „Ereignisschleife“ und die „Rückrufwarteschlange“ (manchmal auch als "Nachrichtenwarteschlange" bezeichnet).

Wenn eine asynchrone Aufgabe im Hauptthread ausgeführt wird, führt der Callback der Ausführungskontext der Funktion in die Callback-Warteschlange gestellt wird, nicht auf dem aufrufen. Die Ereignisschleife ist ein Muster, das reactor, der kontinuierlich fragt den Status des Aufrufstacks und der Callback-Warteschlange ab. Wenn es Aufgaben in und die Ereignisschleife erkennt, dass der Aufrufstack leer ist, werden Aufgaben aus der Callback-Warteschlange nacheinander in den Stack verschoben, ausgeführt haben.