JavaScript Proxy has Trap and the in Operator

The has trap in a JavaScript Proxy handler provides a way to intercept and customize property existence checks. When code checks whether an object contains a specific property using the in operator or Reflect.has(), the Proxy routes the evaluation through the handler.has() method. This allows developers to hide existing properties, emulate the presence of dynamic keys, or enforce custom validation rules during property lookups.

Syntax and Parameters

The has method takes two arguments:

const handler = {
  has(target, prop) {
    // Custom logic
    return true; // Must return a boolean
  }
};

The method must return a boolean value indicating whether the property exists on the target or virtual object.

Intercepted Operations

The has trap intercepts the following operations:

  1. The in operator: 'foo' in proxy
  2. Reflect API: Reflect.has(proxy, 'foo')
  3. with statements: with (proxy) { (foo); }

Practical Example: Hiding Private Properties

A common use case is preventing callers from detecting “private” properties, such as keys prefixed with an underscore (_).

const targetObject = {
  id: 101,
  _internalToken: "secret_abc123",
  username: "johndoe"
};

const handler = {
  has(target, prop) {
    // Hide properties starting with an underscore
    if (typeof prop === "string" && prop.startsWith("_")) {
      return false;
    }
    // Delegate normal existence check to the target
    return prop in target;
  }
};

const proxy = new Proxy(targetObject, handler);

console.log("username" in proxy);       // true
console.log("_internalToken" in proxy); // false
console.log(Reflect.has(proxy, "id"));  // true

In this example, the proxy returns false for _internalToken, even though the property physically exists on targetObject.

Invariants and Constraints

JavaScript enforces specific invariants that the has trap cannot bypass:

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

Difference from Object.hasOwn and hasOwnProperty

The has trap only intercepts operations checking the entire prototype chain (like in). Methods like Object.hasOwn(proxy, prop) or proxy.hasOwnProperty(prop) do not trigger the has trap; instead, they trigger the getOwnPropertyDescriptor trap.