Revoke the proxy
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的性能考虑