Proxy and Reflect

Proxy intercepts fundamental object operations through traps; Reflect provides each operation's default implementation for a trap to delegate to.

                new Proxy(target, handler) / Reflect.get/set/has/deleteProperty(...)
              

Purpose

A Proxy wraps a target object and intercepts fundamental operations (property get/set, has/in, deleteProperty, and more) through handler functions called traps. Reflect provides the default implementation of each such operation as a plain callable function.

Syntax

const handler = {
  get(target, prop, receiver) {
    console.log(`Reading ${String(prop)}`);
    return Reflect.get(target, prop, receiver);
  },
  set(target, prop, value, receiver) {
    if (prop === 'age' && value < 0) throw new TypeError('age must be non-negative');
    return Reflect.set(target, prop, value, receiver);
  },
};

const user = new Proxy({ name: 'Ada', age: 30 }, handler);

Behavior notes

  • A trap that isn’t defined in the handler falls back to the target’s default behavior automatically — a Proxy only needs to define the traps it actually wants to customize.
  • Certain invariants are enforced regardless of trap logic — a get trap cannot report a different value than a non-configurable, non-writable property’s actual value; violating an invariant throws a TypeError automatically.
  • Reflect’s methods mirror the low-level operations Proxy traps intercept, one-to-one — using Reflect.set inside a set trap, rather than target[prop] = value, correctly passes through the receiver argument, which matters for inherited proxies.
  • Every trapped operation adds overhead compared to plain object access, and makes the object’s behavior opaque to simple inspection (console.log, a debugger) — a real cost to weigh against the interception’s benefit.

Common mistakes

  • Reaching for a Proxy for simple validation where a factory function with explicit getter/setter methods would be easier to debug and reason about statically.
  • Forgetting to pass receiver through in a set/get trap when delegating to Reflect, breaking correct behavior for a proxy used as another object’s prototype.

Runtime notes

Core ECMAScript (ES2015), identical behavior across every current browser and Node version, though performance characteristics can differ between engines.

Taught in these lessons