如何实现 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

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

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

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

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

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

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

使用方法很简单:

const p = new Proxy(target, handler);

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

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

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

const p = new Proxy(target, {
    
    
  get: function(target, property, receiver) {
    
    
    
  }
});

其中 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

这里对一个空对象 {} 进行代理,在 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. 首先我们定义了一个 source 对象来作为被代理的对象,同时 sum 属性存储累加的结果。
  2. 我们代理了 source 对象并返回给 add
  3. 对于 get 方法,我们需要判断两种情况:一个是正常的属性访问,另一个是碰到加减法操作时,会进行隐式类型转换成原始数据类型。
  4. 对于正常的属性访问,我们直接将属性累加到 sum,然后继续返回代理对象。
  5. 对于减法操作,我们需要取出计算结果,重置 sum,最后返回一个 函数(Symbol.toPrimitive 方法是内部属性,所以需要返回一个函数)

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

猜你喜欢

转载自blog.csdn.net/p1967914901/article/details/127621032