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);
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
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);
}
});
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)
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)