Skip to main content

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

  • Reflect is neither a class nor a function: it cannot be called with new, it only has static methods.
  • It turns the basic language operations into functions: Reflect.get, Reflect.set, Reflect.has, Reflect.deleteProperty, Reflect.construct.
  • Every Reflect method matches one Proxy trap, which is why they are called inside traps.
  • Unlike Object, the Reflect methods more often return true/false instead of throwing an exception.
  • Reflect.ownKeys() returns all own keys, symbols included, unlike Object.keys().
  • The third receiver argument of get/set preserves the correct this for getters and setters that live on the prototype.

Quick example

javascript
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: age

Using 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:

  1. make those operations available as functions;
  2. simplify writing Proxy traps, because execution can be delegated through Reflect;
  3. make the API symmetric and safe.

The main methods by example

Reading and writing properties.

javascript
const user = { name: 'Tim' }; console.log(Reflect.get(user, 'name')); // "Tim" Reflect.set(user, 'age', 30); console.log(user.age); // 30

The plain-syntax equivalent:

javascript
user.name; // get user.age = 30; // set

But with Reflect you can use this functionally, passing target, key and receiver by hand.

Deleting a property.

javascript
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.

javascript
const user = {}; Reflect.defineProperty(user, 'role', { value: 'admin', writable: false }); console.log(user.role); // admin

Checking for a property and getting the keys.

javascript
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.

javascript
const user = {}; const proto = { greet() { console.log('hi'); } }; Reflect.setPrototypeOf(user, proto); Reflect.getPrototypeOf(user).greet(); // "hi"

Calling a function and constructing instances.

javascript
function sum(a, b) { return a + b; } console.log(Reflect.apply(sum, null, [2, 3])); // 5

This is the safe equivalent of Function.prototype.apply.call(sum, ...), and it works even when a function has overridden apply.

javascript
class User { constructor(name) { this.name = name; } } const user = Reflect.construct(User, ['Tim']); console.log(user.name); // "Tim"

The full list of Reflect methods

MethodWhat 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

AspectObjectReflect
OrientationMethods for working with the structure of objectsUniversal low-level operations
Error reportingMethods may throw exceptionsMethods more often return false
FunctionalityOperators cannot be called as functionsThey can, for example Reflect.deleteProperty instead of delete
Relation to ProxyNoneThe methods map one to one onto the Proxy traps

A summary table of the operations:

What it doesExample
Reads a propertyReflect.get(obj, 'x')
Writes a propertyReflect.set(obj, 'x', 10)
Checks existenceReflect.has(obj, 'x')
Deletes a propertyReflect.deleteProperty(obj, 'x')
Gets the keysReflect.ownKeys(obj)
Calls a functionReflect.apply(fn, thisArg, args)
Constructs an objectReflect.construct(Class, args)
Works with the prototypeReflect.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(). Reflect is neither a function nor a constructor: it is a plain object with static methods, like Math.
  • Ignoring the result of Reflect.set or Reflect.defineProperty. They return false instead of throwing, so a silent write failure easily goes unnoticed.
  • Forgetting to pass receiver in a get/set trap. Without it a prototype getter receives target instead of the proxy, and reactivity or logging misses part of the accesses.
  • Confusing Reflect.ownKeys() with Object.keys(). The former also returns symbols and non-enumerable properties, so the result is wider than expected.
  • Assuming Reflect.get(obj, 'x') always equals obj.x. If you pass a different receiver as the third argument, the getter runs with a different this.
  • Using Reflect where plain syntax is enough. Outside Proxy traps and metaprogramming, obj.x reads better than Reflect.get(obj, 'x').

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.