Reflect
1、是什么?
- window的原生对象,但不能通过new来生成实例;
- 对对象的属性的操作,都可以通过这个来完成(读、写、定义属性等);
- 返回值更加合理(失败返回false而不是抛错);
- 函数式调用(而非属性式)
- 在使用Proxy时,使用本方法效果比直接通过对象属性调用效果更好
2、如何使用(几种常见方法示例)
2.1、读
Reflect.get(target, propertyKey[, receiver])
拦截对target对象的propertyKey属性的读取,返回这个属性的值。
- 参数一是被访问的对象;
- 参数二是属性名;
- 参数三当遇见getter时,将getter的this指向他
参数一和参数二好理解,如代码:
let target = {
_test: 1,
get test() {
return this._test;
}
};
Reflect.get(target, "_test"); //1
Reflect.get(target, "test"); //1
参数三比较特殊,当遇见getter时,getter的this将指向他。下面代码可以很好的体现这一点:
let receiver = {_test: 2}
let target = {
_test: 1,
get test() {
console.log(this === target, this === receiver);
return this._test;
}
};
Reflect.get(target, "test", receiver);
// false true
// 2
target.test;
// true false
// 1
通过Reflect.get调用时(且有第三个参数),this指向参数三;
当通过target.test调用时,this指向对象本身;
2.2、写
Reflect.set(target, propertyKey, value[, receiver])
如果理解Reflect.get
了,那么理解Reflect.set
自然也很简单了。
- 参数一是目标对象;
- 参数二是属性名;
- 参数三是要设置的值;
- 参数四是遇见setter时,this指向的目标;
- 返回值是是否设置成功,成功则为true,报错为false;
如代码:
let receiver = {_test: 2};
let target = {
_test: 1,
get test() {
return this._test;
},
set test(val) {
this._test = val;
}
};
Reflect.set(target, "test", "a"); //true
target.test; //"a"
//修改setter的this指向目标
Reflect.set(target, "test", "b", receiver); //true
target.test; //"a"
receiver._test; //"b"
//修改被禁止修改的属性
Object.defineProperty(target, "test", {
value: 1
});
Reflect.set(target, "test", "a"); //false
target.test; //1
3、观察者模式
观察者模式是结合了Proxy代理以及Reflect实现的,重点是Proxy。
我这里对阮一峰给的示例代码略有修改,并添加注释,以示使用方法:
//存储观察者函数,用数组也可以
const queuedObservers = new Set();
// const queuedObservers = [];
//定义observe函数,执行本函数会将观察者函数添加到queuedObservers中,返回的是观察者函数的列表
const observe = fn => queuedObservers.add(fn);
// const observe = fn => queuedObservers.push(fn);
//定义observable函数,将该对象添加代理后返回proxy实例
const observable = obj => new Proxy(obj, {set});
//handler,执行时会调用观察者里函数里的每个观察者
function set(target, key, value, receiver) {
const result = Reflect.set(target, key, value, receiver);
queuedObservers.forEach(observer => observer(key, value));
return result;
}
//对对象进行代理绑定
const person = observable({
name: '张三',
age: 20
});
//定义2个函数
function printKey(key, value) {
console.log("key: " + key)
}
function printValue(key, value) {
console.log("value: " + value)
}
//将函数设置为观察者函数
observe(printKey);
observe(printValue);
//调用person(proxy实例)的属性name
person.name = '李四';
//输出
//key: name
//value: 李四
//创建另外一个被观察的对象(空)
const foo = observable({});
//给他添加一个属性(触发set),因此会触发所有添加到观察者里的函数
foo.fruit = "梨";
//输出
//key: fruit
//value: 梨
简单来说:
- 可以创建一个函数用于进行proxy绑定(observable);
- 并且创建一个观察者列表,用于添加你的观察者函数(列表queuedObservers,添加函数observe);
- 因为是proxy,所以你还需要一个handler,并且假定你handler里的set会执行所有观察者函数;
- 然后所有被绑定过的函数,在使用它的返回值时(即proxy实例),你设置对象属性就会触发handler的set,而set就会执行所有观察者函数;
- 于是有了上面示例代码发生的情况;