在JavaScript中我们常听说回调函数,回调函数是一段可执行的代码段,它作为一个参数传递给其他的代码,其作用是在需要的时候方便调用这段(回调函数)代码。通俗来说,回调函数就是就是一个可执行的函数,在需要的地方调用它。
我们知道,对象可以作为参数传递给函数,而函数实际上也是对象,那么函数也可以作为参数传递给函数,在实际使用中,我们可以很容易找到回调函数的例子,例如jQuery中

1
2
3
$('div').click(function(){
console.log('hello')
})

其实click中的function就是个匿名的回调函数,当然,回调函数也可以是具名的

1
2
3
4
$('div').click(callback)
function callback(){
console.log('hello')
}

这个回调函数不会立即执行,在传参的过程中,此时传入了回调函数的指针,它并不会立即执行,而是需要等到被调用的时候才会执行。

关于回调函数的this

在回调函数调用时this的执行上下文并不是回调函数定义时的那个上下文,而是调用它的函数所在的上下文。
例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var obj = {
sum: 0,
add: function(num1, num2){
this.sum = num1 + num2;
}
};
function add(num1, num2, callback){
callback(num1, num2);
};
add(1,2, obj.add);
console.log(obj.sum); //=>0
console.log(window.sum); //=>3

上述代码调用回调函数的时候是在全局环境下,因此this指向的是window,所以sum的值是赋值给windows的。

允许多重回调函数

一个典型的例子就是jQuery中ajax的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function successCallback(){
//在发送之前做点什么
}
function successCallback(){
//在信息被成功接收之后做点什么
}
function completeCallback(){
//在完成之后做点什么
}
function errorCallback(){
//当错误发生时做点什么
}
$.ajax({
url:"http://xxx.com/",
success:successCallback,
complete:completeCallback,
error:errorCallback
})

通过回调函数获取数据

在进行跨域的时候,我们需要获取对应接口的数据并对数据进行相应的处理,那么就可以通过接口所提供的回调函数通过传参来获取数据。 例如JSONP跨域时,提供的接口为http://platform.sina.com.cn/slide/album_tech?jsoncallback=func&app_key=1271687855&num=3&page=4

1
2
3
4
5
6
<script>
function func(data){
console.log(data) // 获取数据
}
</script>
<script src="http://platform.sina.com.cn/slide/album_tech?jsoncallback=func&app_key=1271687855&num=3&page=4"></script>

一个回调函数中可以嵌入另一个回调函数,对于这种情况出现多层嵌套时,代码会难以阅读和维护,这个时候可以采用命名回调函数的方式调用,或者采用模块化管理函数。

异步编程

我们知道,JavaScript的执行模式分为两种,异步(Asynchronous)和同步(Synchronous),同步模式指的是代码按照顺序自上而下执行,生活中排队就是同步的一个例子,异步模式指的是代码顺序与执行顺序不保证一致,就好比正好好排队的时候,突然有一个人插队了(不提倡不提倡),这就是异步,但是在浏览器端,异步模式是很重要的,耗时很长的操作都应该异步执行,避免浏览器失去响应,最好的例子就是Ajax操作。在服务器端,”异步模式”甚至是唯一的模式,因为执行环境是单线程的,如果允许同步执行所有http请求,服务器性能会急剧下降,很快就会失去响应。而回调函数就是实现异步编程的一个方式。

定时器就是异步实现的一个例子。

1
2
3
4
var a = 1
setTimeout(function () {
  console.log(a)  // a打印出来是多少呢? 
}, 1000)

我们看到1s之后传入一个匿名的回调函数,并打印出a的值,你也许会说,a是1呀!可是你怎么知道后续的a会不会被修改呢?假如a的值在1000行的代码处被修改为233或者其他的值,此时a的值就不确定了,不管是setTimeout,还是setInterval,当我们没有完整的看完代码——也就是代码在之后执行的时候没有确定a的值的时候,a在1s后的值在目前的代码片段是无法确定的,异步编程的特点在于当前可执行的代码不阻塞后面代码的执行,而回调函数是实现异步编程的基本方法。

除了定时器,还有事件监听、http请求、promise对象中使用回调函数都可以实现异步编程。

参考

理解与使用Javascript中的回调函数
Javascript异步编程的4种方法