Question

JavaScript let vs var: Scope, Hoisting, and When to Use Each

javascriptscopeecmascript-6varlet

Question

ECMAScript 6 introduced the let declaration keyword.

I have heard that it is described as a local variable, but I am still not fully sure how it behaves differently from the var keyword.

What are the differences between let and var in JavaScript? When should let be used instead of var?

Short Answer

By the end of this page, you will understand how let and var differ in scope, hoisting, redeclaration, and global behavior in JavaScript. You will also learn why modern JavaScript usually prefers let and const over var, and how to avoid common bugs caused by var.

Concept

let and var are both used to declare variables in JavaScript, but they follow different rules.

The biggest difference is scope:

  • var is function-scoped
  • let is block-scoped

A block is any code inside curly braces {} such as an if, for, or while statement.

if (true) {
  var a = 10;
  let b = 20;
}

console.log(a); // 10
console.log(b); // ReferenceError

Here, a is still available outside the if block because var ignores block scope. But b only exists inside the block because let respects block scope.

Another important difference is hoisting.

Both var and let are hoisted, which means their declarations are processed before code runs. But they behave differently:

  • var is hoisted and initialized with undefined
  • let is hoisted but not initialized until its declaration is reached
console.log(x); // undefined
var x = 5;

console.log(y); // ReferenceError
let y = 5;

The let example fails because y is in the temporal dead zone: the period between the start of the scope and the line where the variable is declared.

let also prevents accidental redeclaration in the same scope:

let count = 1;
let count = 2; // SyntaxError

But var allows it:

var count = 1;
var count = 2; // allowed

This matters because silent redeclarations can hide bugs.

In real programming, let matters because it makes variable behavior more predictable. It reduces scope-related mistakes, especially in loops, conditionals, and larger codebases.

Mental Model

Think of var as a label attached to an entire room, while let is a label attached to a specific box inside the room.

  • With var, if you declare a variable anywhere in a function, it is available across the whole function.
  • With let, the variable only exists inside the exact block where you created it.

Another way to think about it:

  • var is like a note written on the wall of the whole function.
  • let is like a sticky note placed on one section only.

This is why let gives you tighter control. Variables stay closer to where they are actually used.

Take Quiz

Syntax and Examples

Basic syntax

var name = "Alice";
let age = 25;

Both declare variables, but they behave differently.

Scope example

function test() {
  if (true) {
    var city = "Paris";
    let country = "France";
  }

  console.log(city);    // "Paris"
  console.log(country); // ReferenceError
}

Why this happens

  • city uses var, so it belongs to the whole function test
  • country uses let, so it exists only inside the if block

Loop example

for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log("var:", i), 100);
}

for (let j = 0; j < 3; j++) {
  setTimeout(() => console.log("let:", j), 100);
}

Output

var: 3
var: 3
var: 3
let: 0
let: 1
let: 2

Why this happens

  • var i is shared across all loop iterations
  • let j creates a new binding for each iteration

This is one of the most practical reasons to prefer let in loops.

Redeclaration example

var score = 10;
var score = 20; // allowed

let level = 1;
// let level = 2; // SyntaxError

let helps catch mistakes earlier.

Step by Step Execution

Consider this example:

function demo() {
  console.log(a);
  // console.log(b);

  var a = 1;
  let b = 2;

  if (true) {
    var aInside = "var value";
    let bInside = "let value";
  }

  console.log(a);       // 1
  console.log(b);       // 2
  console.log(aInside); // "var value"
  // console.log(bInside); // ReferenceError
}

demo();

What happens step by step

  1. The function demo starts running.
  2. JavaScript sees var a and hoists it, initializing it to undefined.
  3. JavaScript also hoists let b, but does not initialize it yet.
  4. console.log(a) prints undefined.
  5. If console.log(b) were uncommented here, it would throw a ReferenceError because b is still in the temporal dead zone.
  6. var a = 1 assigns 1 to a.
  7. let b = 2 creates and initializes b.
  8. The if block runs.
  9. var aInside becomes available to the whole function.
  10. let bInside exists only inside the if block.
  11. After the block ends, aInside is still accessible.
  12. After the block ends, bInside no longer exists.

This example shows all the major differences together: hoisting, scope, and block visibility.

Real World Use Cases

let is useful whenever a variable should only exist in a small area of code.

Common real-world examples

Inside loops

for (let index = 0; index < items.length; index++) {
  console.log(items[index]);
}

Each iteration gets the correct index.

Temporary calculation values

function calculateTotal(price, taxRate) {
  let tax = price * taxRate;
  let total = price + tax;
  return total;
}

The variables only exist where they are needed.

Conditional logic

if (user.isAdmin) {
  let message = "Access granted";
  console.log(message);
}

message does not leak outside the block.

API or form validation code

function validateEmail(email) {
  let isValid = email.includes("@");
  return isValid;
}

Modern apps often rely on small, clearly scoped variables like this.

