The const Keyword Doesn't Mean What You Think

Most JavaScript developers learn early on that const declares a constant. The natural assumption is that a constant is a value that can never change. And for primitive values, that holds up:

const hi = 5;

hi = 10;
// 🛑 Uncaught TypeError: Assignment to constant variable.

console.log(hi);
// -> 5

But try the same thing with an object, and something odd happens. You can freely modify properties:

const person = {
  name: 'Hassan',
};

person.name = 'Sujata';
// Seems to work?? 🤔

console.log(person);
// -> { name: 'Sujata' }

How is this possible? The variable was declared with const. The answer lies in the distinction between assignment and mutation—a concept that unlocks a much clearer mental model of how JavaScript actually works.

Labels, Not Boxes

Consider a valid JavaScript program:

5;

Here's another:

['apple', 'banana', 'cherry'];

In both cases, data is created and stored in memory. But this data is useless—there's no way to access it. Variables are the solution. They act as labels that point to the data we create:

// Create it now...
const fruits = ['apple', 'banana', 'cherry'];

// ...and access it later:
console.log(fruits);
// -> ['apple', 'banana', 'cherry']

A common beginner misconception is that the code runs left to right: first you declare a variable, think of it as an empty box, and then you fill the box with data. In reality, the data is created first. The variable label is then attached to that data, pointing to it.

Re-assignment vs. Mutation

When you declare a variable with let, you can change what that label points to. For example, you can re-point the fruits label at an entirely new value:

This is re-assignment. The label is detached from the original data and connected to a different value. The data itself is untouched:

// We start with a labeled array:
let fruits = ['apple', 'banana', 'cherry'];

// ⚠️⚠️⚠️⚠️
// Pick a different option from the list above
// to see how it translates in code!

const takes re-assignment off the table. Once you bind a name with const, you cannot point it at a new value:

Diagram showing that the “fruits” constant is locked to a specific value.

Crucially, this is the only promise const makes. It creates an indestructible link between the name and the data, but it says nothing about modifying the data itself.

Arrays are a perfect example. You can add or remove items from an array declared with const because the label still points to the same array object:

// Point the `fruits` label at this array:
const fruits = ['apple'];

// ⚠️ Click the “Add Item” button to mutate the array!

This operation is mutation. You're editing the contents of the object, not rebinding the variable. The same applies to objects, where you can freely change property values:

const event = {
  title: 'Change me!',
  startsAt: '2023-05-29T16:00:00Z',
  duration: 4,
  confirmed: true,
};

The distinction is fundamental:

  • Re-assignment—pointing a variable name at a new, different thing.
  • Mutation—editing the data inside the thing the variable points to.

With const, you guarantee no re-assignment will occur. You make zero guarantees about mutation. const does not block mutation at all.

Primitives Are Immutable

All the previous examples involve objects and arrays. But what about a primitive value like a number or a string?

let age = 36;

age = 37;

When this code runs, are we reassigning the age label, or are we mutating the number 36 into 37?

The answer: primitive values in JavaScript are immutable. You cannot edit the number 36; it simply doesn't work that way. The only option is re-assignment, pointing the variable at a different number.

To be clear, the runtime doesn't keep an actual list of all possible numbers. The point is that the numbers themselves are fixed, static entities. You can only change which one the label refers to.

This immutability applies to all primitives—strings, booleans, null, and the rest. For those types, mutation is impossible, which makes the assignment/mutation distinction easy to miss. Objects and arrays, being mutable, are where the real difference shows up.