w3schools support sirve que para entre diferencias javascript ecmascript-6

javascript - support - para que sirve el let en js



Explicación de `let` y bloqueo del alcance con bucles for (3)

Entiendo que let evita declaraciones duplicadas, lo cual es bueno.

let x; let x; // error!

Las variables declaradas con let tambié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 entender es cómo se aplica let a los bucles. Esto parece ser específico para for loops. 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 let en este contexto? En mi imaginación, aunque solo hay un bloque visible, for realidad crea un bloque separado para cada iteración y la declaración let se realiza dentro de ese bloque ... pero solo hay una declaración let para inicializar el valor. ¿Es esto solo azúcar sintáctico para ES6? ¿Cómo funciona esto?

Entiendo las diferencias entre var y let y las he ilustrado arriba. Estoy particularmente interesado en entender por qué las diferentes declaraciones resultan en una salida diferente usando un bucle for .


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

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

¿Cómo funciona esto?

Si usa esa palabra clave let en la instrucción for , 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 de incremento)
  • copie los valores de todas las variables con esos nombres de uno al siguiente entorno

Su declaración de bucle for (var i = 0; i < 10; i++) process.nextTick(_ => console.log(i)); desugar 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++; …

while for (let i = 0; i < 10; i++) process.nextTick(_ => console.log(i)); hace "desugar" a los mucho más complicados

// 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++; …


Encontré esta explicación del libro Explorando ES6 el mejor:

declarar una variable en la cabeza 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, por lo que todos devuelven el mismo valor.

Si deja declarar una variable, se crea un nuevo enlace para cada iteración de 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 que era actual en ese momento. Por lo tanto, cada función de flecha devuelve un valor diferente.


let introduce el alcance de bloque y el enlace equivalente, al igual que las funciones crean un alcance con cierre. Creo que la sección relevante de la especificación es 13.2.1 , donde la nota menciona que las declaraciones son parte de un enlace léxico y ambas viven dentro de un entorno léxico. La sección 13.2.2 establece que las declaraciones var se adjuntan a un entorno variable, en lugar de a un enlace léxico.

La explicación de MDN también respalda esto, indicando que:

Funciona al vincular cero o más variables en el ámbito léxico de un solo bloque de código

sugiriendo que las variables están unidas al bloque, que varía cada iteración que requiere un nuevo enlace léxico (creo, no 100% en ese punto), en lugar del entorno léxico circundante o entorno variable que sería constante durante la duración de la llamada.

En resumen, cuando se usa let , el cierre está en el cuerpo del bucle y la variable es diferente cada vez, por lo que debe capturarse nuevamente. Cuando se usa var , la variable está en la función circundante, por lo que no es necesario volver a cerrar y se pasa la misma referencia a cada iteración.

Adaptando su ejemplo para ejecutar en el navegador:

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

Ciertamente muestra la última impresión de cada valor. Si observa cómo Babel transpira esto, produce:

for (var i = 0; i < 10; i++) { setTimeout(function(_) { return console.log(i); }, 0); } var _loop = function(_i) { setTimeout(function(_) { return console.log(_i); }, 0); }; // prints ''0'' through ''9'' for (var _i = 0; _i < 10; _i++) { _loop(_i); }

Asumiendo que Babel es bastante conforme, eso coincide con mi interpretación de la especificación.