Skip to main content

Ways to declare a variable: var, let, const

JavaScript has three ways to declare a variable: var, let and const. They differ in scope, in whether they can be redeclared, and in how they behave under hoisting. Let us go through them in detail.

Theory

TL;DR

  • var, function scoped, allows redeclaration, hoists with the value undefined.
  • let, block scoped, the value can be reassigned, redeclaring it in the same block is not allowed.
  • const, block scoped, cannot be reassigned, but the contents of an object or array can still change.
  • let and const are hoisted too, yet before their declaration line they sit in the temporal dead zone and throw a ReferenceError.
  • The modern standard: const by default, let when needed, var not at all.

Quick example

javascript
function demo() { if (true) { var fromVar = 'visible in the whole function'; let fromLet = 'visible only inside this block'; } console.log(fromVar); // 'visible in the whole function' console.log(fromLet); // ReferenceError: fromLet is not defined }

1. var, the old way (ES5 and earlier)

javascript
var name = 'Tim';

Details:

  • It can be redeclared and reassigned:

    javascript
    var x = 10; var x = 20; // does not raise an error
  • Function scope (it ignores {} blocks):

    javascript
    if (true) { var a = 5; } console.log(a); // 5, the variable "escaped" the block
  • It is hoisted, so the variable is reachable before its declaration, but its value is undefined:

    javascript
    console.log(user); // undefined var user = 'Alex';

Using var in modern code is not recommended.

2. let, the modern way for values that change

javascript
let age = 25; age = 26; // reassignment is allowed

Details:

  • The value can be changed, but the name cannot be redeclared in the same block:

    javascript
    let a = 1; // let a = 2; SyntaxError
  • Block scope:

    javascript
    if (true) { let x = 10; } console.log(x); // ReferenceError, the variable is not visible outside the block
  • It is unreachable before its declaration, unlike var:

    javascript
    console.log(a); // ReferenceError let a = 5;

Use let when the value of the variable is going to change.

3. const, a constant (an immutable binding)

javascript
const PI = 3.14;

Details:

  • It cannot be reassigned or redeclared:

    javascript
    const x = 10; x = 20; // TypeError
  • But if it holds an object or an array, the data inside can still be changed:

    javascript
    const user = { name: 'Tim' }; user.name = 'Alex'; // allowed // user = {} not allowed, the binding itself cannot be reassigned
  • Block scope, same as let.

Use const by default, and let only if the variable really has to change. When you need genuine immutability of an object, apply Object.freeze().

Hoisting and the temporal dead zone

All three declarations are hoisted; the difference is what happens before the declaration line. var is initialised with undefined while the scope is being created, so reading it does not fail and quietly returns undefined, which is a source of hard to catch bugs. let and const reserve the name as well but leave it uninitialised: the gap between the start of the block and the declaration line is called the temporal dead zone (TDZ), and any access inside it throws a ReferenceError.

javascript
console.log(withVar); // undefined, quiet and dangerous var withVar = 1; console.log(withLet); // ReferenceError: Cannot access 'withLet' before initialization let withLet = 1;

A second practical difference shows up in loops: let creates a fresh binding on every iteration, while var has a single one for the whole function.

javascript
for (var i = 0; i < 3; i++) setTimeout(() => console.log(i)); // 3 3 3 for (let j = 0; j < 3; j++) setTimeout(() => console.log(j)); // 0 1 2

Quick comparison

Featurevarletconst
ScopeFunctionBlockBlock
Can be redeclaredyesnono
Can be reassignedyesyesno
HoistingYes, with the value undefinedYes, but unusable before the declarationYes, but unusable before the declaration
Modern standardObsoleteYesYes

Common mistakes

  • Believing that const makes an object immutable. Only the binding is frozen: user.name = 'Alex' works, while user = {} throws a TypeError.
  • Declaring a const without a value. const x; is a SyntaxError, the value is required right away.
  • Relying on var hoisting and reading the variable before its declaration, getting undefined instead of an error.
  • Using var in a loop with asynchronous callbacks and then wondering why every callback sees the final counter value.
  • Picking let where the value never changes. It hides intent: const tells the reader immediately that no reassignment is coming.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.