回調(diào)函數(shù)

首先,回調(diào)函數(shù)一點(diǎn)都不復(fù)雜,也一點(diǎn)都不難。

一、定義:

回調(diào)函數(shù)是從一個(gè)叫函數(shù)式編程的范式中衍生出來的。簡單來說,回調(diào)函數(shù)就是將函數(shù)作為變量。
eg:

//常見的比如jquery的click事件
$('#btn').click(function(){    //這個(gè)一個(gè)匿名函數(shù)被當(dāng)做參數(shù)傳到click函數(shù)了
    cnosole.log('回調(diào)函數(shù)被回調(diào)了');
});

二、回調(diào)函數(shù)是如何運(yùn)行的?

回調(diào)函數(shù)作為一個(gè)參數(shù)傳給其他函數(shù);
我們傳遞的只是這個(gè)函數(shù)的定義,也就是不會(huì)執(zhí)行;
回調(diào)函數(shù)是在接受的函數(shù)中的某個(gè)特定的“點(diǎn)”執(zhí)行的。

三、回調(diào)函數(shù)本質(zhì)上是閉包

回調(diào)函數(shù)作為參數(shù)傳進(jìn)函數(shù),在接受它的函數(shù)中的某個(gè)點(diǎn)執(zhí)行(因?yàn)閭鬟f的是函數(shù)的定義,只有接受它的函數(shù)調(diào)用它,它才會(huì)執(zhí)行)
這就相當(dāng)于回調(diào)函數(shù)在接受它的函數(shù)中定義,那么其實(shí)回調(diào)函數(shù)其實(shí)就是一個(gè)閉包,它可以訪問接受它的函數(shù)里面的變量,也可以訪問全局變量。

四、回調(diào)函數(shù)基本原理

(1)使用命名或者匿名函數(shù)作為回調(diào)

//匿名函數(shù)的情況上面已經(jīng)介紹了,這里就不重復(fù)
//命名函數(shù)回調(diào)
function sayHi(){
    alert( 'hello!' );
}

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

//調(diào)用
getName( 'jack', sayHi  );    //hello!

(2)傳遞參數(shù)給回調(diào)

function sayHi( name ){
    alert( 'hello!' + name + '!' );
}

function getName( name, callback ){
    callback( name );
}
//調(diào)用
getName( 'jack', sayHi  );    //hello,jack!

(3)在執(zhí)行之前確保回調(diào)函數(shù)是一個(gè)函數(shù)

function sayHi( name ){
    alert( 'hello!' + name + '!' );
}

function getName( name, callback ){
    //確保callback是一個(gè)函數(shù)
    if( typeof callback == 'function' ){
        //已經(jīng)確定就可以調(diào)用了
        callback( name );
    }
}
//調(diào)用
getName( 'jack', sayHi  );    //hello,jack!

(4)一個(gè)坑:使用this對(duì)象的方法作為回調(diào)函數(shù)時(shí)
當(dāng)回到函數(shù)是一個(gè)使用了this的對(duì)象的方法時(shí),如下:

var user = {
    name: "No set",
    setUserName: function( name ){
        //setUserName是thiss對(duì)象的一個(gè)方法,待會(huì)作為回調(diào)函數(shù)用
        this.name = name + 'doctor';
    }
}

我們必須改變執(zhí)行回調(diào)函數(shù)的方法來保證this對(duì)象的上下文。否則如果回調(diào)函數(shù)被傳遞給一個(gè)全局函數(shù),this對(duì)象要么指向window對(duì)象(瀏覽器中)。要么指向把包含方法的對(duì)象。
在下面的例子中,user.setUserName被執(zhí)行時(shí),this.name并沒設(shè)置user對(duì)象中的name。
相反,它將設(shè)置window對(duì)象中的name屬性,這是因?yàn)槿ト趾瘮?shù)中的this對(duì)象指向window對(duì)象。

function getName( name, callback ){
    if( typeof callback === 'function' ){
        callback( name );
    }
}

