¿Cuál es la diferencia entre usar "let" y "var"?

5315

ECMAScript 6 presentó la letdeclaración .

Escuché que se describe como una localvariable, pero todavía no estoy muy seguro de cómo se comporta de manera diferente a la varpalabra clave.

¿Cuáles son las diferencias?. ¿Cuándo se debe letusar en lugar de var?

4
  • 114
    ECMAScript es el estándar y letse incluye en el borrador de la sexta edición y probablemente estará en la especificación final. Richard Ayotte 31/03/12 a las 15:08
  • 6
    Consulte kangax.github.io/es5-compat-table/es6 para obtener una matriz de soporte actualizada de las funciones de ES6 (incluido let). En el momento de escribir este artículo, Firefox, Chrome e IE11 lo admiten (aunque creo que la implementación de FF no es del todo estándar). Nico Burns 17 de enero de 2014 a las 12:37
  • 26
    Durante mucho tiempo no supe que las vars en un bucle for tenían el alcance de la función en la que estaba envuelto. Recuerdo haber descubierto esto por primera vez y pensé que era muy estúpido. Veo algo de poder, aunque ahora sé cómo se podrían usar los dos por diferentes razones y cómo, en algunos casos, es posible que desee usar una var en un bucle for y no tener el alcance del bloque. Eric Bishard 7 de mayo de 2015 a las 13:54
  • A medida que mejora la compatibilidad con las funciones de ES6, la pregunta sobre la adopción de ES6 cambia el enfoque de la compatibilidad con las funciones a las diferencias de rendimiento. Como tal, aquí hay un sitio en el que encontré las diferencias de rendimiento de evaluación comparativa entre ES6 y ES5 . Tenga en cuenta que esto probablemente cambiará con el tiempo a medida que los motores se optimicen para el código ES6. timolawl 4 de mayo de 2016 a las 1:02
7022

Reglas de alcance

La principal diferencia son las reglas de alcance. Las variables declaradas por varpalabra clave tienen el alcance del cuerpo de la función inmediata (de ahí el alcance de la función) mientras que las letvariables tienen el alcance del bloque envolvente inmediato denotado por { }(de ahí el alcance del bloque).

function run() {
  var foo = "Foo";
  let bar = "Bar";

  console.log(foo, bar); // Foo Bar

  {
    var moo = "Mooo"
    let baz = "Bazz";
    console.log(moo, baz); // Mooo Bazz
  }

  console.log(moo); // Mooo
  console.log(baz); // ReferenceError
}

run();

La razón por la que letse introdujo la palabra clave en el lenguaje fue el alcance de la función es confusa y fue una de las principales fuentes de errores en JavaScript.

Eche un vistazo a este ejemplo de otra pregunta de stackoverflow :

var funcs = [];
// let's create 3 functions
for (var i = 0; i < 3; i++) {
  // and store them in funcs
  funcs[i] = function() {
    // each should log its value.
    console.log("My value: " + i);
  };
}
for (var j = 0; j < 3; j++) {
  // and now let's run each one to see
  funcs[j]();
}

My value: 3se envió a la consola cada vez que funcs[j]();se invocó, ya que las funciones anónimas estaban vinculadas a la misma variable.

La gente tenía que crear funciones invocadas inmediatamente para capturar los valores correctos de los bucles, pero eso también era complicado.

Izar

Mientras que las variables declaradas con la varpalabra clave se elevan (se inicializan con undefinedantes de que se ejecute el código), lo que significa que son accesibles en su ámbito adjunto incluso antes de que se declaren:

function run() {
  console.log(foo); // undefined
  var foo = "Foo";
  console.log(foo); // Foo
}

run();

letlas variables no se inicializan hasta que se evalúa su definición. Acceder a ellos antes de la inicialización da como resultado un ReferenceError. Variable que se dice que está en "zona muerta temporal" desde el inicio del bloque hasta que se procesa la inicialización.

function checkHoisting() {
  console.log(foo); // ReferenceError
  let foo = "Foo";
  console.log(foo); // Foo
}

checkHoisting();

Creando propiedad de objeto global

En el nivel superior let, a diferencia de var, no crea una propiedad en el objeto global:

var foo = "Foo";  // globally scoped
let bar = "Bar"; // not allowed to be globally scoped

console.log(window.foo); // Foo
console.log(window.bar); // undefined

Redeclaración

En modo estricto, varle permitirá volver a declarar la misma variable en el mismo ámbito mientras letgenera un SyntaxError.

'use strict';
var foo = "foo1";
var foo = "foo2"; // No problem, 'foo1' is replaced with 'foo2'.

let bar = "bar1"; 
let bar = "bar2"; // SyntaxError: Identifier 'bar' has already been declared
18
  • 40
    Recuerda que puedes crear bloque cuando quieras. function () {código; {let inBlock = 5; } código; }; average Joe 14/12/12 a las 10:14
  • 211
    Entonces, ¿el propósito de las declaraciones let es solo para liberar memoria cuando no se necesita en un bloque determinado? NoBugs 7 de junio de 2013 a las 5:18
  • 244
    @NoBugs, Sí, y se recomienda que las variables existan solo donde se necesitan. batman 7 de junio de 2013 a las 15:02
  • 68
    letLa expresión de bloque let (variable declaration) statementno es estándar y se eliminará en el futuro, bugzilla.mozilla.org/show_bug.cgi?id=1023609 . Gajus 17/12/14 a las 14:51
  • 38
    Entonces, simplemente no puedo pensar en ningún caso en el que usar var sea de alguna utilidad. ¿Alguien podría darme un ejemplo de una situación en la que es preferible usar var? Luis Sieira 8/11/15 a las 13:12
719

lettambién se puede utilizar para evitar problemas con los cierres. Vincula valor nuevo en lugar de mantener una referencia anterior, como se muestra en los ejemplos a continuación.

