Proxy & Reflect
HardProxy lets you create a wrapper around an object that intercepts and redefines fundamental operations like property access, assignment, enumeration, and function invocation. Reflect provides the default behavior for each trap. Together they enable validation, observation, data binding (Vue 3 reactivity), negative indexing, and revocable access patterns.
Interactive Visualization
Tagged Templates
A tagged template literal is called
highlight`Hello ${name}!`
Key Points
- Proxy wraps a target object with a handler containing traps for operations
- Common traps: get, set, has, deleteProperty, apply, construct
- Reflect mirrors every Proxy trap and provides the default operation behavior
- Always use Reflect inside traps to forward to the default behavior
- Proxy.revocable() creates proxies that can be permanently disabled
- Vue 3 reactivity is built entirely on Proxy for change detection
Code Examples
Validation Proxy
const validator = { set(target, prop, value) { if (prop === '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'); } return Reflect.set(target, prop, value); }, }; const person = new Proxy({}, validator); person.name = 'Alice'; // works person.age = 30; // works // person.age = -5; // RangeError!
The set trap validates values before they reach the target. Reflect.set forwards valid assignments.
Logging Proxy
function createLogged(target) { return new Proxy(target, { get(target, prop, receiver) { console.log(`GET ${String(prop)}`); return Reflect.get(target, prop, receiver); }, set(target, prop, value, receiver) { console.log(`SET ${String(prop)} = ${JSON.stringify(value)}`); return Reflect.set(target, prop, value, receiver); }, }); } const obj = createLogged({ x: 1 }); obj.x; // logs: GET x obj.z = 3; // logs: SET z = 3
Every fundamental operation is intercepted and logged. Reflect methods forward each operation.
Negative Array Indices
function negativeArray(arr) { return new Proxy(arr, { get(target, prop, receiver) { const index = Number(prop); if (Number.isInteger(index) && index < 0) { prop = String(target.length + index); } return Reflect.get(target, prop, receiver); }, }); } const arr = negativeArray([10, 20, 30, 40, 50]); console.log(arr[-1]); // 50 console.log(arr[-2]); // 40
Python-style negative indexing. The get trap converts negative indices to positive ones.
Revocable Proxy
const { proxy, revoke } = Proxy.revocable( { secret: 'data' }, { get(target, prop, receiver) { return Reflect.get(target, prop, receiver); }, } ); console.log(proxy.secret); // 'data' revoke(); // proxy.secret; // TypeError: Cannot perform 'get' on revoked proxy
Revocable proxies grant temporary access that can be permanently disabled.
Common Mistakes
- Forgetting to return Reflect.set() result (must return true for success)
- Not forwarding the receiver argument to Reflect.get/set
- Creating proxies in hot loops without considering performance overhead
- Assuming Proxy traps fire for internal slots (Map, Set, Date need special handling)
Interview Tips
- Proxy intercepts, Reflect provides the default behavior
- Vue 3 replaced Object.defineProperty with Proxy for reactivity
- Proxy.revocable() for access control and temporary permissions
- Be ready to implement a validation proxy or negative indexing