带你重学ES6 | Reflect

4,406 阅读8分钟

前言

其实说实在的,在没重学 ES6 之前,我对 Reflect 这个对象完全是空白的,一是在实际工作中用不到,二是面试的时候也没人问起。这也是自己的一个问题,在驱动式学习,而没有去自主学习。

那今天就讲讲 Reflect 对象。

1、概述

Reflect 对象不是构造函数,所以创建时不是用 new 来进行创建。

在 ES6 中增加这个对象的目的:

  1. 将 Object 对象的一些明显属于语言内部的方法(比如 Object.defineProperty),放到 Reflect 对象上。现阶段,某些方法同时在 Object 和 Reflect 对象上部署,未来的新方法将只部署在 Reflect 对象上。也就是说,从 Reflect 对象上可以拿到语言内部的方法。
  2. 修改某些 Object 方法的返回结果,让其变得更合理。比如,Object.defineProperty(obj, name, desc)在无法定义属性时,会抛出一个错误,而 Reflect.defineProperty(obj, name, desc)则会返回 false。
  3. 让 Object 操作都变成函数行为。某些 Object 操作是命令式,比如 name in obj 和 delete obj[name],而 Reflect.has(obj, name)和 Reflect.deleteProperty(obj, name)让它们变成了函数行为。
  4. Reflect 对象的方法与 Proxy 对象的方法一一对应,只要是 Proxy 对象的方法,就能在 Reflect 对象上找到对应的方法。这就让 Proxy 对象可以方便地调用对应的 Reflect 方法,完成默认行为,作为修改行为的基础。也就是说,不管 Proxy 怎么修改默认行为,你总可以在 Reflect 上获取默认行为。
var loggedObj = new Proxy(obj, {
  get(target, name) {
    console.log("get", target, name);
    return Reflect.get(target, name);
  },
  deleteProperty(target, name) {
    console.log("delete" + name);
    return Reflect.deleteProperty(target, name);
  },
  has(target, name) {
    console.log("has" + name);
    return Reflect.has(target, name);
  },
});

上面代码中,每一个 Proxy 对象的拦截操作(get、delete、has),内部都调用对应的 Reflect 方法,保证原生行为能够正常执行。添加的工作,就是将每一个操作输出一行日志。

2、静态 API

Reflect 拥有 13 个静态 API,其大部分与 Object 对象的同名方法的作用都是相同的,而且它与 Proxy 对象的方法是一一对应的。

2.1、Reflect.apply()

通过指定的参数列表发起对目标(target)函数的调用。该方法接受是三个参数,target:目标函数。thisArgument:target 函数调用时绑定的 this 对象。argumentsList:target 函数调用时传入的实参列表,该参数应该是一个类数组的对象。

该方法与 ES5 中 Function.prototype.apply()方法类似,Reflect.apply()可以让代码看起来更加的通俗易懂。

Function.prototype.apply.call(Math.floorundefined, [1.75]);
Reflect.apply(Math.floorundefined, [1.75]);

2.2、Reflect.construct()

该方法的行为有点像 new 操作符 构造函数 , 相当于运行 new target(...args)。该方法接受三个参数,target:被运行的目标构造函数。argumentsList:类数组,目标构造函数调用时的参数。newTarget(可选):作为新创建对象的原型对象的 constructor 属性, 参考 new.target 操作符,默认值为 target。

class Person {
  constructor(name) {
    this.name = name;
  }
}
let person = new Person("Jack");
// 等价于
let person = Reflect.construct(Person, ["Jack"]);

2.3、Reflect.defineProperty()

基本等同于 Object.defineProperty() 方法,唯一不同是返回 Boolean 值。该方法接受三个参数,target:目标对象。propertyKey:要定义或修改的属性的名称。attributes:要定义或修改的属性的描述。

let obj = {};
Object.defineProperty({}, "value", {
  value"18",
});
// 等价于
Reflect.defineProperty({}, "value", {
  value"18",
});

2.4、Reflect.deleteProperty()

允许用于删除属性。它很像 delete operator ,但它是一个函数。该方法接受两个参数,target:删除属性的目标对象。propertyKey:需要删除的属性的名称。

let obj = {
  value"18",
};
delete obj["value"];
// 等价于
Reflect.deleteProperty(obj, "value");

2.5、Reflect.get()

该方法与从 对象 (target[propertyKey]) 中读取属性类似,但它是通过一个函数执行来操作的。该方法接受三个参数,target:需要取值的目标对象。propertyKey:需要获取的值的键值。receiver:如果 target 对象中指定了 getter,receiver 则为 getter 调用时的 this 值。值得注意的是如果 target 不是对象的话,则会报错。

let obj = {
  value"18",
};
Reflect.get(obj, "value"); // 18
// 当有getter函数时
let obj = {
  value"18",
  get foo() {
    return this.a + this.b;
  },
};
let foo = {
  a: 2,
  b: 3,
};
Reflect.get(obj, "foo", foo); // 5

