Q114: Proxy and Reflect
What Interviewers Want To Evaluate
This question checks whether you understand JavaScript metaprogramming. Interviewers want proxies, traps, Reflect, property interception, validation, observability, reactivity, invariants, and performance trade-offs.
Senior answers should know when proxies are useful and when they make code too magical.
Short Interview Answer
A Proxy wraps a target object and intercepts operations like property get, set, delete, function call, and construction through traps. Reflect provides standard methods that mirror many object operations, making it useful inside proxy traps to preserve default behavior. Proxies are powerful for validation, reactivity, logging, and virtualization, but they can hurt readability, debugging, and performance if overused.
Detailed Interview Answer
A proxy lets you customize fundamental object operations.
const target = {
name: "Asha",
};
const proxy = new Proxy(target, {
get(object, property, receiver) {
console.log(`Reading ${String(property)}`);
return Reflect.get(object, property, receiver);
},
});
console.log(proxy.name);
The get trap runs when a property is read.
Common Traps
Proxy handlers can define traps for many operations.
Common examples:
get
set
has
deleteProperty
ownKeys
getOwnPropertyDescriptor
apply
construct
Each trap intercepts a specific kind of operation.
Reflect
Reflect exposes object operations as functions.
Reflect.get(object, property, receiver);
Reflect.set(object, property, value, receiver);
Reflect.has(object, property);
Reflect.deleteProperty(object, property);
Inside proxy traps, Reflect is the clean way to delegate to default behavior.
Validation Example
const user = new Proxy(
{},
{
set(target, property, value) {
if (property === "age" && value < 0) {
throw new Error("Age cannot be negative");
}
return Reflect.set(target, property, value);
},
},
);
user.age = 30;
The set trap can enforce rules before storing data.
Reactivity Example
Reactive libraries can use proxies to observe reads and writes.
function reactive(target, onChange) {
return new Proxy(target, {
set(object, property, value) {
const result = Reflect.set(object, property, value);
onChange(property, value);
return result;
},
});
}
This is a simplified version of a larger idea used in some state systems.
Virtual Properties
Proxies can expose computed or virtual behavior.
const config = new Proxy(
{ apiUrl: "/api" },
{
get(target, property) {
if (property === "isProduction") {
return process.env.NODE_ENV === "production";
}
return Reflect.get(target, property);
},
},
);
Use this carefully because hidden behavior can surprise maintainers.
Function Proxies
The apply trap intercepts function calls.
function sum(a, b) {
return a + b;
}
const tracedSum = new Proxy(sum, {
apply(target, thisArg, args) {
console.log("calling sum", args);
return Reflect.apply(target, thisArg, args);
},
});
This can support tracing, metrics, or permission checks around function calls.
Constructor Proxies
The construct trap intercepts new.
class Service {}
const TracedService = new Proxy(Service, {
construct(target, args, newTarget) {
console.log("creating service");
return Reflect.construct(target, args, newTarget);
},
});
This is less common in application code but appears in frameworks and libraries.
Invariants
Proxy traps must respect certain language invariants.
For example, a proxy cannot report impossible behavior for non-configurable properties.
If a trap violates invariants, JavaScript throws a TypeError.
This protects core object assumptions.
Performance Considerations
Proxies can be slower than plain property access because the engine cannot optimize them in the same way.
This does not mean proxies are always bad. It means they should be used where the abstraction value is worth the runtime and debugging cost.
Common Mistakes
A common mistake is returning true from set without actually setting the value.
Another mistake is forgetting the receiver argument in Reflect.get and Reflect.set, which can matter for getters, setters, and inheritance.
Senior Trade-Offs
Use proxies for framework-level behavior, debugging wrappers, validation boundaries, and reactive state abstractions.
Avoid them for ordinary business logic where explicit functions are easier to understand.
Code or Design Example
function createRequiredConfig(config) {
return new Proxy(config, {
get(target, property, receiver) {
if (!(property in target)) {
throw new Error(`Missing config: ${String(property)}`);
}
return Reflect.get(target, property, receiver);
},
});
}
This can fail fast when required configuration is missing.
Debugging Workflow
When proxy behavior is confusing:
check which operation triggers which trap
delegate with Reflect when default behavior is needed
check receiver behavior for getters and setters
check non-configurable property invariants
check stack traces for hidden proxy calls
benchmark if used in hot paths
consider replacing with explicit functions
Interview Framing
Define proxy as an interception wrapper. Explain Reflect as the standard way to perform default object operations. Then give a practical validation or reactivity example.
Revision Notes
Proxy intercepts object operations through traps. Reflect mirrors object operations and is commonly used to preserve default behavior inside traps.
Key Takeaways
Proxies are powerful metaprogramming tools, but they should be used with restraint because hidden behavior can make systems harder to debug.
Follow-Up Questions
- What is a proxy?
- What is a proxy trap?
- Why is
Reflectuseful inside traps? - What does the
gettrap intercept? - What does the
settrap return? - What is the
applytrap? - What is the
constructtrap? - What are proxy invariants?
- Why can proxies affect performance?
- When should you avoid proxies?
How I Would Answer This In A Real Interview
I would say a proxy wraps an object and intercepts operations through traps. I would use Reflect inside the trap to preserve normal behavior, then discuss validation, reactivity, invariants, and why proxies should usually live in libraries rather than everyday feature code.