广告
返回顶部
首页 > 资讯 > 前端开发 > JavaScript >理解与使用JavaScript中的回调函数
  • 690
分享到

理解与使用JavaScript中的回调函数

2024-04-02 19:04:59 690人浏览 独家记忆
摘要

目录概述什么是回调或者高阶函数回调函数是怎样运作的?回调函数是闭包实现回调函数的基本原理使用命名或匿名函数作为回调传递参数给回调函数在执行之前确保回调函数是一个函数使用this对象的

概述

javascript中,函数是第一类对象,这意味着函数可以像对象一样按照第一类管理被使用。既然函数实际上是对象:它们能被“存储”在变量中,能作为函数参数被传递,能在函数中被创建,能从函数中返回。

因为函数是第一类对象,我们可以在JavaScript使用回调函数。在下面的文章中,我们将学到关于回调函数的方方面面。回调函数可能是在JavaScript中使用最多的函数式编程技巧,虽然在字面上看起来它们一直一小段JavaScript或者Jquery代码,但是对于许多开发者来说它任然是一个谜。在阅读本文之后你能了解怎样使用回调函数。

回调函数是从一个叫函数式编程的编程范式中衍生出来的概念。简单来说,函数式编程就是使用函数作为变量。函数式编程过去 - 甚至是现在,依旧没有被广泛使用 - 它过去常被看做是那些受过特许训练的,大师级别的程序员的秘传技巧。

幸运的是,函数是编程的技巧现在已经被充分阐明因此像我和你这样的普通人也能去轻松使用它。函数式编程中的一个主要技巧就是回调函数。在后面内容中你会发现实现回调函数其实就和普通函数传参一样简单。这个技巧是如此的简单以致于我常常感到很奇怪为什么它经常被包含在讲述JavaScript高级技巧的章节中。

什么是回调或者高阶函数

一个回调函数,也被称为高阶函数,是一个被作为参数传递给另一个函数(在这里我们把另一个函数叫做otherFunction)的函数,回调函数在otherFunction中被调用。一个回调函数本质上是一种编程模式(为一个常见问题创建的解决方案),因此,使用回调函数也叫做回调模式。

下面是一个在jQuery中使用回调函数简单普遍的例子:


//注意到click方法中是一个函数而不是一个变量
//它就是回调函数
$("#btn_1").click(function() {
    alert("Btn 1 Clicked");
});

正如你在前面的例子中看到的,我们将一个函数作为参数传递给了click方法。click方法会调用(或者执行)我们传递给它的函数。这是JavaScript中回调函数的典型用法,它在jQuery中广泛被使用。

下面是另一个JavaScript中典型的回调函数的例子:


var friends = ["Mike", "Stacy", "Andy", "Rick"];

friends.forEach(function (eachName, index){
    console.log(index + 1 + ". " + eachName); // 1. Mike, 2. Stacy, 3. Andy, 4. Rick
});

再一次,注意到我们讲一个匿名函数(没有名字的函数)作为参数传递给了forEach方法。

到目前为止,我们将匿名函数作为参数传递给了另一个函数或方法。在我们看更多的实际例子和编写我们自己的回调函数之前,先来理解回调函数是怎样运作的。

回调函数是怎样运作的?

因为函数在JavaScript中是第一类对象,我们像对待对象一样对待函数,因此我们能像传递变量一样传递函数,在函数中返回函数,在其他函数中使用函数。当我们将一个回调函数作为参数传递给另一个函数是,我们仅仅传递了函数定义。我们并没有在参数中执行函数。我们并不传递像我们平时执行函数一样带有一对执行小括号()的函数。

需要注意的很重要的一点是回调函数并不会马上被执行。它会在包含它的函数内的某个特定时间点被“回调”(就像它的名字一样)。因此,即使第一个jQuery的例子如下所示:


//匿名函数不会再参数中被执行
//这是一个回调函数
$("#btn_1").click(function(){
    alert("Btn 1 Clicked");
});

这个匿名函数稍后会在函数体内被调用。即使有名字,它依然在包含它的函数内通过arguments对象获取。

回调函数是闭包

