今天分享一下我遇到的一个面试题,是关于JavaScript回调函数的问题,什么是JavaScript回调函数?
🧇关于回调函数
- 回调函数就是一个被作为参数传递的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用为调用它所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
- 回调函数的使用可以大大提升编程的效率,这使得它在现代编程中被非常多地使用。同时,有一些需求必须要使用回调函数来实现。
- 项目开发中,若想要函数体中某部分功能由调用者决定,此时可以使用回调函数。所谓回调函数指的就是一个函数A作为参数传递给一个函数B,然后在B的函数体内调用函数A。此时,我们称函数A为回调函数。如果没有名称(函数表达式),就叫做匿名回调函数,匿名函数常用作函数的参数传递,实现回调函数。
代码演示
//part1functioncal(num1, num2, fn) { returnfn(num1, num2); } //part2console.log(cal(45, 55, function (a, b) { returna+b; })); //part3console.log(cal(10, 20, function (a, b) { returna*b; }));
- 上述part1代码定义的cal()函数,用于返回fn回调函数的调用结果。 part2代码用于调用cal()函数,并指定该回调函数用于返回其两个参数相加的结果,因此可在控制台查看到结果为100。同理, part3代码在调用cal()函数时,将回调函数指定为返回其两个参数相乘的结果,因此可在控制台查看到结果为200。
- 从以上案例可以看出,在函数(如cal()函数)中设置了回调函数后,可以根据调用时传递不同的参数(如相加的函数,相乘的函数等),在函数体中特定的位置实现不同的功能,相当于在函数体内根据用户的需求完成了不同功能的定制。
🧇两种回调方式
- 回调可用于数组、计时器函数、promise、事件处理中。
- 回调的调用方式有两种:同步(阻塞)和异步(非阻塞)回调。
🚩同步回调
- 同步回调是“阻塞”的:高阶函数直到回调函数完成后才继续执行。
- 许多原生 JavaScript 类型的方法都使用同步回调。
- 最常用的是 array 的方法,例如:array.map(callback),array.forEach(callback),array.find(callback), array.filter(callback), array.reduce(callback, init)
constpersons= ['Ana', 'Elena']; persons.forEach( functioncallback(name) { console.log(name); } ); //输出'Ana' //输出'Elena'constnameStartingA=persons.find( functioncallback(name) { returnname[0].toLowerCase() ==='a'; } ); console.log(nameStartingA); // => 'Ana'constcountStartingA=persons.reduce( functioncallback(count, name) { conststartsA=name[0].toLowerCase() ==='a'; returnstartsA?count+1 : count; },0); console.log(countStartingA); // => 1
🚩异步回调
异步回调是“非阻塞的”:高阶函数无需等待回调完成即可完成其执行。高阶函数可确保稍后在特定事件上执行回调。
⏬什么时候用异步编程
- 在前端编程中(甚至后端有时也是这样),我们在处理一些简短、快速的操作时,例如计算 1 + 1 的结果,往往在主线程中就可以完成。主线程作为一个线程,不能够同时接受多方面的请求。所以,当一个事件没有结束时,界面将无法处理其他请求。
- 现在有一个按钮,如果我们设置它的 onclick 事件为一个死循环,那么当这个按钮按下,整个网页将失去响应。
为了避免这种情况的发生,我们常常用子线程来完成一些可能消耗时间足够长以至于被用户察觉的事情,比如读取一个大文件或者发出一个网络请求。因为子线程独立于主线程,所以即使出现阻塞也不会影响主线程的运行。但是子线程有一个局限:一旦发射了以后就会与主线程失去同步,我们无法确定它的结束,如果结束之后需要处理一些事情,比如处理来自服务器的信息,我们是无法将它合并到主线程中去的。
为了解决这个问题,JavaScript 中的异步操作函数往往通过回调函数来实现异步任务的结果处理。
⏬使用回调的异步编程
在最基本的层面上,JavaScript异步编程是使用回调实现的。回调就是函数,可以传给其他函数。而其他函数会在满足某个条件或发生某个(异步)事件时调用(“回调”)这个函数。回调函数被调用,相当于通知你满足了某个条件或发生了某个事件。有时这个调用还会包含函数参数,能提供更多细节。
⏬异步回调的例子
计时器函数异步调用回调:
setTimeout(functionlater() { console.log('123'); }, 2000); // 两秒后输出'123' setInterval(functionrepeat() { console.log('123'); }, 2000); // 每两秒输出'123'
DOM 事件侦听器还异步调用事件处理函数(回调函数的子类型):
constmyButton=document.getElementById('myButton'); myButton.addEventListener('click', functionhandler() { console.log('Button clicked!'); }); // 点击按钮后输出'Button clicked!'
参考来自