What’s the difference between using “let” and “var”?

What’s the difference between using “let” and “var”?

ECMAScript 6 introduced the let statement.
I’ve heard it that it’s described as a “local” variable, but I’m still not quite sure how it behaves differently than the var keyword.
What are the differences? When should let be used over var?

Solutions/Answers:

Solution 1:

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.

Also, variables declared with let are not accessible before they are declared in their enclosing block. As seen in the demo, this will throw a ReferenceError exception.

Demo

var html = '';

write('#### global ####\n');
write('globalVar: ' + globalVar); //undefined, but visible

try {
  write('globalLet: ' + globalLet); //undefined, *not* visible
} catch (exception) {
  write('globalLet: exception');
}

write('\nset variables');

var globalVar = 'globalVar';
let globalLet = 'globalLet';

write('\nglobalVar: ' + globalVar);
write('globalLet: ' + globalLet);

function functionScoped() {
  write('\n#### function ####');
  write('\nfunctionVar: ' + functionVar); //undefined, but visible

  try {
    write('functionLet: ' + functionLet); //undefined, *not* visible
  } catch (exception) {
    write('functionLet: exception');
  }

  write('\nset variables');

  var functionVar = 'functionVar';
  let functionLet = 'functionLet';

  write('\nfunctionVar: ' + functionVar);
  write('functionLet: ' + functionLet);
}

function blockScoped() {
  write('\n#### block ####');
  write('\nblockVar: ' + blockVar); //undefined, but visible

  try {
    write('blockLet: ' + blockLet); //undefined, *not* visible
  } catch (exception) {
    write('blockLet: exception');
  }

  for (var blockVar = 'blockVar', blockIndex = 0; blockIndex < 1; blockIndex++) {
    write('\nblockVar: ' + blockVar); // visible here and whole function
  };

  for (let blockLet = 'blockLet', letIndex = 0; letIndex < 1; letIndex++) {
    write('blockLet: ' + blockLet); // visible only here
  };

  write('\nblockVar: ' + blockVar);

  try {
    write('blockLet: ' + blockLet); //undefined, *not* visible
  } catch (exception) {
    write('blockLet: exception');
  }
}

function write(line) {
  html += (line ? line : '') + '<br />';
}

functionScoped();
blockScoped();

document.getElementById('results').innerHTML = html;
<pre id="results"></pre>

Global:

They are very similar when used like this outside a function block.

let me = 'go';  // globally scoped
var i = 'able'; // globally scoped

However, global variables defined with let will not be added as properties on the global window object like those defined with var.

console.log(window.me); // undefined
console.log(window.i); // 'able'

Function:

They are identical when used like this in a function block.

function ingWithinEstablishedParameters() {
    let terOfRecommendation = 'awesome worker!'; //function block scoped
    var sityCheerleading = 'go!'; //function block scoped
}

Block:

Here is the difference. let is only visible in the for() loop and var is visible to the whole function.

function allyIlliterate() {
    //tuce is *not* visible out here

    for( let tuce = 0; tuce < 5; tuce++ ) {
        //tuce is only visible in here (and in the for() parentheses)
        //and there is a separate tuce variable for each iteration of the loop
    }

    //tuce is *not* visible out here
}

function byE40() {
    //nish *is* visible out here

    for( var nish = 0; nish < 5; nish++ ) {
        //nish is visible to the whole function
    }

    //nish *is* visible out here
}

Redeclaration:

Assuming strict mode, var will let you re-declare the same variable in the same scope.

'use strict';
var me = 'foo';
var me = 'bar'; // No problem, 'me' is replaced.

On the other hand, let will not:

'use strict';
let me = 'foo';
let me = 'bar'; // SyntaxError: Identifier 'me' has already been declared

Solution 2:

let can also be used to avoid problems with closures. It binds fresh value rather than keeping an old reference as shown in examples below.

Demo

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

