skip to content

The Reflect API

Reflect exposes each internal object operation as a plain function that mirrors a Proxy trap one for one, which is why well-written traps forward to it. The usual question is simply why Reflect exists when we already have Object.* methods and operators.

part ofJavaScriptoverview, primer and where to startread it →
on this pageshow

questions

5

JavaScript's Reflect namespace duplicates work that Object.* methods and operators like `in`, `delete` and `new` already do. What is Reflect for, and how does it actually differ from those older APIs?

level: middleimportance: must knowfreq 58%

answer

  1. one function per internal operation
  2. same names as the trap list
  3. booleans where the twins throw
  4. primitives rejected, not coerced
  5. operators become passable values

basics

~20 s

Reflect is a plain namespace object holding one function per internal object operation, matching the Proxy trap list one for one. Unlike the Object.* helpers and operators it mirrors, its methods report failure by returning false and never coerce primitive arguments.

solid answer

~40 s

`Reflect` is a single built-in object — not a constructor, not callable — that exposes each of the language's internal object operations as an ordinary function: `Reflect.get`, `set`, `has`, `deleteProperty`, `ownKeys`, `defineProperty`, `getPrototypeOf`, `setPrototypeOf`, `apply`, `construct`, and a few more. Its method list matches the `Proxy` handler trap list name for name and argument for argument, which is why a well-written trap forwards to the same-named `Reflect` call to get default behaviour. The differences from the older APIs are real, not cosmetic: `Reflect.defineProperty` returns `false` where `Object.defineProperty` throws; `Reflect.getPrototypeOf('abc')` throws a `TypeError` where `Object.getPrototypeOf('abc')` quietly coerces the string to a wrapper; and `Reflect.has`/`Reflect.construct` are functions, so they compose where `in` and `new` — being operators — cannot. `Reflect.get` and `Reflect.set` also take a `receiver` argument that no `Object.*` method has.

code

javascript · 17 lines
javascript
const obj = { a: 1 };
Object.freeze(obj);

// Failure reported as a value, not an exception
console.log(Reflect.defineProperty(obj, 'b', { value: 2 })); // false

// Primitives rejected instead of coerced
console.log(Object.getPrototypeOf('abc') === String.prototype); // true
try {
  Reflect.getPrototypeOf('abc');
} catch (e) {
  console.log(e.constructor.name); // "TypeError"
}

// Operators become ordinary, passable functions
const keys = ['a', 'b'];
console.log(keys.filter(k => Reflect.has(obj, k))); // ['a']

go deeper

for a junior

Be able to say that Reflect is a built-in namespace object of static functions — like Math or JSON — and that you never write new Reflect(). Naming two or three of its methods is enough at this level.

for a middle

Explain the one-method-per-internal-operation design and the exact mirror of the Proxy trap list, then give the concrete behavioural differences: boolean returns instead of throws, and a TypeError instead of primitive coercion.

for a senior

Show why the design choice matters in code you ship: forwarding traps get spec-correct defaults for free, and boolean returns let you branch on a failed define or set without wrapping everything in try/catch. Say plainly when you would not reach for Reflect.

for a principal

Own the API-surface argument: Reflect deliberately stops at the object protocol while Object keeps the ergonomic composites, and that split is what keeps the meta layer stable. Be ready to judge whether a team's reflective abstraction is worth its debuggability cost at all.

