背景
关于 无限流 的题目 我们在之前的文章中 曾经实现过一次 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中函数是可运行的对象 。
而对象和原始类型运算时 会进行隐式转换。
具体规则如图。
先通过
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世界中的两种重要的编程思想。