You will use the JavaScript Reflect API to perform object operations in a predictable, functional way, especially when writing Proxy traps or inspecting properties.
What it is
Reflect is a built-in JavaScript object that provides static methods for fundamental object operations. It is not a constructor and cannot be called with new. Think of it as a set of safe, function-style wrappers around operations such as reading a property, checking whether a key exists, deleting a property, or calling a function.
Related terms include Proxy, trap, receiver, prototype chain, and own property. Many Reflect methods match the default behavior expected by Proxy traps, so they are often used inside handlers to forward work to the target object.
Why it matters
- It gives you a uniform API for object operations instead of mixing operators, methods, and special cases.
- It is essential when writing
Proxytraps because traps must return values that match the default behavior. - It returns useful booleans for operations like
Reflect.setandReflect.deleteProperty, making failures easier to handle. - It can inspect all own keys, including non-enumerable properties and symbols, with
Reflect.ownKeys. - It supports dynamic property access with a
receiver, which matters for getters and Proxy behavior.
Syntax or steps
The smallest useful pattern is to call a static method with the target object and the property or arguments needed:
Reflect.has(obj, "key");
Reflect.get(obj, "key");
Reflect.set(obj, "key", value);
Reflect.deleteProperty(obj, "key");
For Proxy traps, pass the receiver through so getters and setters see the correct object:
Reflect.get(target, prop, receiver);
Reflect.set(target, prop, value, receiver);
Example
This example creates a Proxy that logs property access and uses Reflect to preserve normal behavior.
const target = { name: "Ada", age: 36 };
const handler = {
get(obj, prop, receiver) {
console.log(`get ${String(prop)}`);
return Reflect.get(obj, prop, receiver);
},
set(obj, prop, value, receiver) {
console.log(`set ${String(prop)}`);
return Reflect.set(obj, prop, value, receiver);
}
};
const proxy = new Proxy(target, handler);
proxy.name = "Grace";
console.log(proxy.name);
console.log(Reflect.has(proxy, "age"));
console.log(Reflect.ownKeys(target));
Part by part:
targetis the real object being wrapped.handler.getruns whenever a property is read. It logs the property name, then usesReflect.getto return the actual value.handler.setruns whenever a property is assigned. It logs the property name, then usesReflect.setto perform the assignment and return a boolean.proxy.name = "Grace"triggers thesettrap.Reflect.has(proxy, "age")checks whetherageexists through the proxy, including prototype chain behavior.Reflect.ownKeys(target)returns the target's own keys, including non-enumerable keys if present.
Common mistakes
- Trying to construct
Reflectwithnew Reflect(). It is a namespace object, not a constructor. - Confusing
Reflect.has(obj, "key")withObject.hasOwn(obj, "key").Reflect.haschecks the prototype chain;Object.hasOwnchecks only own properties. - Forgetting the
receiverin Proxy traps. Without it, getters and setters may run against the wrong object. - Assuming
Reflect.setthrows when assignment fails. It returnsfalse, so check the result if you need to detect failure.
When to use it
Use Reflect when you need predictable object operations, especially inside Proxy traps. Use direct operators or Object methods when you do not need trap-compatible behavior.
| Task | Use Reflect when | Use alternative when |
|---|---|---|
| Check a property | You need the same behavior as in or a Proxy has trap. | You only need own properties; use Object.hasOwn. |
| Read a property | You need a receiver or are writing a Proxy get trap. | Simple access is enough; use obj.prop. |
| Write a property | You want a boolean result instead of an exception. | You want normal assignment semantics; use obj.prop = value. |
| List keys | You need all own keys, including symbols and non-enumerable properties. | You only need enumerable string keys; use Object.keys. |
Practice
Guided exercise: create a Proxy around { count: 0 } that logs every read and write, then uses Reflect to return or set the value.
const target = { count: 0 };
const handler = {
get(obj, prop, receiver) {
console.log("read", prop);
return Reflect.get(obj, prop, receiver);
},
set(obj, prop, value, receiver) {
console.log("write", prop, value);
return Reflect.set(obj, prop, value, receiver);
}
};
const proxy = new Proxy(target, handler);
proxy.count = 1;
console.log(proxy.count);
Expected output: write count 1, then read count, then 1.
Challenge: use Reflect.ownKeys to list all own keys of an object that has a symbol property and a non-enumerable property. Hint: define the non-enumerable property with Object.defineProperty first.
Quick check
Question: Why is Reflect.set often preferred inside a Proxy set trap?
Answer: It performs the default assignment and returns a boolean, which matches what the trap should return.
Summary
Reflect turns common object operations into static methods with consistent return values. It is most valuable when working with Proxy traps, dynamic property access, or cases where you need to inspect all own keys.