Proxy & Reflect
Intercept and customise fundamental object operations — get, set, delete, function calls, and more — with Proxy traps and the Reflect API.
What it is, in plain English: A Proxy wraps an object and lets you intercept (trap) fundamental operations on it: reading a property, writing a property, deleting a property, calling a function, and more. For every trap you define, the corresponding operation is redirected through your handler. The Reflect API provides the default behaviour for each operation — so you can intercept, do something extra, and then delegate the real work to Reflect.
Wrapping objects to intercept their operations
A Proxy sits in front of an object. Every time code tries to read, write, or delete a property on the proxy, your handler function gets called first — giving you a chance to validate, log, transform, or block the operation.
Creating a Proxy
// new Proxy(target, handler)
// target — the real object being wrapped
// handler — an object with "trap" methods
const person = { name: "Alice", age: 30 };
const proxy = new Proxy(person, {
get(target, key) {
// Called every time proxy.someProperty is read
console.log(`→ Reading "${key}"`);
return target[key]; // return the actual value
},
set(target, key, value) {
// Called every time proxy.someProperty = value
console.log(`→ Writing "${key}" = ${value}`);
target[key] = value;
return true; // must return true to indicate success
}
});
proxy.name; // logs: → Reading "name"
proxy.age = 31; // logs: → Writing "age" = 31The most useful trap: validation
function createValidatedUser(data) {
return new Proxy(data, {
set(target, key, value) {
if (key === "age") {
if (typeof value !== "number") throw new TypeError("age must be a number");
if (value < 0 || value > 150) throw new RangeError("age must be 0–150");
}
if (key === "email" && !value.includes("@")) {
throw new Error("Invalid email address");
}
target[key] = value;
return true;
}
});
}
const user = createValidatedUser({ name: "Bob" });
user.name = "Bob"; // fine
user.age = 25; // fine
// user.age = -5; // throws: age must be 0–150
// user.email = "notvalid"; // throws: Invalid email addressReflect — the default implementation
Inside a trap, use Reflect to perform the operation normally.
This is cleaner than manually doing target[key] because Reflect handles
edge cases correctly (inherited properties, getters/setters, etc.).
const handler = {
get(target, key, receiver) {
console.log(`Reading "${key}"`);
return Reflect.get(target, key, receiver); // same as: target[key]
},
set(target, key, value, receiver) {
console.log(`Writing "${key}"`);
return Reflect.set(target, key, value, receiver); // same as: target[key] = value
}
};
const proxy = new Proxy({ x: 1 }, handler);
proxy.x; // "Reading x"
proxy.y = 2; // "Writing y"
Try It Yourself
Official Sources
The Codex links only to official documentation and the ECMAScript specification. No third-party tutorials.