Javascript对函数数组执行`reduce`如何实现函数组合?

How Javascript `reduce` performed on function array achieves function composition?

我在 redux compose 函数中遇到了这种模式。我仍然不明白在下面的示例中如何从最后一个而不是第一个开始评估函数:

function f2(a) {
  return a + a;
}
function f3(a) {
  return a + a + a;
}
function f4(a) {
  return a + a + a + a;
}
function f5(a) {
  return a + a + a + a + a;
}

function compose(...funcs) {
  return funcs.reduce(function x(a, b) {
    return function y(...args) {
      const temp = a(b(...args));
      return temp;
    };
  });
}

const composedFunction = compose(f2, f3, f4, f5);
const result = composedFunction(2);

在第一个 reduce 迭代中,累加器是 f2,因此我们将得到 f2(f3(2))=12。在下一次迭代中,我们将调用 f4(12)=48。在最后一次迭代中,我们将调用 f5(48)=240。所以求值顺序是f5(f4(f2(f3(2))))。但是使用 console.log 我看到评估顺序是 f2(f3(f4(f5(2)))) 巧合也是 240.

据我了解,函数 y 被所有数组元素调用,那么为什么只有最后一个函数获得 2 作为参数?

reduce 没有调用函数 f2、f3、f3、f5,而是从这些函数创建一个函数。这是每次迭代中累加器的值。请注意,该值是 function 而不是函数执行的结果。

1:a=f2;b=f3;return 值(不是临时值,而是函数 y)=f2(f3(...args))

2:a(prev return 值)=f2(f3(...args));b=f4;return 值=f2(f3(f4(...args )))

等等....

让我们通过一个非常简单的示例来逐步执行代码:

 compose(f2, f3, f4)

由于没有向 reduce 传递初始值,它将从数组的第一个 (f2) 和第二个 (f3) 值开始并以此调用回调,x 被调用 af2 并且 bf3。现在 x 什么都不做,它只是 returns 函数 y 可以通过闭包访问 a 和 b。

Reduce 现在将继续到第三个元素,第一个参数是前一个回调的结果(闭包 y),第二个参数是 f4。现在 x 再次被调用,并且在 y 上创建了另一个闭包,y 最终从整个函数返回。

如果我们尝试可视化这样的闭包函数,它将是:

 y { // closure of y
  a -> y { // a references another closure of y
    a -> f3,
    b -> f2
  },
  b -> f4
}

现在你调用闭包 y 并将 2 传递给它,这将调用 b (f4) 并将结果传递给 a (关闭 y).

 a         (  b(...args))   
 y { ... } (  f4(2) )

现在关闭的 y 将执行相同的操作:

 a (  b ( ...args))
 f2( f3( f4( 2 ) ) )

提示: 有时很难跟踪闭包值,因此控制台为您提供了很好的实用工具来跟踪它们:在控制台中打开您的代码 "debugger"选项卡,点击函数调用所在的行号附加断点,然后再次运行代码,每当到达断点时执行都会退出,你可以看到所有变量的值(包括封闭的)。

compose 函数可以重写为:

function compose(...funcs) {
  return funcs.reduce(function (a, b) {
    return function (arg) {
      const temp = a(b(arg));
      return temp;
    };
  });
}

第一次迭代后作为下一个累加器传入的返回函数为:

function (arg) {       // R1
  return f2(f3(arg));

}

第二次迭代后作为下一个累加器传入的返回函数为:

function (arg) {       // R2
  return R1(f4(arg));

}

最后,分配给 composedFunction 的返回函数是:

function (arg) {       // composedFunction
  return R2(f5(arg));

}

所以 运行 composedFunction(2) 然后回到链上:

f5(2) returns 10

R2(10) returns R1(f4(10))
which is       R1(40)

R1(40) returns f2(f3(40))
which is   f2(120)
which is   240

希望这就足够了。

它可以写成一个单一的调用:

function composedFunction(arg) {
  return f2(f3(f4(f5(arg))));
}