Last modified: Jul 21, 2026
JS Immutable Variables Guide
JavaScript variables can be tricky. One key concept is immutability. It means a value cannot change after it is created. This guide explains everything you need to know.
We will cover const, let, and var. You will learn how immutability works with primitives and objects. We will also show common mistakes and how to fix them.
What is an Immutable Variable?
An immutable variable is a variable whose value cannot be reassigned. In JavaScript, const creates an immutable binding. This means you cannot assign a new value to it.
However, immutability does not mean the value itself is frozen. With objects, the contents can still change. Only the variable reference is locked.
Using const for Immutability
The const keyword declares a block-scoped constant. It must be initialized at declaration. Once set, you cannot reassign it.
Here is a basic example:
// Declare a constant
const myName = "Alice";
// This will cause an error
// myName = "Bob"; // TypeError: Assignment to constant variable.
console.log(myName); // Output: Alice
Notice the error. Trying to reassign a const variable throws a TypeError. This prevents accidental changes to your variable binding.
Primitive vs. Object Immutability
Primitive values (strings, numbers, booleans) are immutable by nature. You cannot change a string character. You can only replace the whole value.
Objects (arrays, functions, objects) are mutable. Their contents can change even if the variable is const.
Consider this example:
const person = { name: "Charlie" };
// This is allowed: we modify the object property
person.name = "David";
console.log(person.name); // Output: David
// This is NOT allowed: reassigning the variable
// person = { name: "Eve" }; // Error
The variable person still points to the same object. But the object's name property changed. This is a common source of confusion.
Why Use Immutable Variables?
Immutable variables make your code safer. They prevent accidental overwrites. This is especially useful in large applications.
They also make debugging easier. If a variable never changes, you can trust its value. This reduces cognitive load.
Functional programming relies heavily on immutability. It helps avoid side effects and makes code more predictable.
How to Achieve True Immutability
For objects, const is not enough. You need to freeze the object using Object.freeze(). This prevents property changes.
Here is an example:
const frozenPerson = Object.freeze({ name: "Frank" });
// This will silently fail in non-strict mode
frozenPerson.name = "Grace";
console.log(frozenPerson.name); // Output: Frank (unchanged)
// In strict mode, it throws a TypeError
Note that Object.freeze() is shallow. Nested objects are still mutable. For deep immutability, use libraries like Immutable.js.
Common Mistakes with Immutability
Beginners often confuse const with immutability of value. Remember: const only prevents reassignment of the variable itself.
Another mistake is trying to modify a const array directly. While you can push to it, you cannot reassign the array variable.
Here is an example:
const numbers = [1, 2, 3];
// This is allowed: modifying the array content
numbers.push(4);
console.log(numbers); // Output: [1, 2, 3, 4]
// This is NOT allowed: reassigning the variable
// numbers = [5, 6, 7]; // Error
Always think about whether you need to change the variable binding or the value itself.
Best Practices for Immutability
Use const by default. Only use let when you know the variable will be reassigned. Avoid var entirely in modern code.
When working with objects, consider creating new objects instead of mutating existing ones. This is called the immutable update pattern.
Here is an example using the spread operator:
const original = { a: 1, b: 2 };
// Create a new object instead of mutating
const updated = { ...original, b: 3 };
console.log(original); // Output: { a: 1, b: 2 }
console.log(updated); // Output: { a: 1, b: 3 }
This pattern keeps your original data safe. It also makes state changes explicit.
Immutability and Variable Scope
Immutability works well with block scope. Variables declared with const and let are block-scoped. This means they only exist inside the block where they are defined.
For more on scope, see our JavaScript Variable Scope Explained guide. Understanding scope helps you avoid naming conflicts.
When you combine block scope with immutability, you get safer code. Each block has its own immutable variables.
Immutability in Loops
Using const in a for loop can be tricky. The loop variable changes each iteration. So you must use let for the iterator.
However, inside the loop body, you can declare const variables. They are recreated each iteration.
Example:
for (let i = 0; i < 3; i++) {
const message = `Iteration ${i}`;
console.log(message);
}
// Output:
// Iteration 0
// Iteration 1
// Iteration 2
Here, message is immutable inside each loop iteration. This is safe and clear.
Immutability and Functions
When passing variables to functions, immutability protects the caller. If you pass a primitive, it is copied. The function cannot change the original.
But objects are passed by reference. A function can mutate the object. To prevent this, freeze the object or create a copy.
Here is an example of a safe function:
function updateName(person, newName) {
// Create a new object instead of mutating
return { ...person, name: newName };
}
const user = { name: "Helen", age: 30 };
const updatedUser = updateName(user, "Ivan");
console.log(user.name); // Output: Helen (unchanged)
console.log(updatedUser.name); // Output: Ivan
This function does not mutate the input. It returns a new object. This is a pure function.
Comparing let and const
let allows reassignment. const does not. Both are block-scoped. Use let only when you need to change the variable's value.
For example, a counter in a loop needs let. A configuration value should be const.
Here is a comparison:
let count = 0;
count = 1; // Allowed
const maxCount = 10;
// maxCount = 20; // Not allowed
Choosing const by default makes your intentions clear. It tells other developers that this variable will not change.
Immutability and var
The var keyword creates function-scoped variables. It is not block-scoped. It also allows reassignment.
Modern JavaScript avoids var. It can cause bugs due to hoisting and lack of block scope. Use const or let instead.
If you are working with legacy code, refactor var to const or let. This improves immutability and code quality.
Real-World Example: Configuration Object
Imagine you have a configuration object for an app. You do not want it to change accidentally. Use const and Object.freeze().
Here is how:
const config = Object.freeze({
apiUrl: "https://api.example.com",
timeout: 5000,
retries: 3
});
// Trying to change a property will fail in strict mode
// config.timeout = 6000; // Error in strict mode
console.log(config.timeout); // Output: 5000
This ensures your configuration remains constant throughout the app. It prevents bugs from accidental changes.
Immutability and Arrays
Arrays are objects. They are mutable even with const. To make an array immutable, freeze it.
However, frozen arrays cannot be modified. If you need to add or remove items, create a new array.
Use methods like map, filter, and concat to create new arrays without mutation.
Example:
const originalArray = [1, 2, 3];
// Create a new array with an added element
const newArray = [...originalArray, 4];
console.log(originalArray); // Output: [1, 2, 3]
console.log(newArray); // Output: [1, 2, 3, 4]
This pattern is common in React and Redux. It helps manage state without mutation.
Immutability and Performance
Some developers worry that immutability hurts performance. Creating new objects takes memory. But modern JavaScript engines optimize this well.
The benefits of immutability often outweigh the cost. You get fewer bugs, easier debugging, and better maintainability.
For performance-critical code, use libraries like Immutable.js. They use structural sharing to minimize memory usage.
Immutability in TypeScript
TypeScript adds type safety. You can use readonly modifiers to enforce immutability at compile time.
For example:
interface Config {
readonly apiUrl: string;
readonly timeout: number;
}
const config: Config = { apiUrl: "https://api.example.com", timeout: 5000 };
// config.timeout = 6000; // Error: Cannot assign to 'timeout' because it is a read-only property.
TypeScript catches immutability violations before runtime. This is a powerful tool for large codebases.
Conclusion
Immutable variables are a cornerstone of clean JavaScript code. Use const by default. Freeze objects when needed. Create new values instead of mutating existing ones.
Remember that const only prevents reassignment. For true immutability of objects, use Object.freeze() or libraries.
Start applying these principles today. Your future self will thank you for fewer bugs and clearer code.
For more on variable naming, check our JavaScript Variable Naming Rules guide. And for examples of variable usage, see JavaScript Variables Examples.