¿Por qué algunas variables declaradas usando let inside a function están disponibles en otra función, mientras que otras dan como resultado un error de referencia?

158

No puedo entender por qué las variables actúan tan extrañas cuando se declaran dentro de una función.

  1. En la firstfunción declaro con letlas variables by ccon el valor 10 :

    b = c = 10;

    En la secondfunción que muestro:

    b + ", " + c

    Y esto muestra:

    10, 10
  2. También en firstfunción declaro acon valor 10 :

    let a = b = c = 10;

    Pero en la secondfunción muestra un error:

    No se puede encontrar la variable: a

  3. Ahora en la firstfunción declaro dcon valor 20 :

    var d = 20;

    Pero en la secondfunción muestra el mismo error que antes, pero con la variable d:

    No se puede encontrar la variable: d

Ejemplo:

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

balido intertemento
fuente
31
Estás declarar variables globales, ya que by cno se puede anteponer la varpalabra clave. ay dson locales para first.
VLAZ
1
Los comentarios no son para discusión extendida; Se ha archivado en el chat una conversación tangencial sobre si esta sería una buena pregunta para la entrevista .
Cody Gray
1
Esto me recuerda una situación similar en Visual Basic; Dim Apple, Banana, Pear As Fruitsignifica Dim Apple / Dim Banana / Dim Pear As Fruit, y no Dim Apple As Fruit / ....
Eric Lippert

Respuestas:

179

Es porque en realidad estás diciendo:

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

Y no lo que crees que estás diciendo, que es:

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

Notarás que no importa cuántas variables agregues a tu cadena, solo será la primera (a) que causa el error.

Esto se debe a que "let" define su variable en el bloque (o "localmente", más o menos significa "entre paréntesis") en el que lo declara.

Si declara una variable sin "let", abarca la variable globalmente.

Entonces, en la función donde establece sus variables, todo obtiene el valor 10 (puede ver esto en el depurador si coloca un punto de interrupción). Si coloca un registro de consola para a, b, c en esa primera función, todo está bien.

Pero tan pronto como abandonas esa función, la primera (a), y nuevamente, ten en cuenta que, técnicamente en el orden de asignación, es la última, "desaparece" (de nuevo, puedes ver esto en el depurador si establece un punto de interrupción en la segunda función), pero los otros dos (o los muchos que agregue) todavía están disponibles.

Esto se debe a que "dejar" SOLO SE APLICA (solo a los ámbitos locales) LA PRIMERA VARIABLE - nuevamente, que técnicamente es el último en ser declarado y asignado un valor - en la cadena. El resto técnicamente no tiene "let" delante de ellos. Por lo tanto, técnicamente se declaran globalmente (es decir, en el objeto global), por lo que aparecen en su segunda función.

Pruébelo: elimine la palabra clave "let". Todos sus vars ahora estarán disponibles.

"var" tiene un efecto de alcance local similar, pero difiere en cómo se "alza" la variable, que es algo que definitivamente debe entender, pero que no está directamente relacionado con su pregunta.

(Por cierto, esta pregunta sería suficiente para los desarrolladores profesionales de JS para que sea buena).

Le sugiero encarecidamente que pase tiempo con las diferencias en cómo se pueden declarar las variables en JS: sin una palabra clave, con "let" y con "var".

