Perché alcune variabili dichiarate usando let inside una funzione diventano disponibili in un'altra funzione, mentre altre generano un errore di riferimento?


158

Non riesco a capire perché le variabili si comportino in modo strano quando dichiarate all'interno di una funzione.

  1. Nella firstfunzione dichiaro con letle variabili be ccon il valore 10 :

    b = c = 10;

    Nella secondfunzione mostro:

    b + ", " + c

    E questo mostra:

    10, 10
  2. Anche in firstfunzione dichiaro acon valore 10 :

    let a = b = c = 10;

    Ma nella secondfunzione mostra un errore:

    Impossibile trovare la variabile: a

  3. Ora nella firstfunzione dichiaro dcon valore 20 :

    var d = 20;

    Ma nella secondfunzione mostra lo stesso errore di prima, ma con la variabile d:

    Impossibile trovare la variabile: d

Esempio:

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()


31
Stai dichiarando globali, poiché be cnon hanno il prefisso con la varparola chiave. ae dsono locali a first.
VLAZ,

1
I commenti non sono per una discussione estesa; una conversazione tangenziale sul fatto che questa sarebbe una buona domanda di intervista è stata archiviata in chat .
Cody Grey

1
Questo mi ricorda una situazione simile in Visual Basic; Dim Apple, Banana, Pear As Fruitsignifica Dim Apple / Dim Banana / Dim Pear As Fruite non Dim Apple As Fruit / ....
Eric Lippert,

Risposte:


179

È perché in realtà stai dicendo:

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

E non quello che pensi di dire, che è:

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

Noterai che non importa quante variabili aggiungi alla tua catena, sarà solo la prima (a) a causare l'errore.

Questo perché "let" definisce la propria variabile nel blocco (o, "localmente", che significa più o meno "tra parentesi") in cui viene dichiarata.

Se si dichiara una variabile senza "let", la variabile viene portata a livello globale.

Quindi, nella funzione in cui si impostano le variabili, tutto ottiene il valore 10 (è possibile vederlo nel debugger se si inserisce un punto di interruzione). Se si inserisce un registro della console per a, b, c in quella prima funzione, tutto va bene.

Ma non appena lasci quella funzione, la prima (a) - e ancora, tieni presente, tecnicamente nell'ordine di assegnazione, è l'ultima-- "scompare" (di nuovo, puoi vedere questo nella debugger se si imposta un punto di interruzione nella seconda funzione), ma gli altri due (o comunque molti aggiunti) sono ancora disponibili.

Questo perché, "let" SI APPLICA SOLO A (quindi solo localmente) IL PRIMO VARIABILE - di nuovo, che è tecnicamente l'ultimo a essere dichiarato e assegnato un valore - nella catena. Il resto tecnicamente non ha "lasciato" davanti a loro. Quindi quelli sono tecnicamente dichiarati a livello globale (cioè sull'oggetto globale), motivo per cui compaiono nella tua seconda funzione.

Provalo: rimuovi la parola chiave "let". Tutti i tuoi var saranno ora disponibili.

"var" ha un simile effetto di portata locale, ma differisce nel modo in cui la variabile viene "sollevata", il che è qualcosa che dovresti assolutamente capire, ma che non è direttamente coinvolto nella tua domanda.

(A proposito, questa domanda sarebbe sconcertante abbastanza per gli sviluppatori JS professionisti da renderlo buono).

Consiglio vivamente di dedicare tempo alle differenze nel modo in cui le variabili possono essere dichiarate in JS: senza una parola chiave, con "let" e con "var".


4
È contemporaneamente la cosa migliore e peggiore della programmazione: il computer farà esattamente ciò che gli dici di fare. Non necessariamente quello che intendevi dirgli di fare. I programmi sono perfetti. Creiamo i problemi.
Niet the Dark Absol,

8
@Thevs Perché si raccomandano varsopra letnel contesto di questa risposta? Non capisco.
Klaycon,

4
@Thevs Non sono assolutamente d'accordo con te. varpuò essere soggetto a bug se usato con noncuranza. Controlla questo violino
Cid

