08 Proxy

51 阅读7分钟
│   ├── Proxy 实例的方法
│   │     └─ get()
│   │         └─ `get`方法用于拦截某个属性的读取操作,可以接受三个参数,依次为目标对象、属性名和 proxy 实例本身(严格地说,是操作行为所针对的对象),其中最后一个参数可选。
│   │         └─ 如果访问目标对象不存在的属性,会抛出一个错误。如果没有这个拦截函数,访问不存在的属性,只会返回`undefined``get`方法可以继承。
│   │         └─ 利用 Proxy,可以将读取属性的操作(`get`),转变为执行某个函数,从而实现属性的链式操作。
│   │     └─ apply()
│   │         └─ `apply`方法拦截函数的调用、`call``apply`操作。 另外,直接调用`Reflect.apply`方法,也会被拦截。
│   │         └─ `apply`方法可以接受三个参数,分别是目标对象、目标对象的上下文对象(`this`)和目标对象的参数数组。
│   │     └─ has()
│   │         └─ `has()` 方法用来拦截 `HasProperty`操作,即判断对象是否具有某个属性时,这个方法会生效。典型的操作就是`in`运算符。
│   │         └─ `has()`方法可以接受两个参数,分别是目标对象、需查询的属性名。
│   │         └─ Object.preventExtensions() 方法让一个对象变的不可扩展,也就是永远不能再添加新的属性
│   │         └─ `has()`方法拦截的是`HasProperty`操作,而不是`HasOwnProperty`操作,即`has()`方法不判断一个属性是对象自身的属性,还是继承的属性。虽然`for...in`循环也用到了`in`运算符,但是`has()`拦截对`for...in`循环不生效。
│   │     └─ construct()
│   │         └─ `construct()`方法用于拦截`new`命令,下面是拦截对象的写法。
│   │         └─ `construct()`方法可以接受三个参数。 - `target`:目标对象。 - `args`:构造函数的参数数组。 
│   │         └─ `construct()`方法返回的必须是一个对象,否则会报错。另外,由于`construct()`拦截的是构造函数,所以它的目标对象必须是函数,否则就会报错。
│   │         └─ `construct()`方法中的`this`指向的是`handler`,而不是实例对象。
│   │     └─ deleteProperty()
│   │         └─ `deleteProperty`方法用于拦截`delete`操作,如果这个方法抛出错误或者返回`false`,当前属性就无法被`delete`命令删除。
│   │         └─ 注意,目标对象自身的不可配置(configurable)的属性,不能被`deleteProperty`方法删除,否则报错。
│   │     └─ defineProperty()
│   │         └─ `defineProperty()`方法拦截了`Object.defineProperty()`操作。
│   │         └─ `defineProperty()`方法内部没有任何操作,只返回`false`,导致添加新属性总是无效。注意,这里的`false`只是用来提示操作失败,本身并不能阻止添加新属性。
│   │         └─ 如果目标对象不可扩展(non-extensible),则`defineProperty()`不能增加目标对象上不存在的属性,否则会报错。另外,如果目标对象的某个属性不可写(writable)或不可配置(configurable),则`defineProperty()`方法不得改变这两个设置。
│   │     └─ getOwnPropertyDescriptor()
│   │         └─ `getOwnPropertyDescriptor()`方法拦截`Object.getOwnPropertyDescriptor()`,返回一个属性描述对象或者`undefined`。
│   │     └─ getPrototypeOf()
│   │         └─ `getPrototypeOf()`方法主要用来拦截获取对象原型。具体来说,拦截下面这些操作。
│   │             └─ - `Object.prototype.__proto__`
│   │             └─ - `Object.prototype.isPrototypeOf()`
│   │             └─ - `Object.getPrototypeOf()`
│   │             └─ - `Reflect.getPrototypeOf()`
│   │             └─ - `instanceof`
│   │         └─ `getPrototypeOf()`方法的返回值必须是对象或者`null`,否则报错。另外,如果目标对象不可扩展(non-extensible), `getPrototypeOf()`方法必须返回目标对象的原型对象。
│   │     └─ isExtensible()
│   │         └─ `isExtensible()`方法拦截`Object.isExtensible()`操作。
│   │         └─ 该方法只能返回布尔值,否则返回值会被自动转为布尔值。这个方法有一个强限制,它的返回值必须与目标对象的`isExtensible`属性保持一致,否则就会抛出错误。
│   │     └─ ownKeys()
│   │         └─ `ownKeys()`方法用来拦截对象自身属性的读取操作。
│   │             └─ - `Object.getOwnPropertyNames()`
│   │             └─ - `Object.getOwnPropertySymbols()`
│   │             └─ - `Object.keys()`
│   │             └─ - `for...in`循环
│   │         └─ 使用`Object.keys()`方法时,有三类属性会被`ownKeys()`方法自动过滤,不会返回。
│   │             └─ 目标对象上不存在的属性
│   │             └─ 属性名为 Symbol 值
│   │             └─ 不可遍历(`enumerable`)的属性
│   │             └─ 
│   │         └─ `ownKeys()`方法返回的数组成员,只能是字符串或 Symbol 值。如果有其他类型的值,或者返回的根本不是数组,就会报错。
│   │     └─ preventExtensions()
│   │         └─ `preventExtensions()`方法拦截`Object.preventExtensions()`。该方法必须返回一个布尔值,否则会被自动转为布尔值。
│   │         └─ 这个方法有一个限制,只有目标对象不可扩展时(即`Object.isExtensible(proxy)``false`),`proxy.preventExtensions`才能返回`true`,否则会报错。
│   │     └─ setPrototypeOf()
│   │         └─ `setPrototypeOf()`方法主要用来拦截`Object.setPrototypeOf()`方法。
│   │         └─ 如果目标对象不可扩展(non-extensible),`setPrototypeOf()`方法不得改变目标对象的原型。
│   ├── Proxy 实例的方法
│   │     └─ get()
│   │         └─ `get`方法用于拦截某个属性的读取操作,可以接受三个参数,依次为目标对象、属性名和 proxy 实例本身(严格地说,是操作行为所针对的对象),其中最后一个参数可选。
│   │         └─ 如果访问目标对象不存在的属性,会抛出一个错误。如果没有这个拦截函数,访问不存在的属性,只会返回`undefined``get`方法可以继承。
│   │         └─ 利用 Proxy,可以将读取属性的操作(`get`),转变为执行某个函数,从而实现属性的链式操作。
│   │     └─ apply()
│   │         └─ `apply`方法拦截函数的调用、`call``apply`操作。 另外,直接调用`Reflect.apply`方法,也会被拦截。
│   │         └─ `apply`方法可以接受三个参数,分别是目标对象、目标对象的上下文对象(`this`)和目标对象的参数数组。
│   │     └─ has()
│   │         └─ `has()` 方法用来拦截 `HasProperty`操作,即判断对象是否具有某个属性时,这个方法会生效。典型的操作就是`in`运算符。
│   │         └─ `has()`方法可以接受两个参数,分别是目标对象、需查询的属性名。
│   │         └─ Object.preventExtensions() 方法让一个对象变的不可扩展,也就是永远不能再添加新的属性
│   │         └─ `has()`方法拦截的是`HasProperty`操作,而不是`HasOwnProperty`操作,即`has()`方法不判断一个属性是对象自身的属性,还是继承的属性。虽然`for...in`循环也用到了`in`运算符,但是`has()`拦截对`for...in`循环不生效。
│   │     └─ construct()
│   │         └─ `construct()`方法用于拦截`new`命令,下面是拦截对象的写法。
│   │         └─ `construct()`方法可以接受三个参数。 - `target`:目标对象。 - `args`:构造函数的参数数组。 
│   │         └─ `construct()`方法返回的必须是一个对象,否则会报错。另外,由于`construct()`拦截的是构造函数,所以它的目标对象必须是函数,否则就会报错。
│   │         └─ `construct()`方法中的`this`指向的是`handler`,而不是实例对象。
│   │     └─ deleteProperty()
│   │         └─ `deleteProperty`方法用于拦截`delete`操作,如果这个方法抛出错误或者返回`false`,当前属性就无法被`delete`命令删除。
│   │         └─ 注意,目标对象自身的不可配置(configurable)的属性,不能被`deleteProperty`方法删除,否则报错。
│   │     └─ defineProperty()
│   │         └─ `defineProperty()`方法拦截了`Object.defineProperty()`操作。
│   │         └─ `defineProperty()`方法内部没有任何操作,只返回`false`,导致添加新属性总是无效。注意,这里的`false`只是用来提示操作失败,本身并不能阻止添加新属性。
│   │         └─ 如果目标对象不可扩展(non-extensible),则`defineProperty()`不能增加目标对象上不存在的属性,否则会报错。另外,如果目标对象的某个属性不可写(writable)或不可配置(configurable),则`defineProperty()`方法不得改变这两个设置。
│   │     └─ getOwnPropertyDescriptor()
│   │         └─ `getOwnPropertyDescriptor()`方法拦截`Object.getOwnPropertyDescriptor()`,返回一个属性描述对象或者`undefined`。
│   │     └─ getPrototypeOf()
│   │         └─ `getPrototypeOf()`方法主要用来拦截获取对象原型。具体来说,拦截下面这些操作。
│   │             └─ - `Object.prototype.__proto__`
│   │             └─ - `Object.prototype.isPrototypeOf()`
│   │             └─ - `Object.getPrototypeOf()`
│   │             └─ - `Reflect.getPrototypeOf()`
│   │             └─ - `instanceof`
│   │         └─ `getPrototypeOf()`方法的返回值必须是对象或者`null`,否则报错。另外,如果目标对象不可扩展(non-extensible), `getPrototypeOf()`方法必须返回目标对象的原型对象。
│   │     └─ isExtensible()
│   │         └─ `isExtensible()`方法拦截`Object.isExtensible()`操作。
│   │         └─ 该方法只能返回布尔值,否则返回值会被自动转为布尔值。这个方法有一个强限制,它的返回值必须与目标对象的`isExtensible`属性保持一致,否则就会抛出错误。
│   │     └─ ownKeys()
│   │         └─ `ownKeys()`方法用来拦截对象自身属性的读取操作。
│   │             └─ - `Object.getOwnPropertyNames()`
│   │             └─ - `Object.getOwnPropertySymbols()`
│   │             └─ - `Object.keys()`
│   │             └─ - `for...in`循环
│   │         └─ 使用`Object.keys()`方法时,有三类属性会被`ownKeys()`方法自动过滤,不会返回。
│   │             └─ 目标对象上不存在的属性
│   │             └─ 属性名为 Symbol 值
│   │             └─ 不可遍历(`enumerable`)的属性
│   │             └─ 
│   │         └─ `ownKeys()`方法返回的数组成员,只能是字符串或 Symbol 值。如果有其他类型的值,或者返回的根本不是数组,就会报错。
│   │     └─ preventExtensions()
│   │         └─ `preventExtensions()`方法拦截`Object.preventExtensions()`。该方法必须返回一个布尔值,否则会被自动转为布尔值。
│   │         └─ 这个方法有一个限制,只有目标对象不可扩展时(即`Object.isExtensible(proxy)``false`),`proxy.preventExtensions`才能返回`true`,否则会报错。
│   │     └─ setPrototypeOf()
│   │         └─ `setPrototypeOf()`方法主要用来拦截`Object.setPrototypeOf()`方法。
│   │         └─ 如果目标对象不可扩展(non-extensible),`setPrototypeOf()`方法不得改变目标对象的原型。