getName( 'jack', user.setUserName );

console.log( user.name );    //Not set
console.log( window.name );     //jack

針對(duì)(4)點(diǎn)說到得這個(gè)坑,我們可以“使用call和apply函數(shù)來保存this”解決
我們知道,call和apply可以被用來設(shè)置函數(shù)內(nèi)部的this對(duì)象以及給次函數(shù)傳遞變量。
call第一個(gè)參數(shù)被用來在函數(shù)內(nèi)部當(dāng)做this的對(duì)象,傳遞給函數(shù)的參數(shù)挨個(gè)傳遞
apply第一個(gè)參數(shù)也是被用來在函數(shù)內(nèi)部當(dāng)做this的對(duì)象,最后一個(gè)參數(shù)是傳遞給函數(shù)的值數(shù)組。

var user = {
    name: "No set",
    setUserName: function( name ){
        //setUserName是thiss對(duì)象的一個(gè)方法,待會(huì)作為回調(diào)函數(shù)用
        this.name = name + ' doctor';
    }
}

function getName( name, callback, callbackObj ){
    if( typeof callback === 'function' ){
        //這里指定了callback函數(shù)里面的this的指向是callbackObj
        callback.apply( callbackObj, [name] );
        //callback.call( callbackObj, name );    效果是一樣的
    }
}

getName( 'jack', user.setUserName, user );
console.log( user.name );    //jack doctor

(5)允許多重調(diào)用回調(diào)函數(shù)

//jquery 的$.ajax中
function successCb(){}
function completeCb(){}
function errorCb(){}

$.ajax({
    url: "http://testurl.com",
    success: successCb,
    complete: completeCb,
    error: errorCb
})

(6)避免“回調(diào)地獄”
有時(shí)候業(yè)務(wù)需求會(huì)產(chǎn)生多層回調(diào),就會(huì)出現(xiàn)下面的現(xiàn)象,也就是我們常說的“回調(diào)地獄”,導(dǎo)致代碼很不友好

    var p_client = new Db('integration_tests_20', new Server("127.0.0.1", 27017, {}), {'pk':CustomPKFactory});
    p_client.open(function(err, p_client) {
        p_client.dropDatabase(function(err, done) {
            p_client.createCollection('test_custom_key', function(err, collection) {
                collection.insert({'a':1}, function(err, docs) {
                    collection.find({'_id':new ObjectID("aaaaaaaaaaaa")}, function(err, cursor) {
                        cursor.toArray(function(err, items) {
                            test.assertEquals(1, items.length);

                            // Let's close the db
                            p_client.close();
                        });
                    });
                });
            });
        });
    });

面對(duì)“回調(diào)地獄”,有兩個(gè)建議:
a)少用匿名回調(diào)函數(shù),將回調(diào)函數(shù)先聲明好,在需要用到回調(diào)函數(shù)的時(shí)候,只傳遞函數(shù)名
b)將你的代碼封裝到一個(gè)模塊中,這樣你就可以利用這個(gè)模塊了,特別是大型項(xiàng)目,只需要導(dǎo)入改模塊就可以使用了。

五、總結(jié)

通過以上內(nèi)容的介紹,你應(yīng)該了解回調(diào)函數(shù)的相關(guān)情況了。我們可以看到,回調(diào)函數(shù)室友很多好處的,比如:

  • 避免重復(fù)代碼--在擁有更多多功能函數(shù)的地方實(shí)現(xiàn)更好的抽象
  • 讓代碼有更好地可維護(hù)性
  • 是代碼更容易閱讀
  • 編寫更多特定功能的函數(shù)

所以,在需要的時(shí)候大膽使用它吧,現(xiàn)在的web應(yīng)用有些地方經(jīng)常用到

  • 異步調(diào)用(例如讀取文件,進(jìn)行HTTP請(qǐng)求,等等)
  • 時(shí)間監(jiān)聽器/處理器
  • setTimeout和setInterval方法
  • 一般情況:精簡代碼
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容