一道字节面试题,不会的还有太多

前言

彦祖们,阅读本文前,希望你对 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 作者:前端手术刀

(0)
上一篇 2024年3月31日 上午10:32
下一篇 2024年3月31日 上午10:43

相关推荐

发表回复

登录后才能评论