上一篇文章《Proxy 代理对象使用详解及原理总结》我们详细介绍了 Proxy 代理对象的使用及其工作原理(不了解的同学可以去看看哦),今天我们来聊聊它的好兄弟 Reflect 反射。
Reflect 是一个内置全局对象,将 Object 对象的一些属于语言内部的方法也部署到 Reflect 对象上,并且未来新方法将只部署在 Reflect 对象上。这么做的目的是为了把一些底层实现提取成一个的 API,并且高度聚合到一个对象上,让代码更清晰。
状态标记:很多反射方法返回布尔值,表示目的操作是否成功,而原始对象的操作是抛出错误或返回操作后的对象。
let obj = {name:'孤城浪人'};
let flag = Reflect.defineProperty(obj, 'age', {value:18});
let flag1 = Object.defineProperty(obj,'sex',{value:'男'});
console.log(flag);//true
console.log(flag1);//{name: '孤城浪人', age: 18, sex: '男'}
let obj = {name:'孤城浪人'};
console.log(obj.name);//孤城浪人
console.log(Reflect.get(obj,'name'));//孤城浪人
console.log('name' in obj);//true
console.log(Reflect.has(obj,'name'));//true
Reflect 对象的静态方法有13个与 Proxy 代理可以捕获的 13 种不同的基本操作一一对应,所以无论 Proxy 怎么修改默认行为,总可以在Reflect上获取原始的默认行为。
可以用 Reflact.apply() 代替 Function.prototype.apply.call()。这种情况往往发生在
现在你明白为什么说 Proxy 和 Reflect 是好兄弟了吧。主要原因就是 Proxy 会通过指定拦截函数修改代理对象的内部方法(即默认行为),但同时我们希望在修改默认行为的时候能够调用原来的默认行为,此时就需要 Reflect 对象了。Proxy 和 Reflect 相互配合往往能出奇效。
Reflect 对象一共有 13 个静态方法,它们与 Proxy 代理可以捕获 13 种不同的基本操作一一对应。
下面是一些简单用法
const obj = {
name: "孤城浪人",
test() {
return this == proxyObj;
},
};
console.log(Reflect.get(obj, "name")); // "孤城浪人"
console.log(Reflect.get(obj, "age")); // undefined
Reflect.set(obj, "name", "大帅哥^o^");
console.log(obj.name); // 大帅哥^o^
console.log(Reflect.has(obj, "age")); // false
console.log(Reflect.has(obj, "name")); // true
function Person(name) {
this.name = name;
}
const person = Reflect.construct(Person, ['张三']);
console.log(person);
console.log(Reflect.deleteProperty(obj, "age")); // true
console.log(obj); // { name: "孤城浪人",test() {return this == proxyObj;},}
console.log(Reflect.ownKeys(obj)); // => [ "name", "test","age" ]
上面这些方法如果传入的第一个参数不是对象会报错。而下面这些方法第一个参数不是对象会有自己的处理逻辑,具体请参考阮一峰老师的ECMAScript 6 入门 。
Reflect.defineProperty(obj, "age", { value: 18, configurable: true }); // 返回值 true|false
console.log(obj); // { name: "孤城浪人",age:18,test() {return this == proxyObj;},}
// 返回对象属性的描述对象
console.log(Reflect.getOwnPropertyDescriptor(obj, "name")); // {value: '大帅哥^o^', writable: true, enumerable: true, configurable: true}
console.log(Reflect.getPrototypeOf(obj) === Object.prototype); // true
Reflect.setPrototypeOf(obj, { name: "原型" });
console.log(obj.__proto__); //{ name: "原型" }
const obj = {
name: "孤城浪人",
age: 18,
sex: "男",
};
const ProxyObj = new Proxy(obj, {
get(target, property) {
console.log(`get触发,读取${property}属性`);
// return target[property];直接操作原始对象
return Reflect.get(target, property);
// return Reflect.get(...arguments); // 简写
},
set(target, property, value) {
console.log(`set触发,赋值${property}属性`);
// target[property] = value; 直接操作原始对象
Reflect.set(target, property, value);
// Reflect.set(...arguments); // 简写
},
});
console.log(ProxyObj.name);
ProxyObj.age = 20;
console.log(ProxyObj.age);

这里使用 Proxy 对 obj 进行了代理,指定了读取和设置拦截逻辑,在自定义逻辑中使用 Reflect 对象的 API 方法操作原始对象数据,而不是直接操作原始对象,一眼就看出对原始对象做了什么操作,代码更清晰了。
《Proxy 代理对象使用详解即原理总结》这篇文章曾说过 Reflect 可以解决代理对象的 this 问题,下面我们就好好聊聊它。
如果对象部署了读取函数(getter),则读取函数的 this 会自动绑定 receiver。如下例中,第一次输出 this 就是 me,第二次输出由于传入了 she 作为第三个参数 receiver,所以 this 指向 she,因此输出与第一次不同。
const me = {
name: "孤城浪人",
age: 18,
sex: "男",
get personInfo() {
return `个人信息:姓名:${this.name},年龄:${this.age},性别:${this.sex}`;
},
};
const she = {
name: "李四",
age: 20,
sex: "女",
};
console.log(Reflect.get(me, "personInfo")); // 个人信息:姓名:孤城浪人,年龄:18,性别:男
console.log(Reflect.get(me, "personInfo", she));// 个人信息:姓名:李四,年龄:20,性别:女
我们对这篇文章《Proxy 代理对象使用详解即原理总结》抛出问题的代码做如下简单改造:
const obj = {
_name: "孤城浪人",
get name() {
console.log(this)
return this._name;
},
};
const proxyObj = new Proxy(obj, {
get(target, property, receiver) {
//return target[property];
return Reflect.get(target,property,receiver);
},
});
const obj1 = {
__proto__:proxyObj,
_name:'李四'
}
console.log(obj.name);
console.log(obj1.name);

我们知道代理对象的 get 拦截函数有第三个参数,当 Proxy 代理对象作为其他对象的原型时,receiver 会指向被继承的对象(上面的代码 receiver 就是 obj1)。再将这个receiver 作为 Reflect.get 的第三个参数,那么此时 obj 中的 get 钩子中 this 就正确的指向了 obj1,也就能正确打印obj1.name了。
非常感谢这些文章给予我的帮助。
Reflect 对象就是内置的全局对象,因为它的静态方法与 Proxy 的捕获器一一对应,所以它们就像是一对孪生兄弟,常常放在一起使用,并且效果还不错。
好了,Reflect 的相关内容就介绍到这了,我是孤城浪人,一名正在前端路上摸爬滚打的菜鸟,欢迎大家关注!