什么是JavaScript中的回调函数?JS回调实例教程

240 阅读5分钟

在JavaScript中,有一些高阶方法和函数接受一个函数作为参数。这些作为其他函数参数的函数被称为回调函数。

什么是JavaScript中的回调?

回调是一个作为另一个函数的参数传递的函数。

这意味着父函数通常是建立在使用任何种类的函数上。但另一方面,回调函数是为了在使用父函数的特定情况下(或有限的情况下)使用。

你如何在JavaScript中创建一个回调函数?

你创建一个回调函数,就像JavaScript中的其他函数一样:

function callbackFunction () {
    
}

回调函数和其他函数的区别在于它的使用方式。

一个回调函数是专门用来作为另一个函数的参数使用的:

function anyFunction(fun) {
    // ...
    fun(a, b, c);
    //...
}

anyFunction(callbackFunction);

因此,要创建一个callbackFunction ,你需要知道父函数如何使用回调函数,它传递哪些参数,以及传递参数的顺序。

回调函数的例子是什么?

我们现在要写我们自己的回调函数,因为这是你要做很多次的事情。那么,我们开始吧!

一个已经集成在JavaScript语言中的高阶函数是every 方法。

every 方法是一个数组方法,它使用一个回调来检查数组中的所有元素是否通过了某个测试。

看一下 every 方法的文档,你可以看到回调被传递三个参数:数组中的一个元素,该元素的索引,以及整个数组。

所以回调函数的签名会是这样的:

function callbackFunction(element, index, array) {
    // do something
}

回调函数可以是简单的,也可以是复杂的,就像你需要的那样。为了创造一个例子,我们需要一些背景。

如何在JavaScript中写一个回调函数

所以,假设你正在处理字符串的数组。你需要检查该数组是否只包含长度正好为三个字符的字符串,大写字母,包含所有不同的字母,并且它们在数组内不重复。

这是一个相当复杂的情况,但也许你最终会需要做这样的事情,或者同样复杂的事情,所以这都是很好的练习。

当你建立一个有这么多东西要检查的函数时,你可以一次解决一个条件。

第一个条件是元素是一个字符串,所以,让我们添加它:

function callbackFunction(element, index, array) {
    
    // check that element is a string
	const isNotString = typeof element !== "string";
    // if it's not, end function
    if (isNotString) {return;}
    
}

接下来,字符串必须全部是大写字母,只包含字母,并且是3个字符长。

你可以单独检查这三个条件,也可以用一个正则表达式来检查它们,这个表达式正好可以检查这三点。

这样的正则表达式看起来像这样:/^[A-Z]{3}$/

让我们看看这个正则表达式的组成部分是什么:

  • 开头的字符^ 和结尾的$ 是锚点。这些字符表明,字符串必须以这种方式开始和结束。如果你同时使用这两个字符,它们限制了一个字符串只能包含正则表达式中的模式。
  • [A-Z] 是一个字符类,可以匹配从 到 的任何字符,所以是所有大写字母。A Z
  • {3} 是一个计数器。这表示必须准确地连续三次匹配前面的东西。

上面解释的正则表达式相当于这个正则表达式:/^[A-Z][A-Z][A-Z]$/

在这种情况下,我们没有写计数器{3} ,而是写了三次的类[A-Z]

让我们把这个添加到代码中:

function callbackFunction(element, index, array) {
    
    // check that element is a string
    const isNotString = typeof element !== "string";
    // if it's not, end function
    if (isNotString) {
        return;
    }
    
    // check that string is 3 characters long and only uppercase letters
    const isItThreeUpperCaseLetters = /^[A-Z]{3}$/.test(element);
    // otherwise, end function
    if (!isItThreeUpperCaseLetters) {
        return;
    }
    
}

如果你不喜欢正则表达式,你可以阅读下面如何在不使用正则表达式的情况下进行同样的检查。

然后,接下来,我们需要检查这些字符是否都是不同的。

有三个字符你可以使用:element[0] !== element[1] && element[0] !== element[2] && element[1] !== element[2]

但是,你也可以用一个循环--实际上是一个双循环--来做这件事:

// with the outer loop, you get j, the first index to compare
for (let j = 0; j++; j < element.length) {
    // with the inner loop you get k, the second index to compare
    for (let k = j+1; k++; k < element.length) {
        // you compare the element at index j with the element at index k
        if (element[j] === element[k]) {
            // if they are equal return to stop the function
            return;
        }
    }
}

循环将适用于任何长度,而且你不需要为不同的情况重写它。

这和写三个比较的方法完全一样吗?让我们按照这个循环来检查一下。

在第一次迭代时,我们有那个j=0 ,和k=1 ,所以第一次比较是element[0] === element[1] 。然后k 增加,所以是j=0k=2 ,所以是element[0] === element[2]

在这一点上,内循环停止,外循环(有j )进入下一个迭代。这次是j=1 ,内循环从k=j+1 开始,所以在k=2 - 这里的比较是element[1] === element[2]

内循环已经完成循环,外循环从j=1j=2 ,内循环没有开始,因为k = j+1 = 3 没有通过循环的k < element.length 条件:

