目錄
什麼是回呼? " >什麼是回呼?
為什麼要使用回呼? " >為什麼要使用回呼?
为什么回调如此重要?" >为什么回调如此重要?
回调地狱" >回调地狱
结语" >结语
首頁 web前端 js教程 了解JS中的回調

了解JS中的回調

Oct 21, 2020 pm 05:47 PM
javascript 回調

了解JS中的回調

你有無意間看到 "callback" 但不知道其中的意思麼?不用擔心。不是只有你一個人這樣。很多JavaScript 新手都難以理解回調。

雖然回呼比較令人困惑,你仍然需要徹底的學習理解它們,因為它在 JavaScript 中是一個很關鍵的概念。如果你不知道回調,那麼你無法走的長遠。

這就是今天這篇文章要講解的!你將要學習什麼是回調以及為什麼它們如此重要和怎麼去使用。

這篇文章你會看到 ES6 裡的箭頭函數。如果你還不熟悉它們,我建議你先看看ES6 post。 (只要閱讀箭頭函數部分)。

什麼是回呼?

回呼是一個函數,會作為一個參數傳遞到另一個函數中,並且稍後去執行。 (開發人員說在執行函數時呼叫另一個函數,這就是為什麼 callbacks 稱之為回調的原因)。

它們在 JavaScript 中很常見,以至於你可能不知道它們是回呼函數的時候已經使用過它們。

一個可以接收回呼函數的例子是addEventLisnter

const button = document.querySelector('button')
button.addEventListener('click', function(e) {
    // Adds clicked class to button
    this.classList.add('clicked')
})
登入後複製

沒看出來這是個回呼?來看看下個例子。

const button = document.querySelector('button')

// Function that adds 'clicked' class to the element
function clicked (e) {
    this.classList.add('clicked')
}

// Adds click function as a callback to the event listener
button.addEventListener('click', clicked)
登入後複製

這裡,我們透過 JavaScript 給一個按鈕綁定了click事件。一旦偵測到了點擊時間,JavaScript 就會執行clicked函數。所以,在這個範例中,當addEventListener函數接收一個回呼函數時,clicked就是一個回呼。

現在知道回呼是什麼了麼? :)

我們來看看另外一個例子。這次,我們假設你想過濾一個數字數組來得到一個小於5的列表。這裡,你給filter函數傳遞了一個回呼函數。

const numbers = [3, 4, 10, 20]
const lesserThanFive = numbers.filter(num => num < 5)
登入後複製

現在,如果你把上面的程式碼用具名函數改一下,那麼過濾數組就會變成這樣:

const numbers = [3, 4, 10, 20]
const getLessThanFive = num => num < 5

// Passing getLessThanFive function into filter
const lesserThanFive = numbers.filter(getLessThanFive)
登入後複製

在這個例子中,getLessThanFive是一個回調。 Array.filter是一個可以接收回呼的函數。

現在看看?當你知道回調後會發現無所不在。

下面這個例子告訴你怎麼寫一個回呼函數和一個可以接收回呼的函數。

// Create a function that accepts another function as an argument
const callbackAcceptingFunction = (fn) => {
    // Calls the function with any required arguments
    return fn(1, 2, 3)
}

// Callback gets arguments from the above call
const callback = (arg1, arg2, arg3) => {
    return arg1 + arg2 + arg3
}

// Passing a callback into a callback accepting function
const result = callbackAcceptingFunction(callback)
console.log(result) // 6
登入後複製

請注意,當你把回呼傳給另一個函數時,只是把引用傳遞過去了(不執行,因此沒有()

`const result = callbackAcceptingFunction(callback)`
登入後複製

你只能在callbackAcceptingFunction裡呼叫這個回呼當你這麼做時,你可以給這個回呼函數傳遞可能需要任意數量的參數:

const callbackAcceptingFunction = (fn) => {
    // Calls the callback with three args
    fn(1, 2, 3)
}
登入後複製

這些參數透過callbackAcceptingFunction傳遞到回呼裡,然後用它們的方式在回調裡進行傳遞:

// Callback gets arguments from callbackAcceptingFunction
const callback = (arg1, arg2, arg3) => {
    return arg1 + arg2 + arg3
}
登入後複製

這是一個回呼的結構。現在,你知道了addEventListener包含了event參數:

// Now you know where this event object comes from! :)
button.addEventListener(&#39;click&#39;, (event) => {
    event.preventDefault()
})
登入後複製

