Compile Ready
Module 2 · Variables & Data Types

The var Declaration

Beginner8m read5m practice13m total
varHoistingScopeClosures

Introduction

var is JavaScript's original variable declaration. It is function-scoped, can be redeclared, is hoisted and initialised to undefined, and browser-script globals declared with var become properties on the global object.

Modern JavaScript prefers let and const, but interviews still ask var constantly because it exposes core execution-context ideas: hoisting, scope, closures, and the classic loop bug.

Why This Matters

Legacy codebases, old interview snippets, and many tricky output questions still use var. If you can explain why a var loop prints the final value repeatedly, you can also explain lexical environments, closure capture, and why ES6 introduced let.

Theory

What makes var different

var is scoped to the nearest function or to the global script, not to a block. An if, for, or while block does not create a new var scope.

Featurevar behaviourInterview consequence
ScopeFunction/global scopedA var inside if is visible outside the block.
HoistingDeclaration is hoisted and initialised to undefinedReading before the line does not throw; it returns undefined.
RedeclarationAllowed in the same scopeAccidental duplicate names can hide bugs.
ReassignmentAllowedThe binding can point to a new value.
Global objectTop-level browser-script var creates window.name / globalThis.namelet and const do not do this.

Hoisting precisely

During the creation phase of an execution context, JavaScript registers var declarations and gives them the value undefined. The assignment still happens later during execution.

So console.log(x); var x = 10; behaves like var x; console.log(x); x = 10;. This is different from let and const, which are hoisted too but stay in the temporal dead zone until initialisation.

The loop closure bug

A for (var i = 0; ...) loop has one shared i binding for the whole function. If callbacks run after the loop, they all close over the same binding. By then the loop has finished, so every callback sees the final value.

When should you use it?

Almost never in new code. Use const by default and let when reassignment is needed. Learn var to read legacy code and ace interview questions, not because it is a best practice.

Visual Diagrams

var hoisting and assignment
Creation phase
  var score -> undefined

Execution phase
  console.log(score) -> undefined
  score = 42
  console.log(score) -> 42

The declaration is registered before execution; the assignment still runs at its original line.

One shared loop binding
for (var i = 0; i < 3; i++)
        |
        +-- callback A closes over same i
        +-- callback B closes over same i
        +-- callback C closes over same i

After loop: i === 3, so all callbacks read 3

`var` does not create a fresh binding for each loop iteration.

Code Examples

Function scope, not block scope

The var binding leaks out of block statements because only functions create a new var scope.

Loading…

Redeclaration and browser global caveat

At the top level of a classic browser script, var adds a property to the global object. This is not true for ES modules or Node CommonJS wrappers.

Loading…

Playground

Loading editor…
Console

Press Run to execute the code and see output here.

Output Prediction

Predict the output #1

javascript
1for (var i = 0; i < 3; i++) {
2 setTimeout(function () {
3 console.log(i);
4 }, 0);
5}

Predict the output #2

javascript
1console.log(score);
2var score = 42;
3console.log(score);

Coding Exercises

Capture the current loop value with legacy var

Medium

You are maintaining legacy code that must keep var. Implement makeLoggers(n) so it returns an array of functions. Calling the returned functions should log 0, 1, ..., n - 1 instead of logging the final loop value every time.

Interview Questions

1What is hoisting for `var`, and why does reading a `var` before declaration return `undefined`?

During the creation phase of a function or global execution context, var declarations are registered and initialised to undefined. The assignment remains where it is. Therefore a read before the assignment sees the initial value undefined, not a ReferenceError. let and const are also hoisted, but they are not usable before initialisation because of the temporal dead zone.

Asked at:MicrosoftAmazonGoogle

Follow-ups

  • How is this different from `let`?
  • What is the temporal dead zone?
2Why does `for (var i = 0; i < 3; i++) setTimeout(() => console.log(i))` print `3` three times?

var creates one function-scoped i binding shared by every iteration. The timer callbacks close over that binding, not over a snapshot of its value. By the time callbacks run, the loop has completed and i is 3, so each callback logs 3. Use let, an IIFE, or pass the value as an argument to capture each iteration separately.

Asked at:MetaNetflixApple

Quiz

1. Which statement about `var` is true?

Summary

  • `var` is function-scoped, not block-scoped.
  • `var` declarations are hoisted and initialised to `undefined`; assignments are not hoisted.
  • Top-level `var` in classic browser scripts becomes a global-object property, unlike `let` and `const`.
  • The classic loop closure bug happens because all callbacks share one `var` loop binding.

Cheat Sheet

Scope: nearest function or global script.

Hoisting: declaration hoisted + initialised to undefined; assignment stays put.

Allowed: redeclaration and reassignment.

Avoid in new code: prefer const, then let.

Classic bug: for (var i...) callbacks share one i; use let or an IIFE.