JavaScript高级程序设计(第4版)
上QQ阅读APP看书,第一时间看更新

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:引用的目标对象上的字符串键属性。严格来讲,property参数除了字符串键,也可能是符号(symbol)键。后面几处也一样。——译者注

❑ 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必须可以用作构造函数。