Reflect 是 ES2015 新增的内置对象,它提供拦截 JavaScript 操作的方法。Reflect是一个纯静态对象,而不是一个函数对象,因此它是不可构造的。
与大多数全局对象不同Reflect并非一个构造函数,所以不能通过new运算符对其进行调用,或者将Reflect对象作为一个函数来调用。Reflect的所有属性和方法都是静态的,就像Math对象,只能类似 Math.random 的方式调用。
Reflect 内部封装了一系列对对象的底层操作,一共 14 个,其中 1 个被废弃,还剩下 13 个。
Reflect 的静态方法和 Proxy 描述中的方法完全一致。也就是说 Reflect 成员方法就是 Proxy 处理对象的默认实现。
Proxy 对象默认的方法就是调用了 Reflect 内部的处理逻辑,也就是如果我们调用 get 方法,那么在内部,proxy 就是将 get 原封不动的交给了 Reflect,如下。
const proxy = new Proxy(obj, {get(target, property) {return Reflect.get(target, property);}})
Reflect 和 Proxy 没有绝对的关系,我们一般将他们两个放在一起讲是为了方便对二者的理解。
那为什么会有 Reflect 对象呢,其实他最大的用处就是提供了一套统一操作 Object 的 API。判断对象是否存在某一个属性,可以使用 in 操作符,但是不够优雅,还可以使用Reflect.has(obj, name); 删除一个属性可以使用 delete,也可以使用 Reflect.deleteProperty(obj, name); 获取所有属性名可以使用 Object.keys, 也可以使用 Reflect.ownKeys(obj); 我们更推荐使用 Reflect 的 API 来操作对象,因为他才是未来。
概述
Reflect对象与Proxy对象一样,也是 ES6 为了操作对象而提供的新 API。Reflect对象的设计目的有这样几个。
(1) 将Object对象的一些明显属于语言内部的方法(比如Object.defineProperty),放到Reflect对象上。现阶段,某些方法同时在Object和Reflect对象上部署,未来的新方法将只部署在Reflect对象上。也就是说,从Reflect对象上可以拿到语言内部的方法。
(2) 修改某些Object方法的返回结果,让其变得更合理。比如,Object.defineProperty(obj, name, desc)在无法定义属性时,会抛出一个错误,而Reflect.defineProperty(obj, name, desc)则会返回false。
// 老写法try {Object.defineProperty(target, property, attributes);// success} catch (e) {// failure}// 新写法if (Reflect.defineProperty(target, property, attributes)) {// success} else {// failure}
(3) 让Object操作都变成函数行为。某些Object操作是命令式,比如name in obj和delete obj[name],而Reflect.has(obj, name)和Reflect.deleteProperty(obj, name)让它们变成了函数行为。
// 老写法'assign' in Object // true// 新写法Reflect.has(Object, 'assign') // true
(4)Reflect对象的方法与Proxy对象的方法一一对应,只要是Proxy对象的方法,就能在Reflect对象上找到对应的方法。这就让Proxy对象可以方便地调用对应的Reflect方法,完成默认行为,作为修改行为的基础。也就是说,不管Proxy怎么修改默认行为,你总可以在Reflect上获取默认行为。
Proxy(target, {set: function(target, name, value, receiver) {var success = Reflect.set(target, name, value, receiver);if (success) {console.log('property ' + name + ' on ' + target + ' set to ' + value);}return success;}});
上面代码中,Proxy方法拦截target对象的属性赋值行为。它采用Reflect.set方法将值赋值给对象的属性,确保完成原有的行为,然后再部署额外的功能。
下面是另一个例子。
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方法,保证原生行为能够正常执行。添加的工作,就是将每一个操作输出一行日志。
有了Reflect对象以后,很多操作会更易读。
// 老写法Function.prototype.apply.call(Math.floor, undefined, [1.75]) // 1// 新写法Reflect.apply(Math.floor, undefined, [1.75]) // 1
静态方法
Reflect对象一共有 13 个静态方法。
- Reflect.apply(target, thisArg, args)
- Reflect.construct(target, args)
- Reflect.get(target, name, receiver)
- Reflect.set(target, name, value, receiver)
- Reflect.defineProperty(target, name, desc)
- Reflect.deleteProperty(target, name)
- Reflect.has(target, name)
- Reflect.ownKeys(target)
- Reflect.isExtensible(target)
- Reflect.preventExtensions(target)
- Reflect.getOwnPropertyDescriptor(target, name)
- Reflect.getPrototypeOf(target)
- Reflect.setPrototypeOf(target, prototype)
上面这些方法的作用,大部分与Object对象的同名方法的作用都是相同的,而且它与Proxy对象的方法是一一对应的。下面是对它们的解释。
deleteProperty(target, name)
ES6新增的方法,Reflect.deleteProperty(对象,属性名),返回一个布尔值。
const pet = {species: 'dog',age: 3,name: 'celeste',gender: 'female'};Reflect.deleteProperty(pet, 'gender')Object.keys(pet); // ['species', 'age', 'name']
