var and Why It Was Replaced

JavaScript ES2024 · ✓ verified by execution on 2026-08-06

Before let and const were introduced in 2015, JavaScript had only one way to declare variables: the var keyword. While you shouldn’t use it in modern code, you will encounter it frequently in older codebases, tutorials, and scripts. Understanding how var behaves differently from let is essential for debugging legacy JavaScript.

Function Scope vs Block Scope

Unlike let, which respects block constructs like if or for, var completely ignores them. A variable declared with var is scoped to the entire function it resides in (or the global scope if not in a function).

javascript
function testScope() {
  var a = 10;
}
testScope();
try {
  console.log(a);
} catch (e) {
  console.log(e.name);
}
Output
ReferenceError

Because it is function-scoped, var variables are completely inaccessible outside of the function. However, they easily leak out of standard blocks:

javascript
if (true) {
  var b = 20;
}
console.log(b);
Output
20

The Function Boundary Trap

A modern developer coming from languages like Java or C++ expects a variable to only exist inside the specific {} block it was declared in, and would expect a crash if they tried to access it outside. That is true of let, but not of var, which scopes to the nearest function boundary, treating all other curly braces as invisible!

Why was it designed this way? The original JavaScript engine from 1995 was built in a massive rush and was designed to be forgiving for beginners. By hoisting all declarations to the top of their function and ignoring complex nested block scopes, the engine could parse and execute scripts extremely fast in a single pass without managing complicated memory boundaries.

Because var ignores blocks, it leaks out of if statements and for loops. let and const, added in ES6 (2015), are block-scoped and do not. In new code there is no reason to reach for var; the reason to understand it is that you will meet it in code written before 2015, and in answers on the internet that never got updated.

function calculate() { } if (true) { var b = 20; } Invisible to var! console.log(b); Variable LEAKS out!
var ignores blocks and scopes to the function boundary

This leak issue is even more pronounced in loops:

javascript
for (var i = 0; i < 2; i++) {}
console.log(i);
Output
2

Hoisting: Declarations Move to the Top

Variables declared with var are hoisted. This means the JavaScript engine processes their declarations before executing any code, effectively moving them to the top of their scope.

However, only the declaration is hoisted, not the initialization. Before the actual assignment line runs, the variable exists but holds the value undefined. This is our signature interaction—can you predict what happens if we log a hoisted variable?

Predict the output javascript

Read the code. What exactly will it print? Commit to an answer before you look.

console.log(hoistedVar);
var hoistedVar = "I am hoisted";
Output
undefined

Rather than crashing with a ReferenceError like let would, var silently returns undefined.

Redeclaration and Global Properties

Another major flaw of var is that it allows you to redeclare the same variable multiple times in the same scope without throwing an error. This easily leads to accidental overwrites.

javascript
var c = 1;
var c = 2;
console.log(c);
Output
2

If you declare a var at the top level of a script, it also attaches itself as a property of the global object (like window in browsers or globalThis).

javascript
var uninitializedVar;
console.log(uninitializedVar);
Output
undefined

Edge Cases

Check yourself

What is the actual behavior regarding this belief: 'Using `var` inside an `if` block keeps it isolated to that block.'?

Reveal answer

`var` is only scoped to functions. It completely ignores block scope like `if` or `for`, leaking out to the surrounding function or global scope. — `var` is only scoped to functions. It completely ignores block scope like `if` or `for`, leaking out to the surrounding function or global scope.

What is the actual behavior regarding this belief: 'Accessing a `var` before its declaration line causes the program to crash.'?

Reveal answer

Because of hoisting, the declaration is moved to the top of the scope and initialized with `undefined`. Accessing it before assignment just returns `undefined`. — Because of hoisting, the declaration is moved to the top of the scope and initialized with `undefined`. Accessing it before assignment just returns `undefined`.

What is the actual behavior regarding this belief: 'Declaring the same variable twice with `var` in the same scope throws a SyntaxError.'?

Reveal answer

Unlike `let` and `const`, `var` allows redeclaring the same variable multiple times without any errors, which can lead to accidental overwrites. — Unlike `let` and `const`, `var` allows redeclaring the same variable multiple times without any errors, which can lead to accidental overwrites.

What is the actual behavior regarding this belief: '`let` is just a newer syntax for `var` and they behave identically.'?

Reveal answer

`let` introduces block scoping and prevents accessing the variable before declaration (the Temporal Dead Zone) and prevents redeclaration, fixing the major flaws of `var`. — `let` introduces block scoping and prevents accessing the variable before declaration (the Temporal Dead Zone) and prevents redeclaration, fixing the major flaws of `var`.

Challenges

Challenge 1 +10 XP

Fix the loop scope bug. The `for` loop currently leaks the variable `i` to the outside scope. Change the variable declaration so that `i` is scoped strictly to the loop.

javascript
  • Test 1 — expects "Loop: 0\nLoop: 1\nLoop: 2\nReferenceError"
Need a hint? (−25% XP)

Use the modern block-scoped declaration keyword.

Show solution (0 XP)
for (let i = 0; i < 3; i++) {
  console.log("Loop: " + i);
}

// We shouldn't be able to log i here!
try {
  console.log(i);
} catch(e) {
  console.log(e.name);
}

Challenge 2 +15 XP

Bug Hunt: A developer tried to use a temporary greeting inside the if-block, but it accidentally overwrote the outer greeting because `var` ignores block scope! Fix it so the outer greeting is preserved by replacing `var` with `let`.

javascript
  • Test 1 — expects "Hello"
Need a hint? (−25% XP)

Replace `var` with `let` to enable block scoping.

Show solution (0 XP)
let greeting = "Hello";
if (true) {
  let greeting = "Hi";
}
console.log(greeting);