Prototyp-Vererbung
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 einerdo
...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.