Der Geltungsbereich oder der Satz von Regeln, die bestimmen, wo Ihre Variablen leben, ist eines der grundlegendsten Konzepte einer Programmiersprache. Es ist in der Tat so grundlegend, dass man leicht vergessen kann, wie subtil die Regeln sein können!
Wenn Sie genau wissen, wie die JavaScript-Engine über den Umfang "denkt", werden Sie die üblichen Fehler, die beim Heben entstehen können, nicht aufschreiben, sich darauf vorbereiten, Ihren Kopf um Verschlüsse zu wickeln, und Sie so viel näher daran werden, niemals Fehler zu schreiben je nochmal.
… Nun, es hilft Ihnen trotzdem, das Heben und Schließen zu verstehen.
In diesem Artikel werfen wir einen Blick auf:
Lassen
und const
Wechsel das SpielLass uns eintauchen.
Wenn Sie mehr über ES6 und die Verwendung von Syntax und Features zur Verbesserung und Vereinfachung Ihres JavaScript-Codes erfahren möchten, besuchen Sie die beiden folgenden Kurse:
Wenn Sie bereits eine JavaScript-Zeile geschrieben haben, wissen Sie das wo Du definieren Ihre Variablen bestimmen, wo Sie können benutzen Sie. Die Tatsache, dass die Sichtbarkeit einer Variablen von der Struktur Ihres Quellcodes abhängt, wird aufgerufen lexikalisch Umfang.
Es gibt drei Möglichkeiten, in JavaScript einen Bereich zu erstellen:
Lassen
oder const
innerhalb eines Codeblocks. Solche Deklarationen sind nur innerhalb des Blocks sichtbar. Fang
Block. Glauben Sie es oder nicht, eigentlich tut Erstellen Sie einen neuen Bereich!"streng verwenden"; var mr_global = "Herr Global"; function foo () var mrs_local = "Frau Lokal"; console.log ("Ich kann sehen" + mr_global + "und" + mrs_local + "."); function bar () console.log ("Ich kann auch sehen" + mr_global + "und" + mrs_local + "."); foo (); // Funktioniert wie erwartet try console.log ("/ ich / sehe nicht" + mrs_local + "."); catch (err) console.log ("Sie haben gerade ein" + err + "erhalten.)); let foo = "foo"; const bar = "bar"; console.log ("Ich kann" + foo + bar + "in seinem Block verwenden ..."); try console.log ("Aber nicht außerhalb."); catch (err) console.log ("Du hast gerade ein anderes" + err + "."); // wirft ReferenceError! console.log ("Beachten Sie, dass" + err + "außerhalb von 'catch' nicht existiert!")
Das obige Snippet zeigt alle drei Umfangsmechanismen. Sie können es in Node oder Firefox ausführen, aber Chrome spielt nicht gut mit Lassen
, noch.
Wir werden detailliert über jedes dieser Themen sprechen. Beginnen wir mit einem detaillierten Blick darauf, wie JavaScript ermittelt, welche Variablen zu welchem Bereich gehören.
Wenn Sie ein Stück JavaScript ausführen, passieren zwei Dinge, damit es funktioniert.
Während das Zusammenstellung Schritt, die JavaScript-Engine:
Es ist nur während Ausführung dass die JavaScript-Engine den Wert der Variablenreferenzen tatsächlich ihren Zuweisungswerten gleich setzt. Bis dahin sind sie nicht definiert
.
// Ich kann first_name überall in diesem Programm verwenden. Var first_name = "Peleke"; function popup (first_name) // Ich kann innerhalb dieser Funktion nur last_name verwenden. var last_name = "Sengstacke"; alert (erster_name + "+ letzter_name); popup (erster_name);
Lassen Sie uns durchgehen, was der Compiler macht.
Zuerst liest es die Zeile var first_name = "Peleke"
. Als nächstes bestimmt es was Umfang um die Variable zu speichern. Da wir uns auf der obersten Ebene des Skripts befinden, wird klar, dass wir im globaler Geltungsbereich. Dann speichert es die Variable Vorname
auf den globalen Geltungsbereich und initialisiert seinen Wert auf nicht definiert
.
Zweitens liest der Compiler die Zeile mit Funktions-Popup (Vorname)
. Weil der Funktion Das Schlüsselwort ist das erste in der Zeile, es erstellt einen neuen Gültigkeitsbereich für die Funktion, registriert die Definition der Funktion für den globalen Gültigkeitsbereich, und sucht nach Variablendeklarationen.
Sicher, der Compiler findet einen. Seit wir ... Haben var last_name = "Sengstacke"
In der ersten Zeile unserer Funktion speichert der Compiler die Variable Nachname
zum Geltungsbereich Pop-up
-nicht auf den globalen Geltungsbereich und setzt seinen Wert auf nicht definiert
.
Da innerhalb der Funktion keine Variablendeklarationen mehr vorhanden sind, springt der Compiler zurück in den globalen Gültigkeitsbereich. Und da gibt es keine variablen Deklarationen mehr Dort, Diese Phase ist abgeschlossen.
Beachten Sie, dass wir das eigentlich nicht gemacht haben Lauf noch nichts. Der Compiler hat an dieser Stelle nur die Aufgabe, sicherzustellen, dass er den Namen aller kennt. es ist egal Was tun sie.
Zu diesem Zeitpunkt weiß unser Programm:
Vorname
im globalen Bereich.Pop-up
im globalen Bereich.Nachname
im Rahmen von Pop-up
.Vorname
und Nachname
sind nicht definiert
.Es ist egal, dass wir diese Variablen an anderer Stelle in unserem Code zugewiesen haben. Dafür sorgt der Motor in Ausführung.
Im nächsten Schritt liest die Engine unseren Code erneut, diesmal jedoch, führt aus es.
Zuerst liest es die Zeile, var first_name = "Peleke"
. Dazu sucht die Engine die aufgerufene Variable Vorname
. Da der Compiler bereits eine Variable mit diesem Namen registriert hat, findet die Engine diese und setzt ihren Wert auf "Peleke"
.
Als nächstes liest es die Zeile, Funktions-Popup (Vorname)
. Da sind wir nicht ausführen Die Funktion hier interessiert die Engine nicht und überspringt sie.
Schließlich liest es die Zeile popup (vorname)
. Seit wir sind Hier eine Funktion ausführen, die Engine:
Pop-up
Vorname
Pop-up
als Funktion, den Wert von übergeben Vorname
als ParameterWenn es ausgeführt wird Pop-up
, es durchläuft den gleichen Prozess, aber diesmal in der Funktion Pop-up
. Es:
Nachname
Nachname
Wert gleich "Sengstacke"
warnen
, Ausführen als Funktion mit "Peleke Sengstacke"
als dessen ParameterEs stellt sich heraus, dass unter der Motorhaube viel mehr los ist, als wir gedacht hätten!
Nachdem Sie nun verstanden haben, wie JavaScript den Code liest und ausführt, den Sie schreiben, sind wir bereit, etwas näher bei sich zu Hause anzugehen: wie das Heben funktioniert.
Beginnen wir mit etwas Code.
Bar(); Funktionsleiste () if (! foo) alert (foo + "? Dies ist seltsam ..."); var foo = "bar"; gebrochen (); // TypeError! var broken = function () alert ("Dieser Alarm wird nicht angezeigt!");
Wenn Sie diesen Code ausführen, werden Sie drei Dinge bemerken:
foo
bevor Sie es zuweisen, aber sein Wert ist nicht definiert
.gebrochen
bevor du es definierst, bekommst du aber eine TypeError
.Bar
bevor Sie es definieren, und es funktioniert wie gewünscht.Heben verweist auf die Tatsache, dass JavaScript alle unsere deklarierten Variablennamen verfügbar macht überall in ihren Bereichen - einschließlich Vor wir ordnen ihnen zu.
Die drei Fälle in diesem Snippet sind die drei Fälle, die Sie in Ihrem eigenen Code beachten müssen. Wir gehen jeden Schritt einzeln durch.
Denken Sie daran, wenn der JavaScript-Compiler eine Zeile wie liest var foo = "bar"
, es:
foo
bis zum nächsten Bereichfoo
zu undefiniertDen Grund können wir verwenden foo
Bevor wir es zuweisen, ist es, weil, wenn die Engine die Variable mit diesem Namen nachschlägt, es tut existieren. Deshalb wirft es keine ReferenceError
.
Stattdessen erhält es den Wert nicht definiert
, und versucht, das zu verwenden, was Sie danach gefragt haben. Normalerweise ist das ein Fehler.
Wenn wir dies berücksichtigen, können wir uns vorstellen, dass JavaScript in unserer Funktion erkannt wird Bar
ist eher so:
Funktionsleiste () var foo; // undefined if (! foo) //! undefined ist wahr, also Alarm (foo + "? Dies ist seltsam ..."); foo = "bar";
Dies ist das Erste Regel beim Hochziehen, wenn du willst: Variablen stehen zur Verfügung in ihrem gesamten Umfang, aber den Wert haben nicht definiert
bis Ihr Code ihnen zuweist.
Ein allgemeines JavaScript-Idiom ist, alle Ihre zu schreiben var
Erklärungen oben in ihrem Geltungsbereich, anstatt dort, wo Sie sie zuerst verwenden. Um Doug Crockford zu beschreiben, hilft dies Ihrem Code lesen eher wie es läuft.
Wenn Sie darüber nachdenken, macht das Sinn. Es ist ziemlich klar warum Bar
verhält sich so, wie wir es tun, wenn wir unseren Code so schreiben, wie JavaScript ihn liest, oder? Warum also nicht einfach so schreiben alles die Zeit?
Die Tatsache, dass wir eine bekommen haben TypeError
als wir versuchten, es auszuführen gebrochen
Bevor wir definiert haben, handelt es sich nur um einen Sonderfall der ersten Regel des Anhebens.
Wir haben eine Variable definiert, genannt gebrochen
, die der Compiler im globalen Gültigkeitsbereich registriert und gleich setzt nicht definiert
. Wenn wir versuchen, es auszuführen, sucht der Motor nach dem Wert von gebrochen
, findet, dass es ist nicht definiert
, und versucht es auszuführen nicht definiert
als eine Funktion.
Offensichtlich, nicht definiert
ist nicht eine Funktion, deshalb bekommen wir eine TypeError
!
Schließlich erinnern wir uns daran, dass wir anrufen konnten Bar
bevor wir es definiert haben. Dies liegt an der Zweite Regel des Anhebens: Wenn der JavaScript-Compiler eine Funktionsdeklaration findet, erhält er beide Namen und Definition oben im Geltungsbereich verfügbar. Nochmals unseren Code umschreiben:
Funktionsleiste () if (! foo) alert (foo + "? Dies ist seltsam ..."); var foo = "bar"; var gebrochen; // undefined bar (); // bar ist bereits definiert, führt fine broken () aus; // Kann undefined nicht ausführen! broken = function () alert ("Dieser Alarm wird nicht angezeigt!");
Wieder macht es viel mehr Sinn, wenn Sie schreiben als JavaScript liest, denkst du nicht?
Zu überprüfen:
nicht definiert
bis zur Abtretung.Schauen wir uns jetzt zwei neue Tools an, die etwas anders funktionieren: Lassen
und const
.
Lassen
, const
, & die temporale tote Zone nicht wie var
Deklarationen, mit deklarierte Variablen Lassen
und const
nicht vom Compiler gehisst werden.
Zumindest nicht genau.
Erinnern Sie sich, wie wir anrufen konnten gebrochen
, bekam aber eine TypeError
weil wir versucht haben, auszuführen nicht definiert
? Wenn wir definiert hätten gebrochen
mit Lassen
, wir hätten bekommen ReferenceError
, stattdessen:
"streng verwenden"; // Sie müssen "strict" verwenden, um dies in Node zu versuchen broken (); // Referenzfehler! let broken = function () alert ("Dieser Alarm wird nicht angezeigt!");
Wenn der JavaScript-Compiler im ersten Durchlauf Variablen in seinem Gültigkeitsbereich registriert, wird er behandelt Lassen
und const
anders als es tut var
.
Wenn es einen findet var
Deklaration registrieren wir den Namen der Variablen in ihrem Gültigkeitsbereich und initialisieren ihren Wert sofort mit nicht definiert
.
Mit Lassen
, jedoch der Compiler tut Registrieren Sie die Variable in ihrem Gültigkeitsbereich, aber nichtseinen Wert auf initialisieren nicht definiert
. Stattdessen wird die Variable nicht initialisiert, bis um Die Engine führt Ihre Zuweisungsanweisung aus. Durch den Zugriff auf den Wert einer nicht initialisierten Variablen wird a ReferenceError
, Das erklärt, warum das obige Snippet wirft, wenn wir es ausführen.
Der Abstand zwischen dem Anfang von oben im Gültigkeitsbereich von a Lassen
Deklaration und die Zuweisungsanweisung heißt Temporale tote Zone. Der Name rührt von der Tatsache her, obwohl der Motor weiß über eine Variable namens foo
am oberen Rand des Bereichs von Bar
, Die Variable ist "tot", weil sie keinen Wert hat.
… Auch weil es Ihr Programm tötet, wenn Sie versuchen, es frühzeitig zu verwenden.
Das const
Das Schlüsselwort funktioniert genauso wie Lassen
, mit zwei wesentlichen unterschieden:
const
.const
.Das garantiert das const
werden immerhaben den Wert, den Sie ursprünglich zugewiesen haben.
// Das ist legal const React = required ('reagieren'); // Das ist absolut nicht legal const crypto; crypto = required ('crypto');
Lassen
und const
unterscheiden sich von var
auf eine andere Weise: die Größe ihrer Bereiche.
Wenn Sie eine Variable mit deklarieren var
, es ist sichtbar möglichst weit oben in der Gültigkeitsbereichskette - normalerweise oben in der nächstgelegenen Funktionsdeklaration oder im globalen Gültigkeitsbereich, wenn Sie sie auf oberster Ebene deklarieren.
Wenn Sie eine Variable mit deklarieren Lassen
oder const
, es ist jedoch sichtbar als örtlich wie möglich-nur innerhalb des nächsten Blocks.
EIN Block ist ein Codeabschnitt, der durch geschweifte Klammern hervorgehoben wird ob
/sonst
Blöcke, zum
Schleifen und explizit "blockiert" Codeabschnitte, wie in diesem Snippet.
"streng verwenden"; let foo = "foo"; if (foo) const bar = "bar"; var foobar = foo + bar; console.log ("Ich kann" + Balken + "in diesem Block sehen."); try console.log ("Ich kann" + foo + "in diesem Block sehen, aber nicht" + bar + ".");); catch (err) console.log ("Du hast ein" + err + "."); try console.log (foo + bar); // wird wegen 'foo' ausgelöst, aber beide sind undefiniert catch (err) console.log ("Sie haben gerade ein" + err + "."); console.log (foobar); // Funktioniert gut
Wenn Sie eine Variable mit deklarieren const
oder Lassen
In einem Block ist es nur sichtbar innerhalb des Blocks und nur nachdem Sie es zugewiesen haben.
Eine mit deklarierte Variable var
, ist jedoch sichtbar so weit wie möglich-in diesem Fall im globalen Bereich.
Wenn Sie sich für die winzigsten Details von interessieren Lassen
und const
, Lesen Sie in Exploring ES6: Variables and Scoping nach, was Dr. Rauschmayer dazu zu sagen hat, und werfen Sie einen Blick auf die MDN-Dokumentation.
diese
& PfeilfunktionenAn der Oberfläche, diese
scheint nicht viel mit Umfang zu tun zu haben. Und tatsächlich tut es JavaScript nicht löse die Bedeutung von diese
gemäß den Regeln des Geltungsbereichs, über die wir hier gesprochen haben.
Zumindest normalerweise nicht. JavaScript tut es notorisch nicht Löse die Bedeutung der diese
Keyword basierend auf dem Ort, an dem Sie es verwendet haben:
var foo = name: 'foo', sprachen: ['spanisch', 'französisch', 'italienisch'], sprich: function speak () this.languages.forEach (function (sprache) console.log (this.) Name + "spricht" + Sprache + ".");); foo.speak ();
Die meisten von uns würden es erwarten diese
meinen foo
in der für jeden
Schleife, weil es das war, was es direkt außerhalb bedeutete. Mit anderen Worten, wir erwarten, dass JavaScript die Bedeutung von löst diese
lexikalisch.
Aber es tut nicht.
Stattdessen wird ein erstellt Neu diese
in jeder Funktion, die Sie definieren, und entscheidet anhand dessen, was sie bedeutet Wie Sie rufen die Funktion nicht auf woher du hast es definiert.
Dieser erste Punkt ähnelt dem Fall der Neudefinition irgendein Variable in einem untergeordneten Bereich:
Funktion foo () var bar = "bar"; function baz () // Die Wiederverwendung von Variablennamen wie dieser wird "Shadowing" genannt. var bar = "BAR"; console.log (Bar); // BAR baz (); foo (); // BAR
Ersetzen Bar
mit diese
, und das Ganze sollte sich sofort aufklären!
Traditionell bekommen diese
Um zu funktionieren, wie wir erwarten, dass einfache lexikalisch definierte Variablen funktionieren, ist eine von zwei Problemumgehungen erforderlich:
var foo = name: 'foo', sprachen: ['spanisch', 'französisch', 'italienisch'], speak_self: function speak_s () var self = this; self.languages.forEach (function (language) console.log (self.name + "spricht +" + ".");), speak_bound: function speak_b () this.languages.forEach (function (language ) console.log (this.name + "spricht" + language + "."); .bind (foo)); // Häufiger: .bind (this); ;
Im selbst sprechen
, wir retten die Bedeutung von diese
auf die Variable selbst
, und verwenden Das Variable, um die gewünschte Referenz zu erhalten. Im speak_bound
, wir gebrauchen binden
zu permanent Punkt diese
zu einem bestimmten Objekt.
ES2015 bietet uns eine neue Alternative: Pfeilfunktionen.
Im Gegensatz zu "normalen" Funktionen haben Pfeilfunktionen dies nicht Schatten ihres übergeordneten Bereichs diese
Wert durch eigene Einstellung. Sie lösen vielmehr seine Bedeutung auf lexikalisch.
Mit anderen Worten, wenn Sie verwenden diese
In einer Pfeilfunktion sucht JavaScript seinen Wert wie jede andere Variable.
Zunächst wird der lokale Gültigkeitsbereich für a geprüft diese
Wert. Da die Pfeilfunktionen keinen Wert einstellen, wird kein gefunden. Als nächstes prüft es die Elternteil Spielraum für ein diese
Wert. Wenn es einen findet, wird es stattdessen verwendet.
Dadurch können wir den Code wie folgt umschreiben:
var foo = name: 'foo', sprachen: ['spanisch', 'französisch', 'italienisch'], sprich: function speak () this.languages.forEach ((language) => console.log (this .name + "spricht" + sprache + "."););
Wenn Sie mehr über die Pfeilfunktionen erfahren möchten, werfen Sie einen Blick auf den hervorragenden Kurs von Envato Tuts + Instructor Dan Wellman zu JavaScript ES6 Fundamentals sowie die MDN-Dokumentation zu den Pfeilfunktionen.
Wir haben bis jetzt eine Menge Boden zurückgelegt! In diesem Artikel haben Sie Folgendes gelernt:
Lassen
oder const
vor dem Einsatz wirft ein ReferenceError
, und dass solche Variablen auf den nächsten Block beschränkt sind.diese
, und umgehen Sie die traditionelle dynamische Bindung.Sie haben auch die zwei Regeln des Hebens gesehen:
var
Deklarationen sind in allen definierten Bereichen verfügbar, haben jedoch den Wert nicht definiert
bis Ihre Zuweisungsanweisungen ausgeführt werden.Ein guter nächster Schritt ist es, Ihr Wissen über JavaScripts Geltungsbereiche zu nutzen, um Ihren Kopf um Verschlüsse zu wickeln. Informieren Sie sich dazu über Kyle Simpsons Scopes & Closures.
Schließlich gibt es noch viel mehr zu sagen diese
als ich hier abdecken konnte. Wenn das Schlüsselwort immer noch nach so viel schwarzer Magie aussieht, werfen Sie einen Blick auf diese & Objekt-Prototypen, um den Kopf zu bekommen.
Nehmen Sie in der Zwischenzeit das, was Sie gelernt haben, und schreiben Sie weniger Fehler!
Lernen Sie JavaScript: Das komplette Handbuch
Wir haben ein umfassendes Handbuch zusammengestellt, mit dessen Hilfe Sie JavaScript erlernen können, egal ob Sie gerade erst als Webentwickler anfangen oder sich mit fortgeschrittenen Themen befassen möchten.