Reflect 与 Proxy

概述:

Proxy 与 Reflect 是 ES6 为了操作对象引入的 API 。

Proxy 可以对目标对象的读取、函数调用等操作进行拦截,然后进行操作处理。(例如属性查找,赋值,枚举,函数调用等),等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。Proxy 并不是直接操作对象,而是代理模式,任何对目标的操作都要经过代理。代理就可以对外界的操作进行过滤和改写。

Reflect 是一个内置的对象,可以用于获取目标对象的行为。(与Object类似)


Proxy:

基本语法:

const p = new Proxy(target, handler)

其中,参数 target 为目标对象(可以为任何类型的对象,包括原生数组,函数,甚至另一个代理)。

参数 handler 为一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为。

实例:

其中,target 可以为空对象,它可以通过调用 handler 中的方法,向target (目标对象)添加属性值。

 handler 也可以为空对象,即:不对访问设置拦截操作,可以直接访问目标对象。

由此可见,处理器对象( handler )的各种可代理操作都是对目标对象( target ),如果没有定义某种操作时,那么操作请求将会直接转发到目标对象上。

handler的可代理操作:

一共有 13 种可代理操作,每种操作的代号(属性名/方法名)和触发这种操作的方式列举如下:

handler.apply():该方法用于拦截函数的调用。

handler.construct():该方法用于拦截 new 操作符。为了使 new 操作符生效,目标对象必须是一个函数对象,可构造的。

handler.defineProperty():该方法用于拦截对象的 Object.defineProperty() 操作。

handler.deleteProperty():该方法用于拦截对象属性的 delete 操作。

handler.get():该方法用于拦截对象的读取属性操作。

handler.getOwnPropertyDescriptor():该方法是 Object.getOwnPropertyDescriptor() 方法的钩子。

handler.getPrototypeOf():该方法是一个 Proxy 方法,当读取 Proxy 对象的原型时,该方法就会被调用。

handler.has():该方法是针对 in 操作符的代理方法。

handler.isExtensible():该方法用于拦截对对象的 Object.isExtensible() 方法

handler.ownKeys():该方法用于拦截 Reflect.ownKeys() 方法。

handler.preventExtensions():该方法用于设置对 Object.preventExtensions() 方法的拦截.

handler.set():该方法是设置属性值操作的捕获器。

handler.setPrototypeOf():该方法主要用来拦截 Object.setPrototypeOf() 方法。

上述方法详细见:Proxy 中的 Handler 对象


Reflect :

Reflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法。这些方法与 proxy handler 的方法相同。Reflect 不是一个函数对象,因此它是不可构造的。

注意:与大多数全局对象不同 Reflect 并非一个构造函数,所以不能通过 new 运算符对其进行调用,或者将 Reflect 对象作为一个函数来调用。Reflect 的所有属性和方法都是静态的。

其静态方法列举如下:(命名方式和handler的方法相同)

Reflect.apply(target, thisArgument, argumentsList) :对一个函数进行调用操作,同时可以传入一个数组作为调用参数。它和 Function.prototype.apply() 功能类似。

Reflect.construct(target, argumentsList[, newTarget]) :对构造函数进行 new 操作,相当于执行 new target(...args)。

Reflect.defineProperty(target, propertyKey, attributes) 的功能类似于 Object.defineProperty()。如果设置成功就会返回 true。

Reflect.deleteProperty(target, propertyKey) :作为函数的 delete 操作符,相当于执行 delete target[name]。

Reflect.get(target, propertyKey[, receiver]) :获取对象身上某个属性的值,类似于 target[name]。

Reflect.getOwnPropertyDescriptor(target, propertyKey) :如果对象中存在该属性,则返回对应的属性描述符,否则返回 undefined。

Reflect.getPrototypeOf(target) 的功能类似于 Object.getPrototypeOf()。

Reflect.has(target, propertyKey) :判断一个对象是否存在某个属性,和 in 运算符 的功能完全相同。

Reflect.isExtensible(target) 的功能类似于 Object.isExtensible()。返回一个Boolean。

Reflect.ownKeys(target) :返回一个包含所有自身属性(不包含继承属性)的数组。(类似于 Object.keys(), 但不会受enumerable 影响)。

Reflect.preventExtensions(target) 的功能类似于 Object.preventExtensions()。返回一个Boolean。

Reflect.set(target, propertyKey, value[, receiver]) :将值分配给属性的函数。返回一个Boolean,如果更新成功,则返回true。

Reflect.setPrototypeOf(target, prototype) :设置对象原型的函数。返回一个 Boolean,如果更新成功,则返回 true。

上述静态方法详细请见:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect


注意事项和细节:

1.目标对象的可扩展性: 如果目标对象是不可扩展的(Object.preventExtensions() 或 Object.seal() 或 Object.freeze()),那么在代理对象上添加新属性会导致错误。

实例:

const target = Object.freeze({ name: 'John' });
const handler = {};
const proxy = new Proxy(target, handler);

// 会抛出错误
proxy.age = 30;

2.不可撤销的代理: 一旦创建了代理对象,就无法将其还原回原始对象。如果你尝试撤销代理,会得到一个错误。

实例:

const target = { name: 'John' };
const handler = {};
const proxy = new Proxy(target, handler);

// 撤销代理会抛出错误
Proxy.revocable(proxy, handler).revoke();

3.循环引用: 避免创建循环引用,因为代理对象可能导致无限递归,从而导致堆栈溢出。

实例:

const target = {};
const handler = {
  get(target, property, receiver) {
    return target[property];
  }
};

const proxy = new Proxy(target, handler);

// 创建循环引用,潜在的堆栈溢出
target.circularRef = proxy;

4.不要滥用代理: 使用代理时应该明确其必要性,不要过度使用。过多的代理可能导致代码难以理解和维护。

5.性能考虑: Proxy 的使用可能引入一些性能开销,特别是在频繁调用的场景。在性能敏感的情况下,应该仔细评估代理的影响。

6.拦截器的顺序: 如果使用了多个拦截器,它们的执行顺序是重要的。确保了解拦截器执行的顺序,以便预测代理的行为。

7.非整数属性的顺序: 如果代理对象有非整数属性,那么 for...in 和 Object.keys() 的顺序可能是不确定的。最好避免依赖于属性的顺序。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值