阿里云主机折上折
  • 微信号
Current Site:Index > Revoke the proxy

Revoke the proxy

Author:Chuan Chen 阅读数:32968人阅读 分类: JavaScript

Basic Concepts of Revocable Proxies

The Proxy object introduced in ECMAScript 6 allows the creation of a proxy object to intercept and customize fundamental operations on a target object. A revocable proxy is a special form of Proxy that enables the revocation of proxy control over the target object when needed. Revocable proxies are created using the Proxy.revocable() method, which returns an object containing both the proxy object and a revoke function.

const target = {};
const handler = {
  get(target, prop) {
    return `Intercepted: ${target[prop]}`;
  }
};

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

console.log(proxy.foo); // "Intercepted: undefined"
revoke();
console.log(proxy.foo); // TypeError: Cannot perform 'get' on a proxy that has been revoked

Creation and Usage of Revocable Proxies

To create a revocable proxy, call the Proxy.revocable() static method, which takes two parameters: the target object and the handler object. This method returns an object with two properties: proxy (the proxy object) and revoke (the revoke function).

const { proxy, revoke } = Proxy.revocable(
  { name: 'Alice' },
  {
    get(target, prop) {
      return `Proxy got ${prop}: ${target[prop]}`;
    }
  }
);

console.log(proxy.name); // "Proxy got name: Alice"
revoke();
try {
  console.log(proxy.name);
} catch (e) {
  console.error(e.message); // "Cannot perform 'get' on a proxy that has been revoked"
}

Practical Use Cases for Revocable Proxies

Revocable proxies are particularly useful in scenarios where temporary access needs to be granted. For example, in an API client, a revocable proxy can be used to limit access to sensitive data within a specific time window.

function createTemporaryAccess(data, duration) {
  const { proxy, revoke } = Proxy.revocable(data, {
    get(target, prop) {
      if (prop === 'secretKey') {
        return 'ACCESS DENIED';
      }
      return target[prop];
    }
  });

  setTimeout(revoke, duration);
  return proxy;
}

const sensitiveData = {
  username: 'admin',
  secretKey: '123-456-789'
};

const tempAccess = createTemporaryAccess(sensitiveData, 5000);

console.log(tempAccess.username); // "admin"
console.log(tempAccess.secretKey); // "ACCESS DENIED"

setTimeout(() => {
  try {
    console.log(tempAccess.username);
  } catch (e) {
    console.error('Access expired:', e.message);
  }
}, 6000);

Revocable Proxies and Memory Management

Revocable proxies can aid in memory management, especially when explicit resource release is required. Once a proxy is revoked, the JavaScript engine can more efficiently reclaim related resources.

let largeData = new Array(1000000).fill({ data: 'large object' });

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

// Access large data via the proxy
console.log(proxy.length); // 1000000

// Revoke the proxy and release memory when no longer needed
revoke();
largeData = null; // Explicitly dereference

// Now both the proxy and large data can be garbage-collected

Error Handling with Revocable Proxies

Attempting to use a revoked proxy will throw a TypeError. In practical applications, these errors should be handled appropriately.

const { proxy, revoke } = Proxy.revocable({ value: 42 }, {});

revoke();

try {
  proxy.value = 100;
} catch (e) {
  if (e instanceof TypeError) {
    console.log('Proxy has been revoked');
    // Fallback logic can be executed here
  } else {
    throw e;
  }
}

Advanced Patterns with Revocable Proxies

Revocable proxies can be combined with other ES6 features to create more powerful patterns. For example, combining them with WeakMap enables private member patterns.

const privateData = new WeakMap();

function createObject() {
  const data = { privateInfo: 'secret' };
  const { proxy, revoke } = Proxy.revocable({}, {
    get(target, prop) {
      if (prop === 'close') return revoke;
      return data[prop];
    }
  });

  privateData.set(proxy, { data, revoke });
  return proxy;
}

const obj = createObject();
console.log(obj.privateInfo); // "secret"
obj.close(); // Call the revoke function
console.log(obj.privateInfo); // TypeError

Performance Considerations for Revocable Proxies

While revocable proxies offer flexibility, they are slower than direct object access. They should be used cautiously in performance-critical code paths.

// Performance test: direct object access vs. proxy access vs. revoked proxy access
const obj = { count: 0 };
const { proxy, revoke } = Proxy.revocable(obj, {});

// Test direct object access
console.time('plain object');
for (let i = 0; i < 1000000; i++) {
  obj.count++;
}
console.timeEnd('plain object');

// Test proxy access
console.time('proxy');
for (let i = 0; i < 1000000; i++) {
  proxy.count++;
}
console.timeEnd('proxy');

// Test revoked proxy access
revoke();
console.time('revoked proxy');
try {
  for (let i = 0; i < 1000000; i++) {
    proxy.count++;
  }
} catch (e) {
  // Ignore errors
}
console.timeEnd('revoked proxy');

Security Applications of Revocable Proxies

Revocable proxies can be used to implement security sandboxes, restricting access permissions for untrusted code.

function createSandbox(untrustedCode) {
  const safeEnv = {
    console,
    Date,
    Math,
    // Other safe global objects
  };

  const { proxy, revoke } = Proxy.revocable(safeEnv, {
    has() { return true; }, // Trick the 'in' operator
    get(target, prop) {
      if (prop in target) {
        return target[prop];
      }
      throw new Error(`Access to ${prop} is denied`);
    }
  });

  try {
    untrustedCode(proxy);
  } finally {
    revoke();
  }
}

createSandbox((sandbox) => {
  console.log(sandbox.Date.now()); // Works normally
  try {
    console.log(sandbox.process); // Throws an error
  } catch (e) {
    console.error(e.message);
  }
});

Revocable Proxies and Asynchronous Programming

Revocable proxies can be combined with Promises and async functions to create cancellable asynchronous operations.

function cancellableFetch(url) {
  const controller = new AbortController();
  const { signal } = controller;
  
  const { proxy, revoke } = Proxy.revocable({
    promise: fetch(url, { signal }),
    cancel: () => controller.abort()
  }, {
    get(target, prop) {
      if (prop === 'then') {
        return target.promise.then.bind(target.promise);
      }
      return target[prop];
    }
  });

  proxy.revoke = revoke;
  return proxy;
}

const request = cancellableFetch('https://api.example.com/data');

request.then(response => response.json())
  .then(data => console.log(data))
  .catch(err => console.error('Error:', err));

// Cancel the request
setTimeout(() => {
  request.cancel();
  request.revoke();
}, 100);

Browser Compatibility of Revocable Proxies

Although revocable proxies are part of the ES6 standard, browser compatibility must still be considered in practice. All modern browsers support revocable proxies, but older browsers may require a polyfill.

// Simple revocable proxy polyfill
if (typeof Proxy === 'undefined' || !Proxy.revocable) {
  Proxy.revocable = function(target, handler) {
    const proxy = new Proxy(target, handler);
    return {
      proxy,
      revoke: function() {
        // Simple implementation that cannot truly revoke but can simulate behavior
        handler.get = () => { throw new TypeError('Proxy revoked'); };
        handler.set = () => { throw new TypeError('Proxy revoked'); };
        // Other traps similarly
      }
    };
  };
}

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

上一篇:构造器拦截

下一篇:Proxy的性能考虑

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.