目錄
什麼是回呼或高階函數" >什麼是回呼或高階函數
回呼函數是怎麼運作的? " >回呼函數是怎麼運作的?
回呼函數是閉包" >回呼函數是閉包
實作回呼函數的基本原理" >實作回呼函數的基本原理
使用命名或匿名函數作為回呼" >使用命名或匿名函數作為回呼
传递参数给回调函数" >传递参数给回调函数
在执行之前确保回调函数是一个函数" >在执行之前确保回调函数是一个函数
使用this对象的方法作为回调函数时的问题" >使用this对象的方法作为回调函数时的问题
使用Call和Apply函数来保存this" >使用Call和Apply函数来保存this
允许多重回调函数" >允许多重回调函数
“回调地狱”问题以及解决方案" >“回调地狱”问题以及解决方案
创建你自己的回调函数" >创建你自己的回调函数
结束语" >结束语
首頁 web前端 js教程 詳解JavaScript中的回呼函數並使用它

詳解JavaScript中的回呼函數並使用它

Nov 03, 2020 pm 05:47 PM
javascript 回呼函數

詳解JavaScript中的回呼函數並使用它

在JavaScript中,函數是第一類對象,這表示函數可以像物件一樣按照第一類管理被使用。既然函數其實是物件:它們能被「儲存」在變數中,能作為函數參數被傳遞,能在函數中被創建,能從函數中傳回。

因為函數是第一類對象,我們可以在JavaScript使用回呼函數。在下面的文章中,我們將學到關於回調函數的方方面面。回呼函數可能是在JavaScript中使用最多的函數式程式設計技巧,雖然在字面上看起來它們一直一小段JavaScript或jQuery程式碼,但是對於許多開發者來說它任然是一個謎。閱讀本文之後你能了解如何使用回調函數。

回呼函數是從一個叫函數式程式設計的程式設計範式衍生出來的概念。簡單來說,函數式程式設計就是使用函數作為變數。函數式程式設計過去 - 甚至是現在,依舊沒有被廣泛使用 - 它過去常被看做是那些受過特許訓練的,大師級別的程式設計師的秘傳技巧。

幸運的是,函數是程式設計的技巧現在已經被充分闡明因此像我和你這樣的普通人也能去輕鬆使用它。函數式程式設計中的一個主要技巧就是回呼函數。在後面內容你會發現實作回呼函數其實就跟普通函數傳參一樣簡單。這個技巧是如此的簡單以致於我常常覺得很奇怪為什麼它經常被包含在講述JavaScript高級技巧的章節中。

什麼是回呼或高階函數

#一個回呼函數,也被稱為高階函數,是一個被當作參數傳遞給另一個函數(這裡我們把另一個函數叫做otherFunction)的函數,回呼函數在otherFunction中被呼叫。一個回呼函數本質上是一種程式模式(為常見問題所建立的解決方案),因此,使用回呼函數也稱為回呼模式。

下面是一個在jQuery中使用回呼函數簡單普遍的例子:

//注意到click方法中是一个函数而不是一个变量
//它就是回调函数
$("#btn_1").click(function() {
    alert("Btn 1 Clicked");
});
登入後複製

#正如你在前面的例子中看到的,我們將一個函數作為參數傳遞給了 click方法。 click方法會呼叫(或執行)我們傳遞給它的函數。這是JavaScript中回呼函數的典型用法,它在jQuery中廣泛被使用。

下面是另一個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方法。

到目前為止,我們將匿名函數作為參數傳遞給了另一個函數或方法。在我們看更多的實際範例和寫我們自己的回呼函數之前,先來理解回呼函數是如何運作的。

回呼函數是怎麼運作的?

