前言
最近在网上冲浪无意看到了这个问题,我的第一反应就是直接一脸问号。据说这是一道字节面试题,所以就赶紧学习了一下。
先说结论,proxy和reflect解决了以下几个问题:
- 契合性
- 原子性
- 一致性
- 解决this指向问题
那么我们来逐一分析解决的这几个问题吧,首先来看一段代码:
// 原始对象
const obj={
name:'张三',
get sayMyName(){
return this.name
}
}
// 代理对象
const obj2=new Proxy(obj,{
get(target,prop,receiver){
return target[prop]
}
set(target,prop,value,receiver){
return Reflect.set(target,prop,value,receiver)
}
...
})
console.log(obj2.sayMyName)
在上述代码中,我们创建了一个obj对象和obj2代理obj。obj2可以代理obj的13种的方法,reflect也是与之对应的可以操作那13种方法,因此,这解决了第一个问题契合性
,proxy和reflect有着一一对应的方法操作,而且参数也是一模一样的。
在拦截写操作(set方法)如果使用常规key-value写法的时候,我们需要给定一个返回值类型为boolean的返回值。但是,我们编写set方法时候返回值要么是true要么是false,而且一旦设置了就无法改变。如果我们设置为true时候,要是我们给它设置属性失败了,但是返回的结果又是true,这样就打破了原子性,所以这样操作是不对的。那么我们应该使用reflect了,reflect的返回值正好也是一个布尔值,与set方法是一致的。这样巧妙的解决了原子性
问题和一致性
问题。
请你思考一下上面的this指向的是谁?从上面编写代码上,感觉应该是想要通过obj2代理obj调用其sayMyName方法让它返回张三。我们运行一下上面那段代码也确实如此,this指向的就是obj,所以返回结果自然就是张三了。
那么接下有几个问题要考虑,在那处的this指向的是原始对象。我们原始对象可以收集依赖吗?很显然那些get、set方法是不在原始对象身上的,所以原始对象是不能收集依赖的。那么就应该用reflect的第三个参数receiver来指向代理对象自身来解决这个问题,通过此操作把this指向改变成代理对象obj2了,所以响应式就自然可以收集到了,同时也解决this指向问题
。上面obj2的get函数就应该改正一下
const obj2=new Proxy(obj,{
get(target,prop,receiver){
return Reflect.get(target, prop, receiver)
}
...
})
Ending
当我看完原文讲解的时候,感受到了一个大大的妙字。不知各位看完感受如何,要是面试被问到如此问题,你该如何回答呢?