Reflect
Reflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法。这些方法与处理器对象的方法相同。Reflect 不是一个函数对象,因此它不是不可构造的。
设计目的:
-
将 Object 对象的一些明显属于语言内部的方法(比如
Object.defineProperty
),放到 Reflect 对象上。现阶段,某些方法同时在 Object 和 Reflect 对象上部署,未来的新方法将只部署在 Reflect 对象上。也就是说,从 Reflect 对象上可以拿到语言内部的方法。 -
修改某些 Object 方法的返回结果,让其变得更合理。比如,
Object.defineProperty(obj, name, desc)
在无法定义属性时,会抛出一个错误,而Reflect.defineProperty(obj, name, desc)
则会返回false
。 -
让 Object 的 命令式操作 都变成 函数行为。比如
name in obj
和delete obj[name]
,而Relfect.has(obj, name)
和Reflect.deleteProperty(obj, name)
让它们变成了函数行为 -
Reflect 对象的方法与 Proxy 对象的方法一一对应,只要是 Proxy 对象的方法,就能在 Reflect 对象上找到对应的方法。这就让 Proxy 对象可以方便地调用对应的 Reflect 方法,完成默认行为,作为修改行为的基础。也就是说,不管 Proxy 怎么修改默认行为,你总可以在 Reflect 上获取默认行为。
Proxy(target, {
set: function(target, name, value, receiver) {
const success = Reflect.set(target, name, value, receiver);
if (success) {
console.log('property ' + name + ' on ' + target + ' set to ' + value);
}
return successs;
},
});
上面代码中,Proxy 方法拦截 target
对象的属性赋值行为。它采用 Reflect.set
方法将值赋值给对象的属性,确保完成原有的行为,然后再部署额外的功能。
const proxy = 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 方法,保证原生行为能够正常执行。添加的工作,就是将每一个操作输出一行日志。
有了 Reflect 对象以后,很多操作会更易读。
// 老写法
Function.prototype.apply.call(Math.floor, undefined, [1.75]);
// 1
// 新写法
Reflect.apply(Math.floor, undefined, [1.75]);
// 1
与大多数全局对象不同,Reflect 没有构造函数,你不能将其与一个 new
运算符一起使用,或者 Reflect 对象作为一个函数来调用。Reflect 的所有属性和方法都是静态的(就像 Math 对象)。
静态方法
静态方法 | 说明 |
---|---|
Reflect.apply |
对函数进行调用操作,同时传入一个数组作为调用参数,与 Function.prototype.apply 功能类似 |
Reflect.construct |
对构造函数进行 new 操作,相当于执行 new target(...args) |
Reflect.defineProperty |
和 Object.defineProperty 类似 |
Reflect.deleteProperty |
作为函数的 delete 操作符,相当于执行 delete target[name] |
Reflect.get |
获取对象属性值 |
Reflect.getOwnPropertyDescriptor |
类似于 Object.getOwnPropertyDescriptor |
Reflect.getPrototypeOf |
类似于 Object.getPrototypeOf |
Reflect.has |
判断对象是否存在某个属性,和 in 运算符的功能完全相同 |
Reflect.isExtensible |
类似于 Object.isExtensible |
Reflect.ownKeys |
返回一个包含所有自身属性(不包含继承属性)的数组 |
Reflect.preventExtensions |
类似于 Object.preventExtensions |
Reflect.set |
将值分配给属性的函数,返回 Boolean,如果成功,则返回 true |
Reflect.setPrototypeOf |
类似于 Object.setPrototyeOf |
与传统方法的对比优势
Reflect 操作对象更加符合面向对象,操作对象的方法全部都挂在 Reflect。
Reflect 操作对象 | 老方法操作对象 | |
---|---|---|
面向对象 | 全部挂在 Reflect 对象上,更加符合面向对象 |
各种指令方法,= 、in 、delete |
函数式 | 所有方法都是函数 | 命令式、赋值、函数混用 |
规范报错 | defineProperty 无效返回 false ,后面几个方法参数非法报错 |
defineProperty 无效报错,后面几个方法参数非法不报错 |
方法扩展 | 参数 receiver 指定 this 指向 |
不能 |
示例
观察者模式
观察者模式(Observer mode)指的是函数自动观察数据对象,一旦对象有变化,函数就会自动执行。
const person = observerable({
name: 'Zhange San',
age: 47,
});
function print() {
console.log(`${person.name}, ${person.age}`);
}
observe(print);
person.name = 'Li Si';
// Li Si, 47
上面代码中,数据对象 person
是观察目标,函数 print
是观察者。一旦数据对象发生变化,print
就会自动执行。
下面,使用 Proxy 写一个观察者模式的最简单实现,即实现 observeable
和 observe
这两个函数。思路是 observable
函数返回一个原始对象的 Proxy 对象,拦截赋值操作,触发充当观察者的各个函数。
const queuedObservers = new Set();
const observe = fn => queuedObservers.add(fn);
const observable = obj => new Proxy(obj, { set });
function set(target, key, value, receiver) {
const result = Reflect.set(target, key, value, receiver);
queuedObservers.forEach(observer => observer());
return result;
}
获取设置反射属性
const Ironman = {
firstName: 'Tony',
lastName: 'Stark',
get fullName() {
return `${this.firstName} ${this.lastName}`;
},
};
// 获取自身属性,新老方法都可以实现
Reflect.get(Ironman, 'firstName');
// Tony
Reflect.get(Ironman, 'lastName');
// Tony
Reflect.get(Ironman, 'fullName');
// Tony Stark
const Spiderman = {
firstName: 'Peter',
lastName: 'Parker',
};
// 获取反射属性,只有 Reflect 可以实现
Reflect.get(Ironman, 'fullName', Spiderman);
// Peter Parker