2
@Thevs in che caso varha qualche vantaggio rispetto let? Vorrei chiarire: un contesto moderno in cui entrambe sono opzioni e chiedo il codice che si dovrebbe scrivere. Quando l'ho chiesto prima, ho ricevuto risposte su "puoi ri-dichiarare una variabile con var" nel qual caso devo ricordare alle persone che non dovresti dichiarare nuovamente le variabili . Questo è un bug o un errore nella logica del codice - quindi il vantaggio della ri-dichiarazione è ... che ti permette di scrivere codice difettoso. Devo ancora vedere una ragione ragionevole a favore di varquando letè anche un'opzione.
VLAZ

2
Calcola un altro segno con JavaScript. Tutte le variabili sono globali se non dichiarate locali. :(
JRE

68

Nella funzione first(), le variabili be cvengono create al volo, senza usare varo let.

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

È diverso da

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

Diventano globali impliciti. Ecco perché sono disponibili insecond()

Dalla documentazione

L'assegnazione di un valore a una variabile non dichiarata lo crea implicitamente come variabile globale (diventa una proprietà dell'oggetto globale) quando viene eseguita l'assegnazione.

Per evitare ciò, è possibile utilizzare "use strict"che fornirà errori quando si utilizza una variabile non dichiarata

"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();


15
Inoltre: let a = 10, b = 10, c = 10;o let a, b, c; a = b = c = 10;sarebbe altrimenti il ​​modo corretto di dichiarare le variabili.
Rickard Elimää,

Quindi, con la modalità rigorosa, che dire della variabile b?
Tick20

2
@ Tick20 la variabile bnon verrà valutata / raggiunta, l'errore si verificherà sulla riga let a = b = c = 10;, letto da destra a sinistra . cessendo la prima variabile che causa ReferenceError, il resto della riga non verrà eseguito (lo script si è fermato)
Cid

2
qualcosa di simile let a = 10, b = a, c = b;è valido anche
Kaddath il

8
votato principalmente per l '"uso rigoroso". Nel contesto di una domanda di intervista, sarebbe anche l'inizio del mio commento su questo codice.
Pac0

23

Prima di definire cose strane, conosciamo prima alcune nozioni di base:

var e let sono entrambi usati per la dichiarazione delle variabili in JavaScript. Per esempio,

var one = 1;
let two = 2;

Le variabili possono anche essere dichiarate senza usare varo let. Per esempio,

three = 3;

Ora la differenza tra gli approcci di cui sopra è che:

var è nell'ambito della funzione

e

let è con ambito di blocco.

mentre l'ambito delle variabili dichiarate senza var/ letparola chiave diventa globale indipendentemente da dove viene dichiarato.

È possibile accedere alle variabili globali da qualsiasi punto della pagina Web (sconsigliato perché i globi possono essere modificati accidentalmente).

Ora secondo questi concetti diamo un'occhiata al codice in questione:

 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
}

1
Hai plagiato parte della risposta di JonoJames . Perché?
Peter Mortensen,

2
Mi dispiace ma non avevo tale intenzione, qualcosa di simile potrebbe esserci perché potremmo aver raccolto un'informazione dalla stessa fonte.
fatimasajjad,

2
È possibile che entrambe le risposte contengano contenuti copiati dalla stessa fonte originale senza citazione apparente - possibilmente tutorialsteacher.com/javascript/javascript-variable . La presenza del plagio è evidente, poiché dall'originale viene riprodotto un errore grammaticale: "l'ambito delle variabili dichiarate senza la varparola chiave diventa globale indipendentemente da dove viene dichiarato" dovrebbe essere "l'ambito ... diventa" o "il ambiti ... diventano " . L'uso delle parole esatte di qualcun altro richiede una citazione, sia da qui che altrove. meta.stackexchange.com/q/160071/211183
Michael - sqlbot

Grazie ragazzi, ho aggiunto un link di riferimento per la fonte.
fatimasajjad

6

Le variabili che usano la letparola chiave dovrebbero essere disponibili solo nell'ambito del blocco e non disponibili in una funzione esterna ...

Ogni variabile che stai dichiarando in quel modo non sta usando leto var. Manca una virgola nella dichiarazione delle variabili.

