恋の歌的logo
分类 归档 标签

JavaScript中的Reflect

发表于2020-06-12
更新于2023-02-13
分类于编程
总字数2.2K
阅读时长 ≈8 分钟

前言 🔗

新API,得学学。

Reflect 🔗

ES5(ES2015)的新API,看了看MDN的文档,对Reflect的描述

与大多数全局对象不同,Reflect不是一个构造函数。你不能将其与一个new运算符一起使用,或者将Reflect对象作为一个函数来调用。Reflect的所有属性和方法都是静态的(就像Math对象)。Reflect对象提供以下静态函数,它们具有与处理器对象方法相同的名称。这些方法中的一些与Object上的对应方法相同。

兼容性的话,在MDN上看ie是完全不兼容的,其他浏览器的兼容情况都非常不错。就算不兼容也要学。

Reflect.apply() 🔗

类似Function.prototype.apply()

有三个参数

  • fn 目标函数
  • context 函数执行的上下文
  • arguments 传入fn函数的参数数组(类数组)

返回值为运行绑定了context之后函数的返回值

javascript
function getName() {
  console.log(this.name);
}

var o = {
  name: "lwf",
};

Reflect.apply(getName, o, []);

当参数为空的时候,也要传入一个空的数组(或者类数组),不然报错:

CreateListFromArrayLike called on non-object

当上下文无效的时候,会使用全局的对象,NodeJS中为gloabl,浏览器中为window。

Reflect.construct() 🔗

相当于用new操作符来新建对象

有三个参数

  • fn 目标构造函数
  • arguments 传入构造函数的参数数组(类数组)
  • pFn 构造函数(可选)

通过fnarguments初始化的原型为pFn的原型对象(如果有的情况下)的对象。

javascript
function Person() {
  this.name = "lwf";
}

var p1 = Reflect.construct(Person, []);

var p2 = new Person(); // 与上面等效

这里的pFn是一个构造函数,用于指定新创建对象的原型为该构造函数的原型对象。

javascript
function Person() {
  console.log("person");
}

function People() {
  console.log("people");
}

// 在People上挂say方法
People.prototype.say = function () {
  console.log("people function");
};

var p1 = Reflect.construct(Person, [], People);

p1.say(); // 可以访问say方法

console.log(p1.__proto__ === People.prototype); // 打印true

注意此时对象p1的构造函数已经不是Person了,而是People,因为js通过挂载在prototype上的constructor属性来判断对象的类型。也就是说,现在p1这个对象用instanceof判断,Person为false,而People为true。

javascript
// ...

console.log(p1 instanceof Person); // 打印false
console.log(p1 instanceof People); // 打印true

Reflect.construct这个方法之前,可以用Object.createFunction.prototype.apply来类似等效的创建对象。

javascript
// 省略Person和People构造函数

var o = Object.create(People.prototype); // 以People的原型为原型创建一个对象

Person.apply(o, []); // 以这个对象对上下文调用Person函数。

在MDN上说到一点这两种方式的不同,即在构造函数内部new.target属性的取值

对于new.target的取值,可以用下面这段代码验证其指向了构造函数。

javascript
function Person() {
  console.log(new.target === Person.prototype.constructor);
}

new Person(); // 打印true,证明new.target指向了构造函数

对于通过Object.createFunction.prototype.apply创建的对象,由于没有通过new创建,内部的new.target的值会是undefined

javascript
function Person() {
  console.log(new.target);
}

function People() {}

var o = Object.create(People.prototype);

Person.apply(o, []); // 打印 undefined

