构建你自己的函数

本文介绍了上一篇文章中涉及的大部分基本理论,并提供了实践经验。在这里,你将获得一些构建自己的自定义函数的练习。在此过程中,我们还将解释一些处理函数的有用细节。

¥With most of the essential theory dealt with in the previous article, this article provides practical experience. Here you will get some practice building your own, custom function. Along the way, we'll also explain some useful details of dealing with functions.

先决条件: 对 HTML、CSS 和 JavaScript 第一步 有基本了解。还有, 函数 - 可重用的代码块
目标: 提供一些构建自定义函数的实践,并解释一些更有用的相关细节。

主动学习:让我们构建一个函数

¥Active learning: Let's build a function

我们要构建的自定义函数将被称为 displayMessage()。它将在网页上显示自定义消息框,并将作为浏览器内置 alert() 功能的自定义替代品。我们以前见过这个,但让我们回顾一下。在浏览器的 JavaScript 控制台中的任意页面上键入以下内容:

¥The custom function we are going to build will be called displayMessage(). It will display a custom message box on a web page and will act as a customized replacement for a browser's built-in alert() function. We've seen this before, but let's just refresh our memories. Type the following in your browser's JavaScript console, on any page you like:

js
alert("This is a message");

alert 函数采用单个参数 - 显示在警报框中的字符串。尝试改变字符串来更改消息。

¥The alert function takes a single argument — the string that is displayed in the alert box. Try varying the string to change the message.

alert 功能受限:你可以更改消息,但不能轻易更改其他任何内容,例如颜色、图标或其他任何内容。我们将打造一款更加有趣的产品。

¥The alert function is limited: you can alter the message, but you can't easily vary anything else, such as the color, icon, or anything else. We'll build one that will prove to be more fun.

注意:这个示例应该可以在所有现代浏览器中正常工作,但在稍旧的浏览器中,样式可能看起来有点有趣。我们建议你在 Firefox、Opera 或 Chrome 等现代浏览器中进行此练习。

¥Note: This example should work in all modern browsers fine, but the styling might look a bit funny in slightly older browsers. We'd recommend you do this exercise in a modern browser like Firefox, Opera, or Chrome.

基本功能

¥The basic function

首先,让我们组合一个基本函数。

¥To begin with, let's put together a basic function.

注意:对于函数命名约定,你应该遵循与 变量命名约定 相同的规则。这很好,因为你可以区分它们 - 函数名称后面带有括号,而变量则没有。

¥Note: For function naming conventions, you should follow the same rules as variable naming conventions. This is fine, as you can tell them apart — function names appear with parentheses after them, and variables don't.

  1. 首先访问 function-start.html 文件并制作本地副本。你将看到 HTML 很简单 — 正文仅包含一个按钮。我们还提供了一些基本的 CSS 来设置自定义消息框的样式,以及一个空的 <script> 元素来放置 JavaScript。
  2. 接下来,在 <script> 元素中添加以下内容:
    js
    function displayMessage() {
      ...
    }
    
    我们从关键字 function 开始,这意味着我们正在定义一个函数。接下来是我们想要为函数指定的名称、一组括号和一组大括号。我们想要给函数的任何参数都放在括号内,调用函数时运行的代码都放在大括号内。
  3. 最后,在大括号内添加以下代码:
    js
    const 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),
    );
    

这是相当多的代码,所以我们将逐步引导你完成它。

¥This is quite a lot of code to go through, so we'll walk you through it bit by bit.

第一行通过使用 DOM API 获取全局 document 对象的 body 属性来选择 <body> 元素,并将其分配给名为 body 的常量,以便我们稍后可以对其进行操作:

¥The first line selects the <body> element by using the DOM API to get the body property of the global document object, and assigning that to a constant called body, so we can do things to it later on:

js
const body = document.body;

下一节使用名为 document.createElement() 的 DOM API 函数创建 <div> 元素,并将对其的引用存储在名为 panel 的常量中。该元素将成为消息框的外部容器。

¥The next section uses a DOM API function called document.createElement() to create a <div> element and store a reference to it in a constant called panel. This element will be the outer container of our message box.

然后,我们使用另一个名为 Element.setAttribute() 的 DOM API 函数在面板上设置值为 msgBoxclass 属性。这是为了更容易地设置元素的样式 - 如果你查看页面上的 CSS,你会发现我们正在使用 .msgBox 类选择器来设置消息框及其内容的样式。

¥We then use yet another DOM API function called Element.setAttribute() to set a class attribute on our panel with a value of msgBox. This is to make it easier to style the element — if you look at the CSS on the page, you'll see that we are using a .msgBox class selector to style the message box and its contents.

最后,我们对之前存储的 body 常量调用一个名为 Node.appendChild() 的 DOM 函数,该函数将一个元素嵌套在另一个元素中作为其子元素。我们将面板 <div> 指定为要附加到 <body> 元素内的子面板。我们需要这样做,因为我们创建的元素不仅仅会单独出现在页面上 - 我们需要指定将其放置在哪里。

