上集聊完 DOM 基本概念和 JS 如何與 DOM 互動之後
Ex: innerHTML() , createElement() , appendChild() 等等
這集要來和大家介紹新手朋友看到就頭痛的「Callback function 回呼函式」,大家準備好了嗎?
看過第一、二集學習日記的朋友們應該都清楚:凡是在 JS 中所見即「物件」。所以,當我說函式 (function) 也是物件時,你應該覺得跟呼吸一樣自然🤙
但是,你有想過函式也能如同數值一般,作爲參數 (argument) 被傳遞到另一個函式中嗎?
如果沒有,小白介紹給你知道😍 這個看似很酷(被傳來傳去)的函式就是鼎鼎大名的 –– 「回呼函式 (callback function)」!
如果以上沒聽懂,就讓以下範例來讓你對回呼函式有深刻的認識吧~ Go go!
這是一般的函式的標準長相:
function functionName(param1, param2) { // 其中放入待執行程式碼 或是 return 返回值 }
🦦 小複習:function 可以擁有名字,也可為匿名函式。除此之外,傳入函式的參數 (argument/ parameter) 如果超過一項,需以逗號分隔,同時注意參數的排列具順序、對應性。
但如果今天有兩個函式需要運行… 情況會是如何?
第一個函式:
function firstFunc(x) {
alert(x+1);
}
第二個函式:
function secondFunc(y, z) {
… // 一些功能
}
若小白🙆♀️想先運行第二個函式後執行第一個,小白可以使用回呼這樣寫~
function firstFunc(x) { alert(x+1); } function secondFunc(y, callback) { callback(y); } secondFunc(1, firstFunc); // 呼叫 secondFunc 函式
運作方式: 🔑 務必行行理解,並能說出每一行的用途與先後順序
現在想像你是電腦(由上而下、由左而右掃視)
- 第一眼你看到了兩個函式 >> 你心理有數知道要幹嘛了
- 再瞥了一眼 secondFunc(1, firstFunc); ,你瞬間接收到 secondFunc 要被執行的訊號,這時你開始執行 secondFunc
- 仔細把 secondFunc 的參數看過後,發現裡頭有一個 callback function,也就是我們的 firstFunc
- 將secondFunc 中傳入的值依順序帶入參數,這時我們會發現到:callback(y); 意同 callback(1); 也等同 firstFunc(1)
- 經以上解析後,你轉而執行 alert(1+1); 並跳出內容為 “2” 的對話視窗
用 Codepen 操作會有以上結果👆
小結:回呼函式 (callback function) 本質上與一般函式相同,差別僅在於「被呼叫執行的時機」。更精確地說,回呼函式為「在一支函式執行後,才開始執行的函式」
常見的回呼函式有 2 種:
第一種為用於控制時間與製作計時器的函式
- setTimeout
顧名思義:set (設定)timeout (「時間到」),意即:設定何時時間到
- 功能:延遲了某段時間後,才去執行的指定的程式碼
👉 第一個參數為時間到時要執行的程式,第二個參數為延遲時間(單位:毫秒)
- 特性:程式僅執行一次、不重複(若要重複請見以下 setInterval)
- 時間單位:毫秒 (milliseconds)
🦦 補: 1 秒 = 1000 毫秒
- 清除:可用 clearTimeout() 來取消還未執行的 setTimeout() 功能
註:setTimeout() 中回傳的 ID 必須作為 clearTimeout() 的參數才能終止 setTimeout() 的設定
範例:
<button onclick="irisGreeting()">Interact with Iris!</button> <script> function irisGreeting() { setTimeout(function() { alert("Hola"); }, 3000); } </script>
解說:點擊 button 後三秒電腦會自動彈出寫有「Hola」 對話視窗,視窗關閉後不再顯示
- setInterval
顧名思義:set (設定)interval (間隔),意即:設定時間的間隔
- 功能:延遲固定某段時間後,不斷執行指定的程式碼
👉 第一個參數為時間到時要重複執行的程式,第二個參數為延遲時間(單位:毫秒)
- 特性:間隔一段時間重複進行
- 時間單位:毫秒 (milliseconds)
- 清除:當還未使用 clearInterval() 之前,setInterval() 中的指定程式都會不斷重複執行
註:若要使用 clearInterval(),需事先把 interval 擺入變數中,其後若要終止執行時再將該變數帶入 clearInterval 中作為參數即可
範例:
<button onclick="irisGreeting()">Interact with Iris!</button> <script> function irisGreeting() { setInterval(function() { alert("Hola"); }, 3000); } </script>
解說:點擊 button 後三秒電腦會自動彈出寫有「Hola」 對話視窗,視窗關閉後三秒後又會跳出,不斷循環(寫 setInterval 務必小心~ 如果沒有搭配 clearInterval 容易當機唷)
第二種為事件監聽
什麼是事件監聽?
概念很簡單,就是監聽一個事件(有講等於沒講嘛!?😂)別急著罵我,先動動腦想想在我們使用電腦或是觸控設備(手機、平板)時,有哪些行為?
對~沒錯👍 答案有太多 說都說不完,姑且讓我簡單說幾個:滑鼠點擊、游標滑動、按下按鈕、切換頁面… 太多太多
我們統稱「滑鼠點擊、游標滑動、按下按鈕、切換頁面…」這些行為「事件」。因此,監聽事件就是觀察使用者的一舉一動,當使用者作出某些行為時,電腦方執行相對應的操作。
事件監聽對象有… element、document 和 window
事件監聽的寫法範例:document.addEventListener(event, function, useCapture)
🦦 解說:第一個參數為一個事件(譬如 “click” 點擊);第二個參數為「當出現指定事件後要執行的函式」;第三個參數為布林值可選擇要加與否 [這部分較深入詳細請見其他大大網站]
如上所述,事件分多種,以下列出常見的分類
- 滑鼠 mouse events
例如:click 點擊, mouseover 游標滑過、dbclick 點擊兩次、contextmenu 滑鼠右鍵展開選單
- 觸碰 touch events (手機、平板才有)
例如:touchmove 手指移動、touchstart 手指觸碰到螢幕
- 鍵盤 keyboard events
例如:
- 滾輪 wheel events
例如:wheel 滾動滑鼠
事件監聽範例:
<button id="button"> 我是程式小白 </button> <br> <h5 id=heading> 猜猜我是誰 </h5> <script> document.getElementById("button").addEventListener("click", function () { document.getElementById("heading").innerHTML= "我是你的程式學習好朋友!"; }); </script>
按鈕下方字串因「點擊」事件被觸發而變動(如下二圖)
若仔細觀察以上程式碼,不難察覺小白在 addEventListener 的參數中加入了「函式」,這樣的寫法為回呼函式的內嵌,你也可以寫成以下:
function showWhoIsIris() { document.getElementById("heading").innerHTML= "我是你的程式學習好朋友!"; } document.getElementById("button").addEventListener("click", showWhoIsIris);
說了這麼多,為什麼要用回呼函式?回呼函式又有哪些優點呢?
- Why use callback functions?
- 突破 JS 中同步(程式碼由上而下讀取)和 single-thread 單執行緒的限制,使特定事件能夠優先或按一定先後次序執行
- 假如沒有回呼的設定,程式碼需要按原先撰寫次序運行,雖然在一般狀況下沒有問題,但當一個事件拖太久無法執行時就會造成程式碼阻塞 (blocking) 的情形
- 補充:事件循環 (event loop) 概念 (stack、heap、queue 等)
講到這… 就會不免牽扯到「同步」和「非同步」的問題了
👉 同步 (Synchronous) 指的是程式必須等待前面的程式執行完才能執行。
👉 非同步(Asynchronous) 是指程式不須等待前面的程式執行完就能執行,使瀏覽器能同時執行多個任務而不會產生以上所述頁面阻塞的情形
非同步的概念也能應用於 AJAX (Asynchronous Javascript and XML) 在串接第三方 API 時請求遠端伺服器 (server) 回應之上 這部分有興趣延伸的朋友可以上網爬爬文
- What are the pros (and cons) of callback functions? 回呼函式優缺總匯
優 (Pros)
- 增加重複使用 (reusability) 性
- 程式碼更易讀、易維護
缺 (Cons)
- callback hell:回呼中有回呼終有回呼,以此巢狀方式不斷包裹交織,最後成為一個非常複雜的結構
- 解決以上地獄結構,你可以…
(1) 把程式碼分段拆開減少巢狀寫法重複次數
(2) 使用 ES6 Promise 和 async/ await 等工具
呼🥱 一次吸收這麼多 你應該也跟我一樣快知識爆炸了🤦♀️
最後再複習一次這集日記重點就收工嘍
- Callback function 介紹
- 常見的兩種 Callback function
- Callback function 優缺與改良
最後~ 如果你想繼續追蹤我的 JS 系列筆記 歡迎加入程式小白群😍 和我&其他程式學習朋友們一起討論切磋 JS 和 Python 唷!
參考資料 References:
- https://ithelp.ithome.com.tw/articles/10191970
- https://matthung0807.blogspot.com/2019/04/javascript.html
- https://medium.com/%E9%A6%AC%E6%A0%BC%E8%95%BE%E7%89%B9%E7%9A%84%E5%86%92%E9%9A%AA%E8%80%85%E6%97%A5%E8%AA%8C/js-%E4%BA%8B%E4%BB%B6%E7%AD%86%E8%A8%98-%E4%B8%8A-5377a572be51
- https://matthung0807.blogspot.com/2019/05/javascript-callback-callback-function.html
- https://kuro.tw/posts/2019/02/23/%E8%AB%87%E8%AB%87-JavaScript-%E7%9A%84-setTimeout-%E8%88%87-setInterval/
- https://www.w3schools.com/JSREF/obj_keyboardevent.asp
- https://www.freecodecamp.org/news/javascript-callback-functions-what-are-callbacks-in-js-and-how-to-use-them/
- https://dev.to/marek/are-callbacks-always-asynchronous-bah
- https://eyesofkids.gitbooks.io/javascript-start-from-es6/content/part4/callback.html
- https://www.sitepoint.com/demystifying-javascript-closures-callbacks-iifes/
- https://zellwk.com/blog/callbacks/
- https://www.youtube.com/watch?v=8aGhZQkoFbQ&feature=emb_logo&ab_channel=JSConf
- https://www.dashingd3js.com/lessons/javascript-callback-functions