JavaScript Revocable Proxies with Proxy.revocable

This article explains revocable proxies in JavaScript, how they differ from standard proxies, and how to terminate access to a target object using the built-in Proxy.revocable() factory method. You will learn the syntax, the mechanics of proxy revocation, and the primary use cases for implementing temporary, secure object access.

What is a Revocable Proxy?

In standard JavaScript, a proxy created with new Proxy(target, handler) remains active for its entire lifecycle. As long as a reference to the proxy exists, consumers can interact with the underlying target object through the defined trap handlers.

A revocable proxy is a specialized proxy instance that can be explicitly turned off at runtime. Once revoked, the proxy loses its connection to the target object, and any subsequent attempts to perform operations on the proxy will immediately throw a TypeError.

Creating a Revocable Proxy

Instead of using the new operator with the Proxy constructor, you create a revocable proxy using the static method Proxy.revocable(target, handler).

Syntax

const { proxy, revoke } = Proxy.revocable(target, handler);

Parameters

Return Value

Proxy.revocable() returns a plain JavaScript object with two properties:

  1. proxy: The newly created Proxy object.
  2. revoke: A function with no arguments that, when called, permanently disables the proxy.

How Termination Works

When you invoke the revoke() function, the runtime permanently severs the internal link between the proxy and the target object.

Key behaviors upon revocation include: * Immediate Deactivation: The proxy stops delegating operations to the target or executing handler traps. * Exceptions on Access: Any operation performed on the revoked proxy—such as reading properties, writing properties, checking in, deleting properties, or calling it as a function—throws a TypeError: Cannot perform '[operation]' on a proxy that has been revoked. * Garbage Collection: Revoking the proxy clears its internal reference to the target object, allowing the target to be garbage collected if no other references to it exist. * Idempotency: Calling revoke() multiple times has no additional effect; subsequent calls are simply ignored.

Practical Example

The following example demonstrates creating a revocable proxy, reading data from it, and terminating it:

const sensitiveData = {
  apiKey: "secret_12345",
  environment: "production"
};

// Create the revocable proxy
const { proxy, revoke } = Proxy.revocable(sensitiveData, {
  get(target, prop) {
    console.log(`Accessing property: ${prop}`);
    return target[prop];
  }
});

// Normal usage while active
console.log(proxy.apiKey);
// Output:
// Accessing property: apiKey
// secret_12345

// Terminate access
revoke();

// Attempting to access after revocation
try {
  console.log(proxy.apiKey);
} catch (error) {
  console.error(error.message);
  // Output: Cannot perform 'get' on a proxy that has been revoked
}

Common Use Cases

1. Temporary Access Control and Sandboxing

When passing sensitive objects or APIs to untrusted third-party libraries, plugins, or worker threads, you can wrap the capability inside a revocable proxy. Once the operation or lifecycle hook finishes, invoking revoke() ensures the third-party code cannot retain long-term access.

2. Preventing Memory Leaks

Holding a reference to a standard proxy prevents its target from being garbage collected. In long-running applications, revoking unused proxies drops the target reference inside the proxy, helping the JavaScript engine reclaim memory.

3. Session and State Expiration

Revocable proxies are useful for enforcing state expiration, such as invalidating an authentication token object or disabling user session utilities once a user logs out.