JS进阶 | Reflect映射对象

3,106 阅读4分钟

「这是我参与2022首次更文挑战的第18天,活动详情查看:2022首次更文挑战

JS进阶系列文章

Reflect

Reflect是一个对象,翻译过来是反射的意思,它提供了很多操作JavaScript对象的方法, 是为了弥补Object中对象的一些缺陷。且所有属性和方法都是静态的。

为什么会有Reflect

在早期,JavaScript这门语言中的一些内部方法都被部署到了Object这个对象上。就例如getPrototypedeinfePropertyAPI、类似indelete操作符都放到了Object对象上了。但Object作为一个构造函数(Reflect并非一个构造函数,不能通过new关键字调用),这些方法放到它身上并不合适,所以在ES6之后的内部新方法会部署到Reflect对象中。

使用Reflect对象操作Object对象

Reflect对象让我们操作Object对象不再是通过点语法了,而是变成了函数行为。

我们看下面的例子,获取对象属性可以使用Reflect.get方法、将对象的属性赋值可以使用Reflect.set方法。

const obj = {
  name: "_island",
  age: 18
};

// 获取对应属性的值
console.log(obj.name); // _island
console.log(Reflect.get(obj, "name")); // _island

// 对对象的属性赋值操作
obj.name = "abc";
Reflect.set(obj, "name", "abc");
console.log(Reflect.get(obj, "name")); // abc


// 判断一个对象中是否有该属性
console.log("name" in obj); // true
console.log(Reflect.has(obj, "name")); // true

Reflect中的方法

对象中的方法说明
Reflect.apply()对一个函数进行apply调用
Reflect.construct()对构造函数进行new操作
Reflect.defineProperty()定义一个属性
Reflect.deleteProperty()删除一个属性
Reflect.get()获取一个属性
Reflect.getOwnPropertyDescriptor()获取一个属性描述符
Reflect.getPrototypeOf()获取一个对象的原型
Reflect.has()判断一个属性是否在对象中
Reflect.isExtensible()判断可以扩展
Reflect.ownKeys()获取一个对象中的key集合
Reflect.preventExtensions()使一个对象不可扩展
Reflect.set()设置一个属性
Reflect.setPrototypeOf()设置一个对象的原型

Reflect对象中一些方法与Object相同,但它们存在一些细微的区别,如果你想更加了解可以阅读Reflect和Object中的方法区别

在返回值方便Reflect对象中的方法设计的更加合理。比如defineProperty方法,如果没有将属性设置成功,在Reflect中会返回boolean值,而Object对象中如果没有定义成功则会抛出TypeError

Reflect搭配Proxy

Reflect对象中的方法和上一篇文章将到的Proxy对象的方法的对应的,Proxy对象中的方法也能在Reflect对象中调用。

通常我们将Reflect对象搭配Proxy一起使用,我们看下面这个Reflect搭配Proxy对象使用的案例。

const obj = {
  name: "_island"
};

const objProxy = new Proxy(obj, {
  get: function (target, key, receiver) {
    // 原来的写法
    // return target[key]
    // 使用Reflect
    return Reflect.get(target, key, receiver);
  },
  set: function (target, key, newVal, receiver) {
    // 原来的写法
    // target[key]=newVal
    // 使用Reflect
    Reflect.set(target, key, newVal, receiver);
  },
});

objProxy.name = "abc";
console.log(objProxy.name); // abc

在上面,Proxy对象中getset捕获器多了一个receiver参数,这是这两个捕获器特有的,这个receiver参数是当前代理的目标。

ProxyReflect搭配使用时,Proxy对象会拦截对应的操作,后者完成对应的操作,如果传入receiver,那么Reflect.get属性会触发Proxy.defineProperty捕获器。我们再上面这里案例上再新增一些代码。

const obj = {
  name: "_island"
};

const objProxy = new Proxy(obj, {
  get: function (target, key, receiver) {
    // 原来的写法
    // return target[key]
    // 使用Reflect
    console.log(receiver);
    return Reflect.get(target, key, receiver);
  },
  set: function (target, key, newVal, receiver) {
    // 原来的写法
    // target[key]=newVal
    // 使用Reflect
    Reflect.set(target, key, newVal, receiver);
  },
  defineProperty: function (target, key, attr) {
    console.log("defineProperty");
    console.log(target, key, attr);
    Reflect.defineProperty(target, key, attr);
  }
});

objProxy.name = "abc";
console.log(objProxy.name); 
// defineProperty
// { name: '_island' } name { value: 'abc' }
// { name: 'abc' }
// abc

传入在我们获取代理对象中的name属性时,当Reflectreceiver参数传入时,获取属性值时会获取到receiver中的,所以会触发defineProperty捕获器,如果没有传入receiver参数,则不会触发defineProperty捕获器。

总结

  • Reflect对象中集合了JavaScript内部方法
  • 操作Object对象的方式变成了函数行为
  • Reflect对象中的方法返回结果更加合理