唷!這是回調的基本意義!只要記住關鍵字:將一個函數傳遞到另一個函數中,你將回想起上面提到的機制。

這種傳遞函數的能力是一件很大的事情。它是如此之大,以至於 JavaScript 中的函數都是高階函數。高階函數是函數式程式設計範式中非常重要的東西。

但我們現在不討論這個話題。現在,我確信你已經知道了回調以及如何使用了。但是,為什麼需要使用回調呢?

為什麼要使用回呼?

回呼有二種不同的使用方式 - 在同步函數和在非同步函數中。

同步函數中的回呼

如果你的程式碼執行是一個從上到下,從做到右的方式,順序地,在下一行程式碼執行前會等到程式碼執行完成,那麼你的程式碼是同步的。

我們來看個例子,以便於更早的理解:

const addOne = (n) => n + 1
addOne(1) // 2
addOne(2) // 3
addOne(3) // 4
addOne(4) // 5
登入後複製

在上面的例子中,addOne(1)先執行。執行完成時,addOne(2)開始執行。當addOne(2)執行完成時,addOne(3)開始執行。這個過程一直執行到最後一行程式碼被執行。

但你想讓一部分程式碼跟其他交換簡單時,這時候可以在同步的函數裡使用回呼。

所以,回到上面的Array.filter例子,雖然過濾數組讓其包含小於5的數字,同樣地你也可以重複使用 Array.filter去包含大於10 的數字。

const numbers = [3, 4, 10, 20]
const getLessThanFive = num => num < 5
const getMoreThanTen = num => num > 10

// Passing getLessThanFive function into filter
const lesserThanFive = numbers.filter(getLessThanFive)

// Passing getMoreThanTen function into filter
const moreThanTen = numbers.filter(getMoreThanTen)
登入後複製

這是你為什麼在同步函數中使用回呼。現在,讓我們繼續看看為什麼我們在非同步函數裡使用回調。

非同步函數裡的回呼

#

这里异步的意思是,如果 JavaScript 需要等待某个东西完成,在等待的过程中会执行其余的任务。

一个异步函数例子就是setTimeout。它会一段时间后执行回调函数。

// Calls the callback after 1 second
setTimeout(callback, 1000)
登入後複製

如果你给JavaScript 另一个任务去完成时我们看看setTimeout是怎么工作的:

const tenSecondsLater = _ = > console.log(&#39;10 seconds passed!&#39;)

setTimeout(tenSecondsLater, 10000)
console.log(&#39;Start!&#39;)
登入後複製

在上面的代码里,JavaScript 去执行setTimeout。这时,会等待10秒且打印日志“10 seconds passed!”。

同时,在等到10秒去执行setTimeout时,JavaScript 会执行console.log("Start!")

因此,如果你记录上面的代码,你会看到这一点。

// What happens:
// > Start! (almost immediately)
// > 10 seconds passed! (after ten seconds)
登入後複製

啊。异步操作听起来很复杂,不是么?但是我们为什么在 JavaScript 里到处使用呢?

要理解为什么异步操作很重要,想象一下 JavaScript 是你家里的一个机器人助手。这个助手很蠢。一次只能做一件事情。(这个行为称之为单线程)。

假设你告诉机器人助手帮你订点披萨。但是机器人助手如此蠢,在给披萨店打完电话后,机器人助手坐在你家门前,慢慢的等待披萨送来。在这个过程中不能做任何其他的事情。

等待的过程中,你不能让它去熨烫衣服,拖地板以及其他任何事情。你需要等20分钟,直到披萨送来,才愿意做其他的事情。

这个行为称之为阻塞。在等待一个任务执行完全之前,其他的操作被阻止了。

const orderPizza = flavour => {
    callPizzaShop(`I want a ${flavour} pizza`)
    waits20minsForPizzaToCome() // Nothing else can happen here
    bringPizzaToYou()
}

orderPizza(&#39;Hawaiian&#39;)

// These two only starts after orderPizza is completed
mopFloor()
ironClothes()
登入後複製

现在,阻塞操作是非常令人失望的。

为什么?

我们把愚蠢的机器人助手放在浏览器的运行环境里。想象一下,当按钮被点击时需要改变按钮的颜色。

那这个愚蠢的机器人会怎么做呢?

它会凝视着这个按钮,在按钮被点击之前,忽略掉其他任何的命令。同时,用户不能选择其他任何东西。看看现在这样的情况?这就是异步编程在 JavaScript 为什么如此重要。

但是真正理解在异步操作过程中发生了什么,我们需要理解另外一个东西-事件循环。

事件循环

想象事件循环,可以想象 JavaScript 是一个 todo-list 的管家。这个列表包含了所有你告诉它的事情。JavaScript 会按照你给的顺序,一步步的遍历这个列表。

假设你给JavaScript 的5个命令如下:

const addOne = (n) => n + 1

addOne(1) // 2
addOne(2) // 3
addOne(3) // 4
addOne(4) // 5
addOne(5) // 6
登入後複製

这将会出现在 JavaScript 的todo 列表里。

了解JS中的回調

命令在 JavaScript 的 todo 列表里同步显示。

除了 todo 列表,JavaScript 还保存了一个 waiting 列表,这个列表可以跟踪需要等待的东西。如果你告诉 JavaScript 需要定披萨,它会给披萨店打电话,并把"等待披萨送来"加到等到列表里。同时,它会做 todo 列表已经有的事情。

所以,想象一下有这样的代码。

const orderPizza (flavor, callback) {
    callPizzaShop(`I want a ${flavor} pizza`)
    // Note: these three lines is pseudo code, not actual JavaScript
    whenPizzaComesBack {
        callback()
    }
}

const layTheTable = _ => console.log(&#39;laying the table&#39;)

orderPizza(&#39;Hawaiian&#39;, layTheTable)
mopFloor()
ironClothes()
登入後複製

JavaScript 的初始列表将会是:

了解JS中的回調

定披萨,拖地和熨烫衣服!

这是,当执行到orderPizza,JavaScript 知道需要等待披萨送来。因此,在把"等待披萨送来"加到等待列表中的同时会处理剩下的工作。

了解JS中的回調

JavaScript 等待披萨到达。

当披萨送到时,按门铃会通知 JavaScript并做一个标记,当处理完其他杂事时,会去执行layTheTable

了解JS中的回調

JavaScript 知道通过标记里的命令需要去执行layTheTable

然后,一旦处理完了其他的杂务,JavaScript 就会执行回调函数layTheTable

了解JS中的回調

当其他一切都完成时, JavaScript 会将其放置。

这就是我的朋友,事件循环。你可以用事件循环中的实际关键字来替代我们的巴特勒类比来理解所有的事情。

  • Todo-list-> Call stack

  • Waiting-list-> Web apis

  • Mental note-> Event queue

了解JS中的回調

JavaScript 事件循环

如果你有20分钟空闲时间的话,我强烈推荐你看Philip Roberts在 JSConf 上关于事件循环的演讲。它会帮助你了解事件循环里的细节。

为什么回调如此重要?

哦。我们在事件循环上转了个大圈。现在我们回头来看。

之前,我们提到如果 JavaScript 专注地盯着一个按钮并忽略其他所有的命令,这是非常糟糕的。是吧?

通过异步回调,我们可以提前给 JavaScript 指令而不需要停止整个操作。

现在,当你让 JavaScript 监听一个按钮的点击事件时,它将"监听按钮"放在等待列表里,然后继续做家务。当按钮最终获取到点击事件时,JavaScript 会激活回调,然后继续运行

下面是一些常见的回调函数,告诉 JavaScript 应该怎么做:

  • 当事件被触发(比如:addEventListener

  • Ajax 执行之后(比如:jQuery.ajax

  • 文件读写之后(比如:fs.readFile)

// Callbacks in event listeners
document.addEventListener(button, highlightTheButton)
document.removeEventListener(button, highlightTheButton)

// Callbacks in jQuery&#39;s ajax method
$.ajax(&#39;some-url&#39;, {
    success (data) { /* success callback */ },
    error (err) { /* error callback */}
});

// Callbacks in Node
fs.readFile(&#39;pathToDirectory&#39;, (err, data) => {
    if (err) throw err
    console.log(data)
})

// Callbacks in ExpressJS
app.get(&#39;/&#39;, (req, res) => res.sendFile(index.html))
登入後複製

这就是回调!

希望,你现在已经弄清楚了回调是什么并且怎么去使用。在最开始的时候,你没必要创建很多的回调,更多的去专注于学习如何使用可用的回调函数。

现在,在结束之前,我们来看看回调的第一个问题 - 回调地狱

回调地狱

回调地狱是在多个回调嵌套出现时的一个现象。它发生在一个异步回调执行依赖上一个异步回调执行的时候。这些嵌套的回调会导致代码非常难以理解。

在我的经验里,你只会在 Node.js 里看到回调地狱。当你的 JavaScript 在前台运行时一般都不会遇到回调地狱。

这里有一个回调地狱的例子:

// Look at three layers of callback in this code!
app.get(&#39;/&#39;, function (req, res) {
    Users.findOne({ _id:req.body.id }, function (err, user) {
        if (user) {
            user.update({/* params to update */}, function (err, document) {
            res.json({user: document})
        })
        } else {
            user.create(req.body, function(err, document) {
                res.json({user: document})
            })
        }
     })
})
登入後複製

现在,对你来说,解读上面的代码是一个挑战。相当的难,不是么?难怪在看到嵌套回调时,开发人员会不寒而栗。

解决回调的一个解决方案是将回调函数分解成更小的部分,以减少嵌套代码的数量

const updateUser = (req, res) => {
    user.update({/* params to update */}, function () {
        if (err) throw err;
        return res.json(user)
    })
}

const createUser = (req, res, err, user) => {
    user.create(req.body, function(err, user) {
        res.json(user)
    })
}

app.get(&#39;/&#39;, function (req, res) {
    Users.findOne({ _id:req.body.id }, (err, user) => {
        if (err) throw err
        if (user) {
            updateUser(req, res)
        } else {
            createUser(req, res)
        }
    })
})
登入後複製

阅读起来容易得多,不是么?

在新的JavaScript 版本里,还有一些新的解决回调地狱的方法,比如: promisesasync/await。但是,会在另一个话题中解析它们。

结语

今天,我们学习了什么是回调,为什么会如此重要以及如何去使用它们。同时学习到了什么是回调地狱,以及如何避免。希望,回调不会让你感到害怕。

关于回调你还有其他的问题么?如果你有的话,请在下面留言,我会尽快回复你的。

相关免费学习推荐:js视频教程

以上是了解JS中的回調的詳細內容。更多資訊請關注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脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

<🎜>:泡泡膠模擬器無窮大 - 如何獲取和使用皇家鑰匙
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
北端:融合系統,解釋
4 週前 By 尊渡假赌尊渡假赌尊渡假赌
Mandragora:巫婆樹的耳語 - 如何解鎖抓鉤
3 週前 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)

熱門話題

Java教學
1670
14
CakePHP 教程
1428
52
Laravel 教程
1329
25
PHP教程
1273
29
C# 教程
1256
24
如何使用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協定的全雙工通訊協議,可實現客戶端與伺服器的即時雙向通訊。在即時線上點餐系統中,當使用者選擇菜餚並下訂單

如何使用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

javascript如何使用insertBefore javascript如何使用insertBefore Nov 24, 2023 am 11:56 AM

用法:在JavaScript中,insertBefore()方法用於在DOM樹中插入一個新的節點。這個方法需要兩個參數:要插入的新節點和參考節點(即新節點將要插入的位置的節點)。

如何在JavaScript中取得HTTP狀態碼的簡單方法 如何在JavaScript中取得HTTP狀態碼的簡單方法 Jan 05, 2024 pm 01:37 PM

JavaScript中的HTTP狀態碼取得方法簡介:在進行前端開發中,我們常常需要處理與後端介面的交互,而HTTP狀態碼就是其中非常重要的一部分。了解並取得HTTP狀態碼有助於我們更好地處理介面傳回的資料。本文將介紹使用JavaScript取得HTTP狀態碼的方法,並提供具體程式碼範例。一、什麼是HTTP狀態碼HTTP狀態碼是指當瀏覽器向伺服器發起請求時,服務

See all articles