What is Reflect API?
The JavaScript Reflect API provides a set of static methods for interceptable JavaScript operations. It offers a programmatic, consistent, and robust way to interact with objects and their properties, mirroring many of the internal operations performed by the JavaScript engine.
What is the Reflect API?
Introduced in ECMAScript 2015 (ES6) alongside Proxy, the Reflect API is a built-in object that provides static methods for performing common object operations. Unlike Object methods, Reflect methods are designed to be callable as functions and return consistent results, often including boolean success indicators.
Key Characteristics
- It is not a constructor; all its methods are static.
- Its methods largely correspond to the methods of Proxy handler objects, making it a natural companion for proxies.
- Many methods return a boolean indicating success or failure, which is more informative than throwing an error for certain operations (e.g., Reflect.set, Reflect.deleteProperty).
- Methods like Reflect.apply allow explicit control over 'this' context without binding.
- It provides a clear and consistent interface for performing operations that might otherwise involve operators (like 'in' or 'delete') or Object methods (Object.defineProperty).
Core Methods
Reflect.apply(target, thisArgument, argumentsList): Calls a function with a giventhisvalue and arguments provided as an array.Reflect.construct(target, argumentsList[, newTarget]): Thenewoperator as a function. It calls thetargetconstructor with the givenargumentsList.Reflect.get(target, propertyKey[, receiver]): Gets the value of a property. Similar totarget[propertyKey].Reflect.set(target, propertyKey, value[, receiver]): Sets the value of a property. Similar totarget[propertyKey] = value.Reflect.has(target, propertyKey): Checks if an object has a property. Similar topropertyKey in target.Reflect.deleteProperty(target, propertyKey): Deletes a property. Similar todelete target[propertyKey].Reflect.defineProperty(target, propertyKey, attributes): Defines a property. Similar toObject.defineProperty().Reflect.getOwnPropertyDescriptor(target, propertyKey): Returns a property descriptor. Similar toObject.getOwnPropertyDescriptor().Reflect.getPrototypeOf(target): Returns the prototype of an object. Similar toObject.getPrototypeOf().Reflect.setPrototypeOf(target, prototype): Sets the prototype of an object. Similar toObject.setPrototypeOf().Reflect.isExtensible(target): Checks if an object is extensible. Similar toObject.isExtensible().Reflect.preventExtensions(target): Prevents new properties from being added to an object. Similar toObject.preventExtensions().Reflect.ownKeys(target): Returns an array of the target object's own property keys (both string-keyed and symbol-keyed). Similar toObject.getOwnPropertyNames().concat(Object.getOwnPropertySymbols()).
Why use Reflect?
The primary motivation for Reflect is to provide a standardized, programmatic way to interact with objects that maps directly to the internal operations of the JavaScript engine. This offers several advantages:
- Unified Approach: Offers a single module for performing operations that might otherwise be scattered across different global objects (Object, Function, delete operator, in operator).
- Robustness: Many Reflect methods return
trueorfalseindicating success or failure, rather than throwing an error or silently failing, allowing for more predictable error handling. - Proxy Integration: It is the natural complement to the Proxy API. When defining a Proxy handler, Reflect methods are often used to forward operations to the original target object, ensuring default behavior while allowing custom intercept logic.
- Consistent
this: Methods likeReflect.applyallow direct specification of thethiscontext for function calls, avoiding commonbindorcall/applyboilerplate.
Example: Property Access with `Reflect.get` and `Reflect.set`
const obj = {
name: "Alice",
age: 30,
get greeting() {
return `Hello, ${this.name}!`;
}
};
// Getting a property
console.log(Reflect.get(obj, 'name')); // Output: Alice
// Setting a property
Reflect.set(obj, 'age', 31);
console.log(obj.age); // Output: 31
// Accessing a getter
console.log(Reflect.get(obj, 'greeting')); // Output: Hello, Alice!
// Using a receiver with Reflect.get
const anotherObj = { name: "Bob" };
console.log(Reflect.get(obj, 'greeting', anotherObj)); // Output: Hello, Bob!
In this example, Reflect.get and Reflect.set are used to access and modify properties. The third argument (receiver) in Reflect.get is particularly powerful: when a getter is invoked, this inside the getter refers to the receiver object, not the target object (obj). This allows you to 'borrow' methods or getters from one object and apply them with the context of another.
Reflect and Proxy Synergy
The Reflect API is a perfect match for Proxy objects. When you create a Proxy, its handler methods often need to perform the original operation on the target object. Reflect provides a clean and standard way to do this, ensuring that the internal logic of JavaScript operations is correctly maintained.
const user = {
firstName: "John",
lastName: "Doe"
};
const userProxy = new Proxy(user, {
get(target, property, receiver) {
console.log(`Getting property: ${String(property)}`);
// Use Reflect to perform the default get operation
return Reflect.get(target, property, receiver);
},
set(target, property, value, receiver) {
console.log(`Setting property: ${String(property)} to ${value}`);
if (property === 'age' && typeof value !== 'number') {
console.warn('Age must be a number!');
return false; // Indicate failure
}
// Use Reflect to perform the default set operation
return Reflect.set(target, property, value, receiver);
}
});
console.log(userProxy.firstName); // Triggers get trap, logs "Getting property: firstName", Output: John
userProxy.lastName = "Smith"; // Triggers set trap, logs "Setting property: lastName to Smith"
userProxy.age = "thirty"; // Triggers set trap, logs warning, returns false (but silent for direct assignment)
userProxy.age = 30; // Triggers set trap, logs "Setting property: age to 30"
console.log(userProxy.age); // Triggers get trap, logs "Getting property: age", Output: 30
Here, the get and set traps of the userProxy use Reflect.get and Reflect.set to forward the operations to the original user object. This pattern is common as it allows you to add custom logic (like logging or validation) before or after the default behavior, while still ensuring the core object operation happens correctly.