我在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
作为参数?
让我们通过一个非常简单的示例逐步完成代码:
compose(f2, f3, f4)
由于没有初始值传递给reduce,它将从数组的第一个(f2)和第二个(f3)值开始并用它调用回调,x
被调用a
为f2
而b
为f3
。现在x
什么都不做,它只返回可以通过闭包访问a和b的函数y
。
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 ) ) )
提示:有时很难跟踪关闭的值,因此控制台为您提供了很好的实用程序来跟踪它们:在控制台“调试器”选项卡中打开代码,单击函数调用的行号附加断点,然后再次运行代码,只要达到断点,执行就会产生,并且您可以看到所有变量的值(包括已关闭的变量)。
reduce不是调用函数f2,f3,f3,f5,而是从那些函数创建函数。这是每次迭代中累加器的值。请注意,该值是function
,而不是执行该函数的结果。
1:a = f2; b = f3;返回值(NOT TEMP但函数y)= f2(f3(... args))
2:a(prev返回值)= f2(f3(... args)); b = f4;返回值= f2(f3(f4(... args)))
等等....
撰写函数可以重写为:
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))));
}