¿Cuál es la diferencia entre usar "let" y "var" para declarar una variable en JavaScript?


10 Answers

let también puede usarse para evitar problemas con cierres. Vincula el valor nuevo en lugar de mantener una referencia anterior como se muestra en los ejemplos a continuación.

DEMO

for(var i = 1; i < 6; i++) {
  document.getElementById('my-element' + i)
    .addEventListener('click', function() { alert(i) })
}

El código anterior muestra un problema clásico de cierre de JavaScript. La referencia a la variable i se almacena en el cierre del manejador de clics, en lugar del valor real de i .

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

La solución general es envolver esto en una función anónima y pasarlo como argumento. Tales problemas también pueden evitarse ahora usando let lugar var como se muestra en el código a continuación.

DEMO (Probado en Chrome y Firefox 50)

'use strict';

for(let i = 1; i < 6; i++) {
  document.getElementById('my-element' + i)
    .addEventListener('click', function() { alert(i) })
}
Question

ECMAScript 6 introdujo la declaración de let . He oído que se describe como una variable "local", pero aún no estoy muy seguro de cómo se comporta de forma diferente a la palabra clave var .

¿Cuáles son las diferencias? ¿Cuándo debería let usar sobre var ?




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

  • Una variable definida usando una instrucción var se conoce a través de la función en la que está definida, desde el inicio de la función. (*)
  • Una variable definida mediante una instrucción let solo se conoce en el bloque en el que está definida, desde el momento en que se define. (**)

Para entender 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 j solo se conoce en el primer bucle for, pero no antes y después. Sin embargo, nuestra variable i es conocida en toda la función.

Además, considere que las variables de ámbito de bloque no se conocen antes de que se declaren porque no están elevadas. Tampoco puede redeclarar 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 se generan y que no producen ningún error en el caso de declaraciones múltiples.

¿Es seguro usar let hoy?

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

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

  • Si está escribiendo código JavaScript del lado del servidor ( Node.js ), puede usar la instrucción let seguridad.

  • Si está escribiendo un código JavaScript del lado del cliente y usa un transpiler (como Traceur ), puede usar la instrucción let seguridad, sin embargo, es probable que su código no sea óptimo con respecto al rendimiento.

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

    Hoy, 23 de febrero de 2016, estos son algunos navegadores que no admiten o solo tienen soporte parcial:

    • Internet Explorer 10 y siguientes (sin soporte)
    • Firefox 43 y siguientes (sin soporte)
    • Safari 9 y siguientes (sin soporte)
    • Opera Mini 8 e inferior (sin soporte)
    • Navegador Android 4 y siguientes (sin soporte)
    • Opera 36 y abajo (soporte parcial)
    • Chrome 51 y abajo (soporte parcial)

Cómo realizar un seguimiento del soporte del navegador

Para obtener una descripción actualizada de los navegadores compatibles con la declaración let en el momento de leer esta respuesta, consulte esta página Can I Use ?

(*) Las variables de alcance global y funcional se pueden inicializar y usar antes de que se declaren porque las variables de JavaScript son hoisted . Esto significa que las declaraciones siempre son muy importantes.

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




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

Por ejemplo, alcanza el bloque de inclusión. No existen antes de que se declaren, etc.

Sin embargo, vale la pena señalar que let es solo una parte de las nuevas implementaciones de Javascript y tiene diversos grados de compatibilidad con el navegador .




Aquí hay un ejemplo para agregar a lo que otros ya han escrito. Supongamos que desea crear una matriz de funciones, adderFunctions , donde cada función toma un único argumento Number y devuelve la suma del argumento y el índice de la función en la matriz. Intentar generar adderFunctions con un bucle utilizando la palabra clave var no funcionará de la forma en 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 el conjunto deseado de funciones porque su alcance va más allá de la iteración del bloque for en el que se creó cada función. En cambio, al final del ciclo, i en el cierre de cada función se refiere al valor de i al final del ciclo (1000) para cada función anónima en adderFunctions . Esto no es lo que queríamos en absoluto: ahora tenemos un conjunto 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 todas las adderFunctions .

Sin embargo, podemos intentar de nuevo usando la palabra clave let :

// 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, i se recupera en cada iteración del ciclo for . Cada función ahora conserva el valor de i en el momento de la creación de la función, y adderFunctions comporta como se esperaba.

Ahora, la imagen mezcla los dos comportamientos y probablemente veas por qué no se recomienda mezclar el nuevo let y const con el var anterior en el mismo script. Hacerlo puede dar como resultado 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. Use un linter.

NOTA: Este es un ejemplo de enseñanza destinado a demostrar el comportamiento de var / let en bucles y con cierres de funciones que también serían fáciles de entender. Esta sería una manera terrible de agregar números. Pero la técnica general de captura de datos en cierres anónimos de funciones puede encontrarse en el mundo real en otros contextos. YMMV.




This article clearly defines the difference between var, let and const

const is a signal that the identifier won't be reassigned.

let , is a signal that the variable may be reassigned, such as a counter in a loop, or a value swap in an algorithm. It also signals that the variable will be used only in the block it's defined in, which is not always the entire containing function.

var is now the weakest signal available when you define a variable in JavaScript. The variable may or may not be reassigned, and the variable may or may not be used for an entire function, or just for the purpose of a block or loop.

https://medium.com/javascript-scene/javascript-es6-var-let-or-const-ba58b8dcde75#.esmkpbg9b




Pueden las siguientes dos funciones mostrar 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
}



Some hacks with 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 and setter with 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)






let

Alcance del bloque

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

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

En el nivel superior, las variables declaradas con let no 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 un funciton (pero fuera de un bloque), let tiene 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

No se puede acceder a las variables declaradas usando let 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

Las variables declaradas con bucles de entrada solo se pueden referenciar 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 let lugar de var en un bucle, con cada iteración obtendrá una nueva variable. Eso significa que puede usar con seguridad 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 , no se puede acceder a las variables declaradas mediante let 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 let con el 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

const es bastante similar a let -it's block-scoped y tiene TDZ. Sin embargo, hay dos cosas que son diferentes.

No volver a asignar

La variable declarada usando const no 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() .

Inicializador es requerido

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

const a; // SyntaxError: Missing initializer in const declaration



let is interesting, because it allows us to do something like this:

(() => {
    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++);
            }
        }
    }
})();

Which results in counting [0, 7].

Whereas

(() => {
    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++);
            }
        }
    }
})();

Only counts [0, 1].




var is global scope (hoist-able) variable.

let and const is block scope.

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




Related