Posts

JavaScript var, let, and const Explained

Most JavaScript code starts with a simple act: you give a value a name. We call that a variable declaration. That sounds small, but the way you create that name affects how the rest of your code can use it. A variable …

April 25, 2026 8 min read 1502 words

In this article

Most JavaScript code starts with a simple act: you give a value a name. We call that a variable declaration.

That sounds small, but the way you create that name affects how the rest of your code can use it. A variable might be available only inside a small “if” block, across an entire function, or in places you did not really mean to share it.

That is why var, let, and const are worth understanding before the code gets complicated.

They all create variables, but they answer two different questions:

  1. Can this name point at something else later?
  2. Where can this name be used?

A Useful Mental Model

When you create a variable like this:

let name = "Ada";

You are saying:

Create a label called name, point it at the value "Ada", and let that label point at something else later.

Think of a variable like a label stuck on a box.

The diagram below is the whole trick in miniature: the label can move, or the contents can change, and JavaScript treats those as different moves.

A diagram showing a variable name as a label pointing at a value box, with let reassignment moving the label and const object mutation changing what is inside the same box.

name
  |
  v
+----------------+
| "Ada"          |
+----------------+

The value is the thing inside the box.

The variable name is the label.

With that in mind:

  • let lets the label move to a different box later.
  • const keeps the label attached to the same box.
  • var puts the box in the nearest function room, even if it was created inside a smaller block.

That is the simple version.

The technical version is:

  • let and const are block scoped.
  • var is function scoped.
  • let and var can be reassigned.
  • const cannot be reassigned.

NOTE: var is not always global, but it is often more visible than it looks because it ignores block scope. That is the part that gets people.

Moving The Label vs Changing The Box

Before we talk about the keywords, we need one more piece of the metaphor.

There are two different things that can happen:

  1. The label can move to a different box.
  2. The stuff inside the box can change.

Those are not the same operation.

With simple values like strings and numbers, you mostly experience change as moving the label.

let name = "Ada";

name = "Grace";

In box terms:

before:

name -> +-------+
        | "Ada" |
        +-------+

after:

name -> +---------+
        | "Grace" |
        +---------+

The name label now points at a different value.

Objects are different because an object can hold values inside it.

const user = {
  name: "Ada",
};

user.name = "Grace";

The user label did not move.

The box stayed the same, but one of the values inside the box changed.

same user box:

user
  |
  v
+-----------------------+
| name: "Ada"           |
+-----------------------+

after changing inside the box:

user
  |
  v
+-----------------------+
| name: "Grace"         |
+-----------------------+

That distinction is why const user = { name: "Ada" } can still allow user.name = "Grace".

const keeps the label attached to the same box. It does not automatically freeze everything inside the box.

If you need the object itself to be frozen, that is a different tool: Object.freeze().

That is a separate topic, because freezing the binding and freezing the object are two different operations.

let Means The Label Can Move

let creates a variable that can be reassigned.

let name = "Ada";

name = "Grace";

console.log(name);
// Grace

The name label moved from one value to another value.

That is useful for values that naturally change while the code runs.

let count = 0;

count += 1;
count += 1;

console.log(count);
// 2

count is supposed to change, so let lets you make changes.

Use let when reassignment is part of the plan.

const Means The Label Cannot Move

const creates a variable that cannot be reassigned.

const name = "Ada";

name = "Grace";
// TypeError: Assignment to constant variable.

The label name is attached to "Ada" and cannot be moved to "Grace".

When the value is simple, like a string or a number, const feels like the whole box is sealed. There is no smaller thing inside "Ada" to change.

Objects are where people usually get surprised.

const user = {
  name: "Ada",
};

user.name = "Grace";

console.log(user.name);
// Grace

That is allowed because the user label still points at the same object.

This is not allowed:

const user = {
  name: "Ada",
};

user = {
  name: "Barbara",
};
// TypeError: Assignment to constant variable.

That tries to pull the user label off the first object and stick it on a different object.

So the better way to say it is:

const seals the label to the box. It does not always seal everything inside the box.

Use const when the variable should not be reassigned.

Where The Box Can Be Seen

Now for the second question.

Where can the variable be used?

This is scope.

Think of code like rooms:

This is where var gets weird in older code. The declaration may appear inside a small block, but the name belongs to the nearest function.

A diagram comparing var function scope with let and const block scope inside an if block.

function room
+--------------------------------+
|                                |
|  block room                    |
|  +--------------------------+  |
|  | smaller room             |  |
|  +--------------------------+  |
|                                |
+--------------------------------+

Some variables belong to the smaller block room.

var variables belong to the whole function room.

That difference is why var behaves differently from let and const.

var Belongs To The Function Room

var is function scoped.

That means var belongs to the nearest function, not the nearest block.

function doThing() {
  if (true) {
    var name = "Rob";
  }

  console.log(name);
}

doThing();
// Rob

The name box was created inside the if block.

But because it used var, JavaScript treats that box like it belongs to the whole doThing() function.

function doThing()
+--------------------------------+
|                                |
|  if block                      |
|  +--------------------------+  |
|  | var name = "Rob"         |  |
|  +--------------------------+  |
|                                |
|  console.log(name)             |
|  can still see name            |
+--------------------------------+

That surprises people because the variable looks like it belongs inside the if block.

It does not.

That is why var can feel like someone put the box in a more public spot than you expected.

Not always globally accessible, but often more accessible than the block makes it look.

NOTE: var is not always global, but it ignores block scope. That is the part that gets people.

let and const Stay Inside The Block Room

let and const are block scoped.

A block is usually the code between { and }.

function doThing() {
  if (true) {
    let name = "Rob";
  }

  console.log(name);
}

doThing();
// ReferenceError: name is not defined

This time, name belongs to the if block.

function doThing()
+--------------------------------+
|                                |
|  if block                      |
|  +--------------------------+  |
|  | let name = "Rob"         |  |
|  +--------------------------+  |
|                                |
|  console.log(name)             |
|  cannot see name               |
+--------------------------------+

The name box exists inside the if block.

Once you step outside that block, the name is no longer accessible.

Same idea with const.

function doThing() {
  if (true) {
    const name = "Rob";
    console.log(name);
  }
}

doThing();
// Rob

The name is available inside the block where it was declared.

Outside that block, it is not accessible.

That smaller boundary is useful. It means fewer parts of your code can accidentally depend on or change the variable.

Smaller scope. Fewer accidental dependencies.

A More Relatable Example

Imagine you are calculating a shopping cart total.

const taxRate = 0.08;
let total = 0;

total += 25;
total += 15;

const finalTotal = total + total * taxRate;

console.log(finalTotal);
// 43.2

Those declarations communicate intent:

  • taxRate is const because this calculation should use one tax rate.
  • total is let because we keep adding item prices to it.
  • finalTotal is const because once we calculate it, we do not reassign it.

In box terms:

taxRate    -> label stays on 0.08
total      -> label points at updated totals while adding items
finalTotal -> label stays on the calculated result

Could you write all of that with let?

Yes.

Should you?

No.

Use const as the preferred default and let when reassignment is part of the plan.

My default is:

  1. Start with const.
  2. Use let when reassignment is part of the plan.
  3. Avoid var in new code unless you have a specific reason.

This is not about being modern for the sake of being modern.

It is about making the code easier to read.

When I see const, I know the variable cannot be reassigned.

When I see let, I know reassignment is allowed and I need to watch where it changes.

When I see var, I know the variable is function scoped and may be visible in more places than the block makes it look.

That is the practical difference.

These topics build on each other:

-Rob