共计 6993 个字符,预计需要花费 18 分钟才能阅读完成。
关注前端小讴,浏览更多原创技术文章
代理根底
- ES6 为的代理和反射为开发者提供 拦挡 并向基本操作 嵌入额定行为 的能力
- 代理是指标对象的 形象 ,其能够用作指标对象的 替身 ,但 齐全独立 于指标对象
- 指标对象 既可 间接被操作 ,也可 通过代理来操作,间接操作会绕过代理施予的行为
相干代码 →
创立空代理
- 应用
Proxy
构造函数创立代理,接管 指标对象 和处理程序对象 两个参数(缺一不可) - 空代理是 最简略 的代理,可用空对象作为 处理程序对象,空代理对象仅作为一个形象的指标对象
const target = {
// 指标对象
id: 'target',
}
const handler = {} // 处理程序对象(空对象)const proxy = new Proxy(target, handler) // 创立空代理
- (默认状况下)空代理对象 上执行的 所有操作 都会 利用到指标对象,反之亦然
console.log(target.id) // 'target'
console.log(proxy.id) // 'target'
target.id = 'foo' // 指标对象属性从新赋值
console.log(target.id) // 'foo'
console.log(proxy.id) // 'foo',会反映到代理上
proxy.id = 'bar' // 代理属性从新赋值
console.log(target.id) // 'bar',会反映到指标对象上
console.log(proxy.id) // 'bar'
console.log(target.hasOwnProperty('id')) // true
console.log(proxy.hasOwnProperty('id')) // true
Proxy
构造函数没有prototype
属性,也不能应用instanceof
操作符检测
console.log(Proxy) // [Function: Proxy]
console.log(Proxy.prototype) // undefined
console.log(proxy instanceof Proxy) // TypeError: Function has non-object prototype 'undefined' in instanceof check
- 应用严格相等
===
用以 辨别代理和指标
console.log(target === proxy) // false
定义捕捉器
- 应用代理的次要目标是能够 定义捕捉器 ,即基本操作的 拦截器
- 每个捕捉器 对应一种 基本操作,能够间接或间接在代理上调用,调用操作时会 先调用捕捉器函数,再将操作流传到指标对象
const target2 = {foo: 'bar',}
const handler2 = {// 定义 get()捕捉器函数,以办法名为键
get() {return 'handler override'},
}
const proxy2 = new Proxy(target2, handler2)
get()
函数能够通过 多种形式触发 并被捕捉器拦挡到:proxy[property]
、proxy.property
和Object.create(proxy)[property]
- 只有 代理对象 上执行操作才会 触发捕捉器,指标对象上不会
console.log(proxy2.foo) // 'handler override',代理对象上操作
console.log(proxy2['foo']) // 'handler override',代理对象上操作
console.log(Object.create(proxy2).foo) // 'handler override',代理对象上操作
console.log(target2.foo) // 'bar',指标对象上操作
console.log(target2['foo']) // 'bar',指标对象上操作
console.log(Object.create(target2).foo) // 'bar',指标对象上操作
捕捉器参数和反射 API
get()
捕捉器接管 3 个参数:指标对象、要查问的属性、代理对象
const target3 = {foo: 'bar',}
const handler3 = {// get()捕捉器接管 3 个参数:指标对象、要查问的属性、代理对象
get(tar, pro, rec) {console.log(tar === target3)
console.log(pro)
console.log(rec === handler3)
},
}
const proxy3 = new Proxy(target3, handler3)
proxy3.foo
/*
true
'foo'
false
*/
- 捕捉器利用这些参数 重建 被捕捉办法的 原始行为
const handler4 = {get(tar, pro, rec) {return tar[pro] // target3['foo']
},
}
const proxy4 = new Proxy(target3, handler4)
console.log(proxy4.foo) // 'bar'
- 解决对象的 所有捕捉器办法 都有对应的 反射 API 办法 ( 同名 且行为雷同 ),办法存在于全局对象
Reflect
上
const handler5 = {get() {return Reflect.get(...arguments) // 用 arguments 解耦
},
// get: Reflect.get, // 更简洁的写法
}
const proxy5 = new Proxy(target3, handler5)
console.log(proxy5.foo) // 'bar'
- 创立一个能够 捕捉所有办法 ,并将 每个办法 都转发给反射 API 的 空代理,可不定义处理程序对象
const proxy6 = new Proxy(target3, Reflect)
console.log(proxy6.foo) // 'bar'
- 利用反射 API,可用 起码的代码 批改捕捉的办法
const target4 = {
foo: 'bar',
baz: 'qux',
}
const handler6 = {get(tar, pro, rec) {let dec = ''pro ==='foo'&& (dec ='!!!')
return Reflect.get(...arguments) + dec
},
}
const proxy7 = new Proxy(target4, handler6)
console.log(proxy7.foo) // 'bar!!!'
console.log(proxy7.baz) // 'qux'
捕捉器不变式
- 捕捉处理程序的行为必须遵循 捕捉器不变式
- 如:指标对象有一个 不可配置 且不可重写 的属性,捕捉器批改返回值会报错
const target5 = {}
Object.defineProperty(target5, 'foo', {
configurable: false, // 不可配置
writable: false, // 不可重写
value: 'bar',
})
const handler7 = {get() {return 'qux'},
}
const proxy8 = new Proxy(target5, handler7)
console.log(proxy8.foo) // TypeError: 'get' on proxy: property 'foo' is a read-only and non-configurable data property on the proxy target but the proxy did not return its actual value (expected 'bar' but got 'qux')
可撤销代理
new Proxy()
创立的代理 不可撤销,会在代理对象生命周期内始终存在-
Proxy.revocable()
办法能够用来创立一个 可撤销 的代理对象- 接管 指标对象 和解决对象2 个参数
- 返回构造为
{"proxy":proxyObj,"revoke":revokeFun}
的对象 proxy
为代理对象;revoke
为撤销办法,调用时不需参数- 撤销函数
revoke()
幂等,调用屡次后果雷同 - 撤销操作 不可逆,撤销后再次调用代理会报错
const target6 = {foo: 'bar',}
const handler8 = {get() {return 'intercepted'},
}
const revocable = Proxy.revocable(target6, handler8)
const proxy9 = revocable.proxy // 创立可撤销代理
console.log(proxy9.foo) // 'intercepted'
revocable.revoke() // 撤销代理
revocable.revoke() // 撤销代理,调用屡次后果雷同
revocable.revoke() // 撤销代理,调用屡次后果雷同
// console.log(proxy9.foo) // TypeError: Cannot perform 'get' on a proxy that has been revoked
实用反射 API
反射 API 与对象 API
- 反射 API 不限于捕捉处理程序
-
大多数反射 API 在
Object
类型上有对应的办法:Object
上的办法实用于 通用程序 ,反射办法实用于 细粒度的对象管制与操作
状态标记
-
以下反射办法提供 状态标记 ,返回 布尔值 示意操作是否胜利
Reflect.defineProperty()
、Reflect.preventExtensions()
、Reflect.setPrototypeOf()
、Reflect.set()
、Reflect.deleteProperty()
- (参数格局正确)操作失败 时,不会抛出谬误,而是返回
false
const o = {}
Object.defineProperty(o, 'foo', {writable: false, // 不可重写})
Object.defineProperty(o, 'foo', { value: 'bar'}) // TypeError: Cannot redefine property: foo,Object.defineProperty()定义不胜利会抛出谬误
Reflect.defineProperty(o, 'foo', { value: 'bar'}) // Reflect.defineProperty()定义不胜利不会抛出谬误
console.log(Reflect.defineProperty(o, 'foo', { value: 'bar'})) // false,Reflect.defineProperty()返回“状态标记”的布尔值
// 重构后的代码
if (Reflect.defineProperty(o, 'foo', { value: 'bar'})) {console.log('success')
} else {console.log('failure') // 'failure'
}
用一等函数代替操作符
-
以下反射办法提供 只有通过操作符能力实现的操作
Reflect.get()
:能够代替 对象属性拜访 操作符Reflect.set()
:能够代替赋值操作符=
Reflect.has()
:能够代替in
操作符或with()
Reflect.deleteProperty()
:能够代替delete
操作符Reflect.construct()
:能够代替new
操作符
const o2 = {
foo: 1,
bar: 2,
get baz() {return this.foo + this.bar},
}
Reflect.get(o2, 'foo') // 1
Reflect.set(o2, 'foo', 3)
console.log(o2.foo) // 3
Reflect.has(o2, 'foo') // true
Reflect.deleteProperty(o2, 'bar')
console.log(o2.bar) // undefined
const arr = Reflect.construct(Array, [1, 2, 3])
console.log(arr) // [1, 2, 3]
平安地利用函数
- 对函数原型对象
Function.prototype
的apply
办法利用call
进行绑定时,Reflect.apply()
能够使代码更加简洁易懂
const f1 = function () {console.log(arguments[0] + this.mark)
}
const o3 = {mark: 95,}
f1.apply(o3, [15]) // 110,将 f1 的 this 绑定到 o3
Function.prototype.apply.call(f1, o3, [15]) // 110,函数的原型对象的 apply 办法,利用 call 进行绑定
Reflect.apply(f1, o3, [15]) // 110,通过指定的参数列表发动对指标函数的调用,三个参数(指标函数、绑定的 this 对象、实参列表)
无关 Reflect 对象的具体文档 →
代理另一个代理
- 创立一个代理,通过它 代理另一个代理 ,从而在一个指标对象上 构建多层拦挡网
const target7 = {foo: 'bar',}
const firstProxy = new Proxy(target7, {
// 第一层代理
get() {console.log('first proxy')
return Reflect.get(...arguments)
},
})
const secondProxy = new Proxy(firstProxy, {
// 第二层代理
get() {console.log('second proxy')
return Reflect.get(...arguments)
},
})
console.log(secondProxy.foo)
/*
'second proxy'
'first proxy'
'bar'
*/
代理的问题与有余
代理中的 this
- 代理中的
this
值是潜在的问题起源,例如办法中的this
通常指向调用该办法的对象
const target8 = {thisValEqualProxy() {
return this === proxy10
/*
this 指向:在实例中,指向实例自身
在代理中,指向代理对象
*/
},
}
const proxy10 = new Proxy(target8, {})
console.log(target8.thisValEqualProxy()) // false
console.log(proxy10.thisValEqualProxy()) // true
- 当 指标对象依赖于对象标识 时,
this
的指向会产生问题
const wm = new WeakMap()
class User {constructor(userId) {wm.set(this, userId) // 应用指标对象作为 WeakMap 的键
/*
this 的指向:指标对象
*/
}
get id() {return wm.get(this)
/*
this 的指向:在实例中,指向实例自身 User {}
在代理中,指向代理对象
*/
}
}
const user = new User(123)
console.log(wm) // WeakMap {User => 123}
console.log(user.id) // 123
const userInstanceProxy = new Proxy(user, {}) // 代理 user 实例,User 类 constructor 中的 this 指向 User 类实例
console.log(wm) // WeakMap {User => 123},弱键未发生变化
console.log(userInstanceProxy.id) // undefined
- 将 代理实例 改为 代理类自身,再创立代理实例,解决问题
const userClassProxy = new Proxy(User, {}) // 代理 User 类自身
const proxyUser = new userClassProxy(456) // 创立代理实例,User 类 constructor 中的 this 指向代理实例
console.log(wm) // WeakMap {User => 123, User => 456},弱键发生变化,追加了以代理作为键
console.log(proxyUser.id) // 456
代理与外部槽位
- 有些内置类型可能会依赖代理无法控制的机制:如
Date
类型办法的执行依赖this
值上的外部槽位[[NumberDate]]
,而该槽位 不存在 于代理对象,且无奈被get()
或set()
操作拜访到
const target9 = new Date()
const proxy11 = new Proxy(target9, {})
console.log(target9.getDate()) // 24,当天日期
console.log(proxy11.getDate()) // TypeError: this is not a Date object.
总结 & 问点
- 代理的用途是什么?其和指标对象有怎么的关系?
- 如何创立空代理?如何辨别空代理对象和指标对象?
- 什么是捕捉器?其是如何被调用和触发的?get()函数能够通过哪些模式被捕捉器拦挡?
- get()捕捉器接管哪些参数?写一段代码,利用这些参数重写捕捉办法的原始行为
- 如何创立可撤销代理?撤销后再次撤销会怎么?撤销后调用代理会怎么?
- 如何了解 Reflect 对象?其反射 API 与对象 API 有怎么的关联和异同?如何了解 Reflect.apply()办法?
- 如何通过代理,在一个指标对象上构建多层拦挡网?
- 代理有哪些潜在的问题?如何解决呢?
正文完
发表至: javascript
2021-06-10