为什么学习函数式编程
- 函数式编程随着react的流行受到越来越多的关注
- vue3也开始拥抱函数式编程
- 函数式编程可以抛弃this
- 打包过程中可以更好的利用tree shaking过滤无用代码
- 方便测试,方便并行处理
很多库可以帮助我们进行函数式开发:lodash,underscore,remda
函数相关复习
函数是一等公民
把函数赋值给变量
//把函数赋值给变量
let fn=function(){
console.log("hello")
}
fn()
//一个事例
const blogController={
index(props){return views.index(props)},
show(props){return views.show(props)},
create(props){return views.create(props)},
update(props){return views.update(props)},
destroy(props){return views.destroy(props)},
}
//优化
const blogController={
index:views.index,
show:views.show,
create:views.create,
update:views.update,
destroy :views.destroy
}
高阶函数
高阶函数-函数作为参数
function forEach(array ,fn){
for(let i=0;i<array.length;i++){
fn(array[i])
}
}
let arr=[1,2,3,5]
forEach(arr,(item)=>{
console.log(item)
})
function filter(array,fn){
let results=[]
for(let i=0;i<array.length;i++){
if(fn(arrat[i])){
results.push(array[i])
}
}
}
let arr=[1,2,3,5]
let res = filter(arr,()=>{
return item % 2 === 0
})
高阶函数-函数作为返回值
function makeFn(){
let msg='hello'
return function(){
console.log(msg)
}
}
makeFn()() //"hello"
function once(fn){
let done = false
return function(){
if(!done){
done=true
fn.apply(this,arguments)
}
}
}
let pay=once(function (money){
console.log(`支付:${money}rmb`)
})
//模拟常用高阶函数map,every,some
const map=(array,fn)=>{
let results=[]
for(let value of array){
results.push(fn(value))
}
return results
}
const every=(array,fn)=>{
let result=true
for(let value of array){
result=fn(value)
if(!result) break
}
return result
}
const some=(array,fn)=>{
let result=false
for(let value of array){
result=fn(value)
if(result) break
}
return result
}
let arr=[1,5,6,8]
arr=map(arr,v=>v*v)
let res=every(arr,v=>v>10)
let r=some(arr,v=>v % 2 === 0)
闭包
可以在另一个作用域中调用一个函数内部函数并访问到该函数的作用域中的成员
function makeFn(){
let mes="hello"
return function(){
console.log(msg)
}
}
const fn=makeFn()
fn()
闭包的本质:函数在执行的时候会放到执行栈上当函数执行完毕后会从执行栈上移除,但堆上的作用域成员因为被外部引用不能释放,因此内部函数依然可以访问外部函数的成员。
函数式编程基础
纯函数
纯函数必须有参数和返回值,并且相同的输入始终会得到相同的输出,而且没有任何可观察的副作用
let arr=[1,2,3,4,5]
//多次调用
arr.slice(0,3)//输出相同,纯函数
arr.splice(0,3)//输出不同,不纯
- 函数式编程不会保留计算中间的结果,所以变量是不可变的
- 我们可以吧一个函数的执行结果交给另一个函数去处理
lodash纯函数的代表
const _=require('lodash')
const array=['jack','bob','tim','lucy']
纯函数的好处
- 可缓存,提高函数性能
const _=require('lodash')
function getArea(r){
return MathPI*r*r
}
//模拟memoize
function memoize(fn)
let cache={}
return function(){
let key = JOSN.stringify(arguments)
cache[key]=cache[key]||f.apply(f,arguments)
return cache[key]
}
}
-
可测试
-
并行处理
1.多线程环境下操作共享内存数据很可能出现意外情况
2.纯函数不需要访问共享的内存数据,所以在并行环境可一任意运行纯函数
柯里化
function checkAge(min){
return function (age){
return age>=min
}
}
let checkAge=min=>(age=>age>=min)
let check18=checkAge(18)
let check20=checkAge(20)
console.log(check18(24))
function curry(fun){
return function curriedFn(...args){
if(args.length<func.length){
return function(){
return curriedFn(...args.concant(Array.from(arguments)))
}
}
}
}
- 当一个函数有多个参数的时候先传递一部分参数调用他
- 然后返回一个新的函数接收剩余的参数,返回结果
柯里化总结
- 柯里化可以让我们给一个函数传递较少的参数得到一个已经记住某些固定参数的新函数
- 这是一种对函数参数的缓存
- 让函数变得更加灵活,让函数的粒度更小
- 可以吧多元函数转化成一元函数,可以组合使用函数产生强大的功能
函数组合
- 概念:如果一个函数要经过多个函数处理才能得到最终的结果,这时候可以吧中间过程的函数合并成一个函数
- 函数组合默认是从右到左执行
function compose(){}
lodash中FP模块-函数式编程柯里化后的模块
如果有多个参数时,函数优先数据之后原则
函子
函子Functor
容器:包含值和值的变形关系
函子:是个特殊容器,通过一个普通对象来实现,该对象具有map方法,map方法可以运行一个函数对值进行处理
class Container{
static of(value){
return new Container(value)
}
constructor(value){
this._value=value
}
map(fn){
return Container.of(fn(this._value)
}
}
let r=Container.of(5).map(x=>x+1).map(x=>x*x)
console.log(r)//Container { _value: 36 }
总结
- 函数是编程运算不直接操作值,而是由函子完成
- 函子就是实现了map契约的对象
- 我们可以把函子想象成盒子,盒子里面封装了一个值
- 想要处理盒子重的值,我们需要给盒子的map方法专递一个处理值的函数(纯函数),由这个函数对值进行处理
- 最终map方法返回一个包含新值的盒子(函子)
MayBe函子
class MayBe{
static of(value){
return new MayBe(value)
}
constructor(value){
this._value=value
}
map(fn){
return this.isNothing() ? MayBe.of(null) : MayBe.of(fn(this._value)
}
isNothing(){
return this._value === null || this._value === undifined
}
}
Either函子
- Either两者中的任何一个,类似于if...else...的处理
- 异常会让函数变得不纯,Either函子可以用来做异常处理
class Left{
static of(value){
return new Left(value)
}
constructor(value){
this._value=value
}
map(fn){
return this
}
}
class Right{
static of(value){
return new Right(value)
}
constructor(value){
this._value=value
}
map(fn){
return Right.of(fn(this._value)
}
}
function parseJSON(str){
try{
return Right.of(JSON.parse(str))
}catch(e){
return Left.of({error:e.message})
}
}
IO函子
- IO函子中的_value是一个函数,这里是把函数作为值来处理
- IO函子可以把不纯的动作存储到_value中,延迟执行这个不纯的操作
- 把不纯的操作交给调用者来处理
const fp= require('lodash/fp')
class IO{
static of(value){
return new IO(function(){
return value
})
}
constructor(fn){
this._value=fn
}
map(fn){
return new IO(fp.flowRight(fn.this._value))
}
}
let r =IO.of(process).map(p=>p.execPath)
console.log(r._value())
Task函子
const fs =require('fs')
const {task} =require('folktale/cocurrency.task')
const {spilt,find}=require('lodash/fp')
function readFile(filename){
fs.readFile(filename,"utf-8",(err,data)=>{
if(err) resolver.reject(err)
resolver.resolve(data)
})
}
readFile('pakage.json')
.map(split(''/n'))
.map(find(x=>x.inclues('version')))
.run()
.listen({
onRejected:err=>{},
onResolved:value=>{}
})
网友评论