函数式编程
1.为什么学习函数式编程?
- react的流行
- Vue3的拥抱
- 可以抛弃this
- 打包中更好的利用tree shaking过滤无用代码
- 方便测试、方便并行处理
- 很多库可以帮助我们进行函数式开发:lodash、underscore
2.什么是函数式编程(Functional Programming,FP)?
它是编程范式之一,其他的编程范式还有面向过程、和面向对象
它的思维的方式:把现实世界的事物和事物之间的联系抽象到程序世界中(对运算过程进行的抽象)
注意:
- 函数式编程中的函数指的不是程序中的函数,而是数学中的函数,即映射关系
- 相同的输入始终要得到相同的结果
- 函数式编程用来描述数据(函数)之间的映射
3.为什么说函数是一等公民
- 可以存储在变量中
- 可以作为参数
- 可以作为返回值
其他:函数就是普通的对象
4.什么是高阶函数
可以把函数作为参数传递给另一个函数
可以把函数作为另一个函数的返回结果
//1.函数作为参数
//1.1 forEach
function forEach(array,fn){
for(let i=0;i<array.length;i++){
fn(array[i])
}
}
//filter
function filter(array,fn){
let results=[]
for(let i=0;i<array.length;i++){
if(fn(array[i])){
results.push(array[i])
}
}
return results
}
//2.函数作为返回值
function makeFn(){
let msg='Hello function'
return function(){
console.log(msg)
}
}
const fn=makeFn()
//once
function once(fn){
let done=false
return function(){
if(!done){
done=true
return fn.apply(this,arguments)
}
}
}
let pay=once(function(money){
console.log(`支付:${money}RMB`)
})
//只会支付一次
pay(5)
pay(5)
5.使用高阶函数的意义?
- 用来抽象通用问题
- 抽象可以帮助我们屏蔽细节,关注目标
//面向过程的方式
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)
})
let r=filter(array,item=>{
return item%2===0
})
6.map、every和some的内部实现?
map ( )遍历每一个元素的同时对,每一个元素进行处理。返回一个新数组
every ( )测试一个数组内的所有元素是否都能通过某个指定函数的测试,返回一个布尔值
some ( )测试数组中是不是至少有一个元素满足函数的要求。返回布尔值
// map
function map(func,array){
let result=[]
for(const item of array){
result.push(func(item))
}
return result
}
add=item=>item+1
console.log(map(add,[1,2,3]))
// every
function every(func,array){
let result=true
for(const item of array){
if(!func(item)){
// console.log(func(item),item)
result=false
}
}
return result
}
compareOne=item=>item>=4
console.log(every(compareOne,[1,2,3]))
//some
function some(func,array){
let result=false
for(const item of array){
if(func(item)){
result=true
}
}
return result
}
compareTwo=item=>item>=8
console.log(some(compareTwo,[1,2,5]))
7.闭包的定义?
在另一个作用域中,调用一个函数的内部函数,并且访问该函数作用域中的成员
8.闭包的本质?
函数在执行时,会放到一个执行栈上
当执行完毕会从执行栈上移除
但是堆上的作用域成员因被外部引用而不能释放,因此内部函数依旧可以访问外部函数的成员
9.什么是纯函数?
相同的输入永远会得到相同的输出,而且没有任何可观察的副作用(类似于数学中的函数)
其他:
函数式编程不会保留计算中间的结果,所以变量式不可变的(无状态的)??
可以把一个函数的执行结果交给另一个函数去处理
10.纯函数的好处?
-
可缓存——纯函数对相同的输入始终有相同的结果,所以可以把纯函数的结果缓存起来
-
可测试
-
可并行处理
- 在多线程环境下并行操作共享的数据可能会出现意外情况
- 纯函数不需要访问共享的内存数据,所以在并行环境下可以运行纯函数
11.纯函数中的副作用如何产生?有什么后果?
副作用让纯函数变得不纯,纯函数根据相同的输入返回相同的结果,如果函数依赖于外部的状态就无法保证输出相同,就会带来副作用
//不纯的
let mini=18
function checkAge(age){
return age>=mini
}
//纯的(有硬编码,后续通过柯里化解决)
function checkAge(age){
let mini=18
return age>=mini
}
副作用来源:
- 配置文件
- 数据库
- 获取用户的输入
副作用的后果?
- 使方法通用性下降,不适合扩展和可重用性
- 给程序带来安全隐患
12.柯里化的过程是什么?
- 当一个函数有多个参数的时候,先传递一部分参数调用它(这部分参数以后永远不变)
- 然后返回一个新的函数,接受剩余的参数,返回结果
//普通函数
function checkAge(min,age){
return age>=min
}
checkAge(18,24)
//柯里化
function checkage(min){
return function(age){
return age>=min
}
}
//Es6写法
let checkAge=min=>(age=> age>=min)
let checkAge18=checkAge(18)
let checkAge20=checkAge(20)
checkAge18(24)
checkAge20(20)
13.lodash中的柯里化函数 _.curry(func)如何使用?
功能:
- 创建一个函数,并接收一个或多个函数作为参数
- 如果该函数所需要的参数均被提供,那么则执行这个函数并返回结果
- 否则,继续返回该函数,并等待接受剩余参数
参数:需要柯里化的函数
返回值:柯里化后的函数
const _=require('lodash')
//需要柯里化的函数
function getSum(a,b,c){
return a+b+c
}
//柯里化后的函数
let curried=_.curry(getSum)
//测试
curried(1,2,3)
curried(1)(2)(3)
curried(1,2)(3)
案例
const _=require('lodash')
const match=_.curry(function(reg,str){
return str.match(reg)
})
const haveSpace=match(/\s+/g)
const haveNumber=match(/\d+/g)
const filter=_.curry(function(func,array){
return array.filter(func)
})
console.log(filter(haveSpace,['John Connor','John_Donne']))
const findSpace=filter(haveSpace)
console.log(findSpace['John Connor','John_Donne'])
模拟_.curry()的实现
function curry(func){
return function curriedFn(...args){
//判断实参和形参的个数
if(args.length<func.length){
return function(){
return curried(...args.concat(Array.from(arguments))
}
}
//实参和形参个数相同,调用func,返回结果
return func(...args)
}
}
补充:
- Array.from()方法对一个类似数组或可迭代对象,创建一个新的、浅拷贝的数组实例
- concat() 方法用于连接两个或多个数组。
自测
1.什么是函数式编程?它的思维方式是什么?特点是什么?
2.为什么说函数是一等公民?
3.什么是高阶函数?
4.使用高阶函数的意义?
5.map、every和some内部如何实现?
6.闭包的定义,闭包的本质
7.什么是纯函数以及纯函数的好处?
8.柯里化的过程是什么?
9.lodash中的_.curry如何使用?