Articles

What Is the Reflect API in JavaScript?

JavaScript's Reflect object mirrors fundamental object operations as functions, designed to pair with Proxy traps and simplify metaprogramming.

Takina Takina · · 4 min read
A code editor showing JavaScript components

Reflect is a built-in JavaScript object that provides methods mirroring the language’s own fundamental operations — getting a property, setting a property, calling a function, constructing an object — as ordinary, invokable functions instead of syntax. Reflect.get(obj, "name") does the same thing as obj.name, just as a function call rather than dot-notation syntax. On its own, that sounds redundant; its real purpose is working alongside Proxy, where it solves a problem Proxy handlers run into constantly.

What Reflect actually contains

Reflect is a collection of static methods, not a constructor — there’s never a new Reflect(). Each method corresponds to one of JavaScript’s internal object operations:

  • Reflect.get(target, key) / Reflect.set(target, key, value) — read or write a property.
  • Reflect.has(target, key) — equivalent to the in operator.
  • Reflect.deleteProperty(target, key) — equivalent to delete target[key].
  • Reflect.ownKeys(target) — returns all own property keys, including symbols, in one call where older code needed to combine two separate Object methods.
  • Reflect.apply(fn, thisArg, args) — calls a function with a given this and argument list, without needing Function.prototype.apply.call() gymnastics.
  • Reflect.construct(target, args) — invokes a constructor with a given argument list, equivalent to new target(...args) but usable when target is a variable in a functional context.
  • Reflect.defineProperty(), Reflect.getPrototypeOf(), Reflect.setPrototypeOf(), and others — function-call equivalents of Object methods and prototype operations.

Every one of these already had some equivalent — an operator, a piece of syntax, or a method living on Object. Reflect’s contribution isn’t new capability; it’s consistency. Every method returns a predictable value — Reflect.set returns a boolean indicating success, rather than throwing or silently failing depending on strict mode — and every method takes the same kind of arguments a Proxy trap receives.

Why Reflect and Proxy are designed together

A Proxy wraps an object and lets code intercept fundamental operations through trap functions — a get trap runs whenever a property is read, a set trap runs whenever one is written. Nearly every trap implementation needs to do some version of the default behavior in addition to its custom logic — log the access, then still actually return the property’s value.

Before Reflect existed, code would fall back to target[key] inside a get trap to get the default behavior. That mostly works, but it quietly breaks in a specific case: when the target object’s property is a getter defined higher up the prototype chain that relies on this. Using target[key] runs that getter with this bound to target, not to the proxy itself, which produces wrong behavior for any code relying on inheritance through a proxied object. Reflect.get(target, key, receiver) accepts a third argument specifically to preserve the correct this binding, using exactly the receiver a trap already has available to pass through.

const handler = {
  get(target, key, receiver) {
    console.log(`reading ${String(key)}`);
    return Reflect.get(target, key, receiver);
  },
};
const proxied = new Proxy(original, handler);

This is the pattern nearly every real Proxy handler follows: intercept, do the custom work, then delegate the default behavior to the matching Reflect method rather than reimplementing it with plain syntax. Reflect methods and Proxy traps were deliberately designed as a matched pair — every trap name corresponds to a Reflect method with the same name and argument shape, and that’s not a coincidence.

A cleaner way to do metaprogramming

Outside of Proxy handlers, Reflect methods are also just a more consistent way to do dynamic, “metaprogramming” style operations — calling a function with a computed argument list, invoking a constructor stored in a variable, or checking property existence — situations involving manipulating objects and functions programmatically rather than writing them out directly in source. This kind of dynamic invocation shows up in dependency injection containers, ORMs building objects from database rows, and decorator implementations that need to call the original method with an adjusted argument list; see TypeScript decorators for a case where wrapping a method’s invocation is exactly the problem being solved.

Reflect also cleans up some genuinely awkward corners of Object methods used dynamically. Reflect.deleteProperty(obj, key) returns a boolean, clearly reporting failure; the delete operator does too, but only reveals a failure clearly in strict mode. Reflect.defineProperty() returns a boolean rather than throwing on failure, unlike Object.defineProperty(), which throws — useful when checking success without wrapping every call in a try/catch. This pairs naturally with property descriptor work like Object.freeze vs. Object.seal, where controlling exactly how property mutation fails, silently versus by exception, matters.

Reflect vs. Object methods

ReflectObject
PurposeMirrors language-level operationsGeneral object utility methods
Return on failureConsistent booleansMix of throwing and silent failure
Designed to pair withProxy trapsGeneral-purpose object code
this handling in get/setExplicit receiver argumentImplicit, tied to how the property is accessed

The takeaway

Reflect doesn’t give JavaScript any capability it didn’t already have — every Reflect method has a longstanding operator or Object equivalent. What it gives you is a consistent, function-call form of those operations that returns predictable values and, critically, accepts an explicit receiver argument that lets Proxy traps correctly preserve this binding through inheritance. Outside of writing a Proxy handler, Reflect gets reached for rarely; inside one, delegating default behavior to the matching Reflect method is the standard, correct pattern.

Takina Takina · · 4 min read

Object.groupBy() in JavaScript, Explained

Object.groupBy() buckets array items by a key you compute, replacing the reduce() boilerplate developers have written for years. How it works.

#JavaScript #Web Development #Frontend