因為函數在JavaScript中是第一類對象,我們像對待對像一樣對待函數,因此我們能像傳遞變數一樣傳遞函數,在函數中返回函數,在其他函數中使用函數。當我們將一個回呼函數作為參數傳遞給另一個函數是,我們只是傳遞了函數定義。我們並沒有在參數中執行函數。我們並沒有傳遞像我們平時執行函數一樣帶有一對執行小括號()的函數。

要注意的很重要的一點是回呼函數並不會馬上被執行。它會在包含它的函數內的某個特定時間點被「回呼」(就像它的名字一樣)。因此,即使第一個jQuery的例子如下所示:

//匿名函数不会再参数中被执行
//这是一个回调函数
$("#btn_1").click(function(){
    alert("Btn 1 Clicked");
});
登入後複製

這個匿名函數稍後會在函數體內被呼叫。即使有名字,它依然在包含它的函數內透過arguments物件取得。

回呼函數是閉包

都能夠將一個回呼函數當作變數傳遞給另一個函數時,這個回呼函數在包含它的函數內的某一點執行,就好像這個回呼函數是在包含它的函數中定義的一樣。這意味著回調函數本質上是一個閉包。

如我們所知,閉包能夠進入包含它的函數的作用域,因此回呼函數能取得包含它的函數中的變量,以及全域作用域中的變數。

實作回呼函數的基本原理

#回呼函數並不複雜,但是在我們開始建立並使用回呼函數之前,我們應該熟悉幾個實現回呼函數的基本原理。

使用命名或匿名函數作為回呼

#在前面的jQuery範例以及forEach的範例中,我們使用了在參數位置定義的匿名函數作為回調函數。這是在回調函數使用上的一種普遍的魔術。另一種常見的模式是定義一個命名函數並將函數名稱作為變數傳遞給函數。例如下面的例子:

//全局变量
var allUserData = [];

//普通的logStuff函数,将内容打印到控制台
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函数内被回调(或者执行)
getInput({name:"Rich",speciality:"Javascript"}, logStuff);
//name:Rich
//speciality:Javascript
登入後複製

传递参数给回调函数

既然回调函数在执行时仅仅是一个普通函数,我们就能给它传递参数。我们能够传递任何包含它的函数的属性(或者全局属性)作为回调函数的参数。在前面的例子中,我们将options作为一个参数传递给了回调函数。现在我们传递一个全局变量和一个本地变量:

//全局变量
var generalLastName = "Cliton";

function getInput (options, callback){
    allUserData.push (options);
    //将全局变量generalLastName传递给回调函数
    callback(generalLastName,options);
}
登入後複製

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

在调用之前检查作为参数被传递的回调函数确实是一个函数,这样的做法是明智的。同时,这也是一个实现条件回调函数的最佳时间。

我们来重构上面例子中的getInput函数来确保检查是恰当的。

function getInput(options, callback){
    allUserData.push(options);
    
    //确保callback是一个函数
    if(typeof callback === "function"){
        //调用它,既然我们已经确定了它是可调用的
        callback(options);
    }
}
登入後複製

如果没有适当的检查,如果getInput的参数中没有一个回调函数或者传递的回调函数事实上并不是一个函数,我们的代码将会导致运行错误。

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

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

我们在下面的代码中说明:

//定义一个拥有一些属性和一个方法的对象 //我们接着将会把方法作为回调函数传递给另一个函数

var clientData = {
    id: 094545,
    fullName "Not Set",
    //setUsrName是一个在clientData对象中的方法
    setUserName: fucntion (firstName, lastName){
        //这指向了对象中的fullName属性
        this.fullName = firstName + " " + lastName;
    }
} 

function getUserInput(firstName, lastName, callback){
    //在这做些什么来确认firstName/lastName
    //现在存储names
    callback(firstName, lastName);
}
登入後複製

在下面你的代码例子中,当clientData.setUsername被执行时,this.fullName并没有设置clientData对象中的fullName属性。相反,它将设置window对象中的fullName属性,因为getUserInput是一个全局函数。这是因为全局函数中的this对象指向window对象。

