超级面板
文章目录
最新文章
最近更新
文章分类
标签列表
文章归档

ES6 - Reflect 对象

Reflect 对象是 ES6 提供的一个内置的用于操作对象的的全局对象。但和其他的全局对象不同,Reflect 对象没有构造函数,不能使用 new关键字 创建对象。

Reflect 静态方法

Reflect 对象提供以下静态函数,大部分与 Object 对象上同名方法的作用一样,同时将一些命令式的语法函数化。

Reflect.apply

静态方法 Reflect.apply() 通过指定的参数列表发起对目标函数的调用,类似于 Function.prototype.apply.call(target, thisArgument, argumentsList)

Reflect.apply(target, thisArgument, argumentsList)

  • target: 目标函数
  • thisArgument: 绑定的 this 对象
  • argumentsList: target函数调用时传入的实参列表,该参数应该是一个类数组的对象
  • throws: 如果 target 不可被调用抛出 TypeError 异常

一般来说,若需要绑定函数的 this 对象通常可以 func.apply(obj, args) 但是有时候函数可能自定义了 apply 方法,为了方便,我们可能需要写成 Function.prototype.apply.call(func, obj, args),而采用 Reflect.apply,可以简化操作,并且更容易理解:

// 除非确认 func.apply 未被其他人定义或改写,方可使用
func.apply(obj, args)

// 保险方式,稍显冗余
Function.prototype.apply.call(func, obj, args);

// 保险方式,更加直观
Reflect.apply(func, obj, args);

Reflect.construct

对构造函数进行 new 操作,相当于执行 new target(…args),提供了一种不使用new,来调用构造函数的方法。

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

  • target: 目标对象
  • argumentsList: 参数列表
  • [newTarget[Function]]: 可省略,类似于 new.target 操作符,指向构造方法或函数的引用
  • throws: 如果 target 或者 newTarget 不是构造函数,抛出 TypeError 异常
function Foo(val) {
this.val = val;
}

// new
var foo = new Foo(1);

// Reflect.construct
var foo1 = Reflect.construct(Foo, [1]);

使用 newTarget 参数:

function newTarget() {}
var result = Reflect.construct(Array, [], newTarget);

Reflect.getPrototypeOf(result); // newTarget.prototype
Reflect.getPrototypeOf(result) === newTarget.prototype // true
Array.isArray(result); // true

顺便提下,在普通的函数调用中(和作为构造函数来调用相对),new.target 的值是 undefined,因此可以用来检测是否将构造函数作为普通函数调用:

function Foo() {
if (!new.target)
throw "Foo() must be called with new";
console.log("Foo instantiated with new");
}

Foo(); // throws "Foo() must be called with new"
new Foo(); // logs "Foo instantiated with new"

Reflect.defineProperty

精确添加或修改对象上的属性,和 Object.defineProperty 类似。

Reflect.defineProperty(target, propertyKey, attributes)

Object.defineProperty 返回结果为第一个参数对象,而 Reflect.defineProperty 返回结果为布尔型,表示操作是否成功,两者检查操作结果方式如下:

// Object.defineProperty
try {
Object.defineProperty(obj, name, desc);
// success
} catch (e) {
// failure
}

// Reflect.defineProperty
if (Reflect.defineProperty(obj, name, desc)) {
// success
} else {
// failure
}

Reflect.deleteProperty

删除对象的某个属性,类似于执行 delete target[name],区别在于一个是操作符,一个是函数调用。

Reflect.deleteProperty(target, propertyKey)

// 使用 delete
delete {foo: 1}.foo; // true;

// 使用 Reflect.deleteProperty
Reflect.deleteProperty({foo: 1}, "foo"); // true

//freeze object
delete Object.freeze({foo: 1}).foo; // false
Reflect.deleteProperty(Object.freeze({foo: 1}), "foo"); // false

Reflect.enumerate

已过时

Reflect.get

获取对象上某个属性的值,类似于 target[propertyKey]。

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

  • target: 目标对象
  • propertyKey: 需要获取的值的键值
  • [receiver]: 可省略,如果遇到 getter,则 getterthis 指向的上下文为 receiver
  • return[Any]: 任意类型
Reflect.get({x: 1, y: 2}, x) // 1
Reflect.get([1, 2, 3], 0) // 1

Reflect.get("asdf", 0) // TypeError。参数 target 类型只能是 Object
Reflect.get(new String("asdf"), 0) // a

