我曾试图重写compose函数,以使用不同的参数打印到控制台,但我仍然无法解决。compose函数第一次调用arg
是未定义的。因此,reduce函数将使用数组的第一个元素(console.clear(((作为初始值,并将其用作下一个函数getCurrentTime的参数。
当使用convertToCivilianTime调用compose时,它使用serializeClockTime的返回值作为compose中第一个返回函数的参数。
那么,如何在第一次使用未定义的arg
参数调用compose而不引起错误呢?
const compose = (...fns) =>
(arg) =>
fns.reduce( (composed, f) => f(composed), arg)
const oneSecond = () => 1000
const getCurrentTime = () => new Date()
const clear = () => console.clear()
const log = message => console.log(message)
const serializeClockTime = date => ({
hours: date.getHours(),
minutes: date.getMinutes(),
seconds: date.getSeconds()
})
const civilianHours = clockTime => ({
...clockTime,
hours: ( clockTime.hours > 12 ) ? clockTime.hours - 12 : clockTime.hours
})
const appendAMPM = clockTime => ({
...clockTime,
ampm: ( clockTime.hours >= 12 ) ? "PM" : "AM"
})
const display = target => time => target(time)
const formatClock = format => time => format.replace('hh', time.hours).replace('mm', time.minutes).replace('ss', time.seconds).replace('tt', time.ampm)
const prependZero = key => clockTime => ({
...clockTime,
[key] : ( clockTime[key] < 10 ) ? "0" + clockTime[key] : clockTime[key]
})
const convertToCivilianTime = clockTime => compose(appendAMPM, civilianHours)(clockTime)
const doubleDigits = civilianTime => compose(
prependZero('hours'),
prependZero('minutes'),
prependZero('seconds')
)(civilianTime)
const startTicking = () => setInterval(compose(
clear,
getCurrentTime,
serializeClockTime,
convertToCivilianTime,
doubleDigits,
formatClock('hh:mm:ss tt'),
display(log)
),
oneSecond()
)
startTicking()
将undefined
作为初始参数传递给reduce
并不意味着"使用默认初始参数"(数组的第一个元素(,而只是"使用undefined
作为初始参数":
const arr = [1, 2, 3];
const traceSum = (a, b) => {
console.log(a, b)
return a + b
};
console.log("reduce without initial argument:", arr.reduce(traceSum));
console.log("reduce with initial argument:", arr.reduce(traceSum, 0));
console.log("reduce with initial argument undefined:", arr.reduce(traceSum, undefined));
因此,调用由compose
创建的函数是安全的,即使您没有提供参数,因为arg
将只是一个显式undefined
,因此每个函数都会被执行,但经过它们的初始值是undefined
:
const compose = (...fns) =>
(arg) =>
fns.reduce( (composed, f) => f(composed), arg)
const trace = message => value => {
console.log(message, value);
return value;
}
const test = compose(trace("one"), trace("two"));
console.log(test("hello"));
console.log(test());
你只需要确保你的组合函数可以在没有初始值的情况下工作:
const compose = (...fns) =>
(arg) =>
fns.reduce( (composed, f) => f(composed), arg)
const excited = str => str + "!";
const loud = str => str.toUpperCase();
const greet = () => "hey";
const shout = compose(loud, excited);
const shoutGreeting = compose(greet, shout);
console.log(shout("stop")); //works with initial value
console.log(shoutGreeting()); //works without initial value
console.log(shoutGreeting("hammer time")); //ignores initial value
console.log(shout()); //error - initial value expected
当没有参数作为reduce函数的initialValue传递时,它将第一次用数组的前两个元素调用回调。
例如:
[1, 2, 3, 4, 5].reduce((a, b) => {
console.log(a, ',', b, ',', a + b)
return a + b;
}
它将导致以下日志:
1 , 2 , 3
3 , 3 , 6
6 , 4 , 10
10 , 5 , 15