有關JavaScript中回調函數的所有內容!

2021-03-02 大遷世界

本文已經過原作者 dmitripavlutin  授權翻譯!

回調函數是每個 JS 開發人員都應該知道的概念之一。回調用於數組,計時器函數,promise,事件處理程序等中。

在本文中,會解釋回調函數的概念。另外,還會幫助智米們區分兩種回調:同步和異步

1.回調函數

我們編寫一個問候的函數,首先創建一個函數greet(name),該函數返回歡迎消息:

function greet(name) {
  return `Hello, ${name}!`;
}

greet('小智'); // => 'Hello, 小智!'

如果要向一些人問候怎麼做?這裡,我們可以使用 array.map() 方法:

const persons = ['小智', '王大冶']
const messages = persons.map(greet)

messages // ["Hello, 小智!", "Hello, 王大冶!"]

persons.map(greet)接受person數組的每一項,並使用每一項作為調用參數來調用函數greet():greet('小智'),greet('王大冶')。

有趣的是persons.map(greet)方法接受greet()函數作為參數。這樣做會使reet()成為回調函數。

persons.map(greet)是一個接受另一個函數作為參數的函數,因此將其命名為高階函數

高階函數承擔調用回調函數的全部責任,並為其提供正確的參數。

在前面的示例中,高階函數persons.map(greet)負責調用greet()回調函數,並將數組的每個項目作為參數:'小智'和'王大冶'。

我們可以可以自己編寫使用回調的高階函數。例如,這裡有一個等價的array.map()方法

function map(array, callback) {
  const mappedArray = [];
  for (const item of array) { 
    mappedArray.push(
      callback(item)
    );
  }
  return mappedArray;
}

function greet(name) {
  return `Hello, ${name}!`;
}

const persons = ['小智', '王大冶']

const messages = map(persons, greet);

messages // ["Hello, 小智!", "Hello, 王大冶!"]

map(array, callback)是一個高階函數,因為它接受回調函數作為參數,然後在它的函數體內部調用回調函數:callback(item)。

2.同步回調

回調的調用方式有兩種:同步和異步回調。

同步回調是在使用回調的高階函數執行期間執行的。

換句話說,同步回調處於阻塞狀態:高階函數要等到回調完成執行後才能完成其執行。

function map(array, callback) {
  console.log('map() 開始');
  const mappedArray = [];
  for (const item of array) { mappedArray.push(callback(item)) }
  console.log('map() 完成');
  return mappedArray;
}

function greet(name) {
  console.log('greet() 被調用 ');
  return `Hello, ${name}!`;
}
const persons = ['小智'];

map(persons, greet);

// map() 開始
// greet() 被調用 
// map() 完成

greet()是一個同步回調函數,因為它與高階函數map()同時執行。

2.1 同步回調的例子

很多原生 JavaScript 類型的方法都使用同步回調。

最常用的是數組方法,例如array.map(callback),array.forEach(callback),array.find(callback),array.filter(callback),array.reduce(callback, init):

// 數組上的同步回調的示例

const persons = ['小智', '前端小智']
persons.forEach(
  function callback(name) {
    console.log(name);
  }
);
// 小智
// 前端小智

const nameStartingA = persons.find(
  function callback(name) {
    return name[0].toLowerCase() === '小';
  }
)
// nameStartingA // 小智

const countStartingA = persons.reduce(
  function callback(count, name) {
    const startsA = name[0].toLowerCase() === '小';
    return startsA ? count + 1 : count;
  }, 
  0
);

countStartingA // 1

3.異步回調

異步回調在執行高階函數之後執行。

簡而言之,異步回調是非阻塞的:高階函數無需等待回調即可完成其執行,高階函數可確保稍後在特定事件上執行回調。

在下面的示例中,later()函數的執行延遲為2秒

console.log('setTimeout() 開始')
setTimeout(function later() {
  console.log('later() 被調用')
}, 2000)
console.log('setTimeout() 完成')

// setTimeout() 開始
// setTimeout() 完成
// later() 被調用(2秒後)

3.1 異步回調的示例

計時器函數的異步回調:

setTimeout(function later() {
  console.log('2秒過去了!');
}, 2000);

setInterval(function repeat() {
  console.log('每2秒');
}, 2000);

DOM 事件監聽器也是異步調用事件處理函數(回調函數的一種子類型)

const myButton = document.getElementById('myButton');

myButton.addEventListener('click', function handler() {
  console.log('我被點擊啦!');
})
// 點擊按鈕時,才會列印'我被點擊啦!'

