关注前端小讴,浏览更多原创技术文章

代理捕捉器与反射办法

  • 代理能够捕捉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:描述符对象(描述符对象的属性:configurableenumerablewritablevaluegetset的一个或多个)
    • 捕捉器不变式

      • 如果指标对象不可扩大,则无奈定义属性
      • 如果指标对象有一个可配置的属性,则不能增加同名的不可配置属性
      • 如果指标对象有一个不可配置的属性,则不能增加同名的可配置属性
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) // undefinedconsole.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.foxconsole.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:指标对象
      • prototypetarget的代替原型,顶级原型则为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) // trueconsole.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()属性的描述符对象,属性不存在时返回undefinedObject.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()对象或 nullObject.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:指标对象
prototypetarget的代替原型,顶级原型则为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必须能够用作构造函数