javascript之回调函数

回调函数


在javascript中,Function也是一种类型,方法在js中也是一种对象。所以函数可以作为一个参数传递到另一个函数中,并随后执行。或者是函数可以作为另一个函数(外层函数)的返回值,之后可以执行这个返回的函数。而这个特性,正好是回调函数的精髓。

回调函数的概念源自函数编程范例,在函数编程范例范例中详细说明了函数作为参数来使用的情况。


定义

那么什么是回调函数呢?回调函数是一个作为参数被传递给另一个函数(称“otherFunction”)的函数,回调函数在"otherFunction"中被调用(或执行)。

回调函数本质上是一种模式(模式就是解决一类共性问题的解决方法),回调函数的使用就称为是一种回调模式。

现给出一个回调函数在jQuery中的常用实例:

$("#btn_1").click(function() {
  alert("Btn 1 Clicked");
});
在上面的代码中,将函数作为参数传入到click方法中,当点击鼠标,触发click事件时,会调用这个函数。

现在,我们再看一个在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方法中,传入匿名函数(没有名字的方法)作为参数。


回调函数的工作原理

我们可以像变量一样传递函数,作为另一个函数的返回值,也可以在其他函数中调用它。当我们将回调函数作为参数传递给另一个函数的时候,我们传递的仅仅是函数定义并没有在传递参数过程中执行回调函数

而包含函数(回调函数的传入函数)的参数中包含了回调函数的定义,所以包含函数可以在任何时候执行回调函数。

注意,回调函数不是立即执行的,而是会在包含函数中的某些地方被“回调”。在之前给出的click例子中,即使回调函数作为参数传入到函数中,它也不会立即执行,而是会在函数体中被延迟调用。虽然是一个匿名函数,但是可以通过包含函数的arguments对象来访问匿名函数。


回调函数是闭包

当将回调函数作为一个参数传递给另一个函数的时候,回调函数会在包含函数体内的指定地方被执行,就好像这个回调函数是在包含函数体内定义的一样。这就意味着回调函数就是一个闭包。我们都知道,闭包可以访问包含函数的作用域,所以回调函数也可以访问包含函数的变量,甚至是全局作用域中的变量。


实现回调函数的基本规则

虽然回调函数简单,但是在我们生成回调函数的时候,需要注意一些规则。

(1)作为回调函数的函数可以是有名字的,也可以是匿名的

在之前的例子中,我们已经看到了匿名函数作为回调函数的用法。这是使用回调函数常用的模式。另一种方法是,使声明一个命名的函数,然后将函数名作为参数进行传递(注意传递的只有函数名,没有())。如下例:

//全局变量
​var allUserData = [];
​
​// 将传入的对象内容输出到控制台
​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 ({name:"Rich", speciality:"JavaScript"}, logStuff);
​
//  name: Rich​
​// speciality: JavaScript


(2)给回调函数传递参数

回到函数在执行过程中和普通函数是一样的,也可以为回调函数传递参数。可以将包含函数中的属性或者是全局属性作为参数传递给回调函数。在上一个例子中,我们将options(包含函数的属性)传递给了回调函数。下面我们试着将全局变量和局部变量传递给回调函数:

//Global variable​
​var generalLastName = "Clinton";
​
​function getInput (options, callback) {
    allUserData.push (options);
​// Pass the global variable generalLastName to the callback function​
    callback (generalLastName, options);
}


(3)在执行回调函数前,检查回调函数是否为函数

在调用以函数参数传入的回调函数前,需要检查是否为一个函数。

function getInput(options, callback) {
    allUserData.push(options);
​
    // Make sure the callback is a function​
    if (typeof callback === "function") {
    // Call it, since we have confirmed it is callable​
        callback(options);
    }
}
如果getInput函数没有做适当的检查(检查callback是否是函数,或是否通过参数传递进来了),我们的代码将会导致运行时错误。


(4)使用带有this对象的函数作为回调函数的情况

当使用带有this对象的方法作为回调函数时,我们要改变我们执行回调函数的方式,以确保this对象的上下文环境。否则,当回调函数被传递给一个全局函数时,要么this对象指向了window对象,要么this对象将指向包含函数对象。

var clientData ={
            id : 1234,
            fullname : "no-name",

            setUserName : function(firstname,lastname){
                this.fullname = firstname + " "+lastname;
            }
        }

        function getUserInput(firstname,lastname,callback){
            callback(firstname,lastname);
        }
        getUserInput("Barack","obama",clientData.setUserName);

        console.log(clientData.fullname);//no-name

        console.log(window.fullname);//Barack obama

clientData.setUserName作为回调函数被执行的时候,this.fillname将不会设置clientData对象中的fullname的值。事实是,将会设置window对象的fullname的值,因为getUserInput是一个全局函数,在全局函数中的this对象将指向window对象。

(5)使用call或apply方法保护this的内容
每个函数都有call和apply方法,通过这两个方法,能够指定函数在哪个对象上执行。
var clientData ={
            id : 1234,
            fullname : "no-name",

            setUserName : function(firstname,lastname){
                this.fullname = firstname + " "+lastname;
            }
        }

        function getUserInput(firstname,lastname,callback,callbackObj){
            callback.apply(callbackObj,[firstname,lastname]);
        }
        getUserInput("Barack","obama",clientData.setUserName,clientData);

        console.log(clientData.fullname);//Barack obama

(6)可以传递多个回调函数给另一个函数,就像传递多个变量一样。

回调函数的使用是如此简单,但功能强大。当有以下需求时可以考虑使用回调:

  • 避免重复代码 (DRY—Do Not Repeat Yourself)
  • 在你需要更多的通用功能的地方更好地实现抽象(可处理各种类型的函数)。
  • 增强代码的可维护性
  • 增强代码的可读性
  • 有更多定制的功能

先给出一个实例,说明使用回调函数,避免了繁琐冗余的代码,使用一个函数,通过调用不同的回调函数,实现多个功能,简化代码。
//该函数的作用是根据输入的人名和性别,输出一首诗
function genericPoemMaker(name,gender){
	console.log(name + "is finer than fine wine");
	console.log("A"+gender+" of unfortunate tragedies who still manages a perpetual smile.");
}
//该函数的作用是根据输入的人名和性别,输出问候语
function greetUser(customername,sex){
	var salutation = sex && sex === "man" ? "Mr.":"Ms.";
	console.log("hello,"+salutation+" "+customername);
}
//输入人名,然后执行回调函数
function getUserInput(firstname,lastname,gender,callback){
	var fullname = firstname + " "+ lastname;

	if(typeof callback ==="function"){
		callback(fullname,gender);
	}
}
getUserInput("michael","faster","man",genericPoemMaker);

getUserInput("michael","faster","man",greetUser);

在上面的示例中,输入人名和性别后,可以实现各种操作,只需要替换不同的回调函数就可以了。



参考资料

Understand JavaScript Callback Functions and Use Them

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



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值