ES6(三)Proxy和Reflect
本文最后更新于:19 小时前
Proxy对象用来设置拦截器,提供对外界访问的过滤和改写的机制。Reflect对象继承和规范了对象操作的API
ES6(三)Proxy和Reflect
一、Proxy代理器
1. 简介
Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。
1 | |
target,代理的对象handler,配置对象,用来设置拦截程序
Proxy构造函数返回一个代理器实例,通过这个实例可以实现对原对象(即target)的操作,同时可以自定义一些逻辑,在访问时触发。如果直接访问原对象,将不会触发这些自定义逻辑。
2. 拦截器
Proxy 支持的拦截操作一览,一共 13 种。
- **get(target, propKey, receiver)**:拦截对象属性的读取,比如
proxy.foo和proxy['foo']。 - **set(target, propKey, value, receiver)**:拦截对象属性的设置,比如
proxy.foo = v或proxy['foo'] = v,返回一个布尔值。 - **has(target, propKey)**:拦截
propKey in proxy的操作,返回一个布尔值。 - **deleteProperty(target, propKey)**:拦截
delete proxy[propKey]的操作,返回一个布尔值。 - **ownKeys(target)**:拦截
Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for...in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性。 - **getOwnPropertyDescriptor(target, propKey)**:拦截
Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。 - **defineProperty(target, propKey, propDesc)**:拦截
Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一个布尔值。 - **preventExtensions(target)**:拦截
Object.preventExtensions(proxy),返回一个布尔值。 - **getPrototypeOf(target)**:拦截
Object.getPrototypeOf(proxy),返回一个对象。 - **isExtensible(target)**:拦截
Object.isExtensible(proxy),返回一个布尔值。 - **setPrototypeOf(target, proto)**:拦截
Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。 - **apply(target, object, args)**:拦截 Proxy 实例作为函数调用的操作,比如
proxy(...args)、proxy.call(object, ...args)、proxy.apply(...)。 - **construct(target, args)**:拦截 Proxy 实例作为构造函数调用的操作,比如
new proxy(...args)。
下面重点介绍常用的几种。
get()和set(),分别拦截读取、写操作
1 | |
1 | |
apply(),拦截Proxy示例作为函数调用的操作
1 | |
construct(),拦截Proxy实例作为构造函数调用的操作
1 | |
receiver指向原始的(读)操作所在的那个对象,一般就是Proxy实例
1 | |
3. Proxy.revocable() 可取消的代理器
Proxy.revocable()方法返回一个可取消的 Proxy 实例。
1 | |
一个使用场景是,目标对象不允许直接访问,必须通过代理访问,一旦访问结束,就收回代理权,不允许再次访问。
4. Proxy实例的this指向
Proxy实例的this指向handler配置对象,这会导致一些错误发生:有些原生对象的内部属性,因为只有通过正确的this才能拿到,如Date对象
1 | |
解决办法是:在拦截器中绑定this指向原生对象
1 | |
二、Reflect
1. 简介
Reflect是ES6为了操作对象而提供的新API。Reflect对象的设计目的有这样几个:
- 将
Object对象的一些明显属于语言内部的方法(比如Object.defineProperty),放到Reflect对象上。 - 修改某些
Object方法的返回结果,让其变得更合理。比如,Object.defineProperty(obj, name, desc)在无法定义属性时,会抛出一个错误,而Reflect.defineProperty(obj, name, desc)则会返回false - 让
Object操作都变成函数行为 Reflect对象的方法与Proxy对象的方法一一对应,只要是Proxy对象的方法,就能在Reflect对象上找到对应的方法。这就让Proxy对象可以方便地调用对应的Reflect方法,完成默认行为,作为修改行为的基础。
2. 静态方法
Reflect对象一共有 13 个静态方法。
- Reflect.apply(target, thisArg, args)
- Reflect.construct(target, args),等同于
new target(...args) - Reflect.get(target, name, receiver)
- Reflect.set(target, name, value, receiver)
- Reflect.defineProperty(target, name, desc)
- Reflect.deleteProperty(target, name),等同于
delete obj[name] - Reflect.has(target, name),对应
name in obj里面的in运算符 - Reflect.ownKeys(target),用于返回对象的所有属性
- Reflect.isExtensible(target)
- Reflect.preventExtensions(target)
- Reflect.getOwnPropertyDescriptor(target, name)
- Reflect.getPrototypeOf(target),用于读取对象的
__proto__属性 - Reflect.setPrototypeOf(target, prototype)
Reflect.get方法查找并返回target对象的name属性,如果没有该属性,则返回undefined。
1 | |
Reflect.set方法设置target对象的name属性等于value
1 | |
如果get()和set()给了receiver参数,则读取函数的this绑定receiver
1 | |