函数式编程基本概念
-
写在之前,这些内容参考自O`REILLY系列图书《React学习手册》
-
在React中,UI是用纯函数表示的,并且在构造DOM时,是以声明式(与此相对的,是命令式)的方式。而声明式的编程是函数式编程更广义的一部分。所以,先熟悉函数式的编程,很有必要。
结合了如下所述,我发现了es6中数组的新语法, 像
map
,filter
,reduce
这些,都满足了不可变性,数据转换,纯函数,高阶函数等要点,很精髓。
1,不可变性
- 数据在应用程序中,在不改变原生数据结构的前提下,在此基础上拷贝后进行编辑。
- 在此基础上,就会有浅拷贝和深拷贝的选择。
比如,在原有对象上添加属性,而不影响原对象
let person = {
name: 'Beckham',
age: 33
}
let player = (person, sex) => ({
...person,
sex
})
console.log(person)
console.log(player(person, 'male'))
2,纯函数
结果只依赖输入参数
- 函数至少接收一个参数
- 返回一个值或其他函数
- 不应该修改或影响传递给它的参数
- 不会产生副作用,比如修改了全局变量,影响了程序的状态
3,数据转换
3.1,定义
- 从其他数据源创建一个新的数据集。
3.2,作用
- 为了使用转换后的副本
所以,
map
,filter
,reduce
这些函数都是必不可少的。
- 基本用法
对象转为数组
let country = {
"beijing": 10,
"shanghai": 6,
"shenzhen": 9
}
let obj = Object.keys(country).map(key =>
({
name: key,
value: country[key]
})
)
console.log(obj)
数组中,寻找最大值
let ages = [11,66,33,22,11,55]
let maxAge = ages.reduce((max, age) => {
if (max > age) {
return max
} else {
return age
}
}, 0)
console.log(maxAge)
数组去重,思路:有的话不变,没有的话添加。
let colors = ['red','green','blue','red','green','blue']
const distinctColor = colors.reduce((distinct, color) => (
(distinct.indexOf(color) !== -1) ? distinct : [...distinct, color]
),
[]
)
console.log(distinctColor)
4,高阶函数
- 定义:将函数作为参数传递,或返回一个函数
所以,数组的
map
,filter
,reduce
都是高阶函数
5,递归
- 目的:在涉及到循环时,递归可提供一种替代性的方案
- 在浏览器的调用堆栈中,会依次放入当前执行的函数,在出栈时,后进先出。
打印输出10~0
let countDown = (count, fn) => {
fn(count)
return (count > 0) ? countDown(count-1, fn) : count
}
countDown(10, count => console.log(count))
倒计时输出10~0
let countDown = (count, fn, delay = 1000) => {
fn(count)
return (count > 0) ? setTimeout(() => countDown(count-1, fn), delay) : count
}
countDown(10, count => console.log(count))
6,合成
6.1 定义
- 将具体的业务逻辑拆解为小型纯函数,用户会在特定条件下合成它们,以串联或并联的方式进行调用。
6.2 目标
- 通过整合若干简单函数,构造一个更高阶的函数
链式调用,就是合成技术之一
let template = 'hh:mm:ss tt'
let clockTime = template.replace('hh','09')
.replace('mm','06')
.replace('ss', '52')
.replace('tt', 'PM')
console.log(clockTime) // 09:06:52 PM
7,综上应用
- 获取当前时间,实现实时时钟。
git图的原因,时间看起来加速了。实际运行代码显示是正确的。
![]()
// 计时器时间
const oneSecond = () => 1000
// 获取当前时间
const getCurrentTime = () => new Date()
// 清除控制台输出
const clear = () => console.clear()
// 控制台打印内容
const log = message => console.log(message)
// 构造一个时钟对象,包含时分秒
const abstractClockTime = date =>
({
hours: date.getHours(),
minutes: date.getMinutes(),
seconds: date.getSeconds()
})
// 接收一个时钟对象,
// 该方法用于指定12小时制
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"
})
/*
* @target 目标函数(本例中就是log函数)
* @time 时钟对象
* 返回的函数,会将时间发送给目标函数
* */
const display = target => time => target(time)
/*
* @format 模板字符串 "hh:mm:ss:tt"
* @time 时钟对象
* 返回的函数,将时间进行格式化
* */
const formatClock = format =>
time =>
format.replace("hh", time.hours)
.replace("mm", time.minutes)
.replace("ss", time.seconds)
.replace("tt", time.ampm)
/*
* @key 时钟对象的属性
* @clockTime 时钟对象
* 返回的函数,将时钟对象的属性,包括时分秒,当<10时,加0
* */
const prependZero = key => clockTime =>
({
...clockTime,
[key]: (clockTime[key] < 10) ? "0" + clockTime[key] : clockTime[key]
})
/*
* 接收参数为多个函数,返回一个独立的函数
* compose函数被调用,返回的独立函数进行调用时,如果不传参,
* 就以arg作为reduce的起始值,arg在使用时是undefined,
* 而一个函数在定义时,如果没有设置形参,该函数在调用时,传递的参数无效。
* 在这个栗子中,...fns为多个函数组成的数组。
*
* 也就是说,arg作为第一个函数的参数,如果该函数定义时没有指定形参,arg将被忽略,
* 第一个函数执行的结果,作为第二个函数执行的参数,依次类推。
* */
const compose = (...fns) =>
(arg) =>
fns.reduce(
(composed, f) => f(composed),
arg
)
const convertToCivilianTime = clockTime =>
compose(
appendAMPM,
civilianHours
)(clockTime)
const doubleDigits = civilianTime =>
compose(
prependZero("hours"),
prependZero("minutes"),
prependZero("seconds")
)(civilianTime)
/*
* compose已经被调用了,之后每隔1s,调用一次compose的执行结果
* 注意参与合成的函数的顺序。
*
* 清除打印台,获取时间,构造时钟对象,添加am或pm,12小时制,加0,格式化时分秒,发送给打印函数
* */
const startTicking = () =>
setInterval(
compose(
clear,
getCurrentTime,
abstractClockTime,
convertToCivilianTime,
doubleDigits,
formatClock("hh:mm:ss tt"),
display(log)
),
oneSecond()
)
startTicking()
原文是时间为:2018年06月25日
原文作者:非梧不栖
本文来源: 掘金 如需转载请联系原作者