js无限流: 实现add(2)(10)(19) + 1

654 阅读3分钟

背景

关于 无限流 的题目 我们在之前的文章中 曾经实现过一次 add[1][2][3] + 4

之前我们是通过proxy的方式 实现的一种无限流的累加功能
具体可以参考这篇文章:js元编程:妙用proxy实现add[1][2][3] + 4

完整代码如下:

const add = new Proxy(
  {
    value: 0,
  },
  {
    get(target, property) {
      if (property === Symbol.toPrimitive) {
        return () => {
          return target.value;
        };
      }
      if (Number.isNaN(Number(property))) {
        throw new Error("key应该是一个数字");
      }
      const newValue = target.value + Number(property);
      return new Proxy({ value: newValue }, this);
    },
  }
);
console.log("test add", add[1][2]["3"][4] + 4); // test add 14

今天同样是无限流 实现方式略有不同 具体如下

题目说明

这次我们要实现的如下功能:
add(1,5,10)() 输出结果 16
add(2)(10)(19) + 1 输出结果 32
add(1)(2)(3,4) + '1' 输出结果 '101'

分析

无限流调用如何实现

js元编程:妙用proxy实现add[1][2][3] + 4 之前的这道题目中的无限流是通过proxy动态代理拦截实现的。

在这道题目中 通过分析 我们可以发现 每次执行返回的都是一个函数 然后再传参调用,不断重复 达到无限流的功能

一个函数执行返回一个函数 是不是很熟悉 没错这不就是闭包的形式嘛。 关于闭包的详情可以参考这篇文章 闭包是函数的一种表现形式

所以我们基于此可以实现如下代码:

function add(...args) {
    function innerAdd(...innerArgs) {
        return innerAdd;
    }
    return innerAdd;
}

函数如何与数字求和

既然返回的是一个函数 在遇到 add(2)(10)(19) + 1 这种情况时 函数能和数字类型求和相加嘛?
当然是可以的 别忘了 js中函数是可运行的对象

而对象和原始类型运算时 会进行隐式转换。
具体规则如图。
截屏2024-10-12 17.15.17.png 先通过 Symbol.toPrimitive 方法做类型判断 最终会调用valueOf或者toString方法.

详情参考 js基石之数据类型三:类型转换

函数如何与字符串相加

原理同上。

所以我们可以在原有基础上 实现如下代码

function add(...args) {
    let sum;
    function innerAdd(...innerArgs) {
        return innerAdd;
    }
    innerAdd.valueOf = function() {
        return sum;
    };

    innerAdd.toString = function(){
        return '' + sum;
    }
    return innerAdd;
}

累加和如何获取到

要想得到最终的结果 我们需要获取到输入的参数并存储结果 直到调用结束 获取到最终值。
我们可以利用闭包实现这一点。
缓存累加结果sum。

最终代码如下

function add(...args) {
    // 初始化一个总和,将所有初始传入的参数累加起来
    let sum = args.reduce((acc, curr) => acc + curr, 0);

    // 内部嵌套函数,用于继续接收参数并累加
    function innerAdd(...innerArgs) {
        // 不输入任何参数 直接返回结果
        if(!innerArgs.length){ 
            return sum;
        }
        sum += innerArgs.reduce((acc, curr) => acc + curr, 0); // 累加传入的参数到 sum 上
        return innerAdd;
    }

    // 覆盖 valueOf 方法,使得在隐式类型转换或显式调用 valueOf 时返回 sum
    innerAdd.valueOf = function() {
        return sum;
    };

   // 覆盖 toString 方法,使得在隐式类型转换为字符串时返回 sum
    innerAdd.toString = function(){
        return '' + sum;
    }
    // 返回 innerAdd 函数以允许链式调用
    return innerAdd;
}

add(1,2).valueOf()    // 3
add(1,5,10)()         // 16
add(2)(10)(19) + 1    // 32
add(1)(2)(3,4) + '1'  // '101'

总结

目前关于无限流的题目我们已经掌握了两种方式

一种是元编程的思路 通过proxy代理的动态属性拦截的方式
一种是函数式编程的思路 通过闭包+隐式类型转换。
这两种解题思路分别对应着js世界中的两种重要的编程思想。

系列文章