Skip to content

Reflect

This lesson explains Reflect with clear examples, use cases, and best practices so you can use modern JavaScript confidently in real projects.

ES6+ Reflect Overview

Reflect is a built-in object introduced in ES6 that provides methods for performing interceptable JavaScript operations. Each method corresponds to an internal object operation and mirrors the traps available on a Proxy handler.

Reflect methods return predictable results instead of throwing errors in many cases. This makes them especially useful inside proxy handlers, metaprogramming utilities, and libraries that need to work with objects dynamically.

Feature Description
Introduced In ES6 (ECMAScript 2015)
Type Built-in static object
Purpose Perform object operations in a standard way
Proxy Relation Method names match Proxy trap names
Return Style Often returns booleans instead of throwing
Common Uses Proxy handlers, dynamic property access, forwarding

Basic Reflect Example

const user = {
  name: "Alex",
  role: "admin"
};

console.log(
  Reflect.get(user, "name")
);

Reflect.set(user, "role", "editor");

console.log(
  Reflect.has(user, "email")
);
console.log(user);

Reflect.get() reads a property, Reflect.set() updates one, and Reflect.has() checks whether a property exists on the target object.

How Reflect Works

Reflect exposes standard object operations as named methods. Instead of mixing direct property access with older APIs such as Object.defineProperty(), you can use one consistent interface.

Step Description Example
Choose Operation Pick the Reflect method for the task. Reflect.get()
Pass Target Provide the object being operated on. user
Pass Key or Args Supply property name and value if needed. "name"
Receive Result Get value, boolean, or descriptor back. true
Use in Proxy Forward to default behavior in handlers. Reflect.set(...args)

Reflect Methods Reference

Method Purpose Returns
Reflect.get() Read a property value. Property value
Reflect.set() Assign a property value. Boolean success
Reflect.has() Check if property exists. Boolean
Reflect.deleteProperty() Delete a property. Boolean success
Reflect.defineProperty() Define or update a property descriptor. Boolean success
Reflect.getOwnPropertyDescriptor() Get descriptor for one property. Descriptor or undefined
Reflect.ownKeys() Get all own property keys. Array of keys
Reflect.apply() Call a function with arguments. Function return value
Reflect.construct() Create an instance with new. New object instance

Reflect.get and Reflect.set

const product = {
  title: "Keyboard",
  price: 79
};

const title =
  Reflect.get(product, "title");

const updated =
  Reflect.set(
    product,
    "price",
    69
  );

console.log(title);
console.log(updated);
console.log(product.price);

These methods behave like normal property access and assignment, but they work well when the property name is stored in a variable or when you need a function-style API.

Reflect.has and Reflect.deleteProperty

const settings = {
  theme: "dark",
  debug: true,
  beta: false
};

console.log(
  Reflect.has(settings, "debug")
);

const removed =
  Reflect.deleteProperty(
    settings,
    "beta"
  );

console.log(removed);
console.log(settings);

Reflect.has() is similar to the in operator, while Reflect.deleteProperty() is similar to delete, but both return clear boolean results.

Reflect.defineProperty

const account = {
  balance: 1000
};

const defined =
  Reflect.defineProperty(
    account,
    "balance",
    {
      value: 1000,
      writable: false,
      enumerable: true,
      configurable: false
    }
  );

console.log(defined);
console.log(account.balance);

account.balance = 2000;
console.log(account.balance);

Use Reflect.defineProperty() when you need precise control over property attributes such as writability and enumerability.

Reflect.ownKeys

const id = Symbol("id");

const record = {
  name: "Sam",
  active: true,
  [id]: 42
};

const keys =
  Reflect.ownKeys(record);

console.log(keys);

Reflect.ownKeys() returns both string keys and symbol keys, making it useful for introspection and serialization tools.

Reflect.apply and Reflect.construct

function greet(name, role) {
  return `Hello ${name}, you are a ${role}`;
}

class User {
  constructor(name) {
    this.name = name;
  }
}

