The const Declaration
Introduction
const creates a block-scoped binding that must be initialised and cannot be reassigned. The key interview distinction is that const protects the binding, not the value.
So const arr = []; arr.push(1) works because the array is mutated in place, but arr = [] throws because it tries to reassign the binding.
Why This Matters
Modern JavaScript style is const by default, let when reassignment is required, and almost never var. Understanding binding vs value prevents a common false statement: const does not make objects or arrays immutable.
Theory
Binding vs value
A variable binding is the name-to-value connection. const says that this connection cannot be pointed at a different value after initialisation. It does not recursively freeze the value.
| Operation | With const | Why |
|---|---|---|
| Declare without value | SyntaxError | const must be initialised immediately. |
| Reassign primitive | TypeError at runtime | The binding cannot point elsewhere. |
| Mutate object property | Allowed | The binding still points to the same object. |
| Push into array | Allowed | The array object changes; the binding does not. |
| Redeclare in same scope | SyntaxError | Same as let. |
const and object mutation
Objects, arrays, functions, maps, and sets are reference values. A const binding can still reference a mutable object. To prevent mutation you need techniques such as Object.freeze, immutable update patterns, or persistent data structures. Object.freeze is shallow, so nested objects may still be mutable.
Temporal dead zone
Like let, const is block-scoped and has a TDZ. You cannot read it before initialisation. Unlike let, you cannot declare first and assign later.
Best-practice rule
Use const by default because it communicates intent: this binding should not change. If you later need reassignment, change it to let. This reduces accidental state changes and makes code easier to reason about.
Visual Diagrams
const user ---------------> { name: 'Ada' }
Allowed: user.name = 'Grace'
Forbidden: user = { name: 'Lin' }`const` locks the arrow from the name to the object, not the inside of the object.
Code Examples
Array mutation is not reassignment
The binding still points to the same array after push, so the mutation is allowed.
Shallow freezing
Object.freeze can help, but it is shallow: nested objects need their own freeze if you require deep immutability.
Playground
Press Run to execute the code and see output here.
Output Prediction
Predict the output #1
1const arr = [];2arr.push(1);3console.log(arr.length);4 5try {6 arr = [];7} catch (error) {8 console.log(error.name);9}Coding Exercises
Add an item without mutating the original array
EasyImplement appendItem(items, item) using const-friendly immutable style. It should return a new array with item at the end and leave the original array unchanged.
Interview Questions
1Does `const` make an object immutable?
No. const makes the binding immutable, not the object. You cannot reassign the variable to another object, but you can mutate properties of the referenced object unless the object is frozen or otherwise protected. const user = {}; user.name = 'Ada' is valid; user = {} throws.
Follow-ups
- What does `Object.freeze` do?
- Is `Object.freeze` deep or shallow?
Quiz
1. Which line is valid after `const user = { name: 'Ada' };`?
Summary
- `const` is block-scoped, has a TDZ, and must be initialised immediately.
- `const` prevents reassignment of the binding, not mutation of the referenced value.
- Objects and arrays declared with `const` can still be mutated unless separately frozen or copied immutably.
- Prefer `const` by default; use `let` only when reassignment is intentional.
Cheat Sheet
Must initialise: const x = value.
Cannot: reassign or redeclare in same scope.
Can: mutate referenced objects/arrays (arr.push, obj.x = 1).
Immutability: use immutable updates, Object.freeze (shallow), or libraries — not const alone.
Style: default to const, upgrade to let only when reassignment is needed.