getUserInput("Barack","Obama",clientData.setUserName);
console.log(clientData,fullName);  //Not Set
//fullName属性将在window对象中被初始化     
console.log(window.fullName);  //Barack Obama
登入後複製

使用Call和Apply函数来保存this

我们可以使用Call或者Apply函数来修复上面你的问题。到目前为止,我们知道了每个JavaScript中的函数都有两个方法:CallApply。这些方法被用来设置函数内部的this对象以及给此函数传递变量。

call接收的第一个参数为被用来在函数内部当做this的对象,传递给函数的参数被挨个传递(当然使用逗号分开)。Apply函数的第一个参数也是在函数内部作为this的对象,然而最后一个参数确是传递给函数的值的数组。

听起来很复杂,那么我们来看看使用ApplyCall有多么的简单。为了修复前面例子的问题,我将在下面你的例子中使用Apply函数:

//注意到我们增加了新的参数作为回调对象,叫做“callbackObj”
function getUserInput(firstName, lastName, callback. callbackObj){
    //在这里做些什么来确认名字
    callback.apply(callbackObj, [firstName, lastName]);
}
登入後複製

使用Apply函数正确设置了this对象,我们现在正确的执行了callback并在clientData对象中正确设置了fullName属性:

//我们将clientData.setUserName方法和clientData对象作为参数,clientData对象会被Apply方法使用来设置this对象     
getUserName("Barack", "Obama", clientData.setUserName, clientData);

//clientData中的fullName属性被正确的设置
console.log(clientUser.fullName); //Barack Obama
登入後複製

我们也可以使用Call函数,但是在这个例子中我们使用Apply函数。

允许多重回调函数

我们可以将不止一个的回调函数作为参数传递给一个函数,就像我们能够传递不止一个变量一样。这里有一个关于jQuery中AJAX的例子:

function successCallback(){
    //在发送之前做点什么
}
  
function successCallback(){
//在信息被成功接收之后做点什么
}

function completeCallback(){
//在完成之后做点什么
}

function errorCallback(){
    //当错误发生时做点什么
}

$.ajax({
    url:"http://fiddle.jshell.net/favicon.png",
    success:successCallback,
    complete:completeCallback,
    error:errorCallback
});
登入後複製

“回调地狱”问题以及解决方案

在执行异步代码时,无论以什么顺序简单的执行代码,经常情况会变成许多层级的回调函数堆积以致代码变成下面的情形。这些杂乱无章的代码叫做回调地狱因为回调太多而使看懂代码变得非常困难。我从node-mongodb-native,一个适用于Node.js的MongoDB驱动中拿来了一个例子。这段位于下方的代码将会充分说明回调地狱:

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();
                    });
                });
            });
        });
    });
});
登入後複製

你应该不想在你的代码中遇到这样的问题,当你当你遇到了

  • 你将会时不时的遇到这种情况

  • 这里有关于这个问题的两种解决方案。

  • 给你的函数命名并传递它们的名字作为回调函数,而不是主函数的参数中定义匿名函数。

  • 模块化L将你的代码分隔到模块中,这样你就可以到处一块代码来完成特定的工作。然后你可以在你的巨型应用中导入模块。

创建你自己的回调函数

既然你已经完全理解了关于JavaScript中回调函数的一切(我认为你已经理解了,如果没有那么快速的重读以便),你看到了使用回调函数是如此的简单而强大,你应该查看你的代码看看有没有能使用回调函数的地方。回调函数将在以下几个方面帮助你:

  • 避免重复代码(DRY-不要重复你自己)

  • 在你拥有更多多功能函数的地方实现更好的抽象(依然能保持所有功能)

  • 让代码具有更好的可维护性

  • 使代码更容易阅读

  • 编写更多特定功能的函数

