Time: 20190716
函数式编程的概念在JS中随处可见。
函数可以作为函数的参数进行传递。
函数就是变量。
高阶函数:一个以上的箭头表示的是高阶函数。
示例:
const createScream = logger => message => logger(message.toUpperCase() + "!!!")
函数是第一类成员,函数就是数据,可以像变量一样被保存、检索或者在程序中传递。
声明式编程风格
特点:对执行结果的描述远胜于执行过程。
命令式编程更关注的是达成目标的具体过程,这种编程风格往往需要辅以大量的注释来解释代码的用途。
声明式编程风格的代码本身就描述了要做的事情。
具体到React这里,可以通过对比传统方式和React方式创建DOM的过程来理解:
// 命令式创建
var target = document.getElementById("target")
var wrapper = document.createElement("div")
var headline = document.createElement("h1")
wrapper.id = "welcome"
headline.innerText = "Hello World"
wrapper.appendChild(headline)
target.appendChild(wrapper)
// ReactJS方式
const { render } = ReactDOM // 解构式
const Welcome = () => {
<div id="welcome">
<h1> Hello World~ </h1>
</div>
}
render(
<Welcome />, document.getElementById("target")
)
React采用的是声明式编程风格。
阅读代码可以看到,React的方式更加易读。
什么是函数式编程?
核心概念
- 不可变性
- 纯函数
- 数据转换
- 高阶函数
- 递归
不可变性
表示,数据永远是不可变的,永远无法修改。
但是数据经过交互之后的变化是必然的,也意味着我们需要在数据的拷贝上进行编辑,用于取代原生数据。
let color_lawn = {
title: "lawn",
color: "#00F00",
rating: 4
}
建立函数用于修改颜色对象的评分:
function rateColor(color, rating) {
color.rating = rating
return color
console.log(rateColor(color_lawn, 5).rating) // 5
console.log(color_lawn.rating) // 5
}
会发现原来的数据也被修改了。
因为在JS中,函数参数会指向实际的数据。
比较好的方式是通过使用Object.assign()
来实现。
var rateColor = function(color, rating) {
return Object.assign({}, color, {rating: rating})
}
console.log(rateColor(color_lawn, 5).rating) // 5
console.log(color_lawn.rating) // 4
Object.assign
提供的是一种拷贝机制,提供空白对象,然后将颜色对象拷贝到这个对象上,再重写颜色的评分。
实现的效果是:不改变原生对象,获得包含新的评分值的新的对象。
借用ES6规范下的箭头函数来实现的话:
const rateColor = (color, rating) =>
({
...color, // 扩展运算符
rating
})
纯函数
纯函数的含义是:结果只依赖于输入参数的函数。
纯函数不会产生副作用,不会修改全局变量,或者任何应用程序的状态。
输入的参数对纯函数而言,是不可变数据。
纯函数的三个原则
- 函数至少接收一个参数
- 函数应该返回一个值或者其他函数
- 函数不应该修改或者影响任何传递给它的参数
数据转换
利用函数式编程,数据是不可变的,为了在程序内部响应状态变化,需要将数据变成另外一种数据,即使用函数生成转换后的副本。
在JS中,两个核心函数需要用到:
- Array.map
- Array.reduce
const schools = [
"Yorktown",
"Washington",
"Wakefield"
]
console.log(schools.join(" , ")) // "Yorktown, Washington, Wakefield"
Array.join
也会从一个数据创建新的数据。
const wSchools = schools.filter(school => school[0] === 'W')
console.log(wSchools) // ["Washington", "Wakefield"]
按照指定的布尔函数过滤每个元素,将结果为true
的元素值添加到新的数组中。
另一种变换是Array.map
,将每个元素映射到新的元素上。
const highSchools = schools.map(school => `${school} High School`)
在每个元素后面添加High School
。
新的元素不一定非得是相同类型,可以是任何的对象,数值,数组,函数等。
const highSchools = schools.map(school => ({name: school}))
使用map函数修改对象数组中的某个对象
let schools = [
{name: "Yorktown"},
{name: "Stratford"},
{name: "Washington"},
{name: "Wakefield"}
]
const editName = (oldName, name, arr) => arr.map(item => {
if (item.name === oldName) {
return {
...item,
name
}
} else { return item }
})
let updateSchools = editName("Stratford", "HB Woodlawn", schools)
高阶函数
高阶函数是可以操作其他函数的函数。
一种是将函数当作参数传递,另一种是返回一个函数,或者二者兼而有之。
如Array.map, Array.filter, Array.reduce
都是将函数当作参数传递,这些都是高阶函数。
合成
函数式编程会将业务逻辑拆分为小型的纯函数,最终需要将这些小型函数整合到一起。
或者串联,或者并联调用,合成为更大的函数。
JS中最常用的是链式调用。
const template = "hh:mm:ss tt"
const clockTime = template.replace("hh", "03")
.replace("mm", "33")
.replace("ss": "33")
.replace("tt": "PM")
但链式调用只是一种合成方式,还要其他的方式可供选择。
合成的目的是明确的:整合若干简单函数构造更高阶的函数。
END.
网友评论