function callbackFunction(element, index, array) {
    
    
    // check that element is a string
    const isNotString = typeof element !== "string";
    // if it's not, end function
    if (isNotString) {
        return;
    }
    
    
    // check that string is 3 characters long and only uppercase letters
    const isItThreeUpperCaseLetters = /^[A-Z]{3}$/.test(element);
    // otherwise, end function
    if (!isItThreeUpperCaseLetters) {
        return;
    }
    
    
    // check if all characters are different
    const allDifferentCharacters = element[0] !== element[1] && element[0] !== element[2] && element[1] !== element[2];
    // if not, return to stop the function
    if (!allDifferentCharacters) {
        return;
    }
    
    
    
}

然后,我们需要检查的最后一件事是,这些字符串在数组内没有重复。

我们可以使用indexOf 来检查当前的是数组中第一次出现的element

我们需要引用数组来实现这一点。我们有了它--它是传递给回调的参数之一,即array 参数。

如果这是该字符串在数组中的第一次出现,indexOf 的输出将与index 相同。

如果array.indexOf(element) === indextrue ,这意味着element 在数组中第一次出现在index 。如果是false ,则相同的字符串在数组中较早出现。

让我们在函数中加入这个检查。而如果这个字符串通过了所有的检查,那么这个函数就可以在最后返回true

function callbackFunction(element, index, array) {
    
    
    // check that element is a string
    const isNotString = typeof element !== "string";
    // if it's not, end function
    if (isNotString) {
        return;
    }
    
    
    // check that string is 3 characters long and only uppercase letters
    const isItThreeUpperCaseLetters = /^[A-Z]{3}$/.test(element);
    // otherwise, end function
    if (!isItThreeUpperCaseLetters) {
        return;
    }
    
    
    // check if all characters are different
    const allDifferentCharacters = element[0] !== element[1] && element[0] !== element[2] && element[1] !== element[2];
    // if not, return to stop the function
    if (!allDifferentCharacters) {
        return;
    }
    
    
    // check if it's the first appearence of element inside the array
    const isItFirstAppearence = array.indexOf(element) === index;
    // if not, return to stop the function
    if (!isItFirstAppearence) {
        return;
    }
    
    
    return true;
}

如果我们没有使用正则表达式呢?

在上面的代码中,为了检查三种不同的东西,我们使用了一个正则表达式:/^[A-Z]{3}$/

但如果你不想使用正则表达式,你可以使用length 属性来检查一个字符串是否正好是某个长度。在本例中,element.length === 3 来检查字符串是否正好是三个字符的长度。

接下来,该字符串必须全部为大写字母,并且只包含字母。

你可以使用charCodeAt 来实现这一点。这个方法返回一个字符的ASCII码,知道大写字母的ASCII码从65到90,你可以检查是否只有大写字母。

有三个数字可以检查。element.charCodeAt(0),element.charCodeAt(1), 和element.charCodeAt(2) 。它们都需要在65和90之间。虽然只有三个字符,但我们仍然可以使用一个循环。

所以,这将是如下的:

for (let i = 0; i++; i < element.length) {
    // find the ASCII code of the character
    const code = element.charCodeAt(i);
    // check if it's outside of the range
    if (code < 65 || code > 90) {
        // if it is, return to stop the function
        return;
    }
}

让我们把这个添加到函数中:

function callbackFunction(element, index, array) {
    
    // check that element is a string
    const isNotString = typeof element !== "string";
    // if it's not, end function
    if (isNotString) {return;}
    
    // check that element has length string
    const hasLengthThree = element.length === 3;
    // if it has a different length, end function
    if (!hasLengthThree) {return;}
    
    // loop over the characters
	for (let i = 0; i++; i < element.length) {
        // find the ASCII code of the character
        const code = element.charCodeAt(i);
        // check if it's outside of the range
        if (code < 65 || code > 90) {
            // if it's outside the range, return and stop the function
            return;
        }
    } 
}

如果你是从上面的链接来到这里的,你可以返回那里继续阅读如何完成这个函数,否则,请继续到最后。

如何使用示例中的回调函数

我们已经写好了回调函数。那么你如何使用它呢?

anArray.every(callbackFunction);

你也可以在回调里面使用every 方法--也许是对filter 方法的回调。

随着一个程序变得越来越复杂,它可能会按比例使用更多的回调函数。

为什么我们要在JavaScript中使用回调函数?

回调函数是JavaScript的一个很好的功能。它意味着我们可以有一个一般的函数来做一些事情(比如every ,检查一个数组中的每个元素是否符合某个条件,filter ,删除不符合某个条件的元素,replace ,一个字符串方法接受一个回调来描述如何替换一个字符串的部分内容,等等),还有一个回调函数来为特定情况增加该行为的具体内容:

  • filter 在这种情况下,将删除回调指定的元素。
  • every 将检查该情况下的所有元素是否符合回调函数的指定。
  • replace 在该情况下,将替换回调函数所指定的字符串的部分内容。

高阶函数给代码增加了一个抽象的层次。我们不知道(也不需要知道),every ,如何检查数组的每一个元素,并验证它们是否都通过了回调指定的测试。我们只需要知道这个方法接受了一个回调函数就可以了。

结论

回调是作为其他函数的参数传递的函数。你已经看到了一个如何创建回调函数的例子,以及关于为什么回调函数有用的一些考虑。

谢谢你的阅读!