前端八股2---Proxy 代理

张开发
2026/4/19 1:00:04 15 分钟阅读

分享文章

前端八股2---Proxy 代理
目录一、核心概念基础语法二、核心结构三、Vue3 中的应用3.1 响应式系统3.2 间接使用 Proxy 的场景四、Vue2 vs Vue3 对比面试常考五、Proxy 能拦截的操作响应式相关六、ES 规范版本七、面试高频题Q1Vue3 为什么不兼容 IEQ2Proxy 和 Object.defineProperty 的区别Q3Vue3 的响应式为什么比 Vue2 快Q4Vue3 的 reactive 是深度响应式吗Q5Vue3 的响应式原理是什么Q6用 Proxy 实现一个简单的数据校验Q7Proxy 能代理多层嵌套对象吗Q8用 Proxy 实现一个访问日志记录器Q9Proxy 代理后的对象和原对象有什么关系Q10Reflect 是什么有什么作用Q11为什么 Proxy 中要使用 Reflect一、核心概念Proxy 就是给对象套一层代理拦截所有操作实现监听、控制、增强。给对象、数组、函数包一层拦截层你对目标做任何操作读、改、删、调用…都会先经过这层代理你可以拦截、修改、监听。基础语法const target { name: 张三 }; const proxy new Proxy(target, { // 拦截读取属性 get(target, prop) { console.log(读取了 prop); return target[prop]; }, // 拦截修改属性 set(target, prop, value) { console.log(修改了 prop); target[prop] value; return true; // 必须返回 true 表示成功 } }); proxy.name; // 打印读取了name proxy.name 李四; // 打印修改了name二、核心结构new Proxy(目标对象, { get(target, prop, receiver) {}, // 读取属性 set(target, prop, value, receiver) {}, // 修改属性 deleteProperty(target, prop) {}, // 删除属性 has(target, prop) {}, // in 操作符 apply(target, thisArg, args) {}, // 函数调用 construct(target, args) {}, // new 操作符 ownKeys(target) {}, // Object.getOwnPropertyNames defineProperty(target, prop, desc) {}, // 定义新属性 // ... 还有很多 })三、Vue3 中的应用3.1 响应式系统Vue3 的reactive/ref底层就是Proxy。import { reactive } from vue const state reactive({ name: zs }) // 这个 state 本质就是一个 Proxy 对象作用机制拦截state.name的读取 →收集依赖拦截state.name xxx的修改 →触发更新3.2 间接使用 Proxy 的场景只要用了以下 API底层都在使用 Proxyreactive()ref()computed()defineProps/defineEmits内部有代理组件实例this被代理过的它们都内置了依赖收集 派发更新四、Vue2 vs Vue3 对比面试常考对比项Vue2 (Object.defineProperty)Vue3 (Proxy)新增属性❌ 监听不到需要$set✅ 能监听到删除属性❌ 监听不到需要$delete✅ 能监听到数组下标修改❌ 监听不到✅ 能监听到数组 length 修改❌ 监听不到✅ 能监听到数组方法 (push/pop等)⚠️ 需要拦截重写✅ 天然拦截性能需要递归遍历所有属性懒代理按需拦截Vue2 缺陷总结监听不全面很多情况监听不到必须用额外 API$set、$delete补救Vue3 优势总结能监听新增、删除、数组修改性能更好、功能更强五、Proxy 能拦截的操作响应式相关拦截器对应操作响应式用途get读取属性收集依赖set修改已有属性触发更新deleteProperty删除属性触发更新defineProperty新增属性触发更新数组方法push/pop/shift等触发更新六、ES 规范版本特性版本PromiseES6 (ES2015)ProxyES6 (ES2015)✅async/awaitES2017⚠️注意纠正Proxy 是 ES6ES2015的一部分不是 ES2016。只是浏览器支持较晚Chrome 492016年3月导致很多人误以为版本更晚。七、面试高频题Q1Vue3 为什么不兼容 IEA因为 Vue3 底层用了Proxy实现响应式而 Proxy没有 polyfill无法用纯 JS 模拟IE 全系列不支持Q2Proxy 和 Object.defineProperty 的区别A监听范围Proxy 可监听增/删/数组操作defineProperty 不行性能Proxy 是懒代理按需defineProperty 需要递归遍历语法Proxy 拦截操作更统一defineProperty 需要单独定义 getter/setterQ3Vue3 的响应式为什么比 Vue2 快回答要点懒代理不用递归遍历所有嵌套对象初始化更快按需拦截只代理访问到的对象内存占用更少Proxy 本身性能比 defineProperty 的 getter/setter 机制更高效懒代理 不在一开始就代理所有嵌套对象而是等真正访问到某个属性时才对该属性的值如果是对象进行代理。Q4Vue3 的 reactive 是深度响应式吗回答是的但它是通过懒代理实现的深度响应式。一开始只代理最外层当访问到嵌套对象时才递归创建代理。最终效果和 Vue2 一样是深度响应式但性能和内存都更好。Q5Vue3 的响应式原理是什么回答模板Vue3 的响应式基于 Proxy 实现。通过reactive函数给数据创建 Proxy 代理在get拦截中收集依赖哪些组件在用这个数据在set、deleteProperty等拦截中触发更新通知组件重新渲染。同时采用懒代理策略只在访问到嵌套对象时才递归代理提升性能。// 简化版原理 function reactive(target) { return new Proxy(target, { get(target, key, receiver) { const res Reflect.get(target, key, receiver); track(target, key); // 收集依赖 return res; }, set(target, key, value, receiver) { const result Reflect.set(target, key, value, receiver); trigger(target, key); // 触发更新 return result; } }); }Q6用 Proxy 实现一个简单的数据校验function createValidator(obj, validator) { return new Proxy(obj, { set(target, key, value) { if (validator[key] !validator[key](value)) { throw new Error(${key} 校验失败${value} 不合法); } target[key] value; return true; } }); } const user createValidator( { age: 0 }, { age: (val) val 0 val 150 } ); user.age 25; // ✅ 成功 user.age -10; // ❌ 报错age 校验失败Q7Proxy 能代理多层嵌套对象吗Proxy 默认只代理第一层。要实现深度响应式需要在get拦截中判断返回值是否是对象如果是则递归调用reactive进行代理。这就是 Vue3 的懒代理策略。function reactive(target) { return new Proxy(target, { get(target, key, receiver) { const res Reflect.get(target, key, receiver); // 关键如果是对象递归代理 if (res ! null typeof res object) { return reactive(res); } return res; } }); }reactive 内部有懒代理吗✅ 有这是它的核心特性ref 和 reactive 是 Vue 3 的吗✅ 是的都是 Vue 3 Composition API 的核心Q8用 Proxy 实现一个访问日志记录器function createLogger(obj) { return new Proxy(obj, { get(target, key) { console.log([${new Date().toLocaleTimeString()}] 读取了 ${String(key)}); return target[key]; }, set(target, key, value) { console.log([${new Date().toLocaleTimeString()}] 修改了 ${String(key)} ${value}); target[key] value; return true; } }); } const data createLogger({ name: 张三 }); data.name; // 记录读取日志 data.name 李四; // 记录修改日志Q9Proxy 代理后的对象和原对象有什么关系代理对象和原对象是两个不同的对象。修改代理对象会影响原对象因为代理内部操作的是原对象但直接修改原对象不会触发代理的拦截。所以在 Vue3 中应该始终使用代理对象进行操作。const obj { name: 张三 }; const proxy new Proxy(obj, { set(target, key, value) { console.log(set 拦截); target[key] value; return true; } }); proxy.name 李四; // 触发拦截 ✅ obj.name 王五; // 不触发拦截 ❌Q10Reflect 是什么有什么作用答Reflect 是 ES6 新增的内置对象提供了一套与 Proxy 拦截器一一对应的方法。主要作用与 Proxy 配合保证 this 指向正确提供统一的返回值boolean便于错误处理将对象操作函数化更加规范Q11为什么 Proxy 中要使用 Reflect答主要有两个原因保证 this 正确Reflect.get的receiver参数会作为 getter 中的 this 指向这在处理原型链时至关重要执行默认行为Proxy 拦截后通常还需要执行对象的默认操作Reflect 提供了标准的默认实现

更多文章