注意:第三个参数表示使用指定的 receiver,表示指定的 getter 中存在的 this 指向的上下文:

var foo = {
_val : 'foo',
get val() {
return 'value: ' + this._val;
}
};

var bar = {
_val : 'bar',
get val() {
return 'value: ' + this._val;
}
};

console.log(Reflect.get(foo, "val")); // value: foo
console.log(Reflect.get(foo, "val", bar)); // value: bar,foo.val() 中的 this 指向 bar

Reflect.set

设置对象身上某个属性的值,类似于 target[name] = val。

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

  • target: 目标对象
  • propertyKey: 需要获取的值的键值
  • [receiver]: 可省略,如果遇到 setter,则 setterthis 指向的上下文为 receiver
  • return[Boolean]: 任意类型
var obj = {
val: 1
}

obj.val // 1

Reflect.set(obj, 'val', 2);
obj.val // 2

Reflect.set(obj, 'setval', 3);
obj.val // 3

注意:第三个参数表示使用指定的 receiver,表示指定的 setter 中存在的 this 指向的上下文:

var foo = {
val: 1,
set setval(value) {
return this.val = value;
},
}

var bar = {
val: 2,
}

foo.val // 1
bar.val // 2

Reflect.set(foo, 'setval', 3, bar);

foo.val // 1
bar.val // 3

如果第一个参数不是对象,Reflect.set 会报错。

Reflect.getOwnPropertyDescriptor

如果属性在对象中存在,则返回给定的属性的属性描述符,否则返回 undefined。类似于 Object.getOwnPropertyDescriptor(),唯一不同在于 Object 对非对象参数会进行强制类型转换,Reflect 会抛出 TypeError 异常。

Reflect.getOwnPropertyDescriptor(target, propertyKey)

Reflect.getOwnPropertyDescriptor({x: "hello"}, "x");
Object.getOwnPropertyDescriptor({x:"1"}, "x");
// 都返回 Object {value: "hello", writable: true, enumerable: true, configurable: true}

这两个的区别是 Object.getOwnPropertyDescriptor 会包装非对象参数,将其强制转换为对象, Reflect.getOwnPropertyDescriptor 会抛出 TypeError 异常:

Reflect.getOwnPropertyDescriptor("x", 0); 
//抛出异常: Uncaught TypeError: Reflect.getOwnPropertyDescriptor called on non-object

Object.getOwnPropertyDescriptor("x", 0);
//返回 Object {value: "x", writable: false, enumerable: true, configurable: false}

Reflect.getPrototypeOf

返回指定对象的原型。类似于 Object.getPrototypeOf,唯一不同在于 Object 对非对象参数会进行强制类型转换,Reflect 会抛出 TypeError 异常。

Reflect.getPrototypeOf(target)

var proto = {};
var obj = Object.create(proto);

Object.getPrototypeOf(obj) === proto; // true
Reflect.getPrototypeOf(obj) === proto; // true

如果参数为非对象,将抛出 TypeError 异常,而 Object.isExtensible 会将其强制转换为一个对象:

Object.isExtensible(1); // false

Reflect.isExtensible(1);
// Uncaught TypeError: Reflect.isExtensible called on non-object

Reflect.setPrototypeOf

给对象设置原型, 即更改对象的 __proto__ 属性。类似于 Object.setPrototypeOf()。

Reflect.setPrototypeOf(target, prototype)

注意:如果第一个非对象参数 Object 会对直接返回该参数,Reflect 会抛出 TypeError 异常:

Object.setPrototypeOf(1, {}); //1

Reflect.setPrototypeOf(1, {});
// Uncaught TypeError: Reflect.setPrototypeOf called on non-object

注意:prototype 只能为 Object 或者 null,否则也会抛出 TypeError 异常:

Object.setPrototypeOf({}, 1);
// Uncaught TypeError: Object prototype may only be an Object or null

Reflect.setPrototypeOf({}, 1);
// Uncaught TypeError: Object prototype may only be an Object or null

Reflect.has

判断一个对象是否存在某个属性,和 in 操作符 的功能相同

Reflect.has(target, propertyKey)

var obj = {
foo: 'foo',
};

// 使用 in 操作符
'foo' in obj // true

// 使用 Reflect.has
Reflect.has(obj, 'foo') // true

Reflect.isExtensible

判断一个对象是否可以扩展。类似于 Object.isExtensible,唯一不同在于 Object 对非对象参数会进行强制类型转换,Reflect 会抛出 TypeError 异常。

