javascript 函数初探 (四)--- 回调函数
回调函数
既然函数与任何被赋值给变量的数据是相同的,那么她当然可以像其他数据那样被定义、删除、拷贝,以及当成参数传递给其它函数。
我们定义一个函数,这个函数有两个函数类型的参数,然后他会分别执行这两个参数所执行的函数。
function her(){ return a() + b(); } function one(){ ; } fucntion two(){ ; } her(one, two)
实际上,我们也可以直接用匿名函数(即函数表达式)来替代one(),two(),以作为目标函数的参数:
her(function {}, function {}); // 3
当我们将函数A传递给函数B,并由B来执行A时,A就成了一个回调函数(callback function),如果这是A还是一个匿名函数,我们就称它为匿名回调函数。
回调函数的优势:
1. 她可以让我们在不做命名的情况下传递参数(意味着可以节省变量名的使用);
2. 我们可以将一个函数调用操作委托给另一个函数(意味着可以减少一些代码编写工作);
3. 也有助于提高性能;
再来一个简单的例子:
function main(callback) { alert("I am main function"); alert("Invoke callback function.."); callback(); } function b(){ alert("I am callback function: b"); } function c(){ alert("I am callback function: c"); } function test() { main(b); main(c); }
什么是回调或高级函数?
回调函数被认为是一种高级函数,一种被作为参数传递给另一个函数(在这称作"otherFunction")的高级函数,回调函数会在otherFunction内被调用(或执行)。回调函数的本质是一种模式(一种解决常见问题的模式),因此回调函数也被称为回调模式。
思考一下下面这种在jQuery中常用的回调函数:
$("#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 + ". " + eachName); }); // 1. Mike, 2. Stacy, 3. Andy, 4. Rick
我们再一次用同样的方式传递了一个匿名的函数(没有函数名的函数)给forEach方法,作为forEach的参数。
到目前为止,我们传递了一个匿名的函数作为参数给另一个函数或方法。在看其它更复杂的回调函数之前,让我们理解一下回调的工作原理并实现一个自己的回调函数。
回调函数是如何实现的?
我们可以像使用变量一样使用函数,作为另一个函数的参数,在另一个函数中作为返回结果,在另一个函数中调用它。当我们作为参数传递一个回调函数给另一个函数时,我们只传递了这个函数的定义,并没有在参数中执行它。
当包含(调用)函数拥有了在参数中定义的回调函数后,它可以在任何时候调用(也就是回调)它。
这说明回调函数并不是立即执行,而是在包含函数的函数体内指定的位置“回调”它(形如其名)。所以,即使第一个jQuery的例子看起来是这样:
$("#btn_1").click(function() { alert("Btn 1 Clicked"); });
匿名函数将延迟在click函数的函数体内被调用,即使没有名称,也可以被包含函数通过 arguments对象访问。
回调函数是闭包的
当作为参数传递一个回调函数给另一个函数时,回调函数将在包含函数函数体内的某个位置被执行,就像回调函数在包含函数的函数体内定义一样。这意味着回调函数是闭包的,想更多地了解闭包看下一章吧!
从所周知,闭包函数可以访问包含函数的作用域,所以,回调函数可以访问包含函数的变量,甚至是全局变量。
实现回调函数的基本原则
简单地说,自己实现回调函数的时候需要遵循几条原则。
使用命名函数或匿名函数作为回调
在前面的jQuery和forEach的例子中,我们在包含函数的参数中定义匿名函数,这是使用回调函数的通用形式之一,另一个经常被使用的形式是定义一个带名称的函数,并将函数名作为参数传递给另一个函数,例如:
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 ({name:"Rich", speciality:"JavaScript"}, logStuff);
传递参数给回调函数
因为回调函数在执行的时候就和一般函数一样,我们可以传递参数给它。可以将包含函数的任何属性(或全局的属性)作为参数传递回调函数。在上一个例子中,我们将包含函数的options作为参数传递给回调函数。下面的例子让我们传递一个全局变量或本地变量给回调函数:
var generalLastName = "Clinton"; function getInput (options, callback) { allUserData.push (options); callback (generalLastName, options); }
在执行之前确保回调是一个函数
在调用之前,确保通过参数传递进来的回调是一个需要的函数通常是明智的。此外,让回调函数是可选的也是一个好的实践。
让我们重构一下上面例子中的getInput函数,确保回调函数做了适当的检查。
function getInput(options, callback) { allUserData.push(options); if (typeof callback === "function") { callback(options); } }
如果getInput函数没有做适当的检查(检查callback是否是函数,或是否通过参数传递进来了),我们的代码将会导致运行时错误。
使用含有this对象的回调函数的问题
当回调函数是一个含有this对象的方法时,我们必须修改执行回调函数的方法以保护this对象的内容。否则this对象将会指向全局的window对象(如果回调函数传递给了全局函数),或指向包含函数。让我们看看下面的代码
var clientData = { id: , fullName: "Not Set", setUserName: function (firstName, lastName) { this.fullName = firstName + " " + lastName; } } function getUserInput(firstName, lastName, callback) { callback (firstName, lastName); }
在下面的示例代码中,当clientData.setUserName被执行时,this.fullName不会设置clientData 对象中的属性fullName,而是设置window 对象中的fullName,因为getUserInput是一个全局函数。出现这种现象是因为在全局函数中this对象指向了window对象。
getUserInput ("Barack", "Obama", clientData.setUserName); console.log (clientData.fullName); console.log (window.fullName);
使用Call或Apply函数保护this对象
我们可以通过使用 Call 或 Apply函数来解决前面示例中的问题。到目前为止,我们知道JavaScript中的每一个函数都有两个方法:Call和Apply。这些方法可以被用来在函数内部设置this对象的内容,并内容传递给函数参数指向的对象。
Call takes the value to be used as the this object inside the function as the first parameter, and the remaining arguments to be passed to the function are passed individually (separated by commas of course). The Apply function’s first parameter is also the value to be used as the thisobject inside the function, while the last parameter is an array of values (or the arguments object) to pass to the function. (该段翻译起来太拗口了,放原文自己体会)
这听起来很复杂,但让我们看看Apply和Call的使用是多么容易。为解决前面例子中出现的问题,我们使用Apply函数如下:
function getUserInput(firstName, lastName, callback, callbackObj) { callbackObj callback.apply (callbackObj, [firstName, lastName]); }
通过Apply函数正确地设置this对象,现在我们可以正确地执行回调函数并它正确地设置clientData对象中的fullName属性。
getUserInput ("Barack", "Obama", clientData.setUserName, clientData);
我们也可以使用Call 函数,但在本例中我们使用的Apply 函数。
多重回调函数也是允许的
我们可以传递多个回调函数给另一个函数,就像传递多个变量一样。这是使用jQuery的AJAX函数的典型例子:
function successCallback() { // Do stuff before send } function successCallback() { // Do stuff if success message received } function completeCallback() { // Do stuff upon completion } function errorCallback() { // Do stuff if error received } $.ajax({ url:"http://fiddle.jshell.net/favicon.png", success:successCallback, complete:completeCallback, error:errorCallback });
javascript 函数初探 (四)--- 回调函数的更多相关文章
- javascript 理解和使用回调函数
在javascript中,function是内置的类对象,也就是说它是一种类型的对象,可以和其他String.Array.Number.Objec类的对象一样用于内置对象的管理.因为function实 ...
- 如何写JavaScript中的callback回调函数
如何写回调函数? 如果自己在写一个方法或函数,你有可能会遇到需要一个回调函数.下面就是一个简单的常见回调函数例子: function mySandwich(param1, param2, callba ...
- C语言之函数指针、回调函数的使用
一.背景 首先看下如下代码,这个定义是放在头文件的,在程序中tCdrvCallbackFkt也定义了另一个变量,而且括号后面还跟定义了几个变量,不理解这个定义. typedef void (PUBLI ...
- c++指针函数的使用——回调函数
/* 函数指针 函数也是有地址的 所谓函数指针,就是指向函数的指针,函数指针也是一个变量,可以指向不同的函数.同时通过函数指针可以调用其指向函数,从而使函数的调用更加灵活. 函数指针的用途 */ #i ...
- VC++的函数指针和回调函数 及友元函数
什么是函数指针 函数指针是指向函数的指针变量.也就是说,它是一个指针变量,而且该指针指向一个函数. 对于指针变量来说,它的值是它指向的变量的地址.举个例子:指针变量pi是指向一个整型变量i的指针,则变 ...
- C 函数指针、回调函数
参考链接:https://www.runoob.com/cprogramming/c-fun-pointer-callback.html 函数指针 函数指针就是执行函数的指针,他可以像正常函数一样去调 ...
- JS中的匿名函数、回调函数、匿名回调函数
工欲善其事必先利其器 在学习JavaScript设计模式一书时,遇到了“匿名回调函数”这个概念,有点疑惑,查找了些资料重新看了下函数的相关知识点之后,对这个概念有了认识.九层之台,起于垒土.在熟悉这一 ...
- C语言函数指针和回调函数
彻底搞定C指针-函数名与函数指针 函数名&函数名取地址 函数指针 通常我们可以将指针指向某类型的变量,称为类型指针(如,整型指针).若将一个指针指向函数,则称为函数指针. 函数名的意义 函数名 ...
- golang中匿名函数的应用-回调函数-闭包
package main import ( "fmt" "strconv" ) type funcType func(int, int) int // 自定义函 ...
- mongoose的update函数中的回调函数是必须要传的吗
mongoose中的update的回调函数是必须要传的,如果不传,则不会更新. 例如这样写是不会更新的 tagModel.update({name:tagName},{$inc:{total:1}}, ...
随机推荐
- sql复习第五次
1.在数据库范围内,关系的每一个属性值是不可分解的 关系中不允许出现重复元组 由于关系是一个集合,因此不考虑元组的顺序 2.笛卡儿积是两个关系的所有元组组合而成的,而等值联接是由笛卡儿积和选择运算组合 ...
- Android之线程池深度剖析
1.线程池的引入 引入的好处: 1)提升性能.创建和消耗对象费时费CPU资源 2)防止内存过度消耗.控制活动线程的数量,防止并发线程过多. 使用条件: 假设在一台服务器完成一 ...
- OpenCASCADE Rational Bezier Curves
OpenCASCADE Rational Bezier Curves eryar@163.com Abstract. Although polynomials offer many advantage ...
- Triangle - Delaunay Triangulator
Triangle - Delaunay Triangulator eryar@163.com Abstract. Triangle is a 2D quality mesh generator an ...
- SmtpClient发邮件时为什么用MailMessage.From而不用MailMessage.Sender
今天在看C#高级编程(第9版)的时候,在768页看到这样的一段代码 SmtpClient sc = new SmtpClient(); sc.Host = "邮箱服务器地址"; M ...
- ImageView设置边框的两种方式
转载:http://www.2cto.com/kf/201308/239945.html package cc.testimageviewbounds; import android.os.Bundl ...
- AngularJS之Dependency Injection(五)
前言 这一节我们来讲讲AngularJS中的依赖注入,在实际开发中Angular提供了具体的方法供我们去调用,但是一旦业务不能满足要求或者出现麻烦或者错误时导致无从下手,所以基于此我们有必要深入一点去 ...
- Spring对事务管理的支持的发展历程--转
原文地址:http://www.iteye.com/topic/1123049 1.问题 Connection conn = DataSourceUtils.getConnection(); //开启 ...
- http 错误代码表
所有 HTTP 状态代码及其定义. 代码 指示 2xx 成功 200 正常:请求已完成. 201 正常:紧接 POST 命令. 202 正常:已接受用于处理,但处理尚未完成. 2 ...
- Java类初始化
Java类初始化 成员变量的初始化和构造器 如果类的成员变量在定义时没有进行显示的初始化赋值,Java会给每个成员变量一个默认值 对于 char.short.byte.int.long.float. ...