Proxy Objects
Version: ES2015
Level: Advanced
Proxy objects are exotic objects that transparently wrap other objects providing a method for intercepting operations like property access or assignment. In doing this, they can provide functionality that is not possible with normal JavaScript objects.
To create a Proxy object, you would use the Proxy
constructor, passing in the object to be wrapped followed by a handler object which defines all of the operations that are to be intercepted. The definitions in the handler are known as traps.
let gift = { value: 'Holiday 6 Pack' };
let wrapped = new Proxy(gift, { /* add traps here */ });
console.log(wrapped.value); // Holiday 6 Pack
In the example above, wrapped
is the proxy version of the gift
object. Because the handler is empty ({}
), having no traps, it will appear in every way like the original gift object.
In fact there’s no observable way to tell that a proxy object is not the original wrapped object unless you compared them to each other directly. Even checks like instanceof
will continue to work as though acting on the original object because it goes through the proxy rather than acting on it.
let date = new Date();
let wrapped = new Proxy(date, {});
console.log(wrapped instanceof Date); // true
console.log(wrapped instanceof Proxy); // Error
By defining a trap in the handler we can add additional behavior to the wrapped object. A common trap is the get
trap that is used to intercept property access. The can be used to change the result of what happens when you access the value
property in the proxy version of the gift
object.
let gift = { value: 'Holiday 6 Pack' };
let wrapped = new Proxy(gift, {
get (target, property, receiver) {
let value = Reflect.get(target, property, receiver);
if (property === 'value') {
return value.replace('6', '5'); // altered value
}
return value; // original value
}
});
console.log(wrapped.value); // Holiday 5 Pack
Here, the original value is obtained through Reflect.get()
(from the wrapped gift
object) and if the name of that property is 'value'
, the result is altered changing instances of ‘6’ to ‘5’. Other properties behave normally returning the same value as they would if there were no proxy.
While the above behavior can also be achieved with a getter in an accessor property, what accessors can’t do that proxies can is intercept properties that don’t yet exist. With of this, you could create an array-like behavior that automatically updates a length
property when new indexed values are set - something which would not be possible with accessor properties alone.
let fakeArray = new Proxy({ length: 0 }, {
set (target, property, value, receiver) {
let newLength = Number(property) + 1;
let length = Reflect.get(target, 'length', receiver);
if (newLength > length) {
Reflect.set(target, 'length', newLength, receiver);
}
return Reflect.set(target, property, value, receiver);
}
});
console.log(fakeArray.length); // 0
fakeArray[0] = 'a';
console.log(fakeArray.length); // 1
fakeArray[4] = 'e';
console.log(fakeArray.length); // 5
Traps exist for many other operations include property defining (not just assigning), function calling, getting object keys, and many more. See the MDN link below for the additional traps you can define for a proxy.
More info: