Using Proxy and Reflect in JavaScript

In modern JavaScript, Proxy and Reflect work together to give you fine‑grained control over how objects behave. These tools allow you to intercept operations, add validation, log changes, and even create reactive systems. This guide explains how both features work and provides clear, practical examples.
1. What Is a Proxy?
A Proxy wraps an object and intercepts operations performed on it. You decide how the object reacts when something is read, written, deleted, or called.
Basic syntax
const proxy = new Proxy(target, handler);- target – the original object
- handler – defines which operations (“traps”) you want to intercept
Example: Logging property access
const target = { message: 'Hello, World!' };
const handler = {
get(target, prop) {
console.log(`Getting ${prop}`);
return target[prop];
},
set(target, prop, value) {
console.log(`Setting ${prop} to ${value}`);
target[prop] = value;
return true;
}
};
const proxy = new Proxy(target, handler);
console.log(proxy.message); // Getting message
proxy.message = 'Hello, Proxy!'; // Setting message to Hello, Proxy!Common Proxy traps
-
get— intercepts property access -
set— intercepts assignments -
has— catches theinoperator -
deleteProperty— interceptsdelete obj.prop -
apply— intercepts function calls -
construct— interceptsnew
2. What Is Reflect?
Reflect provides built‑in, standardized methods for object operations—similar to what Proxy intercepts. It’s often used inside Proxy traps to perform the original behavior.
Example: Using Reflect inside a Proxy
const handler = {
get(target, prop) {
console.log(`Reading ${prop}`);
return Reflect.get(target, prop);
},
set(target, prop, value) {
console.log(`Writing ${prop} = ${value}`);
return Reflect.set(target, prop, value);
}
};Common Reflect methods
-
Reflect.get(target, prop) -
Reflect.set(target, prop, value) -
Reflect.has(target, prop) -
Reflect.deleteProperty(target, prop) -
Reflect.apply(func, thisArg, args) -
Reflect.construct(target, args)
Using Reflect ensures your Proxy behaves predictably, especially in complex scenarios.
3. Real‑World Example: Object Validation
You can create objects that validate data automatically.
const validator = {
set(target, prop, value) {
if (typeof value !== 'string') {
throw new Error(`Property ${prop} must be a string`);
}
return Reflect.set(target, prop, value);
}
};
const user = new Proxy({}, validator);
user.name = 'Margaret'; // OK
user.age = 45; // Error: Property age must be a string4. Another Example: Read‑Only Objects
const readonly = {
set() {
console.log('Cannot modify a read‑only object');
return false;
}
};
const settings = new Proxy({ theme: 'dark' }, readonly);
settings.theme = 'light'; // Cannot modify a read‑only object5. Using Proxies for Reactive Programming (like Vue.js)
const watchers = [];
function reactive(obj) {
return new Proxy(obj, {
set(target, prop, value) {
const updated = Reflect.set(target, prop, value);
watchers.forEach(fn => fn());
return updated;
}
});
}
const state = reactive({ count: 0 });
watchers.push(() => console.log(`Count changed to: ${state.count}`));
state.count++; // Count changed to: 1This is a simplified version of how reactive frameworks track changes.
Conclusion
Proxy gives you the power to intercept and customize object behavior, while Reflect provides safe, predictable defaults. Together, they enable powerful patterns such as logging, validation, immutability, and even reactivity. By mastering these tools, you gain deeper control over your JavaScript applications and unlock new ways to architect clean, maintainable code.