JavaScript Reflect

Reflect

Reflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法。这些方法与 proxy handler 的方法相同。

Reflect 不是一个函数对象,因此它是不可构造的。

Reflect 是可简化 Proxy 的创建。

对于每个可被 Proxy 捕获的内部方法,在 Reflect 中都有一个对应的方法,其名称和参数与 Proxy 捕捉器相同。

因此,Reflect 可以解决 Proxy 中内置对象的局限性(如MapSetDatePromise等的内建方法。)

Reflect 的主要特点:

  1. 不是构造函数:不能通过 new 运算符进行实例化,也不能将其当作函数来调用。它的所有属性和方法都是静态的。
  2. 与 Proxy 方法对应:Reflect 对象的方法与 Proxy 对象的方法一一对应,这使得在使用 Proxy 进行对象代理时,可以方便地调用 Reflect 方法来执行默认行为。
  3. 提供默认行为:可以用于获取对象操作的默认行为,方便进行自定义的拦截和处理。

Reflect 对象包含以下静态方法:

  • Reflect.apply(target, thisArg, args):类似于 Function.prototype.apply() 方法,用于调用一个函数,并可以指定函数的 this 值和参数列表。
  • Reflect.construct(target, args, newTarget):类似于使用 new 操作符创建实例对象,相当于执行 new target(...args),还可以指定不同的原型对象。
  • Reflect.defineProperty(target, propertyKey, descriptor):与 Object.defineProperty() 类似,用于定义或修改对象的属性。如果设置成功,返回 true
  • Reflect.deleteProperty(target, propertyKey):类似于 delete 操作符,用于删除对象上的属性,并返回一个布尔值表示是否成功删除。
  • Reflect.get(target, propertyKey, receiver):获取对象上的属性值,类似于 target[propertyKey],如果属性存在读取函数(getter),则 receiver 会作为 getter 函数的 this 值。
  • Reflect.getOwnPropertyDescriptor(target, propertyKey):类似于 Object.getOwnPropertyDescriptor(),返回对象自身属性的描述符,如果不存在则返回 undefined
  • Reflect.getPrototypeOf(target):类似于 Object.getPrototypeOf(),获取对象的原型。
  • Reflect.has(target, propertyKey):判断对象是否具有某个属性,与 in 运算符的功能相同,返回一个布尔值。
  • Reflect.isExtensible(target):类似于 Object.isExtensible(),判断对象是否可扩展。
  • Reflect.ownKeys(target):返回一个包含对象自身所有属性(不包含继承属性)的数组,类似于 Object.keys(),但不会受 enumerable 影响。
  • Reflect.preventExtensions(target):类似于 Object.preventExtensions(),用于阻止对象扩展,并返回一个布尔值表示操作是否成功。
  • Reflect.set(target, propertyKey, value, receiver):设置对象的属性值,类似于 target[propertyKey] = value,如果设置成功,返回 true。它也会考虑属性的设置器(setter)。如果属性存在设置器函数,receiver 会作为 setter 函数的 this 值。
  • Reflect.setPrototypeOf(target, prototype):设置对象的原型,类似于 Object.setPrototypeOf(),如果设置成功,返回 true

为什么 get/set 有第三个参数 receiver?

示例:

let user = {
  _name: "Guest",
  get name() {
    return this._name;
  }
};

let userProxy = new Proxy(user, {
  get(target, prop, receiver) {
    return target[prop]; // target = user
  }
});

// admin 从 user 继承
let admin = {
  __proto__: userProxy,
  _name: "Admin"
};

// 期望输出:Admin
console.log(admin.name); // 实际输出:Guest 

为什么console.log(admin.name)实际输出的是Guest
如果不使用代理,console.log(admin.name)会输出正确值Admin
那问题就出在代理中:

  1. 当我们读取 admin.name 时,由于 admin 对象自身没有name属性,搜索将转到其原型。
  2. admin的原型是 userProxy
    从代理读取 name 属性时,get 捕捉器会被触发,并从原始对象返回 target[prop] 属性。
  3. 当调用 target[prop] 时,若 prop 是一个 getter,它将在 this=target 上下文中运行其代码。因此,结果是来自原始对象 targetthis._name,即来自 user

get 捕捉器的第三个参数 receiver的说明:如果target对象中指定了getterreceiver则为getter调用时的this值。

receiver保证将正确的 this 传递给 getter

在上面的例子中,正确的thisadmin。但是在调用 target[prop] 时,targetadmin

如何把上下文传递给 getter?注意 getter不能“被调用”,只能被访问。
使用Reflect.get

正确示例:

let user = {
  _name: "Guest",
  get name() {
    return this._name;
  }
};

let userProxy = new Proxy(user, {
  get(target, prop, receiver) { 
    // 在读取admin.name时,receiver = admin
    return Reflect.get(target, prop, receiver); // (*)
  }
});


let admin = {
  __proto__: userProxy,
  _name: "Admin"
};

alert(admin.name); // Admin

get 捕捉器中使用 Reflect.get(target, prop, receiver) 来获取属性,receiver 保留了对正确 this 的引用(即 admin),通过 Reflect.get 传递给 getter,从而解决了之前直接使用 target(prop) 导致的 this 指向问题。

  • 13
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值