JavaScript Proxy Objects
When working with objects, we can create a proxy object that intercepts and changes the behavior of an existing object.
We do so using the Proxy native object, introduced in ES2015.
Suppose we have a car
object:
const car = {
color: 'blue'
}
A very simple example we can make is to return a ‘Not found’ string when we try to access a property that does not exist.
You can define a proxy that is called whenever you try to access a property of this object.
You do so by creating another object that has a get()
method, which receives the target object and the property as parameters:
const car = {
color: 'blue'
}
const handler = {
get(target, property) {
return target[property] ?? 'Not found'
}
}
Now we can initialize our proxy object by calling new Proxy()
, passing the original object, and our handler:
const proxyObject = new Proxy(car, handler)
Now try accessing a property contained in the car
object, but referencing it from proxyObject
:
proxyObject.color //'blue'
This is just like calling car.color
.
But when you try to access a property that does not exist on car
, like car.test
, you’d get back undefined
. Using the proxy, you will get back the 'Not found'
string, since that’s what we told it to do.
proxyObject.test //'Not found'
We’re not limited to the get()
method in a proxy handler. That was just the simplest example we could write.
We have other methods we can use:
apply
is called when we useapply()
on the objectconstruct
is called when we access the object constructordeleteProperty
is executed when we try to delete a propertydefineProperty
is called when we define a new property on the objectset
is executed when we try to set a property
and so on. Basically we can create a guarded gate that controls everything that happens on an object, and provide additional rules and controls to implement our own logic.
Other methods (also called traps) we can use are:
enumerate
getOwnPropertyDescriptor
getPrototypeOf
has
isExtensible
ownKeys
preventExtensions
setPrototypeOf
all corresponding to the respective functionality.
You can read more about each of those on MDN.
Let’s make another example using deleteProperty
. We want to prevent deleting properties of an object:
const car = {
color: 'blue'
}
const handler = {
deleteProperty(target, property) {
return false
}
}
const proxyObject = new Proxy(car, handler)
If we call delete proxyObject.color
, we’ll get a TypeError:
TypeError: 'deleteProperty' on proxy: trap returned falsish for property 'color'
Of course one could always delete the property directly on the car
object, but if you write your logic so that that object is inaccessible and you only expose the proxy, that is a way to encapsulate your logic.
I wrote 17 books to help you become a better developer, download them all at $0 cost by joining my newsletter
JOIN MY CODING BOOTCAMP, an amazing cohort course that will be a huge step up in your coding career - covering React, Next.js - next edition February 2025