都能够将一个回调函数作为变量传递给另一个函数时,这个回调函数在包含它的函数内的某一点执行,就好像这个回调函数是在包含它的函数中定义的一样。这意味着回调函数本质上是一个闭包。

正如我们所知,闭包能够进入包含它的函数的作用域,因此回调函数能获取包含它的函数中的变量,以及全局作用域中的变量。

实现回调函数的基本原理

回调函数并不复杂,但是在我们开始创建并使用回调函数之前,我们应该熟悉几个实现回调函数的基本原理。

使用命名或匿名函数作为回调

在前面的jQuery例子以及forEach的例子中,我们使用了在参数位置定义的匿名函数作为回调函数。这是在回调函数使用中的一种普遍的魔术。另一种常见的模式是定义一个命名函数并将函数名作为变量传递给函数。比如下面的例子:


//全局变量
var allUserData = [];

//普通的logStuff函数,将内容打印到控制台
function logStuff (userData){
    if ( typeof userData === "string"){
        console.log(userData);
    } else if ( typeof userData === "object"){
        for(var item in userData){
            console.log(item + ": " + userData[item]);
        }
    }
}

//一个接收两个参数的函数,后面一个是回调函数
function getInput (options, callback){
    allUserData.push(options);
    callback(options);
}

//当我们调用getInput函数时,我们将logStuff作为一个参数传递给它
//因此logStuff将会在getInput函数内被回调(或者执行)
getInput({name:"Rich",speciality:"Javascript"}, logStuff);
//name:Rich
//speciality:Javascript

传递参数给回调函数

既然回调函数在执行时仅仅是一个普通函数,我们就能给它传递参数。我们能够传递任何包含它的函数的属性(或者全局属性)作为回调函数的参数。在前面的例子中,我们将options作为一个参数传递给了回调函数。现在我们传递一个全局变量和一个本地变量:


//全局变量
var generalLastName = "Cliton";

function getInput (options, callback){
    allUserData.push (options);
    //将全局变量generalLastName传递给回调函数
    callback(generalLastName,options);
}

在执行之前确保回调函数是一个函数

在调用之前检查作为参数被传递的回调函数确实是一个函数,这样的做法是明智的。同时,这也是一个实现条件回调函数的最佳时间。

我们来重构上面例子中的getInput函数来确保检查是恰当的。


function getInput(options, callback){
    allUserData.push(options);
    
    //确保callback是一个函数
    if(typeof callback === "function"){
        //调用它,既然我们已经确定了它是可调用的
        callback(options);
    }
}

如果没有适当的检查,如果getInput的参数中没有一个回调函数或者传递的回调函数事实上并不是一个函数,我们的代码将会导致运行错误。

使用this对象的方法作为回调函数时的问题

当回调函数是一个this对象的方法时,我们必须改变执行回调函数的方法来保证this对象的上下文。否则如果回调函数被传递给一个全局函数,this对象要么指向全局window对象(在浏览器中)。要么指向包含方法的对象。

我们在下面的代码中说明:


//定义一个拥有一些属性和一个方法的对象 //我们接着将会把方法作为回调函数传递给另一个函数

var clientData = {
    id: 094545,
    fullName "Not Set",
    //setUsrName是一个在clientData对象中的方法
    setUserName: fucntion (firstName, lastName){
        //这指向了对象中的fullName属性
        this.fullName = firstName + " " + lastName;
    }
} 

function getUserInput(firstName, lastName, callback){
    //在这做些什么来确认firstName/lastName
    //现在存储names
    callback(firstName, lastName);
}

在下面你的代码例子中,当clientData.setUsername被执行时,this.fullName并没有设置clientData对象中的fullName属性。相反,它将设置window对象中的fullName属性,因为getUserInput是一个全局函数。这是因为全局函数中的this对象指向window对象。


getUserInput("Barack","Obama",clientData.setUserName);
console.log(clientData,fullName);  //Not Set
//fullName属性将在window对象中被初始化     
console.log(window.fullName);  //Barack Obama

使用Call和Apply函数来保存this

我们可以使用Call或者Apply函数来修复上面你的问题。到目前为止,我们知道了每个JavaScript中的函数都有两个方法:Call和Apply。这些方法被用来设置函数内部的this对象以及给此函数传递变量。

