实现 redux compose 函数但得到 RangeError

Implement redux compose function but get RangeError

我正在尝试重新实现 redux compose 函数,而不是使用 reduce 我使用 for 循环,这是我的代码:

function compose(...funcs) {
  if (funcs.length === 0) {
    return (arg) => arg;
  }

  if (funcs.length === 1) {
    return funcs[0];
  }

  let result;
  for (let i = funcs.length - 1; i > -1; i--) {
    result = result
      ? (...args) => funcs[i](result(...args))
      : (...args) => funcs[i](...args);
  }

  return result;
}

// test
function fn1(x) {
  return x + 1;
}

function fn2(x) {
  return x * 10;
}

function fn3(x) {
  return x - 1;
}

console.log(compose(fn3, fn2, fn1)(10)); // 109

由于 (10 + 1) * 10 - 1 是 109,因此预计会记录 109,但是它给了我这样的错误:

RangeError: Maximum call stack size 

看起来我在做一些递归,但我所做的只是一个for循环,不知道我的代码哪里有问题?

我认为这个问题就像下面的例子:

a = () => 2;
a = () => 3 * a();
console.log(a); 
// this prints () => 3 * a() in console
// so when you call a(), it will call 3 * a(), which will again call 3 * a() and so on
// leading to infinite recursion

我的解决方案与基于此参考 link 使用 bind 函数略有不同:。

我认为 bind 创建函数的新副本 result 并将其绑定到新对象。不使用 bind 会导致递归,因为这样代码就会像上面的例子一样,result 调用 result.

function compose(...funcs) {
  if (funcs.length === 0) {
    return (arg) => arg;
  }

  if (funcs.length === 1) {
    return funcs[0];
  }

  let result;
  for (let i = funcs.length - 1; i > -1; i--) {
    if (i == funcs.length - 1)
      result = (...args) => funcs[i](...args);
    else {
      let temp = result.bind({});
      result = (...args) => funcs[i](temp(...args));
    }
  }
  return result;
}

// test
function fn1(x) {
  console.log("fn1");
  return x + 1;
}

function fn2(x) {
  console.log("fn2");
  return x * 10;
}

function fn3(x) {
  console.log("fn3");
  return x - 1;
}

//console.log(compose(fn3, fn2, fn1));
let ret = compose(fn3, fn2, fn1);
console.log(ret(10)); // 109

与其在 compose 时尝试组合函数,不如在 调用 时组合函数似乎容易得多:

function compose(...funcs) {
  if (funcs.length === 0) {
    return (arg) => arg
  }

  return function (...args) {
    let result = funcs .at (-1) (...args)
    for (let i = funcs.length - 2; i > -1; i--) {
      result = funcs [i] (result)
    }
    return result
  }
}

// test
function fn1(x) {
  return x + 1;
}

function fn2(x) {
  return x * 10;
}

function fn3(x) {
  return x - 1;
}

console.log(compose(fn3, fn2, fn1)(10)); // 109

但是,reduce 再次使实现更加简洁:

const compose = (...fns) => (arg) => 
  fns .reduceRight ((a, fn) => fn (a), arg)

或者如果你想让最右边的函数接收多个变量,那么

const compose = (...fns) => (...args) => 
  fns .reduceRight ((a, fn) => [fn (...a)], args) [0]