前言
彦祖们,阅读本文前,希望你对 Proxy 有基础的了解
场景
今天打车去公司喝咖啡的途中,刷到了袁老师讲解的这样一道字节面试题
题目如下:
// 请你实现一下这个 obj 对象,使得最后的输出结果为 10 (1+2+3+4)
const res = obj[1][2][3] + 4
题目分析
第一眼看到这个题的时候
笔者大致方向就是设置一个 get
拦截器陷阱,然后返回一个新的对象
这个对象的属性值是当前的属性值加上上一个的属性值
这时候我们就应该想到了代理对象 proxy
最基础的 proxy
先来看下最基础的 proxy
const obj = new Proxy({}, {
get(target, propKey, receiver) {
return Reflect.get(target, propKey, receiver)
}
})
obj[1] // 那么这时候 propKey 就是 1
递归返回 proxy
但是我们如何实现 obj[1][2]
这种效果呢
目前直接访问会报错
Uncaught TypeError: Cannot read properties of undefined (reading '2')
其实和 obj
访问 1
是一样的,我们继续返回一个 proxy
代理对象即可
看下大致的代码
const obj = new Proxy({}, {
get(target, propKey, receiver) {
// 继续返回代理对象
return new Proxy({}, {
get(target, propKey, receiver) {
return Reflect.get(target, propKey, receiver)
}
})
}
})
这样无限递归, 我们就可以抽离一个 createProxy
函数来实现这个效果
const createProxy = ()=>{
return new Proxy({}, {
get(target, propKey, receiver) {
return Reflect.get(target, propKey, receiver)
}
})
}
这样初始化的 obj
就可以通过 createProxy
获取
const obj = createProxy()
obj[1][2][3]... // 发现已经不会再报错了
改进 createProxy
言归正传,回到题目中
obj[1]
需要获取到 1
obj[2]
需要获取到 1+2
obj[3]
需要获取到 1+2+3
改造一下我们的 createProxy
函数
// 这里因为是相加,我们默认 value=0 不会引起任何副作用
const createProxy = (value = 0)=>{
return new Proxy({}, {
get(target, propKey, receiver) {
// 其中的 propKey 就是当前属性值 1,2,3
// value 就是上一个属性值的返回值 0,1,3
// 那么 value + Number(propKey) 就是 1,3,6
return createProxy(value + Number(propKey))
}
})
}
const obj = createProxy()
实现 proxy + number
此时我们的 obj
还是一个 proxy
代理对象,那么如何实现 proxy + 4
呢?
如果我们直接操作,会报错
Uncaught TypeError: Cannot convert a Symbol value to a number
也就是说 Symbol
类型无法转换为 number
类型
Symbol.toPrimitive
关键点来了, Symbol.toPrimitive
它是专门处理对象的原始值转换的
我们可以理解它是一个对象 => 原始值
的一个拦截器陷阱
developer.mozilla.org/en-US/docs/…
所以我们可以判断下这个 propKey
是否为 Symbol.toPrimitive
(注意,它要返回一个函数!)
看下此时的代码
const createProxy = (value = 0)=>{
return new Proxy({}, {
get(target, propKey, receiver) {
// 如果为 Symbol.toPrimitive 那么返回一个函数,这个函数直接返回 value
if(propKey === Symbol.toPrimitive){
return ()=>value
}
return createProxy(value + Number(propKey))
}
})
}
// 测试一下
const obj = createProxy()
const res = obj[1][2][3] + 4 // 10
题外话
接下来,笔者又想到了一个场景,如果拿掉 + 4
, 我们如何来实现以下的 obj
呢?
const res = obj[1][2][3]() // 10
其实也非常简单,我们日常的工作中,处理比较多的是 Object
其实 Proxy
也可以代理 Function
,对应一个 apply
访问器陷阱
有了这个知识点,我们就可以实现这个效果了
const createProxy = (value = 0)=>{
return new Proxy(function(){}, {
get(target, propKey, receiver) {
return createProxy(value + Number(propKey)) //这个和之前一样 递归返回
},
apply(target, thisArg, argArray) {
return value // 进入 apply 陷阱,直接返回 value
}
})
}
const obj = createProxy()
const res = obj[1][2][3]() // 测试一下 完美通过
写在最后
作为业务牛马的笔者,很少会用到这种深度的知识点
希望能帮助优秀的彦祖们,在处理疑难杂症时变得更得心应手
感谢彦祖们的阅读
个人能力有限
如有不对,欢迎指正🌟 如有帮助,建议小心心大拇指三连🌟
原文链接:https://juejin.cn/post/7352018939906424884 作者:前端手术刀