简介
ECMAScript 2016 (ES7) 新增了 Proxy 和 Reflect 两个对象操作 API,Proxy 可以代理对象操作,而 Reflect 提供了一系列操作对象的方法,两者的配合使用使得 JavaScript 对象的操作更加灵活和可控。
本文将详细介绍 Proxy 和 Reflect 的使用方法,包括它们的基本语法、应用场景以及常用的技巧。
Proxy
概述
Proxy 可以拦截对象的各种操作,如读取、赋值、方法调用等,使用 Proxy 可以代理对象的操作,从而加入自定义逻辑,并最终将其传达到原始对象。
-- -------------------- ---- ------- ----- --- - --- --------- - ---- -------- -------- ---- --------- - --------------------- ------ ----- ------ ------------------- ---- ---------- -------------- -- ---- -------- -------- ---- ------ --------- - --------------------- ------ --- ----------- ------ ------------------- ---- ------ ---------- -------------- - --- ----------- - ------- -------- ---------------- ------- --- ------ ------ ------------------------- ---------------- ------- --展开代码
如上代码所示,我们通过 Proxy 对象定义了一个代理对象,通过 get 和 set 方法,拦截了对象属性的读取和赋值操作,并实现了自定义的逻辑。
参数
Proxy 构造函数接收两个参数,分别为待代理的目标对象和一个结构体,结构体用于定义拦截器函数。
const target = {}; const handler = {}; const proxy = new Proxy(target, handler);
代理对象的拦截器函数分为以下 13 种操作:
- get(target, property, receiver):拦截对象属性的读取操作。
- set(target, property, value, receiver):拦截对象属性的赋值操作。
- has(target, property):拦截 in 操作符并返回一个布尔值。如果原始对象拥有指定的属性,则为 true;否则为 false。
- ownKeys(target):返回一个数组,包含原始对象的所有可枚举属性。
- construct(target, args, newTarget):拦截 new 操作符,并返回一个构造函数的实例。
- defineProperty(target, property, descriptor):拦截 Object.defineProperty 操作并返回一个布尔值缩写该操作是否成功。
- deleteProperty(target, property):拦截 delete 操作符并返回一个布尔值缩写该操作是否成功。
- getOwnPropertyDescriptor(target, property):拦截 Object.getOwnPropertyDescriptor 方法。用于拦截获取特定对象属性的描述符操作。
- getPrototypeOf(target):拦截 Object.getPrototypeOf 方法。用于拦截获取对象原型操作。
- isExtensible(target):拦截 Object.isExtensible 方法。如果原始对象不可扩展,则返回一个布尔值表示其是否可扩展。
- preventExtensions(target):拦截 Object.preventExtensions 操作。目的是使得不可扩展的对象不再可扩展。
- setPrototypeOf(target, prototype):拦截 Object.setPrototypeOf 操作。用于设置对象的原型。
- apply(target, object, args):拦截函数的调用操作。
应用场景
数据校验
在实际应用中,经常需要对前端操作的数据进行校验。有些校验可以在数据存储时进行,但使用 Proxy 能够更为灵活地进行校验。
-- -------------------- ---- ------- ----- --- - --- --------- - ---- -------- -------- ---- ------ --------- - -- ------ -- -- - ----- ------------------ - ---- - ------ ------------------- ---- ------ ---------- - - --- ---------- - ---- ------------------ ------------------------ ----------- ---------- - ----- ------展开代码
上述示例中,我们实现了对数据属性的校验,如果设置的值小于等于零,则会抛出异常。
跨域代理
浏览器实现了 XMLHttpRequest 对象,允许向其他域提交请求。在这种情况下,可以使用 Proxy 对象来代理请求目标的地址。
-- -------------------- ---- ------- ----- --------- - -------------------------------------- ----- --- - ---------------------------------------- ----- -------- - ----------------------------------------- ---------- --------- -- ----------- ---------- -- ------------------- --------------- --------- -- ----------- ---------- -- -------------------展开代码
上述代码中,我们将请求的地址代理到 Proxy 服务器,以此防止跨域访问限制。
Reflect
概述
Reflect 提供了一组操作对象的 API,能够对对象进行一系列操作,提供对方法的默认行为重现。
const obj = { name: 'Lucy' }; console.log('name' in obj); //true console.log(Reflect.has(obj, 'name')); //true
如上代码所示,我们调用 in 操作符来判断对象是否拥有指定属性,这个操作符可以被 Reflect.has 方法所替代。
API
Reflect 对象提供了以下 13 个静态方法,用于对象的操作,其中大多数方法与 Proxy 对象的拦截器函数相对应。
- Reflect.apply(target, thisArg, args):在指定的 this 对象上调用给定的函数,并将给定的参数列表传递给函数。
- Reflect.construct(target, args):使用给定的参数列表创建指定的对象。
- Reflect.defineProperty(target, propertyKey, attributes):定义对象的一个属性。
- Reflect.deleteProperty(target, propertyKey):从对象中删除指定的属性。
- Reflect.get(target, propertyKey, receiver):返回对象上的指定属性。
- Reflect.getOwnPropertyDescriptor(target, propertyKey):返回指定对象上的属性的属性描述符。
- Reflect.getPrototypeOf(target):返回对象的原型。
- Reflect.has(target, propertyKey):返回一个布尔值,表示对象是否具有指定的属性。
- Reflect.isExtensible(target):如果对象是可扩展的,则返回 true。
- Reflect.ownKeys(target):返回一个包含所有自身属性(不包括继承属性)名称的数组。
- Reflect.preventExtensions(target):将对象设为不可扩展(即不能再添加新的属性)。
- Reflect.set(target, propertyKey, value, receiver):设定对象的指定属性为指定的值。
- Reflect.setPrototypeOf(target, prototype):将目标对象的原型设置为新原型或另一个对象。
应用场景
固化对象属性
在 JavaScript 中,对象的属性通常是可修改的,因此在一些场景中,需要固化对象的属性,使其不能被修改。
const obj = { name: 'Lucy' }; Reflect.defineProperty(obj, 'name', { value: 'Lucy', writable: false }); obj.name = 'Tom'; //静默失败,对象属性没有被修改
上述示例中,我们使用 Reflect 解决了对象属性可修改的问题,将对象属性定义为只读的。
利用默认行为处理代理
在 Proxy 中,我们常常需要将某些操作传递给原始对象,以此保留原始对象的默认行为。
-- -------------------- ---- ------- ----- --- - - ----- ------- ---- -- -- ----- ----- - --- ---------- - ---- ---------------- ---- --------- - --------------------- ------ ----- ------ ------------------- ---- ---------- -- ---- ---------------- ---- ------ --------- - ------------------- ---- ------ ---------- --------------------- ------ --- ----------- ------ ----- -- --------------- ---------------- ---- - ------ ------------------------------ ----- - --- ----------- ------------- --------- - --- ------------- ------ ----------- -------------展开代码
上述示例中,我们修改了 Proxy 中的 get、set 和 deleteProperty 方法,但在方法内部,仍然使用 Reflect 将操作传递给了原始对象,以此保留了对象的默认行为。
结语
本文详细介绍了 Proxy 和 Reflect 的各种应用场景和使用技巧,希望读者能够掌握这两个 API,在实际编程中运用自如。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/67c7dce4cc0f7239cdfdb1d5