理解回调函数

前言
   最近在看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)一般情况:精简代码

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值