前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
首页程序员面试题库Proxy.revocable 可撤销代理 撤销后行为
JavaScript对象与原型

怎么创建一个可撤销的 Proxy?撤销之后再访问会怎样?

调用 Proxy.revocable(target, handler),它返回一个对象,里面有 proxy 和 revoke 两个属性。调用 revoke() 之后,对 proxy 的读、写、in、delete 等操作都会抛 TypeError,原对象 target 不受影响。

前端进阶之旅 · 一题精讲更新于 2026.09.29
JavaScript#对象与原型
先看核心答案读代码示例
理解线索

可撤销代理由三部分组成

  1. target被代理的原对象,撤销后照常可用,只是代理不再转发给它
  2. proxy交给别人使用的代理对象,撤销后变成一个只会报错的空壳
  3. revoke()切断代理和原对象的联系,一次生效,之后再调用什么也不做

撤销只废掉这一个代理。如果别人在撤销前已经通过代理拿到了原对象里的嵌套对象,那个嵌套对象不经过代理,撤销管不到它。

核心回答

先记住这个答案

用 Proxy.revocable(target, handler) 创建,它不返回代理本身,而是返回 { proxy, revoke }。平时使用 proxy 和普通 Proxy 没有区别。一旦调用 revoke(),这个代理就和原对象断开了,之后对它做任何会触发内部操作的事,比如读属性、赋值、in、delete、Object.keys,都会抛出 TypeError。撤销是一次性的,不能恢复,原对象本身不受影响。

  • Proxy.revocable 返回代理与撤销函数
  • 撤销后几乎所有操作都抛 TypeError
  • 撤销后仍可比较代理引用
  • 撤销不可逆,重复调用 revoke() 没有效果

撤销前后对比

先看一段完整的对比,重点是撤销之后哪些操作会报错、哪些不会。

同一个代理在撤销前后的表现JavaScript
const target = { name: 'Tom' }
const { proxy, revoke } = Proxy.revocable(target, {})

console.log(proxy.name) // 撤销前正常转发:Tom
revoke()

const tryIt = (label, fn) => {
  try { fn(); console.log(label, 'ok') }
  catch (err) { console.log(label, err.name) }
}
tryIt('get', () => proxy.name)
tryIt('set', () => { proxy.age = 18 })
tryIt('in', () => 'name' in proxy)
tryIt('keys', () => Object.keys(proxy))
console.log(typeof proxy, proxy === proxy) // 不触发内部方法
console.log(target.name) // 原对象不受影响
revoke() // 再调一次不会报错,也没有任何效果
查看输出与解释
Tom
get TypeError
set TypeError
in TypeError
keys TypeError
object true
Tom

注意 handler 是空对象,一个 trap 都没写,撤销后照样全部报错。报错不是 trap 抛的,而是引擎在执行代理的内部方法前先检查代理有没有被撤销。typeof 和 === 只看值本身,不会调用代理的内部方法,所以还能正常用。

它解决什么问题:临时授权

普通对象一旦交出去,你就没法再收回来。别人只要存了一份引用,想什么时候读就什么时候读。可撤销代理就是给这种场景准备的:你交出去的不是原对象,而是一个随时能作废的代理。

比如一个插件系统,主程序把配置对象通过代理交给插件,插件被卸载时调用 revoke(),插件里残留的定时器、闭包再想读配置,就会直接报错,而不是悄悄读到最新的数据。再比如只允许某段代码在 5 秒内访问一份数据,到时间用 setTimeout 调一次 revoke() 就行。

这里有个限制要说清楚。代理只管住它自己这一层。如果插件在撤销前执行过 const db = proxy.db,拿到的 db 是原对象里的嵌套对象本身,不是代理,撤销以后它还能继续用。要把整棵对象树都管住,需要在 get trap 里把返回的对象也包成可撤销代理,并记下所有 revoke,统一撤销。这种做法叫「隔离膜」(membrane),一些沙箱方案就是这么实现的。

撤销对内存的影响

revoke() 执行后,代理内部保存的 target 和 handler 引用会被清掉。所以即使代理对象本身还被别人拿着,只要原对象没有其他引用,它也能被垃圾回收。普通 Proxy 做不到这一点,只要代理活着,原对象就一直活着。

回答前,多想一步

容易答错的地方

以为撤销会影响原对象
撤销只作用在代理上。原对象 target 还是普通对象,直接访问它完全正常。想让原对象也不能改,那是 Object.freeze 的事。
以为撤销后所有操作都报错
typeof proxy 返回 'object',proxy === proxy 返回 true,把它作为 WeakMap 的键也可以,因为这些操作只用到对象的身份,不会调用内部方法。但像 Array.isArray(proxy) 这样需要看穿代理去检查原对象的操作,撤销后同样会抛 TypeError。
以为能通过 new Proxy 再加一个开关实现同样效果
可以在每个 trap 里判断一个 revoked 标记来模拟,但要把十几个 trap 都写一遍,而且原对象引用一直留在闭包里,没法被回收。原生的 Proxy.revocable 更完整。
试着用自己的话回答

面试官还会怎么问?

撤销之后还能恢复吗?

不能。revoke() 是单向的,想重新授权只能再调一次 Proxy.revocable 创建一个新的代理,交给对方新的引用。

撤销后报错的时机是什么时候?

是在下一次操作代理的时候,不是调用 revoke() 的那一刻。revoke() 本身只是把内部引用清空,谁再去读写这个代理,谁就收到 TypeError。

Vue 3 的响应式用了可撤销代理吗?

没有。Vue 3 用的是普通的 new Proxy,它需要的是拦截读写做依赖收集和触发更新,不需要收回访问权限。可撤销代理更多出现在沙箱、插件隔离这类安全场景。

从一道题,走向一组知识

把知识连起来

对象与原型

Object.seal 和 Object.freeze 有什么区别?各自还允许改什么?

Object.freeze 是让对象不能被改,可撤销代理是让对象的访问权限能被收回,都是在控制别人对对象能做什么。

对象与原型

深拷贝和浅拷贝有什么区别?手写深拷贝怎么处理循环引用?

把数据交给第三方之前,除了用代理控制访问,另一个办法是给一份深拷贝,两种思路可以对比着记。

参考资料

  • JavaScript:对象与原型参考资料

示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。

本题目录
  1. 先记住这个答案
  2. 撤销前后对比
  3. 它解决什么问题:临时授权
  4. 撤销对内存的影响
  5. 容易答错的地方
  6. 面试官还会怎么问
  7. 把知识连起来
读懂,再试着讲出来

先看核心答案,再读代码。最后展开追问,检查自己有没有遗漏边界。

试着回答追问
浏览全部面试题理解原理,也关注真实的使用场景。回到顶部 ↑