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?
answer
- one function per internal operation
- same names as the trap list
- booleans where the twins throw
- primitives rejected, not coerced
- operators become passable values
basics
~20 sReflect 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 linesconst 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
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.
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.
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.
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