萝三画室

深入理解JS-part4-回调函数

本文是深入理解JS系列的part4,我们要讨论的是一个特别容易让人混乱的概念:回调函数。在查文档时,我们经常会遇见callback这个东西,它通常以函数形式作为方法的参数,比如MDN上对array.map的描述。那我就在想,既然给这个函数起了一个这么洋气的名字,那么它一定是有别的特点的!于是我带着好奇想起一探究竟,看过很多文章之后反而更困惑了。大家各执一词,经常把callback和异步、闭包等串在一起,将原本很简单的概念搞的极其复杂。以上就是我想要写这篇文章的原因。首先给大家剧透一下,callback虽然名字洋气,但是它本身并不洋气。只要理解了深入理解系列前3部分的内容,callback这货也就只是个概念而已。

回调函数的定义

我觉得忘记之前看过的文章,只来看回调函数的定义就可以扫清所有困惑了。(笑哭
下面是维基对回调函数的定义:
https://en.wikipedia.org/wiki/Callback_(computer_programming)#JavaScript
里面还包含了一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function someAction(x, y, someCallback) {
return someCallback(x, y);
}
function calcProduct(x, y) {
return x*y;
}
function calcSum(x, y) {
return x + y;
}
// alerts 75, the product of 5 and 15
alert(someAction(5, 15, calcProduct));
// alerts 20, the sum of 5 and 15
alert(someAction(5, 15, calcSum));

在javascript中,函数也是对象,因此可以作为参数传递。在执行alert((someAction(5, 15, calcProduct))时,calcProduct函数就是回调函数。someAction就是一个接受两个变量、一个回调函数的函数。

因此我们来对回调函数下个定义:回调函数就是用做另一个函数参数的函数
私心再加一小句说明:跟同步和异步没有任何关系
其实可以更大白话一点:只要你把函数B当做参数传来传去,函数B就是回调

——本节完——(开玩笑的

接下来我们说,为啥很多人都将回调和闭包、异步混在一起讲。

使用回调函数就是在使用闭包

因为回调是将函数B传递到另外一个函数A中执行的,回忆我们在作用域和闭包中讲的,你会发现回调函数都会使用闭包。回调函数B运行时可以访问函数A的对象

回调函数可以用在同步代码中,也可以用在异步代码中

回调只是个作为参数的函数,它可以用在同步中可可以用在异步中。很多人将回调和异步混在一起讲而忽略了同步,可能是因为同步回调太平常了吧。

同步回调

同步回调其实是非常普遍的,我们在实际工作中也经常用到。比方说数组的map方法

1
2
3
4
var arr = [1,2,3];
var arrAdded = arr.map(function(item){
return item+1;
});//[2,3,4]

上栗中的匿名函数就是回调。它作为map方法的参数,并能访问到map中的变量。

异步回调

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function add(msg){
console.log(msg)
}
function init(cb){
var tb = "我是同步回调";
var yb = "我是异步回调";
setTimeout(function(){
console.log(yb)
},1000);//异步回调
cb(tb);//同步回调
}
init(add);

运行之后控制台输出:
//我是同步回调
//我是异步回调
并没有神奇的东西,就是这么简单。当然前提还是,理解异步闭包的工作机制,这样回调函数也就仅仅是一个名词了。

——本节完——(这次是真的