函数式编程
背景
react, vue3.0 都在转换为函数式编程的思想
内容
概念
思维模式的不同
1、函数式编程是对运算过程进行抽象;
2、面向对象是对现实世界中的事物进行抽象,通过封装、继承和多态来演示事物之间的联系
函数具体指映射关系,同理数学中的函数概念
重点:相同的输入要有相同的输出
函数在javascript中:
- 函数可以存储在变量中
- 函数可以作为参数
- 函数可以作为返回值
高阶函数
- 可以把函数作为参数传递给另一个函数
- 可以把函数作为另一个函数的返回结果
函数式的意义
- 函数抽象帮助我们屏蔽细节,只关注目标;
- 高阶函数就是用来抽象通用的问题;再抽象一层方法的方法;
闭包(Closure )
提示:Closure可以在浏览器调试模式打点时看到;
函数执行时,当函数执行完毕后,其内部成员都会被释放掉,除非还有其他地方引用了它;
闭包就是函数内部变量被外部引用了。无法释放,多的时候就内存泄漏了,一般问题不大;
概念:在一个作用域中去调用一个函数内部的函数并访问到该函数作用域中的成员,这就是闭包;
1、在一个作用域中可以调用到一个函数的内部函数;
2、在调用内部函数时,可以访问到这个函数作用域中的成员;
闭包的核心作用:及把函数内部成员的作用范围延伸了,外部也可以使用;
闭包的本质:函数在执行的时候会放到一个执行栈上当函数执行完毕之后会从执行栈上移除(进行释放),但是堆上的作用域成员因为被外部引用不能释放,因此内部函数依然可以访问外部函数的成员。
纯函数
概念:相同的输入永远会得到相同的输出,而且没有任何可观察的副作用
心得:相同的输入有相同的输出,利用闭包可以缓存一些东西;
纯函数的好处:
- 可缓存 - 因为纯函数对相同的输入始终有相同的结果,所以可以把纯函数的结果缓存起来;
- 可测试 - 纯函数让测试更方便;
- 并行处理 - 在多线程环境下并行操作共享的内存数据很可能会出现意外情况;
纯函数不需要访问共享的内存数据,所以在并行环境下可以任意运行纯函数 (Web Worker)
副作用(及会把函数变得不纯的情况很多)
所有的外部交互都有可能带来副作用,副作用也使得方法通用性下降不适合扩展和可重用性,同时副作
用会给程序中带来安全隐患给程序带来不确定性,但是副作用不可能完全禁止,尽可能控制它们在可控
范围内发生。
柯里化
1、当一个函数有多个参数的时候先传递一部分参数调用它(这部分参数以后永远不变)
2、然后返回一个新的函数接收剩余的参数,返回结果
3、将函数多远降一元
总结
- 柯里化可以让我们给一个函数传递较少的参数得到一个已经记住了某些固定参数的新函数;
- 这是一种对函数参数的’缓存’;
- 让函数变的更灵活,让函数的粒度更小;
- 可以把多元函数转换成一元函数,可以组合使用函数产生强大的功能
函数组合
注意点:组合函数要满足结合律:例如
我们既可以把 g 和 h 组合,还可以把 f 和 g 组合,结果都是一样的
// 结合律(associativity)
let f = compose(f, g, h)
let associative = compose(compose(f, g), h) == compose(f, compose(g, h))
// true
函数式编程工具
推荐使用Lodash,
lodash 的 fp 模块(lodash/fp)
1、lodash 的 fp 模块提供了实用的对函数式编程友好的方法;
2、参数为函数优先,数据置后并且已经被柯里化后的;
函子(Functor)
背景:
用来处理纯函数的副作用问题(相同的输入没有相同的输出),将其控制在可控范围内,也可以用来处理异常处理、异步操作等。
概念:
- 容器:包含值和值的变形关系(这个变形关系就是函数) ;
- 函子:是一个特殊的容器,通过一个普通的对象来实现,该对象具有 map 方法,map 方法可以运 行一个函数对值进行处理(变形关系);
特点 - 函数式编程的运算不直接操作值,而是由函子完成;
- 函子就是一个实现了 map 契约的对象;
- 我们可以把函子想象成一个盒子,这个盒子里封装了一个值 想要处理盒子中的值,我们需要给盒子的 map 方法传递一个处理值的函数(纯函数),由这个函数来对值进行处理;
- 最终 map 方法返回一个包含新值的盒子(函子);
Folktale库提供了一些函子
Maybe函子 (处理空值null)
Either 函子 (if…else…)
IO函子 (处理纯函数的副作用,把不纯的方式抛给调用者
一个返回函数的函子,想取值时再调用这个函数,可将函数内的运算逻辑延迟到最后调用时。