在前端快速发展的今天,如果不能时刻保持学习就会很快被淘汰。分享一下最近学习的函数式编程的相关知识,希望对大家有所帮助,文章有点长。每天进步一点点。
what【什么是函数式编程】
函数式编程【Functional programming】缩写【FP】,是一种编程范式,也是一种编程风格。
常见编程范式:【面向过程编程、面向对象编程、函数式编程】
1、大多数初中级前端都是在面向过程编程【按照步骤来实现】
// 面向过程编程
let num1 = 2
let num2 = 3
let sum = num1 + num2
console.log(sum)
2、面向对象编程【把现实中的事物抽象成类和对象,通过封装、继承和多态来演示不同事物之间的联系】
3、函数式编程【把现实世界事物和事物之间的联系抽象到程序世界(是对运算过程进行抽象)】
// 函数式编程
function add(n1, n2) {
return n1 + n2
}
let sum = add(2, 3)
console.log(sum)
补充说明:函数式编程中的函数指的不是程序中的函数Function,而是数学中的函数即映射关系,例如:y=sin(x),是这种x和y的关系;
why【为什么学习函数式编程】
为什么要学习函数式编程,函数式编程重要吗?
1、React的高阶组件使用了高阶函数【高阶函数就是函数式编程的一个特性】
2、Vue3也开始拥抱函数式编程
3、函数式编程可以抛弃this
4、打包过程中可以更好的利用tree shaking过滤无用代码
5、方便测试、方便并行处理
而且有很多库可以帮助我们进行函数式开发:如 lodash、underscore、ramda、folktale等
how【如何学习函数式编程】
基础知识
函数是一等公民(First-class Function)
在JS中,函数就是一个普通的对象,所以我们可以把函数像对象一样存储到变量/数组中,函数还可以作为另外一个函数的参数和返回值,甚至可以在程序运行的时候通过 new Function(‘alert(1)’)来构造一个新的函数
// 把函数赋值给变量
let fn = function () {
console.log('Hello First-class Function')
}
fn()
// 一个示例
const BlogController = {
index (posts) { return Views.index(posts) },
show (post) { return Views.show(post) },
create (attrs) { return Db.create(attrs) },
update (post, attrs) { return Db.update(post, attrs) },
destroy (post) { return Db.destroy(post) }
}
// 优化
const BlogController = {
index: Views.index,
show: Views.show,
create: Db.create,
update: Db.update,
destroy: Db.destroy
}
函数是一等公民是学习高阶函数、柯里化等知识的基础
高阶函数(Higher-order function)
1、可以把函数作为参数传递给另一个函数,可以把函数作为另一个函数的返回结果
// 高阶函数 - 函数作为参数
// forEach
function forEach(array, fn) {
for (let i = 0; i < array.length; i++) {
fn(array[i])
}
}
// const arr = [1, 2, 3, 4, 5]
// forEach(arr, item => console.log(item))
// filter
function filter(array, fn) {
let result = []
for (let i = 0; i < array.length; i++) {
if(fn(array[i])) {
result.push(array[i])
}
}
return result
}
const arr = [1, 2, 3, 4, 5]
const r = filter(arr, item => item > 2)
console.log(r)
// 高阶函数 - 函数作为返回值
function makeFn() {
let msg = 'hello'
return function() {
console.log(msg)
}
}
const helloFun = makeFn()
helloFun()
// once
function once () {
let done = false
return function (money) {
if (!done) {
done = true
console.log(`支付 ${money} 美元`)
}
}
}
let pay = once()
pay(5)
pay(5)
2、使用高阶函数的意义
对运算过程进行抽象可以帮我们屏蔽细节,只需要关注我们的目标
高阶函数是用来抽象通用的问题
// 面向过程的方式
let array = [1, 2, 3, 4]
for (let i = 0; i < array.length; i++) {
console.log(array[i])
}
// 高阶函数
let array = [1, 2, 3, 4]
forEach(array, item => {
console.log(item)
})
3、常用高阶函数
forEach、map、filter、every、some、find/findIndex、reduce、sort…
// map
function map(array, fn) {
for (let value of array) {
fn(value)
}
}
let arr = [1, 2, 3, 4, 5]
map(arr, item => console.log(item))
// every
function every(array, fn) {
let result = true
for (let value of array) {
result = fn(value)
if (!result) {
break
}
}
console.log(result)
}
every(arr, item => item > 1)
// some
function some (array, fn) {
let result = false
for (let value of array) {
result = fn(value)
if (result) {
break
}
}
console.log(result)
}
some(arr, item => item > 4)
闭包(Closure)
1、闭包的概念:函数和其周围的状态(词法环境)的引用捆绑在一起形成闭包【可以在另一个作用域中调用一个函数的内部函数并访问到该函数的作用域中的成员】
代码见上面 【高阶函数 - 函数作为返回值】
2、闭包的本质:函数在执行的时候会放到一个执行栈上当函数执行完毕之后会从执行栈上移除,但是 堆上的作用域成员因为被外部引用不能释放,因此内部函数依然可以访问外部函数的成员
3、闭包案例
<!DOCTYPE html>
<html>
<head>
<title>闭包案例</title>
</head>
<body>
<script type="text/javascript">
// 生成计算数字的多少次幂的函数
function makePower(power) {
return function(number) {
return Math.pow(number, power)
}
}
const power2 = makePower(2)
const power3 = makePower(3)
console.log(power2(2))
console.log(power2(3))
console.log(power3(2))
</script>
</body>
</html>
通过断点调试,可以清楚的看到闭包发生的位置
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T4lV1M5M-1616041069681)(C:\Users\71705\AppData\Roaming\Typora\typora-user-images\image-20210313124634062.png)]
纯函数
纯函数的概念
1、纯函数:相同的输入永远会得到相同的输出,而且没有任何可观察的副作用
纯函数就类似数学中的函数(用来描述输入和输出之间的关系),y = f(x)
2、lodash 是一个纯函数的功能库,提供了对数组、数字、对象、字符串、函数等操作的一些方法
数组中的的 slice是纯函数【 返回数组中的指定部分,不会改变原数组】 ,数组的 splice 是不纯的函数【对数组进行操作返回该数组,会改变原数组】
let numbers = [1, 2, 3, 4, 5]
// 纯函数
numbers.slice(0, 3)
// => [1, 2, 3]
numbers.slice(0, 3)
// => [1, 2, 3]
numbers.slice(0, 3)
// => [1, 2, 3]
// 不纯的函数
numbers.splice(0, 3)
// => [1, 2, 3]
numbers.splice(0, 3)
// => [4, 5]
numbers.splice(0, 3)
// => []
lodash常用的方法:first、last、toUpper、reverve、each、includes、find、findIndex等等
使用lodash前需要先初始化一个package.json并安装lodash【npm init -y;npm i lodash】
// lodash 常用方法
// first、last、toUpper、reverve、each、includes、find、findIndex
const _ = require('lodash')
let arr = ['jack', 'jone', 'mack', 'tom']
console.log(_.first(arr))
console.log(_.last(arr))
console.log(_.toUpper(_.first(arr)))
console.log(_.reverse(arr))
let r = _.each(arr, (item, index) => {
console.log(index, item)
})
console.log(r)
3、函数式编程不会保留计算中间的结果,所以变量是不可变的(无状态的)
4、我们可以把一个函数的执行结果交给另一个函数去处理
纯函数的好处
1、可缓存
因为纯函数对相同的输入始终有相同的结果,所以可以把纯函数的结果缓存起来
// lodash的缓存函数 memoize
const _ = require('lodash')
function getArea (r) {
console.log(r)
return Math.PI * r * r
}
// let getAreaWithMemory = _.memoize(getArea)
// console.log(getAreaWithMemory(4))
// console.log(getAreaWithMemory(4))
// console.log(getAreaWithMemory(4))
// 模拟 memoize 函数
function memorize (fn) {
let cache = {}
return function() {
const key