函数式编程 ( Functional Programming ) 是一种以函数为基础的编程方式和代码组织方式,从程序员的思路上来讲,就是将程序拆分并抽象成多个函数,再组装回去。前端
在JavaScript这门语言中,函数是一等公民,函数能够做为函数的参数,函数也能够返回一个函数,所以适合函数式编程风格。程序员
函数式风格的编程,长于数据的处理,且所谓“处理”并不改变数据自己。JavaScript中,数据老是以对象的形式出现,也就是说,函数式的编程对于“接收对象A→处理→处理→处理→得出新对象B”这样的流程能够很优雅地实现,且不影响对象A自己。编程
对于JavaScript程序员(不管是前端仍是后端),最多见且实用的编程模式固然是抽象出类(以构造函数的形式)并实例化,而后在实例上调用方法。函数式风格的编程不是为了取代这种编程模式,而是为了与之造成互补。后端
一些特色:数组
不改变输入的数据安全
不耦合,也就是说,函数尽可能不影响甚至知晓外部的状态数据结构
保持数据组织形式的简单,例如,尽可能使用JavaScript原生的数据结构(对象、数组等)闭包
在函数式编程中,任何代码能够都是函数,且要求具备返回值,以下示例ide
// 非函数式 var title = "Functional Programming"; var saying = "This is not"; console.log(saying + title); // => This is not Functional Programming // 函数式 var say = title => "This is " + title; var text = say("Functional Programming"); // => This is Functional Programming
纯函数在这里指函数内外间是“无”关联的。主要有下面两点
没有反作用(side effect)
不会涉及到外部变量的使用或修改
引用透明
函数内只会依赖传入参数,在任什么时候候对函数输入相同的参数时,总能输出相同的结果函数式编程
// 非纯函数(函数内依赖函数外的变量值) var title = "Functional Programming"; var say = ()=> "This is not" + title; // <= 依赖了全局变量 title // 纯函数 var say = (title)=>"This is " + title; // <= 依赖了以参数 title 传入 say("Functional Programming");
这里主要是指变量值的不可变。当须要基于原变量值改变时,可经过产生新的变量来确保原变量的不变性,以下
// 可变数据 var arr = ["Functional", "Programming"]; arr[0] = "Other"; // <= 修改了arr[0]的值 console.log(arr) // => ["Other", "Programming"] // 变量arr值已经被修改 // 不可变数据 var arr = ["Functional", "Programming"]; // 获得新的变量,不修改了原来的值 var newArr = arr.map(item => { if(item === "Functional"){ return "Other"; } else { return item; } }) console.log(arr); // => ["Functional", "Programming"] 变量arr值不变 console.log(newArr); // => ["Other", "Programming"] 产生新的变量newArr
之因此使用这种不变值,除了更好的函数式编程外,还可以维持线程安全可靠,实际上也能让代码更加清晰。
设想,若是你定义了一个变量A,A在其余地方被其余人修改了,这样是不方便定位A的当前值的。
使用 map, reduce 等数据处理函数
强大的 JavaScript 有着愈来愈多的高能处理数据函数,其中包含了 map、 reduce、 filter 等。
map 可以对原数组中的值进行逐个处理并产生新的数组,一个简单例子
// map var data = [1, 2, 3]; var squares = data.map( (item, index, array) => item * item ); console.log(squares); // => [1, 4, 9] console.log(data);// => [1, 2, 3] data 仍是那个 data
reduce 可以对原数组中的各个值进行结合处理,来产生新的值,以下面例子中,previous 表明上一个值,current 表明当前值,reduce 函数能够传入第二个参数做为 previous 初始值,不传时则 previous 初始值为数组中第一个值。
// reduce var sum = [1, 2, 3].reduce( (previous, current, index, array) => previous + current ); console.log(sum); // => 6
柯里化 是将多参函数转换成一系列的单参函数。结合下面例子来讲明下
// 一个多参函数 var add = (a, b) => a + b; add(1, 2); // => 3
将上面的多参函数进行柯里化,以下
// 柯里化函数 var add = a => b => a + b;
上面柯里化后的函数调用方式也有所转变,第一次传入一个参数返回了一个函数,再传入参数则完成总体的调用,这也是利用的闭包的特性
var add1 = add(1); add1(2); // => 3
柯里化后的函数,也能够应用在生产 “ 函数 ” 上,以下示例
var say = title => type => title + " is " + type; var sayFP = say("Functional Programming"); var sayOther = say("Other Programming"); sayFP("good"); // => Functional Programming is good sayOther("good"); // => Other Programming is good
顾名思义,组合函数是将多个函数进行组合成一个函数。举个例子
var compose = (fn1, fn2) => (arg) => fn1(fn2(arg)); var a = arg => arg + 'a'; var b = arg => arg + 'b'; var c = compose(a, b); // 将a,b函数进行组合 c('c'); // => cba
上面示例中,当调用组合函数 c 时,传入的参数会通过 b 函数,接着将 b 函数的返回值做为 a 函数的参数值,从而输出最终结果。组合函数 c 就像管道同样,将水流( 返回值 )流经各个函数中进行处理。