describe what to do, rather than how to do it。
这句话是函数式编程的思想精髓。它只关心输入数据和输出数据的关系。关注的是做什么而不是怎么做。
特点
惰性求值
表达式不在它被绑定到变量之后就立即求值,而是在该值被取用的时候求值。
确定性
函数无论在什么场景下,都会得到同样的结果。
缺点
数据复制比较严重
常用技术
函数式编程和过程式编程的思维方式完全不一样。过程式编程是在把具体的流程描述出来,所以可以不假思索,而函数式编程的抽象度更大,在实现方式上,有函数套函数、函数返回函数、函数里定义函数...常用的技术有:
递归(recursing)
通过反复调用自身,把一个复杂的问题用很简单的代码描述出来。
这里要注意:递归函数相当于一种循环调用,需要避免死循环,给定一个条件停止调用。
递归常用在嵌套数组或树的遍历中。例如深拷贝:
function clone(o){
var temp = {};
for(var key in o){
if(typeof o[key] == 'object'){
temp[key] = clone(o[key]);
}else{
temp[key] = o[key];
}
}
return temp;
}
尾递归(tail recursion optimization)
当一个函数执行时的最后一个步骤是调用自身,这就叫做尾递归。
-
优点:常规的函数递归很深的话,stack 受不了,并会导致性能大幅度下降。而尾递归优化技术再每次递归时都会重用 stack,这样能够提升性能。
-
缺点:需要把函数内部所有用到的中间变量改写为函数的参数,语义不明显。
柯里化(currying)
就是把一个多参数的函数,转化为单参数函数, 然后将函数多层封装起来,每层函数都返回一个函数去接收下一个参数。
// 方法一:柯里化之前
function add(x, y) {
return x + y;
}
add(1, 2) // 3
// 方法二:柯里化之后
function addCurry(y) {
return function (x) {
return x + y;
};
}
// let addCurry = (y) => (x) => x + y es6写法
let add2 = addCurry(2)
let result = add2(1) //result:3
有人可能会说,求2个数的加法,使用方法一很好啊,一目了然。确实,如果仅是简单的业务需求去求解2个数的加法,犯不着用上函数柯里化。这里多此一举 进行currying,主要是体现函数式编程的理念:
1. 把函数当成变量来用,关注描述问题而不是怎么实现,这样可以让代码更易读。
2. 因为函数返回里面的这个函数,所以函数关注的是表达式,关注的是描述这个问题,而不是怎么实现这个事情。
纯函数(pure function)
不依赖于程序执行期间函数外部任何状态或数据的变化,必须只依赖于其输入参数的函数。
// 非纯函数,有状态
var cnt = 0;
function increment(){
cnt++;
}
//纯函数,无状态
function increment(cnt){
return cnt+1;
}
诸如 ReactJS 和 Redux 等优质的库都使用了纯函数。使用纯函数的优点:
-
可以直接测试。 如果传入相同的参数,它们将始终产生相同的结果。
-
维护和重构代码变得更加容易。因为没有副作用,代码之间耦合少,因此改写会更容易。
高阶函数(higher order function)
高阶函数就是函数当参数,把传入的函数做一个封装,然后返回这个封装函数
map && reduce && filter
map
js中的map函数式array数组原型上的方法,返回一个新的数组。其定义为
/**
第1个参数是函数,数组中的每个元素都会执行这个函数;需要 return
第2个参数用作 "this" 的值,如果缺省,回调函数的 this 为全局对象
*/
array.map(function(currentValue,index,arr), thisValue)
//自定义map函数
Array.prototype.myMap = (callback, context) => {
context = context || window;
let newArr = [];
for(let i=0,len=this.length;i<len;i++) {
if (typeof callback === 'function') {
let val = callback.call(context,this[i],i,this);
newArr[newArr.length] = val;
}
return newArr;
}
由此可见,map函数是一个高阶函数,一个纯函数。
filter
用于对数组进行过滤,返回一个新的数组.新数组中的元素是通过检查指定数组中符合条件的所有元素。定义为:
Array.filter(function(currentValue, index, arr), thisValue)
reduce
该方法对累加器和数组中的每个元素(从左到右)应用一个函数,最终合并为一个值。
array.reduce(function(total, currentValue, currentIndex, arr), initialValue)