構建你自己的函式
在上一篇文章中處理了大部分基本理論之後,本文將提供實踐經驗。在這裡,你將獲得一些構建自己的自定義函式的實踐。在此過程中,我們還將解釋一些處理函式的有用細節。
讓我們構建一個函式
我們要構建的自定義函式將命名為 displayMessage()。它將在網頁上顯示一個自定義訊息框,並作為瀏覽器內建 alert() 函式的自定義替代品。我們之前見過這個,但讓我們重新整理一下記憶。在你的瀏覽器 JavaScript 控制檯中,在任何你喜歡的頁面上輸入以下內容:
alert("This is a message");
alert() 函式接受一個引數——在警報框中顯示的字串。嘗試改變字串來改變訊息。
alert() 函式是有限的:你可以修改訊息,但不能輕易修改其他任何東西,例如顏色、圖示或其他任何東西。我們將構建一個更有趣的。
注意:此示例應該在所有現代瀏覽器中正常工作,但在稍微舊的瀏覽器中樣式可能會有點奇怪。我們建議你在現代瀏覽器(如 Firefox、Opera 或 Chrome)中進行此練習。
基本函式
首先,讓我們組合一個基本函式。
注意:對於函式命名約定,你應該遵循與變數命名約定相同的規則。這很好,因為你可以區分它們——函式名後面帶有括號,而變數名沒有。
-
首先訪問 function-start.html 檔案並製作一個本地副本。你會看到 HTML 很簡單——
只包含一個按鈕。我們還提供了一些基本的 CSS 來樣式化自定義訊息框,以及一個空的<script>元素來放置我們的 JavaScript。 -
接下來,在
<script>元素中新增以下內容jsfunction displayMessage() { // … }我們以關鍵字
function開頭,這意味著我們正在定義一個函式。這後面跟著我們希望給函式起的名稱、一組括號和一組花括號。我們希望給函式的任何引數都放在括號內,而當我們呼叫函式時執行的程式碼則放在花括號內。 -
最後,在花括號內新增以下程式碼
jsconst body = document.body; const panel = document.createElement("div"); panel.setAttribute("class", "msgBox"); body.appendChild(panel); const msg = document.createElement("p"); msg.textContent = "This is a message box"; panel.appendChild(msg); const closeBtn = document.createElement("button"); closeBtn.textContent = "x"; panel.appendChild(closeBtn); closeBtn.addEventListener("click", () => panel.parentNode.removeChild(panel), );
這有很多程式碼需要理解,所以我們將逐步為你講解。
第一行使用 DOM API 獲取全域性 document 物件的 body 屬性,選擇 <body> 元素,並將其賦值給一個名為 body 的常量,以便我們以後可以使用它
const body = document.body;
下一節使用一個名為 document.createElement() 的 DOM API 函式來建立一個 <div> 元素,並將其引用儲存在一個名為 panel 的常量中。這個元素將是我們訊息框的外部容器。
然後,我們使用另一個名為 Element.setAttribute() 的 DOM API 函式,為我們的面板設定一個 class 屬性,值為 msgBox。這使得樣式化元素更容易——如果你檢視頁面上的 CSS,你會看到我們正在使用 .msgBox 類選擇器來樣式化訊息框及其內容。
最後,我們在之前儲存的 body 常量上呼叫一個名為 Node.appendChild() 的 DOM 函式,它將一個元素巢狀在另一個元素內部作為其子元素。我們將面板 <div> 指定為我們要追加到 <body> 元素內部的子元素。我們需要這樣做,因為我們建立的元素不會自己出現在頁面上——我們需要指定將其放在何處。
const panel = document.createElement("div");
panel.setAttribute("class", "msgBox");
body.appendChild(panel);
接下來的兩部分使用了我們已經見過的相同的 createElement() 和 appendChild() 函式來建立兩個新元素——一個 <p> 和一個 <button>——並將它們作為面板 <div> 的子元素插入到頁面中。我們使用它們的 Node.textContent 屬性(它表示元素的文字內容)在段落中插入一條訊息,在按鈕中插入一個“x”。當用戶想要關閉訊息框時,這個按鈕就是需要點選/啟用的。
const msg = document.createElement("p");
msg.textContent = "This is a message box";
panel.appendChild(msg);
const closeBtn = document.createElement("button");
closeBtn.textContent = "x";
panel.appendChild(closeBtn);
最後,我們呼叫 addEventListener() 來新增一個函式,該函式將在使用者點選“關閉”按鈕時被呼叫。該程式碼將從頁面中刪除整個面板——以關閉訊息框。
簡而言之,addEventListener() 方法由按鈕(實際上,是頁面上的任何元素)提供,它可以傳遞一個函式和一個事件名稱。在這種情況下,事件名稱是“click”,這意味著當用戶點選按鈕時,該函式將執行。你將在我們的事件文章中學習更多關於事件的知識。函式內部的行使用 Node.removeChild() DOM API 函式來指定我們要刪除 HTML 元素的特定子元素——在這種情況下,是面板 <div>。
closeBtn.addEventListener("click", () => panel.parentNode.removeChild(panel));
基本上,這整段程式碼正在生成一段看起來像這樣的 HTML,並將其插入到頁面中:
<div class="msgBox">
<p>This is a message box</p>
<button>x</button>
</div>
這需要處理很多程式碼——如果現在你記不住它的每個部分是如何工作的,也別太擔心!我們這裡主要關注的是函式的結構和用法,但我們想為這個例子展示一些有趣的東西。
呼叫函式
你現在已經把你的函式定義很好地寫到了 <script> 元素中,但它目前什麼也不會做。
-
嘗試在函式下方新增以下行來呼叫它:
jsdisplayMessage();此行呼叫函式,使其立即執行。當你儲存程式碼並在瀏覽器中重新載入時,你會看到小訊息框立即出現,只出現一次。畢竟,我們只調用它一次。
-
現在開啟示例頁面上的瀏覽器開發者工具,進入 JavaScript 控制檯並再次輸入該行,你會看到它再次出現!所以這很有趣——我們現在有一個可重用的函式,我們可以隨時呼叫它。
然而,我們可能希望訊息框響應使用者和系統操作而出現。在實際應用中,這樣的訊息框可能會在新資料可用、發生錯誤、使用者嘗試刪除其配置檔案(“你確定嗎?”)或使用者新增新聯絡人並操作成功完成等情況下被呼叫。
在這個演示中,我們將讓訊息框在使用者點選按鈕時出現。以下是你需要遵循的步驟:
-
刪除你之前新增的行(
displayMessage();)。 -
選擇
<button>元素並將其引用儲存在一個常量中。在函式定義上方新增以下行到你的程式碼中:jsconst btn = document.querySelector("button"); -
為按鈕點選建立一個事件監聽器,該監聽器會呼叫我們的函式。在
const btn =之後新增以下行:jsbtn.addEventListener("click", displayMessage);與我們的 closeBtn 的點選事件處理程式類似,這裡我們是在響應按鈕點選時呼叫一些程式碼。但在這種情況下,我們不是呼叫包含某些程式碼的匿名函式,而是按名稱呼叫我們的
displayMessage()函式。 -
最後,嘗試儲存並重新整理頁面——現在你應該會在點選按鈕時看到訊息框出現。
你可能想知道為什麼我們沒有在函式名稱後面加上括號。這是因為我們不想立即呼叫函式——只有在按鈕被點選後才呼叫。如果你嘗試將該行更改為:
btn.addEventListener("click", displayMessage());
然後儲存並重新載入,你會發現訊息框在沒有點選按鈕的情況下就出現了!在這種情況下,括號有時被稱為“函式呼叫運算子”。你只在希望函式在當前作用域中立即執行時使用它們。同樣,匿名函式內部的程式碼不會立即執行,因為它在函式作用域內。
如果你嘗試了上一個實驗,請務必在繼續之前撤消最後一次更改。
用引數改進函式
就目前而言,該函式仍然不是很實用——我們不想每次都只顯示相同的預設訊息。讓我們透過新增一些引數來改進我們的函式,從而允許我們用不同的選項呼叫它。
-
首先,更新函式的第一行
jsfunction displayMessage() {改為
jsfunction displayMessage(msgText, msgType) {現在當我們呼叫函式時,我們可以在括號內提供兩個變數值,以指定要在訊息框中顯示的訊息和訊息的型別。
-
要使用第一個引數,請更新函式中的以下行:
jsmsg.textContent = "This is a message box";改為
jsmsg.textContent = msgText; -
最後但同樣重要的是,你現在需要更新你的函式呼叫,以包含一些更新後的訊息文字。更改以下行:
jsbtn.addEventListener("click", displayMessage);到這個程式碼塊
jsbtn.addEventListener("click", () => displayMessage("Woo, this is a different message!"), );如果我們想在括號中為我們正在呼叫的函式指定引數,那麼我們不能直接呼叫它——我們需要將其放在一個匿名函式中,這樣它就不在立即作用域內,因此不會立即被呼叫。現在,它要等到按鈕被點選後才會被呼叫。
-
重新載入並再次嘗試程式碼,你會看到它仍然執行良好,只是現在你還可以更改引數中的訊息,以在框中顯示不同的訊息!
一個更復雜的引數
接下來是下一個引數。這個引數將涉及更多的工作——我們將設定它,以便根據 msgType 引數的設定,函式將顯示不同的圖示和不同的背景顏色。
-
首先,從 GitHub 下載本次練習所需的圖示(warning 和 chat)。將它們儲存在與 HTML 檔案相同位置的名為
icons的新資料夾中。注意: 警告和聊天圖示最初來自 iconfinder.com,由 Nazarrudin Ansyari 設計——謝謝!(實際的圖示頁面已移動或刪除。)
-
接下來,找到 HTML 檔案中的 CSS。我們將進行一些更改,以便為圖示騰出空間。首先,將
.msgBox寬度從以下內容更新:csswidth: 200px;改為
csswidth: 242px; -
接下來,在
.msgBox p { }規則中新增以下行:csspadding-left: 82px; background-position: 25px center; background-repeat: no-repeat; -
現在我們需要在
displayMessage()函式中新增程式碼來處理圖示的顯示。在函式的右大括號 (}) 正上方新增以下程式碼塊:jsif (msgType === "warning") { msg.style.backgroundImage = 'url("icons/warning.png")'; panel.style.backgroundColor = "red"; } else if (msgType === "chat") { msg.style.backgroundImage = 'url("icons/chat.png")'; panel.style.backgroundColor = "aqua"; } else { msg.style.paddingLeft = "20px"; }這裡,如果
msgType引數設定為"warning",則顯示警告圖示並將面板的背景顏色設定為紅色。如果設定為"chat",則顯示聊天圖示並將面板的背景顏色設定為水藍色。如果msgType引數根本沒有設定(或設定為不同的值),則程式碼的else { }部分將起作用,段落將獲得預設的內邊距,沒有圖示,也沒有設定背景面板顏色。這提供了在未提供msgType引數時的預設狀態,這意味著它是一個可選引數! -
讓我們測試一下我們更新後的函式,嘗試將
displayMessage()呼叫從這個jsdisplayMessage("Woo, this is a different message!");更改為其中一個
jsdisplayMessage("Your inbox is almost full — delete some mails", "warning"); displayMessage("Brian: Hi there, how are you today?", "chat");你可以看到我們的(現在不再那麼)小的函式變得多麼有用。
注意:如果你在讓示例工作時遇到問題,請隨時將你的程式碼與 GitHub 上的完成版本進行檢查(也可以檢視其執行情況),或向我們尋求幫助。
總結
恭喜你到達終點!本文帶領你完成了構建一個實用的自定義函式的整個過程,再稍加努力,這個函式就可以移植到實際專案中。在下一篇文章中,我們將透過解釋另一個重要的相關概念——返回值來總結函式。