创建你的回调函数非常简单。在下面的例子中,我将创建一个函数完成以下工作:读取用户信息,用数据创建一首通用的诗,并且欢迎用户。这本来是个非常复杂的函数因为它包含很多if/else语句并且,它将在调用那些用户数据需要的功能方面有诸多限制和不兼容性。

相反,我用回调函数实现了添加功能,这样一来获取用户信息的主函数便可以通过简单的将用户全名和性别作为参数传递给回调函数并执行来完成任何任务。

简单来讲,getUserInput函数是多功能的:它能执行具有无种功能的回调函数。

//首先,创建通用诗的生成函数;它将作为下面的getUserInput函数的回调函数

function genericPoemMaker(name, gender) {
    console.log(name + " is finer than fine wine.");
    console.log("Altruistic and noble for the modern time.");
    console.log("Always admirably adorned with the latest style.");
    console.log("A " + gender + " of unfortunate tragedies who still manages a perpetual smile");
}

//callback,参数的最后一项,将会是我们在上面定义的genericPoemMaker函数
function getUserInput(firstName, lastName, gender, callback) {
    var fullName = firstName + " " + lastName;
    // Make sure the callback is a function
    if (typeof callback === "function") {
    // Execute the callback function and pass the parameters to it
    callback(fullName, gender);
    }
}
登入後複製

调用getUserInput函数并将genericPoemMaker函数作为回调函数:

getUserInput("Michael", "Fassbender", "Man", genericPoemMaker);
// 输出
/* Michael Fassbender is finer than fine wine.
Altruistic and noble for the modern time.
Always admirably adorned with the latest style.
A Man of unfortunate tragedies who still manages a perpetual smile.
*/
登入後複製

因为getUserInput函数仅仅只负责提取数据,我们可以把任意回调函数传递给它。例如,我们可以传递一个greetUser函数:

unction greetUser(customerName, sex)  {
    var salutation  = sex && sex === "Man" ? "Mr." : "Ms.";
    console.log("Hello, " + salutation + " " + customerName);
}

// 将greetUser作为一个回调函数
getUserInput("Bill", "Gates", "Man", greetUser);

// 这里是输出
Hello, Mr. Bill Gates
登入後複製

我们调用了完全相同的getUserInput函数,但是这次完成了一个完全不同的任务。

正如你所见,回调函数很神奇。即使前面的例子相对简单,想象一下能节省多少工作量,你的代码将会变得更加的抽象,这一切只需要你开始使用毁掉函数。大胆的去使用吧。

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

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

  • 时间监听器/处理器

  • setTimeoutsetInterval方法

  • 一般情况:精简代码

结束语

JavaScript回调函数非常美妙且功能强大,它们为你的Web应用和代码提供了诸多好处。你应该在有需求时使用它;或者为了代码的抽象性,可维护性以及可读性而使用回调函数来重构你的代码。

推荐教程:《JavaScript视频教程

以上是詳解JavaScript中的回呼函數並使用它的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.聊天命令以及如何使用它們
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

如何使用WebSocket和JavaScript實現線上語音辨識系統 如何使用WebSocket和JavaScript實現線上語音辨識系統 Dec 17, 2023 pm 02:54 PM

如何使用WebSocket和JavaScript實現線上語音辨識系統引言:隨著科技的不斷發展,語音辨識技術已成為了人工智慧領域的重要組成部分。而基於WebSocket和JavaScript實現的線上語音辨識系統,具備了低延遲、即時性和跨平台的特點,成為了廣泛應用的解決方案。本文將介紹如何使用WebSocket和JavaScript來實現線上語音辨識系

WebSocket與JavaScript:實現即時監控系統的關鍵技術 WebSocket與JavaScript:實現即時監控系統的關鍵技術 Dec 17, 2023 pm 05:30 PM

