• 如何实现 add[1][2][3] + 4 === 6?


    如何实现 add[1][2][3] + 4 === 6?

    近期参加的笔试过程中碰到了一道很有意思的题目:

    实现一个 add 对象,通过链式传入属性求和返回结果,例如以下示例:

    const result1 = add[1][2] + 3; // 6
    const result2 = add[1][2][3] + 4; // 10
    const result3 = add[1][2][3][4] + 10; // 20
    
    • 1
    • 2
    • 3

    相信你看到这个题目会联想到函数柯里化

    func(1, 2); // 3
    func(1, 2, 3); // 6
    func(1, 2)(3); // 6
    func(1, 2)(3)(4); // 10
    
    • 1
    • 2
    • 3
    • 4

    然而,函数柯里化的实现是基于函数的,调用的时候使用(),而这里我们使用的是 [] 的方式。

    那么我们该如何去实现这个 add 呢?

    我们需要访问一个属性,去触发特定的方法,相信熟悉 ES6 的读者可能会想到一个特性:Proxy。没错,就是它!!!

    Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。

    使用方法很简单:

    const p = new Proxy(target, handler);
    
    • 1

    其中 target 是我们要代理的对象,handler 则是以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为。详细介绍请看 MDN 文档

    在我们这个 add 对象中,我们是访问任意属性的时候触发对应的方法,即对应的是 handler.get

    让我们来看看 handler.get 的使用:

    const p = new Proxy(target, {
      get: function(target, property, receiver) {
        
      }
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5

    其中 target 是目标对象,property 是被获取的属性名,receiver 是 Proxy 或者继承 Proxy 的对象。

    以下代码演示如何拦截属性值的读取操作。

    const p = new Proxy({}, {
      get: function(target, prop, receiver) {
        console.log("called: " + prop);
        return 10;
      }
    });
    
    console.log(p.a); // "called: a"
                      // 10
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    这里对一个空对象 {} 进行代理,在 get 方法里输出要访问的属性,然后返回 10。所以说,当我们当问代理对象的属性时,即使该属性不存在,我们依然能返回值。否则,正常情况下我们访问一个不存在的属性会返回 undefined

    那么接下来我们来看看 add 对象的实现:

    const source = { sum: 0 }; 
    const add = new Proxy(source, {
      get: function(target, property, receiver) {
        // 遇到 + 的操作,会触发隐式类型转换(Symbol.toPrimitive)
        if (property === Symbol.toPrimitive) {
          // 将之前计算过的所有和进行返回,用于后续运算
          const tmp = target.sum;
          // 清空之前的值,不影响后续代理器的访问
          target.sum = 0;
          // Symbol.toPrimitive 方法是内部属性,所以需要返回一个函数
          return () => tmp;
        } else {
          // 简单的访问属性,直接累加
          target.sum += Number(property);
          return receiver;
        }
      }
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    1. 首先我们定义了一个 source 对象来作为被代理的对象,同时 sum 属性存储累加的结果。
    2. 我们代理了 source 对象并返回给 add
    3. 对于 get 方法,我们需要判断两种情况:一个是正常的属性访问,另一个是碰到加减法操作时,会进行隐式类型转换成原始数据类型。
    4. 对于正常的属性访问,我们直接将属性累加到 sum,然后继续返回代理对象。
    5. 对于减法操作,我们需要取出计算结果,重置 sum,最后返回一个 函数(Symbol.toPrimitive 方法是内部属性,所以需要返回一个函数)

    如此我们便实现了我们的最终目的。

  • 相关阅读:
    logback-spring.xml配置文件标签(超详解)
    【C++笔试强训】第八天
    谷粒商城笔记+踩坑(25)——整合Sentinel实现流控和熔断降级
    Feign服务调用
    利用BACnet分布式IO控制器优化Niagara楼宇自动化系统
    Java技术分享系列:Dubbo 与 Spring Cloud 完美结合
    k8s数据持久化,pv和pvc
    激活函数与loss的梯度
    一文详解 Spring AOP
    常见服务器运维管理面板整理汇总
  • 原文地址:https://blog.csdn.net/p1967914901/article/details/127621032