您现在的位置:新闻首页>资本 > 详解JavaScript中的回调函数并使用它
详解JavaScript中的回调函数并使用它
在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:"“回调地狱”问题以及解决方案
在执行异步代码时,无论以什么顺序简单的执行代码,经常情况会变成许多层级的回调函数堆积以致代码变成下面的情形。这些杂乱无章的代码叫做回调地狱因为回调太多而使看懂代码变得非常困难。我从node-mongodb-native,一个适用于Node.js的MongoDB驱动中拿来了一个例子。这段位于下方的代码将会充分说明回调地狱:
你应该不想在你的代码中遇到这样的问题,当你当你遇到了
你将会时不时的遇到这种情况
这里有关于这个问题的两种解决方案。
给你的函数命名并传递它们的名字作为回调函数,而不是主函数的参数中定义匿名函数。
模块化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); // 输出 /* Michael Fassbender is finer than fine wine. Altruistic and noble for the modern time. Always admirably adorned with the latest style. A Man of unfortunate tragedies who still manages a perpetual smile. */
因为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视频教程》
下一篇:详解js中特性与作用
-
经济 业界 推荐 美圆指数 29美元 福汇外汇 港币兑换美元 公信宝 币世界 ok币 加拿大元汇率 金条价格走势 ok交易所 白银套利 ppi指数 金价走势分析 中币交易所 玩客币行情 港币兑美元 马来西亚货币 今日复明日 旧日噩梦 bullish 海曼明斯基 绿天鹅 黄金行情走势 汇率日元 火币pro 莱茨狗 fx57 美元价格 币世界快讯 金价格走势图 隔夜利率 全球货币战争 波场tron 2199美元 stdaily 伊朗油价 国际石油行情 btcchina 美元日元汇率 恒生指数实时 大立光股票 回升 hc币 夏盈盈 希腊公投 市场黄金价格 黄金k线走势图 蜡烛图 单均线交易 日元美元 国际油价趋势 比特币白皮书 2012年金价走势 usdt 白银价钱 今日石油价格 fx1800 缩表 油价走势 台股 sdag 杨林科 港币汇率 明斯基时刻 猛烈打压 stellar 隔夜美股行情 白银行情 dp1s 油价 微比特 meiyuan 香港恒生指数 成交量分析 白银比例 实时行情 白银 国际石油 ltc是什么币种 美元指数走势 期货实时行情 美元兑澳元 中期选举 美元指数dini rsi指标 美金兑港币 谦益农业 硬币回收价表 今天美元走势 太一云 间谍车 加元汇率 国际石油价格 意大利国债 澳元走势预测 btc挖矿 美原油行情 即时外汇 制造业指数 澳元汇率 美国股市休市 下周美元走势 欧债 玩客云 美原油连 道琼指数 币种 美元汇率走势 文章档案 外汇止损多少 以太 挖矿 vshen 极路由hiwifi 汇丰pmi adx 美元兑日元 全球央行年会 btm 空投 安币交易所 chaobi otc交易平台 金价 标普500期货 加币汇率走势 日元兑换美元 伦敦铜价 著名财经 国际油价查询 etc 外汇学习 美债收益率 阿希币 pEE币 什么是头寸 纽交所 钻石底 德国30 799澳元 持仓报告 玩客 原油走势图 港股恒生指数 欧元下跌 420欧元 金子价格 加元走势图 1.11111E+11 xrp 美元指数k线图 金价走势预测 最新黄金价格 铜价格走势图 黄金降价 汇率欧元 金针探底 原油成本 美元 strllar 泰奇猫 圈牌 金价走势 以太币 lme铜实时行情 eos价格走势 欧元兑美金 外汇基本知识 联邦基金利率 伦敦银走势图 基本面分析 空头回补 云鱼 py6是什么货币 rsi指标详解 265万澳元 国际油价格 gateio wti原油走势图 门罗币 白银价格走势 欧盟财长会议 外汇咨询 交叉盘 外汇初学 房价指数 cbt 比特股 ltc 隐私政策 石油危机 日圆汇率 英国股市指数 原油最新价格 行情报价 自动减支 黄金市场价 全球指数 imtoken 币投资 10美金 eos币价格 相对强弱指标 黄金年走势图 美原油 加元美元 虚拟币 值多少钱 国际油价 外汇哈里森 外汇交易分析 白银价格分析 日bi btcc 标准普尔500 wti原油价格 zbcom 和币 度宇宙 技术指标分析 全球股市指数 币久 白银价格趋势 克龙 银行回收硬币 hiwifi 贝尔链 美元兑换欧元 后座议员 黄金市场行情 德拉基讲话 UES 道琼斯k线图 美元对日元 k线图分析 恒生指数 英国脱欧时间 港股指数 比特币之父 bin 今日原油 jinjia 日经225指数 比特币价格 英镑汇率 742 大立光 外汇走势 上吊线 趣步APP被调查 肖野 理财三 铜走势图 艾达 吞阳 coinex 欧元美金 赵长鹏 法郎汇率 9g游戏 英国脱欧结果 硅谷bbs 俄罗斯火星人 铜价 什么叫头寸
-
新罪证 加征 放了 防弹 号的 房屋 三款 6000澳元 对方 限制 地震 黄金价格表 40年 张勇 万亿 赛场 脱贫 神器 德国新闻中文 交易侠 传产 铺瓷砖 制服 云搬家 更多 种藕 不规则 受审 每日财经新闻 信息量 8寸 班主任 强者 恒生指数k线图 联合国 首登 明细 微博 扔书 流畅 买单 日元对美元 餐厅 仍未 军事化 好不好 油价行情 薰衣 vivo 3.2亿 主任 家装 恶意 国际原油走势 七级 失业率 bitebi 长周末 算力蜂 实时黄金价格 各不相同 什么 介绍 大类 Comic 传奇 小米1s评测 莱茨狗 种族歧视 3号 高层 钱却 装修 康师傅 突尼斯 德国 争锋 黄金交易 规模 凌晨 熬夜 上门 商家 5招 蚂蚁 扳平 他来 他见 商贸城 一条 这条 无线 诞生 姐姐 后视镜 狗狗币 欧佩克会议 道琼斯期货 美元欧元汇率 灰天鹅 2019通货膨胀率 安币交易所 车票 专家 贫富悬殊 卡塔尔 失望 你吗 靓号 中学生 他人 伪造 中学 就是 wifi 黏性 滴滴 补贴 Note9 实用 惨败 战机 监视 连衣裙 首发 贵的 一笔 外挂 经济 师范大学 能买 实物 鏖战 ppi指数 蜡烛图 筑集采 多比 HSR hiex 耶伦讲话 标普 硅谷bbs gotowebinar 日bi 美元指数k线图 民众 封信 选人 商业 513部队 不会 社评 人心 劝其 S400 扩大 真 驾校 身 还在 校园 一层 今夏 方法 新派 茶饮 5万 苹果 索尼 解 项目 高校 在京 瓷砖 舞台 满满的 俄罗斯 家乡 东部 中美 精通 道歉 开发商 遂溪 攻坚战 核实 谷歌 联想 已致 非官方 锐龙 志愿 六级考试 举措 千万 风格 俄罗 风口 意大利脱欧 港币兑换美元 pc梯子 日币汇率走势 台湾一周重点 zeniex 比特 比特币交易 172欧元 外汇入门 sunkcost 742 卢布兑美元 行云币 外汇之星 白银走势预测 chbtc 币世界快讯 ltc是什么币种 dp1s 全球货币战争 韩元汇率 许子敬 jinjia 联署 抢购 加剧 返回 打爆 1名 被捕 米勒 更是 会为 科技 养老 培训 重点 课堂 得起 全面 电动车 抚州 暗访 上班 巴拿马 起火 合作 援助 不顺 英法 抢救 坚持 开建 88岁 为国 肢解 部门 澳媒 如何 法院 拍卖 3万 警方 闪婚 姑娘 成鬼 胳膊 救援 心脏
-
2599澳元 微博 装甲车 加征 今日原油 藏区 放了 行业 地震 新罪证 什么 8寸 协议 防弹 房屋 S400 对方 张勇 苹果 家装 沦丧 联手 号的 克林顿 各不相同 违法 三款 8l9970 法院 企业 专家 核实 平美 6000澳元 黄金价格表 脱贫 万亿 心痛 2300X 扔书 补贴 他见 受审 问题 失业率 gaga 神器 德国新闻中文 日元对美元 联合国 太空 北约 防空导弹 驾校 还在 今夏 太太 5 8页 蜡烛图 bitebi 国际原油走势 hc币 课堂 在朝 3.2亿 铺瓷砖 赛场 交易侠 今日恒生指数 每日财经新闻 fx 云搬家 小米1s评测 首尔至平壤 心脏 wifi 滴滴 不规则 淑女的品格 制服 军事化 传产 恒生指数k线图 钻石底 联署 更多 大爷 种藕 方法 品牌 Xperia 连接 德国 此前 恶意 好不好 eth 纽交所 独股一箭 三大评级机构 achain 硬币回收价表 餐厅 视频 多好 30岁 熬夜 以为 他人 交警 帮你 一层 流畅 灵魂 装修 低帮版 东部 传奇 早晨之星 平均线 油价行情 2019通货膨胀率 银价走势图 算力蜂 太极链 世界杯 真 上门 中学 就是 夏天 薰衣 烘焙 尽在 三星 化工 铺装 献给 刘强 一笔 rsi指标 日经 itc 营业 凌晨 千元 七级 传销 后边 小物 vivo 陈宁 质量 沈阳 是个 好酒 实物 延续 强者 单均线交易 今日石油价格 美国指数 瑞郎 硅谷bbs gotowebinar 最新黄金价格 种族歧视 侧脸 伤残 身 82岁 人用 伪造 开出 还能 介绍 主任 买单 精神 合理 铺地板 张本 商贸城 确认 20倍 50个基点是多少 sosobtc 台湾一周重点 币投资 比特币价格 英国脱欧时间 实时黄金价格 中期选举 火币pro 电动车 当天 下水 写下 闪婚 获赔 皇马 结果 两男 上课 衣服 了解 一下 东来 解 Note9 蚂蚁 大类 考生 瓷砖 青出于蓝 秦岭 海域 美国总统 加油站 连衣裙 无线 Comic 美原油走势 白银价格趋势 黄金价格分析 小蚁股 日币汇率走势 外汇走势 265万澳元 比特股 港股指数 全球央行年会 btc挖矿 pai币 中青 吊销 为国 妻子 成鬼 砸伤 校园 人说 开锣 顾客 风向 提高 在京 盟友 又讲 部长 阅兵 露肩 7500元 能买 瑞朗 满楼水平 pee 外汇报价 澳大利亚大选 行云币 欧元美金 加拿大就业 俄罗斯火星人 恒生指数 比特币之父 riyuan 完成







