由一道面试题引出的思考:
实现一个add函数,支持对多个参数求和以及多次调用求各,如下效果:
add(1) // 1
add(1)(2) // 3
add(1,2) //3
add(1,2,3)(4)(6) // 13
- 对于不定参的实现,很容易,使用arguments获取所有参数,然后使用reduce来实现累加
function add() {
console.log(arguments); // [Arguments] { '0': 1, '1': 2, '2': 3 }
return [...arguments].reduce((total, num) => {
return total + num;
})
}
console.log(add(1,2,3));
- 因为要求是链式的,所以我们必须返回的是一个函数
这里,我们可以利用柯里化的思想,将多个参数的函数转化为一次只处理一部分参数的函数
什么是柯里化
柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。
这句话听起来不太好理解,可以用代码来理解:
function add(x, y, z) {
return x + y;
}
console.log(add.length) // 2, 返回的是这个函数对应的参数个数
// 使用柯里化后,函数变为:
function _add(x) {
return function(y) {
return function(z) {
return x + y + z
}
}
}
// 调用形式可以变成 _add(x)(y)(z)
柯里化的实现
// 原函数
function add(x, y, z) {
return x + y;
}
// 柯里化的实现
function curry(fn, args = []) {
// 获取函数的参数个数
let len = fn.length;
// 返回一个函数,用于链式调用
return function(...newArgs) {
// 将内层参数和外层参数合并
args = [...args, ...newArgs]
// 当合并的参数还小于fn所需的参数个数时,继续递归收集参数,直至参数够了为止
if(args.length < len) {
return curry.call(this, fn, args)
}
// 收集够参数后,调用fn函数,相当于调用:fn(...args)
return fn.apply(this, args)
}
}
// add(x, y)
const _add = curry(add)
// 柯里化后,参数传参变成任意组合形式
// _add(x)(y)(z)
// _add(x, y)(z)
// _add(x)(y, z)
const res = _add(2,3)(8)
console.log(res); // 13
// 注意,不能同时调用,因为_add在res时已经执行过函数了,即停止返回函数了,所以这几个调用是不能同时出现的
// 这里只是为了演示结果,才放多一个出来看
const res2 = _add(2)(3)(8)
console.log(res2); // 13
但是这个题目并不是我们设定的固定已知参数长度,而是任意长度,那么判断在什么时候结束递归,执行函数就是我们接下来要解决的事情.
可以按照柯里化的思想,改进上面的add函数,可以实现链式调用
利用打印函数会自动调用toString()方法,和使用函数进行运算时,会默认调用valueOf的方法,我以此设为停止条件
但在nodejs里是要手动调用toString()或valueOf()调用时才停止,怎么才能自动检测到最后一个链式调用呢?
在chrome浏览器里倒是能得出结果,但会有个f字符,感觉都不是最终答案
// 使用柯里化思想改进上面那个加法
function add(...args) {
let arr = args
// 返回一个函数,用于获取链式调用的参数
function fn(...newArgs) {
// 将内层参数和外层参数合并
arr = [...arr, ...newArgs]
// 返回函数本身
return fn
}
// 在调用toString()或valueOf()方法时计算所有参数之和
fn.toString = fn.valueOf = function() {
return arr.reduce((total, num) => total + num)
}
return fn
}
// 在nodejs环境下,要手动调用才行,怎么才能自动检测到最后一个链式调用呢?
console.log(add(1)(2)(3).valueOf()) // 6
console.log(add(1)(2)(3).toString()) // 6
// chrome浏览器环境下,返回 ƒ 6
const res22 = add3(1)(2)(3)
console.log(res22) // ƒ 6
暂时没想到好的解决方案,有大佬会嘛,求请教~~~
网友评论