let is one of the fundamental improvements in modern JavaScript. Below is a compact, well-organized guide: what let does, why it matters, subtle gotchas (TDZ, hoisting, shadowing), comparisons with var/const, and practical examples you can copy/paste.

1. What let does — short answer

let declares a variable that is block scoped (i.e., visible only inside the nearest { ... }), can be reassigned, and is not added as a property of the global object (unlike var in the global scope).

{
let x = 1;
}
console.log(typeof x); // "undefined" — x is not visible outside the block

2. Basic syntax & example

let count = 0;
count = count + 1; // reassignment allowed

You may declare without initialization:

let name;
name = "Zaman";

3. Block scope — concrete examples

Inside a block:

let a = 10;
if (true) {
let a = 20; // different variable — block scoped
console.log(a); // 20
}
console.log(a); // 10

Inside a function:

function foo() {
let local = "inside";
console.log(local);
}
foo();
console.log(typeof local); // "undefined"

4. Hoisting behaviour — clarified

  • let declarations are hoisted (the JS engine knows the name), but they are not initialized until execution reaches the declaration line → TDZ.
  • var is hoisted and initialized to undefined at function/global scope, so you can access it before its declaration (but it’s undefined).
console.log(fooVar); // undefined
var fooVar = 1;console.log(fooLet); // ReferenceError
let fooLet = 1;

5. let in loops — avoids closure traps

A major benefit: let creates a fresh binding per iteration in for loops.

  • Classic var problem:
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 0); // prints 3, 3, 3
}

With let:

for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 0); // prints 0, 1, 2
}

Reason: let i creates a new binding for each iteration.

6. Global scope difference (browser)

<script>
var a = 1;
let b = 2;
console.log(window.a); // 1
console.log(window.b); // undefined
</script>

var creates a property on the global object; let does not.

7. let in switch, try/catch, and blocks

switch:

switch (k) {
case 1: {
let x = "a";
break;
}
case 2: {
let x = "b"; // fine — block scopes are separate
break;
}
}

try/catch:

try {
throw new Error('e');
} catch (err) {
let errorMessage = err.message;
console.log(errorMessage);
}
console.log(typeof err); // undefined — err scoped to catch block

Categorized in:

Javascript ES6,