DEV Community

Cover image for Proxy Pattern: Controlling Object Interactions in JavaScript
Md Enayetur Rahman
Md Enayetur Rahman

Posted on

Proxy Pattern: Controlling Object Interactions in JavaScript

A distilled guide based on *“Learning Patterns” by Lydia Hallie & Addy Osmani***

“With a Proxy object, we get more control over the interactions with certain objects.”


1  What is the Proxy Pattern?

A proxy stands in for another object—the target—and intercepts every attempt to read (get), write (set), or otherwise interact with it. Think of it as speaking to a receptionist who decides how (or if) your message reaches the CEO. In JavaScript we create one with:

const proxy = new Proxy(target, handler);
Enter fullscreen mode Exit fullscreen mode

The handler is a plain object whose methods (called traps) control behaviour for each operation on the target. The two most common traps are:

  • get – fires on property access.
  • set – fires on property assignment.

2  A Minimal Example

const person = { name: "John Doe", age: 42 };

const personProxy = new Proxy(person, {
  get(target, prop) {
    return `The value of ${prop} is ${target[prop]}`;
  },
  set(target, prop, value) {
    console.log(`Changed ${prop} from ${target[prop]} to ${value}`);
    target[prop] = value;
    return true;          // indicate success
  }
});

console.log(personProxy.name);   // The value of name is John Doe
personProxy.age = 43;            // logs: Changed age from 42 to 43
Enter fullscreen mode Exit fullscreen mode

The proxy formats reads and logs writes, without touching consumers of person.


3  Adding Validation

const safePerson = new Proxy(person, {
  set(target, prop, value) {
    if (prop === "age" && typeof value !== "number") {
      throw new TypeError("Age must be numeric");
    }
    return Reflect.set(target, prop, value);
  }
});
Enter fullscreen mode Exit fullscreen mode

Now invalid assignments throw errors before corrupting state—a guard rail noted by the authors.


4  Why Use Reflect?

The built‑in Reflect object contains low‑level operations (Reflect.get, Reflect.set, etc.) that mirror proxy traps. Using it keeps intent clear and preserves default behaviour:

get: (t, p, r) => Reflect.get(t, p, r),
set: (t, p, v, r) => Reflect.set(t, p, v, r)
Enter fullscreen mode Exit fullscreen mode

Hallie & Osmani recommend it as the idiomatic way to forward operations.


5  Practical Use‑Cases

Scenario How a Proxy Helps
Data validation Reject incorrect values before they reach state.
Formatting / internationalisation Convert raw data into user‑friendly strings on the fly.
Access control Hide or virtualise properties (e.g., lazy‑load API data).
Logging / debugging Audit every read/write without sprinkling console.log.

Proxies can also simulate private fields or build reactive systems (cf. Vue 3’s reactive).


6  Mind the Trade‑offs

Because every property access funnels through your traps, avoid expensive logic inside them—especially in critical rendering paths. Measure before adopting.


7  When Not to Use a Proxy

  • You only need to wrap a few functions—simpler patterns (decorators, HOCs) will do.
  • Hot code where nanoseconds matter (tight loops, list virtualisation).
  • Environments missing ES2015 Proxy support (older Node / browsers).

8  Conclusion

The Proxy pattern empowers you to augment, validate, and observe any object without polluting its interface. Used judiciously it becomes a Swiss‑army knife for state‑heavy apps, but—like any sharp tool—wield it with care.

Next in the series: Provider Pattern and how it solves prop‑drilling hell.


This article is part of my #LearningPatterns series. Content adapted from “Learning Patterns” (Patterns.dev, CC‑BY‑NC 4.0).

Top comments (0)