call接收的第一个参数为被用来在函数内部当做this的对象,传递给函数的参数被挨个传递(当然使用逗号分开)。Apply函数的第一个参数也是在函数内部作为this的对象,然而最后一个参数确是传递给函数的值的数组

听起来很复杂,那么我们来看看使用Apply和Call有多么的简单。为了修复前面例子的问题,我将在下面你的例子中使用Apply函数:


//注意到我们增加了新的参数作为回调对象,叫做“callbackObj”
function getUserInput(firstName, lastName, callback. callbackObj){
    //在这里做些什么来确认名字
    callback.apply(callbackObj, [firstName, lastName]);
}

使用Apply函数正确设置了this对象,我们现在正确的执行了callback并在clientData对象中正确设置了fullName属性:


//我们将clientData.setUserName方法和clientData对象作为参数,clientData对象会被Apply方法使用来设置this对象     
getUserName("Barack", "Obama", clientData.setUserName, clientData);

//clientData中的fullName属性被正确的设置
console.log(clientUser.fullName); //Barack Obama

我们也可以使用Call函数,但是在这个例子中我们使用Apply函数。

允许多重回调函数

我们可以将不止一个的回调函数作为参数传递给一个函数,就像我们能够传递不止一个变量一样。这里有一个关于jQuery中ajax的例子:


function successCallback(){
    //在发送之前做点什么
}
  
function successCallback(){
//在信息被成功接收之后做点什么
}

function completeCallback(){
//在完成之后做点什么
}

function errorCallback(){
    //当错误发生时做点什么
}

$.ajax({
    url:"/file/imgs/upload/202211/12/omy5mroowq1.jpg",
    success:successCallback,
    complete:completeCallback,
    error:errorCallback
});

“回调地狱”问题以及解决方案

在执行异步代码时,无论以什么顺序简单的执行代码,经常情况会变成许多层级的回调函数堆积以致代码变成下面的情形。这些杂乱无章的代码叫做回调地狱因为回调太多而使看懂代码变得非常困难。我从node-mongoDB-native,一个适用于node.js的MonGoDB驱动中拿来了一个例子。这段位于下方的代码将会充分说明回调地狱:


var p_client = new Db('integration_tests_20', new Server("127.0.0.1", 27017, {}), 
                 {'pk':CustomPKFactory});
p_client.open(function(err, p_client) {
    p_client.dropDatabase(function(err, done) {
        p_client.createCollection('test_custom_key', function(err, collection) {
            collection.insert({'a':1}, function(err, docs) {
                collection.find({'_id':new ObjectID("aaaaaaaaaaaa")}, 
                function(err, cursor) {
                    cursor.toArray(function(err, items) {
                        test.assertEquals(1, items.length);
                        // Let's close the db
                        p_client.close();
                    });
                });
            });
        });
    });
});

你应该不想在你的代码中遇到这样的问题,当你当你遇到了

  • 你将会时不时的遇到这种情况
  • 这里有关于这个问题的两种解决方案。
  • 给你的函数命名并传递它们的名字作为回调函数,而不是主函数的参数中定义匿名函数。
  • 模块化L将你的代码分隔到模块中,这样你就可以到处一块代码来完成特定的工作。然后你可以在你的巨型应用中导入模块。

创建你自己的回调函数

既然你已经完全理解了关于JavaScript中回调函数的一切(我认为你已经理解了,如果没有那么快速的重读以便),你看到了使用回调函数是如此的简单而强大,你应该查看你的代码看看有没有能使用回调函数的地方。回调函数将在以下几个方面帮助你:

  • 避免重复代码(DRY-不要重复你自己)
  • 在你拥有更多多功能函数的地方实现更好的抽象(依然能保持所有功能)
  • 让代码具有更好的可维护性
  • 使代码更容易阅读
  • 编写更多特定功能的函数

创建你的回调函数非常简单。在下面的例子中,我将创建一个函数完成以下工作:读取用户信息,用数据创建一首通用的诗,并且欢迎用户。这本来是个非常复杂的函数因为它包含很多if/else语句并且,它将在调用那些用户数据需要的功能方面有诸多限制和不兼容性。

相反,我用回调函数实现了添加功能,这样一来获取用户信息的主函数便可以通过简单的将用户全名和性别作为参数传递给回调函数并执行来完成任何任务。

简单来讲,getUserInput函数是多功能的:它能执行具有无种功能的回调函数。


//首先,创建通用诗的生成函数;它将作为下面的getUserInput函数的回调函数

function genericPoemMaker(name, gender) {
    console.log(name + " is finer than fine wine.");
    console.log("Altruistic and noble for the modern time.");
    console.log("Always admirably adorned with the latest style.");
    console.log("A " + gender + " of unfortunate tragedies who still manages a perpetual smile");
}

//callback,参数的最后一项,将会是我们在上面定义的genericPoemMaker函数
function getUserInput(firstName, lastName, gender, callback) {
    var fullName = firstName + " " + lastName;
    // Make sure the callback is a function
    if (typeof callback === "function") {
    // Execute the callback function and pass the parameters to it
    callback(fullName, gender);
    }
}

调用getUserInput函数并将genericPoemMaker函数作为回调函数:


getUserInput("Michael", "Fassbender", "Man", genericPoemMaker);
// 输出

因为getUserInput函数仅仅只负责提取数据,我们可以把任意回调函数传递给它。例如,我们可以传递一个greetUser函数:


unction greetUser(customerName, sex)  {
    var salutation  = sex && sex === "Man" ? "Mr." : "Ms.";
    console.log("Hello, " + salutation + " " + customerName);
}

// 将greetUser作为一个回调函数
getUserInput("Bill", "Gates", "Man", greetUser);

// 这里是输出
Hello, Mr. Bill Gates

我们调用了完全相同的getUserInput函数,但是这次完成了一个完全不同的任务。

正如你所见,回调函数很神奇。即使前面的例子相对简单,想象一下能节省多少工作量,你的代码将会变得更加的抽象,这一切只需要你开始使用毁掉函数。大胆的去使用吧。

在JavaScript编程中回调函数经常以几种方式被使用,尤其是在现代WEB应用开发以及库和框架中:

  • 异步调用(例如读取文件,进行Http请求,等等)
  • 时间监听器/处理器
  • setTimeout和setInterval方法
  • 一般情况:精简代码

结束语

JavaScript回调函数非常美妙且功能强大,它们为你的Web应用和代码提供了诸多好处。你应该在有需求时使用它;或者为了代码的抽象性,可维护性以及可读性而使用回调函数来重构你的代码。

以上就是理解与使用JavaScript中的回调函数的详细内容,更多关于JavaScript的资料请关注编程网其它相关文章!

--结束END--

本文标题: 理解与使用JavaScript中的回调函数

