JavaScript ES6 Reflect对象

 2016年09月06日    265     声明


Reflect 是ECMAScript 2015标准中新增的对象。该对象通常与Proxy(代理)对象一块使用,Proxy通过一些陷入指令来修改对象的默认形为;而Reflect提供了一些静态方法,这些方法与Proxy对象处理器中的方法一一对应,用于获取对象的默认形为、操作对象属性等。

  1. Reflect对象
  2. Reflect对象中的方法

1. Reflect对象

Reflect不同于其它全局对象,它提供了14个静态方法,不需要通过new关键字来构造实例,而可以支持使用Reflect.METHOD()的方式来调用。

Reflect对象中的方法主要有以下几类作用:

1.1 重构Object对象中的方法

Reflect对象中重构了部分ES5Object对象上方法,如:Reflect.getOwnPropertyDescriptor()(获取对象属性描述符)、Reflect.defineProperty()(定义新属性)。但Reflect.defineProperty()Object.defineProperty()实现并不一致,前者返回要添加属性的对象,后者返回一个表示对象 属性设置是否成功的布尔值。

如,使用Object.defineProperty()定义对象属性:

try {
  Object.defineProperty(obj, name, desc);
  // 属性定义成功
} catch (e) {
  // 定义失败或其它异常
}

Reflect.defineProperty()重构为:

if (Reflect.defineProperty(obj, name, desc)) {
  // 成功
} else {
  // 失败
}

Reflect对象中重构Object对象的方法还有:Reflect.deletePropertyReflect.preventExtensionsRelect.setReflect.setPrototypeOf


1.2 函数操作

一些对象属性操作,如判断一个属性在对象中是否存在ES5中这样判断name in obj,删除一个对象的一个属性delete obj[name]

Reflect将这些操作封装成了方法,分别是Reflect.has(obj, name)Reflect.deleteProperty(obj, name)


1.3 便捷可靠的函数执行

在JS中,要执行一个函数f,并向其传递一组参数args,还要绑定this关键字时。会向下面这样:

f.apply(obj, args)

apply可能会被用户重新定义,因此我们需要向下面这样执行:

Function.prototype.apply.call(f, obj, args)

Reflect中,提供了更加简洁的执行方式:

Reflect.apply(f, obj, args)


1.4 可变参数的构造函数

在ES6中,可以像下面这样定义一个可变参数的构造函数:

var obj = new F(...args)

而在ES5标准中,只能使用F.applyF.call来传递可变参数。但,构造函数并不支持。这时,可以使用Reflect对象的Reflect.construct方法:

var obj = Reflect.construct(F, args)


1.5 对象属性访问器

我们使用gettersetter来访问或设置对象属性。如:

var name = 'itbilu.com'; // 属性名
obj[name]; // 访问对象属性
obj[name] = value; // 设置属性值

Reflect对象中,提供了Reflect.setReflect.get来实现相同的功能。它们还支持一个额外的参数wrapper,用于包装this关键字的默认形为:

// wrapper 是一个包装器
Reflect.get(obj, name, wrapper); 
Reflect.set(obj, name, value, wrapper);

如,可以像下面这样定义包装器:

var obj = {
  set foo(value) { return this.bar(); },
  bar: function() {
    alert(1);
  }
};
var wrapper = {
  bar : function() {
    console.log("wrapper");
  }
}
Reflect.set(obj, "foo", "value", wrapper);


1.6 原型访问

对象的__proto__用于保存对象的原型,在ES5中可以使用Object.getPrototypeOf(obj)来访问对象的原型,而ES6还增加了Object.setPrototypeOf(obj, newProto)用于设置原型对象。Relect对象提供了同样的两个方法,Reflect.getPrototypeOf(obj)Reflect.setPrototypeOf(obj, newProto)用于访问或设置对象的原型。


2. Reflect对象中的方法

Reflect对象提供了14个静态方法,它们与Proxy对象的代理处理器具有相同的名称。其中有些方法在Object对象中也同样存在,它们的功能类似,但也有些差别。

2.1 Reflect.apply() - 调用函数

Reflect.apply(target, thisArgument, argumentsList)

调用一个函数,同时可以传入一个数组作为调用参数,功能类似于Function.prototype.apply()

在ES5中,我们会像下面这样调用一个函数:

Function.prototype.apply.call(Math.floor, undefined, [1.75]);

而使用Reflect.apply()

Reflect.apply(Math.floor, undefined, [1.75]); 

2.2 Reflect.construct() - 调用构造函数

Reflect.construct(target, argumentsList[, newTarget])

对构造函数进行new操作,相当于执行new target(...args)

var obj = new Foo(...args);
var obj = Reflect.construct(Foo, args);


2.3 Reflect.defineProperty() - 定义对象属性

Reflect.defineProperty(target, propertyKey, attributes)

定义对象属性,功能类似于Object.defineProperty()

var obj = {};
Reflect.defineProperty(obj, "x", {value: 7}); // true
obj.x; // 7


2.4 Reflect.deleteProperty() - 删除对象属性

Reflect.deleteProperty(target, propertyKey)

删除对象的某个属性,相当于执行delete target[name]

var obj = { x: 1, y: 2 };
Reflect.deleteProperty(obj, "x"); // true
obj; // { y: 2 }

