回到基础:什么是JavaScript中的回调 (1)

47 阅读5分钟

什么是JavaScript回调?

在JavaScript中,回调是一个作为参数传递给第二个函数的函数。接收回调函数的函数决定是否以及何时执行回调:

function myFunction(callback) {
  // 1。做某事
  // 2。然后执行回调
  callback()
}

function myCallback() {
  
}

myFunction(myCallback);

在上面的例子中,我们有两个函数:myFunction和myCallback。顾名思义,myCallback被用作回调函数,我们将它作为参数传递给myFunction。然后,当myFunction准备好执行回调时,它可以执行回调。

许多博客文章会说回调被称为回调是因为你告诉某个函数在它准备好了一个答案时回调你。一个不那么容易混淆的名字是“callafter”:也就是说,在完成其他所有事情之后调用这个函数。

为什么我们需要回调函数?

您经常听到人们说JavaScript是单线程的。这意味着它一次只能做一件事。当执行缓慢的操作时——例如从远程API获取数据——这可能会产生问题。如果你的程序在数据返回之前都冻结了,这将不会是一个很好的用户体验。

JavaScript避免这个瓶颈的方法之一是使用回调。我们可以将第二个函数作为参数传递给负责获取数据的函数。然后启动数据获取请求,但JavaScript解释器不会等待响应,而是继续执行程序的其余部分。当从API接收到一个响应时,回调函数被执行,并可以对结果做一些事情:

function fetchData(url, cb) {
  // 1。对url发出API请求
  // 2。如果响应成功,执行回调
  cb(res);
}

function callback(res) {
  //做一些有结果的事情//做某事
fetchData(“https://sitepoint.com”,callback);
//执行其他操作

JavaScript是一种事件驱动语言

您还会听到人们说JavaScript是一种事件驱动语言。这意味着它可以监听和响应事件,同时继续执行进一步的代码,而不会阻塞它的单个线程。

它是怎么做到的呢?你猜对了:回调。

想象一下,如果您的程序将一个事件监听器附加到一个按钮,然后坐在那里等待有人点击该按钮,而拒绝做任何其他事情。那可不好!

通过使用回调函数,我们可以指定在响应特定事件时应该运行特定的代码块:

function handleClick() {
  //做一些事情(例如验证一个表单)
  //响应用户单击按钮document.querySelector('button').addEventListener('click', handleClick);

在上面的例子中,handleClick函数是一个回调函数,它是在响应web页面上发生的操作(单击按钮)时执行的。

使用这种方法,我们可以对尽可能多的事件做出反应,同时让JavaScript解释器自由地处理它需要做的任何其他事情。

一阶和高阶函数

在学习回调的时候,您可能会遇到另外两个流行词汇是“一级函数”和“高阶函数”。这些听起来很可怕,但实际上并不可怕。

当我们说JavaScript支持第一类函数时,这意味着我们可以将函数视为常规值。我们可以将它们存储在一个变量中,我们可以从另一个函数中返回它们,正如我们已经看到的,我们可以将它们作为参数传递。

至于高阶函数,它们只是一些简单的函数,要么接受函数作为参数,要么返回函数作为结果。有几个原生JavaScript函数也是高阶函数,比如setTimeout。让我们用它来演示如何创建和运行回调。

如何创建回调函数

模式与上面相同:创建一个回调函数,并将其作为参数传递给高阶函数:

function greet(){
  console.log(“Hello, World !”);
}

setTimeout(greet, 1000);

setTimeout函数以1秒的延迟执行greet函数,并记录“Hello, World!”对着控制台说。

不同类型的回调函数

部分由于JavaScript对一级函数的支持,在JavaScript中有多种声明函数的方法,因此在回调中也有多种使用函数的方法。

现在让我们看看这些,并考虑它们的优缺点。

匿名函数

到目前为止,我们一直在给函数命名。这通常被认为是良好的实践,但绝不是强制性的。考虑下面的例子,它使用回调函数来验证一些表单输入:

document.querySelector('form').addEventListener('submit', function(e)  {
  e.preventDefault();
  //执行一些数据验证
  //如果一切正常,那么……
  this.submit();
});

如您所见,回调函数是未命名的。没有名称的函数定义称为匿名函数。匿名函数在只在一个地方调用函数的简短脚本中非常适用。而且,当它们被声明为内联时,它们也可以访问它们的父作用域。

箭头功能

在ES6中引入了箭头函数。由于其简洁的语法,并且由于它们有一个隐式的返回值,它们经常被用于执行简单的一行程序,例如下面的例子,它从数组中过滤重复的值:

const arr = [1, 2, 2, 3, 4, 5, 5];
const unique = arr.filter((el, i) => arr.indexOf(el) === i);
// [1, 2, 3, 4, 5]

但是要注意,它们并不绑定自己的this值,而是从它们的父作用域继承它。这意味着,在前面的例子中,我们不能使用箭头函数来提交表单:

document.querySelector('form').addEventListener('submit', (e) => {
  ...
  //未捕获的TypeError:此。Submit不是一个函数
  // this 指向窗口对象,而不是窗体
  this.submit();
});

箭头函数是近年来我最喜欢添加到JavaScript中的函数之一,开发人员绝对应该熟悉它们。