1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
|
---
title: 创建您自己的函数
slug: learn/JavaScript/Building_blocks/Build_your_own_function
tags:
- JavaScript
- 函数
- 初学者
- 学习
- 教程
translation_of: Learn/JavaScript/Building_blocks/Build_your_own_function
---
<div>{{LearnSidebar}}</div>
<div>{{PreviousMenuNext("Learn/JavaScript/Building_blocks/Functions","Learn/JavaScript/Building_blocks/Return_values", "Learn/JavaScript/Building_blocks")}}</div>
<p class="summary">我们在之前的文章里大多学的是理论,这篇文章将提供一个练习的机会——您将练习构建一些您自己风格的函数。在练习过程中,我们也会解释一些针对函数的更深层的实用细节。</p>
<table class="learn-box standard-table">
<tbody>
<tr>
<th scope="row">先修知识:</th>
<td>基本的电脑常识,对于HTML和CSS的基本了解, <a href="zh-CN/docs/Learn/JavaScript/First_steps">JavaScript第一步</a>, <a href="zh-CN/docs/Learn/JavaScript/Building_blocks/Functions">函数-可复用代码块</a>。</td>
</tr>
<tr>
<th scope="row">目标:</th>
<td>提供一些练习来构建一个传统的函数,并解释一些有用的相关细节。</td>
</tr>
</tbody>
</table>
<h2 id="先活跃下气氛:构建一个函数">先活跃下气氛:构建一个函数</h2>
<p>我们将构建的传统函数将被命名为 <code>displayMessage()</code>,它向用户展示一个传统的消息盒子于web页面的顶部。它充当浏览器内建的 <a href="/zh-CN/docs/Web/API/Window/alert">alert()</a> 函数更有用的替代品。你已经看过了这个,但是我们回复一下我们的记忆——在你的浏览器的 JavaScript控制台中,在任意一个页面里尝试以下代码</p>
<pre class="brush: js notranslate">alert('This is a message');</pre>
<p>这个函数只带有一个参数——在 alert box 中展示的字符串。您可以尝试改变字符串来改变消息。</p>
<p>这个<code>alert()</code>函数不是很好的:您可以<code>alert()</code>出这条信息,但是您不能很容易的表达其他内容,例如颜色,图标或者是其他东西。接下来我们将会构建一个更有趣的函数。</p>
<div class="note">
<p><strong>笔记</strong>: 这个例子能够在现代浏览器上很好的工作,但是这个风格在老的浏览器上并没那么有趣。我们建议你实现这个例子时在现代浏览器上,例如Firefox,Opera或者Chrome浏览器。</p>
</div>
<h2 id="基本函数">基本函数</h2>
<p>首先,让我们来组织一个基本的函数。</p>
<div class="note">
<p><strong>注:</strong>对于函数命名约定,应遵循与<a href="/en-US/Learn/JavaScript/First_steps/Variables#An_aside_on_variable_naming_rules">变量命名约定</a>相同的规则。 这很好,尽你所能理解它们之间的区别 - 函数名称后带有括号,而变量则没有。</p>
</div>
<ol>
<li>我们希望您首先访问<a href="https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/functions/function-start.html">function-start.html</a>文件并创建一个本地拷贝。您将会看到这个HTML很简单 — 我们的body块仅包含一个按钮。我们还提供了一些基本的CSS来装饰自定义消息框,以及一个用于放置JavaScript代码的{{htmlelement("script")}}元素。</li>
<li>接下来,将下面的代码添加至 <code><script></code> 元素中:
<pre class="brush: js notranslate">function displayMessage() {
}</pre>
我们从表示定义一个函数的关键字 <code>function</code>开始,这之后是我们想给我们的函数取的名字;一组括号;和一组大括号。我们要传给我们的函数的任何参数都在括号内,当我们调用该函数时运行的代码均在大括号内。</li>
<li>最后,添加以下代码到大括号中:
<pre class="brush: js notranslate">const html = document.querySelector('html');
const panel = document.createElement('div');
panel.setAttribute('class', 'msgBox');
html.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.onclick = function() {
panel.parentNode.removeChild(panel);
}</pre>
</li>
</ol>
<p>天哪,这么多代码!好吧,一行一行的解释给你听。</p>
<p>第一行代码使用了一个DOM(文档对象模型)的内置方法 {{domxref("document.querySelector()")}} 来选择{{htmlelement("html")}} 元素并且把它存放在一个叫 <code>html</code>的常量中, 这样方便我们接下来使用这个元素:</p>
<pre class="brush: js notranslate">const html = document.querySelector('html');</pre>
<p>下段代码使用了另一个名字叫做 {{domxref("Document.createElement()")}} 的DOM方法,用来创建 {{htmlelement("div")}} 元素并且把该新建元素的引用(实际上是新建对象的地址)放在一个叫做 <code>panel</code>的常量中。 这个元素将成为我们的消息框的外部容器。</p>
<p>然后我们又使用了一个叫做 {{domxref("Element.setAttribute()")}} 的DOM方法给panel元素添加了一个值为<code>msgBox</code> 的<code>class</code> 类属性。 这样做方便我们来给这个元素添加样式 — 查看CSS代码你就知道我们使用<code>.msgBox</code> 类选择器来给消息框和消息内容设置样式。</p>
<p>最后,我们还使用了一个叫做 {{domxref("Node.appendChild()")}} 的DOM方法,给 <code>html</code> 常量(我们之前定义好的)追加了我们设置好样式的panel元素 。该方法追加了元素的同时也把panel<code><div></code>元素指定为<code><html></code>的子元素 。这样做是因为我们创建了一个元素之后这个元素并不会莫名其妙的出现在我们的页面上(浏览器只知道我们创建了一个元素,但是不知道把这个元素怎么呈现出来) — 因此,我们给这个元素了一个定位,就是显示在html里面!</p>
<pre class="brush: js notranslate">const panel = document.createElement('div');
panel.setAttribute('class', 'msgBox');
html.appendChild(panel);</pre>
<p>下面这两段使用了我们之前使用过的方法<code>createElement()</code>和<code>appendChild()</code> — 创建了一个 {{htmlelement("p")}} 元素和一个{{htmlelement("button")}}元素 — 并且把他们追加到了panel<code><div></code>之下。我们使用元素的 {{domxref("Node.textContent")}}(Node泛指一个元素并不是说是某个元素是叫Node) 属性— 表示一个元素的文本属性 — 给一个p元素赋值, 同样按钮也有这个属性,该属性就是按钮显示的‘X’。这个按钮的功能就是关闭消息提示框。</p>
<pre class="brush: js notranslate">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);</pre>
<p>最后我们使用一个叫做 {{domxref("GlobalEventHandlers.onclick")}} 的事件句柄给按钮添加了一个点击事件, 点击事件后定义了一个匿名函数,功能是将消息提示框从父容器中删除 — 达到了关闭的效果。</p>
<p>简单来说,这个 <code>onclick</code> 句柄是一个按钮的属性 (事实上,页面上的任何元素) 当按钮被点击的时候能够执行一些代码。 你可以在之后的介绍事件的章节了解详情。我们给 <code>onclick</code> 句柄绑定了一个匿名函数, 函数中代码在元素被点击的时候运行。函数里面的这行代码使用了 {{domxref("Node.removeChild()")}} DOM 方法指定了我们想要移除的HTML的子元素 — 在这里指panel<code><div></code>.</p>
<p>PS:我来解释下是什么意思,panel是消息框,panel.parentNode就是指panel的上一级,就是整个DOM,然后再来用这个父亲来干掉这个儿子,儿子不能自己干掉自己,所以要这么做。</p>
<pre class="brush: js notranslate">closeBtn.onclick = function() {
panel.parentNode.removeChild(panel);
}</pre>
<p>大体上, 这一整块的代码我就不解释了就是一个div,一个段落,一个按钮, 把这个加在页面上:</p>
<pre class="brush: html notranslate"><div class="msgBox">
<p>This is a message box</p>
<button>x</button>
</div></pre>
<p>啊,看完了这么多代码,是不是很累? — 不用担心,你现在没有必要完全知道这些代码的细节! 这里我们只关心函数的结构和使用方式, 下面的例子将展示一些有意思的东西。</p>
<h2 id="调用函数">调用函数</h2>
<p>相信你已经迫不及待的在你的<code><script></code> 标签中写好了一个函数, 但仅仅是定义而已,这玩意不会做任何事情。</p>
<ol>
<li>把下面这行代码加在写好的函数下面来调用函数(当然,不一定要放在函数下面来调用,在C语言中确实是还要先定义后使用,但是我们现在用的是JavaScript,这玩意很强大,不管你是先定义后调用还是先调用后定义都行,但是别忘了定义):
<pre class="brush: js notranslate">displayMessage();</pre>
这行代码调用了你写的函数, 当浏览器解析到这行代码时会立即执行函数内的代码。当你保存好你的代码以后在浏览器中刷新, 你会马上看到一个小小的提示框弹出来, 但是只弹出了一次。毕竟我们只调用了一次函数是不?</li>
<li>
<p>现在打开浏览器开发工具, 找到JavaScript控制台把上面这一句再输入一遍然后回车, 你会看到又弹出了一次!有点意思... — 现在我们有了一个能够重复调用的函数,只要你高兴可以随时调用它。</p>
<p>但是,这玩意有什么用呢?在真实的应用当中这样的消息提示框一般用来提示一些什么新的东西, 或者是出现了一个什么错误, 或者当用户删除配置文件的时候("你确定要这样做?"), 或者用户添加一个新的联系人之后提示操作成功..等等。 在这个例子里面, 当用户点击这个按钮的时候这个提示框会出现。</p>
</li>
<li>删掉你之前加的那一行代码。</li>
<li>下一步我们用选择器找到这个按钮并赋值给一个常量。 在你的函数定义之前把这行代码加上去:
<pre class="brush: js notranslate">const btn = document.querySelector('button');</pre>
</li>
<li>最后,把这行代码加在上面这行的下面:
<pre class="brush: js notranslate">btn.onclick = displayMessage;</pre>
<code><font face="Open Sans, arial, x-locale-body, sans-serif"><span style="background-color: #ffffff;">跟关闭按钮类似</span></font>closeBtn.onclick...</code> , 当按钮被点击的时候我们运行了点代码。 但不同的是, 之前等号的右边是一个匿名函数,看起来是这样的:<code>btn.onclick = function(){...}</code>, 我们现在是直接使用函数名称来调用。</li>
<li>保存好以后刷新页面 — 现在你应该能看到当你点击按钮的时候提示框弹出来。</li>
</ol>
<p>你会想“怎么函数名后面没有括号呢?”. 这是因为我们不想直接调用这个函数 — 而是只有当按钮被点击的时候才调用这个函数。 试试把代码改成这样:</p>
<pre class="brush: js notranslate">btn.onclick = displayMessage();</pre>
<p>保存刷新, 你会发现按钮都还没点击提示框就出来了! 在函数名后面的这个括号叫做“函数调用运算符”(function invocation operator)。你只有在想直接调用函数的地方才这么写。 同样要重视的是, 匿名函数里面的代码也不是直接运行的, 只要代码在函数作用域内。</p>
<p>如果你做了这个函数括号的实验, 在继续之前把代码恢复到之前的状态。</p>
<h2 id="使用参数列表改进函数">使用参数列表改进函数</h2>
<p>就现在看来,我们的函数还不是特别有用 — 我们想要的不仅仅是每点击一次展示一个默认的消息。我们来改造下我们的函数,给它添加几个参数, 允许我们以不同的方式调用这个函数。</p>
<ol>
<li>第一步,修改函数的第一行代码:
<pre class="brush: js notranslate">function displayMessage() {</pre>
<p>改成这样的:</p>
<pre class="brush: js notranslate">function displayMessage(msgText, msgType) {</pre>
当我们调用函数的时候,我们可以在括号里添加两个变量,来指定显示在消息框里面的消息,和消息的类型。</li>
<li>为了使用第一个参数, 把接下来的一行:
<pre class="brush: js notranslate">msg.textContent = 'This is a message box';</pre>
<p>改成这样:</p>
<pre class="brush: js notranslate">msg.textContent = msgText;</pre>
</li>
<li>最后但同样重要的一点, 我们来调用这个函数,并且使用了带参数的形式,修改下面这行:
<pre class="brush: js notranslate">btn.onclick = displayMessage;</pre>
<p>改成这样:</p>
<pre class="brush: js notranslate">btn.onclick = function() {
displayMessage('Woo, this is a different message!');
};</pre>
如果我们要在点击事件里面绑定这个新函数,我们不能直接使用(<code>btn.onclick = displayMessage('Woo, this is a different message!');</code>)前面已经讲过— 我们要把它放在一个匿名函数里面,不然函数会直接调用,而不是按钮点击之后才会调用,这不是我们想要的结果。</li>
<li>保存刷新, 就像你所期待的那样现在你可以随意的指定消息框里面显示的消息!</li>
</ol>
<h3 id="一个更加复杂的参数">一个更加复杂的参数</h3>
<p>刚才我们只使用了我们定义的第一个参数<code>msgText</code>,对于第二个参数<code>msgType</code>,这个就涉及了稍微多一点的东西— 我们要设置一些依赖于这个 <code>msgType</code> 参数的东西, 我们的函数将会显示不同的图标和不同的背景颜色。</p>
<ol>
<li>第一步, 从Github上下载我们需要的图标 (<a href="https://raw.githubusercontent.com/mdn/learning-area/master/javascript/building-blocks/functions/icons/warning.png">警告图标</a> 和 <a href="https://raw.githubusercontent.com/mdn/learning-area/master/javascript/building-blocks/functions/icons/chat.png">聊天图标</a>) 。 把图标保存在一个叫做<code>icons</code> 的文件夹下,和你的HTML文件在同一个目录下。
<div class="note"><strong>笔记</strong>: 警告和聊天图标是在这个网站iconfinder.com上找到的, 设计者是 <a href="https://www.iconfinder.com/nazarr">Nazarrudin Ansyari</a>. 感谢他!</div>
</li>
<li>下一步, 找到页面的CSS文件. 我们要修改下以便我们使用图标. 首先, 修改 <code>.msgBox</code> 的宽度:
<pre class="brush: css notranslate">width: 200px;</pre>
改成:
<pre class="brush: css notranslate">width: 242px;</pre>
</li>
<li>下一步, 在 <code>.msgBox p { ... }</code> 里面添加几条新规则:
<pre class="brush: css notranslate">padding-left: 82px;
background-position: 25px center;
background-repeat: no-repeat;</pre>
</li>
<li>CSS改完了以后我们就要来修改函数 <code>displayMessage()</code> 让它能够显示图标. 在你的函数结束符之前<code>}</code>添加下面这几行代码:
<pre class="brush: js notranslate">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';
}</pre>
来解释下, 如果第二个参数 <code>msgType</code> 的值为 <code>'warning'</code>, 我们的消息框将显示一个警告图标和一个红色的背景. 如果这个参数的值是 <code>'chat'</code>, 将显示聊天图标和水蓝色的背景. 如果 <code>msgType</code> 没有指定任何值 (或者不是<code>'warning'</code>和<code>'chat'</code>), 然后这个 <code>else { ... }</code> 代码块将会被执行, 代码的意思是给消息段落设置了一个简单的左内边距并且没有图标, 也没有背景颜色。这么做是为了当没有提供 <code>msgType</code> 参数的时候给函数一个默认行为, 意思是这是一个可选参数(你没发现?其实我们已经用过了!就在这里<code>btn.onclick = function() { displayMessage('Woo, this is a different message!'); };</code>只是当时我们没有写这个<code>else</code>段,也就是啥操作也没做)!</li>
<li>现在来测试下我们的新函数, 可以直接调用 <code>displayMessage()</code> 像这样:
<pre class="brush: js notranslate">displayMessage('Woo, this is a different message!');</pre>
<p>或者这样:</p>
<pre class="brush: js notranslate">displayMessage('Your inbox is almost full — delete some mails', 'warning');
displayMessage('Brian: Hi there, how are you today?','chat');</pre>
你能看到我们现在的函数稍微有了点用 (不是非常有用) ,一个小的新功能被我们写出来了(当然,函数可以做很多你想的到的和想不到的事)!</li>
</ol>
<div class="note">
<p><strong>注意</strong>: 如果你写这个例子遇到了困难, 在这里查看免费的代码 <a href="https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/functions/function-stage-4.html">完整版本的代码</a> (或者<a href="http://mdn.github.io/learning-area/javascript/building-blocks/functions/function-stage-4.html">在线运行的完整代码</a>), 也可以向我们寻求帮助。</p>
</div>
<h2 id="测试你的技能!"><font face="x-locale-heading-primary, zillaslab, Palatino, Palatino Linotype, x-locale-heading-secondary, serif"><strong>测试你的技能!</strong></font></h2>
<p>你已经来到了本文章的结尾,但是你还能记得最重要的知识吗?你可以在离开这里找到一些更深度的测试来证实你已经记住了这些知识——查看<a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Test_your_skills:_Functions">测试你的技能:函数</a>(英文)。后两章文本包含了这个测试需要的技能,所以你可能先需要阅读再尝试该测试。</p>
<h2 id="结论">结论</h2>
<p>恭喜你,终于到了这里(等你好久了)! 这篇文章介绍了如何写一个自定义函数, 要把这个新技能在真实项目中使用上你可能还要花点功夫。 下一篇文章中我们将会介绍函数的另一个相关概念 — 返回值。</p>
<ul>
</ul>
<p>{{PreviousMenuNext("Learn/JavaScript/Building_blocks/Functions","Learn/JavaScript/Building_blocks/Return_values", "Learn/JavaScript/Building_blocks")}}</p>
<h2 id="在这个模块中"><font face="x-locale-heading-primary, zillaslab, Palatino, Palatino Linotype, x-locale-heading-secondary, serif"><span style="font-size: 37.33327865600586px;"><strong>在这个模块中</strong></span></font></h2>
<ul>
<li><a href="/zh-CN/docs/learn/JavaScript/Building_blocks/conditionals">在代码中做决定 - 条件语句 在 Wiki 中编辑</a></li>
<li><a href="/zh-CN/docs/learn/JavaScript/Building_blocks/Looping_code">循环吧代码</a></li>
<li><a href="/zh-CN/docs/learn/JavaScript/Building_blocks/Functions">函数-可复用代码块</a></li>
<li><a href="/zh-CN/docs/learn/JavaScript/Building_blocks/Build_your_own_function">创建您自己的函数</a></li>
<li><a href="/zh-CN/docs/learn/JavaScript/Building_blocks/Return_values">函数返回值</a></li>
<li><a href="/zh-CN/docs/Learn/JavaScript/Building_blocks/Events">事件介绍</a></li>
<li><a href="/zh-CN/docs/learn/JavaScript/Building_blocks/%E7%9B%B8%E7%89%87%E8%B5%B0%E5%BB%8A">照片库</a></li>
</ul>
|