An Intro to Javascript Proxy Objects
Metaprogramming is a powerful technique that enables you to write programs that can create other programs.ES6 made it easier to utilize metaprogramming in JavaScript with the help of proxies and many similar features.
What is the javascript Proxy?
A JavaScript Proxy is an object that wraps another object (target) and intercepts the fundamental operations of the target object.
Proxies are Middleware for Javascript Objects.
The fundamental operations can be property lookup, assignment, enumeration, and function invocations, etc.
const obj = {
val: 10
};
console.log(obj.val);
Here, the console.log()
statement performs a get
operation on the object obj
to get the value of the key val
.
Creating a Proxy Object:
To effectively implement and use an ES6 proxy, you must understand three key terms:
target
: The original object which you want to proxyhandler
: An object that defines which operations will be intercepted and how to redefine intercepted operations.trap
: Similar to traps in operating systems, traps in this context are methods that provide access to the object in a certain way
const proxiedObject = new Proxy(target, handler);
Calling the Proxy constructor, new Proxy()
, will return an object that has the values contained in target
but whose basic operations like get
and set
, now have some custom logic specified by the handler
object.
Let’s take an example to understand this,
const handler = {
get: function() {
console.log('A value has been accessed');
return obj[prop]; // Return the value stored in the key being accessed
}
}const initialObj = {
id: 1,
name: 'Foo Bar'
}const proxiedObj = new Proxy(initialObj, handler);console.log(proxiedObj.name);
Now, if we had not made a Proxy Object, calling console.log(proxiedObj.name)
would’ve logged “Foo Bar” to the console.
But now that we’ve made a Proxy, the get
operation has some custom logic that we have defined.
Calling console.log(proxiedObj.name)
will now actually log “A value has been accessed” to the console and the actual value!
A value has been accessed (index):48
Foo Bar (index):60
This custom override that we provided for get
is called a trap
(loosely based on the concept of operating system traps). A handler object is basically an object with a set of traps that would trigger whenever an object property is being accessed.
Proxy Traps
Handler functions are sometimes called traps, presumably because they trap calls to the target object.
The get()
trap
The get()
trap is fired when a property of the target
object is accessed via the proxy object.
In the previous example, a message is printed out when a property of the initialObj
object is accessed by the proxiedObj
object.
Generally, you can develop a custom logic in the get()
trap when a property is accessed.
The set()
trap
The set()
trap controls behavior when a property of the target
object is set.
Suppose that the age
of user must be greater than 18. To enforce this constraint, you develop a set()
trap as follows:
const user = {
firstName: 'John',
lastName: 'Doe',
age: 20
}const handler = {
set(target, property, value) {
if (property === 'age') {
if (typeof value !== 'number') {
throw new Error('Age must be a number.');
}
if (value < 18) {
throw new Error('The user must be 18 or older.')
}
}
target[property] = value;
}
};const proxyUser = new Proxy(user, handler);
First, set the age
of the user to a string:
proxyUser.age = 'foo';
Output:
Error: Age must be a number.
Second, set the age of the user to 16:
proxyUser.age = '16';
Output:
The user must be 18 or older.
Third, set the age of the user to 21:
proxyUser.age = 21;
No error occurred.
In the above examples, we’ve seen the get
and the set
traps.
There are actually a lot more traps that can be set.
If you’ve ever used Proxies before, either in a project or at work, I would love to hear about it! 😄
Thanks for reading and happy coding..!