The Reflect object
Reflect is a built-in object (like Math or JSON) that holds static methods for working with objects and functions at a low level. Put simply, it is a unified API for the basic operations on objects: reading, writing, calling, constructing and so on, and its methods correspond to the very operations you can intercept with Proxy.
Theory
TL;DR
Reflectis neither a class nor a function: it cannot be called withnew, it only has static methods.- It turns the basic language operations into functions:
Reflect.get,Reflect.set,Reflect.has,Reflect.deleteProperty,Reflect.construct. - Every
Reflectmethod matches oneProxytrap, which is why they are called inside traps. - Unlike
Object, theReflectmethods more often returntrue/falseinstead of throwing an exception. Reflect.ownKeys()returns all own keys, symbols included, unlikeObject.keys().- The third
receiverargument ofget/setpreserves the correctthisfor getters and setters that live on the prototype.
Quick example
const user = { name: 'Tim', age: 25 };
const proxy = new Proxy(user, {
get(target, prop, receiver) {
console.log(`Reading property: ${prop}`);
return Reflect.get(target, prop, receiver); // delegate safely
},
set(target, prop, value, receiver) {
console.log(`Changing property: ${prop}`);
return Reflect.set(target, prop, value, receiver);
}
});
proxy.name; // Reading property: name
proxy.age = 30; // Changing property: ageUsing Reflect inside traps guarantees the behaviour stays correct even when the object has getters, setters or a prototype.
Why it is needed
Before ES6, operations such as delete obj.prop or obj[prop] = value were performed by built-in syntax but had no functional equivalent. Reflect appeared in order to:
- make those operations available as functions;
- simplify writing
Proxytraps, because execution can be delegated throughReflect; - make the API symmetric and safe.
The main methods by example
Reading and writing properties.
const user = { name: 'Tim' };
console.log(Reflect.get(user, 'name')); // "Tim"
Reflect.set(user, 'age', 30);
console.log(user.age); // 30The plain-syntax equivalent:
user.name; // get
user.age = 30; // setBut with Reflect you can use this functionally, passing target, key and receiver by hand.
Deleting a property.
const user = { name: 'Tim' };
delete user.name; // the traditional syntax
// The Reflect equivalent:
Reflect.deleteProperty(user, 'name');The difference: Reflect.deleteProperty returns true or false instead of throwing an exception when the property is configurable: false.
Creating a property with flags. This is the functional equivalent of Object.defineProperty.
const user = {};
Reflect.defineProperty(user, 'role', {
value: 'admin',
writable: false
});
console.log(user.role); // adminChecking for a property and getting the keys.
const user = { name: 'Tim' };
console.log('name' in user); // true
console.log(Reflect.has(user, 'name')); // true
const data = { a: 1, b: 2 };
console.log(Reflect.ownKeys(data)); // ['a', 'b']Unlike Object.keys(), Reflect.ownKeys() returns all own keys, including Symbol ones and non-enumerable ones.
Working with the prototype.
const user = {};
const proto = { greet() { console.log('hi'); } };
Reflect.setPrototypeOf(user, proto);
Reflect.getPrototypeOf(user).greet(); // "hi"Calling a function and constructing instances.
function sum(a, b) {
return a + b;
}
console.log(Reflect.apply(sum, null, [2, 3])); // 5This is the safe equivalent of Function.prototype.apply.call(sum, ...), and it works even when a function has overridden apply.
class User {
constructor(name) {
this.name = name;
}
}
const user = Reflect.construct(User, ['Tim']);
console.log(user.name); // "Tim"The full list of Reflect methods
| Method | What it does |
|---|---|
| Reflect.apply(target, thisArg, args) | Calls a function with the given this and arguments |
| Reflect.construct(target, args) | The equivalent of new target(...args) |
| Reflect.defineProperty(target, key, descriptor) | Creates or changes a property |
| Reflect.deleteProperty(target, key) | Deletes a property (the equivalent of delete) |
| Reflect.get(target, key, receiver?) | Returns the value of a property |
| Reflect.set(target, key, value, receiver?) | Sets the value of a property |
| Reflect.has(target, key) | Checks whether a property exists (in) |
| Reflect.ownKeys(target) | Returns all keys, Symbol ones included |
| Reflect.getOwnPropertyDescriptor(target, key) | Returns a property descriptor |
| Reflect.preventExtensions(target) | Forbids adding new properties |
| Reflect.isExtensible(target) | Checks whether properties can be added |
| Reflect.getPrototypeOf(target) | Gets the prototype |
| Reflect.setPrototypeOf(target, proto) | Sets the prototype |
How Reflect differs from Object
| Aspect | Object | Reflect |
|---|---|---|
| Orientation | Methods for working with the structure of objects | Universal low-level operations |
| Error reporting | Methods may throw exceptions | Methods more often return false |
| Functionality | Operators cannot be called as functions | They can, for example Reflect.deleteProperty instead of delete |
| Relation to Proxy | None | The methods map one to one onto the Proxy traps |
A summary table of the operations:
| What it does | Example |
|---|---|
| Reads a property | Reflect.get(obj, 'x') |
| Writes a property | Reflect.set(obj, 'x', 10) |
| Checks existence | Reflect.has(obj, 'x') |
| Deletes a property | Reflect.deleteProperty(obj, 'x') |
| Gets the keys | Reflect.ownKeys(obj) |
| Calls a function | Reflect.apply(fn, thisArg, args) |
| Constructs an object | Reflect.construct(Class, args) |
| Works with the prototype | Reflect.getPrototypeOf(obj) and Reflect.setPrototypeOf(obj, proto) |
The main idea: Reflect is a mirror of JavaScript's basic operations in function form. It makes code cleaner, safer and more predictable, especially when working with Proxy.
Common mistakes
- Trying to call
new Reflect().Reflectis neither a function nor a constructor: it is a plain object with static methods, likeMath. - Ignoring the result of
Reflect.setorReflect.defineProperty. They returnfalseinstead of throwing, so a silent write failure easily goes unnoticed. - Forgetting to pass
receiverin aget/settrap. Without it a prototype getter receivestargetinstead of the proxy, and reactivity or logging misses part of the accesses. - Confusing
Reflect.ownKeys()withObject.keys(). The former also returns symbols and non-enumerable properties, so the result is wider than expected. - Assuming
Reflect.get(obj, 'x')always equalsobj.x. If you pass a differentreceiveras the third argument, the getter runs with a differentthis. - Using
Reflectwhere plain syntax is enough. OutsideProxytraps and metaprogramming,obj.xreads better thanReflect.get(obj, 'x').
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.