Proxy Pattern
Intercept and control interactions to target objects
Overview
The Proxy Pattern uses a Proxy intercept and control interactions to target objects.
Let's say that we have a person
object. We can access properties with either dot or bracket notation,
and modify property values in a similar fashion.
With the Proxy pattern, we don't want to interact with this object directly. Instead, a Proxy object intercepts the request, and (optionally) forwards this to the target object - the person
object in this case.
Implementation
In JavaScript, we can easily create a new proxy by using the built-in Proxy
object.
The Proxy
object receives two arguments:
- The target object
- A handler object, which we can use to add functionality to the proxy. This object comes with some built-in functions that we can use, such as
get
andset
.
The get
method on the handler object gets invoked when we want to access a property, and the set
method gets invoked when we want to modify a property.
const person = {
name: "John Doe",
age: 42,
email: "john@doe.com",
country: "Canada",
};
const personProxy = new Proxy(person, {
get: (target, prop) => {
console.log(`The value of ${prop} is ${target[prop]}`);
return target[prop];
},
set: (target, prop, value) => {
console.log(`Changed ${prop} from ${target[prop]} to ${value}`);
target[prop] = value;
return true;
},
});
Reflect
The built-in Reflect
object makes it easier to manipulate the target object.
Instead of accessing properties through obj[prop]
or setting properties through obj[prop] = value
, we can access or modify properties on the target object through Reflect.get()
and Reflect.set()
. The methods receive the same arguments as the methods on the handler object.
Tradeoffs
Control: Proxies make it easy to add functionality when interacting with a certain object, such as validation, logging, formatting, notifications, debugging.
Long handler execution: Executing handlers on every object interaction could lead to performance issues.
Exercise
Challenge
Add the following validation to the user
object:
- The
username
property has to be astring
that only contains of letters, and is at least 3 characters long - The
email
property has to be a valid email address. - The
age
property has to be a number, and has to be at least18
- When a property is retrieved, change the output to
${new Date()} | The value of ${property}} is ${target[property]}
. For example if we getuser.name
, it needs to log2022-05-31T15:29:15.303Z | The value of name is John