DEV Community

xRdev_38
xRdev_38

Posted on

Immutable JavaScript Objects using the Proxy Object

Immutable JavaScript Objects using the Proxy Object

JavaScript objects are mutable by default.

While this is convenient, it often leads to bugs when unexpected changes are made to data structures that were supposed to remain stable.

👉 What if we could enforce immutability natively, without external libraries?

That’s where the Proxy object comes in.


🔍 Why Immutability Matters

Immutability brings several advantages:

  • Predictability: Your data cannot be accidentally mutated.
  • Debugging made easier: No "hidden" state changes.
  • Functional programming alignment: Works well with React, Redux, and other state-driven approaches.

Traditional immutability requires libraries like Immutable.js or manual Object.freeze.

But Object.freeze is shallow — it only prevents changes at the top level, not nested properties.

This is where Proxy shines ✨.


🛠 Using Proxy for Immutability

The Proxy object allows us to intercept operations performed on an object.

We can "trap" mutations and prevent them.

function immutable(obj) { return new Proxy(obj, { set(target, prop, value) { throw new Error(`❌ Cannot modify property "${prop}". Object is immutable.`); }, deleteProperty(target, prop) { throw new Error(`❌ Cannot delete property "${prop}". Object is immutable.`); }, defineProperty(target, prop, descriptor) { throw new Error(`❌ Cannot redefine property "${prop}". Object is immutable.`); } }); } 
Enter fullscreen mode Exit fullscreen mode

✅ Example in Action

const user = immutable({ name: "Rudy", role: "Frontend Engineer", skills: ["Angular", "TypeScript"] }); console.log(user.name); // Rudy user.name = "John"; // ❌ Error: Cannot modify property "name". Object is immutable. delete user.role; // ❌ Error: Cannot delete property "role". Object is immutable. 
Enter fullscreen mode Exit fullscreen mode

🔁 Deep Immutability

The above works for top-level properties.

But what if we want nested objects to also be immutable?

We can recursively wrap them in a proxy:

function deepImmutable(obj) { if (obj !== null && typeof obj === "object") { return new Proxy(obj, { get(target, prop) { return deepImmutable(target[prop]); }, set() { throw new Error("❌ Cannot modify immutable object."); }, deleteProperty() { throw new Error("❌ Cannot delete property."); } }); } return obj; } 
Enter fullscreen mode Exit fullscreen mode

Usage:

const config = deepImmutable({ app: { theme: "dark", version: "1.0" } }); console.log(config.app.theme); // dark config.app.theme = "light"; // ❌ Error: Cannot modify immutable object. 
Enter fullscreen mode Exit fullscreen mode

🚀 When to Use This

  • State management: Prevent accidental mutations in apps using React/Redux/NgRx.
  • Shared data: When multiple developers handle the same structure, immutability avoids conflicts.
  • APIs: When returning objects, ensuring immutability avoids consumers breaking data contracts.

⚖️ Trade-offs

  • Slight performance overhead due to Proxy wrapping.
  • May be unnecessary for small-scale projects where discipline is enough.
  • Proxies are not supported in older environments (but work in all modern browsers and Node.js).

🎯 Conclusion

Immutability is a powerful concept that reduces bugs and makes reasoning about your code much easier.

With Proxy, JavaScript gives us a native way to enforce immutability without third-party libraries.

Next time you want bulletproof objects, try wrapping them with a Proxy — and make your state safer by design.

Top comments (0)