而通过Reflect.construct来创建的话,new.target会自动指定到构造函数。(如果指定了pFn,那指向pFn,如果没指定,则指向默认,即为fn

javascript
function Person() {
  console.log(new.target);
}

function People() {
  console.log("people");
}

Reflect.construct(Person, [], People); // 打印People构造函数
Reflect.construct(Person, []);

Relfect.defineProperty() 🔗

在一个对象上定义属性,类似Object.defineProperty

有三个参数

  • obj 目标对象
  • propertyKey 定义或者修改的属性名
  • attributes 定义或修改的属性的描述

对于attributes参数,为一个对象,这个对象可选的键有以下:

  • configurable 属性是否可以改变和删除,默认为false
  • enumerable 属性是否可以被枚举,默认为false
  • value 属性对应的值,默认为undefined
  • writable 属性是否可写,默认为false
  • get 属性的getter函数,默认为undefined
  • set 属性的setter函数,默认为undefined

对于这个对象,MDN上有详细的解释,这里就不作详细的展开。

Object.defineProperty() - JavaScript | MDN

javascript
var o = {};

Reflect.defineProperty(o, "name", {
  configurable: true,
  enumerable: true,
  value: "lwf",
  writable: true,
});

console.log(o.name); // 打印"lwf"

Reflect.defineProperty(o, "age", {
  configurable: true,
  enumerable: true,
  get: function () {
    return 18;
  },
  set: function (value) {
    console.log(value);
  },
});

console.log(o.age); // 调用getter,打印18

o.age = 20; // 调用setter,打印了20

console.log(o.age); // 调用getter,依然打印18

Object.defineProperty方法唯一不同就是Object.defineProperty成功定义属性之后会返回传入的对象,失败则会报错。而Reflect.defineProperty会返回boolean值。

javascript
var o = {};

var res = Object.defineProperty(o, "name", {
  configurable: false, // 属性描述符不能被修改了
  enumerable: true,
  value: "lwf",
  writable: true,
});

console.log(o === res); // 打印true

res = Reflect.defineProperty(o, "name", {
  configurable: true, // 尝试改变属性描述符
});

console.log(res); // 打印false

try {
  res = Object.defineProperty(o, "name", {
    configurable: true, // 尝试改变属性描述符
  });
} catch (e) {
  console.log("出错" + e); // 打印TypeError: Cannot redefine property: name
}

Reflect.deleteProperty() 🔗

类似delete操作符

有两个参数

  • obj 目标对象
  • propertyKey 要删除的属性名
javascript
var o = {
  name: "lwf",
};

var res = Reflect.deleteProperty(o, "name");

console.log(o.name); // 打印undefined
console.log(res); // 打印true

delete操作符不同的是,Reflect.deleteProperty为一个函数,返回boolean,表面是否删除成功。

Reflect.get() 🔗

类似obj[propertyKey],但是却是通过函数调用来获取属性值。

有三个参数

  • obj 目标对象
  • propertyKey 需要获取的属性名称
  • context 如果该属性指定了getter,则调用getter时以这个参数为上下文

返回值为该对象的属性名对应的值。

javascript
var o = {};
var context = {
  val: "context",
};

Reflect.defineProperty(o, "name", {
  get: function () {
    return this.val;
  },
});

var res = Reflect.get(o, "name", context);

console.log(res); // 打印"context"

Reflect.getOwnPropertyDescriptor() 🔗

类似Object.getOwnPropertyDescriptor

有两个参数

  • obj 目标对象
  • propertyKey 需要获取属性描述符的属性名称
javascript
var o = {};

Reflect.defineProperty(o, "name", {
  configurable: true,
  enumerable: true,
  value: "lwf",
  writable: true,
});

var res = Reflect.getOwnPropertyDescriptor(o, "name");

console.log(res); // 打印{ value: 'lwf', writable: true, enumerable: true, configurable: true }

Object.getOwnPropertyDescriptor唯一不同点在与如何如理obj参数为非对象的情况,如果参数obj不是对象,Object.getOwnPropertyDescriptor会强制将其转为对象,而Reflect.getOwnPropertyDescriptor会报TypeError错误。

Reflect.getPrototypeOf() 🔗

类似与Object.getPrototypeOf

有一个参数

  • obj 目标对象

返回给定对象obj的原型对象

和通过属性__proto__访问的对象时同一个

javascript
var o = {};

var res = Reflect.getPrototypeOf(o);

console.log(res === o.__proto__);

Reflect.has() 🔗

检测属性是否存在对象上(包括原型链)

in操作符具有相同的效果

有两个参数

  • obj 目标对象
  • propertyKey 需要检测的属性名

返回boolean值,表示属性是否存在。

javascript
var o = {
  name: "lwf",
};

var res = Reflect.has(o, "name");

console.log(res); // 打印true
console.log("name" in o); // 打印true 和上面的操作等价

res = Reflect.has(o, "toString");

console.log(res); // 打印true

Reflect.ownKeys() 🔗

返回对象的属性名组成的数组(属性的描述器配置必须是可枚举的)

有一个参数

  • obj 目标对象
javascript
var o = {
  propertyKey1: "p1",
  propertyKey2: "p2",
};

var res = Reflect.ownKeys(o);

console.log(res); // 打印 ["propertyKey1","propertyKey2"]

Reflect.preventExtensions() 🔗

阻止在一个对象上添加属性,即禁止扩展。

有一个参数

  • obj 目标对象

返回值为boolean,表示阻止是否成功。

这里需要注意一点,创建出来的对象都是默认可以扩展的,也就是可以添加属性的。

javascript
var o = {};

var res = Reflect.isExtensible(o);

console.log(res); // 打印true,表示其是可以扩展的。

res = Reflect.preventExtensible(o);

console.log(res); // 打印true,表示阻止成功。

res = Reflect.defineProperty(o, "name", {
  value: "lwf",
});

console.log(res); // 打印false,表示设置属性失败。

Reflect.isExtensible() 🔗

判断一个对象是否能够新增属性

有一个参数

  • obj 目标对象

返回值为boolean,表示该对象是否可以扩展(即是否可以添加属性)

javascript
var o = {};

var res = Reflect.isExtensible(o);

console.log(res); // 打印true,对象默认都是可以扩展的。

Reflect.preventExtensible(o); // 禁用其扩展。

res = Reflect.isExtensible(o);

console.log(res); // 打印false,该对象已经不可以扩展。

Reflect.set() 🔗

往对象的属性上设置值

有四个参数

  • obj 目标对象
  • propertyKey 需要设置值的属性名
  • value 设置的值
  • context 如果该属性指定了setter,则调用setter时以这个参数为上下文(可选)

返回值为boolean,表示设置属性值是否成功

javascript
var o = {
  name: "lwf",
};

var res = Reflect.set(o, "name", "fwl");

console.log(res); // 打印true
console.log(o.name); // 打印"fwl"

var context = {
  val: "context",
};

Reflect.defineProperty(o, "p", {
  set: function (value) {
    console.log(this.val);
  },
});

Reflect.set(o, "p", "lwf", context);

Reflect.setPrototypeOf() 🔗

设置对象的原型

有两个参数

  • obj 目标对象
  • protoObj 原型对象(可为null

返回值为boolean,表示设置原型对象是否成功。

javascript
var o = {};

var res = Reflect.setPrototypeOf(o, null);

console.log(res); // 打印true

console.log(Reflect.getPrototypeOf(o)); // 打印null

后记 🔗

Reflect上的方法基本上在Object上都有相同名字的方法,主要是修改某些Object上方法的返回值,让其变得合理。newdelete等一些命令式操作都可以用函数的方式调用。

哦呐该,如果没有评论的话,瓦达西...