¥Finally, we call a DOM function called Node.appendChild() on the body constant we stored earlier, which nests one element inside the other as a child of it. We specify the panel <div> as the child we want to append inside the <body> element. We need to do this as the element we created won't just appear on the page on its own — we need to specify where to put it.

js
const panel = document.createElement("div");
panel.setAttribute("class", "msgBox");
body.appendChild(panel);

接下来的两个部分利用我们已经看到的相同的 createElement()appendChild() 函数来创建两个新元素 - <p><button> - 并将它们作为面板 <div> 的子元素插入页面中。我们使用它们的 Node.textContent 属性(代表元素的文本内容)在段落内插入一条消息,并在按钮内插入一个 "x"。当用户想要关闭消息框时,需要单击/激活此按钮。

¥The next two sections make use of the same createElement() and appendChild() functions we've already seen to create two new elements — a <p> and a <button> — and insert them in the page as children of the panel <div>. We use their Node.textContent property — which represents the text content of an element — to insert a message inside the paragraph, and an "x" inside the button. This button will be what needs to be clicked/activated when the user wants to close the message box.

js
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() 添加一个函数,当用户单击 "close" 按钮时将调用该函数。该代码将从页面中删除整个面板 - 以关闭消息框。

¥Finally, we call addEventListener() to add a function that will be called when the user clicks the "close" button. The code will delete the whole panel from the page — to close the message box.

简而言之,addEventListener() 方法由按钮(或者实际上页面上的任何元素)提供,可以向该按钮传递函数和事件名称。在本例中,事件的名称是 'click',这意味着当用户单击按钮时,该函数将运行。你将了解更多有关 事件文章 中事件的信息。该函数内的行使用 Node.removeChild() DOM API 函数来指定我们要删除 HTML 元素的特定子元素 — 在本例中为面板 <div>

¥Briefly, the addEventListener() method is provided by the button (or in fact, any element on the page) that can be passed a function and the name of an event. In this case, the name of the event is 'click', meaning that when the user clicks the button, the function will run. You'll learn a lot more about events in our events article. The line inside the function uses the Node.removeChild() DOM API function to specify that we want to remove a specific child element of the HTML element — in this case, the panel <div>.

js
closeBtn.addEventListener("click", () => panel.parentNode.removeChild(panel));

基本上,整个代码块正在生成一个如下所示的 HTML 块,并将其插入到页面中:

¥Basically, this whole block of code is generating a block of HTML that looks like so, and inserting it into the page:

html
<div class="msgBox">
  <p>This is a message box</p>
  <button>x</button>
</div>

有很多代码需要完成 - 如果你现在不记得它的每一部分是如何工作的,请不要太担心!我们在这里想要关注的主要部分是函数的结构和用法,但我们想展示这个示例中一些有趣的东西。

¥That was a lot of code to work through — don't worry too much if you don't remember exactly how every bit of it works right now! The main part we want to focus on here is the function's structure and usage, but we wanted to show something interesting for this example.

调用函数

¥Calling the function

现在,你已经将函数定义很好地写入了 <script> 元素,但它不会执行任何操作。

¥You've now got your function definition written into your <script> element just fine, but it will do nothing as it stands.

  1. 尝试在函数下面添加以下行来调用它:
    js
    displayMessage();
    
    该行调用该函数,使其立即运行。当你保存代码并将其重新加载到浏览器中时,你会看到小消息框立即出现,仅出现一次。毕竟我们只调用一次。
  2. 现在打开示例页面上的浏览器开发者工具,转到 JavaScript 控制台并再次键入该行,你将看到它再次出现!所以这很有趣 - 我们现在有了一个可重用的函数,我们可以随时调用它。 但我们可能希望它出现以响应用户和系统操作。在实际应用中,可能会调用这样的消息框来响应新数据可用、发生错误、用户尝试删除其个人资料 ("你确定吗?")、或用户添加新联系人并完成操作 成功等 在此演示中,我们将在用户单击按钮时显示消息框。
  3. 删除你添加的上一行。
  4. 接下来,我们将选择该按钮并将对其的引用存储在常量中。将以下行添加到代码中函数定义上方:
    js
    const btn = document.querySelector("button");
    
  5. 最后,在前一行下方添加以下行:
    js
    btn.addEventListener("click", displayMessage);
    
    与 closeBtn 的单击事件处理程序类似,这里我们调用一些代码来响应单击的按钮。但在本例中,我们不是调用包含某些代码的匿名函数,而是通过名称调用 displayMessage() 函数。
  6. 尝试保存并刷新页面 - 现在,当你单击按钮时,你应该会看到消息框出现。

你可能想知道为什么我们没有在函数名称后面包含括号。这是因为我们不想立即调用该函数 - 仅在单击按钮后才调用。如果你尝试将行更改为

¥You might be wondering why we haven't included the parentheses after the function name. This is because we don't want to call the function immediately — only after the button has been clicked. If you try changing the line to

js
btn.addEventListener("click", displayMessage());

保存并重新加载,你将看到消息框出现,而无需单击按钮!此上下文中的括号有时称为 "函数调用运算符"。仅当你想在当前作用域中立即运行该函数时才使用它们。同样,匿名函数内的代码不会立即运行,因为它位于函数作用域内。