var arr = [1, 2, 3, 4, 5];
Reflect.deleteProperty(arr, "3"); // true
arr; // [1, 2, 3, , 5]

// 属性不存在时,返回 true
Reflect.deleteProperty({}, "foo"); // true

// 如果属性是不可配置的,返回 false
Reflect.deleteProperty(Object.freeze({foo: 1}), "foo"); // false


2.5 Reflect.enumerate() - 返回可枚举属性

Reflect.enumerate(target)

返回目标对象身所有可枚举属性的字符串以及继承字符串属性的迭代器,与for...in遍历到的属性相同。

var obj = { x: 1, y: 2 };

for (var name of Reflect.enumerate(obj)) {
  console.log(name);
}
// logs "x" and "y"

注:访方法会在ES7标准中移除,不推荐使用


2.6 Reflect.get() - 获取对象属性

获取对象属性值,功能类似于obj[name]

// Object
var obj = { x: 1, y: 2 };
Reflect.get(obj, "x"); // 1

// Array
Reflect.get(["zero", "one"], 1); // "one"

// Proxy 处理器
var x = {p: 1};
var obj = new Proxy(x, {
  get(t, k, r) { return k + "bar"; }
});
Reflect.get(obj, "foo"); // "foobar"


2.7 Reflect.getOwnPropertyDescriptor() - 获取对象属性描述

Reflect.get(target, propertyKey[, receiver])

获取对象属性描述,功能类似于Object.getOwnPropertyDescriptor()

Reflect.getOwnPropertyDescriptor("foo", 0);
// TypeError: "foo" is not non-null object

Object.getOwnPropertyDescriptor("foo", 0);
// { value: "f", writable: false, enumerable: true, configurable: false }


2.8 Reflect.getPrototypeOf() - 获取对象原型

Reflect.getOwnPropertyDescriptor(target, propertyKey)

Object.getPrototypeOf()操作相同,用于返回对象的原型。

Reflect.getPrototypeOf({}); // Object.prototype
Reflect.getPrototypeOf(Object.prototype); // null
Reflect.getPrototypeOf(Object.create(null)); // null


2.9 Reflect.has() - 判断对象属性是否存在

Reflect.getPrototypeOf(target)

in操作符功能一样,用于判断对象属性是否存在。

Reflect.has({x: 0}, "x"); // true
Reflect.has({x: 0}, "y"); // false

// 如果该属性存在于原型链中,返回true 
Reflect.has({x: 0}, "toString");

// Proxy 对象的 .has() 处理器方法
obj = new Proxy({}, {
  has(t, k) { return k.startsWith("door"); }
});
Reflect.has(obj, "doorbell"); // true
Reflect.has(obj, "dormitory"); // false



2.10 Reflect.isExtensible() - 对象是否可扩展

Reflect.isExtensible(target)

功能类似于Object.isExtensible(),用于判断对象是否是可扩展的。

Reflect.isExtensible(1);
// TypeError: 1 is not an object

Object.isExtensible(1);
// false


2.11 Reflect.ownKeys() - 返回对象属性名数组

Reflect.ownKeys(target)

返回对象所包含的所有自身属性(不包含继承属性)的数组。

Reflect.ownKeys({z: 3, y: 2, x: 1}); // [ "z", "y", "x" ]
Reflect.ownKeys([]); // ["length"]

var sym = Symbol.for("comet");
var sym2 = Symbol.for("meteor");
var obj = {[sym]: 0, "str": 0, "773": 0, "0": 0,
           [sym2]: 0, "-1": 0, "8": 0, "second str": 0};
Reflect.ownKeys(obj);
// [ "0", "8", "773", "str", "-1", "second str", Symbol(comet), Symbol(meteor) ]
// Indexes in numeric order, 
// strings in insertion order, 
// symbols in insertion order


2.12 Reflect.preventExtensions() - 设置对象为不可扩展

Reflect.preventExtensions(target)

设置对象为不可扩展的,功能类似于Object.preventExtensions()


2.13 Reflect.set() - 设置对象属性值

Reflect.set(target, propertyKey, value[, receiver])

设置对象属性值,功能类似于obj[name]

// Object
var obj = {};
Reflect.set(obj, "prop", "value"); // true
obj.prop; // "value"

// Array
var arr = ["duck", "duck", "duck"];
Reflect.set(arr, 2, "goose"); // true
arr[2]; // "goose"

// 可以简写为数组
Reflect.set(arr, "length", 1); // true
arr; // ["duck"];

// 只有一个参数时,属性 key 和 value 是 "undefined"
var obj = {};
Reflect.set(obj); // true
Reflect.getOwnPropertyDescriptor(obj, "undefined");
// { value: undefined, writable: true, enumerable: true, configurable: true }


2.14 Reflect.setPrototypeOf() - 设置对象原型

Reflect.setPrototypeOf(target, prototype)

设置对象原型,与Object.setPrototypeOf()功能相同

Reflect.setPrototypeOf({}, Object.prototype); // true

// 可以将对象的 [[Prototype]] 设置为 null
Reflect.setPrototypeOf({}, null); // true

// 对象不可扩展时,返回 false
Reflect.setPrototypeOf(Object.freeze({}), null); // false

// 原型链循环时,返回 false
var target = {};
var proto = Object.create(target);
Reflect.setPrototypeOf(target, proto); // false