功能:构建一个滴答作响的闹钟。时钟需要显示小时,分钟,秒以及当地时间的日期
要求:
1、每个字段必须是双位数字,不足补上0
2、时钟必须显示每秒的变化
3、十二小时制,用 AM PM 表示上午下午
(主要体现函数式编程)
将业务逻辑分解成更小的部分或者函数。
// 可以将许多函数,合成一个更大的函数,还可以方便地修改被合称函数的顺序(解析详细在下方)
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)
// 用于转换数据的函数: 将日期对象转换成方便时钟程序调用的对象(在不改变原有数据的情况下转换数据)
// 接受date对象,为时钟构造一个包含时分秒的对象
const serialzeClockTime = date => ({
hours: date.getHours(),
minutes: date.getMinutes(),
seconds: date.getSeconds()
})
// 接受一个时钟对象,返回一个小时被转换成本地时间的对象,比如:将1300 => 时钟上的1点
const civilianHours = clockTime => ({
...clockTime,
hours: (clockTime.hours > 12) ?
clockTime.hours - 12 :
clockTime.hours
})
// 接受时钟对象,在该对象中追加日期,AM上午,PM下午
const appendAMPM = clockTime => ({
...clockTime,
ampm: (clockTime.hours > 12) ? "PM" : "AM"
})
// 使用高阶函数
// 获取目标函数,返回的函数将把时间发送给目标(这里的目标是console.log)
const display = target => time => target(time)
// 获取模版字符串,对时钟时间进行响应的格式化,此处使用模版hh:mm:ss
const formarClock = format =>
time =>
format.replace('hh', time.hours)
.replace('mm', time.minutes)
.replace('ss', time.seconds)
.replace('tt', time.ampm)
// 获取某个对象的属性键作为参数,并预先将0赋给对象的属性值,其中的数字小于10,会在该数字前面用0不足双数。
const prependZero = key => clockTime => ({
...clockTime,
[key]: (clockTime[key] < 10) ?
"0" + clockTime[key] :
clockTime[key]
})
// 一个独立函数,会将获取时钟时间作为参数,并通过本地时间规范将时钟时间转换成本地时间
const converToCivilianTime = clockTime =>
compose(
appendAMPM,
civilianHours
)(clockTime)
// 获取本地时间,并确保时分秒是以双位格式现实的,必要情况下数字前补0
const doubleDigits = civilianTime =>
compose(
prependZero('hours'),
prependZero('minutes'),
prependZero('seconds')
)(civilianTime)
// 设置时间间隔启动时钟程序,将会每隔一秒执行一次回调程序,回调函数是由上面所有子程序合成的,
// 每隔一秒,控制台都会被晴空,获取当前时间,转换格式、本地化、格式化、然后显示
const startTicking = () => {
setInterval(
compose(
clear,
getCurrentTime,
serialzeClockTime,
converToCivilianTime,
doubleDigits,
formarClock('hh:mm:ss tt'),
display(log),
),
oneSecond()
)
}
startTicking()
注意注意:大括号小括号!!
所有函数易于测试和复用。他们没有副作用,函数外部不存在全局变量。诊断问题只需按功能步骤诊断。
compose函数 - 高阶函数
const compose = (...fns) =>
(arg) =>
fns.reduce(
(composed, f) => f(composed),
arg
)
接收函数作为参数,并且会返回一个独立的函数,扩展运算符主要用于将这些函数参数添加到fns数组中。随后将会返回可以接受一个参数的函数,即arg。当该函数被调用时,数组fns会将作为在函数之间传递数据的起始管道。每个函数被调用后的结果累积就是上一个函数的输出结果。最后一个函数会被调用并返回了最终结果。看不懂?没关系,下面时钟例子详细解析来啦;
时钟:
1、compose 传的函数全都存在fns中,compose返回的是个函数
2、1s 后执行,此时arg 为空,reduce开始,f是clear, => clear(), 清空了控制台, composed 还是空
3、1s 后再反应,执行的是getCurrentTime, 以此类推,到serialzeClockTime, 返回的是个时钟对象
4、此时 由于reduce,结果存在了composed中,并以函数参数的形式传给了下一个函数。。。
总结:
注意点:写函数的时候注意打括号啊!跟能不能返回数据,继续执行下面的函数有关,
reduce,reduceRight 灰常好用