The Proxy Pattern in JavaScript
The Proxy pattern is like having a helpful assistant that stands between you and an object. This assistant controls how you can access and interact with that object.
Think of it like a receptionist at a company. When you want to talk to someone, you go through the receptionist first. They might check if the person is available, take a message, or direct you to the right place. That’s exactly what a proxy does with objects!
How Does It Work?
The proxy sits between the client (that’s you or your code) and the main object. When you want to do something with the object, your request goes through the proxy first. The proxy can then decide what to do - maybe it adds some extra functionality, validates your request, or simply passes it along.
The cool thing is that you don’t even know you’re talking to a proxy. It looks and acts just like the original object.
Why Would You Want to Use a Proxy?
Here are some common reasons:
- Add extra functionality - Want to log every time someone accesses a property? A proxy can do that without changing your original object.
- Validate data - Make sure only valid data gets set on your object.
- Control access - Decide who can access what and when.
- Handle HTTP requests - Tools like Nginx use proxies to manage web traffic.
A Simple Example
Let’s say you have a dog object and you want to control how people interact with it:
const snickers = {
name: "Snickers",
age: 2,
breed: "King Charles",
owner: "Marcell Ciszek",
}
const handler = {
get(target, prop) {
console.log(`Someone is asking for ${prop}`)
return Reflect.get(target, prop)
},
set(target, prop, value) {
console.log(`Someone is setting ${prop} to ${value}`)
return Reflect.set(target, prop, value)
},
}
const dogProxy = new Proxy(snickers, handler)
// Now when you use dogProxy, it will log every access
console.log(dogProxy.name) // Logs: "Someone is asking for name" then "Snickers"
dogProxy.age = 3 // Logs: "Someone is setting age to 3"
JavaScript makes this easy with the built-in Proxy
object and Reflect
methods. The Reflect
object provides clean methods for getting and setting properties that work perfectly with proxies.
Creating Read-Only Enums
Here’s a practical example - creating enums that can’t be changed:
const createEnum = (values) => {
return new Proxy(values, {
get(target, key) {
if (!target.hasOwnProperty(key)) {
throw new Error(`"${key}" doesn't exist in this enum`)
}
return target[key]
},
set() {
throw new Error("You can't change enum values!")
},
})
}
const Days = createEnum({
Monday: 1,
Tuesday: 2,
Wednesday: 3,
Thursday: 4,
Friday: 5,
})
console.log(Days.Monday) // Works fine: 1
// Days.Monday = 10; // This would throw an error
// Days.Saturday; // This would also throw an error
This creates a safe enum that prevents accidental changes and catches typos.
The Good and Not-So-Good
The Good:
- You get complete control over how objects are used
- Easy to add logging, validation, or debugging
- Great for security and access control
- Doesn’t change your original object
The Not-So-Good:
- Uses a bit more memory
- Can make your code slightly more complex
- Might slow things down a tiny bit
Wrapping Up
The Proxy pattern is like having a smart assistant for your objects. It lets you add extra functionality, control access, and keep your original objects safe - all without changing how you use them.
It’s particularly useful when you need to:
- Log what’s happening with your objects
- Validate data before it gets set
- Create immutable objects or enums
- Add security layers
The JavaScript Proxy API makes this pattern easy to implement and very powerful to use.