2 min read

Proxy and Reflect

The Proxy object enables you to create a proxy for another object, which can intercept and redefine fundamental operations for that object (like property lookup, assignment, enumeration, function invocation, etc.).

Reflect is a built-in object that provides methods for interceptable JavaScript operations. The methods are the same as those of proxy handlers.

1. The Proxy Object

A Proxy is created with two parameters:

  • target: The original object which you want to proxy.
  • handler: An object that defines which operations will be intercepted and how to redefine intercepted operations (traps).

Basic Syntax

const target = {
  message1: "hello",
  message2: "everyone"
};

const handler = {
  get(target, prop, receiver) {
    if (prop === "message2") {
      return "world";
    }
    return Reflect.get(...arguments);
  }
};

const proxy = new Proxy(target, handler);

console.log(proxy.message1); // "hello"
console.log(proxy.message2); // "world"

2. Traps

"Traps" are methods that provide property access. This is analogous to the concept of traps in operating systems.

Common traps include:

  • get(target, prop, receiver): Intercepts reading properties.
  • set(target, prop, value, receiver): Intercepts writing properties.
  • has(target, prop): Intercepts the in operator.
  • deleteProperty(target, prop): Intercepts the delete operator.
  • apply(target, thisArg, argumentsList): Intercepts function calls.
  • construct(target, args): Intercepts the new operator.

3. The Reflect Object

Reflect is a static object (like Math). It provides methods that correspond to the Proxy traps.

Why use Reflect?

  1. Forwarding default behavior: Inside a Proxy trap, you often want to perform the original operation after doing some custom logic. Reflect makes this easy.
  2. Better return values: Reflect.defineProperty returns a boolean (success/failure) instead of throwing an error like Object.defineProperty.

Example: Validation with Proxy and Reflect

const validator = {
  set(obj, prop, value) {
    if (prop === 'age') {
      if (!Number.isInteger(value)) {
        throw new TypeError('The age is not an integer');
      }
      if (value > 200) {
        throw new RangeError('The age seems invalid');
      }
    }

    // The default behavior to store the value
    return Reflect.set(obj, prop, value);
  }
};

const person = new Proxy({}, validator);

person.age = 100;
console.log(person.age); // 100
// person.age = 'young'; // Throws TypeError
// person.age = 300;     // Throws RangeError

4. Use Cases

  1. Validation: Ensuring data integrity before writing to an object.
  2. Logging/Debugging: Tracking when properties are accessed or changed.
  3. Data Binding: Automatically updating the UI when the model changes (Vue 3 uses Proxies for reactivity).
  4. API Mocking: Intercepting calls to non-existent methods.

programming/javascript/vanilla/javascript programming/javascript/vanilla/es6-features