Recently I was trying to figure out how to prevent a deeply nested object from being mutated. That is, I didn't want properties in the object being changed, added, or deleted. Let's use this object as an example:
const object = {
foo: 'foo',
bar: {
baz: 'baz',
qux: {
fred: 'fred',
},
},
};
My first thought was to use Object.freeze
. However, this doesn't work for nested objects. One solution was to write a function that recursively deep freezes an object.
Instead, I decided to give the Proxy
class a try and was able to achieve similar behavior:
const handler = {
set(target, property, value, receiver) {
throw new Error(`Can't assign "${property}" on this Proxy object`);
},
get(target, property, receiver) {
if (typeof target[property] === 'object') {
return new Proxy(target[property], handler);
}
return Reflect.get(...arguments);
},
};
const proxy = new Proxy(object, handler);
With the above, I can access the nested properties of object
as usual:
proxy.foo; // 'foo'
proxy.bar.baz; // 'baz'
proxy.bar.qux.fred; // 'fred'
And if I try to update any of the nested properties, an error is thrown:
proxy.foo = 'updated';
// Error: Can't assign "foo" on this Proxy object
proxy.bar.baz = 'updated';
// Error: Can't assign "baz" on this Proxy object
proxy.bar.qux.fred = 'updated';
// Error: Can't assign "fred" on this Proxy object
Now a little more explantation. In the code above, the get
method of handler
is called when attempting to access immediate properties in object
. If one of those properties is an object, I return a new Proxy
for that nested object with the same behavior. Otherwise, I return the value for that property via Refject.get
. This is like a lazily deep frozen object.
Whenever a property is attempted to be set on the object through the proxy, set
is called, and I just throw an error and the property is never assigned.