var is mostly seen in:

  • older JavaScript codebases
  • legacy tutorials
  • code written before ES6 became standard

In new code, var is usually avoided unless you are maintaining older code or specifically need its function-scoped behavior.

Take Quiz

Real Codebase Usage

In real projects, developers usually follow this pattern:

  • use const by default
  • use let when reassignment is needed
  • avoid var

Typical pattern

const apiUrl = "/users";
let retries = 0;

while (retries < 3) {
  retries++;
}

Why teams avoid var

Fewer scope bugs

let keeps variables inside the block where they are used.

Safer refactoring

If code is moved into an if block or loop, let is less likely to affect surrounding code unexpectedly.

Better loop behavior

let works naturally with asynchronous callbacks inside loops.

Common patterns in codebases

Guard clauses

function processUser(user) {
  if (!user) {
    return;
  }

  let name = user.name.trim();
  console.log(name);
}

Validation

function submit(data) {
  let errors = [];

  if (!data.email) {
    errors.push("Email is required");
  }

  return errors;
}

Transforming data

function formatUsers(users) {
  return users.map((user) => {
    let fullName = `${user.firstName} ${user.lastName}`;
    return fullName;
  });
}

These examples show how let fits naturally into modern JavaScript structure.

Common Mistakes

1. Assuming var is block-scoped

Broken code:

if (true) {
  var message = "hello";
}

console.log(message); // still works

Why it is a problem:

You may expect message to disappear after the block, but it does not.

How to avoid it:

Use let when you want block scope.

if (true) {
  let message = "hello";
}

console.log(message); // ReferenceError

2. Using a let variable before declaring it

Broken code:

console.log(total);
let total = 100;

Why it is a problem:

let is hoisted but not initialized before the declaration line.

How to avoid it:

Declare variables before using them.

3. Redeclaring let in the same scope

Broken code:

let value = 1;
let value = 2;

Why it is a problem:

This causes a SyntaxError.

How to avoid it:

Reassign instead of redeclare.

let value = 1;
value = 2;

4. Using var in asynchronous loops

Broken code:

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

Output:

3
3
3

How to avoid it:

Use let in the loop.

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

5. Confusing let with const

let means the variable can be reassigned.

let count = 1;
count = 2; // allowed

If the value should not be reassigned, use const instead.

Comparisons

Featurevarlet
ScopeFunction scopeBlock scope
HoistedYesYes
Accessible before declarationYes, as undefinedNo, causes ReferenceError
Redeclaration in same scopeAllowedNot allowed
Creates global object property at top levelUsually yesNo
Loop iteration bindingSharedNew binding per iteration
Recommended for modern codeUsually noYes

let vs const

Featureletconst
ReassignmentAllowedNot allowed
Block scopeYesYes
Must be initialized at declarationNoYes
Best use caseValues that changeValues that should stay assigned

Simple rule of thumb

  • Use const if the variable should not be reassigned
  • Use let if the variable will change
  • Avoid var in modern JavaScript
Take Quiz

Cheat Sheet

var x = 1;
let y = 2;

Key rules

  • var is function-scoped
  • let is block-scoped
  • var can be redeclared in the same scope
  • let cannot be redeclared in the same scope
  • var is hoisted and initialized to undefined
  • let is hoisted but stays in the temporal dead zone until declared

Scope example

if (true) {
  var a = 1;
  let b = 2;
}

console.log(a); // 1
console.log(b); // ReferenceError

Hoisting example

console.log(x); // undefined
var x = 5;

console.log(y); // ReferenceError
let y = 5;

Loop example

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

Best practice

  • Prefer const by default
  • Use let when reassignment is needed
  • Avoid var in new code

FAQ

Is let always better than var in JavaScript?

In modern JavaScript, yes for most cases. let is safer because it uses block scope and avoids many bugs caused by var.

Why does var print undefined before declaration?

Because var is hoisted and initialized with undefined before the code runs.

Why does let throw a ReferenceError before declaration?

Because let exists in the temporal dead zone until the declaration line is reached.

Should I still learn var?

Yes. You will still see it in older codebases, interview questions, and legacy tutorials.

What should I use in modern JavaScript: var, let, or const?

Use const by default, let when reassignment is needed, and avoid var unless you are working with older code.

Does let replace var completely?

For most modern code, yes. But understanding var is still important for reading existing JavaScript.

Can let be used inside loops safely?

Yes. In fact, it is usually better in loops because each iteration gets its own variable binding.

Take Quiz

Mini Project

Description

Build a small loop-based logging script that shows the practical difference between var and let in asynchronous code. This project demonstrates one of the most common real-world bugs caused by var: every callback using the same final loop value.

Goal

Create a script that schedules delayed log messages and observe how var and let produce different results inside a loop.

Requirements

  • Create one loop using var and one loop using let
  • Use setTimeout inside both loops
  • Log the loop variable from each callback
  • Show that var logs the final value repeatedly
  • Show that let logs each iteration value correctly
Take Quiz

Keep learning

Related questions

View more