Under The Hood
frontend javascript ES2015+ (Proxy/Reflect)

Proxy & Reflect: Trapping [[Get]], [[Set]], and Engine Invariants

Last updated
Prerequisites:
JavaScript object property semantics
Understanding of getters/setters and descriptors
  • proxy
  • reflect
  • meta-programming
  • internal-methods
  • traps
  • invariants

Read at your depth

The practical view

A Proxy wraps an object (the target) and lets you intercept operations via traps: get, set, has, deleteProperty, ownKeys, apply, construct, and more. const p = new Proxy(target, { get(obj, key) { return key in obj ? obj[key] : 'missing'; } });. Reflect provides the default behavior as plain functions: Reflect.get(target, key) does the standard lookup, and calling it inside a trap avoids infinite recursion. Use Proxies for validation, logging, observability (like Vue's reactivity), memoization, and mocking. Note: a proxy does NOT change the target — traps are added behavior around it. Proxies are slower than plain objects, so use them where interception is the feature, not on every hot path.

The same idea in other frameworks

react equivalent
React dev tools and state libs use proxies to instrument component state; production hot paths avoid proxies for speed.

Legacy vs modern

Naive forwarding trap vs correct Reflect-forwarding trap with receiver

Hand-rolling get traps with target[key] breaks getter receiver binding; forwarding through Reflect preserves correct this semantics.

before → after
Manual forwarding without receiver
get(target, key) { return target[key]; } // ❌ OLD: breaks getter `this` — getters see target, not proxy
Reflect.get with receiver forwarding
get(target, key, receiver) { return Reflect.get(target, key, receiver); } // ✅ NEW: getters see the proxy

Interview gotchas

Context

This probes your understanding of proxy invariants — the hard contract that keeps proxies from corrupting the object model and lets engines trust object metadata.

The mechanical answer

A non-configurable, non-writable own data property on the target is fixed: no operation may change or conceal its value without violating the object's invariant. The get trap's result is checked against the target: the engine calls [[Get]] on the target, and if the property is a non-configurable, non-writable data property, the trap must return exactly that value. Violating this throws a TypeError. This invariant protects engine invariants (e.g. shape/Map assumptions) and guarantees that a proxy cannot masquerade as having a different value for a permanently fixed property, which would otherwise break spec-guaranteed behaviors like constant folding.

Trap

The naive answer is 'the proxy can return anything it wants because it is a proxy'. Invariants forbid lying about non-configurable properties, including non-configurable accessors (get must be undefined if no setter), ownKeys must include non-configurable keys, and getPrototypeOf must return the target's actual non-extensible prototype. Another trap: assuming the invariant check is optional — engines enforce it with a TypeError, so violating it is a hard crash, not a silent fallback.

Under The Hood — a multi-depth technical interview hub.

Third-party licenses Press ⌘ K to search.