WebSocket與JavaScript:實現即時監控系統的關鍵技術引言:隨著互聯網技術的快速發展,即時監控系統在各個領域中得到了廣泛的應用。而實現即時監控的關鍵技術之一就是WebSocket與JavaScript的結合使用。本文將介紹WebSocket與JavaScript在即時監控系統中的應用,並給出程式碼範例,詳細解釋其實作原理。一、WebSocket技

如何利用JavaScript和WebSocket實現即時線上點餐系統 如何利用JavaScript和WebSocket實現即時線上點餐系統 Dec 17, 2023 pm 12:09 PM

如何利用JavaScript和WebSocket實現即時線上點餐系統介紹:隨著網路的普及和技術的進步,越來越多的餐廳開始提供線上點餐服務。為了實現即時線上點餐系統,我們可以利用JavaScript和WebSocket技術。 WebSocket是一種基於TCP協定的全雙工通訊協議,可實現客戶端與伺服器的即時雙向通訊。在即時線上點餐系統中,當使用者選擇菜餚並下訂單

java回呼函數怎麼寫 java回呼函數怎麼寫 Jan 09, 2024 pm 02:24 PM

java回調函數的寫法有:1、接口回調,定義一個接口,其中包含一個回調方法,在需要觸發回調的地方,使用該接口作為參數,並在合適的時機調用回調方法;2、匿名內部類回調,可以使用匿名內部類別來實現回調函數,避免創建額外的實作類別;3、Lambda表達式回調,在Java 8以上版本中,可以使用Lambda表達式來簡化回調函數的寫法等。

如何使用WebSocket和JavaScript實現線上預約系統 如何使用WebSocket和JavaScript實現線上預約系統 Dec 17, 2023 am 09:39 AM

如何使用WebSocket和JavaScript實現線上預約系統在當今數位化的時代,越來越多的業務和服務都需要提供線上預約功能。而實現一個高效、即時的線上預約系統是至關重要的。本文將介紹如何使用WebSocket和JavaScript來實作一個線上預約系統,並提供具體的程式碼範例。一、什麼是WebSocketWebSocket是一種在單一TCP連線上進行全雙工

JavaScript與WebSocket:打造高效率的即時天氣預報系統 JavaScript與WebSocket:打造高效率的即時天氣預報系統 Dec 17, 2023 pm 05:13 PM

JavaScript和WebSocket:打造高效的即時天氣預報系統引言:如今,天氣預報的準確性對於日常生活以及決策制定具有重要意義。隨著技術的發展,我們可以透過即時獲取天氣數據來提供更準確可靠的天氣預報。在本文中,我們將學習如何使用JavaScript和WebSocket技術,來建立一個高效的即時天氣預報系統。本文將透過具體的程式碼範例來展示實現的過程。 We

簡易JavaScript教學:取得HTTP狀態碼的方法 簡易JavaScript教學:取得HTTP狀態碼的方法 Jan 05, 2024 pm 06:08 PM

JavaScript教學:如何取得HTTP狀態碼,需要具體程式碼範例前言:在Web開發中,經常會涉及到與伺服器進行資料互動的場景。在與伺服器進行通訊時,我們經常需要取得傳回的HTTP狀態碼來判斷操作是否成功,並根據不同的狀態碼來進行對應的處理。本篇文章將教你如何使用JavaScript來取得HTTP狀態碼,並提供一些實用的程式碼範例。使用XMLHttpRequest

Java中回呼函數的基本語法與應用 Java中回呼函數的基本語法與應用 Jan 30, 2024 am 08:12 AM

Java回呼函數的基本寫法和使用方法引言:在Java程式設計中,回呼函數是一種常見的程式模式,透過回呼函數,可以將某個方法作為參數傳遞給另一個方法,從而實現方法的間接呼叫。回調函數的使用,在事件驅動、非同步程式設計和介面實作等場景中非常常見。本文將介紹Java回呼函數的基本寫法和使用方法,並提供具體的程式碼範例。一、回呼函數的定義回呼函數是一種特殊的函數,它可以作為參數

See all articles