const message =
  Reflect.apply(
    greet,
    null,
    ["Alex", "developer"]
  );

const user =
  Reflect.construct(
    User,
    ["Riya"]
  );

console.log(message);
console.log(user);

Reflect.apply() calls a function with a specific this value and argument list. Reflect.construct() creates an object as if new were used.

Reflect with Proxy Handlers

const target = {
  count: 0
};

const handler = {
  get(obj, key, receiver) {
    console.log(`Reading ${String(key)}`);
    return Reflect.get(
      obj,
      key,
      receiver
    );
  },

  set(obj, key, value, receiver) {
    console.log(
      `Setting ${String(key)} to ${value}`
    );
    return Reflect.set(
      obj,
      key,
      value,
      receiver
    );
  }
};

const proxy =
  new Proxy(target, handler);

proxy.count = 5;
console.log(proxy.count);

The most common real-world use of Reflect is inside Proxy handlers. Forwarding to Reflect keeps default object behavior intact while adding custom logging, validation, or access control.

Validation Proxy with Reflect

function createValidatedUser(data) {
  return new Proxy(data, {
    set(target, key, value) {
      if (
        key === "age" &&
        typeof value !== "number"
      ) {
        throw new TypeError(
          "Age must be a number"
        );
      }

      return Reflect.set(
        target,
        key,
        value
      );
    }
  });
}

const user =
  createValidatedUser({
    name: "Alex",
    age: 28
  });

user.age = 30;
console.log(user.age);

Reflect makes it easy to add validation or transformation logic in a proxy and still return the correct boolean result from set traps.

Reflect vs Object Methods

Operation Reflect Object / Operator
Read property Reflect.get(obj, "key") obj.key
Set property Reflect.set(obj, "key", value) obj.key = value
Check property Reflect.has(obj, "key") "key" in obj
Delete property Reflect.deleteProperty(obj, "key") delete obj.key
Define property Returns boolean Object.defineProperty() may throw

Common Reflect Use Cases

  • Forwarding operations inside Proxy handlers.
  • Dynamic property access when keys come from variables.
  • Building object utilities and schema validators.
  • Framework code that inspects or modifies objects safely.
  • Calling functions or constructors programmatically.
  • Collecting own keys including symbol properties.

Reflect Best Practices

  • Use Reflect inside Proxy traps to preserve default behavior.
  • Prefer Reflect when you need boolean success results.
  • Keep proxy handlers small and forward unhandled cases to Reflect.
  • Use descriptive logging before forwarding in debug utilities.
  • Validate input before calling Reflect on unknown objects.
  • Learn Reflect together with Proxy for metaprogramming tasks.
  • Avoid Reflect for simple static property access when direct syntax is clearer.

Common Reflect Mistakes

  • Forgetting to return the result of Reflect.set() in proxy traps.
  • Using Reflect everywhere instead of normal property syntax.
  • Assuming Reflect methods always succeed without checking booleans.
  • Not passing the receiver argument when needed in getters.
  • Confusing Reflect.has() with Object.hasOwn().
  • Trying to use Reflect without understanding Proxy behavior first.
  • Mutating frozen or sealed objects and ignoring failed boolean results.

Reflect vs Proxy

Feature Reflect Proxy
Role Performs default object operations. Intercepts object operations.
Usage Direct method calls. Wrap target with handler traps.
Best For Forwarding and standard behavior. Custom behavior and control.
Together Often used inside Proxy handlers. Provides interception layer.

Key Takeaways

  • Reflect provides a standard API for object operations in ES6+.
  • Its methods mirror Proxy trap names such as get, set, and has.
  • Reflect is especially useful for forwarding behavior in proxy handlers.
  • Many Reflect methods return booleans instead of throwing errors.
  • Use Reflect intentionally for dynamic or metaprogramming tasks.

Pro Tip

In a Proxy set trap, always return Reflect.set(...). Returning false or forgetting the return value can silently break assignment behavior.