Explicación del alcance de `let` y bloque con bucles for

Resuelto Explosion Pills asked hace 9 años • 6 respuestas

Entiendo que eso letevita declaraciones duplicadas, lo cual es bueno.

let x;
let x; // error!

Las variables declaradas con lettambién se pueden usar en cierres que se pueden esperar

let i = 100;
setTimeout(function () { console.log(i) }, i); // '100' after 100 ms

Lo que me cuesta un poco entender es cómo letse aplica a los bucles. Esto parece ser específico de forlos bucles. Considere el problema clásico:

// prints '10' 10 times
for (var i = 0; i < 10; i++) { process.nextTick(_ => console.log(i)) }
// prints '0' through '9'
for (let i = 0; i < 10; i++) { process.nextTick(_ => console.log(i)) }

¿Por qué funciona el uso leten este contexto? En mi imaginación, aunque solo es visible un bloque, foren realidad crea un bloque separado para cada iteración y la letdeclaración se realiza dentro de ese bloque... pero solo hay uno.let declaración para inicializar el valor. ¿Es esto solo azúcar sintáctico para ES6? ¿Cómo funciona esto?

Entiendo las diferencias entre vary lety las he ilustrado arriba. Estoy particularmente interesado en comprender por qué las diferentes declaraciones dan como resultado resultados diferentes usando un forbucle.

Explosion Pills avatar Jun 18 '15 01:06 Explosion Pills
Aceptado

¿Es esto solo azúcar sintáctico para ES6?

No, es más que azúcar sintáctico. Los detalles sangrientos están enterrados en §13.6.3.9 CreatePerIterationEnvironment .

¿Cómo funciona esto?

Si usa esa letpalabra clave en la fordeclaración, verificará qué nombres vincula y luego

  • cree un nuevo entorno léxico con esos nombres para a) la expresión inicializadora b) cada iteración (antes de evaluar la expresión incremental)
  • copiar los valores de todas las variables con esos nombres de un entorno al siguiente

Su declaración de bucle for (var i = 0; i < 10; i++) process.nextTick(_ => console.log(i));se reduce a un simple

// omitting braces when they don't introduce a block
var i;
i = 0;
if (i < 10)
    process.nextTick(_ => console.log(i))
    i++;
    if (i < 10)
        process.nextTick(_ => console.log(i))
        i++;
        …

mientras que for (let i = 0; i < 10; i++) process.nextTick(_ => console.log(i));hace "desazúcar" a lo mucho más complicado

// using braces to explicitly denote block scopes,
// using indentation for control flow
{ let i;
  i = 0;
  __status = {i};
}
{ let {i} = __status;
  if (i < 10)
      process.nextTick(_ => console.log(i))
      __status = {i};
}   { let {i} = __status;
      i++;
      if (i < 10)
          process.nextTick(_ => console.log(i))
          __status = {i};
    }   { let {i} = __status;
          i++;
          …
Bergi avatar Jun 17 '2015 19:06 Bergi

Esta explicación del libro Explorando ES6 me pareció la mejor:

La declaración var de una variable en el encabezado de un bucle for crea un enlace único (espacio de almacenamiento) para esa variable:

const arr = [];
for (var i=0; i < 3; i++) {
    arr.push(() => i);
}
arr.map(x => x()); // [3,3,3]

Cada i en los cuerpos de las tres funciones de flecha se refiere al mismo enlace, razón por la cual todas devuelven el mismo valor.

Si deja declarar una variable, se crea un nuevo enlace para cada iteración del bucle:

const arr = [];
for (let i=0; i < 3; i++) {
    arr.push(() => i);
}

arr.map(x => x()); // [0,1,2]

Esta vez, cada i se refiere al enlace de una iteración específica y conserva el valor actual en ese momento. Por lo tanto, cada función de flecha devuelve un valor diferente.

swapnil_mishra avatar Mar 15 '2017 14:03 swapnil_mishra