for(var i=1; i<6; i++) {
  $("#div" + i).click(function () { console.log(i); });
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p>Clicking on each number will log to console:</p> 
<div id="div1">1</div>
<div id="div2">2</div>
<div id="div3">3</div>
<div id="div4">4</div>
<div id="div5">5</div>

El código anterior demuestra un problema clásico de cierre de JavaScript. La referencia a la ivariable se almacena en el cierre del controlador de clic, en lugar del valor real de i.

Cada controlador de un solo clic se referirá al mismo objeto porque solo hay un objeto de contador que contiene 6, por lo que obtiene seis en cada clic.

Una solución general es envolver esto en una función anónima y pasar icomo argumento. Estos problemas también se pueden evitar ahora utilizando en su letlugar varcomo se muestra en el código a continuación.

(Probado en Chrome y Firefox 50)

for(let i=1; i<6; i++) {
  $("#div" + i).click(function () { console.log(i); });
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p>Clicking on each number will log to console:</p> 
<div id="div1">1</div>
<div id="div2">2</div>
<div id="div3">3</div>
<div id="div4">4</div>
<div id="div5">5</div>
13
  • 63
    Eso es realmente genial. Esperaría que "i" se defina fuera del cuerpo del bucle que contiene entre paréntesis y que NO forme un "cierre" alrededor de "i". Por supuesto, su ejemplo demuestra lo contrario. Creo que es un poco confuso desde el punto de vista de la sintaxis, pero este escenario es tan común que tiene sentido apoyarlo de esa manera. Muchas gracias por mencionar este tema. Karol Kolenda 27/07/2015 a las 12:49
  • 9
    IE 11 es compatible let, pero alerta "6" para todos los botones. ¿Tiene alguna fuente que diga cómo letse supone que debe comportarse? Jim Hunziker 22/10/15 a las 13:29
  • 10
    Parece que su respuesta es el comportamiento correcto: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…Jim Hunziker 22/10/15 a las 13:32
  • 13
    De hecho, este es un error común en Javascript y ahora puedo ver por qué letsería realmente útil. La configuración de detectores de eventos en un bucle ya no requiere una expresión de función invocada inmediatamente para el alcance local ien cada iteración. Adrian Moisa 21 de febrero de 2016 a las 8:12
  • 20
    El uso de "dejar" simplemente pospone este problema. Por lo tanto, cada iteración crea un alcance de bloque privado independiente, pero la variable "i" aún puede corromperse por cambios posteriores dentro del bloque (dado que la variable del iterador generalmente no se cambia dentro del bloque, pero otras variables let declaradas dentro del bloque pueden muy bien be) y cualquier función declarada en el bloque de lata, cuando se invoca, corromper el valor de "i" para otras funciones declarado dentro del bloque, ya que hacen comparten el mismo ámbito de bloque privado, por tanto, la misma referencia a "i". gary 7 de septiembre de 2016 a las 23:10
269

¿Cuál es la diferencia entre lety var?

  • Una variable definida mediante una vardeclaración se conoce en toda la función en la que se define, desde el inicio de la función. (*)
  • Una variable definida mediante un letenunciado solo se conoce en el bloque en el que está definida, desde el momento en que se define en adelante. (**)

Para comprender la diferencia, considere el siguiente código:

// i IS NOT known here
// j IS NOT known here
// k IS known here, but undefined
// l IS NOT known here

function loop(arr) {
    // i IS known here, but undefined
    // j IS NOT known here
    // k IS known here, but has a value only the second time loop is called
    // l IS NOT known here

    for( var i = 0; i < arr.length; i++ ) {
        // i IS known here, and has a value
        // j IS NOT known here
        // k IS known here, but has a value only the second time loop is called
        // l IS NOT known here
    };

    // i IS known here, and has a value
    // j IS NOT known here
    // k IS known here, but has a value only the second time loop is called
    // l IS NOT known here

    for( let j = 0; j < arr.length; j++ ) {
        // i IS known here, and has a value
        // j IS known here, and has a value
        // k IS known here, but has a value only the second time loop is called
        // l IS NOT known here
    };

    // i IS known here, and has a value
    // j IS NOT known here
    // k IS known here, but has a value only the second time loop is called
    // l IS NOT known here
}

loop([1,2,3,4]);

for( var k = 0; k < arr.length; k++ ) {
    // i IS NOT known here
    // j IS NOT known here
    // k IS known here, and has a value
    // l IS NOT known here
};

for( let l = 0; l < arr.length; l++ ) {
    // i IS NOT known here
    // j IS NOT known here
    // k IS known here, and has a value
    // l IS known here, and has a value
};

loop([1,2,3,4]);

// i IS NOT known here
// j IS NOT known here
// k IS known here, and has a value
// l IS NOT known here

Aquí, podemos ver que nuestra variable jsolo se conoce en el primer ciclo for, pero no antes ni después. Sin embargo, nuestra variable ise conoce en toda la función.

Además, tenga en cuenta que las variables de ámbito de bloque no se conocen antes de declararse porque no están elevadas. Tampoco se le permite volver a declarar la misma variable de ámbito de bloque dentro del mismo bloque. Esto hace que las variables de ámbito de bloque sean menos propensas a errores que las variables de ámbito global o funcional, que son elevadas y que no producen ningún error en caso de múltiples declaraciones.


¿Es seguro usarlo lethoy?

Algunas personas argumentarían que en el futuro SOLO usaremos declaraciones let y que las declaraciones var se volverán obsoletas. El gurú de JavaScript, Kyle Simpson, escribió un artículo muy elaborado sobre por qué cree que ese no será el caso .

Hoy, sin embargo, definitivamente ese no es el caso. De hecho, necesitamos preguntarnos si es seguro usar la letdeclaración. La respuesta a esa pregunta depende de su entorno:

  • Si está escribiendo código JavaScript del lado del servidor ( Node.js ), puede usar la letdeclaración de forma segura .

  • Si está escribiendo código JavaScript del lado del cliente y usa un transpiler basado en navegador (como Traceur o babel-standalone ), puede usar la letdeclaración de manera segura , sin embargo, es probable que su código sea cualquier cosa menos óptimo con respecto al rendimiento.

  • Si está escribiendo código JavaScript del lado del cliente y usa un transpilador basado en Node (como el script de shell traceur o Babel ), puede usar la letdeclaración de manera segura . Y debido a que su navegador solo conocerá el código transpilado, los inconvenientes de rendimiento deberían ser limitados.

  • Si está escribiendo código JavaScript del lado del cliente y no usa un transpilador, debe considerar la compatibilidad con el navegador.

    Todavía hay algunos navegadores que no son compatibles leten absoluto:

ingrese la descripción de la imagen aquí


Cómo realizar un seguimiento de la compatibilidad del navegador

Para obtener una descripción general actualizada de qué navegadores admiten la letdeclaración en el momento de leer esta respuesta, consulte esta Can I Usepágina .


(*) Las variables de ámbito global y funcional se pueden inicializar y usar antes de declararlas porque las variables de JavaScript están elevadas . Esto significa que las declaraciones están siempre en la parte superior del ámbito.

(**) Las variables de ámbito de bloque no se elevan

6
  • dieciséis
    con respecto a la respuesta v4: ¡ iSE conoce en todas partes en el bloque de funciones! ¡Comienza como undefined(debido al izado) hasta que le asigna un valor! ps: lettambién se iza (en la parte superior del bloque que lo contiene), pero dará un ReferenceErrorcuando se haga referencia en el bloque antes de la primera asignación. (ps2: soy un tipo profesional del punto y coma, pero realmente no necesitas un punto y coma después de un bloque). Dicho esto, ¡gracias por agregar la verificación de la realidad con respecto al soporte! GitaarLAB 21 de mayo de 2016 a las 4:41
  • @GitaarLAB: Según Mozilla Developer Network : "En ECMAScript 2015, los enlaces no están sujetos a Variable Hoisting, lo que significa que las declaraciones no se mueven a la parte superior del contexto de ejecución actual". - ¡De todos modos, hice algunas mejoras en mi respuesta que deberían aclarar la diferencia en el comportamiento de elevación entre lety var! John Slegers 26 feb 2018 a las 23:37
  • 1
    Tu respuesta mejoró mucho (lo comprobé minuciosamente). Tenga en cuenta que el mismo enlace al que hizo referencia en su comentario también dice: "La variable (let) está en una" zona muerta temporal "desde el inicio del bloque hasta que se procesa la inicialización". Eso significa que el 'identificador' (la cadena de texto 'reservado' para apuntar a 'algo') ya está reservado en el alcance relevante, de lo contrario se convertiría en parte del alcance raíz / host / ventana. Para mí, personalmente, "izar" no significa más que reservar / vincular los "identificadores" declarados a su alcance relevante; excluyendo su inicialización / asignación / modificabilidad! GitaarLAB 1 mar 18 a las 18:16
  • Y .. + 1. Ese artículo de Kyle Simpson que vinculó es una lectura excelente , ¡gracias por eso! También está claro acerca de la "zona muerta temporal" también conocida como "TDZ". Una cosa interesante que me gustaría agregar: lo leí en MDN lety constse me recomendó que lo use solo cuando realmente necesite su funcionalidad adicional , porque hacer cumplir / verificar estas características adicionales (como const de solo escritura) resulta en 'más trabajo '(y nodos de alcance adicionales en el árbol de alcance) para que los motores (actuales) hagan cumplir / verificar / verificar / configurar. GitaarLAB 1 mar 18 a las 18:17
  • 1
    Tenga en cuenta que MDN dice que IE interpreta let correctamente. Cual es developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…Katinka Hesselink 6 feb 2019 a las 12:42
155

Aquí hay una explicación de la letpalabra clave con algunos ejemplos.

let works very much like var. The main difference is that the scope of a var variable is the entire enclosing function

Esta tabla en Wikipedia muestra qué navegadores son compatibles con Javascript 1.7.

Tenga en cuenta que solo los navegadores Mozilla y Chrome lo admiten. IE, Safari y potencialmente otros no lo hacen.

5
  • 5
    La parte clave del texto del documento vinculado parece ser, "let funciona de manera muy parecida a var. La principal diferencia es que el alcance de una variable var es la función completa que la encierra". Michael Burr 17/04/09 a las 20:25
  • 55
    @olliej, en realidad Mozilla está un poco por delante del juego. Consulte la página 19 de ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdfTyler Crompton 18/06/12 a las 20:16
  • @TylerCrompton ese es solo el conjunto de palabras que se han reservado durante años. Cuando mozilla agregó let, era puramente una extensión de mozilla, sin especificaciones relacionadas. ES6 debería definir el comportamiento de las sentencias let, pero eso vino después de que mozilla introdujera la sintaxis. Recuerde que moz también tiene E4X, que está completamente muerto y solo moz. olliej 11/07/12 a las 18:49
  • 9
    IE11 agregó soporte para let msdn.microsoft.com/en-us/library/ie/dn342892%28v=vs.85%29.aspxeloyesp 24 de diciembre de 2013 a las 12:59
  • Ahora es letcompatible con todos los navegadores más recientes, excepto los navegadores Opera, Blackberry y QQ. Shapon Pal 8 de enero de 2019 a las 5:11
128

A la respuesta aceptada le falta un punto:

{
  let a = 123;
};

console.log(a); // ReferenceError: a is not defined
4
  • 20
    La respuesta aceptada NO explica este punto en su ejemplo. La respuesta aceptada solo lo demostró en un forinicializador de bucle, lo que redujo drásticamente el ámbito de aplicación de las limitaciones de let. Voto a favor. Jon Davis 22 de septiembre de 2015 a las 6:55
  • 43
    @ stimpy77 Establece explícitamente "let tiene el alcance del bloque circundante más cercano"; ¿Es necesario incluir todas las formas en las que se manifiestan? Dave Newton 31/03/2016 a las 21:32
  • 7
    hubo muchos ejemplos y ninguno de ellos demostró adecuadamente el asunto. ¿Podría haber votado a favor tanto de la respuesta aceptada como de esta? Jon Davis 31/03/2016 a las 21:38
  • 6
    Esta contribución demuestra que un "bloque" puede ser simplemente un conjunto de líneas entre corchetes; es decir, que no necesita estar asociado con cualquier tipo de flujo de control, de bucle, etc.webelo 22/11/2017 a las 14:37
110

let

Alcance del bloque

Las variables declaradas con la letpalabra clave tienen un alcance de bloque, lo que significa que solo están disponibles en el bloque en el que se declararon.

En el nivel superior (fuera de una función)

En el nivel superior, las variables declaradas mediante letno crean propiedades en el objeto global.

var globalVariable = 42;
let blockScopedVariable = 43;

console.log(globalVariable); // 42
console.log(blockScopedVariable); // 43

console.log(this.globalVariable); // 42
console.log(this.blockScopedVariable); // undefined

Dentro de una función

Dentro de una función (pero fuera de un bloque), lettiene el mismo alcance que var.

(() => {
  var functionScopedVariable = 42;
  let blockScopedVariable = 43;

  console.log(functionScopedVariable); // 42
  console.log(blockScopedVariable); // 43
})();

console.log(functionScopedVariable); // ReferenceError: functionScopedVariable is not defined
console.log(blockScopedVariable); // ReferenceError: blockScopedVariable is not defined

Dentro de un bloque

letNo se puede acceder a las variables declaradas dentro de un bloque fuera de ese bloque.

{
  var globalVariable = 42;
  let blockScopedVariable = 43;
  console.log(globalVariable); // 42
  console.log(blockScopedVariable); // 43
}

console.log(globalVariable); // 42
console.log(blockScopedVariable); // ReferenceError: blockScopedVariable is not defined

Dentro de un bucle

letSolo se puede hacer referencia a las variables declaradas con bucles in dentro de ese bucle.

for (var i = 0; i < 3; i++) {
  var j = i * 2;
}
console.log(i); // 3
console.log(j); // 4

for (let k = 0; k < 3; k++) {
  let l = k * 2;
}
console.log(typeof k); // undefined
console.log(typeof l); // undefined
// Trying to do console.log(k) or console.log(l) here would throw a ReferenceError.

Bucles con cierres

Si usa en letlugar de varen un bucle, con cada iteración obtendrá una nueva variable. Eso significa que puede usar de forma segura un cierre dentro de un bucle.

// Logs 3 thrice, not what we meant.
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 0);
}