¥and saving and reloading, you'll see that the message box appears without the button being clicked! The parentheses in this context are sometimes called the "function invocation operator". You only use them when you want to run the function immediately in the current scope. In the same respect, the code inside the anonymous function is not run immediately, as it is inside the function scope.

如果你尝试了最后一个实验,请确保在继续之前撤消最后的更改。

¥If you tried the last experiment, make sure to undo the last change before carrying on.

通过参数改进功能

¥Improving the function with parameters

就目前而言,该功能仍然不是很有用 - 我们不想每次都显示相同的默认消息。让我们通过添加一些参数来改进我们的函数,允许我们使用一些不同的选项来调用它。

¥As it stands, the function is still not very useful — we don't want to just show the same default message every time. Let's improve our function by adding some parameters, allowing us to call it with some different options.

  1. 首先,更新函数的第一行:
    js
    function displayMessage() {
    
    对此:
    js
    function displayMessage(msgText, msgType) {
    
    现在,当我们调用该函数时,我们可以在括号内提供两个变量值来指定要在消息框中显示的消息以及消息的类型。
  2. 要使用第一个参数,请更新函数内的以下行:
    js
    msg.textContent = "This is a message box";
    
    to
    js
    msg.textContent = msgText;
    
  3. 最后但并非最不重要的一点是,你现在需要更新函数调用以包含一些更新的消息文本。更改以下行:
    js
    btn.addEventListener("click", displayMessage);
    
    到这个块:
    js
    btn.addEventListener("click", () =>
      displayMessage("Woo, this is a different message!"),
    );
    
    如果我们想在调用的函数的括号内指定参数,那么我们不能直接调用它 - 我们需要将它放在一个匿名函数中,这样它就不在直接作用域中,因此不会立即被调用 。现在,只有单击按钮才会调用它。
  4. 重新加载并再次尝试代码,你会发现它仍然可以正常工作,只是现在你还可以更改参数内的消息以在框中显示不同的消息!

更复杂的参数

¥A more complex parameter

继续下一个参数。这将涉及更多的工作 - 我们将对其进行设置,以便根据 msgType 参数的设置,该函数将显示不同的图标和不同的背景颜色。

¥On to the next parameter. This one is going to involve slightly more work — we are going to set it so that depending on what the msgType parameter is set to, the function will display a different icon and a different background color.

  1. 首先,从 GitHub 下载本练习所需的图标(warningchat)。将它们保存在名为 icons 的新文件夹中,与 HTML 文件位于同一位置。

    注意:警告和聊天图标最初是在 iconfinder.com 上找到的,由 纳扎鲁丁·安西亚里 设计的 - 谢谢!(实际的图标页面已被移动或删除。)

    ¥Note: The warning and chat icons were originally found on iconfinder.com, and designed by Nazarrudin Ansyari — Thanks! (The actual icon pages were since moved or removed.)

  2. 接下来,在 HTML 文件中找到 CSS。我们将进行一些更改以为图标让路。首先,更新 .msgBox 宽度:
    css
    width: 200px;
    
    to
    css
    width: 242px;
    
  3. 接下来,在 .msgBox p { } 规则中添加以下行:
    css
    padding-left: 82px;
    background-position: 25px center;
    background-repeat: no-repeat;
    
  4. 现在我们需要向 displayMessage() 函数添加代码来处理图标的显示。在函数的右大括号 (}) 上方添加以下块:
    js
    if (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 参数,这将提供默认状态,这意味着它是一个可选参数!
  5. 让我们测试一下更新后的函数,尝试从此更新 displayMessage() 调用:
    js
    displayMessage("Woo, this is a different message!");
    
    其中之一:
    js
    displayMessage("Your inbox is almost full — delete some mails", "warning");
    displayMessage("Brian: Hi there, how are you today?", "chat");
    
    你可以看到我们的(现在不那么)小功能变得多么有用。

注意:如果你在使示例正常运行时遇到问题,请随时根据 GitHub 上的完成版本(也包括 看到它实时运行)检查你的代码,或者向我们寻求帮助。

¥Note: If you have trouble getting the example to work, feel free to check your code against the finished version on GitHub (see it running live also), or ask us for help.

测试你的技能!

¥Test your skills!

你已读完本文,但你还记得最重要的信息吗?在继续之前,你可以找到一些进一步的测试来验证你是否已保留此信息 - 请参阅 测试你的技能:函数。这些测试需要下一篇文章中介绍的技能,因此你可能需要在尝试测试之前先阅读该文章。

¥You've reached the end of this article, but can you remember the most important information? You can find some further tests to verify that you've retained this information before you move on — see Test your skills: Functions. These tests require skills that are covered in the next article, so you might want to read that first before trying the test.

结论

¥Conclusion

恭喜你到达终点!本文带你完成了构建实用自定义函数的整个过程,只需做一些工作就可以将其移植到实际项目中。在下一篇文章中,我们将通过解释另一个基本的相关概念 - 返回值来总结函数。

¥Congratulations on reaching the end! This article took you through the entire process of building up a practical custom function, which with a bit more work could be transplanted into a real project. In the next article, we'll wrap up functions by explaining another essential related concept — return values.