js实现链式加法的思考

2021-04-17  本文已影响0人  miao8862

由一道面试题引出的思考:
实现一个add函数,支持对多个参数求和以及多次调用求各,如下效果:

add(1) // 1
add(1)(2) // 3
add(1,2) //3
add(1,2,3)(4)(6) // 13
  1. 对于不定参的实现,很容易,使用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));
  1. 因为要求是链式的,所以我们必须返回的是一个函数
    这里,我们可以利用柯里化的思想,将多个参数的函数转化为一次只处理一部分参数的函数

什么是柯里化

柯里化(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

暂时没想到好的解决方案,有大佬会嘛,求请教~~~

上一篇 下一篇

猜你喜欢

热点阅读