JavaScript
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax and Types
-
Section 3: Strings and Numbers in Depth
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Objects and Arrays
-
Section 7: Maps, Sets, and Symbols
-
Section 8: Asynchronous JavaScript
-
Section 9: Object-Oriented and Prototypes
-
Section 10: The DOM
-
Section 11: Browser APIs
-
Section 12: Modern JavaScript (ES2015-ES2025)
-
Section 13: Functional Programming Patterns
-
Section 14: Error Handling and Debugging
-
Section 15: Testing
-
Section 16: Accessibility for JavaScript Developers
-
Section 17: Internationalization and Localization
-
Section 18: Performance
-
Section 19: Node.js Fundamentals
-
Section 20: Regular Expressions
-
Section 21: Design Patterns in JavaScript
-
Section 22: Security Basics
-
Section 23: Data Structures and Algorithms in JavaScript
-
Section 24: Practical Projects
-
Section 25: More Advanced Async Patterns
-
Section 26: More Object and Class Practice
-
Section 27: Working with Dates and Internationalization
-
Section 28: Web Components
-
Section 29: More DOM and Browser Practice
-
Section 30: Build Tooling for Vanilla JavaScript
-
Section 31: More Practice Projects
-
Section 32: Interview and Algorithm Practice
-
Section 33: Error Objects (MDN Reference)
-
Section 34: TypedArrays and Binary Data
-
Section 35: Reflection and Metaprogramming (MDN Reference)
-
Section 36: More Global Functions (MDN Reference)
48: Object.freeze and Immutability Patterns
Think of Object.freeze like a legal contract that's been signed, notarized, and filed with the court. Before the signing, you can move clauses around, change the payment terms, or add new requirements. But the moment that notary stamps it? It's locked. If you try to take a pen and cross out a line to change the price, the contract doesn't magically change—it just becomes a defaced document, and the court will ignore your changes.
In JavaScript, we map this directly to how we handle data:
- The Draft: A standard JavaScript object where you can add, delete, or change properties.
- The Notary Stamp: Calling
Object.freeze(yourObject). - The Defaced Document: Trying to assign a new value to a frozen property. In strict mode, JavaScript will throw an error; in non-strict mode, it'll just silently fail.
- The Supplemental Agreement: Instead of editing the original, you create a new object that copies the old one but adds the specific change you need.
Locking the Doors with Object.freeze
You'll most often use this when you have a set of constants or a configuration object that must not change while the app is running. I've seen way too many bugs where a developer accidentally mutated a global config object in one function, which then broke five other unrelated features. It's a nightmare to debug.
"use strict";
const API_CONFIG = Object.freeze({
baseUrl: "https://api.myapp.com/v1",
timeout: 5000,
retries: 3
});
// This will throw a TypeError because we're in strict mode
API_CONFIG.timeout = 10000;
// This will also fail (silently or with an error)
API_CONFIG.newSetting = "enabled";
By freezing the object, you're telling other developers (and your future self), "This data is a source of truth. Do not touch it."
The Shallow Freeze Trap
Here is where a lot of people get tripped up: Object.freeze is shallow. It only locks the immediate properties of the object. If one of those properties is another object or an array, that nested object is still fair game. It's like notarizing a folder; you can't replace the folders inside, but you can still reach into a folder and scribble on the papers inside it.
const APP_STATE = Object.freeze({
version: "1.0.0",
settings: {
theme: "dark",
notifications: true
}
});
APP_STATE.version = "2.0.0"; // Fails (Frozen)
APP_STATE.settings.theme = "light"; // Works! (The nested object wasn't frozen)
If you need a "Deep Freeze," you'd have to write a recursive function that freezes every object it finds inside the root object. In most professional projects, however, we handle this by adopting an immutability pattern instead of trying to freeze everything.
The Immutability Workflow
In modern JavaScript—especially if you ever touch React or Redux—we rarely "freeze" everything. Instead, we treat objects as if they were frozen. When we need to change something, we don't mutate the original; we create a copy with the updated value using the spread operator (...).
I prefer this approach because it gives you a history of your state. If you mutate an object, the previous version is gone forever. If you create a new object, you can keep the old one for undo/redo functionality or debugging.
const user = {
id: 1,
name: "Alex",
email: "alex@example.com"
};
// Instead of: user.email = "new@example.com";
// Do this:
const updatedUser = {
...user,
email: "new@example.com"
};
console.log(user.email); // "alex@example.com" (Original preserved)
console.log(updatedUser.email); // "new@example.com" (New version created)
This pattern—combining Object.freeze for true constants and the spread operator for state updates—is the gold standard for writing predictable, bug-free JavaScript.
📋 Practical Task
Exercise: The Immutable Game Engine Config
You are building a game engine. The core physics rules must be immutable so that the game logic remains consistent, but the player's session settings should be updatable using immutability patterns.
Your Task:
- Create a constant object called
PHYSICS_RULEScontaininggravity: -9.81andfriction: 0.5. UseObject.freezeto ensure these cannot be changed. - Create a
playerSessionobject withusername: "Player1",level: 1, andinventory: ["Sword"]. - Write a function called
updateLevel(session, newLevel). This function must not mutate the original session object. Instead, it should return a new object with the updated level using the spread operator. - Try to change
PHYSICS_RULES.gravityand verify (viaconsole.log) that it did not change. - Call
updateLeveland verify that the originalplayerSessionremains at level 1, while the returned object is at the new level.
There are no comments for now.