高阶函数入门指北
woodbrick opened this issue · comments
1 引言
作为一等公民,函数在JavaScript中拥有重要的地位,函数本身的特性也使得JavaScript能够轻易并且优雅的处理其他语言难以处理的问题。
JavaScript中的函数,有以下两个特性:
- 闭包
- 函数本身是一个变量
而高阶函数正是利用这两个特性,来实现的。
2 正文
由于JavaScript的函数指向某个变量。既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。
举个栗子:
function add(a, b, func) {
return func(a) + func(b);
}
这是一个最简单的高阶函数结构,那么他能用来做什么的呢?
add(-5, 5, Math.abs)
大家想一下,他返回的值是什么呢?
没错,通过这个高阶函数,我们把两个数字的绝对值相加了。
写到这里,大家也许会想,其实我直接调用两次绝对值函数,也能够很快实现同样的功能啊?
function addByAbs(a, b) {
return Math.abs(a) + Math.abs(b);
}
那么,这两种方式,本质有什么区别呢?
在于变量,通过高阶函数,我们可以灵活控制求和的前置方法。
如果现在我们有一份数据:
let scores = [
{
name: '张三',
math: 88,
english: 66,
chinese: 77
},
{
name: '李四',
math: 66,
english: 99,
chinese: 99
},
{
name: '赵五',
math: 77,
english: 88,
chinese: 88
}
];
我们应该要如何把同学的成绩,按照总分降序排序呢?
scores.sort((a, b) =>
(b.math + b.english + b.chinese)
- (a.math + a.english + a.chinese)
)
JavaScript原生的sort方法,支持我们传入一个比较器,来对数组中的元素进行比较,从而实现排序算法和排序业务逻辑的分离。
它接受了一个函数作为参数,使得我们的业务逻辑可以灵活的变动,而不影响排序的代码。
如果此时老师说,对于理科生,数学更重要,数学成绩需要乘以1.5,此时我们去修改排序的算法,应该怎么写呢?
scores.sort((a, b) =>
(b.math * 1.5 + b.english + b.chinese)
- (a.math + a.english + a.chinese)
)
我们快速的修改了代码,然后运行,糟糕,结果出错了!
原来我们把成绩求和的算法写了两遍,漏改了其中一份,实际应该是
scores.sort((a, b) =>
(b.math * 1.5 + b.english + b.chinese)
- (a.math * 1.5 + a.english + a.chinese)
)
但是这段代码,看起来不太优雅,也产生了一定的维护成本,因为我们手写了两遍求总分的算法,需求变动的时候,需要修改两处,容易错漏。
那么我们是否可以通过高阶函数的**,来进行处理呢?
大家回忆一下开篇的代码,不难得出一下结论
function getTotalScore(score){
score.math * 1.5 + score.english + score.chinese
}
function compare(a, b, func) {
return func(b) - func(a);
}
但是问题又来了,sort函数的参数,是一个比较器方法,只允许传递两个元素,这种情况,要怎么处理呢?
function getTotalScore(score){
score.math * 1.5 + score.english + score.chinese
}
function getDescComparator(func) {
return function(a, b) {
return func(b) - func(a);
};
}
let scoreComparator = getDescComparator(getTotalScore);
scores.sort(scoreComparator);
我们现在获得了两个方法:
1.业务逻辑相关的方法getTotalScore,这部分表示如何获取一个学生的排序分数
2.高阶函数getDescComparator,他将获取比较分数的方法作为参数,返回一个比较方法,当然在这个过程中,形成了一个闭包,即比较器持有了getTotalScore这个变量(函数)
现在我们回顾一下开篇:
JavaScript的函数特性有
- 闭包
- 函数本身是一个变量
而高阶函数正是利用这两个特性,来实现的。
小结
高阶函数提供了我们两种解决问题的思路:
- 我们可以将需要动态处理的问题,封装成单独的函数,并且传递给不同的使用场景。
- 我们可以将一个函数进行变化,获取新的函数,最终在函数之间,构建一种排列组合的关系,而不是通过重复劳动来写很多相似的代码。
最后,送大家一个小福利:
function thunkingDebug(func) {
return function() {
try {
func.apply(this, arguments)
} catch (e) {
window.location.href =
"http://stackoverflow.com/search?q=[js]+" + e.message;
}
}
}
by kaolafed/yaofeng
参考文档: