在本章开篇就有提到过,函数在 JavaScript 中是一等公民。这里所谓的一等公民,就是指函数可以像其他数据类型一样作为函数的参数传入,也可以通过返回值的形式来返回。
这里要介绍的回调(callback)就是利用了这一特性,我们将传递给另一个函数作为实参的函数称之为回调函数(callback)。
5-4-1 回调函数基本介绍
所谓回调函数,通俗的来讲,就是指将一个函数作为参数传递给另外一个函数,然后在另外一个函数里面执行传递过去的函数,我们来看一个具体的示例,如下:
let test = function(fn){
fn();
}
let test2 = function(){
console.log("Hello World");
}
test(test2); // Hello World
这里,我们的 test2 就被称之为回调函数。因为 test2 是作为一个参数传递到了 test 函数里面,然后在 test 里面进行的 test2 函数调用。
回调函数可以和其他参数一起传入到一个参数里面,如下:
let test = function(name,fn){
console.log(`My name is ${name}`);
fn();
}
let test2 = function(){
console.log("I'm coding");
}
test("Mr.Yan",test2);
// My name is Mr.Yan
// I'm coding
5-4-2 内置回调函数介绍
在之前的学习中,我们就已经接触过一个内置回调函数了。那就是sort()
。
使用sort()
为数组进行排序的时候,默认是使用的 ASCII 码来进行的排序。如果想要按照数值来进行排序,就需要我们传递一个回调函数进去。这里我们一起来复习一下:
let arr = [0,12,3,7,-12,23];
console.log(arr.sort(function(a,b){
return a - b;
// 降序就返回 b - a
}));
甚至我们还可以使用前面小节所介绍过的箭头函数,将上面的排序写作如下:
let arr = [0,12,3,7,-12,23];
console.log(arr.sort((a,b) => a - b));
在 JavaScript 里面,除了sort()
以外,还有诸如forEach()
,map()
,every()
,some()
等函数,也是很常见的内置回调函数。
迭代方法
every()
是对数组的每一项运行给定的函数,如果该函数每一项都返回 true,则返回 true。
let arr = [1,2,3,4,5,6,7,8,9,10];
// 将数组的每一项传入到回调函数,如果每一项返回 true,那么最终返回 true
let i = arr.every(function(item){
if(item % 2 == 0){
return true;
}else{
return false;
}
});
console.log(i); // false
与every()
比较相似的是some()
,该方法可以对数组的每一项运行指定的函数,如果该函数只要有一项返回 true 则返回 true。
let arr = [1,2,3,4,5,6,7,8,9,10];
// 将数组的每一项传入到回调函数,如果有一项返回 true,那么最终返回 true
let i = arr.some(function(item){
if(item % 2 == 0){
return true;
}else{
return false;
}
});
console.log(i); // true
filter()
中的 filter 是过滤的意思,这个方法会返回一个数组,数组里面返回过滤过的元素。
let arr = [1,2,3,4,5,6,7,8,9,10];
// 将数组的每一项传入到回调函数,然后将返回为 true 的项目组成一个数组
let i = arr.filter(function(item){
if(item % 2 == 0){
return true;
}else{
return false;
}
});
console.log(i); // [ 2, 4, 6, 8, 10 ]
forEach()
方法在前面介绍数组遍历的时候,就已经见到过了。该方法就是简单的将数组每一项传入到函数,然后执行该函数里面的代码。需要注意一下的是,该回调函数没有返回值。
let arr = [1,2,3,4,5,6,7,8,9,10];
// 将数组的每一项传入到回调函数,然后执行回调函数里面的操作
let i = arr.forEach(function(item){
console.log(item);
});
console.log(i); // undefined
map()
方法是对数组的每一项运行回调函数。最终返回一个数组,这个数组是每次调用函数后的运行结果。
let arr = [1,2,3,4,5,6,7,8,9,10];
// 将数组的每一项传入到回调函数,然后将返回的结果组成一个新的数组返回
let i = arr.map(function(item){
if(item % 2 == 0){
return true;
}else{
return false;
}
});
console.log(i);
// [ false, true, false, true, false, true, false, true, false, true ]
注:以上方法都不会改变原数组的值。
并且这些方法都可以接收两个参数,第一个是数组的元素值,第二个是数组的索引。上面的例子中我们都只接收了一个参数,即数组的值。
归并方法(扩展)
归并方法有两个,reduce()
和reduceRight()
,一个是从数组第一项开始,另外一个是从数组最后一项开始,两个方法都会迭代数组所有的项,然后构建一个最终的返回值。
这两个方法都接受两个参数:一个在每一项回调用的函数和一个可选的初始值。
关于作为回调被传入的函数,里面又可以接收 4 个参数,分别是前一项值,当前值,数组索引和数组对象。
注:这里的前一项值指的是上一次迭代时的计算结果。
这里我们可以将回调函数的参数打印出来看一下,如下:
没有初始值的情况:
let arr = [1,2,3,4,5];
let i = arr.reduce(function(pre,cur,index,arr){
console.log(pre,cur,index,arr);
return pre + cur
});
console.log(i);
// 1 2 1 [ 1, 2, 3, 4, 5 ]
// 3 3 2 [ 1, 2, 3, 4, 5 ]
// 6 4 3 [ 1, 2, 3, 4, 5 ]
// 10 5 4 [ 1, 2, 3, 4, 5 ]
// 15
有初始值的情况:
let arr = [1,2,3,4,5];
let i = arr.reduce(function(pre,cur,index,arr){
console.log(pre,cur,index,arr);
return pre + cur
},100);
console.log(i);
// 100 1 0 [ 1, 2, 3, 4, 5 ]
// 101 2 1 [ 1, 2, 3, 4, 5 ]
// 103 3 2 [ 1, 2, 3, 4, 5 ]
// 106 4 3 [ 1, 2, 3, 4, 5 ]
// 110 5 4 [ 1, 2, 3, 4, 5 ]
// 115
其实,reduce()
方法就和前面的forEach()
,map()
等方法很相似,将数组的每一项应用到函数里面。只不过会将每次的计算结果放入下一次迭代时进行使用。
下面的例子演示了使用reduce()
方法来实现数值的累加:
let arr = [1,2,3,4,5,6,7,8,9,10];
let i = arr.reduce(function(pre,cur){
return pre + cur;
});
console.log(i); // 55
reduce()
是从左往右进行归并,reduceRight()
是从右往左开始归并,这里就不再做演示了。
5-4-3 链式调用
本节的最后介绍一下链式调用。所谓链式调用,就是可以像链条一样一直调用方法。其实链式调用的原理也非常简单,在调用方法时,方法里面会返回一个对象,然后这个对象又可以调用方法,这样我们就可以实现链式调用。
示例:求数组的偶数和
let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20];
let sum = arr.filter((item) => item % 2 === 0).reduce((a, b) => a + b);
console.log(sum); // 110
// 因为 filter() 返回的是一个数组,所以我们可以直接再次调用 reduce() 方法
Comments