在这篇文章里,咱们讨论函数式编程。html
什么是函数式编程?根据百度百科的描述,“函数式编程是种编程典范,它将电脑运算视为函数的计算。函数编程语言最重要的基础是 λ 演算(lambda calculus)。并且λ演算的函数能够接受函数看成输入(参数)和输出(返回值)。和指令式编程相比,函数式编程强调函数的计算比指令的执行重要。和过程化编程相比,函数式编程里,函数的计算可随时调用。”编程
能够看出,在函数式编程中,函数被看作是“一等公民”。JavaScript能够经过巧妙地函数组合来构建抽象,经过内嵌函数的方式,在软件开发的过程当中,咱们能够把更多的精力放在“函数要作什么”上,而不用太关心“函数如何作”的问题。数组
能够操做其余函数的函数,被称为高阶函数。例如咱们想对数组的每个元素作某种操做,那么咱们须要遍历整理数组,当操做发生改变时,咱们还要重复编写遍历代码,利用高阶函数,能够简化这个过程。示例以下:app
1 function forEach(array,func){ 2 for(var i = 0; i < array.length; i++){ 3 func(array[i]); 4 } 5 } 6 7 var a = ["a","b","c"]; 8 forEach(a,function(obj){print(obj);}); 9 10 forEach(a,function(obj){print(obj + 1);}); 11 12 13 //输出结果 14 a 15 b 16 c 17 a1 18 b1 19 c1
forEach函数包含两个参数,第一个参数是一个数组,第二个参数是一个函数,在forEach函数体内,会遍历数组的每个元素,而后针对每个元素调用func函数。编程语言
在调用forEach函数时,咱们针对第二个参数,使用了匿名函数,它接受一个参数,这个参数其实就是数组中的元素。函数式编程
从这个示例中,咱们能够看到经过这种方式,能够明显简化对数组的操做。函数
咱们能够经过高阶函数很方便的修改已有函数的功能,示例以下:学习
1 function reverse(func){ 2 return function(value){ 3 return !func(value); 4 } 5 } 6 7 print(isNaN(NaN)); 8 var isNotNaN = reverse(isNaN); 9 print(isNotNaN(NaN)); 10 11 12 //输出结果 13 true 14 false
reverse的做用是逆转传入函数的操做,在示例中,isNaN函数返回传入参数是不是NaN。xml
Reduce函数经过重复调用一个函数,将数组转换为单一的值。规约函数结构以下:htm
1 function reduce(combine,base,array){ 2 forEach(array, function(value){ 3 base=combine(base,value); 4 }); 5 return base; 6 }
Reduce函数中参数的顺序是一个传统,咱们能够将第一个参数做为匿名函数的方式传入。
下面是两个使用Reduce函数的示例,分别计算数组元素的和以及数组中0的个数:
1 function countZeros(count,value){ 2 return value == 0 ?(count+1) : count; 3 } 4 5 function add(sum,value){ 6 return value+sum; 7 } 8 9 var a=[1,2,3,4,0]; 10 print(reduce(add,0,a)); 11 print(reduce(countZeros,0,a)); 12 13 14 //输出结果 15 10 16 1
Map函数会遍历数组,针对数组的每一个元素,调用指定的操做,而后将操做得出的值存储到另一个数组中,并返回新数组。
Map函数的结构以下:
1 function map(func,array){ 2 var result=[]; 3 forEach(array, function(value){ 4 result.push(func(value)); 5 }); 6 return result; 7 }
咱们能够以下调用map方法:
1 var a=[1,2,3,4,0]; 2 3 print(map(function(value){ 4 return value*value; 5 }, a)); 6 7 //输出结果 8 1,4,9,16,0
这个示例将数组中的每一个元素进行平方操做,而后输出。
在JavaScript学习(1):基础中,咱们使用内嵌函数实现了四则运算,接下来咱们试着另一种实现方式:
1 var a=[1,2,3,4,0]; 2 var ops={"+":function(x,y){return x+y;}, 3 "-":function(x,y){return x-y;}, 4 "*":function(x,y){return x*y;}, 5 "/":function(x,y){return x/y;}, 6 }; 7 8 function operation(op, array){ 9 if (op in ops){ 10 return reduce(ops[op],0,array); 11 } 12 else{ 13 throw new Error("invalid operation."); 14 } 15 } 16 print(operation("+", a)); 17 print(operation("^", a)); 18 19 //输出结果 20 10
对象中,属性的值不单单能够是集合属性,也能够是函数。上述示例使用这种方式对四则运算进行了封装。而后调用reduce方法去计算数组元素的和。
若是咱们须要一个函数,但其中一个操做符的参数已经给定了,那应该如何处理?例如咱们想对数组中的每一个元素都作加1操做,使用map的方式实现:
1 print(map(function(value){ 2 return value + 1; 3 },a));
在这里,1是放在匿名函数中。咱们还能够这样作,使用分布应用的方式在外函数和内嵌函数中分别保存部分参数。
分布应用的结构以下:
1 function partial(func){ 2 var knowArgs=arguments; 3 return function(){ 4 var realArgs=[]; 5 for(var i = 1; i < knowArgs.length; i++){ 6 realArgs.push(knowArgs[i]); 7 } 8 for(var i = 0; i < arguments.length; i++){ 9 realArgs.push(arguments[i]); 10 } 11 return func.apply(null, realArgs); 12 } 13 }
若是想要实现上面一样的功能,代码以下:
1 print(map(partial(ops["+"], 1), a));
须要注意的是partial函数中在内嵌函数中如何将外函数和内嵌函数的参数进行整合,构成完整的参数列表。
以a=[1,2,3,4,0]为例,map函数会遍历数组中的每个元素,当遍历到2时,参数的变化过程:
1. 外函数参数:1) ops["+"]; 2) 1。
2. 内嵌函数参数: 2
3. 完整参数:1,2
4. func.apply(null, realArgs): ops["+"](1,2)
函数组合的含义是在调用函数A的过程当中,它使用函数B来计算返回结果。
就像这样:
1 function compose(f1,f2){ 2 return function(){ 3 return f1(f2.apply(null,realArgs)); 4 } 5 }
上面示例中的isNotNaN也是这种状况。