4. 異步回調函數 vs 異步函數

放在函數定義之前的特殊關鍵字async創建一個異步函數:

async function fetchUserNames() {
  const resp = await fetch('https://api.github.com/users?per_page=5');
  const users = await resp.json();
  const names = users.map(({ login }) => login);
  console.log(names);
}

fetchUserNames()是異步的,因為它的前綴是async。該函數await fetch('https://api.github.com/users?per_page=5')從 GitHub 前5個用戶。然後從響應對象中提取 JSON 數據:await resp.json()。

async函數是 Promise 的語法糖。當遇到表達式await <promise>時(注意,調用fetch()將返回一個 promise),異步函數將暫停執行直到該promise得以解決。

異步回調函數和異步函數是不同的術語。

異步回調函數由高階函數以非阻塞方式執行。但是異步函數在等待promise(await <promise>)解析時暫停其執行。

但是,我們可以將異步函數用作異步回調!

我們異步函數fetchUserNames()設為單擊按鈕時調用的異步回調:

const button = document.getElementById('fetchUsersButton');

button.addEventListener('click', fetchUserNames);

總結

回調是一個可以作為參數接受並由另一個函數(高階函數)執行的函數.

有兩種回調函數:同步和異步。

同步回調函數與使用回調函數的高階函數同時執行,同步回調是阻塞的。另一方面,異步回調的執行時間比高階函數的執行時間晚,異步回調是非阻塞的。

完~,感謝大家的觀看,我是小智,我去刷碗啦!

作者:Shadeed  譯者:前端小智 來源:dmitripavlutin原文:https://dmitripavlutin.com/javascript-variables-practices/


