什么是回调函数?
简而言之:作为参数被传递给另一个函数的函数叫作回调函数。
为什么需要回调函数?
JavaScript 按从上到下的顺序运行代码。但是,在有些情况下,必须在某些情况发生之后,代码才能运行(或者说必须运行),这就不是按顺序运行了。这是异步编程。
回调函数确保:函数在某个任务完成之前不运行,在任务完成之后立即运行。它帮助我们编写异步 JavaScript 代码,避免问题和错误。
在 JavaScript 里创建回调函数的方法是将它作为参数传递给另一个函数,然后当某个任务完成之后,立即调用它。
补充:同步和异步(解释来源于MDN)
下面是一个同步编程的案例:
const name = "Miriam";
const greeting = `Hello, my name is ${name}!`;
console.log(greeting);
// "Hello, my name is Miriam!"
实际上浏览器是按照我们书写代码的顺序一行一行地执行程序的。浏览器会等待代码的解析和工作,在上一行完成后才会执行下一行。这样做是很有必要的,因为每一行新的代码都是建立在前面代码的基础之上的。
这也使得它成为一个同步程序。
事实上,调用函数的时候也是同步的,就像这样:
function makeGreeting(name) {
return `Hello, my name is ${name}!`;
}
const name = "Miriam";
const greeting = makeGreeting(name);
console.log(greeting);
// "Hello, my name is Miriam!"
在这里 makeGreeting() 就是一个同步函数,因为在函数返回之前,调用者必须等待函数完成其工作。
但是如果函数的逻辑非常耗时呢?会发生什么?下面是一个小例子,代码就不贴了,主要为了说明道理。
你会发现,当我们的 生成素数函数运行时,用户不能在文本框输入任何东西,也不能点击任何东西,或做任何其他事情。
这就是耗时的同步函数的基本问题。因此我们希望找到一种方法,使得:
- 通过调用一个函数来启动一个长期运行的操作
- 让函数开始操作并立即返回,这样我们的程序就可以保持对其他事件做出反应的能力
- 当操作最终完成时,通知我们操作的结果(因为我们不知道该操作具体什么时候会执行完成)。
这就是异步函数为我们提供的能力
定时器、ajax请求、时间绑定等一般都是异步代码。
如何创建回调函数?
const message = function() {
console.log("This message is shown after 3 seconds");
}
setTimeout(message, 3000);
上面函数的作用是在控制台打印一条消息(message),它在 3 秒之后显示。
message 函数是在发生某事之后(在本示例中为 3 秒之后),而不是在此之前被调用。因此,message 函数就是一个回调函数
匿名函数也可以作为回调函数
setTimeout(function() {
console.log("This message is shown after 3 seconds");
}, 3000);
// 这里的回调函数没有名称
也可以用箭头函数写回调函数
setTimeout(() => {
console.log("This message is shown after 3 seconds");
}, 3000);
箭头函数和匿名函数的区别
语法上的区别没什么好说的,主要区别在于:
this 关键字的行为:
箭头函数: 没有自己的 this,它会捕获所在上下文的 this 值。在箭头函数内部使用的 this 始终指向函数定义时的 this。
function Counter() {
this.count = 0;
setInterval(() => {
// 'this' 在这里指向 Counter 对象
this.count++;
console.log(this.count);
}, 1000);
}
匿名函数: 有自己的 this,它的 this 指向函数被调用时的上下文。
function Counter() {
this.count = 0;
setInterval(function() {
// 'this' 在这里可能不指向 Counter 对象,取决于调用方式
this.count++;
console.log(this.count);
}, 1000);
}
arguments 对象:
箭头函数: 没有 arguments 对象,可以使用剩余参数(...args)来获取所有参数。
匿名函数: 有 arguments 对象,可以直接使用。