Here are few differences between using let and var:

1. Redeclaration:

In strict mode, var will let you re-declare the same variable in the same scope while let throws a SyntaxError.

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

let bar = "bar1";
let bar = "bar2"; // SyntaxError: Identifier 'bar' has already been declared

2. Creating global object property:

let, in contrast to var, doesn't make a property on the global object:

var foo = "Foo";  // globally scoped
let bar = "Bar"; // globally scoped

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

3. Scoping rules:

The most important difference is the scoping rules. Variables declared by var keyword are scoped to the immediate function body while let variables are scoped to the immediate enclosing block meant by { }.

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

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

  {
    let baz = "Bazz";
    console.log(baz); // Bazz
  }

  console.log(baz); // ReferenceError
}

run();

The motivation behind why let keyword was introduced with the language was function scope is confusing and was one of the main sources of bugs in JavaScript.

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("Value: " + i);
  };
}
for (var j = 0; j < 3; j++) {
  // and now let's run each one to see
  funcs[j]();
}

Value: 3 was output to console each time funcs[j](); was used since anonymous functions were bound to the same variable.

Developers needed to make immediately invoked functions to catch correct value from the loops yet that was also hairy.

4. Hoisting:

While variables declared with var keyword are hoisted (initialized with undefined before the code is run) which implies they are accessible in their enclosing scope even before they are declared:

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

run();

let variables are not initialized until their definition is evaluated. Accessing them before the initialization brings about a ReferenceError. Variable said to be in "temporal dead zone" from the beginning of the block until the initialization is handled.

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

checkHoisting();
0