9.2 代理捕获器与反射方法
代理可以捕获13种不同的基本操作。这些操作有各自不同的反射API方法、参数、关联ECMAScript操作和不变式。
正如前面示例所展示的,有几种不同的JavaScript操作会调用同一个捕获器处理程序。不过,对于在代理对象上执行的任何一种操作,只会有一个捕获处理程序被调用。不会存在重复捕获的情况。
只要在代理上调用,所有捕获器都会拦截它们对应的反射API操作。
9.2.1 get()
get()捕获器会在获取属性值的操作中被调用。对应的反射API方法为Reflect.get()。
const myTarget = {}; const proxy = new Proxy(myTarget, { get(target, property, receiver) { console.log('get()'); return Reflect.get(...arguments) } }); proxy.foo; // get()
1.返回值
返回值无限制。
2.拦截的操作
❑ proxy.property
❑ proxy[property]
❑ Object.create(proxy)[property]
❑ Reflect.get(proxy, property, receiver)
3.捕获器处理程序参数
❑ target:目标对象。
❑ property:引用的目标对象上的字符串键属性。
❑ receiver:代理对象或继承代理对象的对象。
4.捕获器不变式
如果target.property不可写且不可配置,则处理程序返回的值必须与target.property匹配。
如果target.property不可配置且[[Get]]特性为undefined,处理程序的返回值也必须是undefined。
9.2.2 set()
set()捕获器会在设置属性值的操作中被调用。对应的反射API方法为Reflect.set()。
const myTarget = {}; const proxy = new Proxy(myTarget, { set(target, property, value, receiver) { console.log('set()'); return Reflect.set(...arguments) } }); proxy.foo = 'bar'; // set()
1.返回值
返回true表示成功;返回false表示失败,严格模式下会抛出TypeError。
2.拦截的操作
❑ proxy.property = value
❑ proxy[property] = value
❑ Object.create(proxy)[property] = value
❑ Reflect.set(proxy, property, value, receiver)
3.捕获器处理程序参数
❑ target:目标对象。
❑ property:引用的目标对象上的字符串键属性。
❑ value:要赋给属性的值。
❑ receiver:接收最初赋值的对象。
4.捕获器不变式
如果target.property不可写且不可配置,则不能修改目标属性的值。
如果target.property不可配置且[[Set]]特性为undefined,则不能修改目标属性的值。
在严格模式下,处理程序中返回false会抛出TypeError。
9.2.3 has()
has()捕获器会在in操作符中被调用。对应的反射API方法为Reflect.has()。
const myTarget = {}; const proxy = new Proxy(myTarget, { has(target, property) { console.log('has()'); return Reflect.has(...arguments) } }); 'foo' in proxy; // has()
1.返回值
has()必须返回布尔值,表示属性是否存在。返回非布尔值会被转型为布尔值。
2.拦截的操作
❑ property in proxy
❑ property in Object.create(proxy)
❑ with(proxy) {(property); }
❑ Reflect.has(proxy, property)
3.捕获器处理程序参数
❑ target:目标对象。
❑ property:引用的目标对象上的字符串键属性。
4.捕获器不变式
如果target.property存在且不可配置,则处理程序必须返回true。
如果target.property存在且目标对象不可扩展,则处理程序必须返回true。
9.2.4 defineProperty()
defineProperty()捕获器会在Object.defineProperty()中被调用。对应的反射API方法为Reflect.defineProperty()。
const myTarget = {}; const proxy = new Proxy(myTarget, { defineProperty(target, property, descriptor) { console.log('defineProperty()'); return Reflect.defineProperty(...arguments) } }); Object.defineProperty(proxy, 'foo', { value: 'bar' }); // defineProperty()
1.返回值
defineProperty()必须返回布尔值,表示属性是否成功定义。返回非布尔值会被转型为布尔值。
2.拦截的操作
❑ Object.defineProperty(proxy, property, descriptor)
❑ Reflect.defineProperty(proxy, property, descriptor)
3.捕获器处理程序参数
❑ target:目标对象。
❑ property:引用的目标对象上的字符串键属性。
❑ descriptor:包含可选的enumerable、configurable、writable、value、get和set定义的对象。
4.捕获器不变式
如果目标对象不可扩展,则无法定义属性。
如果目标对象有一个可配置的属性,则不能添加同名的不可配置属性。
如果目标对象有一个不可配置的属性,则不能添加同名的可配置属性。
9.2.5 getOwnPropertyDescriptor()
getOwnPropertyDescriptor()捕获器会在Object.getOwnPropertyDescriptor()中被调用。对应的反射API方法为Reflect.getOwnPropertyDescriptor()。
const myTarget = {}; const proxy = new Proxy(myTarget, { getOwnPropertyDescriptor(target, property) { console.log('getOwnPropertyDescriptor()'); return Reflect.getOwnPropertyDescriptor(...arguments) } }); Object.getOwnPropertyDescriptor(proxy, 'foo'); // getOwnPropertyDescriptor()
1.返回值
getOwnPropertyDescriptor()必须返回对象,或者在属性不存在时返回undefined。
2.拦截的操作
❑ Object.getOwnPropertyDescriptor(proxy, property)
❑ Reflect.getOwnPropertyDescriptor(proxy, property)
3.捕获器处理程序参数
❑ target:目标对象。
❑ property:引用的目标对象上的字符串键属性。
4.捕获器不变式
如果自有的target.property存在且不可配置,则处理程序必须返回一个表示该属性存在的对象。
如果自有的target.property存在且可配置,则处理程序必须返回表示该属性可配置的对象。
如果自有的target.property存在且target不可扩展,则处理程序必须返回一个表示该属性存在的对象。
如果target.property不存在且target不可扩展,则处理程序必须返回undefined表示该属性不存在。
如果target.property不存在,则处理程序不能返回表示该属性可配置的对象。
9.2.6 deleteProperty()
deleteProperty()捕获器会在delete操作符中被调用。对应的反射API方法为Reflect. deleteProperty()。
const myTarget = {}; const proxy = new Proxy(myTarget, { deleteProperty(target, property) { console.log('deleteProperty()'); return Reflect.deleteProperty(...arguments) } }); delete proxy.foo // deleteProperty()
1.返回值
deleteProperty()必须返回布尔值,表示删除属性是否成功。返回非布尔值会被转型为布尔值。
2.拦截的操作
❑ delete proxy.property
❑ delete proxy[property]
❑ Reflect.deleteProperty(proxy, property)
3.捕获器处理程序参数
❑ target:目标对象。
❑ property:引用的目标对象上的字符串键属性。
4.捕获器不变式
如果自有的target.property存在且不可配置,则处理程序不能删除这个属性。
9.2.7 ownKeys()
ownKeys()捕获器会在Object.keys()及类似方法中被调用。对应的反射API方法为Reflect. ownKeys()。
const myTarget = {}; const proxy = new Proxy(myTarget, { ownKeys(target) { console.log('ownKeys()'); return Reflect.ownKeys(...arguments) } }); Object.keys(proxy); // ownKeys()
1.返回值
ownKeys()必须返回包含字符串或符号的可枚举对象。
2.拦截的操作
❑ Object.getOwnPropertyNames(proxy)
❑ Object.getOwnPropertySymbols(proxy)
❑ Object.keys(proxy)
❑ Reflect.ownKeys(proxy)
3.捕获器处理程序参数
❑ target:目标对象。
4.捕获器不变式
返回的可枚举对象必须包含target的所有不可配置的自有属性。
如果target不可扩展,则返回可枚举对象必须准确地包含自有属性键。
9.2.8 getPrototypeOf()
getPrototypeOf()捕获器会在Object.getPrototypeOf()中被调用。对应的反射API方法为Reflect.getPrototypeOf()。
const myTarget = {}; const proxy = new Proxy(myTarget, { getPrototypeOf(target) { console.log('getPrototypeOf()'); return Reflect.getPrototypeOf(...arguments) } }); Object.getPrototypeOf(proxy); // getPrototypeOf()
1.返回值
getPrototypeOf()必须返回对象或null。
2.拦截的操作
❑ Object.getPrototypeOf(proxy)
❑ Reflect.getPrototypeOf(proxy)
❑ proxy.__proto__
❑ Object.prototype.isPrototypeOf(proxy)
❑ proxy instanceof Object
3.捕获器处理程序参数
❑ target:目标对象。
4.捕获器不变式
如果target不可扩展,则Object.getPrototypeOf(proxy)唯一有效的返回值就是Object. getPrototypeOf(target)的返回值。
9.2.9 setPrototypeOf()
setPrototypeOf()捕获器会在Object.setPrototypeOf()中被调用。对应的反射API方法为Reflect.setPrototypeOf()。
const myTarget = {}; const proxy = new Proxy(myTarget, { setPrototypeOf(target, prototype) { console.log('setPrototypeOf()'); return Reflect.setPrototypeOf(...arguments) } }); Object.setPrototypeOf(proxy, Object); // setPrototypeOf()
1.返回值
setPrototypeOf()必须返回布尔值,表示原型赋值是否成功。返回非布尔值会被转型为布尔值。
2.拦截的操作
❑ Object.setPrototypeOf(proxy)
❑ Reflect.setPrototypeOf(proxy)
3.捕获器处理程序参数
❑ target:目标对象。
❑ prototype:target的替代原型,如果是顶级原型则为null。
4.捕获器不变式
如果target不可扩展,则唯一有效的prototype参数就是Object.getPrototypeOf(target)的返回值。
9.2.10 isExtensible()
isExtensible()捕获器会在Object.isExtensible()中被调用。对应的反射API方法为Reflect.isExtensible()。
const myTarget = {}; const proxy = new Proxy(myTarget, { isExtensible(target) { console.log('isExtensible()'); return Reflect.isExtensible(...arguments) } }); Object.isExtensible(proxy); // isExtensible()
1.返回值
isExtensible()必须返回布尔值,表示target是否可扩展。返回非布尔值会被转型为布尔值。
2.拦截的操作
❑ Object.isExtensible(proxy)
❑ Reflect.isExtensible(proxy)
3.捕获器处理程序参数
❑ target:目标对象。
4.捕获器不变式
如果target可扩展,则处理程序必须返回true。
如果target不可扩展,则处理程序必须返回false。
9.2.11 preventExtensions()
preventExtensions()捕获器会在Object.preventExtensions()中被调用。对应的反射API方法为Reflect.preventExtensions()。
const myTarget = {}; const proxy = new Proxy(myTarget, { preventExtensions(target) { console.log('preventExtensions()'); return Reflect.preventExtensions(...arguments) } }); Object.preventExtensions(proxy); // preventExtensions()
1.返回值
preventExtensions()必须返回布尔值,表示target是否已经不可扩展。返回非布尔值会被转型为布尔值。
2.拦截的操作
❑ Object.preventExtensions(proxy)
❑ Reflect.preventExtensions(proxy)
3.捕获器处理程序参数
❑ target:目标对象。
4.捕获器不变式
如果Object.isExtensible(proxy)是false,则处理程序必须返回true。
9.2.12 apply()
apply()捕获器会在调用函数时中被调用。对应的反射API方法为Reflect.apply()。
const myTarget = () => {}; const proxy = new Proxy(myTarget, { apply(target, thisArg, ...argumentsList) { console.log('apply()'); return Reflect.apply(...arguments) } }); proxy(); // apply()
1.返回值
返回值无限制。
2.拦截的操作
❑ proxy(...argumentsList)
❑ Function.prototype.apply(thisArg, argumentsList)
❑ Function.prototype.call(thisArg, ...argumentsList)
❑ Reflect.apply(target, thisArgument, argumentsList)
3.捕获器处理程序参数
❑ target:目标对象。
❑ thisArg:调用函数时的this参数。
❑ argumentsList:调用函数时的参数列表
4.捕获器不变式
target必须是一个函数对象。
9.2.13 construct()
construct()捕获器会在new操作符中被调用。对应的反射API方法为Reflect.construct()。
const myTarget = function() {}; const proxy = new Proxy(myTarget, { construct(target, argumentsList, newTarget) { console.log('construct()'); return Reflect.construct(...arguments) } }); new proxy; // construct()
1.返回值
construct()必须返回一个对象。
2.拦截的操作
❑ new proxy(...argumentsList)
❑ Reflect.construct(target, argumentsList, newTarget)
3.捕获器处理程序参数
❑ target:目标构造函数。
❑ argumentsList:传给目标构造函数的参数列表。
❑ newTarget:最初被调用的构造函数。
4.捕获器不变式
target必须可以用作构造函数。