03月08, 2019

JS基础(5)——函数(4)——回调函数

在本章开篇就有提到过,函数在 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() 方法

本文链接:http://www.yanhongzhi.com/post/js-basis-15.html

-- EOF --

Comments