Tim Consolazio
fuente
44
Al mismo tiempo, es lo mejor y lo peor de la programación: la computadora hará exactamente lo que usted le dice que haga. No necesariamente lo que pretendías decirle que hiciera. Los programas son perfectos. Creamos los problemas.
Niet the Dark Absol
8
@Thevs ¿Por qué recomienda varmás leten el contexto de esta respuesta? No entiendo.
Klaycon
44
@Thevs Estoy totalmente en desacuerdo contigo. varpuede ser propenso a errores si se usa descuidadamente. Mira este violín
Cid
2
@Thevs, ¿en qué caso vartiene alguna ventaja let? Permítanme aclarar: un contexto moderno cuando ambas son opciones y estoy pidiendo un código que uno debería escribir. Cuando pregunté esto antes, recibí respuestas sobre "puede volver a declarar una variable con var", en cuyo caso tengo que recordarle a la gente que no debería volver a declarar variables . Eso es un error o un error en la lógica del código, por lo que el beneficio de la nueva declaración es ... que le permite escribir código defectuoso. Todavía no he visto ninguna razón sensata a favor de varcuándo lettambién es una opción.
VLAZ
2
Marque otra marca contra javascript. Todas las variables son globales a menos que se declaren locales. :(
JRE
68

En la función first(), las variables by cse crean sobre la marcha, sin usar varo let.

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

Es diferente de

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

Se vuelven globales implícitos. Por eso están disponibles ensecond()

De la documentación

La asignación de un valor a una variable no declarada lo crea implícitamente como una variable global (se convierte en una propiedad del objeto global) cuando se ejecuta la asignación.

Para evitar esto, puede usar "use strict"eso proporcionará errores cuando uno usa una variable no declarada

"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
fuente
15
Además: let a = 10, b = 10, c = 10;o let a, b, c; a = b = c = 10;sería la forma correcta de declarar las variables.
Rickard Elimää
Entonces, con el modo estricto, ¿qué pasa con la variable b?
Marque 20 de
2
@ Tick20 la variable bno será evaluada / alcanzada, el error ocurrirá en la línea let a = b = c = 10;, leída de derecha a izquierda . csiendo la primera variable que causa ReferenceError, el resto de la línea no se ejecutará (el script se ha detenido)
Cid
2
algo así let a = 10, b = a, c = b;es válido también
Kaddath
8
votado principalmente por el "uso estricto". En el contexto de una pregunta de entrevista, ese también sería también el comienzo de mi comentario sobre este código.
Pac0
23

Antes de llamar las cosas extrañas, conozcamos algunos conceptos básicos primero:

var y let se usan para la declaración de variables en JavaScript. Por ejemplo,

var one = 1;
let two = 2;

Las variables también se pueden declarar sin usar varo let. Por ejemplo,

three = 3;

Ahora la diferencia entre los enfoques anteriores es que:

var es el alcance de la función

y

let es bloque de ámbito.

mientras que el alcance de las variables declaradas sin var/ letpalabra clave se convierte en global independientemente de dónde se declare.

Se puede acceder a las variables globales desde cualquier lugar de la página web (no se recomienda porque las variables globales pueden modificarse accidentalmente).

Ahora, de acuerdo con estos conceptos, echemos un vistazo al código en cuestión:

 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
fuente
1
Plagiaste parte de la respuesta de JonoJames . ¿Por qué?
Peter Mortensen
2
Lo siento, pero no tenía esa intención, algo similar podría estar allí porque podríamos haber recopilado una información de la misma fuente.
fatimasajjad
2
Es posible que ambas respuestas contengan contenido copiado de la misma fuente original sin cita aparente, posiblemente tutorialsteacher.com/javascript/javascript-variable . La presencia de plagio es evidente, ya que se reproduce un error gramatical del original: "el alcance de las variables declaradas sin elvar palabra clave se vuelve global, independientemente de dónde se declare" debería ser "el alcance ... se convierte" o "el ámbitos ... convertido en " . Usar las palabras exactas de otra persona requiere una cita, ya sea de aquí o de otra parte. meta.stackexchange.com/q/160071/211183
Michael - sqlbot
Gracias chicos, he agregado un enlace de referencia para la fuente.
fatimasajjad
6

Las variables que usan la letpalabra clave solo deberían estar disponibles dentro del alcance del bloque y no estar disponibles en una función externa ...

Cada variable que está declarando de esa manera no está usando leto var. Te falta una coma en la declaración de variables.

No se recomienda declarar una variable sin la varpalabra clave. Puede sobrescribir accidentalmente una variable global existente. El alcance de las variables declaradas sin la varpalabra clave se convierte en global independientemente de dónde se declare. Se puede acceder a las variables globales desde cualquier lugar de la página 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();

JonoJames
fuente
3

Es porque cuando no usa leto varcuando la variable se declara sobre la marcha, mejor declara como sigue.

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

El extraño problema es causado por las reglas de alcance en 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
}

