本文已参与「新人创作礼」活动,一起开启掘金创作之路。
关键词: 结构型 拦截 接受控制 Proxt get/set Reflect
一图胜千言
代理模式是一种结构型设计模式。由代理控制对原对象的访问,并允许在将请求提交给对象前后进行一些处理(副作用:数据校验,数据格式化)。
JS 代理模式长什么样?
在 JS 中,可以使用 Proxy
来控制与对象的交互。不直接操作对象,而是通过代理实现基本操作的拦截。
JS 也提供了 Reflect
内置对象。为什么使用 Reflect
呢?
大多数情况直接用对象就满足需求了,如果遇到 target 上也存在 get/set
,那就建议使用 Reflect
, 并把 reciever
传递到方法中。可以在对象的拦截器上做一些校验 validation,格式化 formatting 等副作用。
Proxy
创建代理对象,Reflect
替代原始操作(访问/赋值)。
const person = { name: "kanelogger", age: 18, sex: 'male' }; const personProxy = new Proxy(person, { get: (obj, prop, receiver) => { // const str = `属性 ${prop} 的值 ${obj[prop] || '不存在'}`; const str = `属性 ${prop} 的值 ${Reflect.get(obj, prop, receiver) || '不存在'}` Reflect.has(obj, prop) // 检测一个对象是否存在特定属性 Reflect.ownKeys(obj); // Object.keys console.log(str); }, set: (obj, prop, value, receiver) => { if (prop === 'age' && typeof value !== 'number') { console.log('年纪必须是数字'); return false } else if (!obj[prop]) { Reflect.set(obj, prop, value, receiver); // 添加新属性 return false } // Reflect.deleteProperty(obj, prop) // 删除属性 console.log(`属性 ${prop} 的值从 ${obj[prop]} 变为 ${value}`); obj[prop] = value; } }); personProxy.name // 触发 handle.get 属性 name 的值 kanelogger personProxy.cb // 触发 handle.get 属性 cb 的值 不存在 personProxy.age = '12' // 触发 handle.set 年纪必须是数字 personProxy.age // 18 personProxy.age = 29 // 触发 handle.set 属性 age 的值从 18 变为 29 console.log(`person.age: ${person.age}`) // 29
顺便说一句:
在 Reflect
的场景下,receiver 可以改变计算属性中 this 的指向。Reflect.get(obj, prop, {xx: 'xx'})
在 Proxy
的场景下,这个 receiver 永远指向 Proxy 本身或者继承它的对象。
参考资料