## What Reflect actually is `Reflect` is one ordinary built-in object, introduced in ES2015. It is **not** a constructor and **not** a function: both `new Reflect()` and `Reflect()` throw a `TypeError`. It has no prototype-based API to inherit from — it is simply a namespace, in the same spirit as `Math` or `JSON`. It carries a `Symbol.toStringTag` of `"Reflect"`, so `Object.prototype.toString.call(Reflect)` returns `"[object Reflect]"`. ```js typeof Reflect; // "object" Object.prototype.toString.call(Reflect); // "[object Reflect]" ``` ## One function per internal operation Every JavaScript object answers a fixed set of *internal methods* the specification defines: `[[Get]]`, `[[Set]]`, `[[HasProperty]]`, `[[Delete]]`, `[[OwnPropertyKeys]]`, `[[DefineOwnProperty]]`, `[[GetOwnProperty]]`, `[[GetPrototypeOf]]`, `[[SetPrototypeOf]]`, `[[IsExtensible]]`, `[[PreventExtensions]]`, and — for callable objects — `[[Call]]` and `[[Construct]]`. Before ES2015 these were reachable only through a scattered mix of operators (`obj.x`, `x in obj`, `delete obj.x`, `new C()`), `Object.*` helpers, and `Function.prototype` methods. `Reflect` gives each one a plain function with a uniform shape: ```js Reflect.get(obj, 'x'); // obj.x Reflect.set(obj, 'x', 1); // obj.x = 1 Reflect.has(obj, 'x'); // 'x' in obj Reflect.deleteProperty(obj, 'x'); // delete obj.x Reflect.apply(fn, thisArg, args); // fn.apply(thisArg, args) Reflect.construct(C, args); // new C(...args) ``` That is thirteen methods in total. (An extra `Reflect.enumerate` appeared in the ES2015 draft and was removed in ES2016 — do not expect it to exist.) ## Why the shape matters: the Proxy connection A `Proxy` handler's traps are named after the very same internal operations, and each trap receives exactly the arguments the matching `Reflect` method takes. That one-to-one correspondence is deliberate: it makes "do the default thing" a single mechanical call rather than a hand-written reimplementation. ```js const logged = new Proxy(target, { get(t, key, receiver) { console.log('read', key); return Reflect.get(t, key, receiver); // exact default behaviour } }); ``` Without `Reflect`, the trap author would have to re-derive default semantics by hand — walking descriptors, invoking getters with the right `this`, checking the prototype chain — and would get it subtly wrong. ## Three real differences from the older APIs **1. Failure is a return value, not an exception.** `Reflect.defineProperty`, `set`, `deleteProperty`, `preventExtensions` and `setPrototypeOf` all return a boolean. Their `Object.*` twins either throw a `TypeError` on failure or return the object, so you cannot branch on the outcome without a `try`/`catch`. This is not a stylistic preference: traps are *required* to return booleans, so `Reflect` had to speak that language. **2. No primitive coercion.** Since ES2015 many `Object.*` methods quietly coerce a primitive to its wrapper instead of complaining. `Reflect` refuses: ```js Object.getPrototypeOf('abc'); // String.prototype Reflect.getPrototypeOf('abc'); // TypeError: Reflect.getPrototypeOf called on non-object Object.keys('abc'); // ['0', '1', '2'] Reflect.ownKeys('abc'); // TypeError ``` For meta-programming that strictness is a feature — a primitive reaching a reflective call is almost always a bug you want surfaced. **3. Functions instead of operators.** `in`, `delete` and `new` are syntax; you cannot pass them to `map`, store them in a table, or apply them to a dynamic argument list. `Reflect.has`, `Reflect.deleteProperty` and `Reflect.construct` are values. ```js const present = ['a', 'b', 'c'].filter(k => Reflect.has(obj, k)); ``` ## What Reflect is not It is not a replacement for `Object`. There is no `Reflect.keys`, `Reflect.entries`, `Reflect.assign` or `Reflect.freeze` — the everyday, ergonomic, enumerable-string-key helpers stay on `Object`. `Reflect.ownKeys` is the closest thing, and it deliberately returns *all* own keys, strings and symbols, enumerable or not. The division is: `Object.*` is the application-level convenience layer, `Reflect.*` is the meta-programming layer that mirrors the object protocol exactly. Outside of proxies, the honest answer for most application code is that you will reach for `Object.*` and rarely touch `Reflect` at all.

  • If Reflect mostly mirrors things that already existed, what would actually break in a Proxy handler written without it?
    Nothing crashes, but defaults become hand-rolled and drift from the spec. A `get` trap returning `target[key]` invokes getters with the wrong `this`; a `set` trap using `target[key] = value` silently ignores the receiver and returns the wrong boolean; an `ownKeys` trap built from `Object.keys` drops symbols and non-enumerable keys, which then trips the proxy's own invariant checks.
  • Why is there no Reflect.keys or Reflect.assign?
    Because neither corresponds to an internal object operation. `Object.keys` is a filtered, string-only, enumerable-only view built on top of `[[OwnPropertyKeys]]`, and `Object.assign` is a copying loop over several operations. `Reflect` only exposes the primitive protocol, one method per internal method; anything composed or filtered stays on `Object`.
  • Is Reflect useful outside of Proxy code?
    Occasionally. `Reflect.has` and `Reflect.deleteProperty` compose where the operators cannot; `Reflect.ownKeys` is the one call that returns strings and symbols together; `Reflect.defineProperty` lets you branch on failure without a try/catch; and `Reflect.construct` can pass a `newTarget`. But for ordinary application code `Object.*` remains the idiomatic choice.

saying these in an interview costs you the question

  • Reflect is a class you instantiate with new Reflect()
  • Reflect deprecates Object and its static methods
  • Reflect methods throw on failure exactly like the Object twins
  • Reflect only works inside a Proxy handler
  • Reflect.ownKeys is just another name for Object.keys

context

open as a page

Reflect.defineProperty and Reflect.set return booleans where Object.defineProperty and a plain assignment throw. Why does Reflect report failure that way, and what does it mean for calling code?

level: middleimportance: should knowfreq 38%

basics

~20 s

Reflect's mutating methods return true or false because Proxy traps are required to return a boolean success flag, so the mirror API had to speak the same protocol. The cost is that a failure is silent unless the caller checks the returned value.

open as a page

A Proxy get trap can return either `target[key]` or `Reflect.get(target, key, receiver)`. What does that third argument of Reflect.get change, and in which situation do the two forms return different values?

level: seniorimportance: should knowfreq 44%

basics

~20 s

The third argument of Reflect.get is the receiver: the value used as this when the property turns out to be a getter. Omitting it makes getters run against the proxy's target, so a proxy sitting on a prototype chain returns the prototype's data instead of the instance's.

open as a page

What does Reflect.apply(fn, thisArg, argsArray) do in JavaScript, and why would you use it instead of writing fn.apply(thisArg, argsArray)?

level: juniorimportance: nice to knowfreq 28%

basics

~20 s

Reflect.apply calls a function with an explicit this value and an array-like list of arguments, exactly as fn.apply would. It goes straight to the function's internal call operation, so it still works when the function object has its own apply property shadowing the inherited one.

open as a page

What does the third argument of Reflect.construct(target, argumentsList, newTarget) control, and what can it build that `new target(...)` cannot?

level: seniorimportance: nice to knowfreq 24%

basics

~20 s

newTarget decides which constructor's .prototype the newly created object gets, and becomes the value of new.target inside the call. It lets one constructor allocate the object while a different one supplies the prototype, which is how an ordinary function can produce a real exotic Array or Error instance.

open as a page