Skip to content

add

题目描述

实现一个特殊的 add 对象,要求支持属性访问链式调用和数值运算:

  • add[100] + 1 输出 101
  • value[200][300] + 100 输出 701 (累加 100+200+300+100)
  • add[100][200][300] - 300 输出 300 (累加 100+200+300-300)

本题考查 Proxy 代理对象Symbol.toPrimitive 的深度应用。

核心知识点

1. Proxy 代理对象

  • 基本概念: Proxy 可以拦截并自定义操作(如属性查找、赋值、枚举、函数调用等)
  • get 捕获器: 拦截对象的读取属性操作
  • 应用场景: 动态属性生成、数据验证、操作记录等

2. Symbol.toPrimitive

  • 作用: 对象转换为原始值时调用的方法
  • 优先级: 最高,优先于 valueOf() 和 toString()
  • 参数: hint 可以是 'number'、'string' 或 'default'

3. 链式调用设计模式

  • 核心思想: 每次操作返回新的相同类型对象
  • 累积状态: 在对象内部维护累积值

代码实现

javascript
function createProxy(value = 0) {
  return new Proxy(
    {},
    {
      get(target, property) {
        // 拦截 Symbol.toPrimitive,用于数值转换
        if (property === Symbol.toPrimitive) {
          return () => value
        }
        // 累加属性值,返回新的代理对象
        return createProxy(value + Number(property))
      },
    },
  )
}

const add = createProxy()
export default add

关键技术点

1. Proxy get 捕获器的妙用

javascript
// 捕获器参数详解
get(target, property, receiver) {
  // target: 被代理的原始对象
  // property: 被访问的属性名(可以是字符串或 Symbol)
  // receiver: 代理对象本身
}

2. 数值转换的关键点

javascript
// Symbol.toPrimitive 的实现
if (property === Symbol.toPrimitive) {
  return () => value; // 返回函数,而不是直接返回值
}

3. 类型转换注意事项

javascript
Number('100') // 100 - 正常转换
Number('abc') // NaN - 非数字字符串
Number('') // 0   - 空字符串
Number(null) // 0   - null转换

扩展思考

1. 性能优化思路

javascript
// 缓存优化版本
const proxyCache = new Map()
function createProxy(value = 0) {
  if (proxyCache.has(value)) {
    return proxyCache.get(value)
  }
  const proxy = new Proxy(
    {},
    {
      get(t, p) {
        if (p === Symbol.toPrimitive) {
          return () => value
        }
        return createProxy(value + Number(p))
      },
    },
  )
  proxyCache.set(value, proxy)
  return proxy
}

2. 支持更多操作符

javascript
// 扩展版本支持减法
function createProxy(value = 0) {
  return new Proxy(
    {},
    {
      get(t, p) {
        if (p === Symbol.toPrimitive) {
          return () => value
        }
        // 支持负数语法 add.sub[100]
        if (typeof p === 'string' && p.startsWith('sub')) {
          return createProxy(value - Number(p.slice(3)))
        }
        return createProxy(value + Number(p))
      },
    },
  )
}

3. 类型安全版本

javascript
function createProxy(value = 0) {
  return new Proxy(
    {},
    {
      get(t, p) {
        if (p === Symbol.toPrimitive) {
          return () => value
        }
        const numValue = Number(p)
        if (isNaN(numValue)) {
          throw new TypeError(`Invalid property: ${String(p)}`)
        }
        return createProxy(value + numValue)
      },
    },
  )
}

内容基于 MIT 许可 | 保持节奏 · 持续积累