概述
本文为拉勾网大前端高薪训练营第一期笔记
函数式编程的意义
- 随着react的流行受到越来越多的关注
- Vue 3也开始用函数式编程
- 抛弃this
- 打包过程可以更好的tree shaking过滤无用代码
- 方便测试,方便并行处理
- 很多库可以帮助做函数式开发,lodash、underscore、ramda
MDN first-class function
- 函数可以存储在变量
- 函数可以作为参数
- 函数作为返回值
函数可以存在变量
const BlogController = {
create(content){ return Db.create(content) },
}
等效
const BlogController = {
create: Db.create
}
高阶函数
函数作为返回值
意义:
- 抽象屏蔽细节,我们只需要关注目标
- 抽象通用的问题
闭包
在另一个作用域调用一个函数的内部函数,并访问到该函数的作用域中的成员
function makeFn(){
let msg = 'Hello Function'
return function() {
console.log(msg)
}
}
const fn = makeFn()
fn()
function once(fn) {
let done = false
return function (){
if(!done){
done = true
return fn.apply(this, arguments)
}
}
}
let pay = once(function (money){
console.log(`pay: ${money}`)
})
pay(5)
pay(5)
闭包的本质
函数在执行的时候会放在一个执行栈上,当函数执行完毕之后会从执行栈上移除,但是堆上的作用域成员因为被外部引用不能释放,因为内部函数依然可以访问外部函数的成员
纯函数
相同的输入永远得到相同的输出
纯函数的好处两点
- 可缓存,由于特性是同输入,输出恒定,可以用记忆函数来加速
例如
const getAreaWithMemory = _.memoize(getArea)
- 可测试,因为每次输入相同输出应该恒定
副作用:如果有外部变量参与纯函数运算,就会把纯函数变成不纯
函数柯里化currying
把多个参数的函数,变成函数包函数,第一次只传部分不怎么变化的参数,然后第二次传剩下函数。
_.curry()函数柯里化
const sum = _.curry((a,b,c)=>a+b+c)
sum(1,2,3)
sum(1)(2,3)
sum(2,3)(1)
自己实现_.curry()
const curry = (fn) => {
return function curriedFn(...args){
if(args.length < fn.length){
return function(){
return fn(...args.concat(Array.from(arguments)))
}
} else {
return fn(...args)
}
}
}
const sum = curry((a,b,c)=>a+b+c)
console.log(sum(1,2,3))
console.log(sum(1,2)(3))
fn.length能拿到形参的数量
组合函数
_.flow() _.flowRight()后者是从右向左执行
自己实现flowRight
const flowRight = function (...args) {
return function (value) {
return args.reverse().reduce((acc, fn)=>{
fn(acc)
}, value)
}
}
//单行写法
const flowRight = (...args) => value=> {return args.reverse.reduce(()=>{fn(acc)}, value)}
函数组合符合结合律
compose(f,g,h)等效compose(compose(f,g), h)等效compose(f, compose(g,h))
函数组合的注意
函数组合传入的都需要是一元函数,如果是二元或者更多就需要柯里化变成一元,调试的时候可以加log在函数组合之中
const log = v => {
console.log(v)
return v
}
//tag版本的log
const trace = _.curry((tag, v) => {
console.log(tag, v)
return v
})
trace('tag')
lodash fp
const fp = require(‘lodash/fp’)
fp.map(_.toLower)(array)
自带柯里化和参数的函数优先,数值后置
fp.map只会把数据传到下一个函数里,不会带上index collection
Point Free
- 不需要指明处理的数据
- 只需要合成运算过程
- 需要定义一些辅助的基本运算函数
Functor(函子)
容器:包含值和值的变形关系(这个变形关系就是函数)
函子:是一个特殊的容器,通过一个普通的对象来实现,该对象具有map方法,map方法可以运行一个函数对值进行处理(变形关系)
我的理解为用容器包数值,然后通过map传运算函数,最后返回新的容器,不断重复这个过程
maybe函子是处理了null和undefined的情况
either函子是处理了有错误的情况,可以把错误传到最后
IO函子是返回处理的function,不传值,最后运行的时候才生成值,函子部分永远是纯的,因为返回的是运算本身,不纯的操作甩锅到最后执行
folktale库可以有compose curry,也制造task函子,使用起来像spark
const { compose, curry } = require('folktale/core/lambda')
// Task 处理异步任务
const fs = require('fs')
const { task } = require('folktale/concurrency/task')
const { split, find } = require('lodash/fp')
function readFile (filename) {
return task(resolver => {
fs.readFile(filename, 'utf-8', (err, data) => {
if (err) resolver.reject(err)
resolver.resolve(data)
})
})
}
readFile('package.json')
.map(split('n'))
.map(find(x => x.includes('version')))
.run()
.listen({
onRejected: err => {
console.log(err)
},
onResolved: value => {
console.log(value)
}
})
pointed函子是实现了of静态方法的函子
避免使用new创建函子,更深层的含义是of方法用来把值放在上下文Context(把值放在函子,用map处理值)
Monad函子就是变扁的Pointed函子,一个函子如果同时具有join和of两个方法并遵守一些定律就是一个Monad
我的理解是当一个函子返回一个函子时,就flatMap,解决函子嵌套的问题,flatMap里就是把map合并操作和返回函子内部值join连续操作,如果普通的fp.toUpper这样的函数就直接用map就好了
错题分析
函数式编程不能提高性能
副作用也就是外界参数参与纯函数,使得纯函数变得不纯,不可避免
本文为拉勾网大前端高薪训练营第一期笔记
最后
以上就是专一大山为你收集整理的4.函数式编程范式笔记的全部内容,希望文章能够帮你解决4.函数式编程范式笔记所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复