JS中的 回调函数(callback)

549 阅读3分钟

1.什么是回调函数(callback)呢?

把函数当作一个参数传到另外一个函数中,当需要用这个函数是,再回调运行()这个函数.

回调函数是一段可执行的代码段,它作为一个参数传递给其他的代码,其作用是在需要的时候方便调用这段(回调函数)代码。(作为参数传递到另外一个函数中,这个作为参数的函数就是回调函数)

理解:函数可以作为一个参数传递到另外一个函数中

<script>
    function add(num1, num2, callback) {
        var sum = num1 + num2;
        callback(sum);
    }

    function print(num) {
        console.log(num);
    }

    add(1, 2, print); //3
</script>

add(1, 2, print);中,函数print作为一个参数传入到add函数中,但并不是马上起作用,而是var sum = num1 + num2;运行完之后需要打印输出sum的时候才会调用这个函数。(这个作为参数传递到另外一个函数中,这个作为参数的函数就是回调函数

匿名回调函数:

<script>
    function add(num1, num2, callback) {
        var sum = num1 + num2;
        callback(sum);
    }

    add(1, 2, function (sum) {
        console.log(sum); //=>3
    });
</script>

2.回调函数有哪些特点?

1.不会立即执行

回调函数作为参数传递给一个函数的时候,传递的只是函数的定义并不会立即执行。和普通的函数一样,回调函数在调用函数数中也要通过()运算符调用才会执行。

2.回调函数是一个闭包

回调函数是一个闭包,也就是说它能访问到其外层定义的变量。

3.执行前类型判断

在执行回调函数前最好确认其是一个函数。

<script>
    function add(num1, num2, callback) {
        var sum = num1 + num2;
        //判定callback接收到的数据是一个函数
        if (typeof callback === 'function') {
            //callback是一个函数,才能当回调函数使用
            callback(sum);
        }
    }
</script>

3.回调函数中this的指向问题

注意在回调函数调用时this的执行上下文并不是回调函数定义时的那个上下文,而是调用它的函数所在的上下文

<script>
    function createData(callback){
        callback();
    }
    var obj ={
        data:100,
        tool:function(){
            createData(function(n){
                console.log(this,1111);  //window 1111
            })   
        }
    }
    obj.tool();
</script>

分析:this指向是 离它最近的或者嵌套级别的 function/方法的调用者,这里离它最近的function是

function(n),会回到上面的callback()中,这时候调用者就不是obj而是window。

 解决回调函数this指向的方法1:箭头函数

<script>
    function createData(callback){
        callback();
    }
    var obj ={
        data:100,
        tool:function(){
            createData((n)=>{
                this.data = n;
            })   
        }
    }
    obj.tool();
    console.log(obj.data); 
</script>

回调函数用箭头函数写之后,this指向很明确,就是 离它最近的或者嵌套级别的 function/方法的调用者,所以这里是 obj 。

解决回调函数this指向的方法2:var self = this

<script>
    function createData(callback){
        callback(999);
    }
    var obj ={
        data:100,
        tool:function(){
            var self = this;   //这里的this指向obj,然后当一个变量取用
            createData(function(n){
                self.data = n;
            })   
        }
    }
    obj.tool();
    console.log(obj.data);
</script>

4.为什么要用到回调函数?

有一个非常重要的原因 —— JavaScript 是事件驱动的语言。这意味着,JavaScript 不会因为要等待一个响应而停止当前运行,而是在监听其他事件时继续执行

<script>
    function first() {
        console.log(1);
    }

    function second() {
        console.log(2);
    }

    first();
    second();
</script>

first 函数首先被执行,随后 second 被执行 —— 控制台输出:1 2

但如果函数 first 包含某种不能立即执行的代码会如何呢?例如我们必须发送请求然后等待响应的 API 请求?为了模拟这种状况,我们将使用 setTimeout,它是一个在一段时间之后调用函数的 JavaScript 函数。我们将函数延迟 500 毫秒来模拟一个 API 请求

<script>
    function first() {
        // 模拟代码延迟
        setTimeout(function () {  //所以function(){console.log(1)}是回调函数
            console.log(1);
        }, 500);
    }

    function second() {
        console.log(2);
    }

    first();
    second();
</script>

这里 function(){console.log(1)}函数当作一个参数传入setTimeout函数中,因为setTimeout是官方提供得一个函数,里面有很多复杂的业务程序,所以函数 function(){console.log(1)}传入后,不一定马上运行,要setTimeout里面要运行到function(){console.log(1)}时才会运行该函数参数

整个程序运行结果为: 2 1 ,并不是原先的1 2 .即使我们首先调用了 first() 函数,我们记录的输出结果却在 second() 函数之后。

这不是 JavaScript 没有按照我们想要的顺序执行函数的问题,而是 JavaScript 在继续向下执行 second() 之前没有等待 first() 响应的问题。回调正是确保一段代码执行完毕之后再执行另一段代码的方式