关注前端小讴,阅读更多原创技术文章
代理捕获器与反射方法
- 代理可以捕获13 种不同的基本操作,代理对象上执行的任一种操作只会有一种捕获处理程序被调用,不存在重复捕获现象
- 只要在代理上操作,所有捕获器都会拦截对应的反射 API 操作
get()
在获取属性值的操作中被调用,对应的反射 API 方法为
Reflect.get(target, property, receiver)
返回值
- 无限制
拦截的操作
proxy.property
proxy[property]
Object.create(proxy)[property]
Reflect.get(proxy,property,receiver)
捕获器处理程序参数
target
:目标对象property
:目标对象的键属性(字符串键或符号键)receiver
:代理对象或继承代理对象的对象
捕获器不变式
- 如果
target.property
不可写且不可配,处理程序返回值必须与target.property
匹配 - 如果
target.property
不可配置且[[Get]]
特性为undefined
,处理程序返回值必须是undefined
- 如果
const myTarget = {
foo: 'bar',
}
const proxy = new Proxy(myTarget, {
get(target, property, receiver) {
console.log(target) // { foo: 'bar' },目标对象
console.log(property) // 'foo',目标对象键属性
console.log(receiver) // { foo: 'bar' },代理对象
return Reflect.get(...arguments)
},
})
console.log(proxy.foo) // 'bar',拦截的操作
// 捕获器不变式
Object.defineProperty(myTarget, 'foo', {
configurable: false, // 不可配置
})
const proxy2 = new Proxy(myTarget, {
get() {
return 'baz' // 试图重写
},
})
console.log(proxy2.foo) // TypeError
set()
在设置属性值的操作中被调用,对应的反射 API 方法为
Reflect.set(target, property, value, receiver)
返回值
- 布尔值,true 成功 / false 失败(严格模式下抛出 TypeError)
拦截的操作
proxy.property = value
proxy[property] = value
Object.create(proxy)[property] = value
Reflect.get(proxy,property,receiver) = value
捕获器处理程序参数
target
:目标对象property
:目标对象的键属性(字符串键或符号键)value
:要赋给属性的值receiver
:接收最初赋值的对象
捕获器不变式
- 如果
target.property
不可写且不可配,则不能修改目标属性的值 - 如果
target.property
不可配置且[[Set]]
特性为undefined
,则不能修改目标属性的值 - 严格模式下,处理程序返回
false
会抛出 TypeError
- 如果
const myTarget2 = {}
const proxy3 = new Proxy(myTarget2, {
set(target, property, value, receiver) {
console.log(target) // {},目标对象
console.log(property) // 'foo',目标对象键属性
console.log(value) // 'baz',要赋给属性的值
console.log(receiver) // {},接收最初赋值的对象
console.log(Reflect.set(...arguments)) // true,操作成功
return Reflect.set(...arguments)
},
})
proxy3.foo = 'baz' // 拦截的操作
console.log(proxy3.foo) // 'baz'
console.log(myTarget2.foo) // 'baz',赋值转移到目标对象上
// 捕获器不变式
Object.defineProperty(myTarget2, 'foo', {
configurable: false, // 不可配置
writable: false, // 不可重写
})
const proxy4 = new Proxy(myTarget2, {
set() {
console.log(Reflect.set(...arguments)) // false,操作失败
return undefined
},
})
proxy4.foo = 'bar'
console.log(proxy4.foo) // 'baz'
console.log(myTarget2.foo) // 'baz'
has()
在in 操作符中被调用,对应的反射 API 方法为
Reflect.has(target, property)
返回值
- 布尔值,表示属性是否存在,非布尔值会被转型为布尔值
拦截的操作
property in proxy
property in Object.create(proxy)
with(proxy) {(property)}
Reflect.has(proxy,property)
捕获器处理程序参数
target
:目标对象property
:目标对象的键属性(字符串键或符号键)
捕获器不变式
- 如果
target.property
存在且不可配置,则处理程序必须返回true
- 如果
target.property
存在且目标对象不可扩展,则处理程序必须返回true
- 如果
const myTarget3 = { foo: 'bar' }
const proxy5 = new Proxy(myTarget3, {
has(target, property) {
console.log(target) // { foo: 'bar' },目标对象
console.log(property) // 'foo',目标对象键属性
console.log(Reflect.set(...arguments)) // true,属性存在
return Reflect.set(...arguments)
},
})
'foo' in proxy5 // 拦截的操作
// 捕获器不变式
Object.defineProperty(myTarget3, 'foo', {
configurable: false, // 不可配置
})
const proxy6 = new Proxy(myTarget3, {
has() {
return false // 试图返回false
},
})
'foo' in proxy6 // TypeError
defineProperty()
在
Object.defineProperty()
中被调用,对应的反射 API 方法为Reflect.defineProperty(target, property, descriptor)
返回值
- 布尔值,表示属性是否成功定义,非布尔值会被转型为布尔值
拦截的操作
Object.defineProperty(proxy, property, descriptor)
Reflect.defineProperty(proxy, property, descriptor)
捕获器处理程序参数
target
:目标对象property
:目标对象的键属性(字符串键或符号键)descriptor
:描述符对象(描述符对象的属性:configurable
、enumerable
、writable
、value
、get
、set
的一个或多个)
捕获器不变式
- 如果目标对象不可扩展,则无法定义属性
- 如果目标对象有一个可配置的属性,则不能添加同名的不可配置属性
- 如果目标对象有一个不可配置的属性,则不能添加同名的可配置属性
const myTarget4 = {}
const proxy7 = new Proxy(myTarget4, {
defineProperty(target, property, descriptor) {
console.log(target) // {},目标对象
console.log(property) // 'foo',目标对象键属性
console.log(descriptor) // { value: 'bar' },描述符对象
console.log(Reflect.defineProperty(...arguments)) // true,属性定义成功
return Reflect.defineProperty(...arguments)
},
})
Object.defineProperty(proxy7, 'foo', { value: 'bar' }) // 拦截的操作
console.log(proxy7.foo) // 'bar'
console.log(myTarget4.foo) // 'bar'
// 捕获器不变式
Object.defineProperty(myTarget4, 'fox', {
value: 'baz',
configurable: false, // 不可配置
})
const proxy8 = new Proxy(myTarget4, {
defineProperty() {
return true
},
})
Object.defineProperty(proxy8, 'fox', {
value: 'qux',
configurable: true,
writable: true,
}) // TypeError
getOwnPropertyDescriptor()
在
Object.getOwnPropertyDescriptor()
中被调用,对应的反射 API 方法为Reflect.getOwnPropertyDescriptor(target, property)
返回值
- 属性的描述符对象,属性不存在时返回
undefined
- 属性的描述符对象,属性不存在时返回
拦截的操作
Object.getOwnPropertyDescriptor(proxy, property)
Reflect.getOwnPropertyDescriptor(proxy, property)
捕获器处理程序参数
target
:目标对象property
:目标对象的键属性(字符串键或符号键)
捕获器不变式
- 如果自有的
target.property
存在且不可配置,则处理程序必须返回一个表示该属性存在的对象 - 如果自有的
target.property
存在且可配置,则处理程序必须返回该属性可可配置的对象 - 如果自有的
target.property
存在且目标对象不可扩展,则处理程序必须返回一个表示该属性存在的对象 - 如果
target.property
不存在且目标对象不可扩展,则处理程序必须返回undefined
表示改属性不存在 - 如果
target.property
不存在,则处理程序不能返回表示该属性存在的对象
- 如果自有的
const myTarget5 = { foo: 'bar' }
const proxy9 = new Proxy(myTarget5, {
getOwnPropertyDescriptor(target, property) {
console.log(target) // { foo: 'bar' },目标对象
console.log(property) // 'foo',目标对象键属性
console.log(Reflect.getOwnPropertyDescriptor(...arguments)) // { value: 'bar', writable: true, enumerable: true, configurable: true },属性的描述符对象
return Reflect.getOwnPropertyDescriptor(...arguments)
},
})
Object.getOwnPropertyDescriptor(proxy9, 'foo') // 拦截的操作
// 捕获器不变式
Object.defineProperty(myTarget5, 'fox', {
value: 'baz', // 目标对象的属性存在
configurable: false, // 不可配置
})
const proxy10 = new Proxy(myTarget5, {
getOwnPropertyDescriptor() {
// return undefined // TypeError,必须返回一个表示该属性存在的对象
return {
value: 'baz',
writable: false,
enumerable: false,
configurable: false,
}
},
})
Object.getOwnPropertyDescriptor(proxy10, 'fox')
deleteProperty()
在delete 操作符中被调用,对应的反射 API 方法为
Reflect.deleteProperty(target, property)
返回值
- 布尔值,表示删除属性是否成功,非布尔值会被转型为布尔值
拦截的操作
delete proxy.property
delete proxy[property]
Reflect.delete(proxy,property)
捕获器处理程序参数
target
:目标对象property
:目标对象的键属性(字符串键或符号键)
捕获器不变式
- 如果
target.property
存在且不可配置,则处理程序不能删除这个属性
- 如果
const myTarget6 = { foo: 'bar' }
const proxy11 = new Proxy(myTarget6, {
deleteProperty(target, property) {
console.log(target) // { foo: 'bar' },目标对象
console.log(property) // 'foo',目标对象键属性
console.log(Reflect.deleteProperty(...arguments)) // true,删除属性成功
return Reflect.deleteProperty(...arguments)
},
})
delete proxy11.foo // 拦截的操作
console.log(proxy11.foo) // undefined
console.log(myTarget6.foo) // undefined
// 捕获器不变式
Object.defineProperty(myTarget6, 'fox', {
value: 'baz', // 目标对象的属性存在
configurable: false, // 不可配置
})
const proxy12 = new Proxy(myTarget6, {
deleteProperty() {
console.log(Reflect.deleteProperty(...arguments)) // false,删除属性失败
return Reflect.deleteProperty(...arguments)
},
})
delete proxy12.fox
console.log(proxy12.fox) // 'baz'
console.log(myTarget6.fox) // 'baz'
ownKeys()
在
Object.keys()
及类似方法中被调用,对应的反射 API 方法为Reflect.ownKeys(target)
返回值
- 必须返回包含字符串或符号的可枚举对象
拦截的操作
Object.getOwnPropertyNames(proxy)
Object.getOwnPropertySymbols(proxy)
Object.keys(proxy)
Reflect.ownKeys(proxy)
捕获器处理程序参数
target
:目标对象
捕获器不变式
- 返回的可枚举对象必须包含
target
的所有不可配置的自有属性 - 如果
target
不可扩展,则返回可枚举对象必须准确包含自有属性键
- 返回的可枚举对象必须包含
Object.defineProperty(myTarget7, 'fox', {
value: 'baz',
configurable: false, // 不可配置
})
const proxy14 = new Proxy(myTarget7, {
ownKeys() {
// return [] // TypeError: 'ownKeys' on proxy: trap result did not include 'fox',必须包含目标对象所有不可配置的自有属性
return ['fox', 'qux'] // 必须包含fox键
},
})
Object.keys(proxy14)
getPrototypeOf()
在
Object.getPrototypeOf()
中被调用,对应的反射 API 方法为Reflect.getPrototypeOf(target)
返回值
- 必须返回对象或 null
拦截的操作
Object.getPrototypeOf(proxy)
Reflect.getPrototypeOf(proxy)
proxy.__proto__
Object.prototype.isPrototyprOf(proxy)
proxy instanceof Object
捕获器处理程序参数
target
:目标对象
捕获器不变式
- 如果
target
不可扩展,则Object.getPrototypeOf(proxy)
唯一有效的返回值就是Object.getPrototypeOf(target)
的返回值
- 如果
const myTarget8 = new Array(1, 2, 3)
const proxy15 = new Proxy(myTarget8, {
getPrototypeOf(target) {
console.log(target) // [ 1, 2, 3 ],目标对象
console.log(Reflect.getPrototypeOf(...arguments)) // Array的原型对象,可在浏览器中查看
return Reflect.getPrototypeOf(...arguments)
},
})
Object.getPrototypeOf(proxy15) // 拦截的操作
// 捕获器不变式
Object.preventExtensions(myTarget8) // 不可扩展
const proxy16 = new Proxy(myTarget8, {
getPrototypeOf(target) {
// return [] // TypeError,唯一有效的返回值是Object.getPrototypeOf(target)的返回值
return Object.getPrototypeOf(target) // 有效
},
})
Object.getPrototypeOf(proxy16)
setPrototypeOf()
在
Object.setPrototypeOf()
中被调用,对应的反射 API 方法为Reflect.setPrototypeOf(target, prototype)
返回值
- 布尔值,表示原型赋值是否成功,非布尔值会被转型为布尔值
拦截的操作
Object.setPrototypeOf(proxy)
Reflect.setPrototypeOf(proxy)
捕获器处理程序参数
target
:目标对象prototype
:target
的替代原型,顶级原型则为null
捕获器不变式
- 如果
target.property
不可扩展,则唯一有效的prototype
参数就是Object.getPrototypeOf(target)
的返回值
- 如果
const myTarget9 = {}
const targetPrototype = { foo: 'bar' }
const proxy17 = new Proxy(myTarget9, {
setPrototypeOf(target, prototype) {
console.log(target) //{},目标对象
console.log(targetPrototype) // { foo: 'bar' },target的替代原型
console.log(Reflect.setPrototypeOf(...arguments)) // true,原型赋值成功
return Reflect.setPrototypeOf(...arguments)
},
})
Object.setPrototypeOf(proxy17, targetPrototype)
console.log(proxy17) // {}
console.log(proxy17.__proto__ === targetPrototype) // true
console.log(proxy17.foo) // 'bar'
console.log(myTarget9.foo) // 'bar'
// 捕获器不变式
Object.preventExtensions(myTarget9) // 不可扩展
const proxy18 = new Proxy(myTarget9, {
setPrototypeOf() {
return Reflect.setPrototypeOf(...arguments)
},
})
// Object.setPrototypeOf(proxy18, targetPrototype) // TypeError,唯一有效的prototype参数是Object.getPrototypeOf(target)的返回值
Object.setPrototypeOf(proxy18, Object.getPrototypeOf(myTarget9)) // 有效
console.log(proxy18.__proto__ === Object.getPrototypeOf(myTarget9)) // true
isExtensible()
在
Object.isExtensible()
中被调用,对应的反射 API 方法为Reflect.isExtensible(target)
返回值
- 布尔值,表示
target
是否可被扩展,非布尔值会被转型为布尔值
- 布尔值,表示
拦截的操作
Object.isExtensible(proxy)
Reflect.isExtensible(proxy)
捕获器处理程序参数
target
:目标对象
捕获器不变式
- 如果
target
可扩展,则处理程序必须返回true
- 如果
target
不可扩展,则处理程序必须返回false
- 如果
const myTarget10 = {}
const proxy19 = new Proxy(myTarget10, {
isExtensible(target) {
console.log(target) // {},目标对象
console.log(Reflect.isExtensible(...arguments)) // true,target可扩展
return Reflect.isExtensible(...arguments)
},
})
Object.isExtensible(proxy19) // 拦截的操作
// 捕获器不变式
Object.preventExtensions(myTarget10) // 不可扩展
const proxy20 = new Proxy(myTarget10, {
isExtensible() {
// return true // TypeError,不可扩展必须返回false
console.log(Reflect.isExtensible(...arguments)) // false,target不可扩展
return false
},
})
Object.isExtensible(proxy20)
preventExtensions()
在
Object.preventExtensions()
中被调用,对应的反射 API 方法为Reflect.preventExtensions(target)
返回值
- 布尔值,表示
target
是否已经不可扩展,非布尔值会被转型为布尔值
- 布尔值,表示
拦截的操作
Object.preventExtensions(proxy)
Reflect.preventExtensions(proxy)
捕获器处理程序参数
target
:目标对象
捕获器不变式
- 如果
Object.isExtensions(target)
是false
,则处理程序必须返回true
- 如果
const myTarget11 = {}
const proxy21 = new Proxy(myTarget11, {
preventExtensions(target) {
console.log(target) // {},目标对象
console.log(Reflect.preventExtensions(...arguments)) // true,target已经不可扩展
return Reflect.preventExtensions(...arguments)
},
})
Object.preventExtensions(proxy21) // 拦截的操作
// 捕获器不变式
Object.preventExtensions(myTarget11) // 不可扩展
console.log(Object.isExtensible(myTarget11)) // false,目标对象已经不可扩展
const proxy22 = new Proxy(myTarget11, {
preventExtensions() {
// return false // TypeError,处理程序此时必须返回true
return true
},
})
Object.preventExtensions(proxy22)
apply()
在调用函数时被调用,对应的反射 API 方法为
Reflect.apply(target, thisArg, argumentsList)
返回值
- 返回值无限制
拦截的操作
proxy(...argumentsList)
Function.prototype.apply(thisArg, argumentsList)
Function.prototype.call(thisArg, ...argumentsList)
Reflect.apply(proxy, thisArg, argumentsList)
捕获器处理程序参数
target
:目标对象thisArg
:调用函数时的 this 参数argumentsList
:调用函数时的参数列表
捕获器不变式
target
必须是一个函数对象
const myTarget12 = () => {}
const targetApply = { foo: 'bar' }
const proxy23 = new Proxy(myTarget12, {
apply(target, thisArg, argumentsList) {
console.log(target) // [Function: myTarget12],目标对象
console.log(thisArg) // { foo: 'bar' },调用函数时的 this 参数
console.log(argumentsList) // [ 1, 2, 3 ],调用函数时的参数列表
return Reflect.apply(...arguments)
},
})
Reflect.apply(proxy23, targetApply, [1, 2, 3]) // 拦截的操作
// 捕获器不变式
const proxy24 = new Proxy(targetApply, {
apply() {
return Reflect.apply(...arguments)
},
})
// proxy24() // TypeError: proxy24 is not a function,target必须是函数对象
construct()
在new 操作符时被调用,对应的反射 API 方法为
Reflect.construct(target, argumentsList, newTarget)
返回值
- 必须返回一个对象
拦截的操作
new proxy(...argumentsList)
Reflect.construct(proxy, argumentsList, newTarget)
捕获器处理程序参数
target
:目标构造函数argumentsList
:传给目标构造函数的参数列表newTarget
:最初被调用的构造函数
捕获器不变式
target
必须可以用作构造函数
const MyTarget13 = function () {}
const proxy25 = new Proxy(MyTarget13, {
construct(target, argumentsList, newTarget) {
console.log(target) // [Function: MyTarget13],目标构造函数
console.log(argumentsList) // [ 123 ],传给目标构造函数的参数列表
console.log(newTarget) // [Function: MyTarget13],最初被调用的构造函数
return Reflect.construct(...arguments)
},
})
new proxy25(123) // 拦截的操作
const MyTarget14 = () => {} // 箭头函数,不能用作构造函数
const proxy26 = new Proxy(MyTarget14, {
construct() {
return Reflect.construct(...arguments)
},
})
new proxy26() // TypeError: proxy26 is not a constructor,target必须可以用作构造函数
总结 & 问点
捕获器 | 被调用 | 反射 API | 返回值 | 拦截操作 | 参数 | 不变式 |
---|---|---|---|---|---|---|
get() | 获取属性值 | Reflect.get() | 无限制 | proxy.property proxy[property] Object.create(proxy)[property] Reflect.get(proxy,property,receiver) | target :目标对象property :目标对象的键属性(字符串键或符号键)receiver :代理对象或继承代理对象的对象 | 如果target.property 不可写且不可配,处理程序返回值必须与target.property 匹配如果 target.property 不可配置且[[Get]] 特性为undefined ,处理程序返回值必须是undefined |
set() | 设置属性值 | Reflect.set() | 布尔值,配置是否成功 | proxy.property = value proxy[property] = value Object.create(proxy)[property] = value Reflect.get(proxy,property,receiver) = value | target :目标对象property :目标对象的键属性(字符串键或符号键)value :要赋给属性的值receiver :代理对象或继承代理对象的对象 | 如果target.property 不可写且不可配,则不能修改目标属性的值如果 target.property 不可配置且[[Set]] 特性为undefined ,则不能修改目标属性的值严格模式下,处理程序返回 false 会抛出 TypeError |
has() | in 操作符 | Reflect.has() | 布尔值,属性是否存在 | property in proxy property in Object.create(proxy) with(proxy) {(property)} Reflect.has(proxy,property) | target :目标对象property :目标对象的键属性(字符串键或符号键) | 如果target.property 存在且不可配置,则处理程序必须返回true 如果 target.property 存在且目标对象不可扩展,则处理程序必须返回true |
defineProperty() | Object.defineProperty() | Reflect.defineProperty() | 布尔值,属性是否成功定义 | Object.defineProperty(proxy, property, descriptor) Reflect.defineProperty(proxy, property, descriptor) | target :目标对象property :目标对象的键属性(字符串键或符号键) | 如果目标对象不可扩展,则无法定义属性 如果目标对象有一个可配置的属性,则不能添加同名的不可配置属性 如果目标对象有一个不可配置的属性,则不能添加同名的可配置属性 |
getOwnPropertyDescriptor() | Object.getOwnPropertyDescriptor() | Reflect.getOwnPropertyDescriptor() | 属性的描述符对象,属性不存在时返回undefined | Object.getOwnPropertyDescriptor(proxy, property) Reflect.getOwnPropertyDescriptor(proxy, property) | target :目标对象property :目标对象的键属性(字符串键或符号键) | 如果自有的target.property 存在且不可配置,则处理程序必须返回一个表示该属性存在的对象如果自有的 target.property 存在且可配置,则处理程序必须返回该属性可可配置的对象如果自有的 target.property 存在且目标对象不可扩展,则处理程序必须返回一个表示该属性存在的对象如果 target.property 不存在且目标对象不可扩展,则处理程序必须返回undefined 表示改属性不存在如果 target.property 不存在,则处理程序不能返回表示该属性存在的对象 |
deleteProperty() | delete 操作符 | Reflect.deleteProperty() | 布尔值,删除属性是否成功 | delete proxy.property delete proxy[property] Reflect.delete(proxy,property) | target :目标对象property :目标对象的键属性(字符串键或符号键) | 如果target.property 存在且不可配置,则处理程序不能删除这个属性 |
ownKeys() | Object.keys() 及类似方法 | Reflect.ownKeys() | 包含字符串或符号的可枚举对象 | Object.getOwnPropertyNames(proxy) Object.getOwnPropertySymbols(proxy) Object.keys(proxy) Reflect.ownKeys(proxy) | target :目标对象 | 返回的可枚举对象必须包含target 的所有不可配置的自有属性如果 target 不可扩展,则返回可枚举对象必须准确包含自有属性键 |
getPrototypeOf() | Object.getPrototypeOf() | Reflect.getPrototypeOf() | 对象或 null | Object.getPrototypeOf(proxy) Reflect.getPrototypeOf(proxy) proxy.__proto__ Object.prototype.isPrototyprOf(proxy) proxy instanceof Object | target :目标对象 | 如果target 不可扩展,则Object.getPrototypeOf(proxy) 唯一有效的返回值就是Object.getPrototypeOf(target) 的返回值 |
setPrototypeOf() | Object.setPrototypeOf() | Reflect.setPrototypeOf() | 布尔值,原型赋值是否成功 | Object.setPrototypeOf(proxy) Reflect.setPrototypeOf(proxy) | target :目标对象prototype :target 的替代原型,顶级原型则为null | 如果target.property 不可扩展,则唯一有效的prototype 参数就是Object.getPrototypeOf(target) 的返回值 |
isExtensible() | Object.isExtensible() | Reflect.isExtensible() | 布尔值,target 是否可被扩展 | Object.isExtensible(proxy) Reflect.isExtensible(proxy) | target :目标对象 | 如果target 可扩展,则处理程序必须返回true 如果 target 不可扩展,则处理程序必须返回false |
preventExtensions() | Object.preventExtensions() | Reflect.preventExtensions() | 布尔值,target 是否不可扩展 | Object.preventExtensions(proxy) Reflect.preventExtensions(proxy) | target :目标对象 | 如果Object.isExtensions(target) 是false ,则处理程序必须返回true |
apply() | 调用函数 | Reflect.apply() | 无限制 | proxy(...argumentsList) Function.prototype.apply(thisArg, argumentsList) Function.prototype.call(thisArg, ...argumentsList) Reflect.apply(proxy, thisArg, argumentsList) | target :目标对象thisArg :调用函数时的 this 参数argumentsList :调用函数时的参数列表 | target 必须是一个函数对象 |
construct() | new 操作符 | Reflect.construct() | 一个对象 | new proxy(...argumentsList) Reflect.construct(proxy, argumentsList, newTarget) | target :目标构造函数argumentsList :传给目标构造函数的参数列表newTarget :最初被调用的构造函数 | target 必须可以用作构造函数 |
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。