Javascript/ES6代理Proxy用法2
上页展示了ES6代理的两个用法,本页继续展示代理在实战中的应用。
安静记录对象的访问
一些方法可能是资源密集型,运行缓慢或时间比较长,我们需要记录它们的性能,Proxy代理可以在后台安静地记录。
let api = { _apiKey: '123abc456def', getUsers: function() { /* ... */ }, getUser: function(userId) { /* ... */ }, setUser: function(userId, config) { /* ... */ } };
api = new Proxy(api, { get: function(target, key, proxy) { var value = target[key]; return function(...arguments) { logMethodAsync(new Date(), key); return Reflect.apply(value, target, arguments); }; } });
// executes apply trap in the background
api.getUsers();
function logMethodAsync(timestamp, method) { setTimeout(function() { console.log(`${timestamp} - Logging ${method} request asynchronously.`); }, 0) }
上面使用异步方法记录了你的代码可能会堵塞的地方,花不了多少代码就能时刻跟踪某个方法的性能。
警告或防止某些操作
如果你想阻止某人删除属性noDelete,你要让调用者知道oldMethod已经丢弃了,你要阻止任何人改变doNotChange属性:
let dataStore = {
noDelete: 1235,
oldMethod: function() {/*...*/ },
doNotChange: "tried and true"
};
const NODELETE = ['noDelete']; const DEPRECATED = ['oldMethod']; const NOCHANGE = ['doNotChange']; dataStore = new Proxy(dataStore, { set(target, key, value, proxy) { if (NOCHANGE.includes(key)) { throw Error(`Error! ${key} is immutable.`); } return Reflect.set(target, key, value, proxy); },
deleteProperty(target, key) { if (NODELETE.includes(key)) { throw Error(`Error! ${key} cannot be deleted.`); } return Reflect.deleteProperty(target, key); }, get(target, key, proxy) { if (DEPRECATED.includes(key)) { console.warn(`Warning! ${key} is deprecated.`); } var val = target[key]; return typeof val === 'function' ? function(...args) { Reflect.apply(target[key], target, args); } : val; } });
// these will throw errors or log warnings, respectively
dataStore.doNotChange = "foo";
delete dataStore.noDelete;
dataStore.oldMethod();
阻止不必要的重资源操作
假设你有一个服务器端点会返回一个非常大的文件,当之前有一个请求没有完成,或者文件正在下载,Proxy是一个好的缓冲机制,可以阻断用户对这个端点的重复请求。
let obj = {
getGiantFile: function(fileId) {/*...*/ }
};
obj = new Proxy(obj, { get(target, key, proxy) { return function(...args) { const id = args[0]; let isEnroute = checkEnroute(id); let isDownloading = checkStatus(id); let cached = getCached(id); if (isEnroute || isDownloading) { return false; } if (cached) { return cached; } return Reflect.apply(target[key], target, args); } } });
立即撤销访问敏感数据
Proxy任何时候提供取消对目标原始对象的访问,这是在安全和性能保护时有用的,如下案例,使用revocable方法,注意当你使用它时,你不能在Proxy是new关键词。
let sensitiveData = { username: 'devbryce' };
const {sensitiveData, revokeAccess} = Proxy.revocable(sensitiveData, handler);
function handleSuspectedHack(){ // Don't panic // Breathe revokeAccess(); }
// logs 'devbryce'
console.log(sensitiveData.username);
handleSuspectedHack();
// TypeError: Revoked
console.log(sensitiveData.username);