Suponiendo que desea declarar 3 variables locales inicializadas al mismo valor (100). Su primer () se verá a continuación. En este caso, second () no tendrá acceso a ninguna de las variables porque son locales a 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
}

Sin embargo, si desea variables globales , su primer () se verá a continuación. En este caso, el segundo tendrá acceso a todas las variables porque están en alcance global

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
}

Variables locales (también conocidas como accesibles en el bloque de código donde se declaran).
Un bloque de código es cualquier {} con línea (s) de código entre ellas.

  • function () {var, let, const aquí es accesible para toda la función},
  • for () {var aquí está accesible al alcance externo, let, const accesible solo aquí},
  • etc.

Variables globales (también conocido como accesible en el ámbito global).
Estas variables están unidas al objeto global. El objeto global depende del entorno. Es el objeto de ventana en los navegadores.

Nota especial: puede declarar variables en JavaScript sin utilizar las palabras clave var, let, const. Una variable declarada de esta manera se adjunta al objeto global, por lo tanto accesible en el ámbito global.
a = 100 // is valid and is in global scope

Algunos artículos para leer más: https://www.sitepoint.com/demystifying-javascript-variable-scope-hoisting/ https://scotch.io/tutorials/understanding-scope-in-javascript https: //www.digitalocean .com / community / tutorials / understand-variables-scope-hoisting-in-javascript

Funwie
fuente
0

La principal diferencia son las reglas de alcance. Las variables declaradas por la palabra clave var tienen un alcance en el cuerpo de la función inmediata (de ahí el alcance de la función) mientras que las variables tienen un alcance en el bloque de cierre inmediato denotado por {} (de ahí el alcance del bloque). Y cuando dices

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

c y b tienen una vida útil tan divertida como la de tener solo una duración de bloque y si intentas acceder a a haciendo referencia, siempre muestra un error, pero c y b son globales, por lo que no lo notarás. Notarás que no importa cuántos Las variables que agregue a su cadena, solo serán las primeras (a) que causen el error. Esto se debe a que "let" define su variable en el bloque (o "localmente", más o menos significa "entre paréntesis") en el que lo declaras. Si declaras una variable sin "let", la mira globalmente. Entonces, en la función donde configuras tus variables, todo obtiene el valor 10 (puedes ver esto en el depurador si pones un punto de quiebre). Si coloca un registro de consola para a, b, c en esa primera función, todo está bien, pero tan pronto como abandone esa función, la primera (a), y nuevamente, tenga en cuenta que

Muhammad Fahad
fuente
0

Estos son los 3 aspectos interesantes de las declaraciones de variables en JavaScript:

  1. var restringe el alcance de la variable al bloque en el que se define. ( 'var' es para alcance local ).

  2. let permite la anulación temporal del valor de una variable externa dentro de un bloque.

  3. Simplemente declarar una variable sin var o let hará que la variable sea global, independientemente de dónde se declare.

Aquí hay una demostración de let , que es la última incorporación al lenguaje:

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

Salida:

$ 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

Explicación:

Las variables a y b se delimitaron dentro de ' first () ', sin var o let palabras clave.

Por lo tanto, un y b son globales, y por lo tanto, se puede acceder a través del programa.

En la función denominada 'segundo' , la instrucción 'let a = 5' establece temporalmente el valor de ' a ' en ' 5 ', solo dentro del alcance de la función.

Fuera del alcance de ' second () ', IE, en el alcance global, el valor de ' a ' será como se definió anteriormente.

Gopinath
fuente