// Logs 0, 1 and 2, as expected.
for (let j = 0; j < 3; j++) {
  setTimeout(() => console.log(j), 0);
}

Zona muerta temporal

Debido a la zona muerta temporal , letno se puede acceder a las variables declaradas usando antes de que se declaren. Intentar hacerlo arroja un error.

console.log(noTDZ); // undefined
var noTDZ = 43;
console.log(hasTDZ); // ReferenceError: hasTDZ is not defined
let hasTDZ = 42;

No volver a declarar

No puede declarar la misma variable varias veces usando let. Tampoco puede declarar una variable usando letel mismo identificador que otra variable que fue declarada usando var.

var a;
var a; // Works fine.

let b;
let b; // SyntaxError: Identifier 'b' has already been declared

var c;
let c; // SyntaxError: Identifier 'c' has already been declared

const

constes bastante similar a let—tiene alcance de bloque y tiene TDZ. Sin embargo, hay dos cosas que son diferentes.

No reasignar

La variable declarada usando constno se puede reasignar.

const a = 42;
a = 43; // TypeError: Assignment to constant variable.

Tenga en cuenta que no significa que el valor sea inmutable. Sus propiedades aún se pueden cambiar.

const obj = {};
obj.a = 42;
console.log(obj.a); // 42

Si desea tener un objeto inmutable, debe usar Object.freeze().