相關焦點

  • 【JavaScript】Promise函數的用法
    javascript
  • [翻譯]淺談JavaScript中的高階函數
    Array.prototype.mapmap()方法通過將輸入數組中的每個元素作為參數來調用提供的回調函數來創建一個新數組。 map()方法將從回調函數中獲取每個返回的值,並使用這些值創建一個新數組。傳遞給map()方法的回調函數接受3個參數:element,index和array。
  • 分享幾個javascript實用函數
    從本文開始小編將定期發布javascript相關的代碼集錦,每次發十個與大家分享,首先是數組篇,也許有人會說,可以用常用的lodash的等庫啊。源碼的人並不多吧,所以分享的代碼集錦權當一種學習了,首先開始的是數組篇,基於es6 規範allallEqual找出數組中滿足篩洗條件中的所有元素
  • JavaScript回調函數的使用心得(同步調用以及解耦合)
    二、回調函數關於回調函數的理解,可以參考徹底理解JavaScript中回調函數 (推薦)基於上一點中提到的先後順序的問題,另外還有一種情況也能達到效果,那就是回調函數。ajax有success成功的回調函數,將第二個請求放在第一個ajax的success中執行,也能保證順序,這是最常見回調函數。如果有幾個請求,每個請求都要依賴上一個請求的返回結果,全部都寫在回調函數success中,會怎麼樣呢?代碼是不是很龐大,可閱讀性會大大降低,那該怎麼做呢。
  • Koo.js加入回調函數支持,實現自定義功能擴展
    本次更新加入回調函數支持,通過回調函數實現自定義功能擴展,使用方法如下:<script type="text/javascript"> var callback = function () { alert('執行回調函數'); return false; } $(document
  • JavaScript 執行機制
    1.關於javascriptjavascript是一門單線程語言,在最新的HTML5中提出了Web-Worker,但javascript是單線程這一核心仍未改變。所以一切javascript版的"多線程"都是用單線程模擬出來的,一切javascript多線程都是紙老虎!
  • javascript 定時器工作原理
    說到 javascript 中的定時器,我們肯定會想到 setTimeout() 和 setInterval() 這兩個函數。
  • 關於JavaScript中的回調看這篇就夠了
    // 每日前端夜話 第463篇// 正文共:1600 字// 預計閱讀時間:8 分鐘回調函數是每個前端程式設計師都應該知道的概念之一。回調可用於數組、計時器函數、promise、事件處理中。persons.map(greet) 獲取 persons 數組的所有元素,並分別用每個元素作為調用參數來調用 greet() 函數:greet('Cristina'), greet('Ana')。
  • 這一次,徹底弄懂 JavaScript 執行機制
    而process.nextTick(callback)類似node.js版的"setTimeout",在事件循環的下一次循環中調用 callback 回調函數。接著執行所有的微任務。然後再次從宏任務開始,找到其中一個任務隊列執行完畢,再執行所有的微任務。
  • 重新認識javascript的settimeout和異步
    然後看了一下文章下面的評論,發現5樓和6樓的回答很有道理,主要意思就是說javascript引擎是單線程執行的,while循環那裡執行的時候,settimeout裡面的函數根本沒有執行的機會,這樣while那裡永遠為真,造成死循環。
  • javascript常用函數推薦
    繼續上一篇的內容,本文繼續javascript數組相關的常用函數推薦,基於ES6+規範,上一篇請查看這裡countOccurrences計算數組中值的出現次數。每次遇到數組內的特定值時,使用Array.prototype.reduce()遞增計數器。
  • JavaScript函數 - 事件驅動
    什麼是事件驅動函數? 最後給大家補充一個知識,叫做事件驅動函數,它到底是做什麼的呢?在頁面交互的過程中所調用的函數,該函數被稱之為事件驅動函數。現在先來簡單的了解一下,以後會詳細講到什麼是事件?和頁面交互的行為稱之為事件比如:滑鼠點擊某個按鈕時(onclick)、滑鼠浮動,或者滑鼠離開某一個區域(onmouseover、onmouseout)、文本框獲取焦點和失去焦點時(onfocus、onblur)等等如果我們想給一個按鈕綁定一個事件,就要通過事件驅動函數來綁定,並且通過id來找到它<script type = "text/javascript
  • C++類與回調函數
    從C的回調函數說起在C語言中,回調函數是一個非常重要的概念
  • javaScript入門—函數和document使用方法,新手必看!
    javaScript代碼只需要嵌入到html頁面中(頁面頭部、body,javaScript代碼大部分放在頁面頭部)就可以執行,也不需要任何額外的插件,大多數瀏覽器(firefox、ie、oprea、safari等)都可以直接運行javaScript代碼。上面代碼在瀏覽器中打開會顯示「hello !」的alert的對話框,如果沒有檢查一下瀏覽器是否啟用了javascript。
  • 走近 (javascript, 函數式)
    javascript 對函數式編程的支持在 javascript 中,函數是一等的,也就是說,在 javascript 中,函數本身也是一個值。;}someDoOuter(someDoParam);因為 javascript 中函數的一等性和高階性,使得函數可以作為語言中主要的工作單元,從而完整的支持函數式編程的特性。
  • 來一輪帶注釋的demo,徹底搞懂javascript中的replace函數
    javascript這門語言一直就像一位帶著面紗的美女,總是看不清,摸不透,一直專注伺服器端,也從來沒有特別重視過,直到最近幾年,javascript越來越重要,越來越通用。最近和前端走的比較近,藉此機會,好好鞏固一下相關知識點。
  • jquery中淡入淡出切換效果函數使用方法?
    jquery是javascript很常用和熱門的一個類庫,前端開發人員必須學習的一個js類庫,接下來來看看怎麼使用jquery中的怎麼切換使用淡入淡出方法。jquery最大的特點就是極大地簡化了 JavaScript 編程。而且使用起來也是很簡單的,比javascript更方便簡單。
  • [基礎] PHP回調函數及匿名函數,加深一下吧!
    1、回調函數PHP的回調函數其實和C、Java等語言的回調函數的作用是一模一樣的,都是在主線程執行的過程中,突然跳去執行設置的回調函數;回調函數執行完畢之後,再回到主線程處理接下來的流程。而在php調用回調函數,不想c以及java那樣直接使用函數名作為函數參數,而是在php中使用函數對應的字符串名稱執行<?
  • JavaScript日期函數 - 計時器、innerHTML
    (或匿名函數),毫秒數 ); 或者setInterval( function(){ } , 毫秒數 ); 只不過一個是寫了函數名,一個是直接定義了函數功能:每隔所傳參數的毫秒數,就調用一次所傳參數的函數返回值:當前頁面上對於這個定時器的唯一標識,定時器的ID有了定時器的ID我們就可以取消定時器,下面會講到舉個小例子:
  • 使用Promise模式來簡化JavaScript的異步回調
    如常見的 ajax 請求,需要在請求完成時響應操作,請求通常是異步的,請求的過程中用戶還能進行其他的操作,不會對頁面進行阻塞,這種異步的交互效果對用戶來說是挺有友好的。但是對於開發者來說,要大量處理這種操作,就很不友好了。異步請求完成的操作必須預先定義在回調函數中,等到請求完成就必須調用這個函數。