TypeScript Proxy 13种陷阱方法详解:从 get/set 到 construct/apply
TypeScript Proxy 13种陷阱方法深度解析从基础拦截到高级元编程在TypeScript的元编程领域Proxy对象如同一个精密的瑞士军刀它允许开发者拦截并自定义对象的基础操作行为。本文将系统解析Proxy支持的13种陷阱方法结合TypeScript类型系统与Reflect API带您从基础拦截进阶到高级应用场景。1. Proxy基础架构与核心机制Proxy的核心理念是通过包装器模式实现对目标对象的操作拦截。其构造函数接受两个参数const proxy new Proxy(target, handler);其中target是被代理的原始对象handler则是定义拦截行为的陷阱对象。当对代理对象执行操作时对应的陷阱方法会被触发这使得我们可以在操作执行前后插入自定义逻辑。关键设计原则透明性代理对象应尽可能保持与原始对象相同的行为特征可控性通过陷阱方法精确控制哪些操作需要拦截安全性保持JavaScript对象操作的基本不变式(invariant)2. 属性访问拦截get陷阱深度解析get陷阱是最常用的拦截器它在读取属性值时触发。TypeScript中我们可以这样强化类型安全type User { id: number; name: string; age?: number; }; const user: User { id: 1, name: Alice }; const proxy new Proxy(user, { get(target, prop: keyof User, receiver) { console.log(访问属性: ${String(prop)}); return Reflect.get(target, prop, receiver); } }); console.log(proxy.name); // 输出日志并返回Alice关键注意事项receiver参数指向代理对象本身确保getter中的this正确绑定使用Reflect.get保持默认行为的同时添加自定义逻辑通过keyof类型约束确保属性访问的安全性3. 属性赋值拦截set陷阱的完整实现set陷阱在属性赋值时触发适合用于数据验证和响应式编程const validator { set(target: any, prop: keyof User, value: any, receiver: any) { if (prop age (typeof value ! number || value 0)) { throw new TypeError(年龄必须是正数); } const success Reflect.set(target, prop, value, receiver); if (success) { console.log(属性${String(prop)}更新为: ${value}); } return success; } }; const userProxy new Proxy(user, validator); userProxy.age 25; // 正常 userProxy.age -1; // 抛出TypeError高级技巧结合Reflect.set的返回值处理严格模式下的错误情况使用类型守卫进行精细化验证在Vue3等响应式系统中此处会触发依赖更新4. 存在性检查has陷阱的应用场景has陷阱拦截in操作符可用于创建私有属性效果const privateHandler { has(target, prop) { if (prop.startsWith(_)) { return false; // 隐藏私有属性 } return Reflect.has(target, prop); } }; const obj { _secret: 42, public: 100 }; const privateProxy new Proxy(obj, privateHandler); console.log(_secret in privateProxy); // false console.log(public in privateProxy); // true5. 属性删除deleteProperty陷阱实现拦截delete操作时deleteProperty陷阱非常有用const persistentHandler { deleteProperty(target, prop) { if (prop id) { throw new Error(ID属性不可删除); } return Reflect.deleteProperty(target, prop); } }; const persistentProxy new Proxy(user, persistentHandler); delete persistentProxy.name; // 成功 delete persistentProxy.id; // 抛出错误6. 原型操作拦截getPrototypeOf与setPrototypeOf这两个陷阱分别拦截获取和设置原型链的操作const protoHandler { getPrototypeOf(target) { console.log(正在访问原型); return Reflect.getPrototypeOf(target); }, setPrototypeOf(target, newProto) { if (newProto null) { throw new Error(不能设置null原型); } return Reflect.setPrototypeOf(target, newProto); } }; const protoProxy new Proxy(user, protoHandler); Object.getPrototypeOf(protoProxy); // 触发日志 Object.setPrototypeOf(protoProxy, {}); // 正常7. 可扩展性控制isExtensible与preventExtensions管理对象的可扩展状态const extensibilityHandler { isExtensible(target) { console.log(检查可扩展性); return Reflect.isExtensible(target); }, preventExtensions(target) { if (target.important) { return false; // 重要对象不允许冻结 } return Reflect.preventExtensions(target); } };8. 属性描述符操作defineProperty与getOwnPropertyDescriptor精细控制属性定义过程const descriptorHandler { defineProperty(target, prop, descriptor) { if (typeof prop string prop.length 20) { throw new Error(属性名过长); } return Reflect.defineProperty(target, prop, descriptor); }, getOwnPropertyDescriptor(target, prop) { const desc Reflect.getOwnPropertyDescriptor(target, prop); if (desc) { desc.configurable false; // 返回不可配置的描述符 } return desc; } };9. 自有属性枚举ownKeys陷阱的妙用ownKeys陷阱拦截Object.keys()等操作const filteredKeysHandler { ownKeys(target) { return Reflect.ownKeys(target).filter( key !key.toString().startsWith(_) ); } }; const filteredProxy new Proxy({ _secret: 1, a: 2 }, filteredKeysHandler); console.log(Object.keys(filteredProxy)); // 只输出[a]10. 函数调用拦截apply陷阱实现AOP代理函数对象时apply陷阱能实现装饰器模式function logCall(target: Function, thisArg: any, args: any[]) { console.log(调用函数: ${target.name}, args); return target.apply(thisArg, args); } const functionProxy new Proxy( (x: number, y: number) x y, { apply: logCall } ); functionProxy(2, 3); // 输出调用日志并返回511. 构造函数拦截construct陷阱的高级应用construct陷阱可以定制new操作符行为class Person { constructor(public name: string) {} } const singletonHandler { instance: null as Person | null, construct(target, args) { if (!this.instance) { this.instance Reflect.construct(target, args); } return this.instance; } }; const SingletonPerson new Proxy(Person, singletonHandler); const p1 new SingletonPerson(Alice); const p2 new SingletonPerson(Bob); console.log(p1 p2); // true实现单例模式12. Reflect API与Proxy的黄金组合Reflect提供了与Proxy陷阱一一对应的原子操作Proxy陷阱Reflect方法对应操作getReflect.get属性读取setReflect.set属性赋值hasReflect.hasin操作符constructReflect.constructnew操作最佳实践const safeHandler { get(target, prop, receiver) { // 前置逻辑 const result Reflect.get(target, prop, receiver); // 后置逻辑 return result; } };13. 综合应用实现类型安全的观察者系统结合多种陷阱方法构建响应式系统type ObserverT (prop: keyof T, value: any) void; function observableT extends object(obj: T) { const observers new SetObserverT(); return new Proxy(obj, { set(target, prop: keyof T, value, receiver) { const oldValue target[prop]; const success Reflect.set(target, prop, value, receiver); if (success oldValue ! value) { observers.forEach(obs obs(prop, value)); } return success; } }); } const state observable({ count: 0 }); state.count 1; // 触发所有观察者性能优化技巧使用WeakMap存储观察者避免内存泄漏批量更新时合并通知针对数组操作进行特殊处理