Code above demonstrates a classic JavaScript closure problem. Reference to the i variable is being stored in the click handler closure, rather than the actual value of i.

Every single click handler will refer to the same object because there’s only one counter object which holds 6 so you get six on each click.

General workaround is to wrap this in an anonymous function and pass i as argument. Such issues can also be avoided now by using let instead var as shown in code below.

Demo (Tested in Chrome and Firefox 50)

'use strict';

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

Solution 3:

What’s the difference between let and var?

  • A variable defined using a var statement is known throughout the function it is defined in, from the start of the function.*
  • A variable defined using a let statement is only known in the block it is defined in, from the moment it is defined onward.**

To understand the difference, consider the following code:

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

Here, we can see that our variable j is only known in the first for loop, but not before and after. Yet, our variable i is known in the entire function.

Also, consider that block scoped variables are not known before they are declared because they are not hoisted. You’re also not allowed to redeclare the same block scoped variable within the same block. This makes block scoped variables less error prone than globally or functionally scoped variables, which are hoisted and which do not produce any errors in case of multiple declarations.


Is it safe to use let today?

Some people would argue that in the future we’ll ONLY use let statements and that var statements will become obsolete. JavaScript guru Kyle Simpson wrote a very elaborate article on why he believes that won’t be the case.

Today, however, that is definitely not the case. In fact, we need actually to ask ourselves whether it’s safe to use the let statement. The answer to that question depends on your environment:

  • If you’re writing server-side JavaScript code (Node.js), you can safely use the let statement.

  • If you’re writing client-side JavaScript code and use a browser based transpiler (like Traceur or babel-standalone), you can safely use the let statement, however your code is likely to be anything but optimal with respect to performance.

  • If you’re writing client-side JavaScript code and use a Node based transpiler (like the traceur shell script or Babel), you can safely use the let statement. And because your browser will only know about the transpiled code, performance drawbacks should be limited.

  • If you’re writing client-side JavaScript code and don’t use a transpiler, you need to consider browser support.

    There are still some browsers that don’t support let at all:

Support table


How to keep track of browser support

For an up-to-date overview of which browsers support the let statement at the time of your reading this answer, see this Can I Use page.


* Globally and functionally scoped variables can be initialized and used before they are declared because JavaScript variables are hoisted. This means that declarations are always much to the top of the scope.

** Block scoped variables are not hoisted

Solution 4:

Here’s an explanation of the let keyword with some examples.

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

This table on Wikipedia shows which browsers support Javascript 1.7.

Note that only Mozilla and Chrome browsers support it. IE, Safari, and potentially others don’t.

Solution 5:

The accepted answer is missing a point:

{
  let a = 123;
};

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

Solution 6:

let

Block scope

Variables declared using the let keyword are block-scoped, which means that they are available only in the block in which they were declared.

At the top level (outside of a function)

At the top level, variables declared using let don’t create properties on the global object.

var globalVariable = 42;
let blockScopedVariable = 43;

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

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

Inside a function

Inside a function (but outside of a block), let has the same scope as 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

Inside a block

Variables declared using let inside a block can’t be accessed outside that block.

{
  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

Inside a loop

Variables declared with let in loops can be referenced only inside that loop.

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.

Loops with closures

If you use let instead of var in a loop, with each iteration you get a new variable. That means that you can safely use a closure inside a loop.

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

Temporal dead zone

Because of the temporal dead zone, variables declared using let can’t be accessed before they are declared. Attempting to do so throws an error.

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

No re-declaring

You can’t declare the same variable multiple times using let. You also can’t declare a variable using let with the same identifier as another variable which was declared using 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 is quite similar to let—it’s block-scoped and has TDZ. There are, however, two things which are different.

No re-assigning

Variable declared using const can’t be re-assigned.

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

Note that it doesn’t mean that the value is immutable. Its properties still can be changed.

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

If you want to have an immutable object, you should use Object.freeze().

Initializer is required

You always must specify a value when declaring a variable using const.

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