Warum werden einige Variablen, die mit let inside einer Funktion deklariert wurden, in einer anderen Funktion verfügbar, während andere zu einem Referenzfehler führen?

158

Ich kann nicht verstehen, warum Variablen so seltsam wirken, wenn sie in einer Funktion deklariert werden.

  1. In der firstFunktion deklariere ich mit letden Variablen bund cmit dem Wert 10 :

    b = c = 10;

    In der secondFunktion zeige ich:

    b + ", " + c

    Und das zeigt:

    10, 10
  2. Auch in firstFunktion deklariere ich amit Wert 10 :

    let a = b = c = 10;

    Aber in der secondFunktion zeigt es einen Fehler:

    Variable kann nicht gefunden werden: a

  3. Jetzt firstdeklariere ich in der Funktion dmit Wert 20 :

    var d = 20;

    Aber in der secondFunktion zeigt es den gleichen Fehler wie zuvor, aber mit der Variablen d:

    Variable kann nicht gefunden werden: d

Beispiel:

function first() {
  let a = b = c = 10;
  var d = 20;
  second();
}

function second() {
  console.log(b + ", " + c); //shows "10, 10"

  try{ console.log(a); }  // Rreference error
  catch(e){ console.error(e.message) }

  try{ console.log(d); } // Reference error
  catch(e){ console.error(e.message) }
}
first()

meckern interteiment
quelle
31
Sie erklärt Globals, da bund cnicht mit dem Präfix varStichwort. aund dsind lokal zu first.
VLAZ
1
Kommentare sind nicht für eine ausführliche Diskussion gedacht. Im Chat wurde ein tangentiales Gespräch darüber archiviert, ob dies eine gute Interviewfrage wäre .
Cody Gray
1
Dies erinnert mich an eine ähnliche Situation in Visual Basic. Dim Apple, Banana, Pear As Fruitbedeutet Dim Apple / Dim Banana / Dim Pear As Fruitund nicht Dim Apple As Fruit / ....
Eric Lippert

Antworten:

179

Es ist, weil Sie tatsächlich sagen:

c = 10;
b = c;
let a = b;

Und nicht das, was Sie zu sagen glauben:

let a = 10;
let b = 10;
let c = 10;

Sie werden feststellen, dass unabhängig von der Anzahl der Variablen, die Sie Ihrer Kette hinzufügen, nur die ersten (a) den Fehler verursachen.

Dies liegt daran, dass "let" Ihre Variable auf den Block (oder "lokal", mehr oder weniger "in Klammern") bezieht, in dem Sie sie deklarieren.

Wenn Sie eine Variable ohne "let" deklarieren, wird die Variable global erfasst.

In der Funktion, in der Sie Ihre Variablen festlegen, erhält alles den Wert 10 (Sie können dies im Debugger sehen, wenn Sie einen Haltepunkt setzen). Wenn Sie in dieser ersten Funktion ein Konsolenprotokoll für a, b, c einfügen, ist alles in Ordnung.

Aber sobald Sie diese Funktion verlassen, "verschwindet" die erste (a) - und denken Sie daran, technisch gesehen in der Reihenfolge der Zuordnung, es ist die letzte - "wieder können Sie dies in der sehen Debugger, wenn Sie in der zweiten Funktion einen Haltepunkt setzen), die anderen beiden (oder wie viele Sie hinzufügen) jedoch noch verfügbar sind.

Dies liegt daran, dass "let" NUR FÜR DIE ERSTE VARIABLE in der Kette gilt (also nur für lokale Bereiche) - wiederum technisch gesehen die letzte, die deklariert und mit einem Wert versehen wurde. Der Rest hat technisch gesehen kein "Lassen" vor sich. Diese werden also technisch global deklariert (dh auf dem globalen Objekt), weshalb sie in Ihrer zweiten Funktion angezeigt werden.

Probieren Sie es aus: Entfernen Sie das Schlüsselwort "let". Alle Ihre Vars sind jetzt verfügbar.

"var" hat einen ähnlichen Effekt im lokalen Bereich, unterscheidet sich jedoch darin, wie die Variable "gehisst" wird. Dies sollten Sie unbedingt verstehen, aber nicht direkt mit Ihrer Frage in Verbindung bringen.

(Übrigens würde diese Frage genug Pro-JS-Entwickler überraschen, um sie zu einer guten zu machen).

