JavaScript Proxy and Reflect

Learn how Proxy traps and the Reflect API intercept and control object behavior in JavaScript.

TL;DR

  1. 01Wrap target objects with Proxy wrappers to intercept standard operations.
  2. 02Define handler traps like get and set to customize behaviors.
  3. 03Use the Reflect API to forward default actions inside traps.

Tips

  1. 01Invoke matching Reflect methods inside every proxy trap to preserve default language behavior for properties.
  2. 02Create validation layers using a proxy set trap to reject invalid assignments before updating targets.

Warnings

  1. 01Always pass the receiver argument to Reflect.get to keep getters bound to the correct context.
  2. 02Avoid wrapping objects in performance-critical loops because proxy traps introduce function invocation overhead.

Creating a Proxy

    Proxy wrapper

    Wraps target objects using the Proxy constructor with custom handlers.

    const target = { name: "Ada" };
    const proxy = new Proxy(target, {});
    console.log(proxy.name); // "Ada"
    Handler traps

    Specifies trap functions in handlers to intercept object reads.

    const handler = {
      get(target, prop) {
        return target[prop];
      }
    };
    const proxy = new Proxy({ x: 1 }, handler);
    Function proxying

    Wraps executable function objects to intercept call parameters.

    function greet(name) { return `Hi ${name}`; }
    const pr = new Proxy(greet, {
      apply(t, thisArg, args) {
        return t(...args);
      }
    });

Common Traps

    get trap

    Intercepts property access lookups and method executions.

    const p = new Proxy({ a: 1 }, {
      get(target, prop) {
        return prop in target ? target[prop] : "missing";
      }
    });
    set trap

    Intercepts property assignments and returns confirmation flags.

    const p = new Proxy({}, {
      set(target, prop, value) {
        target[prop] = value;
        return true;
      }
    });
    has trap

    Intercepts the boolean property presence verification check.

    const p = new Proxy({ secret: 1 }, {
      has(target, prop) {
        return prop === "secret" ? false : prop in target;
      }
    });

Reflect Default Behavior

    Reflect forwarding

    Invokes default target operations inside custom proxy traps.

    const p = new Proxy({ a: 1 }, {
      get(target, prop, receiver) {
        return Reflect.get(target, prop, receiver);
      }
    });
    Context preservation

    Passes receivers to Reflect to maintain correct property accessor bindings.

    const target = {
      _v: 10,
      get v() { return this._v; }
    };
    Execution return flags

    Exposes execution success values as standard true or false flags.

    const obj = Object.freeze({ a: 1 });
    const ok = Reflect.set(obj, "a", 2); // false

Practical Use Cases

    Input validation

    Enforces variable type constraints prior to committing assignments.

    const validator = {
      set(target, prop, value) {
        if (typeof value !== "number") return false;
        return Reflect.set(target, prop, value);
      }
    };
    Default attributes

    Supplies default values when missing property keys are queried.

    const fallback = {
      get(target, prop) {
        return prop in target ? target[prop] : 0;
      }
    };

Common Pitfalls

    Losing this context

    Avoids method execution failure by forwarding matching receivers.

    // Always pass 'receiver' to Reflect.get()
    Identity checks

    Checks references carefully since proxies do not equal target references.

    const target = {};
    const proxy = new Proxy(target, {});
    console.log(proxy === target); // false

In Practice

FAQ