目录
一.范畴论
- 函数式编程是范畴论的数学分支是一门很复杂的数学,认为世界上所有概念体系都可以抽象出一个个范畴
- 彼此之间存在某种关系概念、事物、对象等等,都构成范畴。任何事物只要找出他们之间的关系,就能定义
- 箭头表示范畴成员之间的关系,正式的名称叫做“态射”(morphism)。范畴论认为,同一个范畴的所有成员,就是不同状态的“变形”(transformation)。通过“态射”,一个成员可以变形成另一个成员
二.函数式编程基础理论
- 函数式编程其实相对于计算机的历史而言是一个非常古老的概念,甚至早于第一台计算机的诞生。函数式编程的基础模型来源于λ(Lambda x=>x*2)演算,而λ演算并非设计于在计算机上执行,它是在20世纪三十年代引入的一套用于研究函数定义、函数应用和递归的形式系统。
- 函数式编程不是用函数来编程,也不是传统的面向过程编程。主旨在于将复杂的函数符合成简单的函数(计算理论,或者递归论,或者拉姆达演算)。运算过程尽量写成一系列嵌套的函数调用。
- Javascript是披着c外衣的Lisp
// 用函数编程,非函数式编程 function test(){ } test()
- 真正的火热是随着React的高阶函数而逐渐升温
三.特性
- 函数是一等公民。所谓“第一等公民”,指的是函数与其他数据类型一样,传入另一个函数,或者作为别的函数的返回值。
- 不可改变变量。在函数式编程中,我们通常理解的变量在函数式编程中也被函数代替了:在函数式编程中变量仅仅代表某个表达式。这里所说的"变量"是不能被修改的。所有的变量只能被赋一次初值
- 没有“副作用”
- 不修改状态
- map和reduce是最常用的函数式编程方法
四.高阶函数
1.纯函数
- 对于相同的输入,永远会得到相同的输出,而且没有任何可观察的副作用,也不依赖外部环境的状态。
-
var xs = [1,2,3,4]; //Array.slice是纯函数,因为它没有副作用,对于固定的输入,输出总是固定的 xs.slice(0,2); xs.splice(0,2); //会改变源数据,非纯函数
优缺点:
-
import _ from 'loadsh'; var sin = _.memorize(x => Math.sin(x)); //第一次计算的时候会稍微慢一点 var a = sin(1); //第二次有了缓存,速度极快 var b = sin(1); //纯函数不仅可以有效降低系统的复杂度,还有很多很棒的特性,比如可缓存性
-
//不纯的 var min = 18; var checkage = age => age > min; //纯的,这很函数式 var checkage = age => age > 18; //在不纯的版本中,checkage不仅取决于age 还有外部依赖的变量min //纯的checkage把关键数字18硬编码在函数内部,扩展性比较差,柯里化优雅的函数式可以解决问题 //柯里化之前 function add(x, y) { return x + y } add(1, 2) //柯里化之后 function addX(y) { return function (x) { return x + y } } addX(2)(1)
2.函数的柯里化
- 传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。
-
var checkage = min => (age => age > min); var checkage2 = checkage(18); checkage2(20);
function foo(p1, p2) { this.val = p1 + p2; } var bar = foo.bind(null, 'p1'); var baz = new bar('p2'); console.log(baz.val);
事实上柯里化是一种 “预加载” 函数的方法,通过传递较少的参数,得到一个已经记住了这些参数的新函数,某种意义上讲,这是一种对参数的“缓存”,是一种非常高效的编写函数的方法。
3.point free
- 把一些对象自带的方法转化成纯函数,不要命名转瞬即逝的中间变量
-
//转变前,我们不关心str这个中间变量 const f = str => str.toUpperCase().split(' '); //转换后 var toUpperCase = word => word.toUpperCase(); var split = x => (str => str.split(x)); var f = compose(split(' '), toUpperCase); f("abcd efgh") //这种风格能够帮助我们减少不必要的命名,让代码保持简洁和通用 //实现一个compose function compose(...funcs) { // 没有传入函数运行直接返回参数 if (funcs.length === 0) { return arg => arg } // 只传入一个函数,就返回其本身 if (funcs.length === 1) { return funcs[0] } // 核心代码其实就是一句reduce, reduce特性就是按顺序执行,并且将结果传