Es wird dringend empfohlen, Zeit mit den Unterschieden zu verbringen, wie Variablen in JS deklariert werden können: ohne Schlüsselwort, mit "let" und mit "var".

Tim Consolazio
quelle
4
Es ist gleichzeitig das Beste und das Schlechteste an der Programmierung: Der Computer macht genau das , was Sie ihm sagen. Nicht unbedingt das, was Sie ihm sagen wollten. Programme sind perfekt. Wir schaffen die Probleme.
Niet the Dark Absol
8
@Thevs Warum empfehlen varSie letim Rahmen dieser Antwort über? Ich verstehe nicht
Klaycon
4
@ Thevs Ich stimme dir überhaupt nicht zu. varkann bei unachtsamer Verwendung zu Fehlern neigen. Überprüfen Sie diese Geige
Cid
2
@Thevs in welchem Fall nicht varhat jeden Vorteil gegenüber let? Lassen Sie mich klarstellen: Ein moderner Kontext, wenn beide Optionen sind und ich nach Code frage, den man schreiben sollte . Als ich dies zuvor gefragt habe, habe ich Antworten zu "Sie können eine Variable mit neu deklarieren mit var" erhalten. In diesem Fall muss ich die Leute daran erinnern, dass Sie Variablen nicht erneut deklarieren sollten . Dies ist entweder ein Fehler oder ein Fehler in der Logik des Codes. Der Vorteil einer erneuten Deklaration besteht also darin, dass Sie fehlerhaften Code schreiben können. Ich habe noch keinen vernünftigen Grund dafür gesehen, varwann dies letebenfalls eine Option ist.
VLAZ
2
Kreide eine weitere Marke gegen Javascript. Alle Variablen sind global, sofern sie nicht als lokal deklariert sind. :(
JRE
68

In der Funktion first()werden Variablen bund cim laufenden Betrieb ohne Verwendung von varoder erstellt let.

let a = b = c = 10; // b and c are created on the fly

Ist anders als

let a = 10, b = 10, c = 10; // b and c are created using let (note the ,)

Sie werden implizit global. Deshalb sind sie in verfügbarsecond()

Aus der Dokumentation

Durch das Zuweisen eines Werts zu einer nicht deklarierten Variablen wird dieser implizit als globale Variable erstellt (er wird eine Eigenschaft des globalen Objekts), wenn die Zuweisung ausgeführt wird.

Um dies zu vermeiden, können Sie "use strict"Fehler verwenden, wenn eine nicht deklarierte Variable verwendet wird

"use strict"; // <-------------- check this

function first() {
   /*
    * With "use strict" c is not defined.
    * (Neither is b, but since the line will be executed from right to left,
    * the variable c will cause the error and the script will stop)
    * Without, b and c become globals, and then are accessible in other functions
    */
   let a = b = c = 10;
   var d = 20;
   second();
}

function second() {
   console.log(b + ", " + c); //reference error
   console.log(a); //reference error
   console.log(d); //reference error
}

first();

Cid
quelle
15
Außerdem: let a = 10, b = 10, c = 10;oder let a, b, c; a = b = c = 10;wäre sonst die richtige Art, die Variablen zu deklarieren.
Rickard Elimää
Was ist also mit dem Variablen b im strengen Modus?
Tick20
2
@ Tick20 Die Variable bwird nicht ausgewertet / erreicht, der Fehler tritt in der Zeile auf let a = b = c = 10;, die von rechts nach links gelesen wird . cDa dies die erste verursachende Variable ist ReferenceError, wird der Rest der Zeile nicht ausgeführt (das Skript wurde gestoppt)
Cid
2
so etwas let a = 10, b = a, c = b;ist auch gültig
Kaddath
8
vor allem für die "Verwendung streng" bewertet. Im Rahmen einer Interviewfrage wäre dies auch der Beginn meines Kommentars zu diesem Code.
Pac0
23

Bevor Sie die Dinge als seltsam bezeichnen, sollten Sie zunächst einige Grundlagen kennen:

var und let werden beide für die Variablendeklaration in JavaScript verwendet. Zum Beispiel,

var one = 1;
let two = 2;

Variablen können auch ohne varoder deklariert werden let. Zum Beispiel,

three = 3;

Nun ist der Unterschied zwischen den oben genannten Ansätzen ist , dass:

var ist funktionsbezogen

und

let ist blockumfangig.

Der Umfang der ohne var/ letkeyword deklarierten Variablen wird global, unabhängig davon, wo sie deklariert sind.

Auf globale Variablen kann von überall auf der Webseite zugegriffen werden (nicht empfohlen, da Globale versehentlich geändert werden können).

Schauen wir uns nun nach diesen Konzepten den fraglichen Code an:

 function first() {
   let a = b = c = 10;
   /* The above line means:
    let a=10; // Block scope
    b=10; // Global scope
    c=10; // Global scope
    */

   var d = 20; // Function scope
   second();
}

function second() {
   alert(b + ", " + c); // Shows "10, 10" //accessible because of global scope
   alert(a); // Error not accessible because block scope has ended
   alert(d); // Error not accessible because function scope has ended
}
fatimasajjad
quelle
1
Sie haben einen Teil von JonoJames 'Antwort plagiiert . Warum?
Peter Mortensen
2
Es tut mir leid, aber ich hatte keine solche Absicht, etwas Ähnliches könnte da sein, weil wir möglicherweise eine Information aus derselben Quelle gesammelt haben.
Fatimasajjad
2
Es kann sein, dass beide Antworten Inhalte enthalten, die ohne ersichtliches Zitieren aus derselben Originalquelle kopiert wurden - möglicherweise tutorialsteacher.com/javascript/javascript-variable . Das Vorhandensein von Plagiaten ist offensichtlich, da ein Grammatikfehler aus dem Original reproduziert wird - "der Umfang der ohne das varSchlüsselwort deklarierten Variablen wird global, unabhängig davon, wo er deklariert wird" sollte entweder "der Umfang ... wird" oder "der Bereiche ... werden " . Um die genauen Wörter eines anderen zu verwenden, muss zitiert werden, ob von hier oder anderswo. meta.stackexchange.com/q/160071/211183
Michael - sqlbot
Danke Jungs, ich habe einen Referenzlink für die Quelle hinzugefügt.
Fatimasajjad
6

Variablen, die das letSchlüsselwort verwenden, sollten nur im Rahmen des Blocks und nicht in einer externen Funktion verfügbar sein ...

Jede Variable, die Sie auf diese Weise deklarieren, verwendet letoder nicht var. In der Variablendeklaration fehlt ein Komma.

Es wird nicht empfohlen , eine Variable ohne das varSchlüsselwort zu deklarieren . Es kann versehentlich eine vorhandene globale Variable überschreiben. Der Umfang der ohne das varSchlüsselwort deklarierten Variablen wird global, unabhängig davon, wo sie deklariert sind. Auf globale Variablen kann von überall auf der Webseite zugegriffen werden.

function first() {
   let a = 10;
   let b = 10;
   let c = 10;
   var d = 20;
   second();
}

function second() {
   console.log(b + ", " + c); //shows "10, 10"
   console.log(a); //reference error
   console.log(d); //reference error
}

first();

JonoJames
quelle
3

Es liegt daran, dass wenn Sie nicht verwenden letoder vardann Variable im laufenden Betrieb deklariert wird, es besser ist, wenn Sie wie folgt deklarieren.

let a = 10;
let b = 10;
let c = 10;
Mac Rathod
quelle
2

Das seltsame Problem wird durch Gültigkeitsbereichsregeln in JavaScript verursacht

function first() {
   let a = b = c = 10; // a is in local scope, b and c are in global scope
   var d = 20; // d is in local scope
   second(); // will have access to b and c from the global scope
}

Angenommen, Sie möchten 3 lokale Variablen deklarieren , die mit demselben Wert (100) initialisiert wurden. Ihr erstes () wird wie folgt aussehen. In diesem Fall hat second () keinen Zugriff auf eine der Variablen, da diese lokal für first () sind.

function first() {
   let a = 100; // a is in local scope init to 100
   let b = a; // b is in local scope init to a
   let c = b // c is in local scope init to b

   var d = 20; // d is in local scope
   second(); // will not have access a, b, c, or d
}

Wenn Sie jedoch globale Variablen möchten, sieht Ihr first () wie folgt aus. In diesem Fall hat second Zugriff auf alle Variablen, da sie sich im globalen Bereich befinden

function first() {
   a = 100; // a is in global scope
   b = a; // b is in global scope
   c = b // c is in global scope

   d = 20; // d is in global scope
   second(); // will have access to a, b, c, and d from the global scope
}

Lokale Variablen (auch bekannt als im Codeblock zugänglich, in dem sie deklariert sind).
Ein Codeblock ist ein beliebiges {} mit Codezeilen dazwischen.

  • function () {var, let, const hier ist für die gesamte Funktion zugänglich},
  • for () {var hier ist für den äußeren Bereich zugänglich, let, const ist nur hier zugänglich},
  • usw.

Globale Variablen (auch bekannt als im globalen Bereich zugänglich).
Diese Variablen werden an das globale Objekt angehängt. Das globale Objekt ist umgebungsabhängig. Es ist das Fensterobjekt in Browsern.

Besonderer Hinweis: Sie können Variablen in JavaScript deklarieren, ohne die Schlüsselwörter var, let, const zu verwenden. Eine auf diese Weise deklarierte Variable wird an das globale Objekt angehängt und ist daher im globalen Bereich verfügbar.
a = 100 // is valid and is in global scope

Einige Artikel zur weiteren Lektüre: https://www.sitepoint.com/demystifying-javascript-variable-scope-hoisting/ https://scotch.io/tutorials/understanding-scope-in-javascript https: //www.digitalocean .com / community / tutorials / verständnisvariablen-scope-hoisting-in-javascript

Funwie
quelle
0

Der Hauptunterschied besteht in den Geltungsbereichsregeln. Mit dem Schlüsselwort var deklarierte Variablen werden auf den unmittelbaren Funktionskörper (daher der Funktionsumfang) beschränkt, während Variablen auf den mit {} bezeichneten unmittelbaren umschließenden Block (daher der Blockumfang) abgelegt werden. Und wenn du sagst

c = 10;
b = c;
let a = b;

c und b haben eine Lebensdauer, die Spaß macht, aber nur eine Blockspanne hat. Wenn Sie versuchen, durch Referenzieren auf a zuzugreifen, wird immer ein Fehler angezeigt, aber c und b sind global, sodass dies nicht der Fall ist. Sie werden feststellen, dass es egal wie viele sind Variablen, die Sie zu Ihrer Kette hinzufügen, sind nur die ersten (a), die den Fehler verursachen. Dies liegt daran, dass "let" Ihre Variable auf den Block bezieht (oder "lokal" mehr oder weniger "in Klammern" bedeutet). Wenn Sie eine Variable ohne "let" deklarieren, wird die Variable global erfasst. In der Funktion, in der Sie Ihre Variablen festlegen, erhält alles den Wert 10 (Sie können dies im Debugger sehen, wenn Sie eine eingeben Haltepunkt). Wenn Sie ein Konsolenprotokoll für a, b, c in diese erste Funktion einfügen, ist alles in Ordnung. Sobald Sie diese Funktion verlassen, die erste (a) - und denken Sie erneut daran:

Muhammad Fahad
quelle
0

Hier sind die 3 interessanten Aspekte von Variablendeklarationen in JavaScript:

  1. var beschränkt den Gültigkeitsbereich der Variablen auf den Block, in dem sie definiert ist. ( 'var' ist für den lokalen Bereich .)

  2. let ermöglicht das vorübergehende Überschreiben des Werts einer externen Variablen innerhalb eines Blocks.

  3. Durch einfaches Deklarieren einer Variablen ohne var oder let wird die Variable global, unabhängig davon, wo sie deklariert ist.

Hier ist eine Demo von let , die neueste Ergänzung der Sprache:

// File name:  let_demo.js

function first() {
   a = b = 10
   console.log("First function:    a = " + a)
   console.log("First function:    a + b = " + (a + b))
}

function second() {
    let a = 5
    console.log("Second function:    a = " + a)
    console.log("Second function:    a + b = " + (a + b))
}

first()   

second()

console.log("Global:    a = " + a)
console.log("Global:    a + b = " + (a + b))

Ausgabe:

$ node let_demo.js 

First function:    a = 10
First function:    a + b = 20

Second function:    a = 5
Second function:    a + b = 15

Global:    a = 10
Global:    a + b = 20

Erläuterung:

Die Variablen a und b wurden in ' first () ' ohne var- oder let-Schlüsselwörter delcared .

Daher sind a und b global und daher während des gesamten Programms zugänglich.

In der Funktion 'second' setzt die Anweisung 'let a = 5' den Wert von ' a ' vorübergehend auf ' 5 ', nur im Rahmen der Funktion.

Außerhalb des Bereichs von ' second () ', IE, wird im globalen Bereich der Wert von ' a ' wie zuvor definiert sein.

Gopinath
quelle