Reflect.isExtensible(target)

var obj = {};

Object.isExtensible(obj); // true

Reflect.isExtensible(obj); // true

// 阻止对象扩展
Reflect.preventExtensions(obj);

Object.isExtensible(obj); // false
Reflect.isExtensible(obj); // false

// 被冻结的对象不可扩展
obj = Object.freeze({})

Object.isExtensible(obj); // false
Reflect.isExtensible(obj); // false

// 被密封的对象不可扩展
obj = Object.seal({})

Object.isExtensible(obj); // false
Reflect.isExtensible(obj); // false

Reflect.ownKeys

返回一个包含所有自身属性(不包含继承属性)的数组。Object 对象不存在这个方法,而是提供两个独立的方法:getOwnPropertyNamesgetOwnPropertySymbolsReflect.ownKeys 返回值等同于 Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target))

Reflect.ownKeys(target)

var obj = {
1: 1,
2: 2,
a: 'a',
b: 'b',
[Symbol('c')]: 'c',
[Symbol('d')]: 'd'
}

Object.getOwnPropertyNames(obj).concat(Object.getOwnPropertySymbols(obj));
// ["1", "2", "a", "b", Symbol(c), Symbol(d)]

Reflect.ownKeys(obj);
// ["1", "2", "a", "b", Symbol(c), Symbol(d)]

Reflect.preventExtensions

用于让一个对象变为不可扩展,返回布尔值,表示是否操作成功,类似于 Object.preventExtensions, Object 对非对象参数会进行强制类型转换,Reflect 会抛出 TypeError 异常。

Reflect.preventExtensions(target)

var obj = {};

Reflect.isExtensible(obj); // true
Reflect.preventExtensions(obj); // true
Reflect.isExtensible(obj); // false

这两个函数都只能阻止一个对象不能再添加新的自身属性,仍然可以为该对象的原型添加属性:

var obj = {};
var proto = {}
obj.a = "a";
obj.__proto__ = proto;

Reflect.preventExtensions(obj); // true

obj.b = "b";
console.log(obj.b); // undefined
proto.b = "b"
console.log(obj.b); // b

注意:使用 Object.definePropertyReflect.defineProperty 方法为一个不可扩展的对象添加新属性会抛出异常:

var obj = { removable: true };
Reflect.preventExtensions(obj);
Reflect.defineProperty(obj, "new", { value: 1 }); // 抛出TypeError异常
Object.defineProperty(obj, "new", { value: 1 }); // 抛出TypeError异常

注意:在严格模式下,为不可扩展对象添加新属性会抛出TypeError异常:

var obj = { removable: true };
Reflect.preventExtensions(obj);
function fail() {
"use strict";
obj.newProperty = "FAIL";
}
fail(); // throws a TypeError

总结

Reflect 上的几乎所有操作都是原本存在的操作, Reflect 对其做了统一的整合,具有以下优点:

将对象上的操作函数化

过去的某些对象上的操作是命令式的,如 name in objdelete obj[name], Reflect 将这些操作统一为函数调用

name in obj
// 使用 Reflect
Reflect.has(obj, 'name')

delete obj[name]
// 使用 Reflect
Reflect.deleteProperty(obj, 'name')

更有用的返回值

Reflect 上有好些方法和 Object 上的一样,但是修改了返回的结果,比如 Object.defineProperty 在无法定义属性时,会抛出错误, Reflect 则返回 false

// Object.defineProperty
try {
Object.defineProperty(obj, name, desc);
// success
} catch (e) {
// failure
}

// Reflect.defineProperty
if (Reflect.defineProperty(obj, name, desc)) {
// success
} else {
// failure
}

其他相似的方法如 Reflect.deletePropertyReflect.preventExtensionsRelect.setReflect.setPrototypeOf 也是如此。

控制访问器中 this 的绑定

Reflect.getReflect.set 通过提供额外的第三个参数 receiver 可以方便的控制访问器 gettersetter 中的 this 的指向,比如在访问器中不想使用自己的属性或方法,而是使用包装器的属性或方法:

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

提供和 Proxy 一一对应的方法

Proxy 对象支持的所有方法都可以在 Reflect 上找到,Proxy 对象可以方便地调用对应的 Reflect 方法,执行基础的操作。同时,不论 Proxy 做了怎样的修改,都可以通过 Reflect 获取原始行为。

浏览器兼容性

Chrome Edge Firefox IE Opera Safari
49.0 Yes 42.0 未实现 未实现 10

参考