本文链接: https://www.lsjlt.com/news/124764.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 理解与使用JavaScript中的回调函数
    目录概述什么是回调或者高阶函数回调函数是怎样运作的?回调函数是闭包实现回调函数的基本原理使用命名或匿名函数作为回调传递参数给回调函数在执行之前确保回调函数是一个函数使用this对象的...
    99+
    2022-11-12
  • 如何使用JavaScript中的回调函数
    这篇文章将为大家详细讲解有关如何使用JavaScript中的回调函数,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。JavaScript是什么JS是JavaScript的简称,它是一种直译式的脚本语言,其解...
    99+
    2023-06-14
  • 关于JavaScript回调函数的深入理解
    目录前言快速回顾:JavaScript 函数什么是函数?声明一个函数调用一个函数什么是回调函数?为什么我们需要回调如何使用回调函数回调的工作方式Javascript 回调是异步的吗?...
    99+
    2022-11-12
  • 浅析JavaScript中回调地狱与asyn函数和await函数原理
    目录一、回调地狱二、Promise对象三、syn函数和await函数一、回调地狱 1、回调函数:把一个函数作为参数传递给另一个函数,在另一个函数中作为参数的函数不会立即执行,只有当满...
    99+
    2023-01-10
    JS回调地狱 JS asyn函数 JS await函数
  • JavaScript中的回调函数详细解析
    以上就是JavaScript中的回调函数详细解析的详细内容,更多请关注编程网其它相关文章!...
    99+
    2022-11-23
    JavaScript
  • C语言中回调函数的含义与使用场景详解
    目录举例动态改变回调函数的实现的方法:1)编译时直接赋值2)运行时实现动态注册3)作为函数参数传递到指定的函数内总结举例 在下述程序中函数 test2_cal() 中调用&...
    99+
    2022-11-13
  • C语言中回调函数的含义与使用场景详解(2)
    目录详解C语言中回调函数的含义与使用场景(2)使用场景一(重定义):使用场景二(扩展函数功能):使用场景三(分层):总结详解C语言中回调函数的含义与使用场景(2) 引言:在上一篇文章...
    99+
    2022-11-13
  • Golang函数的回调函数应用讲解
    Golang作为一门高效的编程语言,其函数的回调函数应用极为重要,因此在本篇文章中,我们将深入讲解Golang函数的回调函数应用的相关知识。一、什么是回调函数?回调函数就是函数指针,它作为参数直接传给其他函数。当这个参数函数执行完毕后,再回...
    99+
    2023-05-16
    Golang函数 回调函数 应用讲解
  • keras回调函数的使用
    目录回调函数fit()方法中使用callbacks参数模型的保存和加载通过对Callback类子类化来创建自定义回调函数【其他】模型的定义 和 数据加载回调函数 回调函数是一个对象(...
    99+
    2023-03-13
    keras回调函数 keras回调
  • JS中的回调函数(callback)讲解
    目录1.什么是回调函数(callback)呢?2.回调函数有哪些特点?3.回调函数中this的指向问题4.为什么要用到回调函数?5.回调函数和异步操作的关系是什么?回调函数是异步么?...
    99+
    2022-12-14
    js 回调函数 js 回调函数callback 回调函数callback
  • JavaScript中回调函数的内容有哪些
    这篇文章将为大家详细讲解有关JavaScript中回调函数的内容有哪些,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。回调函数是每个 JS 开发人员都应该知道...
    99+
    2022-10-19
  • Python中的回调函数怎么使用
    本文小编为大家详细介绍“Python中的回调函数怎么使用”,内容详细,步骤清晰,细节处理妥当,希望这篇“Python中的回调函数怎么使用”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。摘要主要是介绍python 的...
    99+
    2023-07-02
  • 如何使用Node.js中的回调函数
    本篇内容主要讲解“如何使用Node.js中的回调函数”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“如何使用Node.js中的回调函数”吧!趣讲Node.js的回...
    99+
    2022-10-19
  • GO语言中回调函数的使用
    目录0.前言1.使用2.几个例子2.1 异步访问URL2.2 Sort.Slice函数2.3 简易计算器0.前言 回调函数是一种在编程中常见的技术,通常在异步编程中使用。简单来说,回...
    99+
    2023-03-06
    GO语言回调函数 GO 回调
  • JavaScript中的回调函数是什么及如何用
    今天小编给大家分享一下JavaScript中的回调函数是什么及如何用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。1.什么是...
    99+
    2023-07-04
  • Node.js 中如何使用回调函数
    今天就跟大家聊聊有关Node.js 中如何使用回调函数,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。Node.js 回调函数Node.js 异步编程...
    99+
    2022-10-19
  • VB.NET中回调函数如何使用
    VB.NET中回调函数如何使用,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。VB.NET经过长时间的发展,很多用户都很了解VB.NET回调函数了,这里我发表一下...
    99+
    2023-06-17
  • C# 中回调函数如何使用
    本篇文章给大家分享的是有关C# 中回调函数如何使用,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。什么是C# 回调函数C# 回调函数是应用程序提供给Windows系统DLL或其它...
    99+
    2023-06-18
  • java中回调函数如何使用
    java中回调函数如何使用,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。public class Callback {  &n...
    99+
    2023-06-02
  • C语言中回调函数和qsort函数的用法详解
    目录回调函数指向函数指针数组的指针qsort(qulick sort)-库函数回调函数 通过函数指针调用的函数,如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调...
    99+
    2022-11-13
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作