JavaScript Proxy get Trap: Intercept Property Reads

JavaScript Proxies provide a powerful mechanism to intercept and customize fundamental language operations on objects. This article explains how the Proxy handler’s get trap functions to intercept property reading operations, detailing its parameters, return behaviors, and invariants, along with practical code examples demonstrating its use for fallback values and dynamic properties.

What is the get Trap?

In JavaScript, a Proxy wraps a target object and enables the interception of internal operations via a handler object. The get trap intercepts any attempt to read a property from the proxy instance.

Operations that trigger the get trap include: - Dot notation: proxy.property - Bracket notation: proxy['property'] - Object inheritance lookups: Object.create(proxy).property - Methods like Reflect.get(proxy, property)

When any of these operations occur, the JavaScript engine suspends default property resolution and instead executes the get method defined on the proxy’s handler.

Syntax and Parameters

The get trap is defined as a method within the handler object passed to the Proxy constructor:

const handler = {
  get(target, prop, receiver) {
    // Custom logic here
  }
};

const proxy = new Proxy(targetObject, handler);

The method accepts three parameters:

  1. target: The original target object wrapped by the proxy.
  2. prop: The name (a string or Symbol) of the property being accessed.
  3. receiver: The this value used for the property access, which is usually the proxy instance itself or an object that inherits from it.

How Interception Works

When a read operation is attempted, the get trap function executes and determines the final return value.

Basic Interception and Default Fallbacks

const user = {
  name: "Alice",
  age: 30
};

const handler = {
  get(target, prop, receiver) {
    if (prop in target) {
      return target[prop];
    }
    return `Property '${String(prop)}' does not exist.`;
  }
};

const proxyUser = new Proxy(user, handler);

console.log(proxyUser.name); // "Alice"
console.log(proxyUser.email); // "Property 'email' does not exist."

In this example, accessing proxyUser.name returns the value from the target, while accessing an undefined property like proxyUser.email returns a custom fallback message instead of undefined.

Using Reflect.get()

The recommended practice when forwarding operations to the underlying target is using Reflect.get(). This ensures that getters with this references execute within the correct context (the receiver):

const handler = {
  get(target, prop, receiver) {
    console.log(`Accessing property: ${String(prop)}`);
    return Reflect.get(target, prop, receiver);
  }
};

const proxy = new Proxy({ a: 1 }, handler);
console.log(proxy.a); 
// Logs: "Accessing property: a"
// Returns: 1

Trap Invariants

JavaScript enforces specific rules (invariants) to guarantee predictable behavior even when traps are used:

Violating these invariants will cause the JavaScript runtime to throw a TypeError.