您的位置:首页 > Web前端 > Node.js

理解回调函数

2017-12-06 19:18 106 查看

前言

最近在看Node.js,有介绍一些关于回调函数的内容,之前在js里面有用过,但是只是知道要那样用,为什么要这样做和这样做有什么用处并不清楚。自己查了一些资料,理解了一下,记录下来作为回顾也方便之后复习。


一、什么是回调函数?

A callback is a function that is passed as an argument to another function and is executed after its parent function has completed.

例如:

function Cat(words, callback) {
callback(words);
}

function Speak(words) {
console.log(words);
}

Cat("miao", Speak);
Cat("miao", function(words) {
console.log(words);
});


上述代码Cat中的Speak函数和匿名函数就被称为回调函数。

然后再来看一段代码:

function f1() {
console.log("f1 finished.");
}

function f2(cb) {
cb();
console.log("f2 finished.");
}

//执行结果: f1 finished.
//         f2 finished.


那么问题来了,不是说回调函数最后执行吗????

对,很多介绍回调函数的例子讲到这里是就完了,异步回调函数的确是应该在函数的最后执行,不过上面的例子是一个同步回调函数,函数的执行顺序依然自上而下顺序执行。 那么什么是异步回调呢? 我们又怎么实现异步回调呢? 请往下看。

异步回调函数:

function f2() {
console.log('f2 finished');
}

function f1(cb) {
setTimeout(cb,1000);        //用setTimeout()模拟耗时操作
console.log('f1 finished');
}

f1(f2);    //得到的结果是 f1 finished ,f2 finished


因为setTimout()是异步函数,所以这里是先将f1执行完后再执行的回调函数f2。

小结:因为函数在Javascript中是第一类对象,我们像对待对象一样对待函数,因此我们能像传递变量一样传递函数,在函数中返回函数,在其他函数中使用函数。当我们将一个回调函数作为参数传递给另一个函数是,我们仅仅传递了函数定义。我们并没有在参数中执行函数。【需要注意的很重要的一点是回调函数并不会马上被执行。它会在包含它的函数内的某个特定时间点被“回调”。就好像它是在这个函数里面定义的,这意味着回调函数本质上是一个闭包。正如我们所知,闭包能够进入包含它的函数的作用域,因此,回调函数能获取包含它的函数中的变量,以及全局作用域中的变量。】最后,强调一点,并不是使用了回调函数就是异步,回调函数只是异步的一种实现方式而已。

二、为什么要使用回调函数?

可能有人想问,那为什么不直接从Cat函数里面调用Speak呢???如果你直接在函数Cat里调用的话,那么这个回调函数就被限制死了。但是使用函数做参数就有下面的好处:当你Cat(Speak)的时候函数Speak就成了回调函数,而你还可以Cat(Smell)这个时候,函数Smell就成了回调函数。如果你写成了function Cat(){…;Speak();}就失去了变量的灵活性。

另外一点是,当函数的实现过程非常漫长,你是选择等待函数完成处理,还是使用回调函数进行异步处理?这种情况下,使用回调函数变得至关重要,例如:AJAX请求,。若是使用回调函数进行处理,代码就可以继续进行其他任务,而无需空等。实际开发中,经常在javascript中使用异步调用。

三、使用回调函数应该注意些什么?

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

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

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

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

例如:

var localData = {
id: 094545,
name :"nothing",
//setUsrName是一个在clientData对象中的方法
setName: function (name){
//这指向了对象中的fullName属性
this.name = name;
}
}

function getName(name, callback){
callback(name);
}

getName("Tom",localData.setName);
console.log(localData.name);           //nothing
console.log(window.name);              //Tom


当你执行getName函数时,因为getName函数是一个全局函数,所以它上下文是getName的上下文,是全局window对象,所以它将设置window对象的name属性为“Tom”。

4.使用Call和Apply函数来保存this:

为了解决3的问题,我们通常使用apply或者call。当需要用到this的时候,我们只需在函数中加一个参数,以确定我们的上下文对象。例如:

function successCb() {
//...
}

function errorCb() {
//...
}

$.ajax({
type:"get",
url:"http://localhost/index.php",
data:{uid:id,password:password},
dataType:"jsonp",
jsonp:"callback",
success:successCb,
error:errorCb,
});


四、总结

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

1)异步调用(例如读取文件,进行HTTP请求,等等)

2)时间监听器/处理器

3)setTimeout和setInterval方法

4)一般情况:精简代码
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息