2.6、Reflect.getOwnPropertyDescriptor()

该方法与 Object.getOwnPropertyDescriptor() 方法相似。如果在对象中存在,则返回给定的属性的属性描述符。否则返回 undefined。该方法接受两个参数,target:需要寻找属性的目标对象。propertyKey:获取自己的属性描述符的属性的名称。值得注意的是如果 target 不是对象的话,会报错。

var myObject = {};
Object.defineProperty(myObject, "hidden", {
  value: true,
  enumerable: false,
});
var theDescriptor = Object.getOwnPropertyDescriptor(myObject, "hidden");
// 等价于
var theDescriptor = Reflect.getOwnPropertyDescriptor(myObject, "hidden");

2.7、Reflect.getPrototypeOf()

该方法与 Object.getPrototypeOf() 方法几乎是一样的。都是返回指定对象的原型(即内部的 [[Prototype]] 属性的值)。该方法只能接受一个参数,target:获取原型的目标对象。注意得是如果 target 不是对象的话,会报错。

class MyObj {}
let myObj = new MyObj();
Object.getPrototypeOf(myObj) === MyObj.prototype// true
// 等价于
Reflect.getPrototypeOf(myObj) === MyObj.prototype// true

2.8、Reflect.has()

作用与 in 操作符 相同。该方法接受两个参数,target:目标对象。propertyKey:属性名,需要检查目标对象是否存在此属性。注意,如果目标对象并非 Object 类型,则会报错。

let obj = {
  value"18",
};
"value" in obj; // true
// 等价于
Reflect.has(obj, "value"); // ture

2.9、Reflect.isExtensible()

判断一个对象是否可扩展 (即是否能够添加新的属性)。与它 Object.isExtensible() 方法相似,但有一些不同,如果该方法的第一个参数不是一个对象(原始值),那么将会报错。对于 Object.isExtensible(),非对象的第一个参数会被强制转换为一个对象。该方法只接受一个参数,target:检查是否可扩展的目标对象。

let obj = {};
Object.isExtensible(obj); // true
// 等价于
Reflect.isExtensible(obj); // true

2.10、Reflect.ownKeys()

返回一个由目标对象自身的属性键组成的数组。该方法接受一个参数,target:获取自身属性键的目标对象。返回一个由目标对象的自身属性键组成的 Array。返回值等同于 Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target))。

let myObject = {
  foo: 1,
  bar: 2,
  [Symbol.for("baz")]: 3,
  [Symbol.for("bing")]: 4,
};

Object.getOwnPropertyNames(myObject).concat(
  Object.getOwnPropertySymbols(myObject)
);
// 等价于
Reflect.ownKeys(myObject); // ['foo''bar', Symbol(baz), Symbol(bing)]

2.11、Reflect.preventExtensions()

该方法阻止新属性添加到对象 (例如:防止将来对对象的扩展被添加到对象中)。该方法与 Object.preventExtensions()相似,但有一些不同点。该方法接受一个参数,target:阻止扩展的目标对象。

let obj = {};
Object.preventExtensions(obj); // Object {}
// 等价于
Reflect.preventExtensions(obj); // true

2.12、Reflect.set()

该方法设置 target 对象的 name 属性等于 value。该方法接受四个参数,target:设置属性的目标对象。propertyKey:设置的属性的名称。value:设置的值。receiver:如果遇到 setter,receiver 则为 setter 调用时的 this 值。

let person = {
  name: "Jack",
};
person.name; // 'Jack'
Reflect.set(person, "name""Luci");
person.name; // 'Luci'
// 当有setter函数时
let person = {
  name: "Jack",
  set foo(value) {
    return (this.name = value);
  },
};
let receiverObject = {
  name: "Luci",
};
Reflect.set(person, "foo""Jhon", receiverObject);
receiverObject.name; // Jhon

2.13、Reflect.setPrototypeOf()

该方法与 Object.setPrototypeOf() 方法是一样的。它可设置对象的原型(即内部的 [[Prototype]] 属性)为另一个对象或 null,如果操作成功返回 true,否则返回 false。该方法接受两个参数,target:设置原型的目标对象。prototype:对象的新原型(一个对象或 null)。如果 target 不是对象的话将会报错。

const myObj = {};

Object.setPrototypeOf(myObj, Array.prototype);
// 等价于
Reflect.setPrototypeOf(myObj, Array.prototype);

参考

阮一峰:ECMAScript 6 入门——Reflect

后语

觉得还可以的,麻烦走的时候能给点个赞,大家一起学习和探讨!

相关文章:

还可以关注我的博客希望能给我的github上点个Start,小伙伴们一定会发现一个问题,我的所有用户名几乎都与番茄有关,因为我真的很喜欢吃番茄❤️!!!

想跟车不迷路的小伙还希望可以关注公众号 前端老番茄 或者扫一扫下面的二维码👇👇👇。

我是一个编程界的小学生,您的鼓励是我不断前进的动力,😄希望能一起加油前进。