The Reflect object
Reflect is a built-in object that holds a set of helper methods for working with objects: accessing properties, deleting them, defining them, calling functions and so on. In plain terms, Reflect is a set of "safe" counterparts to JavaScript operators (in, delete, [], Object.defineProperty() and friends) collected in one place.
Theory
TL;DR
Reflectis a built-in object, not a function and not a constructor:new Reflect()is impossible.- It gives a single interface to low-level operations on objects.
- Its methods return
trueorfalseinstead of throwing when an operation is impossible. - Every
Proxytrap has a matchingReflectmethod with the same name and the same signature. - Inside traps,
Reflectguarantees thatthisis passed correctly through thereceiverargument.
Quick example
const user = { name: 'Tim' };
console.log(Reflect.get(user, 'name')); // 'Tim'
Reflect.set(user, 'age', 25);
console.log(user); // { name: 'Tim', age: 25 }
console.log(Reflect.has(user, 'age')); // true
Reflect.deleteProperty(user, 'age');
console.log(Reflect.has(user, 'age')); // falseThe main goals of Reflect
- A single interface for low-level operations on objects, instead of a scattering of operators and functions (
delete,in,Object.definePropertyand so on). - Working together with
Proxy, so that you can delegate actions back to the original object. - More predictable behaviour: methods return
trueorfalsewithout throwing when the operation is impossible.
Popular Reflect methods
| Method | What it does | Equivalent without Reflect |
|---|---|---|
Reflect.get(obj, prop) | Returns the value of a property | obj[prop] |
Reflect.set(obj, prop, value) | Sets the value of a property | obj[prop] = value |
Reflect.has(obj, prop) | Checks whether a property exists | 'prop' in obj |
Reflect.deleteProperty(obj, prop) | Deletes a property | delete obj[prop] |
Reflect.ownKeys(obj) | Returns all keys, including Symbol ones | Object.getOwnPropertyNames(obj).concat(Object.getOwnPropertySymbols(obj)) |
Reflect.defineProperty(obj, prop, desc) | Defines a property | Object.defineProperty(obj, prop, desc) |
Reflect.getOwnPropertyDescriptor(obj, prop) | Gets a property descriptor | Object.getOwnPropertyDescriptor(obj, prop) |
Reflect.apply(fn, thisArg, argsArray) | Calls a function with a context and arguments | fn.apply(thisArg, argsArray) |
Reflect.construct(fn, argsArray) | Creates an instance, like new fn(...) | new fn(...argsArray) |
Reflect and Proxy together
Reflect is often used inside Proxy traps, to forward the default behaviour back to the target object (target).
const user = { name: 'Tim' };
const proxy = new Proxy(user, {
get(target, prop, receiver) {
console.log(`Reading property: ${prop}`);
// safer than target[prop]:
return Reflect.get(target, prop, receiver);
},
set(target, prop, value, receiver) {
console.log(`Writing property: ${prop} = ${value}`);
return Reflect.set(target, prop, value, receiver);
}
});
proxy.name; // log: Reading property: name
proxy.age = 30; // log: Writing property: age = 30Why do it this way? Because Reflect guarantees that this and the internal context are passed correctly, unlike direct access through target[prop]. On top of that, Reflect.set returns a ready true or false, which is exactly what the set trap is required to return.
Summary
| Item | Description |
|---|---|
Reflect | A built-in object with methods for working with objects |
| Goal | Unify low-level operations (read, write, delete, define and so on) |
| Distinctive trait | Safe counterparts to operators, returning true or false instead of errors |
| Used most often | Inside Proxy traps |
Common mistakes
- Calling
Reflectas a function or a constructor. It is a plain namespace object;new Reflect()throws aTypeError. - Confusing
Reflect.definePropertywithObject.defineProperty. The first returnsfalse, the second throws aTypeError, which is exactly whyReflectis the better fit inside traps. - Forgetting to pass
receivertoReflect.getandReflect.set. Without it, getters and setters inherited from a prototype receive the wrongthis. - Assuming
Reflect.ownKeysreturns only string keys. It returns symbols and non-enumerable properties too, unlikeObject.keys. - Using
target[prop]inside agettrap. It breaks inheritance and makes the proxy opaque for classes with getters. - Thinking
ReflectreplacesObject. SomeObjectmethods (Object.keys,Object.assignand others) have noReflectcounterpart; the two sets serve different purposes.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.