看了这篇文章,你就会高阶函数了,是不是听起来很牛?高阶函数,听起来很高级,其实是很接地气,大家经常会用到的东西,比如filter,map,回调函数。
高阶函数是对其他函数进行操作的函数,可以将它们作为参数或通过返回它们。简单来说,高阶函数是一个函数,它接收函数作为参数或将函数作为输出返回。
在《javascript设计模式和开发实践》中是这样定义的: 1.函数可以作为参数被传递; 2.函数可以作为返回值输出。
举个例子:
function foo(f){
if(f instanceof Function){
f();
}
}
foo(function(){
alert("asdf");
})
定义了一个foo函数,foo函数传入了一个函数作为参数,foo函数里面添加判断,参数如果是函数就执行该参数。这就是一个简单的高阶函数。
这个基本上是我们非常常用的高阶函数了,我们可以来看一下回调函数的代码:
function greeting(name) {
alert('Hello ' + name);
}
function processUserInput(callback) {
var name = prompt('请输入你的名字。');
callback(name);
}
processUserInput(greeting);
定义了一个greeting函数,又定义了一个processUserInput函数,之后我们又把greeting函数作为参数传到了processUserInput中,这个就符合了高阶函数中的第一个定义,作为参数被传递。
回调函数作为高阶函数中的一种,它是干什么的呢?
回调函数是指 使用者自己定义一个函数,实现这个函数的程序内容,然后把这个函数(入口地址)作为参数传入别人(或系统)的函数中,由别人(或系统)的函数在运行时来调用的函数。
这么说,可能听不明白。
callback 一词本来用于打电话。你可以打电话(call)给别人,也可以留下电话号码,让别人回电话(callback)。回调函数的意思就是,你写了一个函数,让别人来调用,就是回调函数。
来看一看异步的回调函数:
function ff(a, b, cbk) {
setTimeout(() => {
cbk(a + b);
}, 3000);
}
function f(callback) {
var x = 3, y = 4;
var z = 0;
callback(x, y, function (re) {
z = re;
console.log(z)
});
console.log("主函数")
return z;
}
f(ff);
对比来看,回调与同步、异步并没有直接的联系,回调只是一种实现方式,既可以有同步回调,也可以有异步回调,还可以有事件处理回调和延迟函数回调,这些在我们工作中有很多的使用场景。
我们可以像使用变量一样使用函数,作为另一个函数的参数,在另一个函数中作为返回结果,在另一个函数中调用它。当我们作为参数传递一个回调函数给另一个函数时,我们只传递了这个函数的定义,并没有在参数中执行它。
当包含(调用)函数拥有了在参数中定义的回调函数后,它可以在任何时候调用(也就是回调)它。
这说明回调函数并不是立即执行,而是在包含函数的函数体内指定的位置“回调”它(形如其名)。
回调函数是闭包的。
当作为参数传递一个回调函数给另一个函数时,回调函数将在包含函数函数体内的某个位置被执行,就像回调函数在包含函数的函数体内定义一样。闭包函数可以访问包含函数的作用域,所以,回调函数可以访问包含函数的变量,甚至是全局变量。
什么时候用回调函数?
我一般和别人合作项目的时候,想让人感觉我的代码写的很厉害,有时候会故意写两个回调,但是这种行为不可取的,减少代码冗余,无用的代码只会造成维护上的困难。
假如,你点击了一个按钮,你想让它执行连个函数,先执行函数A,再执行函数B,这时候就可以用回调函数了。
//定义主函数,回调函数作为参数
function A(callback) {
callback();
console.log('我是主函数');
}
//定义回调函数
function B(){
setTimeout("console.log('我是回调函数')", 3000);//模仿耗时操作
}
//调用主函数,将函数B传进去
A(B);
这里有一个问题,我们执行A函数后,执行B函数,为什么不直接在A函数里调用,要传参过去呢?
function A(callback) {
B();
console.log('我是主函数');
}
function B(){
setTimeout("console.log('我是回调函数')", 3000);//模仿耗时操作
}
这段代码的作用和回调函数好像是一样的。其实这两种方法在性能上是没有区别的,只是在灵活性上有很大的区别。 例如,我定义了一个C函数为回调函数。
//定义主函数,回调函数作为参数
function A(callback) {
callback();
console.log('我是主函数');
}
//定义回调函数
function B(){
setTimeout("console.log('我是回调函数')", 3000);//模仿耗时操作
}
//调用主函数,将函数B传进去
function C(){
setTimeout("console.log('我是回调函数')", 3000);//模仿耗时操作
}
//调用主函数,将函数C传进去
A(B);
A(C);
这时候,在试一下这段代码,就会出现很大的分歧。
function A(callback) {
B();
C();
console.log('我是主函数');
}
function B(){
setTimeout("console.log('我是回调函数')", 3000);//模仿耗时操作
}
function C(){
setTimeout("console.log('我是回调函数')", 3000);//模仿耗时操作
}
这是另一种高阶函数,老样子,先看一下代码。
array.filter(function(currentValue,index,arr), thisValue)
filter 接收一个函数作为参数,所以它也符合高阶函数中的第一个定义,作为参数被传递。
那什么是filter呢? filter()方法会创建一个新数组,原数组的每个元素传入回调函数中,回调函数中有return返回值,若返回值为true,这个元素保存到新数组中;若返回值为false,则该元素不保存到新数组中;原数组不发生改变。
简单来说,filter就是一个过滤数组的方法,符合条件的被传入新的数组,不符合条件的,就不管它了。
举个例子:
在一个Array中,删掉偶数,只保留奇数,可以这么写:
var arr = [1, 2, 4, 5, 6, 9, 10, 15];
var r = arr.filter(function (x) {
return x % 2 !== 0;
});
r; // [1, 5, 9, 15]
定义了arr这个数组,arr数组使用的过滤器,过滤器中函数的作用就是把数组中的偶数过滤出来,放进r数组中。
使用filter,注意两点,1.filter() 不会对空数组进行检测;2. filter() 不会改变原始数组。
过滤器很强大,比如,我们面试经常会考的数组去重:
'use strict';
var r,arr = ['apple', 'strawberry', 'banana', 'pear', 'apple', 'orange', 'orange', 'strawberry'];
r = arr.filter(function (element, index, self) {
return self.indexOf(element) === index;
});
console.log(r.toString()); //apple,strawberry,banana,pear,orange
map()方法定义在JavaScript的Array中,它返回一个新的数组,数组中的元素为原始数组调用函数处理后的值。
注意: 1.map()不会对空数组进行检测;2.map()不会改变原始数组。
来看一看map的示例:
function pow(x){
return x*x;
}
var arr = [1,2,3,4,5,6];
var results = arr.map(pow);
console.log(results); //[1,4,9,16,25,36]
定义了一个pow函数,函数的做用是让参数平方,map函数的参数是pow参数,map遍历了数组,把数组的每一项传进了pow函数里面,再return出来。
map也是一个典型的高阶函数。
高阶函数就只讲这两个方法了,我相信以各位的聪明才智已经理解了什么是高阶函数。
最后留一个小思考,闭包,是不是高阶函数呢?