Se requiere inicializador

Siempre debe especificar un valor al declarar una variable usando const.

const a; // SyntaxError: Missing initializer in const declaration
1
  • Esta es una explicación muy clara sobre la declaración en JS ... Y de repente me di cuenta de dónde está mi problema en uno de los forbucles: - | 1000Gbps 7 de abril a las 3:04
81

En términos más básicos,

for (let i = 0; i < 5; i++) {
  // i accessible ✔️
}
// i not accessible ❌

for (var i = 0; i < 5; i++) {
  // i accessible ✔️
}
// i accessible ✔️

⚡️ Sandbox para jugar ↓

Editar let vs var

0
60

Aquí hay un ejemplo de la diferencia entre los dos (el soporte acaba de comenzar para Chrome):
ingrese la descripción de la imagen aquí

Como puede ver, la var jvariable todavía tiene un valor fuera del alcance del bucle for (Alcance del bloque), pero la let ivariable no está definida fuera del alcance del bucle for.

"use strict";
console.log("var:");
for (var j = 0; j < 2; j++) {
  console.log(j);
}

console.log(j);

console.log("let:");
for (let i = 0; i < 2; i++) {
  console.log(i);
}

console.log(i);
0
57

La principal diferencia es la diferencia de alcance , mientras que let solo puede estar disponible dentro del alcance que se declara, como en el bucle for, se puede acceder a var fuera del bucle, por ejemplo. De la documentación en MDN (ejemplos también de MDN):

let allows you to declare variables that are limited in scope to the block, statement, or expression on which it is used. This is unlike the var keyword, which defines a variable globally, or locally to an entire function regardless of block scope.

Variables declared by let have as their scope the block in which they are defined, as well as in any contained sub-blocks. In this way, let works very much like var. The main difference is that the scope of a var variable is the entire enclosing function:

function varTest() {
  var x = 1;
  if (true) {
    var x = 2;  // same variable!
    console.log(x);  // 2
  }
  console.log(x);  // 2
}

function letTest() {
  let x = 1;
  if (true) {
    let x = 2;  // different variable
    console.log(x);  // 2
  }
  console.log(x);  // 1
}`

At the top level of programs and functions, let, unlike var, does not create a property on the global object. For example:

var x = 'global';
let y = 'global';
console.log(this.x); // "global"
console.log(this.y); // undefined

When used inside a block, let limits the variable's scope to that block. Note the difference between var whose scope is inside the function where it is declared.

var a = 1;
var b = 2;

if (a === 1) {
  var a = 11; // the scope is global
  let b = 22; // the scope is inside the if-block

  console.log(a);  // 11
  console.log(b);  // 22
} 

console.log(a); // 11
console.log(b); // 2

Además, no olvide que es la función ECMA6, por lo que aún no es totalmente compatible, por lo que es mejor que siempre la transpile a ECMA5 usando Babel, etc. para obtener más información sobre visitar el sitio web de babel

1
  • No sé si ese último ejemplo es exacto. Porque al llamarlo no desde una función sino desde una línea de comando directa, todavía se considera parte de la misma función. Entonces, si lo llamó desde fuera de una función, no debería comportarse de la misma manera. ACopeLan 28/08/20 a las 18:20
57

Hay algunas diferencias sutiles: el letalcance se comporta más como lo hace el alcance variable en más o menos cualquier otro idioma.

por ejemplo, se aplica al bloque adjunto, no existen antes de que se declaren, etc.

Sin embargo, vale la pena señalar que letes solo una parte de las implementaciones de Javascript más recientes y tiene diversos grados de compatibilidad con el navegador .

5
  • 11
    También vale la pena señalar que ECMAScript es el estándar y letestá incluido en el borrador de la sexta edición y probablemente estará en la especificación final. Richard Ayotte 31/03/12 a las 15:09
  • 5
    Acabo de abordar esta pregunta y en 2012 sigue siendo el caso que solo los navegadores de Mozilla admiten let. Safari, IE y Chome no lo hacen. pseudosavant 13/07/12 a las 17:38
  • 2
    La idea de crear accidentalmente un alcance de bloque parcial en caso de accidente es un buen punto, tenga cuidado, letno se eleva, para usar una variable definida por un letdefinido en la parte superior de su bloque. Si tiene una ifdeclaración que es más que unas pocas líneas de código, puede olvidar que no puede usar esa variable hasta que esté definida. GRAN PUNTO !!! Eric Bishard 7 de mayo de 2015 a las 14:01
  • Esta es una de las distinciones más importantes entre let y var y no está en la respuesta aceptada jaja. Especialmente considerando los numerosos errores que pueden ocurrir gracias al levantamiento y al alcance. Siento que no hay muchas diferencias entre let y var si no mencionas el izado. Jay 21/06/2015 a las 16:12
  • 2
    @EricB: sí y no: "En ECMAScript 2015, let elevará la variable a la parte superior del bloque. Sin embargo, hacer referencia a la variable en el bloque antes de la declaración de la variable da como resultado un ReferenceError (mi nota: en lugar del bueno undefined). La variable está en una 'zona muerta temporal' desde el inicio del bloque hasta que se procesa la declaración ". Lo mismo ocurre con las "declaraciones de cambio porque solo hay un bloque subyacente". Fuente: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…GitaarLAB 21 de mayo de 2016 a las 4:15
29
  • Variable sin elevación

    letserá no polipasto a todo el ámbito del bloque que aparecen. Por el contrario, varpodría izar como a continuación.

    {
       console.log(cc); // undefined. Caused by hoisting
       var cc = 23;
    }
    
    {
       console.log(bb); // ReferenceError: bb is not defined
       let bb = 23;
    }
    

    En realidad, Per @Bergi, Ambos vary letestán izados .

  • Recolección de basura

    El alcance de bloque de letes útil se relaciona con cierres y recolección de basura para recuperar memoria. Considerar,

    function process(data) {
        //...
    }
    
    var hugeData = { .. };
    
    process(hugeData);
    
    var btn = document.getElementById("mybutton");
    btn.addEventListener( "click", function click(evt){
        //....
    });
    

    La clickdevolución de llamada del controlador no necesita la hugeDatavariable en absoluto. En teoría, después de las process(..)ejecuciones, la enorme estructura de datos hugeDatapodría recolectarse basura. Sin embargo, es posible que algún motor JS todavía tenga que mantener esta enorme estructura, ya que la clickfunción tiene un cierre en todo el alcance.

    Sin embargo, el alcance del bloque puede convertir esta enorme estructura de datos en basura recolectada.

    function process(data) {
        //...
    }
    
    { // anything declared inside this block can be garbage collected
        let hugeData = { .. };
        process(hugeData);
    }
    
    var btn = document.getElementById("mybutton");
    btn.addEventListener( "click", function click(evt){
        //....
    });
    
  • let bucles

    leten el bucle puede volver a vincularlo a cada iteración del bucle, asegurándose de reasignarle el valor del final de la iteración del bucle anterior. Considerar,

    // print '5' 5 times
    for (var i = 0; i < 5; ++i) {
        setTimeout(function () {
            console.log(i);
        }, 1000);  
    }
    

    Sin embargo, reemplácelo varconlet

    // print 1, 2, 3, 4, 5. now
    for (let i = 0; i < 5; ++i) {
        setTimeout(function () {
            console.log(i);
        }, 1000);  
    }
    

    Debido a que letcrea un nuevo entorno léxico con esos nombres para a) la expresión del inicializador b) cada iteración (antes de evaluar la expresión de incremento), hay más detalles aquí .

4
  • 4
    Yip, están izados, pero se comportan como si no los hubieran izado debido a la (redoble de tambores) Zona muerta temporal, un nombre muy dramático para un identificador que no es accesible hasta que se declara :-)Drenai 31/12/2016 a las 15:42
  • Entonces, ¿está izado, pero no disponible? ¿En qué se diferencia eso de "no izar"? N-ate 21/11/2017 a las 21:46
  • Ojalá Brian o Bergi vuelvan a responder a esta pregunta. ¿Se eleva la declaración de alquiler, pero no la cesión? ¡Gracias! N-ate 22/11/2017 a las 16:18
  • Es interesante que incluso se le llame izar cuando se trata de arrendar. Entiendo que técnicamente el motor de análisis lo está pre-capturando, pero para todos los efectos, un programador debería tratarlo como si no existiera. La elevación de var, por otro lado, tiene implicaciones para un programador. N-ate 23/11/2017 a las 23:49
29

La diferencia está en el alcance de las variables declaradas con cada uno.

En la práctica, hay una serie de consecuencias útiles de la diferencia de alcance:

  1. letlas variables solo son visibles en su bloque adjunto más cercano ( { ... }).
  2. letlas variables solo se pueden usar en líneas de código que ocurren después de que se declara la variable (¡incluso aunque estén elevadas !).
  3. letlas variables no pueden volver a declararse mediante una varo posterior let.
  4. Las letvariables globales no se agregan al windowobjeto global .
  5. letLas variables son fáciles de usar con cierres (no provocan condiciones de carrera ).

Las restricciones impuestas por letreducen la visibilidad de las variables y aumentan la probabilidad de que se encuentren pronto colisiones inesperadas de nombres. Esto hace que sea más fácil rastrear y razonar sobre las variables, incluida su accesibilidad (lo que ayuda a recuperar la memoria no utilizada).

En consecuencia, letes menos probable que las variables causen problemas cuando se utilizan en programas grandes o cuando los marcos desarrollados de forma independiente se combinan de formas nuevas e inesperadas.

varaún puede ser útil si está seguro de que desea el efecto de enlace único al usar un cierre en un bucle (n. ° 5) o para declarar variables globales visibles externamente en su código (n. ° 4). El uso de varpara las exportaciones se puede suplantar si exportmigra del espacio del transpilador al lenguaje principal.

Ejemplos de

1. Sin uso fuera del bloque envolvente más cercano: este bloque de código arrojará un error de referencia porque el segundo uso de xocurre fuera del bloque donde se declara con let:

{
    let x = 1;
}
console.log(`x is ${x}`);  // ReferenceError during parsing: "x is not defined".

Por el contrario, el mismo ejemplo con varobras.

2. Sin uso antes de la declaración:
este bloque de código arrojará un ReferenceErrorantes de que el código se pueda ejecutar porque xse usa antes de que se declare:

{
    x = x + 1;  // ReferenceError during parsing: "x is not defined".
    let x;
    console.log(`x is ${x}`);  // Never runs.
}

Por el contrario, el mismo ejemplo con varanaliza y ejecuta sin lanzar ninguna excepción.

3. Sin redeclaración: el siguiente código demuestra que una variable declarada con letno puede volver a declararse más adelante:

let x = 1;
let x = 2;  // SyntaxError: Identifier 'x' has already been declared

4. Globals no vinculados a window:

var button = "I cause accidents because my name is too common.";
let link = "Though my name is common, I am harder to access from other JS files.";
console.log(link);  // OK
console.log(window.link);  // undefined (GOOD!)
console.log(window.button);  // OK

5. Fácil uso con cierres: las variables declaradas con varno funcionan bien con cierres dentro de bucles. Aquí hay un ciclo simple que genera la secuencia de valores que tiene la variable ien diferentes momentos:

for (let i = 0; i < 5; i++) {
    console.log(`i is ${i}`), 125/*ms*/);
}

Específicamente, esto produce:

i is 0
i is 1
i is 2
i is 3
i is 4

En JavaScript, a menudo usamos variables mucho más tarde que cuando se crean. Cuando demostramos esto retrasando la salida con un cierre pasado a setTimeout:

for (let i = 0; i < 5; i++) {
    setTimeout(_ => console.log(`i is ${i}`), 125/*ms*/);
}

... la salida permanece sin cambios mientras nos mantengamos let. Por el contrario, si hubiéramos utilizado en su var ilugar:

for (var i = 0; i < 5; i++) {
    setTimeout(_ => console.log(`i is ${i}`), 125/*ms*/);
}

... el bucle emite inesperadamente "i es 5" cinco veces:

i is 5
i is 5
i is 5
i is 5
i is 5
2
  • 8
    # 5 no es causado por una condición de carrera. Al usar en varlugar de let, el código es equivalente a: var i = 0; while (i < 5) { doSomethingLater(); i++; } iestá fuera del cierre y, en el momento en que doSomethingLater()se ejecuta, iya se ha incrementado 5 veces, por lo que la salida es i is 5cinco veces. Al usar let, la variable iestá dentro del cierre, por lo que cada llamada asíncrona obtiene su propia copia de en ilugar de usar la 'global' que se creó con var. Daniel T. 2 de junio de 2017 a las 1:12
  • @DanielT .: No creo que la transformación de sacar la definición de variable del inicializador de bucle explique nada. Esa es simplemente la definición normal de la semántica de for. Una transformación más precisa, aunque más complicada, es la clásica for (var i = 0; i < 5; i++) { (function(j) { setTimeout(_ => console.log(i es $ {j} ), 125/*ms*/); })(i); }que introduce un "registro de activación de función" para guardar cada valor de icon el nombre de jdentro de la función. mormegil 25/07/2017 a las 7:13
24

Aquí hay un ejemplo para agregar a lo que otros ya han escrito. Suponga que desea crear una matriz de funciones, adderFunctionsdonde cada función toma un único argumento de Número y devuelve la suma del argumento y el índice de la función en la matriz. Intentar generar adderFunctionscon un bucle usando la varpalabra clave no funcionará de la manera que alguien podría esperar ingenuamente:

// An array of adder functions.
var adderFunctions = [];

for (var i = 0; i < 1000; i++) {
  // We want the function at index i to add the index to its argument.
  adderFunctions[i] = function(x) {
    // What is i bound to here?
    return x + i;
  };
}

var add12 = adderFunctions[12];

// Uh oh. The function is bound to i in the outer scope, which is currently 1000.
console.log(add12(8) === 20); // => false
console.log(add12(8) === 1008); // => true
console.log(i); // => 1000

// It gets worse.
i = -8;
console.log(add12(8) === 0); // => true

El proceso anterior no genera la matriz deseada de funciones porque iel alcance se extiende más allá de la iteración del forbloque en el que se creó cada función. En cambio, al final del ciclo, ien el cierre de cada función se refiere al ivalor de 'al final del ciclo (1000) para cada función anónima en adderFunctions. Esto no es lo que queríamos en absoluto: ahora tenemos una matriz de 1000 funciones diferentes en la memoria con exactamente el mismo comportamiento. Y si posteriormente actualizamos el valor de i, la mutación afectará a todos los adderFunctions.

Sin embargo, podemos volver a intentarlo con la letpalabra clave:

// Let's try this again.
// NOTE: We're using another ES6 keyword, const, for values that won't
// be reassigned. const and let have similar scoping behavior.
const adderFunctions = [];

for (let i = 0; i < 1000; i++) {
  // NOTE: We're using the newer arrow function syntax this time, but 
  // using the "function(x) { ..." syntax from the previous example 
  // here would not change the behavior shown.
  adderFunctions[i] = x => x + i;
}

const add12 = adderFunctions[12];

// Yay! The behavior is as expected. 
console.log(add12(8) === 20); // => true

// i's scope doesn't extend outside the for loop.
console.log(i); // => ReferenceError: i is not defined

Esta vez, ise recupera en cada iteración del forbucle. Cada función ahora mantiene el valor de ien el momento de la creación de la función y se adderFunctionscomporta como se esperaba.

Ahora, visualice la mezcla de los dos comportamientos y probablemente verá por qué no se recomienda mezclar el más nuevo lety constel más antiguo varen el mismo script. Hacerlo puede resultar en un código espectacularmente confuso.

const doubleAdderFunctions = [];

for (var i = 0; i < 1000; i++) {
    const j = i;
    doubleAdderFunctions[i] = x => x + i + j;
}

const add18 = doubleAdderFunctions[9];
const add24 = doubleAdderFunctions[12];

// It's not fun debugging situations like this, especially when the
// code is more complex than in this example.
console.log(add18(24) === 42); // => false
console.log(add24(18) === 42); // => false
console.log(add18(24) === add24(18)); // => false
console.log(add18(24) === 2018); // => false
console.log(add24(18) === 2018); // => false
console.log(add18(24) === 1033); // => true
console.log(add24(18) === 1030); // => true

No dejes que esto te pase a ti. Usa un linter.

NOTE: This is a teaching example intended to demonstrate the var/let behavior in loops and with function closures that would also be easy to understand. This would be a terrible way to add numbers. But the general technique of capturing data in anonymous function closures might be encountered in the real world in other contexts. YMMV.

3
  • 2
    @aborz: También una sintaxis de funciones anónimas muy interesante en el segundo ejemplo. Es a lo que estoy acostumbrado en C #. Hoy he aprendido algo. Barton 20 de febrero de 2015 a las 8:59
  • Corrección: Técnicamente, la sintaxis de la función Arrow se describe aquí => developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…Barton 16 de marzo de 2015 a las 6:58
  • 3
    En realidad, no es necesario let value = i;. La fordeclaración crea un bloque léxico. Toothbrush 22/10/15 a las 22:38
21

Que las siguientes dos funciones muestren la diferencia:

function varTest() {
    var x = 31;
    if (true) {
        var x = 71;  // Same variable!
        console.log(x);  // 71
    }
    console.log(x);  // 71
}

function letTest() {
    let x = 31;
    if (true) {
        let x = 71;  // Different variable
        console.log(x);  // 71
    }
    console.log(x);  // 31
}
0
16

Función VS alcance del bloque:

La principal diferencia entre vary letes que las variables declaradas con vartienen un ámbito de función . Mientras que las funciones declaradas con lettienen un alcance de bloque . Por ejemplo:

function testVar () {
  if(true) {
    var foo = 'foo';
  }

  console.log(foo);
}

testVar();  
// logs 'foo'


function testLet () {
  if(true) {
    let bar = 'bar';
  }

  console.log(bar);
}

testLet(); 
// reference error
// bar is scoped to the block of the if statement 

variables con var:

Cuando testVarse llama a la primera función, la variable foo, declarada con var, todavía es accesible fuera de la ifdeclaración. Esta variable fooestaría disponible en todas partes dentro del alcance de la testVar función .

variables con let:

Cuando testLetse llama a la segunda función, la barra de variables, declarada con let, solo es accesible dentro de la ifdeclaración. Dado que las variables declaradas con letson de ámbito bloque (donde un bloque es el código entre corchetes, por ejemplo if{}, for{}, function{}).

let las variables no se elevan:

Otra diferencia entre vary letes que las variables declaradas con let no se eleven . Un ejemplo es la mejor manera de ilustrar este comportamiento:

variables con let no se izan:

console.log(letVar);

let letVar = 10;
// referenceError, the variable doesn't get hoisted

las variables con las var que se elevan:

console.log(varVar);

var varVar = 10;
// logs undefined, the variable gets hoisted

Global letno se apega a window:

Una variable declarada letdentro del alcance global (que es un código que no está en una función) no se agrega como una propiedad en el windowobjeto global . Por ejemplo (este código es de alcance global):

var bar = 5;
let foo  = 10;

console.log(bar); // logs 5
console.log(foo); // logs 10

console.log(window.bar);  
// logs 5, variable added to window object

console.log(window.foo);
// logs undefined, variable not added to window object


When should let be used over var?

Utilizar letmás varcada vez que pueda, ya que es simplemente cuyo ámbito más específico. Esto reduce los posibles conflictos de nombres que pueden ocurrir cuando se trabaja con una gran cantidad de variables. varse puede usar cuando desea que una variable global esté explícitamente en el windowobjeto (siempre considere cuidadosamente si esto es realmente necesario).

15

let es interesante, porque nos permite hacer algo como esto:

(() => {
    var count = 0;

    for (let i = 0; i < 2; ++i) {
        for (let i = 0; i < 2; ++i) {
            for (let i = 0; i < 2; ++i) {
                console.log(count++);
            }
        }
    }
})();

Lo que da como resultado el recuento [0, 7].

Mientras que

(() => {
    var count = 0;

    for (var i = 0; i < 2; ++i) {
        for (var i = 0; i < 2; ++i) {
            for (var i = 0; i < 2; ++i) {
                console.log(count++);
            }
        }
    }
})();

Solo cuenta [0, 1].

0
15

También parece que, al menos en Visual Studio 2015, TypeScript 1.5, "var" permite múltiples declaraciones del mismo nombre de variable en un bloque, y "let" no.

Esto no generará un error de compilación:

var x = 1;
var x = 2;

Esta voluntad:

let x = 1;
let x = 2;
14

ES6 introdujo dos nuevas palabras clave ( let y const ) alternativas a var .

Cuando necesite una desaceleración a nivel de bloque, puede ir con let y const en lugar de var.

La siguiente tabla resume la diferencia entre var, let y const

ingrese la descripción de la imagen aquí

1
  • 3
    La columna izada es incorrecta. Todos levantan variable. La diferencia con vares que se elevan pero no se inicializan al undefinedvalor. Si no levantaran, no enmascararían variables del mismo nombre en bloques de cierre : stackoverflow.com/q/63337235/2326961Maggyero 11/08/20 a las 9:43
13

var es una variable de alcance global (con capacidad de elevación).

lety constes alcance de bloque.

test.js

{
    let l = 'let';
    const c = 'const';
    var v = 'var';
    v2 = 'var 2';
}

console.log(v, this.v);
console.log(v2, this.v2);
console.log(l); // ReferenceError: l is not defined
console.log(c); // ReferenceError: c is not defined
10

Cuando usas let

La letpalabra clave adjunta la declaración de variable al alcance de cualquier bloque (comúnmente un { .. }par) en el que está contenido. En otras palabras, letsecuestra implícitamente el alcance de cualquier bloque para su declaración de variable.

letno se puede acceder a las variables en el windowobjeto porque no se puede acceder a ellas de forma global.

function a(){
    { // this is the Max Scope for let variable
        let x = 12;
    }
    console.log(x);
}
a(); // Uncaught ReferenceError: x is not defined

Cuando usas var

var y las variables en ES5 tienen alcances en funciones, lo que significa que las variables son válidas dentro de la función y no fuera de la función en sí.

varSe puede acceder a las variables en el windowobjeto porque no se puede acceder a ellas de forma global.

function a(){ // this is the Max Scope for var variable
    { 
        var x = 12;
    }
    console.log(x);
}
a(); // 12

Si quieres saber más sigue leyendo a continuación

una de las preguntas de entrevista más famosas sobre el alcance también puede ser suficiente con el uso exacto de lety varcomo se indica a continuación;

Cuando usas let

for (let i = 0; i < 10 ; i++) {
    setTimeout(
        function a() {
            console.log(i); //print 0 to 9, that is literally AWW!!!
        }, 
        100 * i);
}

Esto se debe a que cuando se usa let, para cada iteración del ciclo, la variable tiene un alcance y tiene su propia copia.

Cuando usas var

for (var i = 0; i < 10 ; i++) {
    setTimeout(
        function a() {
            console.log(i); //print 10 times 10
        }, 
        100 * i);
}

Esto se debe a que cuando se usa var, para cada iteración de bucle, la variable tiene un alcance y tiene una copia compartida.

9

Si leo las especificaciones correctamente, let afortunadamente también se pueden aprovechar para evitar las funciones de autoinvocación utilizadas para simular miembros solo privados, un patrón de diseño popular que disminuye la legibilidad del código, complica la depuración, que no agrega protección de código real u otro beneficio, excepto tal vez satisfacer las necesidades de alguien. deseo de semántica, así que deja de usarla. /despotricar

var SomeConstructor;

{
    let privateScope = {};

    SomeConstructor = function SomeConstructor () {
        this.someProperty = "foo";
        privateScope.hiddenProperty = "bar";
    }

    SomeConstructor.prototype.showPublic = function () {
        console.log(this.someProperty); // foo
    }

    SomeConstructor.prototype.showPrivate = function () {
        console.log(privateScope.hiddenProperty); // bar
    }

}

var myInstance = new SomeConstructor();

myInstance.showPublic();
myInstance.showPrivate();

console.log(privateScope.hiddenProperty); // error

Ver ' Emulación de interfaces privadas '

2
  • ¿Puede explicar cómo las expresiones de función invocadas inmediatamente no proporcionan "protección de código" y lo lethacen? (Supongo que te refieres a IIFE con "función de autoinvocación".)Robert Siemer 1 de marzo de 2020 a las 3:58
  • ¿Y por qué lo pones hiddenPropertyen el constructor? Solo hay uno hiddenPropertypara todas las instancias de su "clase". Robert Siemer 1/03/20 a las 12:52
7

Algunos trucos con let:

1.

    let statistics = [16, 170, 10];
    let [age, height, grade] = statistics;

    console.log(height)

2.

    let x = 120,
    y = 12;
    [x, y] = [y, x];
    console.log(`x: ${x} y: ${y}`);

3.

    let node = {
                   type: "Identifier",
                   name: "foo"
               };

    let { type, name, value } = node;

    console.log(type);      // "Identifier"
    console.log(name);      // "foo"
    console.log(value);     // undefined

    let node = {
        type: "Identifier"
    };

    let { type: localType, name: localName = "bar" } = node;

    console.log(localType);     // "Identifier"
    console.log(localName);     // "bar"

Getter y setter con let:

let jar = {
    numberOfCookies: 10,
    get cookies() {
        return this.numberOfCookies;
    },
    set cookies(value) {
        this.numberOfCookies = value;
    }
};

console.log(jar.cookies)
jar.cookies = 7;

console.log(jar.cookies)
3
  • por favor, ¿qué significa esto let { type, name, value } = node;? crea un nuevo objeto con 3 propiedades tipo / nombre / valor y lo inicializa con los valores de las propiedades del nodo? AlainIb 15 de junio de 2017 a las 7:55
  • En el ejemplo 3, está volviendo a declarar el nodo que causa la excepción. Todos estos ejemplos también funcionan perfectamente con var. Rehan Haider 9 de enero de 2019 a las 10:57
  • Esto no responde a la pregunta; podría beneficiarse de una explicación sobre lo que hace cada bloque de código. TylerH 13 de abril a las 18:50
7

A continuación se muestra cómo 'let' y 'var' son diferentes en el alcance:

let gfoo = 123;
if (true) {
    let gfoo = 456;
}
console.log(gfoo); // 123

var hfoo = 123;
if (true) {
    var hfoo = 456;
}
console.log(hfoo); // 456

El gfoo, definido por letinicialmente está en el alcance global , y cuando declaramos gfoonuevamente dentro del, if clausesu alcance cambió y cuando se asigna un nuevo valor a la variable dentro de ese alcance, no afecta el alcance global.

Considerando que hfoo, definido por varestá inicialmente en el alcance global , pero nuevamente cuando lo declaramos dentro de if clause, considera el alcance global hfoo, aunque var se ha usado nuevamente para declararlo. Y cuando reasignamos su valor, vemos que el alcance global hfoo también se ve afectado. Ésta es la principal diferencia.

6

let es parte de es6. Estas funciones explicarán la diferencia de forma sencilla.

function varTest() {
  var x = 1;
  if (true) {
    var x = 2;  // same variable!
    console.log(x);  // 2
  }
  console.log(x);  // 2
}

function letTest() {
  let x = 1;
  if (true) {
    let x = 2;  // different variable
    console.log(x);  // 2
  }
  console.log(x);  // 1
}
6

vamos vs var. Se trata de alcance .

Las variables var son globales y se puede acceder a ellas básicamente en todas partes, mientras que las variables let no son globales y solo existen hasta que un paréntesis de cierre las mata.

Vea mi ejemplo a continuación y observe cómo la variable lion (let) actúa de manera diferente en los dos console.logs; queda fuera del alcance en el segundo archivo console.log.

var cat = "cat";
let dog = "dog";

var animals = () => {
    var giraffe = "giraffe";
    let lion = "lion";

    console.log(cat);  //will print 'cat'.
    console.log(dog);  //will print 'dog', because dog was declared outside this function (like var cat).

    console.log(giraffe); //will print 'giraffe'.
    console.log(lion); //will print 'lion', as lion is within scope.
}

console.log(giraffe); //will print 'giraffe', as giraffe is a global variable (var).
console.log(lion); //will print UNDEFINED, as lion is a 'let' variable and is now out of scope.
4

Como se ha mencionado más arriba:

The difference is scoping. var is scoped to the nearest function block and let is scoped to the nearest enclosing block, which can be smaller than a function block. Both are global if outside any block.Lets see an example:

Ejemplo 1:

En mis dos ejemplos tengo una función myfunc. myfunccontiene una variable myvarigual a 10. En mi primer ejemplo, verifico si myvares igual a 10 ( myvar==10). Si es así, nuevamente declaro una variable myvar(ahora tengo dos variables myvar) usando la varpalabra clave y le asigno un nuevo valor (20). En la siguiente línea imprimo su valor en mi consola. Después del bloque condicional, vuelvo a imprimir el valor de myvaren mi consola. Si observa la salida de myfunc, myvartiene un valor igual a 20.

dejar palabra clave

Example2: En mi segundo ejemplo, en lugar de usar una varpalabra clave en mi bloque condicional, declaro myvarusar una letpalabra clave. Ahora, cuando llamo myfunc , obtengo dos salidas diferentes: myvar=20y myvar=10.

Entonces la diferencia es muy simple, es decir, su alcance.

1
  • 4
    Por favor, no publique imágenes de código, se considera una mala práctica en SO, ya que no se podrá buscar para futuros usuarios (así como también por problemas de accesibilidad). Además, esta respuesta no agrega nada que otras respuestas aún no hayan abordado. inostia 24/08/18 a las 17:29
3

Quiero vincular estas palabras clave al contexto de ejecución, porque el contexto de ejecución es importante en todo esto. El contexto de ejecución tiene dos fases: una fase de creación y una fase de ejecución. Además, cada contexto de ejecución tiene un entorno variable y un entorno exterior (su entorno léxico).

Durante la Fase de Creación de un Contexto de Ejecución, var, let y const aún almacenarán su variable en la memoria con un valor indefinido en el Entorno de Variables del Contexto de Ejecución dado. La diferencia está en la fase de ejecución. Si usa una referencia a una variable definida con var antes de que se le asigne un valor, simplemente estará indefinida. No se planteará ninguna excepción.

Sin embargo, no puede hacer referencia a la variable declarada con let o const hasta que se declare. Si intenta utilizarlo antes de que se declare, se generará una excepción durante la fase de ejecución del contexto de ejecución. Ahora la variable todavía estará en la memoria, cortesía de la Fase de Creación del Contexto de Ejecución, pero el Motor no le permitirá usarla:

function a(){
    b;
    let b;
}
a();
> Uncaught ReferenceError: b is not defined

Con una variable definida con var, si el motor no puede encontrar la variable en el entorno variable del contexto de ejecución actual, subirá por la cadena de alcance (el entorno exterior) y comprobará el entorno variable del entorno exterior para la variable. Si no puede encontrarlo allí, continuará buscando en la Cadena de Alcance. Este no es el caso de let y const.

La segunda característica de let es que introduce el alcance del bloque. Los bloques se definen con llaves. Los ejemplos incluyen bloques de función, bloques if, para bloques, etc. Cuando declaras una variable con let dentro de un bloque, la variable solo está disponible dentro del bloque. De hecho, cada vez que se ejecuta el bloque, como dentro de un bucle for, se creará una nueva variable en la memoria.

ES6 también introduce la palabra clave const para declarar variables. const también tiene ámbito de bloque. La diferencia entre let y const es que las variables const deben declararse usando un inicializador, o generará un error.

Y, finalmente, cuando se trata del contexto de ejecución, las variables definidas con var se adjuntarán al objeto 'this'. En el contexto de ejecución global, ese será el objeto de ventana en los navegadores. Este no es el caso de let o const.

3

Como actualmente estoy tratando de obtener una comprensión profunda de JavaScript, compartiré mi breve investigación que contiene algunas de las grandes piezas ya discutidas, además de algunos otros detalles en una perspectiva diferente.

Comprender la diferencia entre var y let puede ser más fácil si comprendemos la diferencia entre función y alcance de bloque .

Consideremos los siguientes casos:

(function timer() {
    for(var i = 0; i <= 5; i++) {
        setTimeout(function notime() { console.log(i); }, i * 1000);
    }
})();


   Stack            VariableEnvironment //one VariablEnvironment for timer();
                                       // when the timer is out - the value will be the same value for each call
5. [setTimeout, i]  [i=5] 
4. [setTimeout, i]  
3. [setTimeout, i]
2. [setTimeout, i]
1. [setTimeout, i]
0. [setTimeout, i]

####################    

(function timer() {
    for (let i = 0; i <= 5; i++) {
        setTimeout(function notime() { console.log(i); }, i * 1000);
    }
})();

   Stack           LexicalEnvironment - each iteration has a new lexical environment
5. [setTimeout, i]  [i=5]       
                      LexicalEnvironment 
4. [setTimeout, i]    [i=4]     
                        LexicalEnvironment 
3. [setTimeout, i]      [i=3]       
                         LexicalEnvironment 
2. [setTimeout, i]       [i=2]
                           LexicalEnvironment 
1. [setTimeout, i]         [i=1]
                             LexicalEnvironment 
0. [setTimeout, i]           [i=0]

cuando timer()se llama, se crea un ExecutionContext que contendrá tanto el VariableEnvironment como todos los LexicalEnvironments correspondientes a cada iteración.

Y un ejemplo más simple

Alcance de la función

function test() {
    for(var z = 0; z < 69; z++) {
        //todo
    }
    //z is visible outside the loop
}

Alcance del bloque

function test() {
    for(let z = 0; z < 69; z++) {
        //todo
    }
    //z is not defined :(
}
3

Me encontré con un caso de uso que tenía que utilizar varmás de letintroducir nueva variable. He aquí un caso:

Quiero crear una nueva variable con nombres de variables dinámicas.

let variableName = 'a';
eval("let " + variableName + '= 10;');
console.log(a);   // this doesn't work
var variableName = 'a';
eval("var " + variableName + '= 10;');
console.log(a);   // this works

El código anterior no funciona porque evalintroduce un nuevo bloque de código. La declaración usando vardeclarará una variable fuera de este bloque de código ya que vardeclara una variable en el alcance de la función.

let, por otro lado, declara una variable en un alcance de bloque. Entonces, la avariable solo será visible en el evalbloque.

1
  • ¿Cuándo tendrá que crear un nombre de variable dinámica y acceder a él más tarde? Es mucho mejor crear un objeto y asignarle claves y valores. typeof null is object 9/11/20 a las 19:18
2

Ahora creo que hay un mejor alcance de variables para un bloque de declaraciones usando let:

function printnums()
{
    // i is not accessible here
    for(let i = 0; i <10; i+=)
    {
       console.log(i);
    }
    // i is not accessible here

    // j is accessible here
    for(var j = 0; j <10; j++)
    {
       console.log(j);
    }
    // j is accessible here
}

Creo que la gente comenzará a usar let aquí después para que tengan un alcance similar en JavaScript como otros lenguajes, Java, C #, etc.

Las personas que no tenían una comprensión clara sobre el alcance en JavaScript solían cometer el error anteriormente.

La elevación no es compatible con el uso let.

Con este enfoque, se eliminan los errores presentes en JavaScript.

Consulte ES6 en profundidad: deje y const para entenderlo mejor.

0