关注前端小讴,浏览更多原创技术文章
代理模式
相干代码 →
跟踪属性拜访
- 通过捕捉
get
、set
、has
等操作,能够监控对象何时何处被拜访过
const user = { name: 'Jake',}const proxy = new Proxy(user, { get(target, property, receiver) { console.log(`Getting ${property}`) return Reflect.get(...arguments) }, set(target, property, value, receiver) { console.log(`Setting ${property}=${value}`) return Reflect.set(...arguments) },})proxy.name // 'Getting name',触发get()拦挡proxy.age = 27 // 'Setting age=27',触发set()拦挡
暗藏属性
- 代理的外部实现对外部代码不可见,可暗藏指标对象的指定属性
const hiddenProperties = ['foo', 'bar'] // 要暗藏的键const targetObject = { // 指标对象 foo: 1, bar: 2, baz: 3,}const proxy2 = new Proxy(targetObject, { get(target, property) { if (hiddenProperties.includes(property)) { return undefined // 暗藏属性 } else { return Reflect.get(...arguments) } }, has(target, property) { if (hiddenProperties.includes(property)) { return undefined // 暗藏属性 } else { return Reflect.get(...arguments) } },})// get()拦挡console.log(proxy2.foo) // undefined,在代理外部被暗藏console.log(proxy2.bar) // undefined,在代理外部被暗藏console.log(proxy2.baz) // 3// has()拦挡console.log('foo' in proxy2) // false,在代理外部被暗藏console.log('bar' in proxy2) // false,在代理外部被暗藏console.log('baz' in proxy2) // true
属性验证
- 所有的赋值操作都会触发
set()
捕捉器,可依据所赋的值决定容许还是回绝赋值
const target = { onlyNumberGoHere: 0,}const proxy3 = new Proxy(target, { set(target, property, value) { if (typeof value !== 'number') { return false } else { return Reflect.set(...arguments) } },})proxy3.onlyNumberGoHere = 1 // 拦挡操作,所赋的值为Number类型console.log(proxy3.onlyNumberGoHere) // 1,赋值胜利proxy3.onlyNumberGoHere = '2' // 拦挡操作,所赋的值为String类型console.log(proxy3.onlyNumberGoHere) // 1,赋值失败
函数与结构函数参数验证
- 可对函数和构造函数参数进行审查,让函数只接管某数类型的值
function median(...nums) { return nums.sort()[Math.floor(nums.length / 2)]}const proxy4 = new Proxy(median, { apply(target, thisArg, argumentsList) { for (const arg of argumentsList) { if (typeof arg !== 'number') { // 只接管Number类型 throw 'Non-number argument provided' } } return Reflect.apply(...arguments) },})console.log(proxy4(4, 7, 1)) // 4console.log(proxy4(4, 7, '1')) // Error: Non-number argument provided
- 可要求实例化时必须给构造函数传参
class User { constructor(id) { this._id = id }}const proxy5 = new Proxy(User, { construct(target, argumentsList, newTarget) { if (argumentsList[0] === undefined) { // 必须传参 throw 'User cannot be instantiated without id' } return Reflect.construct(...arguments) },})new proxy5(1)// new proxy5() // Error: 'User cannot be instantiated without id'
数据绑定与可察看对象
- 通过代理把运行时本来不相干的局部分割到一起:可将被代理的类绑定到一个全局汇合,让所有创立的实例都被增加到该汇合中
const userList = []class User2 { constructor(name) { this._name = name }}const proxy6 = new Proxy(User2, { construct() { const newUser = Reflect.construct(...arguments) userList.push(newUser) // 将实例增加到全局汇合 return newUser },})new proxy6('John')new proxy6('Jacob')new proxy6('Jake')console.log(userList) // [ User2 { _name: 'John' }, User2 { _name: 'Jacob' }, User2 { _name: 'Jake' } ]
- 可把汇合绑定到一个事件分派程序,每次插入新实例时发送音讯
const eventList = []function emit(newValue) { console.log(newValue) /* John Jacob */}const proxy7 = new Proxy(eventList, { set(target, property, value, receiver) { console.log(target, property, value) /* [] 0 John [ 'John' ] length 1 [ 'John' ] 1 Jacob [ 'John', 'Jacob' ] length 2 */ const result = Reflect.set(...arguments) if (result && property !== 'length') { emit(value) } return result },})proxy7.push('John')proxy7.push('Jacob')
总结 & 问点
- 应用代理写一段代码,监控对象在何时何处被拜访
- 应用代理写一段代码,暗藏指标对象的指定属性
- 应用代理写一段代码,依据所赋的值决定容许或回绝给对象赋值
- 应用代理写一段代码,让函数只接管 String 类型的参数
- 应用代理写一段代码,让构造函数实例化时必须传参
- 应用代理写一段代码,让被代理的类在实例化时增加到全局汇合
- 应用代理写一段代码,让被代理的汇合在每次插入数据时发送音讯