Non è consigliabile dichiarare una variabile senza la varparola chiave. Può sovrascrivere accidentalmente una variabile globale esistente. L'ambito delle variabili dichiarate senza la varparola chiave diventa globale indipendentemente da dove viene dichiarata. È possibile accedere alle variabili globali da qualsiasi punto della pagina Web.

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();


3

È a causa di quando non si utilizza leto varquindi la variabile viene dichiarata al volo, meglio dichiarare come segue.

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

2

Lo strano problema è causato dalle regole di scoping in JavaScript

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
}

Supponendo che si desideri dichiarare 3 variabili locali inizializzate sullo stesso valore (100). Il tuo primo () apparirà come sotto. In questo caso, second () non avrà accesso a nessuna delle variabili perché sono locali per first ()

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
}

Tuttavia, se vuoi variabili globali, il tuo primo () apparirà come di seguito. In questo caso, il secondo avrà accesso a tutte le variabili perché sono di portata globale

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
}

Variabili locali (ovvero accessibili nel blocco di codice in cui sono dichiarate).
Un blocco di codice è qualsiasi {} con una o più righe di codice tra.

  • function () {var, let, const qui è accessibile all'intera funzione},
  • for () {var qui è accessibile all'ambito esterno, let, const accessibile solo qui},
  • eccetera.

Variabili globali (ovvero accessibili nell'ambito globale).
Queste variabili sono associate all'oggetto globale. L'oggetto globale dipende dall'ambiente. È l'oggetto finestra nei browser.

Nota speciale: puoi dichiarare le variabili in JavaScript senza usare le parole chiave var, let, const. Una variabile dichiarata in questo modo è collegata all'oggetto globale, quindi accessibile nell'ambito globale.
a = 100 // is valid and is in global scope

Alcuni articoli per ulteriori letture: https://www.sitepoint.com/demystifying-javascript-variable-scope-hoisting/ https://scotch.io/tutorials/understanding-scope-in-javascript https: //www.digitalocean .com / comunità / tutorial / comprensione-variabili-scope-sollevamento-in-javascript


0

La differenza principale sono le regole di scoping. Le variabili dichiarate dalla parola chiave var sono incluse nel corpo della funzione immediata (da qui l'ambito della funzione) mentre le variabili sono portate nel blocco di chiusura immediato indicato da {} (da qui l'ambito del blocco). E quando dici

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

c e b hanno la durata della vita come divertente ma hanno solo la durata del blocco e se si tenta di accedere a a facendo riferimento, mostra sempre l'errore ma c e b sono globalmente quindi non lo fanno. Noterete che non importa quante variabili che aggiungi alla tua catena, sarà solo la prima (a) che causa l'errore. Questo perché "let" definisce la tua variabile nel blocco (o, "localmente", più o meno significato "tra parentesi") in cui la dichiari. Se dichiari una variabile senza "let", la scopa a livello globale. Quindi, nella funzione in cui imposti le variabili, tutto ottiene il valore 10 (puoi vederlo nel debugger se metti un punto di rottura). Se metti un log di console per a, b, c in quella prima funzione, tutto va bene. Ma non appena lasci quella funzione, la prima (a) - e ancora, tieni presente,


0

Ecco i 3 aspetti interessanti delle dichiarazioni variabili in JavaScript:

  1. var limita l'ambito della variabile al blocco in cui è definita. ( 'var' è per ambito locale .)

  2. let consente la sostituzione temporanea del valore di una variabile esterna all'interno di un blocco.

  3. La semplice dichiarazione di una variabile senza var o let renderà la variabile globale, indipendentemente da dove viene dichiarata.

Ecco una demo di let , che è l'ultima aggiunta alla lingua:

// 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))

Produzione:

$ 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

Spiegazione:

Le variabili a e b sono stati delcared dentro ' prima () ', senza var o lasciare che le parole chiave.

Pertanto, un e b sono globali, e quindi, sono accessibili tutto il programma.

Nella funzione denominata "second" , l'istruzione "let a = 5" imposta temporaneamente il valore di " a " su " 5 ", solo nell'ambito della funzione.

Al di fuori dell'ambito di " secondo () ", IE, nell'ambito globale, il valore di " a " sarà definito in precedenza.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.