Proxy intercepts fundamental operations on objects (property access, assignment, function calls) through handler traps, while Reflect provides the default behavior for each trap — together they enable validation, reactive systems, and transparent wrappers.
new Proxy(target, handler) intercepts operations through traps like get, set, has, deleteProperty, apply, and construct — 13 traps covering all fundamental operations.
Built-in object with static methods mirroring each Proxy trap — provides the correct default behavior inside traps with proper this handling.
Vue 3's reactivity uses Proxy get traps for dependency tracking and set traps for triggering re-renders — replacing Vue 2's Object.defineProperty approach.
Proxy.revocable() returns a proxy with a revoke() function that permanently disables it — useful for temporary access control.
Proxies add overhead to every intercepted operation — suitable for framework-level concerns but not for hot paths or tight loops.
Proxy and Reflect (both ES6) are JavaScript's metaprogramming primitives — they let you intercept and customize fundamental language operations on objects. Proxies power the reactivity systems in frameworks like Vue 3, MobX, and Immer.
new Proxy(target, handler) creates a proxy object that wraps target. The handler is an object with "trap" methods that intercept operations on the proxy. When you interact with the proxy (read a property, assign a value, call it as a function), the corresponding trap is invoked instead of the default behavior. If a trap is not defined, the operation passes through to the target unchanged.
get(target, prop, receiver) — Intercepts property access (proxy.foo). Return any value. Used for default values, lazy loading, property validation.set(target, prop, value, receiver) — Intercepts property assignment (proxy.foo = 'bar'). Must return true to indicate success. Used for validation, logging, reactive updates.has(target, prop) — Intercepts the in operator ('foo' in proxy). Return boolean.deleteProperty(target, prop) — Intercepts delete proxy.foo.apply(target, thisArg, args) — Intercepts function calls when the target is a function.construct(target, args, newTarget) — Intercepts new proxy() when the target is a constructor.ownKeys(target) — Intercepts Object.keys(), for...in, and property enumeration.There are 13 traps total, covering every fundamental operation JavaScript can perform on an object.
Reflect is a built-in object (not a constructor) with static methods that mirror every Proxy trap. Reflect.get(target, prop) does the same thing as target[prop], Reflect.set(target, prop, value) does the same as target[prop] = value, and so on.
Why use Reflect? Inside a trap, you often want to perform the default operation after your custom logic. Reflect.get(target, prop, receiver) correctly handles prototype chain lookups and getter/setter this binding — which a simple target[prop] may not in edge cases. It also returns meaningful boolean results (Reflect.set returns true/false) instead of throwing or returning undefined.
Validation: A set trap can validate values before they're assigned — reject negative numbers for an age property, enforce type constraints, or prevent adding new properties.
Reactive systems: Vue 3's reactivity is built on Proxy. When you access a reactive property, the get trap tracks which component is reading it (dependency tracking). When you modify a property, the set trap triggers re-renders for all components that depend on it.
Logging and debugging: Wrap any object in a Proxy to log every property access and assignment — useful for understanding how third-party code interacts with your objects.
Default values: A get trap can return a default value when a property doesn't exist on the target, eliminating undefined checks.
Access control: Hide sensitive properties by intercepting get, has, and ownKeys to make certain properties invisible.
Proxy.revocable(target, handler) returns { proxy, revoke }. Calling revoke() permanently disables the proxy — any further operation throws TypeError. Useful for granting temporary access to an object.
Proxies add overhead to every intercepted operation. They are not suitable for hot paths or performance-critical code (tight loops, frequent property access). Use them for framework-level concerns (reactivity, validation) rather than in per-frame rendering logic.
Proxy intercepts operations on objects through handler traps. Reflect provides the default behavior for each trap. Together they enable transparent wrappers that can validate, observe, or transform any object interaction. Vue 3 replaced Object.defineProperty (Vue 2) with Proxy for reactivity — Proxy can intercept new property additions and array mutations, which Object.defineProperty cannot.
Fun Fact
Vue 3's migration from Object.defineProperty to Proxy was one of the main reasons for the Vue 2 → Vue 3 rewrite. Proxy can intercept new property additions and array index mutations that Object.defineProperty simply cannot detect — eliminating the need for Vue.set() and array mutation workarounds that confused many Vue 2 developers.