Both let and var allow you to create variables in JavaScript. The main differences between them are scope, hoisting and closures.

JavaScript scopes in the context of var and let

The variables declared with the var keyword can be global, while the ones declared with let can’t. It’s important if you try to create a global variable in the browser and then access it later as a property of the object window.

var x = 17;
let y = 25;

console.log(window.x); // 17
console.log(window.y); // undefined

Variables declared with var don’t have block scope, it’s only available to the variables declared with let. It’s very dangerous as you may accidentally overwrite something you didn’t mean to.

var i = 0;
let j = 0;

if (i === 0) {
  var i = 'overwritten!';

if (j === 0) {
  let j = 'overwritten!';

console.log(i); // overwritten!
console.log(j); // 0

Hoisting in JavaScript

Hoisting in JS is the process or “lifting” the variables up to the top of the scope before they’re used.

It can be best described with the code example.

console.log(message);  // undefined

var message = 'hello';

Once you run this code, you’ll see the value undefined logged to the console.

However, if you change the variable declaration from var to let, the result will be completely different.

console.log(message);  // ReferenceError: Cannot access 'message' before initialization

var message = 'hello';

In this case you’ll get an error ReferenceError: Cannot access 'message' before initialization. This is a useful error that can prevent a lot of bugs as there’s no point in trying to access something before initializing it.

Closure behaviour for var and let

Using let allows you to capture the present value of the variable in JavaScript closure. With var you wouldn’t be able to do that.

The best example would be a for loop with a setTimeout.

for(var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 1000);

If you expected to see the values 0, 1, and 2 to be logged to console after a one second delay, I have bad news for you. It’d not that’s going to happen.

Instead, you’ll see the number 3 logged three times to the console.

The problem? The scoping issue with the var keyword doesn’t allow JavaScript to capture the present value of the counter i when you set the timeout on each iteration of the loop.

The good news is that it’s easily fixable with let.

for(let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 1000);

Now, you’ll see the values 0, 1, and 2 in the console as expected.

As a conclusion, I suggest you always use let over var unless you’re working in some ancient legacy environment that doesn’t support declaring variables with let.