aboutsummaryrefslogtreecommitdiff
path: root/files/zh-cn/learn/javascript
diff options
context:
space:
mode:
authorPeter Bengtsson <mail@peterbe.com>2020-12-08 14:40:17 -0500
committerPeter Bengtsson <mail@peterbe.com>2020-12-08 14:40:17 -0500
commit33058f2b292b3a581333bdfb21b8f671898c5060 (patch)
tree51c3e392513ec574331b2d3f85c394445ea803c6 /files/zh-cn/learn/javascript
parent8b66d724f7caf0157093fb09cfec8fbd0c6ad50a (diff)
downloadtranslated-content-33058f2b292b3a581333bdfb21b8f671898c5060.tar.gz
translated-content-33058f2b292b3a581333bdfb21b8f671898c5060.tar.bz2
translated-content-33058f2b292b3a581333bdfb21b8f671898c5060.zip
initial commit
Diffstat (limited to 'files/zh-cn/learn/javascript')
-rw-r--r--files/zh-cn/learn/javascript/building_blocks/build_your_own_function/index.html256
-rw-r--r--files/zh-cn/learn/javascript/building_blocks/conditionals/index.html609
-rw-r--r--files/zh-cn/learn/javascript/building_blocks/events/index.html564
-rw-r--r--files/zh-cn/learn/javascript/building_blocks/functions/index.html428
-rw-r--r--files/zh-cn/learn/javascript/building_blocks/index.html55
-rw-r--r--files/zh-cn/learn/javascript/building_blocks/looping_code/index.html781
-rw-r--r--files/zh-cn/learn/javascript/building_blocks/return_values/index.html172
-rw-r--r--files/zh-cn/learn/javascript/building_blocks/相片走廊/index.html244
-rw-r--r--files/zh-cn/learn/javascript/client-side_web_apis/client-side_storage/index.html798
-rw-r--r--files/zh-cn/learn/javascript/client-side_web_apis/drawing_graphics/index.html881
-rw-r--r--files/zh-cn/learn/javascript/client-side_web_apis/fetching_data/index.html395
-rw-r--r--files/zh-cn/learn/javascript/client-side_web_apis/index.html47
-rw-r--r--files/zh-cn/learn/javascript/client-side_web_apis/introduction/index.html275
-rw-r--r--files/zh-cn/learn/javascript/client-side_web_apis/manipulating_documents/index.html300
-rw-r--r--files/zh-cn/learn/javascript/client-side_web_apis/third_party_apis/index.html484
-rw-r--r--files/zh-cn/learn/javascript/client-side_web_apis/video_and_audio_apis/index.html499
-rw-r--r--files/zh-cn/learn/javascript/first_steps/a_first_splash/index.html744
-rw-r--r--files/zh-cn/learn/javascript/first_steps/arrays/index.html522
-rw-r--r--files/zh-cn/learn/javascript/first_steps/index.html57
-rw-r--r--files/zh-cn/learn/javascript/first_steps/math/index.html429
-rw-r--r--files/zh-cn/learn/javascript/first_steps/silly_story_generator/index.html239
-rw-r--r--files/zh-cn/learn/javascript/first_steps/strings/index.html288
-rw-r--r--files/zh-cn/learn/javascript/first_steps/test_your_skills_colon__variables/index.html78
-rw-r--r--files/zh-cn/learn/javascript/first_steps/useful_string_methods/index.html474
-rw-r--r--files/zh-cn/learn/javascript/first_steps/variables/index.html431
-rw-r--r--files/zh-cn/learn/javascript/first_steps/what_is_javascript/index.html501
-rw-r--r--files/zh-cn/learn/javascript/first_steps/what_went_wrong/index.html284
-rw-r--r--files/zh-cn/learn/javascript/howto/index.html294
-rw-r--r--files/zh-cn/learn/javascript/index.html61
-rw-r--r--files/zh-cn/learn/javascript/objects/basics/index.html243
-rw-r--r--files/zh-cn/learn/javascript/objects/index.html51
-rw-r--r--files/zh-cn/learn/javascript/objects/inheritance/index.html251
-rw-r--r--files/zh-cn/learn/javascript/objects/json/index.html327
-rw-r--r--files/zh-cn/learn/javascript/objects/object-oriented_js/index.html267
-rw-r--r--files/zh-cn/learn/javascript/objects/object_building_practice/index.html454
-rw-r--r--files/zh-cn/learn/javascript/objects/object_prototypes/index.html357
-rw-r--r--files/zh-cn/learn/javascript/objects/向“弹跳球”演示程序添加新功能/index.html468
-rw-r--r--files/zh-cn/learn/javascript/objects/测试你的技能_colon_面向对象的javascript/index.html95
-rw-r--r--files/zh-cn/learn/javascript/异步/async_await/index.html379
-rw-r--r--files/zh-cn/learn/javascript/异步/choosing_the_right_approach/index.html523
-rw-r--r--files/zh-cn/learn/javascript/异步/index.html59
-rw-r--r--files/zh-cn/learn/javascript/异步/promises语法/index.html589
-rw-r--r--files/zh-cn/learn/javascript/异步/概念/index.html162
-rw-r--r--files/zh-cn/learn/javascript/异步/简介/index.html272
-rw-r--r--files/zh-cn/learn/javascript/异步/超时和间隔/index.html617
45 files changed, 16304 insertions, 0 deletions
diff --git a/files/zh-cn/learn/javascript/building_blocks/build_your_own_function/index.html b/files/zh-cn/learn/javascript/building_blocks/build_your_own_function/index.html
new file mode 100644
index 0000000000..107d68a202
--- /dev/null
+++ b/files/zh-cn/learn/javascript/building_blocks/build_your_own_function/index.html
@@ -0,0 +1,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>&lt;script&gt;</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>&lt;div&gt;</code>元素指定为<code>&lt;html&gt;</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>&lt;div&gt;</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>&lt;div&gt;</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">&lt;div class="msgBox"&gt;
+ &lt;p&gt;This is a message box&lt;/p&gt;
+ &lt;button&gt;x&lt;/button&gt;
+&lt;/div&gt;</pre>
+
+<p>啊,看完了这么多代码,是不是很累? — 不用担心,你现在没有必要完全知道这些代码的细节! 这里我们只关心函数的结构和使用方式, 下面的例子将展示一些有意思的东西。</p>
+
+<h2 id="调用函数">调用函数</h2>
+
+<p>相信你已经迫不及待的在你的<code>&lt;script&gt;</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>
diff --git a/files/zh-cn/learn/javascript/building_blocks/conditionals/index.html b/files/zh-cn/learn/javascript/building_blocks/conditionals/index.html
new file mode 100644
index 0000000000..c4135c9e29
--- /dev/null
+++ b/files/zh-cn/learn/javascript/building_blocks/conditionals/index.html
@@ -0,0 +1,609 @@
+---
+title: 在代码中做决定 - 条件语句
+slug: learn/JavaScript/Building_blocks/conditionals
+translation_of: Learn/JavaScript/Building_blocks/conditionals
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{NextMenu("Learn/JavaScript/Building_blocks/Looping_code", "Learn/JavaScript/Building_blocks")}}</div>
+
+<p class="summary">在任何的编程语言中,代码需要依靠不同的输入作出决定并且采取行动。例如,在游戏中,如果玩家的生命值变成了0,那么游戏就结束了。在天气应用中,如果在早晨运行,就显示一张日出的图片;如果在晚上,就显示星星和月亮的图片。在这篇文章中,我们将探索在JavaScript中所谓的条件语句是怎样工作的。</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">预备知识:</th>
+ <td>基本的计算机知识,对HTML和CSS有基本的了解,<a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps">JavaScript的第一步</a>。</td>
+ </tr>
+ <tr>
+ <th scope="row">目标:</th>
+ <td>了解怎样在JavaScript中使用条件语句的结构。</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="只需一个条件你就可以拥有……!">只需一个条件你就可以拥有……!</h2>
+
+<p>人类(以及其他的动物)无时无刻不在做决定,这些决定都影响着他们的生活,从小事(“我应该吃一片还是两片饼干”)到重要的大事(“我应该留在我的祖国,在我父亲的农场工作;还是应该去美国学习天体物理学”)。</p>
+
+<p>条件语句结构允许我们来描述在JavaScript中这样的选择,从不得不作出的选择(例如:“一片还是两片”)到产生的结果或这些选择(也许是“吃一片饼干”可能会“仍然感觉饿”,或者是“吃两片饼干”可能会“感觉饱了,但妈妈会因为我吃掉了所有的饼干而骂我”。)</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/13703/cookie-choice-small.png" style="display: block; margin: 0 auto;"></p>
+
+<h2 id="if_..._else_语句">if ... else 语句</h2>
+
+<p><code><font face="Open Sans, arial, sans-serif">让我们看看到目前为止你将会在JavaScript中用到的最常见的条件语句类型 — </font><a href="/en-US/docs/Web/JavaScript/Reference/Statements/if...else">if ... else语句</a>。</code></p>
+
+<h3 id="基本的的_if…else_语法">基本的的 if…else 语法</h3>
+
+<p>基本的if…else语法看起来像下面的 {{glossary("伪代码")}}:</p>
+
+<pre class="notranslate">if (condition) {
+ code to run if condition is true
+} else {
+ run some other code instead
+}</pre>
+
+<p>在这里我们有:</p>
+
+<ol>
+ <li>关键字 if,并且后面跟随括号。</li>
+ <li>要测试的条件,放到括号里(通常是“这个值大于另一个值吗”或者“这个值存在吗”)。这个条件会利用<a href="https://developer.mozilla.org/en-US/Learn/JavaScript/First_steps/Math#Comparison_operators">比较运算符</a>(我们会在最后的模块中讨论)进行比较,并且返回true或者false。</li>
+ <li>一组花括号,在里面我们有一些代码——可以是任何我们喜欢的代码,并且只会在条件语句返回true的时候运行。</li>
+ <li>关键字else。</li>
+ <li>另一组花括号,在里面我们有一些代码——可以是任何我们喜欢的代码,并且当条件语句返回值不是true的话,它才会运行。</li>
+</ol>
+
+<p>这段代码真的非常易懂——它说“<strong>如果(if)条件(condition)</strong>返回true,运行代码A,<strong>否则(else)</strong>运行代码B”</p>
+
+<p>注意:你不一定需要else和第二个花括号——下面的代码也是符合语法规则的:</p>
+
+<pre class="notranslate">if (condition) {
+ code to run if condition is true
+}
+
+run some other code</pre>
+
+<p>不过,这里你需要注意——在这种情况下,第二段代码不被条件语句控制,所以它总会运行,不管条件返回的是true还是false。这不一定是一件坏事,但这可能不是你想要的——你经常只想要运行一段代码或者另一段,而不是两个都运行。</p>
+
+<p>最后,有时候你可能会看到 if…else 语句没有写花括号,像下面的速记风格:</p>
+
+<pre class="notranslate">if (condition) code to run if condition is true
+else run some other code instead</pre>
+
+<p>这是完全有效的代码,但不建议这样使用——因为如果有花括号进行代码切割的话,整体代码被切割为多行代码,更易读和易用。</p>
+
+<h3 id="一个真实的例子">一个真实的例子</h3>
+
+<p>为了更好的理解这种语法,让我们考虑一个真实的例子。想像一个孩子被他的父母要求帮助他们做家务。父母可能会说“嗨,宝贝儿,如果你帮我去购物,我会给你额外的零花钱,这样你就能买得起你想要的玩具了。”在JavaScript中,我们可以这样表示:</p>
+
+<pre class="brush: js notranslate">var shoppingDone = false;
+
+if (shoppingDone === true) {
+ var childsAllowance = 10;
+} else {
+ var childsAllowance = 5;
+}</pre>
+
+<p>这段代码显示的结果是变量 <code>shoppingDone </code>总是返回 <code>false</code>, 意味着对我们的穷孩子来说很失望。如果孩子去购物的话,就需要依靠我们提供机制来使父母把变量 <code>shoppingDone</code> 变成 <code>true</code>。</p>
+
+<div class="note">
+<p><strong>Note</strong>: 你可以看到在<a href="https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/allowance-updater.html">Github上这个例子的完整版本</a>(也可以<a href="http://mdn.github.io/learning-area/javascript/building-blocks/allowance-updater.html">在线运行</a>)</p>
+</div>
+
+<h3 id="else_if">else if</h3>
+
+<p>最后一个例子提供给我们两个选择或结果,但是如果我们想要两个以上呢?</p>
+
+<p>有一种方法来让你的 <code>if…else </code>连接你的额外的选择和结果——使用<code>else if </code>。每一个额外的选择要求放到 <code>if() { ... }</code> 和 <code>else { ... }</code> 里——看看下面更多涉及到的例子,它们属于一个普通的天气预报的应用的一部分。</p>
+
+<pre class="brush: html notranslate">&lt;label for="weather"&gt;Select the weather type today: &lt;/label&gt;
+&lt;select id="weather"&gt;
+ &lt;option value=""&gt;--Make a choice--&lt;/option&gt;
+ &lt;option value="sunny"&gt;Sunny&lt;/option&gt;
+ &lt;option value="rainy"&gt;Rainy&lt;/option&gt;
+ &lt;option value="snowing"&gt;Snowing&lt;/option&gt;
+ &lt;option value="overcast"&gt;Overcast&lt;/option&gt;
+&lt;/select&gt;
+
+&lt;p&gt;&lt;/p&gt;</pre>
+
+<pre class="brush: js notranslate">var select = document.querySelector('select');
+var para = document.querySelector('p');
+
+select.addEventListener('change', setWeather);
+
+function setWeather() {
+ var choice = select.value;
+
+ if (choice === 'sunny') {
+ para.textContent = 'It is nice and sunny outside today. Wear shorts! Go to the beach, or the park, and get an ice cream.';
+ } else if (choice === 'rainy') {
+ para.textContent = 'Rain is falling outside; take a rain coat and a brolly, and don\'t stay out for too long.';
+ } else if (choice === 'snowing') {
+ para.textContent = 'The snow is coming down — it is freezing! Best to stay in with a cup of hot chocolate, or go build a snowman.';
+ } else if (choice === 'overcast') {
+ para.textContent = 'It isn\'t raining, but the sky is grey and gloomy; it could turn any minute, so take a rain coat just in case.';
+ } else {
+ para.textContent = '';
+ }
+}
+
+</pre>
+
+<p>{{ EmbedLiveSample('else_if', '100%', 100) }}</p>
+
+<ol>
+ <li>这里我们有 HTML {{htmlelement("select")}} 元素让我们选择不同的天气,以及一个简单的段落。</li>
+ <li>在 JavaScript 中, 我们同时存储了对 {{htmlelement("select")}} 和 {{htmlelement("p")}} 的引用, 并对 <code>&lt;select&gt;</code> 添加了一个事件监听器,因此,当它的值改变时,<code>setWeather()</code>函数被执行。</li>
+ <li>当函数运行时,我们首先新建了一个 <code>choice</code> 变量去存储当前被选的 <code>&lt;select&gt;</code> 中的值。接着我们用条件判断语句根据 <code>choice</code> 的值选择性的展示段落中的文本。注意 <code>else if() {...}</code>段中的条件是怎么被判断的,除了第一个,它是在 <code>if() {...}中被判断的。</code></li>
+ <li>最后一个 <code>else {...}</code> 中的选择通常被叫做 “最后招数”  — 在所有的条件都不为 true 时其中的代码会被执行。在这个例子中,如果用户没有选择任何一个选项,它会将段落中的文本清空,例如当用户决定重新选择最开始出现的"--Make a choice--"选项时,就会有这样的效果。</li>
+</ol>
+
+<div class="note">
+<p><strong>Note</strong>: 你可以 <a href="https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/simple-else-if.html">在 GitHub 上找到这个例子</a> (也可以<a href="http://mdn.github.io/learning-area/javascript/building-blocks/simple-else-if.html">在线运行</a>。)</p>
+</div>
+
+<h3 id="关于比较运算符">关于比较运算符</h3>
+
+<p>比较运算符是用来判断条件语句中的条件的。我们先回过头来看看<a href="/en-US/Learn/JavaScript/First_steps/Math#Comparison_operators">Basic math in JavaScript — numbers and operators</a> 文章中的比较运算符。我们有如下选择:</p>
+
+<ul>
+ <li><code>===</code> 和 <code>!==</code> — 判断一个值是否严格等于,或不等于另一个。</li>
+ <li><code>&lt;</code> 和 <code>&gt;</code> — 判断一个值是否小于,或大于另一个。</li>
+ <li><code>&lt;=</code> 和 <code>&gt;=</code> — 判断一个值是否小于或等于,或者大于或等于另一个。</li>
+</ul>
+
+<div class="note">
+<p><strong>Note</strong>: 如果你想复习这些内容,可以回顾之前链接上的材料。</p>
+</div>
+
+<p>我们想特别提到测试布尔值(true / false),和一个通用模式,你会频繁遇到它,任何不是 <code>false</code>, <code>undefined</code>, <code>null</code>, <code>0</code>, <code>NaN</code> 的值,或一个空字符串('')在作为条件语句进行测试时实际返回true,因此您可以简单地使用变量名称来测试它是否为真,甚至是否存在(即它不是未定义的)。例如: </p>
+
+<pre class="brush: js notranslate">var cheese = 'Cheddar';
+
+if (cheese) {
+ console.log('Yay! Cheese available for making cheese on toast.');
+} else {
+ console.log('No cheese on toast for you today.');
+}</pre>
+
+<p>而且,回到我们以前关于孩子为自己的父母做家务的例子,你可以这样写:</p>
+
+<pre class="brush: js notranslate">var shoppingDone = false;
+
+if (shoppingDone) { // don't need to explicitly specify '=== true'
+ var childsAllowance = 10;
+} else {
+ var childsAllowance = 5;
+}</pre>
+
+<h3 id="嵌套if_..._else">嵌套if ... else</h3>
+
+<p>将另一个if ... else 语句放在另一个中 - 嵌套它是完全可行的。例如,我们可以更新我们的天气预报应用程序,以显示更多的选择,具体取决于温度:</p>
+
+<pre class="brush: js notranslate">if (choice === 'sunny') {
+ if (temperature &lt; 86) {
+ para.textContent = 'It is ' + temperature + ' degrees outside — nice and sunny. Let\'s go out to the beach, or the park, and get an ice cream.';
+ } else if (temperature &gt;= 86) {
+ para.textContent = 'It is ' + temperature + ' degrees outside — REALLY HOT! If you want to go outside, make sure to put some suncream on.';
+ }
+}</pre>
+
+<p>即使代码全部一起工作,每个if ... else语句完全独立于另一个。</p>
+
+<h3 id="逻辑运算符:_和_!">逻辑运算符:&amp;&amp;  , || 和 !</h3>
+
+<p>如果要测试多个条件,而不需要编写嵌套if ... else语句,逻辑运算符可以帮助您。当在条件下使用时,前两个执行以下操作:</p>
+
+<ul>
+ <li><code>&amp;&amp;</code> — 逻辑与; 使得并列两个或者更多的表达式成为可能,只有当这些表达式每一个都返回<code>true</code>时,整个表达式才会返回<code>true.</code></li>
+ <li><code>||</code> — 逻辑或; 当两个或者更多表达式当中的任何一个返回 <code>true</code> 则整个表达式将会返回 <code>true</code>.</li>
+ <li>!  — 逻辑非; 对一个布尔值取反, 非true返回false,非false返回true.</li>
+</ul>
+
+<p>举一个逻辑 &amp;&amp; 的例子, 刚才的那段代码片段可以写成下面这样:</p>
+
+<pre class="brush: js notranslate">if (choice === 'sunny' &amp;&amp; temperature &lt; 86) {
+ para.textContent = 'It is ' + temperature + ' degrees outside — nice and sunny. Let\'s go out to the beach, or the park, and get an ice cream.';
+} else if (choice === 'sunny' &amp;&amp; temperature &gt;= 86) {
+ para.textContent = 'It is ' + temperature + ' degrees outside — REALLY HOT! If you want to go outside, make sure to put some suncream on.';
+}</pre>
+
+<p>所以,只有当<code>choice === 'sunny'</code>并且<code>temperature &lt; 86</code>都返回<code>true</code>时,第一个代码块才能运行。</p>
+
+<p>让我们快速看一个 <strong>|| </strong>的例子:</p>
+
+<pre class="brush: js notranslate">if (iceCreamVanOutside || houseStatus === 'on fire') {
+ console.log('You should leave the house quickly.');
+} else {
+ console.log('Probably should just stay in then.');
+}</pre>
+
+<p>最后一种类型的逻辑运算符,  <strong>逻辑非<strong><code>!</code></strong>  </strong>运算符表示, 可以用于对一个表达式取否. 让我们把 <strong> 非运算符 </strong>结合上一个例子里的<strong>  或表达式 </strong>看看:</p>
+
+<pre class="brush: js notranslate">if (!(iceCreamVanOutside || houseStatus === 'on fire')) {
+ console.log('Probably should just stay in then.');
+} else {
+ console.log('You should leave the house quickly.');
+}</pre>
+
+<p>在这一段代码中,如果<strong>逻辑或</strong>所在的语句返回 <code>true</code>,则<strong>非运算符</strong>会将其取否,于是整个表达式的返回值将会是<code>false</code>。</p>
+
+<p>您可以在任何结构中随意合并很多个逻辑表达式。接下来的例子将会只在<strong>或运算符</strong>两边的语句同时返回true时才会执行代码,这也就意味着整个<strong>与运算符</strong>语句将会返回true:</p>
+
+<pre class="brush: js notranslate">if ((x === 5 || y &gt; 3 || z &lt;= 10) &amp;&amp; (loggedIn || userName === 'Steve')) {
+ // run the code
+}</pre>
+
+<p>在条件语句中运用<strong>或逻辑运算符</strong>最常见的错误是尝试声明变量后,仅检查该变量一次的情况下赋予很多个都会返回true的值,不同的值之间用 <code>||</code> (或)运算符分隔。比如:</p>
+
+<pre class="example-bad brush: js notranslate">if (x === 5 || 7 || 10 || 20) {
+ // run my code
+}</pre>
+
+<p>在这个例子里 <code>if(...)</code> 里的条件总为真,因为 7 (或者其它非零的数) 的值总是为真. 这个条件实际意思是 "如果x等于5, 或者7为真 — 它总是成立的". 这不是我们想要的逻辑,为了 让它正常工作你必须指定每个或<strong>表达式</strong>两边都是完整的检查:</p>
+
+<pre class="brush: js notranslate">if (x === 5 || x === 7 || x === 10 ||x === 20) {
+ // run my code
+}</pre>
+
+<h2 id="switch语句">switch语句</h2>
+
+<p><code>if...else</code> 语句能够很好地实现条件代码,但是它们不是没有缺点。 它们主要适用于您只有几个选择的情况,每个都需要相当数量的代码来运行,和/或 的条件很复杂的情况(例如多个逻辑运算符)。 对于只想将变量设置一系列为特定值的选项或根据条件打印特定语句的情况,语法可能会很麻烦,特别是如果您有大量选择。</p>
+
+<p><a href="/en-US/docs/Web/JavaScript/Reference/Statements/switch"><code>switch</code> </a>语句在这里是您的朋友 - 他们以单个表达式/值作为输入,然后查看多个选项,直到找到与该值相匹配的选项,执行与之相关的代码。 这里有一些伪代码,可以给你一点灵感:</p>
+
+<pre class="notranslate">switch (expression) {
+ case choice1:
+ run this code
+ break;
+
+ case choice2:
+ run this code instead
+ break;
+
+ // include as many cases as you like
+
+ default:
+ actually, just run this code
+}</pre>
+
+<p>这里我们得到:</p>
+
+<ol>
+ <li>关键字 <code>switch</code>, 后跟一组括号.</li>
+ <li>括号内的表达式或值.</li>
+ <li>关键字 <code>case</code>, 后跟一个选项的表达式/值,后面跟一个冒号.</li>
+ <li>如果选择与表达式匹配,则运行一些代码.</li>
+ <li>一个 <code>break</code> 语句, 分号结尾. 如果先前的选择与表达式/值匹配,则浏览器在此停止执行代码块,并执行switch语句之后的代码.</li>
+ <li>你可以添加任意的 case 选项(选项3-5).</li>
+ <li>关键字 <code>default</code>, 后面跟随和 <code>case</code> 完全相同的代码模式 (选项 3–5), except that <code>default</code> 之后不需要再有选项, 并且您不需要 <code>break</code> 语句, 因为之后没有任何运行代码. 如果之前没有选项匹配,则运行<code>default</code>选项.</li>
+</ol>
+
+<div class="note">
+<p><strong>Note</strong>: <code>default</code> 部分不是必须的 - 如果表达式不可能存在未知值,则可以安全地省略它。 如果有机会,您需要包括它来处理未知的情况。</p>
+</div>
+
+<h3 id="switch语句示例">switch语句示例</h3>
+
+<p>我们来看一个真实的例子 - 我们将重写天气预报应用程序,以改用switch语句:</p>
+
+<pre class="brush: html notranslate">&lt;label for="weather"&gt;Select the weather type today: &lt;/label&gt;
+&lt;select id="weather"&gt;
+ &lt;option value=""&gt;--Make a choice--&lt;/option&gt;
+ &lt;option value="sunny"&gt;Sunny&lt;/option&gt;
+ &lt;option value="rainy"&gt;Rainy&lt;/option&gt;
+ &lt;option value="snowing"&gt;Snowing&lt;/option&gt;
+ &lt;option value="overcast"&gt;Overcast&lt;/option&gt;
+&lt;/select&gt;
+
+&lt;p&gt;&lt;/p&gt;</pre>
+
+<pre class="brush: js notranslate">var select = document.querySelector('select');
+var para = document.querySelector('p');
+
+select.addEventListener('change', setWeather);
+
+
+function setWeather() {
+ var choice = select.value;
+
+ switch (choice) {
+ case 'sunny':
+ para.textContent = 'It is nice and sunny outside today. Wear shorts! Go to the beach, or the park, and get an ice cream.';
+ break;
+ case 'rainy':
+ para.textContent = 'Rain is falling outside; take a rain coat and a brolly, and don\'t stay out for too long.';
+ break;
+ case 'snowing':
+ para.textContent = 'The snow is coming down — it is freezing! Best to stay in with a cup of hot chocolate, or go build a snowman.';
+ break;
+ case 'overcast':
+ para.textContent = 'It isn\'t raining, but the sky is grey and gloomy; it could turn any minute, so take a rain coat just in case.';
+ break;
+ default:
+ para.textContent = '';
+ }
+}</pre>
+
+<p>{{ EmbedLiveSample('A_switch_example', '100%', 100) }}</p>
+
+<div class="note">
+<p><strong>Note</strong>: 你可以 <a href="https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/simple-switch.html">在 GitHub 上找到这个例子</a> (也可以<a href="http://mdn.github.io/learning-area/javascript/building-blocks/simple-switch.html">在线运行</a>。)</p>
+</div>
+
+<h2 id="三元运算符">三元运算符</h2>
+
+<p><font><font>在我们举一些例子之前,我们要介绍一下最后一句语法。</font></font><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator"><font><font>三元或条件运算符</font></font></a><font><font>是一个语法的小点,用于测试一个条件,并返回一个值/表达,如果它是</font></font><code>true</code><font><font>,另一个是</font></font><code>false</code><font><font>-这种情况下是有用的,并且可以占用比</font></font><code>if...else</code><font><font>块较少的代码块。如果你只有两个通过</font></font><code>true</code><font><font>/ </font></font><code>false</code><font><font>条件</font><font>选择</font><font>。</font><font>伪代码看起来像这样:</font></font></p>
+
+<pre class="notranslate">( condition ) ? run this code : run this code instead</pre>
+
+<p>所以我们来看一个简单的例子:</p>
+
+<pre class="brush: js notranslate">var greeting = ( isBirthday ) ? 'Happy birthday Mrs. Smith — we hope you have a great day!' : 'Good morning Mrs. Smith.';</pre>
+
+<p><font><font>在这里我们有一个变量叫做</font></font><code>isBirthday</code><font><font>- 如果它是</font></font><code>true</code><font><font>,我们给客人一个生日快乐的消息; </font><font>如果不是,我们给她标准的每日问候。</font></font></p>
+
+<h3 id="三元运算符示例">三元运算符示例</h3>
+
+<p>你不需要用三元运算符设置变量值; 你也可以运行任何你喜欢的函数或代码行。以下实例显示了一个简单的主题选择器,其中该站点的样式应用了三元运算符。</p>
+
+<pre class="brush: html notranslate">&lt;label for="theme"&gt;Select theme: &lt;/label&gt;
+&lt;select id="theme"&gt;
+ &lt;option value="white"&gt;White&lt;/option&gt;
+ &lt;option value="black"&gt;Black&lt;/option&gt;
+&lt;/select&gt;
+
+&lt;h1&gt;This is my website&lt;/h1&gt;</pre>
+
+<pre class="brush: js notranslate">var select = document.querySelector('select');
+var html = document.querySelector('html');
+document.body.style.padding = '10px';
+
+function update(bgColor, textColor) {
+ html.style.backgroundColor = bgColor;
+ html.style.color = textColor;
+}
+
+select.onchange = function() {
+ ( select.value === 'black' ) ? update('black','white') : update('white','black');
+}
+</pre>
+
+<p>{{ EmbedLiveSample('Ternary_operator_example', '100%', 300) }}</p>
+
+<p><font><font>在这里,我们有一个</font></font><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select" title="HTML &lt;select>元素表示一个控件,提供一个选项菜单:"><code>&lt;select&gt;</code></a><font><font>选择主题(黑色或白色)</font><font>的</font><font>元素,加上一个简单</font></font><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h1" title="标题元素实现六个级别的文档标题,&lt;h1>是最重要的,&lt;h6>是最少的。 标题元素简要介绍了它介绍的部分的主题。 标题信息可以由用户代理使用,例如,自动构建文档的目录。"><code>&lt;h1&gt;</code></a><font><font>的显示网站标题。</font><font>我们也有一个函数叫做</font></font><code>update()</code><font><font>,它将两种颜色作为参数(输入)。</font><font>网站的背景颜色设置为第一个提供的颜色,其文本颜色设置为第二个提供的颜色。</font></font></p>
+
+<p><font><font>最后,我们还有一个</font></font><a href="https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onchange"><font><font>onchange</font></font></a><font><font>事件监听器,用于运行一个包含三元运算符的函数。</font><font>它以测试条件开始</font></font><code>select.value === 'black'</code><font><font>。</font><font>如果这返回</font></font><code>true</code><font><font>,我们运行</font></font><code>update()</code><font><font>带有黑色和白色参数</font><font>的</font><font>函数,这意味着我们最终得到黑色的背景颜色和白色的文字颜色。</font><font>如果返回</font></font><code>false</code><font><font>,我们运行</font></font><code>update()</code><font><font>带有白色和黑色参数</font><font>的</font><font>函数,这意味着站点颜色被反转。</font></font></p>
+
+<div class="note">
+<p><strong>Note</strong>: 你可以 <a href="https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/simple-ternary.html">在 GitHub 上找到这个例子</a> (也可以<a href="http://mdn.github.io/learning-area/javascript/building-blocks/simple-ternary.html">在线运行</a>。)</p>
+</div>
+
+<h2 id="主动学习:一个简单的日历">主动学习:一个简单的日历</h2>
+
+<p><font><font>在这个例子中,您将帮助我们完成一个简单的日历应用程序。</font><font>在你的代码中:</font></font></p>
+
+<ul>
+ <li><font><font>一个</font></font><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select" title="HTML &lt;select>元素表示一个控件,提供一个选项菜单:"><code>&lt;select&gt;</code></a><font><font>元素,允许用户在不同月份之间进行选择。</font></font></li>
+ <li><code>onchange</code><font><font>检测</font></font><code>&lt;select&gt;</code><font><font>菜单中</font><font>选择的值何时</font><font>更改</font><font>的</font><font>事件处理程序</font><font>。</font></font></li>
+ <li><font><font>一个函数叫做</font></font><code>createCalendar()</code><font><font>绘制日历并在</font></font><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h1" title="标题元素实现六个级别的文档标题,&lt;h1>是最重要的,&lt;h6>是最少的。 标题元素简要介绍了它介绍的部分的主题。 标题信息可以由用户代理使用,例如,自动构建文档的目录。"><code>&lt;h1&gt;</code></a><font><font>元素中</font><font>显示正确的月份</font><font>。</font></font></li>
+</ul>
+
+<p><font><font>我们需要你在</font></font><code>onchange</code><font><font>处理函数中</font><font>写一个条件语句</font><font>,就在</font></font><code>// ADD CONDITIONAL HERE</code><font><font>任务的</font><font>下面  </font><font>。</font><font>这应该:</font></font></p>
+
+<ol>
+ <li><font><font>查看所选月份(存储在</font></font><code>choice</code><font><font>变量中,这将是</font></font><code>&lt;select&gt;</code><font><font>值更改后的元素值,例如“1月”)。</font></font></li>
+ <li><font><font>设置一个被调用</font></font><code>days</code><font><font>为等于所选月份天数的</font><font>变量</font><font>。</font><font>为此,您必须查看一年中每个月的天数。</font><font>为了这个例子的目的,你可以忽略闰年。</font></font></li>
+</ol>
+
+<p>提示:</p>
+
+<ul>
+ <li><font><font>建议您使用逻辑或将多个月组合成一个单一条件; </font><font>他们中的许多人共享相同的天数。</font></font></li>
+ <li><font><font>考虑最常用的天数,并将其用作默认值。</font></font></li>
+</ul>
+
+<p><font>如果您犯了错误,您可以随时使用“Reset”按钮重置该示例。</font><font>如果真的卡住了,请按“Show solution”查看解决方案。</font></p>
+
+<div class="hidden">
+<h6 id="Playable_code">Playable code</h6>
+
+<pre class="brush: html notranslate">&lt;div class="output" style="height: 500px;overflow: auto;"&gt;
+ &lt;label for="month"&gt;Select month: &lt;/label&gt;
+ &lt;select id="month"&gt;
+ &lt;option value="January"&gt;January&lt;/option&gt;
+ &lt;option value="February"&gt;February&lt;/option&gt;
+ &lt;option value="March"&gt;March&lt;/option&gt;
+ &lt;option value="April"&gt;April&lt;/option&gt;
+ &lt;option value="May"&gt;May&lt;/option&gt;
+ &lt;option value="June"&gt;June&lt;/option&gt;
+ &lt;option value="July"&gt;July&lt;/option&gt;
+ &lt;option value="August"&gt;August&lt;/option&gt;
+ &lt;option value="September"&gt;September&lt;/option&gt;
+ &lt;option value="October"&gt;October&lt;/option&gt;
+ &lt;option value="November"&gt;November&lt;/option&gt;
+ &lt;option value="December"&gt;December&lt;/option&gt;
+ &lt;/select&gt;
+
+ &lt;h1&gt;&lt;/h1&gt;
+
+ &lt;ul&gt;&lt;/ul&gt;
+&lt;/div&gt;
+
+&lt;hr&gt;
+
+&lt;textarea id="code" class="playable-code" style="height: 500px;"&gt;
+var select = document.querySelector('select');
+var list = document.querySelector('ul');
+var h1 = document.querySelector('h1');
+
+select.onchange = function() {
+ var choice = select.value;
+
+ // ADD CONDITIONAL HERE
+
+ createCalendar(days, choice);
+}
+
+function createCalendar(days, choice) {
+ list.innerHTML = '';
+ h1.textContent = choice;
+ for (var i = 1; i &lt;= days; i++) {
+ var listItem = document.createElement('li');
+ listItem.textContent = i;
+ list.appendChild(listItem);
+ }
+}
+
+createCalendar(31,'January');
+&lt;/textarea&gt;
+
+&lt;div class="playable-buttons"&gt;
+ &lt;input id="reset" type="button" value="Reset"&gt;
+ &lt;input id="solution" type="button" value="Show solution"&gt;
+&lt;/div&gt;
+</pre>
+
+<pre class="brush: css notranslate">.output * {
+ box-sizing: border-box;
+}
+
+.output ul {
+ padding-left: 0;
+}
+
+.output li {
+ display: block;
+ float: left;
+ width: 25%;
+ border: 2px solid white;
+ padding: 5px;
+ height: 40px;
+ background-color: #4A2DB6;
+ color: white;
+}
+</pre>
+
+<pre class="brush: js notranslate">var textarea = document.getElementById('code');
+var reset = document.getElementById('reset');
+var solution = document.getElementById('solution');
+var code = textarea.value;
+
+function updateCode() {
+ eval(textarea.value);
+}
+
+reset.addEventListener('click', function() {
+ textarea.value = code;
+ updateCode();
+});
+
+solution.addEventListener('click', function() {
+ textarea.value = jsSolution;
+ updateCode();
+});
+
+var jsSolution = 'var select = document.querySelector(\'select\');\nvar list = document.querySelector(\'ul\');\nvar h1 = document.querySelector(\'h1\');\n\nselect.onchange = function() {\n var choice = select.value;\n var days = 31;\n if(choice === \'February\') {\n days = 28;\n } else if(choice === \'April\' || choice === \'June\' || choice === \'September\'|| choice === \'November\') {\n days = 30;\n }\n\n createCalendar(days, choice);\n}\n\nfunction createCalendar(days, choice) {\n list.innerHTML = \'\';\n h1.textContent = choice;\n for(var i = 1; i &lt;= days; i++) {\n var listItem = document.createElement(\'li\');\n listItem.textContent = i;\n list.appendChild(listItem);\n }\n }\n\ncreateCalendar(31,\'January\');';
+
+textarea.addEventListener('input', updateCode);
+window.addEventListener('load', updateCode);
+</pre>
+</div>
+
+<p>{{ EmbedLiveSample('Playable_code', '100%', 1110) }}</p>
+
+<h2 id="主动学习:更多颜色选择!">主动学习:更多颜色选择!</h2>
+
+<p><font><font>在这个例子中,您将要采取我们前面看到的三元运算符示例,并将三元运算符转换为一个switch语句,这将允许我们对简单的网站应用更多的选择。</font><font>看看</font></font><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select" title="HTML &lt;select>元素表示一个控件,提供一个选项菜单:"><code>&lt;select&gt;</code></a><font><font>- 这次你会看到它不是两个主题选项,而是五个。</font><font>您需要在</font></font><code>// ADD SWITCH STATEMENT</code><font><font>注释</font><font>下面添加一个switch语句</font><font>:</font></font></p>
+
+<ul>
+ <li><font><font>它应该接受</font></font><code>choice</code><font><font>变量作为其输入表达式。</font></font></li>
+ <li><font><font>对于每种情况,选择应该等于可以选择的可能值之一,即白色,黑色,紫色,黄色或迷幻色。</font></font></li>
+ <li><font><font>对于每种情况,应运行</font></font><code>update()</code><font><font>函数,并传递两个颜色值,第一个颜色值为背景颜色,第二个颜色值为文本颜色。</font><font>请记住,颜色值是字符串,因此需要用引号括起来。</font></font></li>
+</ul>
+
+<p><font>如果您犯了错误,您可以随时使用“Reset”按钮重置该示例。</font><font>如果真的卡住了,请按“Show solution”查看解决方案。</font></p>
+
+<div class="hidden">
+<h6 id="Playable_code_2">Playable code 2</h6>
+
+<pre class="brush: html notranslate">&lt;div class="output" style="height: 300px;"&gt;
+ &lt;label for="theme"&gt;Select theme: &lt;/label&gt;
+ &lt;select id="theme"&gt;
+ &lt;option value="white"&gt;White&lt;/option&gt;
+ &lt;option value="black"&gt;Black&lt;/option&gt;
+ &lt;option value="purple"&gt;Purple&lt;/option&gt;
+ &lt;option value="yellow"&gt;Yellow&lt;/option&gt;
+ &lt;option value="psychedelic"&gt;Psychedelic&lt;/option&gt;
+ &lt;/select&gt;
+
+ &lt;h1&gt;This is my website&lt;/h1&gt;
+&lt;/div&gt;
+
+&lt;hr&gt;
+
+&lt;textarea id="code" class="playable-code" style="height: 450px;"&gt;
+var select = document.querySelector('select');
+var html = document.querySelector('.output');
+
+select.onchange = function() {
+ var choice = select.value;
+
+ // ADD SWITCH STATEMENT
+}
+
+function update(bgColor, textColor) {
+ html.style.backgroundColor = bgColor;
+ html.style.color = textColor;
+}&lt;/textarea&gt;
+
+&lt;div class="playable-buttons"&gt;
+ &lt;input id="reset" type="button" value="Reset"&gt;
+ &lt;input id="solution" type="button" value="Show solution"&gt;
+&lt;/div&gt;
+</pre>
+
+<pre class="brush: js notranslate">var textarea = document.getElementById('code');
+var reset = document.getElementById('reset');
+var solution = document.getElementById('solution');
+var code = textarea.value;
+
+function updateCode() {
+ eval(textarea.value);
+}
+
+reset.addEventListener('click', function() {
+ textarea.value = code;
+ updateCode();
+});
+
+solution.addEventListener('click', function() {
+ textarea.value = jsSolution;
+ updateCode();
+});
+
+var jsSolution = 'var select = document.querySelector(\'select\');\nvar html = document.querySelector(\'.output\');\n\nselect.onchange = function() {\n var choice = select.value;\n\n switch(choice) {\n case \'black\':\n update(\'black\',\'white\');\n break;\n case \'white\':\n update(\'white\',\'black\');\n break;\n case \'purple\':\n update(\'purple\',\'white\');\n break;\n case \'yellow\':\n update(\'yellow\',\'darkgray\');\n break;\n case \'psychedelic\':\n update(\'lime\',\'purple\');\n break;\n }\n}\n\nfunction update(bgColor, textColor) {\n html.style.backgroundColor = bgColor;\n html.style.color = textColor;\n}';
+
+textarea.addEventListener('input', updateCode);
+window.addEventListener('load', updateCode);
+</pre>
+</div>
+
+<p>{{ EmbedLiveSample('Playable_code_2', '100%', 850) }}</p>
+
+<h2 id="结论">结论</h2>
+
+<p><font><font>这就是现在您真正需要了解的JavaScript中的条件结构!</font><font>我相信你会理解这些概念,并轻松地通过这些例子; </font><font>如果有什么不明白的,请随时阅读文章,或者</font></font><a href="https://developer.mozilla.org/en-US/Learn#Contact_us"><font><font>联系我们</font></font></a><font><font>寻求帮助。</font></font></p>
+
+<h2 id="参见">参见</h2>
+
+<ul>
+ <li><a href="https://developer.mozilla.org/en-US/Learn/JavaScript/First_steps/Math#Comparison_operators"><font><font>比较运算符</font></font></a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Control_flow_and_error_handling#Conditional_statements"><font><font>条件声明详细</font></font></a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/if...else"><font><font>如果...其他参考</font></font></a></li>
+ <li><font><font><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator">条件(三元)运算符引用</a></font></font></li>
+</ul>
+
+<p>{{NextMenu("Learn/JavaScript/Building_blocks/Looping_code", "Learn/JavaScript/Building_blocks")}}</p>
diff --git a/files/zh-cn/learn/javascript/building_blocks/events/index.html b/files/zh-cn/learn/javascript/building_blocks/events/index.html
new file mode 100644
index 0000000000..605a4efae0
--- /dev/null
+++ b/files/zh-cn/learn/javascript/building_blocks/events/index.html
@@ -0,0 +1,564 @@
+---
+title: 事件介绍
+slug: Learn/JavaScript/Building_blocks/Events
+tags:
+ - Event
+ - Guide
+ - JavaScript
+ - 事件
+ - 事件处理
+ - 初学者
+translation_of: Learn/JavaScript/Building_blocks/Events
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/JavaScript/Building_blocks/Return_values","Learn/JavaScript/Building_blocks/Image_gallery", "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="/en-US/docs/Learn/JavaScript/First_steps">JavaScript first steps</a>.</td>
+ </tr>
+ <tr>
+ <th scope="row">目标:</th>
+ <td>了解事件的基本理论,它们怎么在浏览器上运行的,以及在不同的编程环境下事件有何不同。</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="一系列事件">一系列事件</h2>
+
+<p>就像上面提到的, <strong>事件</strong>是您在编程时系统内发生的动作或者发生的事情——系统会在事件出现时产生或触发某种信号,并且会提供一个自动加载某种动作(列如:运行一些代码)的机制,比如在一个机场,当跑道清理完成,飞机可以起飞时,飞行员会收到一个信号,因此他们开始起飞。</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/14077/MDN-mozilla-events-runway.png" style="display: block; margin: 0px auto;"></p>
+
+<p>在 Web 中, 事件在浏览器窗口中被触发并且通常被绑定到窗口内部的特定部分 — 可能是一个元素、一系列元素、被加载到这个窗口的 HTML 代码或者是整个浏览器窗口。举几个可能发生的不同事件:</p>
+
+<ul>
+ <li>用户在某个元素上点击鼠标或悬停光标。</li>
+ <li>用户在键盘中按下某个按键。</li>
+ <li>用户调整浏览器的大小或者关闭浏览器窗口。</li>
+ <li>一个网页停止加载。</li>
+ <li>提交表单。</li>
+ <li>播放、暂停、关闭视频。</li>
+ <li>发生错误。</li>
+</ul>
+
+<p>如果您想看看更多其他的事件 ,请移步至MDN的<a href="/en-US/docs/Web/Events">Event reference</a>。</p>
+
+<p>每个可用的事件都会有一个<strong>事件处理器</strong>,也就是事件触发时会运行的代码块。当我们定义了一个用来回应事件被激发的代码块的时候,我们说我们<strong>注册了一个事件处理器</strong>。注意事件处理器有时候被叫做<strong>事件监听器</strong>——从我们的用意来看这两个名字是相同的,尽管严格地来说这块代码既监听也处理事件。监听器留意事件是否发生,然后处理器就是对事件发生做出的回应。</p>
+
+<div class="note">
+<p><strong>注:</strong> 网络事件不是 JavaScript 语言的核心——它们被定义成内置于浏览器的 JavaScript APIs。</p>
+</div>
+
+<h3 id="一个简单的例子">一个简单的例子</h3>
+
+<p>让我们看一个简单的例子。前面您已经见到过很多事件和事件监听器,现在我们概括一下以巩固我们的知识。在接下来的例子中,我们的页面中只有一个 button,按下时,背景会变成随机的一种颜色。</p>
+
+<pre class="brush: html notranslate">&lt;button&gt;Change color&lt;/button&gt;</pre>
+
+<div class="hidden">
+<pre class="brush: css notranslate">button { margin: 10px };</pre>
+</div>
+
+<p>JavaScript代码如下所示:</p>
+
+<pre class="brush: js notranslate">const btn = document.querySelector('button');
+
+function random(number) {
+ return Math.floor(Math.random()*(number+1));
+}
+
+btn.onclick = function() {
+ const rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
+ document.body.style.backgroundColor = rndCol;
+}</pre>
+
+<p>我们使用 <code>btn</code> 变量存储 button,并使用了<code>Document.querySelector()</code> 函数。我们也定义了一个返回随机数字的函数。代码第三部分就是事件处理器。<code>btn</code>变量指向 button 元素,在 button 这种对象上可触发一系列的事件,因此也就可以使用事件处理器。我们通过将一个匿名函数(这个赋值函数包括生成随机色并赋值给背景色的代码)赋值给“点击”事件处理器参数,监听“点击”这个事件。</p>
+
+<p>只要点击事件在<code>&lt;button&gt;</code>元素上触发,该段代码就会被执行。即每当用户点击它时,都会运行此段代码。</p>
+
+<p>示例输出如下:</p>
+
+<p>{{ EmbedLiveSample('A_simple_example', '100%', 200, "", "", "hide-codepen-jsfiddle") }}</p>
+
+<h3 id="这不仅应用在网页上">这不仅应用在网页上</h3>
+
+<p>值得注意的是并不是只有 JavaScript 使用事件——大多的编程语言都有这种机制,并且它们的工作方式不同于 JavaScript。实际上,JavaScript 网页上的事件机制不同于在其他环境中的事件机制。</p>
+
+<p>比如, <a href="/en-US/docs/Learn/Server-side/Express_Nodejs">Node.js</a> 是一种非常流行的允许开发者使用 JavaScript 来建造网络和服务器端应用的运行环境。<a href="https://nodejs.org/docs/latest-v5.x/api/events.html">Node.js event model</a> 依赖定期监听事件的监听器和定期处理事件的处理器——虽然听起来好像差不多,但是实现两者的代码是非常不同的,Node.js 使用像 on ( ) 这样的函数来注册一个事件监听器,使用 once ( ) 这样函数来注册一个在运行一次之后注销的监听器。 <a href="https://nodejs.org/docs/latest-v5.x/api/http.html#http_event_connect">HTTP connect event docs</a> 提供了很多例子。</p>
+
+<p>另外一个例子:您可以使用 JavaScript 来开发跨浏览器的插件(使用 <a href="/en-US/docs/Mozilla/Add-ons/WebExtensions">WebExtensions</a> 开发技术。事件模型和网站的事件模型是相似的,仅有一点点不同——事件监听属性是大驼峰的(如<code>onMessage</code>而不是<code>onmessage</code>),还需要与 <code>addListener</code> 函数结合, 参见 <a href="/en-US/Add-ons/WebExtensions/API/runtime/onMessage#Examples">runtime.onMessage page</a> 上的一个例子。</p>
+
+<p>您现在不需要掌握这些,我们只想表明不同的编程环境下事件机制是不同的,</p>
+
+<h2 id="使用网页事件的方式">使用网页事件的方式</h2>
+
+<p>您可以通过多种不同的方法将事件侦听器代码添加到网页,以便在关联的事件被触发时运行它。在本节中,我们将回顾不同的机制,并讨论应该使用哪些机制。</p>
+
+<h3 id="事件处理器属性">事件处理器属性</h3>
+
+<p><em>这些是我们的课程中最常见到的代码 - 存在于事件处理程序过程的属性中</em>。回到上面的例子:</p>
+
+<pre class="brush: js notranslate">const btn = document.querySelector('button');
+
+btn.onclick = function() {
+ const rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
+ document.body.style.backgroundColor = rndCol;
+}</pre>
+
+<p>这个 <code><a href="/en-US/docs/Web/API/GlobalEventHandlers/onclick">onclick</a></code> 是被用在这个情景下的事件处理器的属性,它就像 button 其他的属性(如 <code><a href="/en-US/docs/Web/API/Node/textContent">btn.textContent</a></code>, or <code><a href="/en-US/docs/Web/API/HTMLElement/style">btn.style</a></code>), 但是有一个特别的地方——当您将一些代码赋值给它的时候,只要事件触发代码就会运行。</p>
+
+<p>您也可以将一个有名字的函数赋值给事件处理参数(正如我们在 <a href="/en-US/docs/Learn/JavaScript/Building_blocks/Build_your_own_function">Build your own function</a> 中看到的),下面的代码也是这样工作的:</p>
+
+<pre class="brush: js notranslate">const btn = document.querySelector('button');
+
+function bgChange() {
+ const rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
+ document.body.style.backgroundColor = rndCol;
+}
+
+btn.onclick = bgChange;</pre>
+
+<p>有很多事件处理参数可供选择,我们来做一个实验。</p>
+
+<p>首先将 <a href="https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/events/random-color-eventhandlerproperty.html">random-color-eventhandlerproperty.html</a> 复制到本地,然后用浏览器打开。别慌,这只是我们之前已经进行过的一个简单随机颜色的示例的代码复制。将 <code>btn.onclick</code> 依次换成其他值,在浏览器中观察效果。</p>
+
+<ul>
+ <li><code><a href="/en-US/docs/Web/API/GlobalEventHandlers/onfocus">btn.onfocus</a></code>及<code><a href="/en-US/docs/Web/API/GlobalEventHandlers/onblur">btn.onblur</a></code> — 颜色将于按钮被置于焦点或解除焦点时改变(尝试使用Tab移动至按钮上,然后再移开)。这些通常用于显示有关如何在置于焦点时填写表单字段的信息,或者如果表单字段刚刚填入不正确的值,则显示错误消息。</li>
+ <li><code><a href="/en-US/docs/Web/API/GlobalEventHandlers/ondblclick">btn.ondblclick</a></code> — 颜色将仅于按钮被双击时改变。</li>
+ <li><code><a href="/en-US/docs/Web/API/GlobalEventHandlers/onkeypress">window.onkeypress</a></code>, <code><a href="/en-US/docs/Web/API/GlobalEventHandlers/onkeydown">window.onkeydown</a></code>, <code><a href="/en-US/docs/Web/API/GlobalEventHandlers/onkeyup">window.onkeyup</a></code> — 当按钮被按下时颜色会发生改变. <code>keypress</code> 指的是通俗意义上的按下按钮 (按下并松开), 而 <code>keydown</code> 和 <code>keyup</code> 指的是按键动作的一部分,分别指按下和松开. 注意如果你将事件处理器添加到按钮本身,它将不会工作 — 我们只能将它添加到代表整个浏览器窗口的 <a href="/en-US/docs/Web/API/Window">window</a>对象中。</li>
+ <li><code><a href="/en-US/docs/Web/API/GlobalEventHandlers/onmouseover">btn.onmouseover</a></code> 和 <code><a href="/en-US/docs/Web/API/GlobalEventHandlers/onmouseout">btn.onmouseout</a></code> — 颜色将会在鼠标移入按钮上方时发生改变, 或者当它从按钮移出时.</li>
+</ul>
+
+<p>一些事件非常通用,几乎在任何地方都可以用(比如 onclick 几乎可以用在几乎每一个元素上),然而另一些元素就只能在特定场景下使用,比如我们只能在 video 元素上使用 <a href="/en-US/docs/Web/API/GlobalEventHandlers/GlobalEventHandlers.onplay">onplay</a> 。</p>
+
+<h3 id="行内事件处理器_-_请勿使用">行内事件处理器 - 请勿使用</h3>
+
+<p>你也许在你的代码中看到过这么一种写法:</p>
+
+<pre class="brush: html notranslate">&lt;button onclick="bgChange()"&gt;Press me&lt;/button&gt;
+</pre>
+
+<pre class="brush: js notranslate">function bgChange() {
+ const rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
+ document.body.style.backgroundColor = rndCol;
+}</pre>
+
+<div class="note">
+<p><strong>Note</strong>: 您可以在<a href="https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/events/random-color-eventhandlerattributes.html">GitHub</a>上找到这个示例的完整源代码(也可以<a href="http://mdn.github.io/learning-area/javascript/building-blocks/events/random-color-eventhandlerattributes.html">在线运行</a>).</p>
+</div>
+
+<p>在Web上注册事件处理程序的最早方法是类似于上面所示的<strong>事件处理程序HTML属性</strong>(也称为内联事件处理程序)—属性值实际上是当事件发生时要运行的JavaScript代码。上面的例子中调用一个在{{htmlelement("script")}}元素在同一个页面上,但也可以直接在属性内插入JavaScript,例如:</p>
+
+<pre class="brush: html notranslate">&lt;button onclick="alert('Hello, this is my old-fashioned event handler!');"&gt;Press me&lt;/button&gt;</pre>
+
+<p>你会发现HTML属性等价于对许多事件处理程序的属性;但是,你不应该使用这些 —— 他们被认为是不好的做法。使用一个事件处理属性似乎看起来很简单,如果你只是在做一些非常快的事情,但很快就变得难以管理和效率低下。</p>
+
+<p>一开始,您不应该混用 HTML 和 JavaScript,因为这样文档很难解析——最好的办法是只在一块地方写 JavaScript 代码。</p>
+
+<p>即使在单一文件中,内置事件处理器也不是一个好主意。一个按钮看起来还好,但是如果有一百个按钮呢?您得在文件中加上100个属性。这很快就会成为维护人员的噩梦。使用 Java Script,您可以给网页中的 button 都加上事件处理器。就像下面这样:</p>
+
+<pre class="brush: js notranslate">const buttons = document.querySelectorAll('button');
+
+for (let i = 0; i &lt; buttons.length; i++) {
+ buttons[i].onclick = bgChange;
+}</pre>
+
+<div class="note">
+<p><strong>注释</strong>: 将您的编程逻辑与内容分离也会让您的站点对搜索引擎更加友好。</p>
+</div>
+
+<h3 id="addEventListener_和removeEventListener">addEventListener() 和removeEventListener()</h3>
+
+<p>新的事件触发机制被定义在 <a href="https://www.w3.org/TR/DOM-Level-2-Events/">Document Object Model (DOM) Level 2 Events</a> Specification, 这个细则给浏览器提供了一个函数 — <code><a href="/en-US/docs/Web/API/EventTarget/addEventListener">addEventListener()</a></code>。这个函数和事件处理属性是类似的,但是语法略有不同。我们可以重写上面的随机颜色背景代码:</p>
+
+<pre class="brush: js notranslate">const btn = document.querySelector('button');
+
+function bgChange() {
+ const rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
+ document.body.style.backgroundColor = rndCol;
+}
+
+btn.addEventListener('click', bgChange);</pre>
+
+<div class="note">
+<p><strong>注释</strong>: 您可以在<a href="https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/events/random-color-addeventlistener.html">Github</a>上找到这个示例的完整源代码(也可以<a href="http://mdn.github.io/learning-area/javascript/building-blocks/events/random-color-addeventlistener.html"> 在线运行</a>)。</p>
+</div>
+
+<p><code>在addEventListener()</code> 函数中, 我们具体化了两个参数——我们想要将处理器应用上去的事件名称,和包含我们用来回应事件的函数的代码。注意将这些代码全部放到一个匿名函数中是可行的:</p>
+
+<pre class="brush: js notranslate">btn.addEventListener('click', function() {
+ var rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
+ document.body.style.backgroundColor = rndCol;
+});
+</pre>
+
+<p>这个机制带来了一些相较于旧方式的优点。有一个相对应的方法,<code><a href="/en-US/docs/Web/API/EventTarget/removeEventListener">removeEventListener()</a>,</code>这个方法移除事件监听器。例如,下面的代码将会移除上个代码块中的事件监听器:</p>
+
+<pre class="brush: js notranslate">btn.removeEventListener('click', bgChange);</pre>
+
+<p>在这个简单的、小型的项目中可能不是很有用,但是在大型的、复杂的项目中就非常有用了,可以非常高效地清除不用的事件处理器,另外在其他的一些场景中也非常有效——比如您需要在不同环境下运行不同的事件处理器,您只需要恰当地删除或者添加事件处理器即可。</p>
+
+<p>您也可以给同一个监听器注册多个处理器,下面这种方式不能实现这一点:</p>
+
+<pre class="brush: js notranslate">myElement.onclick = functionA;
+myElement.onclick = functionB;</pre>
+
+<p>第二行会覆盖第一行,但是下面这种方式就会正常工作了:</p>
+
+<pre class="brush: js notranslate">myElement.addEventListener('click', functionA);
+myElement.addEventListener('click', functionB);</pre>
+
+<p>当元素被点击时两个函数都会工作:</p>
+
+<p>此外,该事件机制还提供了许多其他强大的特性和选项。这对于本文来说有点超出范围,但是如果您想要阅读它们,请查看<code><a href="https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener">addEventListener()</a></code>和<code><a href="https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener">removeEventListener()</a></code>参考页面。</p>
+
+<h3 id="我该使用哪种机制?">我该使用哪种机制?</h3>
+
+<p>在三种机制中,您绝对不应该使用HTML事件处理程序属性 - 这些属性已经过时了,而且也是不好的做法,如上所述.</p>
+
+<p>另外两种是相对可互换的,至少对于简单的用途:</p>
+
+<ul>
+ <li>事件处理程序属性功能和选项会更少,但是具有更好的跨浏览器兼容性(在Internet Explorer 8的支持下),您应该从这些开始学起。</li>
+ <li>DOM Level 2 Events (<code>addEventListener()</code>, etc.) 更强大,但也可以变得更加复杂,并且支持不足(只支持到Internet Explorer 9)。 但是您也应该尝试这个方法,并尽可能地使用它们。</li>
+</ul>
+
+<p>第三种机制(DOM Level 2 Events (<code>addEventListener()</code>, etc.))的主要优点是,如果需要的话,可以使用<code>removeEventListener()</code>删除事件处理程序代码,而且如果有需要,您可以向同一类型的元素添加多个监听器。例如,您可以在一个元素上多次调用<code>addEventListener('click', function() { ... })</code>,并可在第二个参数中指定不同的函数。对于事件处理程序属性来说,这是不可能的,因为后面任何设置的属性都会尝试覆盖较早的属性,例如:</p>
+
+<pre class="brush: js notranslate">element.onclick = function1;
+element.onclick = function2;
+etc.</pre>
+
+<div class="note">
+<p><strong>注解</strong>:如果您在工作中被要求支持比Internet Explorer 8更老的浏览器,那么您可能会遇到困难,因为这些古老的浏览器会使用与现代浏览器不同的事件处理模型。但是不要害怕,大多数 JavaScript 库(例如 jQuery )都内置了能够跨浏览器差异的函数。在你学习 JavaScript 旅程里的这个阶段,不要太担心这个问题。</p>
+</div>
+
+<h2 id="其他事件概念">其他事件概念</h2>
+
+<p>本节我们将简要介绍一些与事件相关的高级概念。在这一点并不需要完全理解透彻,但它可能有助于你解释一些经常会遇到的代码模式。</p>
+
+<h3 id="事件对象">事件对象</h3>
+
+<p>有时候在事件处理函数内部,您可能会看到一个固定指定名称的参数,例如<code>event</code>,<code>evt</code>或简单的<code>e</code>。 这被称为<strong>事件对象</strong>,它被自动传递给事件处理函数,以提供额外的功能和信息。 例如,让我们稍稍重写一遍我们的随机颜色示例:</p>
+
+<pre class="brush: js notranslate">function bgChange(e) {
+ const rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
+ e.target.style.backgroundColor = rndCol;
+ console.log(e);
+}
+
+btn.addEventListener('click', bgChange);</pre>
+
+<div class="note">
+<p><strong>Note</strong>: 您可以在Github上查看这个示例的 <a href="https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/events/random-color-eventobject.html">完整代码</a> ,或者在这里查看 <a href="http://mdn.github.io/learning-area/javascript/building-blocks/events/random-color-eventobject.html">实时演示</a>。</p>
+</div>
+
+<p>在这里,您可以看到我们在函数中包括一个事件对象<code>e</code>,并在函数中设置背景颜色样式在<code>e.target上</code> - 它指的是按钮本身。 事件对象 <code>e</code> 的<code>target</code>属性始终是事件刚刚发生的元素的引用。 所以在这个例子中,我们在按钮上设置一个随机的背景颜色,而不是页面。</p>
+
+<div class="note">
+<p><strong>Note</strong>: 您可以使用任何您喜欢的名称作为事件对象 - 您只需要选择一个名称,然后可以在事件处理函数中引用它。 开发人员最常使用 e / evt / event,因为它们很简单易记。 坚持标准总是很好。</p>
+</div>
+
+<p>当您要在多个元素上设置相同的事件处理程序时,<code>e.target</code>非常有用,并且在发生事件时对所有元素执行某些操作.  例如,你可能有一组16块方格,当它们被点击时就会消失。用e.target总是能准确选择当前操作的东西(方格)并执行操作让它消失,而不是必须以更困难的方式选择它。在下面的示例中(请参见<a href="https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/events/useful-eventtarget.html">useful-eventtarget.html</a>完整代码;也可以在线运行<a href="http://mdn.github.io/learning-area/javascript/building-blocks/events/useful-eventtarget.html">running live</a>)我们使用JavaScript创建了16个<code>&lt;div&gt;</code>元素。接着我们使用 <code>document.querySelectorAll()</code>选择全部的元素,然后遍历每一个,为每一个元素都添加一个<code>onclick</code>单击事件,每当它们点击时就会为背景添加一个随机颜色。</p>
+
+<pre class="brush: js notranslate">const divs = document.querySelectorAll('div');
+
+for (let i = 0; i &lt; divs.length; i++) {
+ divs[i].onclick = function(e) {
+ e.target.style.backgroundColor = bgChange();
+ }
+}</pre>
+
+<p>输出如下(试着点击它-玩的开心):</p>
+
+<div class="hidden">
+<h6 id="Hidden_example">Hidden example</h6>
+
+<pre class="brush: html notranslate">&lt;!DOCTYPE html&gt;
+&lt;html&gt;
+ &lt;head&gt;
+ &lt;meta charset="utf-8"&gt;
+ &lt;title&gt;Useful event target example&lt;/title&gt;
+ &lt;style&gt;
+ div {
+ background-color: red;
+ height: 100px;
+ width: 25%;
+ float: left;
+ }
+ &lt;/style&gt;
+ &lt;/head&gt;
+ &lt;body&gt;
+ &lt;script&gt;
+ for (let i = 1; i &lt;= 16; i++) {
+ const myDiv = document.createElement('div');
+ document.body.appendChild(myDiv);
+ }
+
+ function random(number) {
+ return Math.floor(Math.random()*number);
+ }
+
+ function bgChange() {
+ var rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
+ return rndCol;
+ }
+
+ const divs = document.querySelectorAll('div');
+
+ for (let i = 0; i &lt; divs.length; i++) {
+ divs[i].onclick = function(e) {
+ e.target.style.backgroundColor = bgChange();
+ }
+ }
+ &lt;/script&gt;
+ &lt;/body&gt;
+&lt;/html&gt;</pre>
+</div>
+
+<p>{{ EmbedLiveSample('Hidden_example', '100%', 400) }}</p>
+
+<p>你遇到的大多数事件处理器的事件对象都有可用的标准属性和函数(方法)(请参阅完整列表 <code>Event</code> 对象引用 )。然而,一些更高级的处理程序会添加一些专业属性,这些属性包含它们需要运行的额外数据。例如,媒体记录器API有一个<code>dataavailable</code>事件,它会在录制一些音频或视频时触发,并且可以用来做一些事情(例如保存它,或者回放)。对应的<code>ondataavailable</code>处理程序的事件对象有一个可用的数据属性。</p>
+
+<h3 id="阻止默认行为">阻止默认行为</h3>
+
+<p>有时,你会遇到一些情况,你希望事件不执行它的默认行为。 最常见的例子是Web表单,例如自定义注册表单。 当你填写详细信息并按提交按钮时,自然行为是将数据提交到服务器上的指定页面进行处理,并将浏览器重定向到某种“成功消息”页面(或 相同的页面,如果另一个没有指定。)</p>
+
+<p>当用户没有正确提交数据时,麻烦就来了 - 作为开发人员,你希望停止提交信息给服务器,并给他们一个错误提示,告诉他们什么做错了,以及需要做些什么来修正错误。 一些浏览器支持自动的表单数据验证功能,但由于许多浏览器不支持,因此建议你不要依赖这些功能,并实现自己的验证检查。 我们来看一个简单的例子。</p>
+
+<p>首先,一个简单的HTML表单,需要你填入名(first name)和姓(last name)</p>
+
+<pre class="brush: html notranslate">&lt;form&gt;
+ &lt;div&gt;
+ &lt;label for="fname"&gt;First name: &lt;/label&gt;
+ &lt;input id="fname" type="text"&gt;
+ &lt;/div&gt;
+ &lt;div&gt;
+ &lt;label for="lname"&gt;Last name: &lt;/label&gt;
+ &lt;input id="lname" type="text"&gt;
+ &lt;/div&gt;
+ &lt;div&gt;
+ &lt;input id="submit" type="submit"&gt;
+ &lt;/div&gt;
+&lt;/form&gt;
+&lt;p&gt;&lt;/p&gt;</pre>
+
+<div class="hidden">
+<pre class="brush: css notranslate">div {
+ margin-bottom: 10px;
+}
+</pre>
+</div>
+
+<p>这里我们用一个<code>onsubmit</code>事件处理程序(在提交的时候,在一个表单上发起<code>submit</code>事件)来实现一个非常简单的检查,用于测试文本字段是否为空。 如果是,我们在事件对象上调用<code>preventDefault()</code>函数,这样就停止了表单提交,然后在我们表单下面的段落中显示一条错误消息,告诉用户什么是错误的:</p>
+
+<pre class="brush: js notranslate">const form = document.querySelector('form');
+const fname = document.getElementById('fname');
+const lname = document.getElementById('lname');
+const submit = document.getElementById('submit');
+const para = document.querySelector('p');
+
+form.onsubmit = function(e) {
+ if (fname.value === '' || lname.value === '') {
+ e.preventDefault();
+ para.textContent = 'You need to fill in both names!';
+ }
+}</pre>
+
+<p>显然,这是一种非常弱的表单验证——例如,用户输入空格或数字提交表单,表单验证并不会阻止用户提交——这不是我们例子想要达到的目的。输出如下:</p>
+
+<p>{{ EmbedLiveSample('Preventing_default_behaviour', '100%', 140) }}</p>
+
+<div class="note">
+<p><strong>Note</strong>: 查看完整的源代码 <a href="https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/events/preventdefault-validation.html">preventdefault-validation.html</a> (也可以 <a href="http://mdn.github.io/learning-area/javascript/building-blocks/events/preventdefault-validation.html">running live</a> )</p>
+</div>
+
+<h3 id="事件冒泡及捕获">事件冒泡及捕获</h3>
+
+<p>最后即将介绍的这个主题你常常不会深究,但如果你不理解这个主题,就会十分痛苦。事件冒泡和捕捉是两种机制,主要描述当在一个元素上有两个相同类型的事件处理器被激活会发生什么。为了容易理解,我们来看一个例子——在新标签页打开这个<a href="http://mdn.github.io/learning-area/javascript/building-blocks/events/show-video-box.html">show-video-box.html</a> 例子(在这里可以查看源码 <a href="https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/events/show-video-box.html">source code</a>)。也可以在下面查看:</p>
+
+<div class="hidden">
+<h6 id="Hidden_video_example">Hidden video example</h6>
+
+<pre class="brush: html notranslate">&lt;!DOCTYPE html&gt;
+&lt;html&gt;
+ &lt;head&gt;
+ &lt;meta charset="utf-8"&gt;
+ &lt;title&gt;Show video box example&lt;/title&gt;
+ &lt;style&gt;
+ div {
+ position: absolute;
+ top: 50%;
+ transform: translate(-50%,-50%);
+ width: 480px;
+ height: 380px;
+ border-radius: 10px;
+ background-color: #eee;
+ background-image: linear-gradient(to bottom, rgba(0,0,0,0), rgba(0,0,0,0.1));
+ }
+
+ .hidden {
+ left: -50%;
+ }
+
+ .showing {
+ left: 50%;
+ }
+
+ div video {
+ display: block;
+ width: 400px;
+ margin: 40px auto;
+ }
+
+ &lt;/style&gt;
+ &lt;/head&gt;
+ &lt;body&gt;
+ &lt;button&gt;Display video&lt;/button&gt;
+
+ &lt;div class="hidden"&gt;
+ &lt;video&gt;
+ &lt;source src="https://raw.githubusercontent.com/mdn/learning-area/master/javascript/building-blocks/events/rabbit320.mp4" type="video/mp4"&gt;
+ &lt;source src="https://raw.githubusercontent.com/mdn/learning-area/master/javascript/building-blocks/events/rabbit320.webm" type="video/webm"&gt;
+ &lt;p&gt;Your browser doesn't support HTML5 video. Here is a &lt;a href="rabbit320.mp4"&gt;link to the video&lt;/a&gt; instead.&lt;/p&gt;
+ &lt;/video&gt;
+ &lt;/div&gt;
+
+ &lt;script&gt;
+
+ const btn = document.querySelector('button');
+ const videoBox = document.querySelector('div');
+ const video = document.querySelector('video');
+
+ btn.onclick = function() {
+ displayVideo();
+ }
+
+ function displayVideo() {
+ if(videoBox.getAttribute('class') === 'hidden') {
+ videoBox.setAttribute('class','showing');
+ }
+ }
+
+ videoBox.addEventListener('click',function() {
+ videoBox.setAttribute('class','hidden');
+ });
+
+ video.addEventListener('click',function() {
+ video.play();
+ });
+
+ &lt;/script&gt;
+ &lt;/body&gt;
+&lt;/html&gt;</pre>
+</div>
+
+<p>{{ EmbedLiveSample('Hidden_video_example', '100%', 500) }}</p>
+
+<p>这是一个非常简单的例子,它显示和隐藏一个包含<code>&lt;video&gt;</code>元素的<code>&lt;div&gt;</code>元素:</p>
+
+<pre class="brush: html notranslate">&lt;button&gt;Display video&lt;/button&gt;
+
+&lt;div class="hidden"&gt;
+ &lt;video&gt;
+ &lt;source src="rabbit320.mp4" type="video/mp4"&gt;
+ &lt;source src="rabbit320.webm" type="video/webm"&gt;
+ &lt;p&gt;Your browser doesn't support HTML5 video. Here is a &lt;a href="rabbit320.mp4"&gt;link to the video&lt;/a&gt; instead.&lt;/p&gt;
+ &lt;/video&gt;
+&lt;/div&gt;</pre>
+
+<p>当‘’button‘’元素按钮被单击时,将显示视频,它是通过将改变<code>&lt;div&gt;的</code>class属性值从<code>hidden</code>变为<code>showing</code>(这个例子的CSS包含两个<code>class</code>,它们分别控制这个<code>&lt;div&gt;</code>盒子在屏幕上显示还是隐藏。):</p>
+
+<pre class="brush: js notranslate">btn.onclick = function() {
+ videoBox.setAttribute('class', 'showing');
+}</pre>
+
+<p>然后我们再添加几个<code>onclick</code>事件处理器,第一个添加在<code>&lt;div&gt;</code>元素上,第二个添加在<code>&lt;video&gt;</code>元素上。这个想法是当视频(<code>&lt;video&gt;</code>)外 <code>&lt;div&gt;</code>元素内这块区域被单击时,这个视频盒子应该再次隐藏;当单击视频(<code>&lt;video&gt;</code>)本身,这个视频将开始播放。</p>
+
+<pre class="notranslate">videoBox.onclick = function() {
+ videoBox.setAttribute('class', 'hidden');
+};
+
+video.onclick = function() {
+ video.play();
+};</pre>
+
+<p>但是有一个问题 - 当您点击<code>video</code>开始播放的视频时,它会在同一时间导致<code>&lt;div&gt;</code>也被隐藏。 这是因为<code>video</code>在<code>&lt;div&gt;</code>之内 - <code>video</code>是<code>&lt;div&gt;</code>的一个子元素 - 所以点击<code>video</code>实际上是同时也运行<code>&lt;div&gt;</code>上的事件处理程序。</p>
+
+<h4 id="对事件冒泡和捕捉的解释">对事件冒泡和捕捉的解释</h4>
+
+<p>当一个事件发生在具有父元素的元素上(例如,在我们的例子中是<code>&lt;video&gt;</code>元素)时,现代浏览器运行两个不同的阶段 - 捕获阶段和冒泡阶段。 在捕获阶段:</p>
+
+<ul>
+ <li>浏览器检查元素的最外层祖先<code>&lt;html&gt;</code>,是否在捕获阶段中注册了一个<code>onclick</code>事件处理程序,如果是,则运行它。</li>
+ <li>然后,它移动到<code>&lt;html&gt;</code>中单击元素的下一个祖先元素,并执行相同的操作,然后是单击元素再下一个祖先元素,依此类推,直到到达实际点击的元素。</li>
+</ul>
+
+<p>在冒泡阶段,恰恰相反:</p>
+
+<ul>
+ <li>浏览器检查实际点击的元素是否在冒泡阶段中注册了一个<code>onclick</code>事件处理程序,如果是,则运行它</li>
+ <li>然后它移动到下一个直接的祖先元素,并做同样的事情,然后是下一个,等等,直到它到达<code>&lt;html&gt;</code>元素。</li>
+</ul>
+
+<p><a href="https://mdn.mozillademos.org/files/14075/bubbling-capturing.png"><img alt="" src="https://mdn.mozillademos.org/files/14075/bubbling-capturing.png" style="display: block; height: 452px; margin: 0px auto; width: 960px;"></a></p>
+
+<p>(单击图片可以放大这个图表)</p>
+
+<p>在现代浏览器中,默认情况下,所有事件处理程序都在冒泡阶段进行注册。因此,在我们当前的示例中,当您单击视频时,这个单击事件从 <code>&lt;video&gt;</code>元素向外冒泡直到<code>&lt;html&gt;</code>元素。沿着这个事件冒泡线路:</p>
+
+<ul>
+ <li>它发现了<code>video.onclick...</code>事件处理器并且运行它,因此这个视频<code>&lt;video&gt;</code>第一次开始播放。</li>
+ <li>接着它发现了(往外冒泡找到的) <code>videoBox.onclick...</code>事件处理器并且运行它,因此这个视频<code>&lt;video&gt;</code>也隐藏起来了。</li>
+</ul>
+
+<h4 id="用_stopPropagation_修复问题">用 stopPropagation() 修复问题</h4>
+
+<p>这是令人讨厌的行为,但有一种方法来解决它!标准事件对象具有可用的名为 <code><a href="/en-US/docs/Web/API/Event/stopPropagation">stopPropagation()</a></code>的函数, 当在事件对象上调用该函数时,它只会让当前事件处理程序运行,但事件不会在<strong>冒泡</strong>链上进一步扩大,因此将不会有更多事件处理器被运行(不会向上冒泡)。所以,我们可以通过改变前面代码块中的第二个处理函数来解决当前的问题:</p>
+
+<pre class="brush: js notranslate">video.onclick = function(e) {
+ e.stopPropagation();
+ video.play();
+};</pre>
+
+<p>你可以尝试把 <a href="https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/events/show-video-box.html">show-video-box.html source code</a> 拷贝到本地,然后自己动手修复它,或者在 <a href="http://mdn.github.io/learning-area/javascript/building-blocks/events/show-video-box-fixed.html">show-video-box-fixed.html</a> 页面查看修复结果(也可以在这里 <a href="https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/events/show-video-box-fixed.html">source code</a> 查看源码)。</p>
+
+<div class="note">
+<p><strong>注解</strong>: 为什么我们要弄清楚捕捉和冒泡呢?那是因为,在过去糟糕的日子里,浏览器的兼容性比现在要小得多,Netscape(网景)只使用事件捕获,而Internet Explorer只使用事件冒泡。当W3C决定尝试规范这些行为并达成共识时,他们最终得到了包括这两种情况(捕捉和冒泡)的系统,最终被应用在现在浏览器里。</p>
+</div>
+
+<div class="note">
+<p><strong>注解</strong>: 如上所述,默认情况下,所有事件处理程序都是在冒泡阶段注册的,这在大多数情况下更有意义。如果您真的想在捕获阶段注册一个事件,那么您可以通过使用<code><a href="https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener">addEventListener()</a></code>注册您的处理程序,并将可选的第三个属性设置为true。</p>
+</div>
+
+<h4 id="事件委托">事件委托</h4>
+
+<p>冒泡还允许我们利用事件委托——这个概念依赖于这样一个事实,如果你想要在大量子元素中单击任何一个都可以运行一段代码,您可以将事件监听器设置在其父节点上,并让子节点上发生的事件冒泡到父节点上,而不是每个子节点单独设置事件监听器。</p>
+
+<p>一个很好的例子是一系列列表项,如果你想让每个列表项被点击时弹出一条信息,您可以将<code>click</code>单击事件监听器设置在父元素<code>&lt;ul&gt;</code>上,这样事件就会从列表项冒泡到其父元素<code>&lt;ul&gt;</code>上。</p>
+
+<p>这个的概念在David Walsh的博客上有更多的解释,并有多个例子——看看<a href="https://davidwalsh.name/event-delegate">How JavaScript Event Delegation Works</a>.</p>
+
+<h2 id="结论">结论</h2>
+
+<p>现在您应该知道在这个早期阶段您需要了解的所有web事件。如上所述,事件并不是JavaScript的核心部分——它们是在浏览器Web APIs中定义的。</p>
+
+<p>另外,理解JavaScript在不同环境下使用不同的事件模型很重要——从Web api到其他领域,如浏览器WebExtensions和Node.js(服务器端JavaScript)。我们并不期望您现在了解所有这些领域,但是当您在学习web开发的过程中,理解这些事件的基础是很有帮助的。</p>
+
+<p>如果你有什么不明白的地方,请重新阅读这篇文章,或者联系<a href="https://developer.mozilla.org/en-US/Learn#Contact_us">contact us</a>我们寻求帮助。</p>
+
+<h2 id="参见">参见</h2>
+
+<ul>
+ <li><a href="http://www.quirksmode.org/js/events_order.html">Event order</a> (discussion of capturing and bubbling) — an excellently detailed piece by Peter-Paul Koch.</li>
+ <li><a href="http://www.quirksmode.org/js/events_access.html">Event accessing</a> (discussing of the event object) — another excellently detailed piece by Peter-Paul Koch.</li>
+ <li><a href="/en-US/docs/Web/Events">Event reference</a></li>
+</ul>
+
+<p>{{PreviousMenuNext("Learn/JavaScript/Building_blocks/Return_values","Learn/JavaScript/Building_blocks/Image_gallery", "Learn/JavaScript/Building_blocks")}}</p>
diff --git a/files/zh-cn/learn/javascript/building_blocks/functions/index.html b/files/zh-cn/learn/javascript/building_blocks/functions/index.html
new file mode 100644
index 0000000000..a4cdc2c0c2
--- /dev/null
+++ b/files/zh-cn/learn/javascript/building_blocks/functions/index.html
@@ -0,0 +1,428 @@
+---
+title: 函数-可复用代码块
+slug: learn/JavaScript/Building_blocks/Functions
+tags:
+ - API
+ - JavaScript
+ - 函数
+ - 初学者
+ - 匿名
+ - 参数
+ - 学习
+ - 方法
+ - 浏览器
+ - 自定义
+translation_of: Learn/JavaScript/Building_blocks/Functions
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/JavaScript/Building_blocks/Looping_code","Learn/JavaScript/Building_blocks/Build_your_own_function", "Learn/JavaScript/Building_blocks")}}</div>
+
+<p class="summary">在JavaScript中另一个基本概念是<strong>函数</strong>, 它允许你在一个代码块中存储一段用于处理单任务的代码,然后在任何你需要的时候用一个简短的命令来调用,而不是把相同的代码写很多次。在本文中,我们将探索函数的基本概念,如基本语法、如何定义和调用、范围和参数。</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">前提条件:</th>
+ <td>基本的电脑知识,对HTML与CSS有基本的了解,及已阅读: <a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps">JavaScript first steps</a>(JS的入门)。</td>
+ </tr>
+ <tr>
+ <th scope="row">目标:</th>
+ <td>了解 Javascript 函数背后的基本概念。</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="我能在哪找到函数">我能在哪找到函数?</h2>
+
+<p>在 JavaScript中, 你将发现函数无处不在 。事实上, 到目前为止,我们一直在使用函数,只是我们还没很好的讨论它们。然而现在是时候了,让我们开始聊聊函数,并探索它们的语法。</p>
+
+<p>几乎任何时候,只要你使用一个带有一对圆括号()的JavaScript结构,并且你不是在使用一个常见的比如for for循环,while或do…while循环,或者if语句这样的内置语言结构时,那么您就正在使用函数。</p>
+
+<div class="hidden"></div>
+
+<h2 id="浏览器内置函数">浏览器内置函数</h2>
+
+<p>在这套课程中我们已经使用了很多浏览器内置函数,当我们操作一个字符串的时候,例如:</p>
+
+<pre class="brush: js notranslate">var myText = 'I am a string';
+var newString = myText.replace('string', 'sausage');
+console.log(newString);
+// the replace() string function takes a string,
+// replaces one substring with another, and returns
+// a new string with the replacement made</pre>
+
+<p>或者当我们操作一个数组的时候:</p>
+
+<pre class="brush: js notranslate">var myArray = ['I', 'love', 'chocolate', 'frogs'];
+var madeAString = myArray.join(' ');
+console.log(madeAString);
+// the join() function takes an array, joins
+// all the array items together into a single
+// string, and returns this new string</pre>
+
+<p>或者当我们生成一个随机数时:</p>
+
+<pre class="brush: js notranslate">var myNumber = Math.random()
+// the random() function generates a random
+// number between 0 and 1, and returns that
+// number</pre>
+
+<p>...我们已经使用过函数了!</p>
+
+<div class="note">
+<p>提示:如果需要,你可以随意将这些代码输入浏览器控制台以便于你熟悉其功能。</p>
+</div>
+
+<p>JavaScript有许多内置的函数<span style="">,可以让您做很多有用的事情,而无需自己编写所有的代码。事实上</span>, 许多你调用(运行或者执行的专业词语)浏览器内置函数时调用的代码并不是使用JavaScript来编写——大多数调用浏览器后台的函数的代码,是使用像C++这样更低级的系统语言编写的,而不是像JavaScript这样的web编程语言。</p>
+
+<p>请记住,这些内置浏览器函数不是核心JavaScript语言的一部分——被定义为浏览器API的一部分,它建立在默认语言之上,以提供更多的功能(请参阅本课程的早期部分以获得更多的描述)。我们将在以后的模块中更详细地使用浏览器API。</p>
+
+<h2 id="函数与方法">函数与方法</h2>
+
+<p>程序员把函数称为对象<strong>方法(method)</strong>的一部分。你还不必了解JavaScript中已建构的对象在更深层次上是如何运作的<font><font>——你可以等到下一小节,我们会教给你有关对象运作方式的一切。</font></font><font><font>在我们继续前进之前,我们需要澄清一些有关方法和函数概念之间可能存在的误会——当你在网络上浏览相关信息的时候,你很可能会碰上这两个术语。</font></font></p>
+
+<p>到目前为止我们所使用的内置代码同属于这两种形式:函数和方法。你可以在<a href="/zh-CN/docs/Web/JavaScript/Reference/Global_Objects">这里</a>查看内置函数,内置对象以及其相关方法的完整列表。</p>
+
+<p><font><font>严格说来,内置浏览器函数并不是函数——它们是</font></font><strong style=""><font><font>方法</font></font></strong><font><font>。</font><font>这听起来有点可怕和令人困惑,但不要担心 ——函数和方法在很大程度上是可互换的,</font></font>至少在我们的学习阶段是这样的<font><font>。</font></font></p>
+
+<p>二者区别在于方法是在对象内定义的函数。浏览器内置函数(方法)和变量(称为属性)存储在结构化对象内,以使代码更加高效,易于处理。</p>
+
+<div class="hidden"></div>
+
+<h2 id="自定义函数">自定义函数</h2>
+
+<p>您在过去的课程中还看到很多定制功能 - 在代码中定义的功能,而不是在浏览器中。每当您看到一个自定义名称后面都带有括号,那么您使用的是自定义函数. <font><font>在我们</font><font>的</font><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Looping_code"><font>循环文章中</font></a><font>的</font></font><a href="http://mdn.github.io/learning-area/javascript/building-blocks/loops/random-canvas-circles.html"><font><font>random-canvas-circles.html</font></font></a><font><font>示例(另见完整的</font></font><a href="https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/loops/random-canvas-circles.html"><font><font>源代码</font></font></a><font><font>)中</font><font>,我们包括一个如下所示的自定义</font><font>函数:</font></font><code>draw()</code></p>
+
+<pre class="brush: js notranslate">function draw() {
+ ctx.clearRect(0,0,WIDTH,HEIGHT);
+ for (var i = 0; i &lt; 100; i++) {
+ ctx.beginPath();
+ ctx.fillStyle = 'rgba(255,0,0,0.5)';
+ ctx.arc(random(WIDTH), random(HEIGHT), random(50), 0, 2 * Math.PI);
+ ctx.fill();
+ }
+}</pre>
+
+<p><font><font>该函数在</font></font><a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas" title="使用HTML &lt;canvas>元素与canvas脚本API来绘制图形和动画。"><code>&lt;canvas&gt;</code></a><font><font>元素中</font><font>绘制100个随机圆</font><font>。</font><font>每次我们想要这样做,我们可以使用这个函数来调用这个功能</font></font></p>
+
+<pre class="brush: js notranslate">draw();</pre>
+
+<p><font><font>而不是每次我们想重复一遍,都要写出所有的代码。</font><font>函数可以包含任何您喜欢的代码 - 甚至可以从内部函数调用其他函数。</font><font>以上函数例如调用</font></font><code>random()</code><font><font>函数三次,由以下代码定义:</font></font></p>
+
+<pre class="brush: js notranslate">function random(number) {
+ return Math.floor(Math.random()*number);
+}</pre>
+
+<p><font><font>我们需要这个函数,因为浏览器的内置</font></font><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random"><font><font>Math.random()</font></font></a><font><font>函数只生成一个0到1之间的随机十进制数。我们想要一个0到一个指定数字之间的随机整数。</font></font></p>
+
+<div class="hidden"></div>
+
+<h2 id="调用函数">调用函数</h2>
+
+<p>现在你可能很清楚这一点,<font>但仅仅为了防止……,要在函数定义之后,实际使用它,你必须运行或调用它。</font><font>这是通过将函数名包含在代码的某个地方,后跟圆括号来完成的。</font></p>
+
+<pre class="brush: js notranslate">function myFunction() {
+ alert('hello');
+}
+
+myFunction()
+// calls the function once</pre>
+
+<h2 id="匿名函数">匿名函数</h2>
+
+<p><font>您可能会以稍微不同的方式看到定义和调用的函数。</font><font>到目前为止,我们刚刚创建了如下函数:</font></p>
+
+<pre class="brush: js notranslate">function myFunction() {
+ alert('hello');
+}</pre>
+
+<p>但是您也可以创建一个没有名称的函数:</p>
+
+<pre class="brush: js notranslate">function() {
+ alert('hello');
+}</pre>
+
+<p>这个函数叫做<strong>匿名函数</strong> — 它没有函数名! 它也不会自己做任何事情。 你通常将匿名函数与事件处理程序一起使用, 例如,如果单击相关按钮,以下操作将在函数内运行代码:</p>
+
+<pre class="brush: js notranslate">var myButton = document.querySelector('button');
+
+myButton.onclick = function() {
+ alert('hello');
+}</pre>
+
+<p>上述示例将要求{{htmlelement("button")}} <font>在页面上提供可用于选择并单击</font><font>的</font><font>元素。</font><font>您在整个课程中已经看到过这种结构了几次,您将在下一篇文章中了解更多信息并在其中使用。</font></p>
+
+<p>你还可以将匿名函数分配为变量的值,例如:</p>
+
+<pre class="brush: js notranslate">var myGreeting = function() {
+ alert('hello');
+}</pre>
+
+<p>现在可以使用以下方式调用此函数:</p>
+
+<pre class="brush: js notranslate">myGreeting();</pre>
+
+<p>有效地给变量一个名字;还可以将该函数分配为多个变量的值,例如:</p>
+
+<pre class="brush: js notranslate">var anotherGreeting = function() {
+ alert('hello');
+}</pre>
+
+<p>现在可以使用以下任一方法调用此函数</p>
+
+<pre class="brush: js notranslate">myGreeting();
+anotherGreeting();</pre>
+
+<p>但这只会令人费解,所以不要这样做!创建方法时,最好坚持下列形式:</p>
+
+<pre class="brush: js notranslate">function myGreeting() {
+ alert('hello');
+}</pre>
+
+<p>您将主要使用匿名函数来运行负载的代码以响应事件触发(如点击按钮) - 使用事件处理程序。再次,这看起来像这样:</p>
+
+<pre class="brush: js notranslate">myButton.onclick = function() {
+ alert('hello');
+ // I can put as much code
+ // inside here as I want
+}</pre>
+
+<div class="note">
+<p>匿名函数也称为函数表达式。函数表达式与函数声明有一些区别。函数声明会进行声明提升(declaration hoisting),而函数表达式不会。</p>
+</div>
+
+<h2 id="函数参数">函数参数</h2>
+
+<p>一些函数需要在调用它们时指定参数 ——这些参数值需要放在函数括号内,才能正确地完成其工作。</p>
+
+<div class="note">
+<p><strong>Note</strong>: 参数有时称为参数(arguments),属性(properties)或甚至属性(attributes)</p>
+</div>
+
+<p><font><font>例如,浏览器的内置</font></font><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random"><font><font>Math.random()</font></font></a><font><font>函数不需要任何参数。</font><font>当被调用时,它总是返回0到1之间的随机数:</font></font></p>
+
+<pre class="brush: js notranslate">var myNumber = Math.random();</pre>
+
+<p><font><font>浏览器的内置字符串</font></font><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace"><font><font>replace()</font></font></a><font><font>函数需要两个参数:在主字符串中查找的子字符串,以及用以下替换该字符串的子字符串:</font></font></p>
+
+<pre class="brush: js notranslate">var myText = 'I am a string';
+var newString = myText.replace('string', 'sausage');</pre>
+
+<div class="note">
+<p><strong>Note</strong>:当您需要指定多个参数时,它们以逗号分隔。</p>
+</div>
+
+<p><font><font>还应该注意,有时参数不是必须的 </font></font>—— <font><font>您不必指定它们。</font><font>如果没有,该功能一般会采用某种默认行为。</font><font>作为示例,数组 </font></font><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/join"><font><font>join()</font></font></a><font><font>函数的参数是可选的:</font></font></p>
+
+<pre class="brush: js notranslate">var myArray = ['I', 'love', 'chocolate', 'frogs'];
+var madeAString = myArray.join(' ');
+// returns 'I love chocolate frogs'
+var madeAString = myArray.join();
+// returns 'I,love,chocolate,frogs'</pre>
+
+<p>如果没有包含参数来指定加入/分隔符,默认情况下会使用逗号</p>
+
+<div class="hidden"></div>
+
+<h2 id="函数作用域和冲突">函数作用域和冲突</h2>
+
+<p>我们来谈一谈 {{glossary("scope")}}即作用域 — 处理函数时一个非常重要的概念。当你创建一个函数时,函数内定义的变量和其他东西都在它们自己的单独的范围内, 意味着它们被锁在自己独立的隔间中, 不能被函数外的代码访问。</p>
+
+<p>所有函数的最外层被称为全局作用域。 在全局作用域内定义的值可以在任意地方访问。</p>
+
+<p><font>JavaScript由于各种原因而建立,但主要是由于安全性和组织性。</font><font>有时您不希望变量可以在代码中的任何地方访问 - 您从其他地方调用的外部脚本可能会开始搞乱您的代码并导致问题,因为它们恰好与代码的其他部分使用了相同的变量名称,造成冲突。</font><font>这可能是恶意的,或者是偶然的。</font></p>
+
+<div class="hidden"></div>
+
+<p>例如,假设您有一个HTML文件,它调用两个外部JavaScript文件,并且它们都有一个使用相同名称定义的变量和函数:</p>
+
+<pre class="brush: html notranslate">&lt;!-- Excerpt from my HTML --&gt;
+&lt;script src="first.js"&gt;&lt;/script&gt;
+&lt;script src="second.js"&gt;&lt;/script&gt;
+&lt;script&gt;
+ greeting();
+&lt;/script&gt;</pre>
+
+<pre class="brush: js notranslate">// first.js
+var name = 'Chris';
+function greeting() {
+ alert('Hello ' + name + ': welcome to our company.');
+}</pre>
+
+<pre class="brush: js notranslate">// second.js
+var name = 'Zaptec';
+function greeting() {
+ alert('Our company is called ' + name + '.');
+}</pre>
+
+<p><font><font>这两个函数都使用 </font></font><code>greeting()</code><font><font> 形式调用,但是你只能访问到 <font face="consolas, Liberation Mono, courier, monospace"><span style="">first.js</span></font> 文件的</font></font><code>greeting()</code><font><font>函数(第二个文件被忽视了)。另外,第二次尝试使用 <code>let</code> 关键字定义 <code>name</code> 变量导致了一个错误。</font></font></p>
+
+<div class="note">
+<p><strong>Note</strong>: 您可以参考这个例子 <a href="http://mdn.github.io/learning-area/javascript/building-blocks/functions/conflict.html">running live on GitHub</a> (查看完整 <a href="https://github.com/mdn/learning-area/tree/master/javascript/building-blocks/functions">源代码</a>).</p>
+</div>
+
+<p>将代码锁定在函数中的部分避免了这样的问题,并被认为是最佳实践。</p>
+
+<p><font>这有点像一个动物园。</font><font>狮子,斑马,老虎和企鹅都保留在自己的园子中,只能拿到到它们园子中的东西 —— 与其函数作用域相同。</font><font>如果他们能进入其他园子,就会出现问题。不同的动物会在不熟悉的栖息地内感到真的不舒服 - 一只狮子或老虎会在企鹅的水多的,冰冷的的领域中感到可怕。最糟糕的是,狮子和老虎可能会尝试吃企鹅!</font></p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/14079/MDN-mozilla-zoo.png" style="display: block; margin: 0 auto;"></p>
+
+<p>动物园管理员就像全局作用域 - 他或她有钥匙访问每个园子,重新投喂食物,照顾生病的动物等。</p>
+
+<h3 id="主动学习_和_scope_玩耍">主动学习: 和 scope 玩耍</h3>
+
+<p>我们来看一个真正的例子来展示范围</p>
+
+<ol>
+ <li><font><font>首先,制作我们的</font></font><a href="https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/functions/function-scope.html"><font><font>function-scope.html</font></font></a><font><font>示例</font><font>的本地副本</font><font>。它</font><font>包含两个函数</font></font><code>a()</code><font><font>和</font></font><code>b()</code><font><font>,和三个变量—— </font></font><code>x</code><font><font>,</font></font><code>y</code><font><font>和</font></font><code>z</code><font><font>——其中两个在函数中被定义,另一个被定义在全局作用域内。</font><font>它还包含一个名为</font></font><code>output()</code>的函数<font><font>,它接收一个参数,并将其输出到页面的一个段落中。</font></font></li>
+ <li><font><font>在浏览器和文本编辑器中打开示例。</font></font></li>
+ <li><font><font>在浏览器开发工具中打开JavaScript控制台。</font><font>在JavaScript控制台中,输入以下命令:</font></font></li>
+</ol>
+
+<pre class="brush: js notranslate">output(x);</pre>
+
+<p><font><font>您应该看到变量</font></font><code>x</code><font><font>输出到屏幕</font><font>的值</font><font>。</font></font></p>
+
+<p>       4.现在尝试在您的控制台中输入以下内容</p>
+
+<pre class="brush: js notranslate">output(y);
+output(z);</pre>
+
+<p><font><font>这两个都应该返回错误沿“ </font></font><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Not_defined"><font><font>ReferenceError:y未定义</font></font></a><font><font> ”。</font><font>这是为什么?</font><font>由于函数作用域 - </font></font><code>y<font face="Open Sans, arial, x-locale-body, sans-serif"><span style="">和</span></font></code><code>z</code>被<font><font>锁定在</font><font>函数</font></font><code>a()</code><font><font>和</font></font><code>b()</code><font><font>函数中,所以</font></font><code>output()</code><font><font>从全局作用域调用时无法访问它们。</font></font></p>
+
+<p><font><font>        5.但是,从另一个函数里面调用什么呢?</font><font>尝试编辑</font></font><code>a()</code><font><font>,</font></font><code>b()</code><font><font>所以他们看起来像这样:</font></font></p>
+
+<pre class="brush: js notranslate">function a() {
+ var y = 2;
+ output(y);
+}
+
+function b() {
+ var z = 3;
+ output(z);
+}</pre>
+
+<p><font><font>保存代码并重新加载到浏览器中,然后尝试</font><font>从JavaScript控制台</font><font>调用</font></font><code>a()</code><font><font>和</font></font><code>b()</code><font><font>函数:</font></font></p>
+
+<pre class="brush: js notranslate">a();
+b();</pre>
+
+<p><font><font>您应该看到</font><font>页面中输出的</font></font><code>y</code><font><font>和</font></font><code>z</code>的<font><font>值。</font><font>这样就没问题,因为</font></font><code>output()</code><font><font>函数在其他函数的内部被调用 - 在这种情况下,输出变量的定义和函数的调用都在同一个作用域中(译者注:即函数作用域)。</font></font><code>output()</code><font><font>它可以从任何地方被调用,因为它在全局作用域中被定义。</font></font></p>
+
+<p>       6.现在尝试更新您的代码,如下所示:</p>
+
+<pre class="brush: js notranslate">function a() {
+ var y = 2;
+ output(x);
+}
+
+function b() {
+ var z = 3;
+ output(x);
+}</pre>
+
+<p>再次保存并重新加载,并在JavaScript控制台中再次尝试:</p>
+
+<pre class="brush: js notranslate">a();
+b();</pre>
+
+<p><font><font>函数 </font></font><code>a()</code><font><font>和</font></font><code>b()</code><font><font>都应该输出x---1的值。这些没有问题,因为即使</font></font><code>output()</code>的<font><font>调用与</font></font><code>x</code>的<font><font>定义</font><font>不在同一个作用域内</font><font>,但</font></font><code>x</code><font><font>是一个全局变量,所以在所有代码中都可用。</font></font></p>
+
+<p>7.最后,尝试更新您的代码,如下所示:</p>
+
+<pre class="brush: js notranslate">function a() {
+ var y = 2;
+ output(z);
+}
+
+function b() {
+ var z = 3;
+ output(y);
+}</pre>
+
+<p>再次保存并重新加载,并在JavaScript控制台中再次尝试:</p>
+
+<pre class="brush: js notranslate">a();
+b();</pre>
+
+<p><font><font>这次</font></font><code>a()</code><font><font>和</font></font><code>b()</code><font><font>调用都会返回那个令人讨厌的</font></font> "<a href="/en-US/docs/Web/JavaScript/Reference/Errors/Not_defined">ReferenceError: z is not defined</a>" error — 这是因为<code>output()</code>函数的调用和输出变量的定义不在同一个函数作用域内 - 变量对这些函数调用是不可见的。</p>
+
+<div class="note">
+<p><strong>注意</strong>:相同的范围规则不适用于循环(for(){...})和条件块(if(){...}) - 它们看起来非常相似,但它们不一样!小心不要让这些困惑。</p>
+</div>
+
+<div class="note">
+<p>注意:ReferenceError:“x”未定义错误是您遇到的最常见的错误。如果您收到此错误,并且确定您已经定义了该问题的变量,请检查它的范围。</p>
+</div>
+
+<ul>
+</ul>
+
+<h3 id="函数内部的函数">函数内部的函数</h3>
+
+<p><font>请记住,您可以从任何地方调用函数,甚至可以在另一个函数中调用函数。</font><font>这通常被用作保持代码整洁的方式 - 如果您有一个复杂的函数,如果将其分解成几个子函数,它更容易理解:</font></p>
+
+<pre class="brush: js notranslate">function myBigFunction() {
+ var myValue;
+
+ subFunction1();
+ subFunction2();
+ subFunction3();
+}
+
+function subFunction1() {
+ console.log(myValue);
+}
+
+function subFunction2() {
+ console.log(myValue);
+}
+
+function subFunction3() {
+ console.log(myValue);
+}
+</pre>
+
+<p>要确保函数调取的数值处在有效的作用域内。上面的例子中会产生一个错误提示,<code>ReferenceError:myValue is not define</code>,因为尽管<code>myValue</code>变量与函数调用指令处在同一个作用域中, 但它却没有在函数内被定义 <font><font>——</font></font> 实际代码在调用函数时就开始运行了。为了使代码正确运作,你必须将值作为参数传递给函数,如下所示:</p>
+
+<pre class="brush: js notranslate">function myBigFunction() {
+ var myValue = 1;
+
+ subFunction1(myValue);
+ subFunction2(myValue);
+ subFunction3(myValue);
+}
+
+function subFunction1(value) {
+ console.log(value);
+}
+
+function subFunction2(value) {
+ console.log(value);
+}
+
+function subFunction3(value) {
+ console.log(value);
+}</pre>
+
+<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>
+
+<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>
+
+<h2 id="参见">参见</h2>
+
+<ul>
+ <li><a href="/en-US/docs/Web/JavaScript/Guide/Functions">Functions detailed guide</a> — covers some advanced features not included here.</li>
+ <li><a href="/en-US/docs/Web/JavaScript/Reference/Functions">Functions reference</a></li>
+ <li><a href="/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters">Default parameters</a>, <a href="/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions">Arrow functions</a> — advanced concept references</li>
+</ul>
+
+<ul>
+</ul>
+
+<p>{{PreviousMenuNext("Learn/JavaScript/Building_blocks/Looping_code","Learn/JavaScript/Building_blocks/Build_your_own_function", "Learn/JavaScript/Building_blocks")}}</p>
diff --git a/files/zh-cn/learn/javascript/building_blocks/index.html b/files/zh-cn/learn/javascript/building_blocks/index.html
new file mode 100644
index 0000000000..0f6f3798d2
--- /dev/null
+++ b/files/zh-cn/learn/javascript/building_blocks/index.html
@@ -0,0 +1,55 @@
+---
+title: 创建JavaScript代码块
+slug: learn/JavaScript/Building_blocks
+tags:
+ - JavaScript
+ - 事件
+ - 优先级
+ - 函数
+ - 循环
+ - 教程
+ - 文章
+ - 新手
+ - 条件
+ - 模块
+ - 编码
+ - 评估
+translation_of: Learn/JavaScript/Building_blocks
+---
+<div>{{LearnSidebar}}</div>
+
+<p class="summary">在这个模块中, 我们将继续介绍JavaScript的关键基本特性,在这一章中我们将关注条件控制语句、循环语句、函数模块、事件等通用代码块。你可能在之前的的课程中见过这些模块,但仅仅是见过—在这篇模块中我们将明确讨论这些模块.</p>
+
+<h2 id="预备知识">预备知识</h2>
+
+<p>在开始这部分模块之前, 你应该熟悉基本的HTML和CSS, 并且已经看完我们之前的模块:<a href="/zh-CN/docs/Learn/JavaScript/First_steps">JavaScript 第一步</a>。</p>
+
+<div class="note">
+<p><strong>注</strong>: 如果你在使用无法创建自己文件的电脑/平板/其他设备,你可以试试在线编辑器,例如 <a href="http://jsbin.com/">JSBin</a> 或 <a href="https://thimble.mozilla.org/">Thimble</a>.</p>
+</div>
+
+<h2 id="指南">指南</h2>
+
+<dl>
+ <dt><a href="/zh-CN/docs/Learn/JavaScript/Building_blocks/conditionals">在代码中做决定 — 条件</a></dt>
+ <dd>在任何程序语言中,程序需要根据不同的输入数据作出相应的选择并执行相关的操作。例如,在游戏中,如果玩家的生命值是0,那么游戏就结束了。在天气应用中,如果在早上打开应用,则显示一个太阳升起的图片,如果在晚上打开,则显示星星和月亮。在这篇文章里我们将探索如何在JS中使用条件结构。</dd>
+ <dt><a href="/zh-CN/docs/Learn/JavaScript/Building_blocks/Looping_code">循环语句</a></dt>
+ <dd>有时候你需要在一个行中重复执行某一个任务。例如,查看一整列的名字。在程序中,循环能非常好的处理好这个问题。在本章中我们将介绍JavaScript的循环语句。</dd>
+ <dt><a href="/zh-CN/docs/Learn/JavaScript/Building_blocks/Functions">函数 — 可重用的代码块</a></dt>
+ <dd>在编码中的另一个基本概念是函数(functions)。 <strong>函数</strong> 允许你在定义的区块内存储一段代码用来执行一个单独的任务,然后调用该段代码时,你需要使用一个简短的命令,而不用重复编写多次该段代码。在这篇文章中我们将探讨函数的基本概念,如语法、如何调用定义的函数、作用域和参数。</dd>
+ <dt><a href="/zh-CN/docs/Learn/JavaScript/Building_blocks/Build_your_own_function">打造自己的函数</a></dt>
+ <dd>本文结合前几篇文章中所涉及的基本理论,提供了一个实践经验。在这里你会得到一些实践,并且编写自己的自定义函数。随后,我们也将进一步解释一些与函数相关的有用的细节。</dd>
+ <dt><a href="/zh-CN/docs/Learn/JavaScript/Building_blocks/Return_values">函数返回值</a></dt>
+ <dd>在这个课程中,我们要讨论的最后一个基本概念是返回值(通过返回值结束我们的函数)。有些函数在完成后不返回任何值,而有些函数返回。重要的是了解返回的值是什么,和如何在你的代码中使用他们,以及如何使自定义的函数返回需要的值。</dd>
+ <dt><a href="/zh-CN/docs/Learn/JavaScript/Building_blocks/Events">事件介绍</a></dt>
+ <dd>事件是你正在编写的系统中发生的动作或事件,系统告诉你的是这些动作或事件,如果需要的话,你可以以某种方式对它们做出反应。例如,如果用户单击网页上的按钮,您可能希望通过显示信息框来响应该操作。在这最后一篇文章中,我们将重点讨论一些围绕事件有关的概念,看看他们如何在浏览器中工作。</dd>
+</dl>
+
+<h2 id="评估">评估</h2>
+
+<p>下面的评估将测试您对JavaScript基础知识的理解。</p>
+
+<dl>
+ <dt><a href="https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Building_blocks/Image_gallery">图片画廊</a></dt>
+ <dd>现在我们已经学习了构建JavaScript的基本代码块,我们会通过构建一个在很多网站上相当常见的项目——一个由JavaScript驱动的相册,来测试你循环、函数、条件语句和事件方面的知识。</dd>
+</dl>
diff --git a/files/zh-cn/learn/javascript/building_blocks/looping_code/index.html b/files/zh-cn/learn/javascript/building_blocks/looping_code/index.html
new file mode 100644
index 0000000000..3fb1a217da
--- /dev/null
+++ b/files/zh-cn/learn/javascript/building_blocks/looping_code/index.html
@@ -0,0 +1,781 @@
+---
+title: 循环吧代码
+slug: learn/JavaScript/Building_blocks/Looping_code
+tags:
+ - JavaScript
+ - break
+ - continue
+ - for
+ - while
+ - 初学者
+ - 学习
+ - 循环
+translation_of: Learn/JavaScript/Building_blocks/Looping_code
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/JavaScript/Building_blocks/conditionals","Learn/JavaScript/Building_blocks/Functions", "Learn/JavaScript/Building_blocks")}}</div>
+
+<p class="summary">编程语言可以很迅速方便地帮我们完成一些重复性的任务,<font>从多个基本计算到几乎完成了很多类似工作的其他情况。现在</font><font>我们来看看处理这种需求的JavaScript中可用的循环结构。</font></p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">前提条件:</th>
+ <td>基本的电脑知识,对HTML与CSS有基本的了解,及已阅读: <a href="/en-US/docs/Learn/JavaScript/First_steps">JavaScript first steps</a>(JS的入门).</td>
+ </tr>
+ <tr>
+ <th scope="row">目标:</th>
+ <td>学习如何在JS里面使用循环语句.</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="来一起循环">来一起循环</h2>
+
+<p>循环,循环,循环. 就与这些:<a href="https://en.wikipedia.org/wiki/Froot_Loops">popular breakfast cereals</a>, <a href="https://en.wikipedia.org/wiki/Vertical_loop">roller coasters</a> and <a href="https://en.wikipedia.org/wiki/Loop_(music)">musical production</a>一样,类似存在于编程中.编程中的循环也是一直重复着去做一件事 - 此处循环便是编程中的术语.</p>
+
+<p>让我们来想一下下图,这位农夫考虑为他的家庭做一周的食物计划,他或许就需要执行一段循环:</p>
+
+<p><img alt="" src="https://raw.githubusercontent.com/agnoCJY/agno.github.io/master/loop_js-02-farm-cn.png" style="height: 563px; width: 900px;"><br>
+  </p>
+
+<p>一段循环通常需要一个或多个条件:</p>
+
+<ul>
+ <li><strong>一个开始条件,</strong>它被初始化为一个特定的值 - 这是循环的起点("开始:我没有食物”,上面)。</li>
+ <li><strong>一个结束条件,</strong>这是循环停止的标准 - 通常计数器达到一定值。 以上所说的“我有足够的食物”吗? 假设他需要10份食物来养活他的家人。</li>
+ <li><strong>一个迭代器,</strong>这通常在每个连续循环上递增少量的计数器,直到达到退出条件。 我们以前没有明确说明,但是我们可以考虑一下农民能够每小时收集2份食物。 每小时后,他收集的食物量增加了两倍,他检查他是否有足够的食物。 如果他已经达到10分(退出条件),他可以停止收集回家。</li>
+</ul>
+
+<p>在 {{glossary("伪代码")}} 中,这看起来就像下面这样:</p>
+
+<pre>loop(food = 0; foodNeeded = 10) {
+ if (food = foodNeeded) {
+ exit loop;
+ // 我们有足够的食物了,回家吧。
+ } else {
+ food += 2; // 花一个小时多收集两个食物。
+ // 循环将会继续执行。
+ }
+}</pre>
+
+<p>所以需要的食物量定为10,农民目前的数量为0。在循环的每次迭代中,我们检查农民的食物量是否等于他需要的量。 如果是这样,我们可以退出循环。 如果没有,农民花一个小时收集两部分食物,循环再次运行。</p>
+
+<h3 id="为何?"><font><font>为何?</font></font></h3>
+
+<p>在这一点上,您可能会了解循环中的高级概念,但您可能会认为“好的,但是,这有助于我编写更好的JavaScript代码?” 正如我们前面所说,循环与所做的事情都是一样的,这对于快速完成重复任务是非常有用的。</p>
+
+<p>通常,循环的每个连续迭代的代码将略有不同,这意味着您可以完成相同但略有不同的任务的全部负载 - 如果您有很多不同的计算要做, 做不同的一个,不一样的一个又一个!</p>
+
+<p>让我们来看一个例子来完美地说明为什么循环是一件好事。 假设我们想在{{htmlelement("canvas")}}元素上绘制100个随机圆(按更新按钮一次又一次地运行示例以查看不同的随机集):</p>
+
+<div class="hidden">
+<h6 id="Hidden_code">Hidden code</h6>
+
+<pre class="brush: html">&lt;!DOCTYPE html&gt;
+&lt;html&gt;
+ &lt;head&gt;
+ &lt;meta charset="utf-8"&gt;
+ &lt;title&gt;Random canvas circles&lt;/title&gt;
+ &lt;style&gt;
+ html {
+ width: 100%;
+ height: inherit;
+ background: #ddd;
+ }
+
+ canvas {
+ display: block;
+ }
+
+ body {
+ margin: 0;
+ }
+
+ button {
+ position: absolute;
+ top: 5px;
+ left: 5px;
+ }
+ &lt;/style&gt;
+ &lt;/head&gt;
+ &lt;body&gt;
+
+ &lt;button&gt;Update&lt;/button&gt;
+
+ &lt;canvas&gt;&lt;/canvas&gt;
+
+
+ &lt;script&gt;
+ var btn = document.querySelector('button');
+ var canvas = document.querySelector('canvas');
+ var ctx = canvas.getContext('2d');
+
+ var WIDTH = document.documentElement.clientWidth;
+ var HEIGHT = document.documentElement.clientHeight;
+
+ canvas.width = WIDTH;
+ canvas.height = HEIGHT;
+
+ function random(number) {
+ return Math.floor(Math.random()*number);
+ }
+
+ function draw() {
+ ctx.clearRect(0,0,WIDTH,HEIGHT);
+ for (var i = 0; i &lt; 100; i++) {
+ ctx.beginPath();
+ ctx.fillStyle = 'rgba(255,0,0,0.5)';
+ ctx.arc(random(WIDTH), random(HEIGHT), random(50), 0, 2 * Math.PI);
+ ctx.fill();
+ }
+ }
+
+ btn.addEventListener('click',draw);
+
+ &lt;/script&gt;
+
+ &lt;/body&gt;
+&lt;/html&gt;</pre>
+</div>
+
+<p>{{ EmbedLiveSample('Hidden_code', '100%', 400, "", "", "hide-codepen-jsfiddle") }}</p>
+
+<p>您现在不需要理解所有代码,但我们来看看实际绘制100个圆的那部分代码:</p>
+
+<pre class="brush: js">for (var i = 0; i &lt; 100; i++) {
+ ctx.beginPath();
+ ctx.fillStyle = 'rgba(255,0,0,0.5)';
+ ctx.arc(random(WIDTH), random(HEIGHT), random(50), 0, 2 * Math.PI);
+ ctx.fill();
+}
+</pre>
+
+<p> </p>
+
+<ul>
+ <li><code>random()</code>,在前面的代码中定义过了,返回一个 <code>0</code> 到 <font face="consolas, Liberation Mono, courier, monospace"><span style="background-color: rgba(220, 220, 220, 0.5);">x-1</span></font> 间的整数。</li>
+ <li><code>WIDTH</code> 和<code>HEIGHT</code> 浏览器内部窗口的宽度和高度。</li>
+</ul>
+
+<p> </p>
+
+<p>您应该有一个基本的想法 - 我们使用一个循环来运行这个代码的100次迭代,其中每一个在页面上的随机位置绘制一个圆。 无论我们绘制100个圆,1000还是10,000,所需的代码量将是相同的。 只有一个数字必须改变。</p>
+
+<p>如果我们在这里没有使用循环,我们必须为我们想要绘制的每个圆重复以下代码:</p>
+
+<pre class="brush: js">ctx.beginPath();
+ctx.fillStyle = 'rgba(255,0,0,0.5)';
+ctx.arc(random(WIDTH), random(HEIGHT), random(50), 0, 2 * Math.PI);
+ctx.fill();</pre>
+
+<p>这将非常无聊而且很难维持高速。 循环真的相当好用。</p>
+
+<h2 id="循环的标准">循环的标准</h2>
+
+<p>我们开始探索一些特定的循环结构。 第一个,你会经常使用到它,for循环 - 以下为for循环的语法:</p>
+
+<pre>for (initializer; exit-condition; final-expression) {
+ // code to run
+}</pre>
+
+<p><font><font>我们有:</font></font></p>
+
+<ol>
+ <li><font><font>关键字</font></font><code>for</code><font><font>,后跟一些括号。</font></font></li>
+ <li><font><font>在括号内,我们有三个项目,以分号分隔:</font></font>
+ <ol>
+ <li><font><font>一个</font></font><strong>初始化器</strong><font><font> - 这通常是一个设置为一个数字的变量,它被递增来计算循环运行的次数。</font><font>它也有时被称为</font></font><strong>计数变量</strong><font><font>。</font></font></li>
+ <li><font><font>一个</font></font><strong>退出条件</strong><font><font> -如前面提到的,这个定义循环何时停止循环。</font><font>这通常是一个表现为比较运算符的表达式,用于查看退出条件是否已满足的测试。</font></font></li>
+ <li>一个<strong>最终条件</strong><font><font> -这总是被判断(或运行),每个循环已经通过一个完整的迭代消失时间。</font><font>它通常用于增加(或在某些情况下递减)计数器变量,使其更接近退出条件值。</font></font></li>
+ </ol>
+ </li>
+ <li><font><font>一些包含代码块的花括号 - 每次循环迭代时都会运行这个代码。</font></font></li>
+</ol>
+
+<p><font><font>我们来看一个真实的例子,所以我们可以看出这些做得更清楚。</font></font></p>
+
+<pre class="brush: js">var cats = ['Bill', 'Jeff', 'Pete', 'Biggles', 'Jasmin'];
+var info = 'My cats are called ';
+var para = document.querySelector('p');
+
+for (var i = 0; i &lt; cats.length; i++) {
+ info += cats[i] + ', ';
+}
+
+para.textContent = info;</pre>
+
+<p>这给我们以下输出:</p>
+
+<div class="hidden">
+<h6 id="Hidden_code_2">Hidden code 2</h6>
+
+<pre class="brush: html">&lt;!DOCTYPE html&gt;
+&lt;html&gt;
+ &lt;head&gt;
+ &lt;meta charset="utf-8"&gt;
+ &lt;title&gt;Basic for loop example&lt;/title&gt;
+ &lt;style&gt;
+
+ &lt;/style&gt;
+ &lt;/head&gt;
+ &lt;body&gt;
+
+ &lt;p&gt;&lt;/p&gt;
+
+
+ &lt;script&gt;
+ var cats = ['Bill', 'Jeff', 'Pete', 'Biggles', 'Jasmin'];
+ var info = 'My cats are called ';
+ var para = document.querySelector('p');
+
+ for (var i = 0; i &lt; cats.length; i++) {
+ info += cats[i] + ', ';
+ }
+
+ para.textContent = info;
+
+ &lt;/script&gt;
+
+ &lt;/body&gt;
+&lt;/html&gt;</pre>
+</div>
+
+<p>{{ EmbedLiveSample('Hidden_code_2', '100%', 60, "", "", "hide-codepen-jsfiddle") }}</p>
+
+<div class="note">
+<p><strong>注</strong>: 您可以<a href="https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/loops/basic-for.html">在GitHub上找到这段示例代码</a>。 (也可以<a href="http://mdn.github.io/learning-area/javascript/building-blocks/loops/basic-for.html">在线运行</a>)。</p>
+</div>
+
+<p>这显示了一个循环用于迭代数组中的项目,并与每个项目进行一些操作 - JavaScript中非常常见的模式。 这里:</p>
+
+<ol>
+ <li>迭代器<code>i</code>从0开始(<code>var i = 0</code>)。</li>
+ <li>循环将会一直运行直到它不再小于猫数组的长度。 这很重要 - 退出条件显示循环仍然运行的条件。 所以在这种情况下,<code>&lt;cats.length</code>仍然是真的,循环仍然运行。</li>
+ <li>在循环中,我们将当前的循环项(<code>cats[i]</code>是<code>cats[当前下标的任何东西]</code>)以及逗号和空格连接到<code>info</code>变量的末尾。 所以:
+ <ol>
+ <li>在第一次运行中,<code>i = 0</code>,所以<code>cats[0] +','将</code>被连接到<code>info("Bill")</code>上。</li>
+ <li>在第二次运行中,<code>i = 1</code>,所以<code>cats[1] +','</code>将被连接到<code>info("Jeff")</code>上。</li>
+ <li>等等。 每次循环运行后,1将被添加到i(i ++),然后进程将再次启动。</li>
+ </ol>
+ </li>
+ <li>当等于<code>cats.length</code>时,循环将停止,浏览器将移动到循环下面的下一个代码位。</li>
+</ol>
+
+<div class="note">
+<p><strong>注</strong>: 我们将退出条件设为<code>&lt; cats.length</code>,而不是<code>&lt;= cats.length</code>是因为计算机从0开始,而不是1 - 开始时<code>i</code>是0,并且逐步增加到i<code> = 4</code>(最后一个数组的索引)。 <code>cats.length</code>返回5,因为数组中有5个项目,但是我们不希望达到<code>i = 5</code>,因为这将返回未定义的最后一个项目(没有索引为5的数组项目)。 所以我们想要比<code>cats.length</code>(<code>i &lt;</code>)少1,而不是<code>cats.length</code>(<code>i &lt;=</code>)。</p>
+</div>
+
+<div class="note">
+<p><strong>注</strong>:  退出条件的一个常见错误是使它们使用“等于”(<code>===</code>)而不是说“小于或等于”(<code>&lt;=</code>)。 如果我们想要运行我的循环到<code>i = 5</code>,退出条件将需要是<code>i &lt;= cats.length</code>。如果我们设置为<code>i === cats.length</code>,循环将不会运行,因为在第一次循环迭代时 i 不等于5,所以循环会立即停止。</p>
+</div>
+
+<p>我们留下的一个小问题是最后的输出句子形式不是很好:</p>
+
+<blockquote>
+<p>My cats are called Bill, Jeff, Pete, Biggles, Jasmin,</p>
+</blockquote>
+
+<p>理想情况下,我们想改变最后循环迭代中的连接,以便在句子末尾没有逗号。 嗯,没问题 - 我们可以很高兴地在我们的for循环中插入一个条件来处理这种特殊情况:</p>
+
+<pre class="brush: js">for (var i = 0; i &lt; cats.length; i++) {
+ if (i === cats.length - 1) {
+ info += 'and ' + cats[i] + '.';
+ } else {
+ info += cats[i] + ', ';
+ }
+}</pre>
+
+<div class="note">
+<p><strong>注</strong>: 您可以<a href="https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/loops/basic-for-improved.html">在GitHub上找到这个例子</a>。(也可以<a href="http://mdn.github.io/learning-area/javascript/building-blocks/loops/basic-for-improved.html">在线运行</a>)</p>
+</div>
+
+<div class="warning">
+<p><strong>重要:</strong> 使用<code>for</code>- 与所有循环一样,您必须确保初始化程序被迭代,以便最终达到退出条件。 如果没有,循环将永不停止,浏览器将强制它停止,否则会崩溃。 这被称为无限循环。</p>
+</div>
+
+<h2 id="使用break退出循环">使用break退出循环</h2>
+
+<p>如果要在所有迭代完成之前退出循环,可以使用break语句。 当我们查看switch语句时,我们已经在上一篇文章中遇到过这样的情况 - 当switch语句中符合输入表达式的情况满足时,break语句立即退出switch语句并移动到代码之后。</p>
+
+<p>与循环相同 - break语句将立即退出循环,并使浏览器移动到跟随它的任何代码。</p>
+
+<p>说我们想搜索一系列联系人和电话号码,只返回我们想要找的号码? 首先,一些简单的HTML - 一个文本{{htmlelement("input")}},允许我们输入一个名称来搜索,一个{{htmlelement("button")}}元素来提交搜索,以及一个{{htmlelement ("p")}}元素显示结果:<br>
+  </p>
+
+<pre class="brush: html">&lt;label for="search"&gt;Search by contact name: &lt;/label&gt;
+&lt;input id="search" type="text"&gt;
+&lt;button&gt;Search&lt;/button&gt;
+
+&lt;p&gt;&lt;/p&gt;</pre>
+
+<p>然后是JavaScript:</p>
+
+<pre class="brush: js">var contacts = ['Chris:2232322', 'Sarah:3453456', 'Bill:7654322', 'Mary:9998769', 'Dianne:9384975'];
+var para = document.querySelector('p');
+var input = document.querySelector('input');
+var btn = document.querySelector('button');
+
+btn.addEventListener('click', function(){
+ var searchName = input.value;
+ input.value = '';
+ input.focus();
+ for (var i = 0; i &lt; contacts.length; i++) {
+ var splitContact = contacts[i].split(':');
+ if (splitContact[0] === searchName) {
+ para.textContent = splitContact[0] + '\'s number is ' + splitContact[1] + '.';
+ break;
+ } else {
+ para.textContent = 'Contact not found.';
+ }
+ }
+});</pre>
+
+<div class="hidden">
+<h6 id="Hidden_code_3">Hidden code 3</h6>
+
+<pre class="brush: html">&lt;!DOCTYPE html&gt;
+&lt;html&gt;
+ &lt;head&gt;
+ &lt;meta charset="utf-8"&gt;
+ &lt;title&gt;Simple contact search example&lt;/title&gt;
+ &lt;style&gt;
+
+ &lt;/style&gt;
+ &lt;/head&gt;
+ &lt;body&gt;
+
+ &lt;label for="search"&gt;Search by contact name: &lt;/label&gt;
+ &lt;input id="search" type="text"&gt;
+ &lt;button&gt;Search&lt;/button&gt;
+
+ &lt;p&gt;&lt;/p&gt;
+
+
+ &lt;script&gt;
+ var contacts = ['Chris:2232322', 'Sarah:3453456', 'Bill:7654322', 'Mary:9998769', 'Dianne:9384975'];
+ var para = document.querySelector('p');
+ var input = document.querySelector('input');
+ var btn = document.querySelector('button');
+
+ btn.addEventListener('click', function() {
+ var searchName = input.value;
+ input.value = '';
+ input.focus();
+ for (var i = 0; i &lt; contacts.length; i++) {
+ var splitContact = contacts[i].split(':');
+ if (splitContact[0] === searchName) {
+ para.textContent = splitContact[0] + '\'s number is ' + splitContact[1] + '.';
+ break;
+ } else if (i === contacts.length-1)
+              para.textContent = 'Contact not found.';
+        }
+ }
+ });
+ &lt;/script&gt;
+
+ &lt;/body&gt;
+&lt;/html&gt;</pre>
+</div>
+
+<p>{{ EmbedLiveSample('Hidden_code_3', '100%', 100, "", "", "hide-codepen-jsfiddle") }}</p>
+
+<ol>
+ <li>首先我们有一些变量定义 - 我们有一个联系信息数组,每个项目是一个字符串,包含一个以冒号分隔的名称和电话号码。</li>
+ <li>接下来,我们将一个事件监听器附加到按钮(<code>btn</code>)上,这样当按下它时,运行一些代码来执行搜索并返回结果。</li>
+ <li>我们将输入的值输入到一个名为<code>searchName</code>的变量中,然后清空文本输入并重新对准它,准备进行下一个搜索。</li>
+ <li>现在有趣的部分,for循环:
+ <ol>
+ <li>我们的计数器开始时为在0,直到计数器不再小于<code>contacts.length</code>,并在循环的每次迭代之后将<code>i</code>递增1。</li>
+ <li>在循环中,我们首先将当前联系人(<code>contacts [i]</code>)拆分为冒号字符,并将生成的两个值存储在名为<code>splitContact</code>的数组中。</li>
+ <li>然后,我们使用条件语句来测试<code>splitContact [0]</code>(联系人姓名)是否等于输入的<code>searchName</code>。 如果是,我们在段落中输入一个字符串来报告联系人的号码,并使用break来结束循环。</li>
+ </ol>
+ </li>
+ <li>在<code>(contacts.length-1)</code> 迭代后,如果联系人姓名与输入的搜索不符,则段落文本设置为“未找到联系人”,循环继续迭代。</li>
+</ol>
+
+<div class="note">
+<p><strong>注:</strong>您可以<a href="https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/loops/contact-search.html">在GitHub上找到这个例子</a>或<a href="http://mdn.github.io/learning-area/javascript/building-blocks/loops/contact-search.html">在线运行</a>。</p>
+</div>
+
+<h2 id="使用continue跳过迭代">使用continue跳过迭代</h2>
+
+<p>continue语句以类似的方式工作,而不是完全跳出循环,而是跳过当前循环而执行下一个循环。 我们来看另外一个例子,它把一个数字作为一个输入,并且只返回开平方之后为整数的数字(整数)。</p>
+
+<p>HTML与最后一个例子基本相同 - 一个简单的文本输入和一个输出段落。 JavaScript也是一样的,虽然循环本身有点不同:</p>
+
+<pre class="brush: js">var num = input.value;
+
+for (var i = 1; i &lt;= num; i++) {
+ var sqRoot = Math.sqrt(i);
+ if (Math.floor(sqRoot) !== sqRoot) {
+ continue;
+ }
+
+ para.textContent += i + ' ';
+}</pre>
+
+<p>Here's the output:</p>
+
+<div class="hidden">
+<h6 id="Hidden_code_4">Hidden code 4</h6>
+
+<pre class="brush: html">&lt;!DOCTYPE html&gt;
+&lt;html&gt;
+ &lt;head&gt;
+ &lt;meta charset="utf-8"&gt;
+ &lt;title&gt;Integer squares generator&lt;/title&gt;
+ &lt;style&gt;
+
+ &lt;/style&gt;
+ &lt;/head&gt;
+ &lt;body&gt;
+
+ &lt;label for="number"&gt;Enter number: &lt;/label&gt;
+ &lt;input id="number" type="text"&gt;
+ &lt;button&gt;Generate integer squares&lt;/button&gt;
+
+ &lt;p&gt;Output: &lt;/p&gt;
+
+
+ &lt;script&gt;
+ var para = document.querySelector('p');
+ var input = document.querySelector('input');
+ var btn = document.querySelector('button');
+
+ btn.addEventListener('click', function() {
+ para.textContent = 'Output: ';
+ var num = input.value;
+ input.value = '';
+ input.focus();
+ for (var i = 1; i &lt;= num; i++) {
+ var sqRoot = Math.sqrt(i);
+ if (Math.floor(sqRoot) !== sqRoot) {
+ continue;
+ }
+
+ para.textContent += i + ' ';
+ }
+ });
+ &lt;/script&gt;
+
+ &lt;/body&gt;
+&lt;/html&gt;</pre>
+</div>
+
+<p>{{ EmbedLiveSample('Hidden_code_4', '100%', 100, "", "", "hide-codepen-jsfiddle") }}</p>
+
+<ol>
+ <li>在这种情况下,输入应为数字(<code>num</code>)。 for循环给定一个从1开始的计数器(在这种情况下,我们对0不感兴趣),一个退出条件,当计数器大于输入<code>num</code>时循环将停止,并且迭代器的计数器将每次增加1。</li>
+ <li>在循环中,我们使用<code>Math.sqrt(i)</code>找到每个数字的平方根,然后测试平方根是否是一个整数,通过判断当它被向下取整时,它是否与自身相同(这是<code>Math.floor(...)</code>对传递的数字的作用)。</li>
+ <li>如果平方根和四舍五入的平方根不相等(<code>!==</code>),则表示平方根不是整数,因此我们对此不感兴趣。 在这种情况下,我们使用continue语句跳过当前循环而执行下一个循环迭代,而不在任何地方记录该数字。</li>
+ <li>如果平方根是一个整数,我们完全跳过if块,所以continue语句不被执行; 相反,我们将当前i值加上一个空格连接到段落内容的末尾。</li>
+</ol>
+
+<div class="note">
+<p><strong>注:</strong>您可以<a href="https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/loops/integer-squares.html">在GitHub上查看完整代码</a>,或者<a href="http://mdn.github.io/learning-area/javascript/building-blocks/loops/integer-squares.html">在线运行</a>。</p>
+</div>
+
+<h2 id="while语句和do_..._while语句">while语句和do ... while语句</h2>
+
+<p><code>for</code> 不是JavaScript中唯一可用的循环类型。 实际上还有很多其他的,而现在你不需要理解所有这些,所以值得看几个人的结构,这样你就可以在稍微不同的方式识别出相同的功能。</p>
+
+<p>首先,我们来看看while循环。 这个循环的语法如下所示:</p>
+
+<pre>initializer
+while (exit-condition) {
+ // code to run
+
+ final-expression
+}</pre>
+
+<p>除了在循环之前设置初始化器变量,并且在运行代码之后,循环中包含final-expression,而不是这两个项目被包含在括号中,这与以前的for循环非常类似。 退出条件包含在括号内,前面是while关键字而不是for。</p>
+
+<p>同样的三个项目仍然存在,它们仍然以与for循环中相同的顺序定义 - 这是有道理的,因为您必须先定义一个初始化器,然后才能检查它是否已到达退出条件; 在循环中的代码运行(迭代已经完成)之后,运行最后的条件,这只有在尚未达到退出条件时才会发生。</p>
+
+<p>我们再来看看我们的猫列表示例,但是重写了一个while循环:</p>
+
+<pre class="brush: js">var i = 0;
+
+while (i &lt; cats.length) {
+ if (i === cats.length - 1) {
+ info += 'and ' + cats[i] + '.';
+ } else {
+ info += cats[i] + ', ';
+ }
+
+ i++;
+}</pre>
+
+<div class="note">
+<p><strong>Note</strong>: This still works just the same as expected — have a look at it <a href="http://mdn.github.io/learning-area/javascript/building-blocks/loops/while.html">running live on GitHub</a> (also view the <a href="https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/loops/while.html">full source code</a>).</p>
+</div>
+
+<p><a href="/en-US/docs/Web/JavaScript/Reference/Statements/do...while">do...while</a>循环非常类似但在while后提供了终止条件:</p>
+
+<pre>initializer
+do {
+ // code to run
+
+ final-expression
+} while (exit-condition)</pre>
+
+<p>在这种情况下,在循环开始之前,初始化程序先重新开始。 do关键字直接在包含要运行的代码的花括号和终止条件之前。</p>
+
+<p>这里的区别在于退出条件是一切都包含在括号中,而后面是一个while关键字。 在do ... while循环中,花括号中的代码总是在检查之前运行一次,以查看是否应该再次执行(在while和for中,检查首先出现,因此代码可能永远不会执行)。</p>
+
+<p>我们再次重写我们的猫列表示例,以使用do...while循环:</p>
+
+<pre class="brush: js">var i = 0;
+
+do {
+ if (i === cats.length - 1) {
+ info += 'and ' + cats[i] + '.';
+ } else {
+ info += cats[i] + ', ';
+ }
+
+ i++;
+} while (i &lt; cats.length);</pre>
+
+<div class="note">
+<p><strong>Note</strong>: 再一次,它正如我们期望的那样工作 — 看一看它在<a href="http://mdn.github.io/learning-area/javascript/building-blocks/loops/do-while.html">Github在线运行</a> (或者查看<a href="https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/loops/do-while.html">完整源代码</a>).</p>
+</div>
+
+<div class="warning">
+<p><strong>Important</strong>: 使用 while 和 do...while — 所有循环都一样 — 你必须保证初始变量是迭代的,那么它才会逐渐地达到退出条件. 不然, 它将会永远循环下去, 要么浏览器会强制终止它,要么它自己会崩溃. 这称为无限循环.</p>
+</div>
+
+<h2 id="主动学习:启动倒计时!">主动学习:启动倒计时!</h2>
+
+<p>在这个练习中,我们希望你打印出一个简单的启动倒计时到输出框,从10到关闭。 具体来说,我们希望你:</p>
+
+<ul>
+ <li>从10下降到0.我们为您提供了一个初始化器 - var i = 10;</li>
+ <li>对于每次迭代,创建一个新的段落并将其附加到输出&lt;div&gt;,我们使用<code>var output = document.querySelector('.output');</code>。在评论中,我们为您提供了需要在循环中某处使用的三条代码行:
+ <ul>
+ <li><code>var para = document.createElement('p');</code> —新建一个段落。</li>
+ <li><code>output.appendChild(para);</code> — 将段落附加到输出 <code>&lt;div&gt;</code>中。</li>
+ <li><code>para.textContent =</code> — 段落内的文字等于您放在右侧的任何内容。</li>
+ </ul>
+ </li>
+ <li>不同的迭代数字需要将不同的文本放在该迭代的段落中(您需要一个条件语句和多个<code>para.textContent = lines</code>):
+ <ul>
+ <li>如果数字是10,打印“Countdown 10”到段落。</li>
+ <li>如果数字为0,请打印“Blast off!” 到段落。</li>
+ <li>对于任何其他数字,只打印段落的数字。</li>
+ </ul>
+ </li>
+ <li>记住要包括一个迭代器! 然而,在这个例子中,我们在每次迭代之后都下降,而不是上升,所以你不想要<code>i++</code> - 你如何向下迭代?</li>
+</ul>
+
+<p>如果您犯了错误,您可以随时使用“重置”按钮重置该示例。 如果你真的卡住了,请按“显示解决方案”来查看解决方案。</p>
+
+<div class="hidden">
+<h6 id="Active_learning">Active learning</h6>
+
+<pre class="brush: html">&lt;div class="output" style="height: 410px;overflow: auto;"&gt;
+
+&lt;/div&gt;
+
+
+&lt;textarea id="code" class="playable-code" style="height: 300px;"&gt;
+var output = document.querySelector('.output');
+output.innerHTML = '';
+
+// var i = 10;
+
+// var para = document.createElement('p');
+// para.textContent = ;
+// output.appendChild(para);
+&lt;/textarea&gt;
+
+&lt;div class="playable-buttons"&gt;
+ &lt;input id="reset" type="button" value="Reset"&gt;
+ &lt;input id="solution" type="button" value="Show solution"&gt;
+&lt;/div&gt;
+</pre>
+
+<pre class="brush: js">var textarea = document.getElementById('code');
+var reset = document.getElementById('reset');
+var solution = document.getElementById('solution');
+var code = textarea.value;
+
+function updateCode() {
+ eval(textarea.value);
+}
+
+reset.addEventListener('click', function() {
+ textarea.value = code;
+ updateCode();
+});
+
+solution.addEventListener('click', function() {
+ textarea.value = jsSolution;
+ updateCode();
+});
+
+var jsSolution = 'var output = document.querySelector(\'.output\');\noutput.innerHTML = \'\';\n\nvar i = 10;\n\nwhile(i &gt;= 0) {\n var para = document.createElement(\'p\');\n if(i === 10) {\n para.textContent = \'Countdown \' + i;\n } else if(i === 0) {\n  para.textContent = \'Blast off!\';\n } else {\n para.textContent = i;\n }\n\n output.appendChild(para);\n\n i--;\n}';
+
+textarea.addEventListener('input', updateCode);
+window.addEventListener('load', updateCode);
+</pre>
+</div>
+
+<p>{{ EmbedLiveSample('Active_learning', '100%', 880, "", "", "hide-codepen-jsfiddle") }}</p>
+
+<h2 id="主动学习:填写来宾列表">主动学习:填写来宾列表</h2>
+
+<p>在本练习中,我们希望您获取存储在数组中的名称列表,并将其放入来宾列表中。 但这不是那么容易 - 我们不想让菲尔和洛拉进来,因为他们是贪婪和粗鲁的,总是吃所有的食物! 我们有两个名单,一个是客人承认的,一个是客人拒绝的。</p>
+
+<p>具体来说,我们希望你:</p>
+
+<ul>
+ <li>编写一个循环,它将从0迭代到people数组的长度。 你需要从一个初始化器<code>var i = 0</code>开始,但是你需要什么退出条件?</li>
+ <li>在每个循环迭代期间,使用条件语句检查当前数组项是否等于“Phil”或“Lola”:
+ <ul>
+ <li>如果是,则将数组项连接到拒绝段落的<code>textContent</code>的末尾,后跟逗号和空格。</li>
+ <li>如果不是,则将数组项连接到接收段落的<code>textContent</code>的末尾,后跟逗号和空格。</li>
+ </ul>
+ </li>
+</ul>
+
+<p>我们已经提供给您:</p>
+
+<ul>
+ <li><code>var i = 0;</code> — 你的初始化程序</li>
+ <li><code>refused.textContent +=</code> - 将连接某些东西的行的开头,结束于<code>refused.textContent</code>。</li>
+ <li><code>admitted.textContent +=</code> - 将连接某些内容到一行的结尾的行的开始。</li>
+</ul>
+
+<p>额外的奖金问题 - 成功完成上述任务后,您将留下两个名称列表,用逗号分隔,但它们将不整齐 - 每个结尾处都会有一个逗号。 你可以制定出如何在每种情况下编写最后一个逗号的行,并添加一个完整的停止? 看看有用的字符串方法文章的帮助。</p>
+
+<p>如果您犯了错误,您可以随时使用“重置”按钮重置该示例。 如果你真的卡住了,请按“显示解决方案”来查看解决方案。</p>
+
+<div class="hidden">
+<h6 id="Active_learning_2">Active learning 2</h6>
+
+<pre class="brush: html">&lt;div class="output" style="height: 100px;overflow: auto;"&gt;
+ &lt;p class="admitted"&gt;Admit: &lt;/p&gt;
+  &lt;p class="refused"&gt;Refuse: &lt;/p&gt;
+&lt;/div&gt;
+
+&lt;textarea id="code" class="playable-code" style="height: 400px;"&gt;
+var people = ['Chris', 'Anne', 'Colin', 'Terri', 'Phil', 'Lola', 'Sam', 'Kay', 'Bruce'];
+
+var admitted = document.querySelector('.admitted');
+var refused = document.querySelector('.refused');
+admitted.textContent = 'Admit: ';
+refused.textContent = 'Refuse: '
+
+// var i = 0;
+
+// refused.textContent += ;
+// admitted.textContent += ;
+
+&lt;/textarea&gt;
+
+&lt;div class="playable-buttons"&gt;
+ &lt;input id="reset" type="button" value="Reset"&gt;
+ &lt;input id="solution" type="button" value="Show solution"&gt;
+&lt;/div&gt;
+</pre>
+
+<pre class="brush: js">var textarea = document.getElementById('code');
+var reset = document.getElementById('reset');
+var solution = document.getElementById('solution');
+var code = textarea.value;
+
+function updateCode() {
+ eval(textarea.value);
+}
+
+reset.addEventListener('click', function() {
+ textarea.value = code;
+ updateCode();
+});
+
+solution.addEventListener('click', function() {
+ textarea.value = jsSolution;
+ updateCode();
+});
+
+var jsSolution = 'var people = [\'Chris\', \'Anne\', \'Colin\', \'Terri\', \'Phil\', \'Lola\', \'Sam\', \'Kay\', \'Bruce\'];\n\nvar admitted = document.querySelector(\'.admitted\');\nvar refused = document.querySelector(\'.refused\');\n\nvar i = 0;\n\ndo {\n if(people[i] === \'Phil\' || people[i] === \'Lola\') {\n refused.textContent += people[i] + \', \';\n } else {\n admitted.textContent += people[i] + \', \';\n }\n i++;\n} while(i &lt; people.length);\n\nrefused.textContent = refused.textContent.slice(0,refused.textContent.length-2) + \'.\';\nadmitted.textContent = admitted.textContent.slice(0,admitted.textContent.length-2) + \'.\';';
+
+textarea.addEventListener('input', updateCode);
+window.addEventListener('load', updateCode);
+</pre>
+</div>
+
+<p>{{ EmbedLiveSample('Active_learning_2', '100%', 680, "", "", "hide-codepen-jsfiddle") }}</p>
+
+<h2 id="应该使用哪种循环类型?">应该使用哪种循环类型?</h2>
+
+<p>对于基本用途,for,while和do ... while循环大部分可互换。 他们都可以用来解决相同的问题,你使用哪一个将在很大程度上取决于你的个人偏好 - 哪一个你最容易记住或最直观的。 我们再来看看他们。</p>
+
+<p>首先是 <code>for</code>:</p>
+
+<pre>for (initializer; exit-condition; final-expression) {
+ // code to run
+}</pre>
+
+<p><code>while</code>:</p>
+
+<pre>initializer
+while (exit-condition) {
+ // code to run
+
+ final-expression
+}</pre>
+
+<p>最后是 <code>do...while</code>:</p>
+
+<pre>initializer
+do {
+ // code to run
+
+ final-expression
+} while (exit-condition)</pre>
+
+<p>我们建议使用<code>for</code>,因为它可能是最简单地帮你记住一切 - 初始化程序,退出条件和最终表达式都必须整齐地放入括号,所以很容易看到他们在哪里并检查你没有丢失他们。</p>
+
+<div class="note">
+<p><strong>注:</strong>还有其他循环类型/特性,这些特性在 高级/专门 的情况下是有用的,超出了本文的范围。如果您想进一步了解循环学习,请阅读我们的高级<a href="/en-US/docs/Web/JavaScript/Guide/Loops_and_iteration">循环和迭代指南</a>。</p>
+</div>
+
+<h2 id="结论">结论</h2>
+
+<p>本文向您展示了背后的基本概念,以及JavaScript中循环代码时可用的不同选项。 你现在应该明白为什么循环是一个处理重复代码的好机制,并且在你自己的例子中使用它们!</p>
+
+<p>如果您有什么不明白的地方,可以再通读一遍,或者<a href="/en-US/Learn#Contact_us">联系我们</a>寻求帮助。</p>
+
+<h2 id="相关链接">相关链接</h2>
+
+<ul>
+ <li><a href="/en-US/docs/Web/JavaScript/Guide/Loops_and_iteration">Loops and iteration in detail</a></li>
+ <li><a href="/en-US/docs/Web/JavaScript/Reference/Statements/for">for statement reference</a></li>
+ <li><a href="/en-US/docs/Web/JavaScript/Reference/Statements/while">while</a> and <a href="/en-US/docs/Web/JavaScript/Reference/Statements/do...while">do...while</a> references</li>
+ <li><a href="/en-US/docs/Web/JavaScript/Reference/Statements/break">break</a> and <a href="/en-US/docs/Web/JavaScript/Reference/Statements/continue">continue</a> references</li>
+ <li>
+ <p class="entry-title"><a href="https://www.impressivewebs.com/javascript-for-loop/">What’s the Best Way to Write a JavaScript For Loop?</a> — some advanced loop best practices</p>
+ </li>
+</ul>
+
+<p>{{PreviousMenuNext("Learn/JavaScript/Building_blocks/conditionals","Learn/JavaScript/Building_blocks/Functions", "Learn/JavaScript/Building_blocks")}}</p>
+
+<p> </p>
+
+<h2 id="在本单元中">在本单元中</h2>
+
+<ul>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/conditionals">Making decisions in your code — conditionals</a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Looping_code">Looping code</a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Functions">Functions — reusable blocks of code</a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Build_your_own_function">Build your own function</a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Return_values">Function return values</a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events">Introduction to events</a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Image_gallery">Image gallery</a></li>
+</ul>
+
+<p> </p>
diff --git a/files/zh-cn/learn/javascript/building_blocks/return_values/index.html b/files/zh-cn/learn/javascript/building_blocks/return_values/index.html
new file mode 100644
index 0000000000..38f9fe0eff
--- /dev/null
+++ b/files/zh-cn/learn/javascript/building_blocks/return_values/index.html
@@ -0,0 +1,172 @@
+---
+title: 函数返回值
+slug: learn/JavaScript/Building_blocks/Return_values
+tags:
+ - JavaScript
+ - 函数
+ - 返回值
+translation_of: Learn/JavaScript/Building_blocks/Return_values
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/JavaScript/Building_blocks/Build_your_own_function","Learn/JavaScript/Building_blocks/Events", "Learn/JavaScript/Building_blocks")}}</div>
+
+<p class="summary">函数返回值-是本章中最后一个基础概念,让我们一起来瞧瞧.。有些函数在执行完毕后不会返回一个有用的值,但有些会, 重要的是理解返回的是什么,怎样使用这些值在你的代码中,我们将在下面讨论这些。</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">前提:</th>
+ <td>
+ <p>基础的计算机知识, 懂得基础的HTML 和CSS, <a href="/en-US/docs/Learn/JavaScript/First_steps">JavaScript </a>第一步学习, 函数-<a href="/en-US/docs/Learn/JavaScript/Building_blocks/Functions"> </a>可重用的代码块.</p>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row">目标:</th>
+ <td>理解什么函数的返回值   , 和如何使用它们</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="什么是返回值">什么是返回值?</h2>
+
+<p>返回值意如其名,是指函数执行完毕后返回的值。你已经多次遇见过返回值,尽管你可能没有明确的考虑过他们。让我们一起回看一些熟悉的代码:</p>
+
+<pre class="brush: js notranslate">var myText = 'I am a string';
+var newString = myText.replace('string', 'sausage');
+console.log(newString);
+// the replace() string function takes a string,
+// replaces one substring with another, and returns
+// a new string with the replacement made</pre>
+
+<p>在第一篇函数文章中,我们确切地看到了这一块代码。我们对 <code>myText</code> 字符串调用 <a href="https://wiki.developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace">replace()</a> 功能,并通过这两个参数的字符串查找,和子串替换它。当这个函数完成(完成运行)后,它返回一个值,这个值是一个新的字符串,它具有替换的功能。在上面的代码中,我们保存这个返回值,以作为<code>newString</code>变量的内容。</p>
+
+<p>如果你看看替换功能MDN参考页面,你会看到一个<a href="/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String/replace#Return_value">返回值</a>。知道和理解函数返回的值是非常有用的,因此我们尽可能地包含这些信息。</p>
+
+<p>一些函数没有返回值就像(在我们的参考页中,返回值在这种情况下被列出为空值 <code>void</code> 或未定义值 <code>undefined</code> 。).例如, 我们在前面文章中创建的 <a href="https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/functions/function-stage-4.html#L50">displayMessage() function</a> , 由于调用的函数的结果,没有返回特定的值。它只是让一个提示框出现在屏幕的某个地方——就是这样!</p>
+
+<p>通常,返回值是用在函数在计算某种中间步骤。你想得到最终结果,其中包含一些值。那些值需要通过一个函数计算得到,然后返回结果可用于计算的下一个阶段。</p>
+
+<h3 id="在自定义的函数中使用返回值">在自定义的函数中使用返回值</h3>
+
+<p>要从自定义函数返回值,您需要使用…等待它… <a href="https://wiki.developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/return">return</a> 关键字。 我们最近在<a href="https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/loops/random-canvas-circles.html">random-canvas-circles.html</a>示例中看到了这一点。 我们的 <code>draw()</code> 函数绘制100随机圆在HTML的{{htmlelement("canvas")}}:</p>
+
+<pre class="brush: js notranslate">function draw() {
+ ctx.clearRect(0,0,WIDTH,HEIGHT);
+ for (var i = 0; i &lt; 100; i++) {
+ ctx.beginPath();
+ ctx.fillStyle = 'rgba(255,0,0,0.5)';
+ ctx.arc(random(WIDTH), random(HEIGHT), random(50), 0, 2 * Math.PI);
+ ctx.fill();
+ }
+}</pre>
+
+<p>在每个循环迭代,<code>random()</code>函数调用了三次,分别生成当前圆的x坐标,一个随机值Y坐标和半径。<code>random()</code>函数接受一个参数-一个整数,返回0到这个整数之间的随机数。看起来像这样:</p>
+
+<pre class="brush: js notranslate">function randomNumber(number) {
+ return Math.floor(Math.random()*number);
+}</pre>
+
+<p>这也可以写成下面这样:</p>
+
+<pre class="brush: js notranslate">function randomNumber(number) {
+ var result = Math.floor(Math.random()*number);
+ return result;
+}</pre>
+
+<p>但是第一个版本写得更快,而且更紧凑。</p>
+
+<p>我们每次调用函数都返回<code>Math.floor(Math.random()*number)</code>计算的数学结果。这个返回值出现在调用函数的位置上,并且代码继续。例如,如果我们运行下面的行:</p>
+
+<pre class="brush: js notranslate">ctx.arc(random(WIDTH), random(HEIGHT), random(50), 0, 2 * Math.PI);</pre>
+
+<p>这三次<code>random()</code>调用分别返回值500, 200和35,实际上这一行这样运行:</p>
+
+<pre class="brush: js notranslate">ctx.arc(500, 200, 35, 0, 2 * Math.PI);</pre>
+
+<p>在运行该行之前,首先运行该行上的函数调用,并用其返回值替换该函数调用。</p>
+
+<h2 id="主动学习:我们自己的返回值函数">主动学习:我们自己的返回值函数</h2>
+
+<p>让我们着手编写具有我们自己的返回值的函数。</p>
+
+<ol>
+ <li>首先,从GitHub的<a href="https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/functions/function-library.html">function-library.html</a>文件复制一份本地副本。这是一个简单的HTML页面包含一个 {{htmlelement("input")}} 文本域和一个段落。 还有一个 {{htmlelement("script")}} 元素,我们在两个变量中存储了对两个HTML元素的引用。这个小页面允许你在文本框中输入一个数字,并在下面的段落中显示不同的数字。</li>
+ <li>让我们添加一些有用的函数。在现有的两行JavaScript下面,添加以下函数定义:
+ <pre class="brush: js notranslate">function squared(num) {
+ return num * num;
+}
+
+function cubed(num) {
+ return num * num * num;
+}
+
+function factorial(num) {
+ var x = num;
+ while (x &gt; 1) {
+ num *= x-1;
+ x--;
+ }
+ return num;
+}</pre>
+ <code>squared()</code> 和 <code>cubed()</code> 功能是相当明显的-他们的平方或立方的数作为一个参数返回。factorial()函数返回给定数字的阶乘。</li>
+ <li>接下来,我们将包括一种打印输入到文本输入中的数字的信息的方法。在现有函数下面输入以下事件处理程序:
+ <pre class="brush: js notranslate">input.onchange = function() {
+ var num = input.value;
+ if (isNaN(num)) {
+ para.textContent = 'You need to enter a number!';
+ } else {
+ para.textContent = num + ' squared is ' + squared(num) + '. ' +
+ num + ' cubed is ' + cubed(num) + '. ' +
+ num + ' factorial is ' + factorial(num) + '.';
+ }
+}</pre>
+
+ <p>这里我们创建一个<code>onchange</code>事件处理程序,当文本框上面的change事件被触发的之后,事件处理程序就会运行 - 就是说,一个新的值被输入到文本框并且被提交(就比如,输入一个值,然后按Tab)。当这个匿名函数运行时,输入框中的值将被存储在<code>num</code>变量中。</p>
+
+ <p>接下来,我们进行条件测试——如果输入的值不是数字,则在段落中打印错误消息。if语句判断<a href="/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/isNaN">isNaN(num)</a>表达式是否返回true。我们用<code>isNaN()</code>函数测试<code>num</code>的值是否不是一个数字-如果不是数字,就返回<code>true</code>,否则返回<code>false</code>。</p>
+
+ <p>如果测试返回false,则数值是一个数字,所以我们在段落元素中打印出一个句子,说明数字的平方、立方体和阶乘是什么。这句话叫squared(),cubed(),和factorial()函数来获得所需的值。</p>
+ </li>
+ <li>保存您的代码,将其加载到浏览器中,然后尝试. </li>
+</ol>
+
+<div class="note">
+<p><strong>Note</strong>:如果你有麻烦让例子工作,对比<a href="https://github.com/mdn/learning-area/blob/master/javascript/building-blocks/functions/function-library-finished.html">GitHub的已完成版</a>检查你的代码(或<a href="http://mdn.github.io/learning-area/javascript/building-blocks/functions/function-library-finished.html">看它在线运行</a>),或寻求我们的帮助。</p>
+</div>
+
+<p>在这一点上,我们希望您编写一个自己的几个函数,并将它们添加到库中。这个数的平方根或立方根,或一个圆的周长和半径是多少?</p>
+
+<p>这个练习提出了一些重要的观点,除了研究如何使用返回语句之外。此外,我们还有:</p>
+
+<ul>
+ <li>查看另一个将错误处理写入函数的示例。它是否提供了任何必要的参数通常是一个好主意,另一方面对可选参数提供默认值。这样,你的程序就不太可能出错了。</li>
+ <li>关于创建函数库思想的思考。随着你深入到你的编程生涯,你将开始一次又一次地做同样的事情。这是一个好主意,开始保持你自己的实用工具库,你经常使用-你可以把它们复制到你的新代码,甚至只是把它应用到任何你需要的HTML页面。</li>
+</ul>
+
+<h2 id="结论">结论</h2>
+
+<p>因此,我们让它-功能是有趣的,非常有用的,虽然有很多要谈论他们的语法和功能,相当容易理解的正确的文章学习。</p>
+
+<p>如果您有什么不明白的地方,可以再通读一遍,或者<a href="https://developer.mozilla.org/en-US/Learn#Contact_us">联系我们</a>寻求帮助。</p>
+
+<h2 id="参见">参见</h2>
+
+<ul>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions">Functions in-depth</a> — 详细介绍更多高级功能相关信息的指南。</li>
+ <li><a href="https://www.impressivewebs.com/callback-functions-javascript/">Callback functions in JavaScript</a> — 一个常见的JavaScript模式是把一个函数传递给另一个函数作为参数,然后在第一个函数中调用它。这有点超出了这门课的范围,但值得学习很久。</li>
+</ul>
+
+<p>{{PreviousMenuNext("Learn/JavaScript/Building_blocks/Build_your_own_function","Learn/JavaScript/Building_blocks/Events", "Learn/JavaScript/Building_blocks")}}</p>
+
+<h2 id="在本单元中">在本单元中</h2>
+
+<ul>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/conditionals">Making decisions in your code — conditionals</a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Looping_code">Looping code</a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Functions">Functions — reusable blocks of code</a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Build_your_own_function">Build your own function</a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Return_values">Function return values</a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events">Introduction to events</a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Image_gallery">Image gallery</a></li>
+</ul>
diff --git a/files/zh-cn/learn/javascript/building_blocks/相片走廊/index.html b/files/zh-cn/learn/javascript/building_blocks/相片走廊/index.html
new file mode 100644
index 0000000000..22101b20ba
--- /dev/null
+++ b/files/zh-cn/learn/javascript/building_blocks/相片走廊/index.html
@@ -0,0 +1,244 @@
+---
+title: 照片库
+slug: learn/JavaScript/Building_blocks/相片走廊
+tags:
+ - 事件
+ - 事件句柄
+ - 初学者
+ - 学习
+ - 循环
+ - 评估
+translation_of: Learn/JavaScript/Building_blocks/Image_gallery
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenu("Learn/JavaScript/Building_blocks/Events", "Learn/JavaScript/Building_blocks")}}</div>
+
+<p class="summary">我们已经学习了 JavaScript 基础的块结构,下面我们通过编写一个常见的基于 JavaScript 的照片库来测验一下你对于循环、函数、条件和事件的掌握情况。</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">预备知识:</th>
+ <td>请读完本章其它所有小节的内容后再开始这个测验。</td>
+ </tr>
+ <tr>
+ <th scope="row">目标:</th>
+ <td>测试你对 JavaScript 的循环、函数、条件语句和事件处理的掌握程度。</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="起点">起点</h2>
+
+<p><a href="https://raw.githubusercontent.com/roy-tian/learning-area/master/javascript/building-blocks/gallery/gallery-start.zip">下载压缩包</a> 并在本地解压。</p>
+
+<div class="note">
+<p><strong>注</strong>:你还可以使用类似 <a class="external external-icon" href="http://jsbin.com/">JSBin</a> 或 <a class="external external-icon" href="https://thimble.mozilla.org/">Thimble</a> 这些在线编辑器来完成测验。你可以把 HTML、CSS 和 JavaScript 代码复制过去。如果你选的工具没有独立的 JavaScript/CSS 板面,可以随时在 HTML 页面中添加 <code>&lt;script&gt;</code>/<code>&lt;style&gt;</code> 元素。</p>
+</div>
+
+<h2 id="项目简介">项目简介</h2>
+
+<p>我们提供了一些 HTML、CSS、相片和几行 JavaScript 代码。需要你来编写必要的 JavaScript 代码让这个项目运行起来。HTML 的 body 部分如下:</p>
+
+<pre class="brush: html notranslate">&lt;h1&gt;照片库&lt;/h1&gt;
+
+&lt;div class="full-img"&gt;
+ &lt;img class="displayed-img" src="images/pic1.jpg"&gt;
+ &lt;div class="overlay"&gt;&lt;/div&gt;
+ &lt;button class="dark"&gt;变暗&lt;/button&gt;
+&lt;/div&gt;
+
+&lt;div class="thumb-bar"&gt;
+
+&lt;/div&gt;</pre>
+
+<p>你可以尝试操作一下这个示例,也可 <a href="https://roy-tian.github.io/mdn-examples/javascript/gallery/">在线打开</a>。(不要偷看源代码哦!)</p>
+
+<div class="hidden">
+<h6 id="Image_gallery">Image gallery</h6>
+
+<pre class="brush: html notranslate">&lt;!DOCTYPE html&gt;
+&lt;html&gt;
+ &lt;head&gt;
+ &lt;meta charset="utf-8"&gt;
+ &lt;style&gt;
+ h1 {
+ font-family: helvetica, arial, sans-serif;
+ text-align: center;
+ }
+
+ body {
+ width: 640px;
+ margin: 0 auto;
+ }
+
+ .full-img {
+ position: relative;
+ display: block;
+ width: 640px;
+ height: 480px;
+ }
+
+ .overlay {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 640px;
+ height: 480px;
+ background-color: rgba(0,0,0,0);
+ }
+
+ button {
+ background: rgba(150,150,150,0.6);
+ text-shadow: 1px 1px 1px white;
+ border: 1px solid #999;
+ position: absolute;
+ cursor: pointer;
+ top: 2px;
+ left: 2px;
+ }
+
+ .thumb-bar img {
+ display: block;
+ width: 20%;
+ float: left;
+ cursor: pointer;
+ }
+  &lt;/style&gt;
+ &lt;/head&gt;
+
+ &lt;body&gt;
+ &lt;h1&gt;照片库&lt;/h1&gt;
+
+ &lt;div class="full-img"&gt;
+ &lt;img class="displayed-img" src="https://roy-tian.github.io/mdn-examples/gallery/images/pic1.jpg"&gt;
+ &lt;div class="overlay"&gt;&lt;/div&gt;
+ &lt;button class="dark"&gt;变暗&lt;/button&gt;
+ &lt;/div&gt;
+
+ &lt;div class="thumb-bar"&gt; &lt;/div&gt;
+ &lt;script&gt;
+ var displayedImage = document.querySelector('.displayed-img');
+ var thumbBar = document.querySelector('.thumb-bar');
+
+ btn = document.querySelector('button');
+ var overlay = document.querySelector('.overlay');
+
+ for(var i = 1; i &lt;= 5; i++) {
+ var newImage = document.createElement('img');
+ newImage.setAttribute('src', 'https://roy-tian.github.io/mdn-examples/gallery/images/pic' + i + '.jpg');
+ thumbBar.appendChild(newImage);
+ newImage.onclick = function(e) {
+ var imgSrc = e.target.getAttribute('src');
+ displayImage(imgSrc);
+ }
+ }
+
+ function displayImage(imgSrc) {
+ displayedImage.setAttribute('src', imgSrc);
+ }
+
+ btn.onclick = function() {
+ var btnClass = btn.getAttribute('class');
+ if(btnClass === 'dark') {
+ btn.setAttribute('class','light');
+ btn.textContent = '变亮';
+ overlay.style.backgroundColor = 'rgba(0,0,0,0.5)';
+ } else {
+ btn.setAttribute('class','dark');
+ btn.textContent = '变暗';
+ overlay.style.backgroundColor = 'rgba(0,0,0,0)';
+ }
+ }
+  &lt;/script&gt;
+ &lt;/body&gt;
+&lt;/html&gt;</pre>
+</div>
+
+<ul>
+</ul>
+
+<p>{{ EmbedLiveSample('Image_gallery', '100%', 680, "", "", "hide-codepen-jsfiddle") }}</p>
+
+<p>以下是本例中 CSS 文件最值得关注的部分:</p>
+
+<ul>
+ <li><code>full-img &lt;div&gt;</code> 中有三个绝对定位的元素:一个显示全尺寸图片的 <code>&lt;img&gt;</code>,一个空 <code>&lt;div&gt;</code>(覆盖在 <code>&lt;img&gt;</code> 之上,且与之大小相同,用来设置半透明背景色来使图片变暗),和一个用来控制变暗效果的 <code>&lt;button&gt;</code>。</li>
+ <li>将 <code>thumb-bar &lt;div&gt;</code> 中图片(即“缩略图”)的宽度设置为20%,并且将它们浮动至左端,使得它们在同一行上依次排列。</li>
+</ul>
+
+<p>你的 JavaScript 需要:</p>
+
+<ul>
+ <li>遍历所有相片,为每张相片生成一个 <code>&lt;img&gt;</code> 元素并把它们插入 <code>thumb-bar &lt;div&gt;</code> 中,这样图片就会嵌入页面。</li>
+ <li>为 <code>thumb-bar &lt;div&gt;</code> 里的每个 <code>&lt;img&gt;</code> 元素添加一个 <code>onclick</code> 处理器,在图片被点击时相应的图片被显示到 <code>displayed-img &lt;img&gt;</code> 元素上。</li>
+ <li>给 <code>&lt;button&gt;</code> 元素添加一个 <code>onclick</code> 处理器,当按钮被点击时,将全尺寸图片变暗,再次点击时取消。</li>
+</ul>
+
+<p>可以看一下 <a class="external external-icon" href="https://roy-tian.github.io/learning-area/javascript/building-blocks/gallery/">完成的示例</a> 体会一下。(别偷看代码哦)</p>
+
+<h2 id="步骤">步骤</h2>
+
+<p>以下是你的工作。</p>
+
+<h3 id="遍历照片">遍历照片</h3>
+
+<p>我们提供的代码中用一个名为 <code>thumBar</code> 的变量用来存储 <code>thumb-bar &lt;div&gt;</code> 的引用,创建了一个新的 <code>&lt;img&gt;</code> 元素,将它的 <code>src</code> 属性值设置成 <code>xxx</code> 占位符,并且将这个新的 <code>&lt;img&gt;</code> 元素添加到 <code>thumbBar</code> 里。</p>
+
+<p>你应该:</p>
+
+<ol>
+ <li>在"遍历图片"注释下方添加一个循环来遍历 5 张图片,只需要遍历 5 个数字,每个数字代表一张图片。</li>
+ <li>每次迭代中,用图片路径的字符串替换掉占位符 <code>xxx</code>。即在每次迭代中把 <code>src</code> 属性设置为图片的路径。记住,图片都在 images 目录下,文件名是 <code>pic1.jpg</code><font face="Open Sans, arial, x-locale-body, sans-serif">、</font><code>pic2.jpg</code>,等等。</li>
+</ol>
+
+<h3 id="给每一个缩略图添加点击处理器">给每一个缩略图添加点击处理器</h3>
+
+<p>每次迭代中,你需要给当前的 <code>newImage</code> 加上一个 <code>onclick</code> 事件处理函数——它应该:</p>
+
+<ol>
+ <li>找到当前图片的 <code>src</code> 属性值。这个可以通过对当前的 <code>&lt;img&gt;</code> 用 <code>"src"</code> 作为参数调用 <code><a href="/zh-CN/docs/Web/API/Element/getAttribute">getAttribute()</a></code> 函数来完成,但是如何在代码里获取图片?用 <code>newImage</code> 是不行的,因为在事件处理函数应用之前循环已经结束,这样每次迭代 <code>src</code> 的值都会是最后一张图片。因此,对于每个事件处理器,<code>&lt;img&gt;</code> 都是函数的目标。是否可以从事件对象获得相关信息呢。</li>
+ <li>调用一个函数,取上一步返回的 <code>src</code> 值作为参数。可以给这个函数起一个喜欢的名字。</li>
+ <li>事件处理器函数应该把 <code>displayed-img &lt;img&gt;</code> 的 <code>src</code> 属性值设为作为参数传入的 <code>src</code> 值。我们已经提供了一个 <code>displayedImg</code> 变量存储相关的 <code>&lt;img&gt;</code>。注意我们需要的是一个定义好的、有名字的函数。</li>
+</ol>
+
+<h3 id="为变亮变暗按钮编写处理器">为变亮/变暗按钮编写处理器</h3>
+
+<p>还剩最后的变亮/变暗 <code>&lt;button&gt;</code>。我们已经提供了一个名为 <code>btn</code> 的变量来存储 <code>&lt;button&gt;</code> 的引用。需要添加一个 <code>onclick</code> 事件处理函数:</p>
+
+<ol>
+ <li>检查当前 <code>&lt;button&gt;</code> 按钮的类名称,仍可用 <code>getAttribute()</code> 函数取得。</li>
+ <li>如果类名的值是 <code>"dark"</code>, 将 <code>&lt;button&gt;</code> 的类名变为 <code>"light"</code>(使用 <code><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/Element/setAttribute">setAttribute()</a></code>), 文本内容变为 "变亮",蒙板 <code>&lt;div&gt;</code> 的{{cssxref("background-color")}} 设为 <code>"rgba(0,0,0,0.5)"</code>。</li>
+ <li>如果类名的值不是 <code>"dark"</code>, 将 <code>&lt;button&gt;</code> 的类名变为 <code>"dark"</code>,文本内容变为 "变暗",蒙板 <code>&lt;div&gt;</code> 的{{cssxref("background-color")}} 设为 <code>"rgba(0,0,0,0)"</code>。</li>
+</ol>
+
+<p>以下是实现上述 2、3 点所提功能的基本代码:</p>
+
+<pre class="brush: js notranslate">btn.setAttribute('class', xxx);
+btn.textContent = xxx;
+overlay.style.backgroundColor = xxx;</pre>
+
+<h2 id="提示">提示</h2>
+
+<ul>
+ <li>完全不需要修改 HTML 和 CSS 文件。</li>
+</ul>
+
+<h2 id="测验">测验</h2>
+
+<p>如果你是在课堂上进行这个测验,你可以把作品交给导或教授去打分了。如果你是在自学,可以很容易地在 <a href="https://discourse.mozilla.org/t/image-gallery-assessment/24687">本节测验的讨论页</a> 或 <a href="https://wiki.mozilla.org/IRC" rel="noopener">Mozilla 聊天室</a>的 <a href="irc://irc.mozilla.org/mdn">#mdn</a> 频道回复得到批改指南。请先自己试着做,作弊学不到任何东西!</p>
+
+<p>{{PreviousMenu("Learn/JavaScript/Building_blocks/Events", "Learn/JavaScript/Building_blocks")}}</p>
+
+<h2 id="本章目录">本章目录</h2>
+
+<ul>
+ <li><a href="https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Building_blocks/conditionals">条件语句:在代码中作出决策</a></li>
+ <li><a href="https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Building_blocks/Looping_code">循环代码</a></li>
+ <li><a href="https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Building_blocks/Functions">函数:可复用的代码块</a></li>
+ <li><a href="https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Building_blocks/Build_your_own_function">创建自己的函数</a></li>
+ <li><a href="https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Building_blocks/Return_values">函数的返回值</a></li>
+ <li><a href="https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Building_blocks/Events">初识“事件”</a></li>
+ <li><a href="https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Building_blocks/Image_gallery">照片库</a></li>
+</ul>
diff --git a/files/zh-cn/learn/javascript/client-side_web_apis/client-side_storage/index.html b/files/zh-cn/learn/javascript/client-side_web_apis/client-side_storage/index.html
new file mode 100644
index 0000000000..8124baee4e
--- /dev/null
+++ b/files/zh-cn/learn/javascript/client-side_web_apis/client-side_storage/index.html
@@ -0,0 +1,798 @@
+---
+title: 客户端存储
+slug: Learn/JavaScript/Client-side_web_APIs/Client-side_storage
+tags:
+ - API
+ - IndexedDB
+ - JavaScript
+ - 初学者
+ - 存储
+ - 文章
+translation_of: Learn/JavaScript/Client-side_web_APIs/Client-side_storage
+---
+<p>{{LearnSidebar}}</p>
+
+<div>{{PreviousMenu("Learn/JavaScript/Client-side_web_APIs/Video_and_audio_APIs", "Learn/JavaScript/Client-side_web_APIs")}}</div>
+
+<div></div>
+
+<p class="summary">现代web浏览器提供了很多在用户电脑web客户端存放数据的方法 — 只要用户的允许 — 可以在它需要的时候被重新获得。这样能让你存留的数据长时间保存, 保存站点和文档在离线情况下使用, 保留你对其站点的个性化配置等等。本篇文章只解释它们工作的一些很基础的部分。</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">Prerequisites:</th>
+ <td>JavaScript 基础 (查看 <a href="/en-US/docs/Learn/JavaScript/First_steps">第一步</a>, <a href="/en-US/docs/Learn/JavaScript/Building_blocks">构建的块</a>, <a href="/en-US/docs/Learn/JavaScript/Objects">JavaScript 对象</a>), <a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Introduction"> 基础的客户端 API</a></td>
+ </tr>
+ <tr>
+ <th scope="row">Objective:</th>
+ <td>学习如何使用客户端存储 API 来存储应用数据。</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="客户端存储">客户端存储?</h2>
+
+<p>在其他的MDN学习中我们已经讨论过 静态网站(<a href="/en-US/docs/Learn/Server-side/First_steps/Client-Server_overview#Static_sites">static sites</a>) 和动态网站( <a href="/en-US/docs/Learn/Server-side/First_steps/Client-Server_overview#Dynamic_sites">dynamic sites</a>)的区别。 大多数现代的web站点是动态的— 它们在服务端使用各种类型的数据库来存储数据(服务端存储), 之后通过运行服务端( <a href="/en-US/docs/Learn/Server-side">server-side</a>) 代码来重新获取需要的数据,把其数据插入到静态页面的模板中,并且生成出HTML渲染到用户浏览上。</p>
+
+<p>客户端存储以相同的原理工作,但是在使用上有一些不同。它是由 JavaScript APIs 组成的因此允许你在客户端存储数据 (比如在用户的机器上),而且可以在需要的时候重新取得需要的数据。这有很多明显的用处,比如:</p>
+
+<ul>
+ <li>个性化网站偏好(比如显示一个用户选择的窗口小部件,颜色主题,或者字体)。</li>
+ <li>保存之前的站点行为 (比如从先前的session中获取购物车中的内容, 记住用户是否之前已经登陆过)。</li>
+ <li>本地化保存数据和静态资源可以使一个站点更快(至少让资源变少)的下载, 甚至可以在网络失去链接的时候变得暂时可用。</li>
+ <li>保存web已经生产的文档可以在离线状态下访问。</li>
+</ul>
+
+<p>通常客户端和服务端存储是结合在一起使用的。例如,你可以从数据库中下载一个由网络游戏或音乐播放器应用程序使用的音乐文件,将它们存储在客户端数据库中,并按需要播放它们。用户只需下载音乐文件一次——在随后的访问中,它们将从数据库中检索。</p>
+
+<div class="note">
+<p><strong>注意:</strong>使用客户端存储 API 可以存储的数据量是有限的(可能是每个API单独的和累积的总量);具体的数量限制取决于浏览器,也可能基于用户设置。有关更多信息,获取更多信息,请参考<a href="https://developer.mozilla.org/zh-CN/docs/Web/API/IndexedDB_API/Browser_storage_limits_and_eviction_criteria">浏览器存储限制和清理标准</a>。</p>
+</div>
+
+<h3 id="传统方法:cookies">传统方法:cookies</h3>
+
+<p>客户端存储的概念已经存在很长一段时间了。从早期的网络时代开始,网站就使用 <a href="/zh-CN/docs/Web/HTTP/Cookies">cookies</a> 来存储信息,以在网站上提供个性化的用户体验。它们是网络上最早最常用的客户端存储形式。<br>
+ 因为在那个年代,有许多问题——无论是从技术上的还是用户体验的角度——都是困扰着 cookies 的问题。这些问题非常重要,以至于当第一次访问一个网站时,欧洲居民会收到消息,告诉他们是否会使用 cookies 来存储关于他们的数据,而这是由一项被称为<a href="/zh-CN/docs/Web/HTTP/Cookies#%E6%AC%A7%E7%9B%9FCookie%E6%8C%87%E4%BB%A4">欧盟 Cookie 条例</a>的欧盟法律导致的。</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/15734/cookies-notice.png" style="display: block; margin: 0 auto;"></p>
+
+<p>由于这些原因,我们不会在本文中教你如何使用cookie。毕竟它过时、存在各种<a href="/zh-CN/docs/Web/HTTP/Cookies#安全">安全问题</a>,而且无法存储复杂数据,而且有更好的、更现代的方法可以在用户的计算机上存储种类更广泛的数据。<br>
+ cookie的唯一优势是它们得到了非常旧的浏览器的支持,所以如果您的项目需要支持已经过时的浏览器(比如 Internet Explorer 8 或更早的浏览器),cookie可能仍然有用,但是对于大多数项目(很明显不包括本站)来说,您不需要再使用它们了。其实cookie也没什么好说的,<code><a href="/zh-CN/docs/Web/API/Document/cookie">document.cookie</a></code>一把梭就完事了。</p>
+
+<div class="note">
+<p>为什么仍然有新创建的站点使用 cookies?这主要是因为开发人员的习惯,使用了仍然使用cookies的旧库,以及存在许多web站点,提供了过时的参考和培训材料来学习如何存储数据。</p>
+</div>
+
+<h3 id="新流派:Web_Storage_和_IndexedDB">新流派:Web Storage 和 IndexedDB</h3>
+
+<p>现代浏览器有比使用 cookies 更简单、更有效的存储客户端数据的 API。</p>
+
+<ul>
+ <li><a href="/en-US/docs/Web/API/Web_Storage_API">Web Storage API</a> 提供了一种非常简单的语法,用于存储和检索较小的、由名称和相应值组成的数据项。当您只需要存储一些简单的数据时,比如用户的名字,用户是否登录,屏幕背景使用了什么颜色等等,这是非常有用的。</li>
+ <li><a href="/en-US/docs/Web/API/IndexedDB_API">IndexedDB API</a> 为浏览器提供了一个完整的数据库系统来存储复杂的数据。这可以用于存储从完整的用户记录到甚至是复杂的数据类型,如音频或视频文件。</li>
+</ul>
+
+<p>您将在下面了解更多关于这些 API 的信息。</p>
+
+<h3 id="未来:Cache_API">未来:Cache API</h3>
+
+<p>一些现代浏览器支持新的 {{domxref("Cache")}} API。这个API是为存储特定HTTP请求的响应文件而设计的,它对于像存储离线网站文件这样的事情非常有用,这样网站就可以在没有网络连接的情况下使用。缓存通常与 <a href="/en-US/docs/Web/API/Service_Worker_API">Service Worker API</a> 组合使用,尽管不一定非要这么做。<br>
+ Cache 和 Service Workers 的使用是一个高级主题,我们不会在本文中详细讨论它,尽管我们将在下面的  {{anch("离线文件存储")}} 一节中展示一个简单的例子。</p>
+
+<h2 id="存储简单数据_—_web_storage">存储简单数据 — web storage</h2>
+
+<p><a href="/en-US/docs/Web/API/Web_Storage_API">Web Storage API</a> 非常容易使用 — 你只需存储简单的 键名/键值 对数据 (限制为字符串、数字等类型) 并在需要的时候检索其值。</p>
+
+<h3 id="基本语法">基本语法</h3>
+
+<p>让我们来告诉你怎么做:</p>
+
+<ol>
+ <li>
+ <p>第一步,访问 GitHub 上的 <a href="https://mdn.github.io/learning-area/javascript/apis/client-side-storage/web-storage/index.html">web storage blank template</a> (在新标签页打开此<a href="https://mdn.github.io/learning-area/javascript/apis/client-side-storage/web-storage/index.html" rel="noopener"><font>模板</font></a>)。</p>
+ </li>
+ <li>
+ <p>打开你浏览器开发者工具的 JavaScript 控制台。</p>
+ </li>
+ <li>
+ <p>你所有的 web storage 数据都包含在浏览器内两个类似于对象的结构中: {{domxref("Window.sessionStorage", "sessionStorage")}} 和 {{domxref("Window.localStorage", "localStorage")}}。 第一种方法,只要浏览器开着,数据就会一直保存 (关闭浏览器时数据会丢失) ,而第二种会一直保存数据,甚至到浏览器关闭又开启后也是这样。我们将在本文中使用第二种方法,因为它通常更有用。</p>
+ </li>
+ <li>
+ <p>{{domxref("Storage.setItem()")}} 方法允许您在存储中保存一个数据项——它接受两个参数:数据项的名字及其值。试着把它输入到你的JavaScript控制台(如果你愿意的话,可以把它的值改为你自己的名字!)</p>
+
+ <pre class="brush: js notranslate">localStorage.setItem('name','Chris');</pre>
+ </li>
+ <li>
+ <p>{{domxref("Storage.getItem()")}} 方法接受一个参数——你想要检索的数据项的名称——并返回数据项的值。现在将这些代码输入到你的JavaScript控制台:</p>
+
+ <pre class="brush: js notranslate">var myName = localStorage.getItem('name');
+myName</pre>
+
+ <p>在输入第二行时,您应该会看到 <code>myName</code>变量现在包含<code>name</code>数据项的值。</p>
+ </li>
+ <li>
+ <p>{{domxref("Storage.removeItem()")}} 方法接受一个参数——你想要删除的数据项的名称——并从 web storage 中删除该数据项。在您的JavaScript控制台中输入以下几行:</p>
+
+ <pre class="brush: js notranslate">localStorage.removeItem('name');
+var myName = localStorage.getItem('name');
+myName</pre>
+
+ <p>第三行现在应该返回 <code>null</code> —  <code>name</code> 项已经不存在于 web storage 中。</p>
+ </li>
+</ol>
+
+<h3 id="数据会一直存在!">数据会一直存在!</h3>
+
+<p>web storage 的一个关键特性是,数据在不同页面加载时都存在(甚至是当浏览器关闭后,对localStorage的而言)。让我们来看看这个:</p>
+
+<ol>
+ <li>
+ <p>再次打开我们的 Web Storage 空白模板,但是这次你要在不同的浏览器中打开这个教程!这样可以更容易处理。</p>
+ </li>
+ <li>
+ <p>在浏览器的 JavaScript 控制台中输入这几行:</p>
+
+ <pre class="brush: js notranslate">localStorage.setItem('name','Chris');
+var myName = localStorage.getItem('name');
+myName</pre>
+
+ <p>你应该看到 name 数据项返回。</p>
+ </li>
+ <li>
+ <p>现在关掉浏览器再把它打开。</p>
+ </li>
+ <li>
+ <p>再次输入下面几行:</p>
+
+ <pre class="brush: js notranslate">var myName = localStorage.getItem('name');
+myName</pre>
+
+ <p>你应该看到,尽管浏览器已经关闭,然后再次打开,但仍然可以使用该值。</p>
+ </li>
+</ol>
+
+<h3 id="为每个域名分离储存">为每个域名分离储存</h3>
+
+<p>每个域都有一个单独的数据存储区(每个单独的网址都在浏览器中加载). 你 会看到,如果你加载两个网站(例如google.com和amazon.com)并尝试将某个项目存储在一个网站上,该数据项将无法从另一个网站获取。</p>
+
+<p>这是有道理的 - 你可以想象如果网站能够查看彼此的数据,就会出现安全问题!</p>
+
+<h3 id="更复杂的例子">更复杂的例子</h3>
+
+<p><font>让我们通过编写一个简单的工作示例来应用这些新发现的知识,让你了解如何使用网络存储。</font><font>我们的示例将允许你输入一个名称,然后该页面将刷新,以提供个性化问候。</font><font>这种状态也会页面/浏览器重新加载期间保持,因为这个名称存储在Web Storage 中。</font></p>
+
+<p>你可以在 <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/client-side-storage/web-storage/personal-greeting.html">personal-greeting.html</a> 中找到示例文件 —— 这包含一个具有标题,内容和页脚,以及用于输入您的姓名的表单的简单网站。</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/15735/web-storage-demo.png" style="display: block; margin: 0 auto;"></p>
+
+<p>让我们来构建示例,以便了解它的工作原理。</p>
+
+<ol>
+ <li>
+ <p>首先,在您的计算机上的新目录中创建一个 <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/client-side-storage/web-storage/personal-greeting.html">personal-greeting.html</a> 文件的副本。</p>
+ </li>
+ <li>
+ <p><font><font>接下来,请注意我们的HTML如何引用一个名为</font></font><code>index.js</code><font><font>的 JavaScript 文件</font></font><font><font>(请参见第40行)。</font><font>我们需要创建它并将 JavaScript 代码写入其中。</font></font><font><font>在与HTML文件相同的目录中</font><font>创建一个</font></font><code>index.js</code><font><font>文件。</font></font> </p>
+ </li>
+ <li>
+ <p><font>我们首先创建对所有需要在此示例中操作的HTML功能的引用 - 我们将它们全部创建为常量,因为这些引用在应用程序的生命周期中不需要更改。</font><font>将以下几行添加到你的 JavaScript 文件中:</font></p>
+
+ <pre class="brush: js notranslate">// 创建所需的常量
+const rememberDiv = document.querySelector('.remember');
+const forgetDiv = document.querySelector('.forget');
+const form = document.querySelector('form');
+const nameInput = document.querySelector('#entername');
+const submitBtn = document.querySelector('#submitname');
+const forgetBtn = document.querySelector('#forgetname');
+
+const h1 = document.querySelector('h1');
+const personalGreeting = document.querySelector('.personal-greeting');</pre>
+ </li>
+ <li>
+ <p><font>接下来,我们需要包含一个小小的事件监听器,以在按下提交按钮时阻止实际的提交表单动作自身,因为这不是我们想要的行为。</font><font>在您之前的代码下添加此代码段:</font> 在你之前的代码后添加这段代码:</p>
+
+ <pre class="brush: js notranslate">// 当按钮按下时阻止表单提交
+form.addEventListener('submit', function(e) {
+ e.preventDefault();
+});</pre>
+ </li>
+ <li>
+ <p><font><font>现在我们需要添加一个事件监听器,当单击“Say hello”按钮时,它的处理函数将会运行。</font><font>这些注释详细解释了每一处都做了什么,但实际上我们在这里获取用户输入到文本输入框中的名字并使用</font></font><code>setItem()</code><font><font>将它保存在网络存储中</font></font><font><font>,然后运行一个名为</font></font><code>nameDisplayCheck()</code><font><font>的函数</font></font><font><font>来处理实际的网站文本的更新。将此添加到代码的底部:</font></font> </p>
+
+ <pre class="brush: js notranslate">// run function when the 'Say hello' button is clicked
+submitBtn.addEventListener('click', function() {
+ // store the entered name in web storage
+ localStorage.setItem('name', nameInput.value);
+ // run nameDisplayCheck() to sort out displaying the
+ // personalized greetings and updating the form display
+ nameDisplayCheck();
+});</pre>
+ </li>
+ <li>
+ <p><font><font>此时,我们还需要一个事件处理程序,以便在单击“</font></font>Forget<font><font>”按钮时运行一个函数——且仅在单击“Say hello”按钮(两种表单状态来回切换)后才显示。在这个功能中,我们</font></font><font><font>使用</font></font><code>removeItem()</code><font><font>从网络存储中</font><font>删除</font><font>项目</font></font><code>name</code><font><font>,然后再次运行</font></font><code>nameDisplayCheck()</code><font><font>以更新显示。</font><font>将其添加到底部:</font></font> </p>
+
+ <pre class="brush: js notranslate">// run function when the 'Forget' button is clicked
+forgetBtn.addEventListener('click', function() {
+ // Remove the stored name from web storage
+ localStorage.removeItem('name');
+ // run nameDisplayCheck() to sort out displaying the
+ // generic greeting again and updating the form display
+ nameDisplayCheck();
+});</pre>
+ </li>
+ <li>
+ <p><font><font>现在是时候定义</font></font><code>nameDisplayCheck()</code><font><font>函数本身了。</font><font>在这里,我们通过使用</font></font><code>localStorage.getItem('name')</code><font><font>作为测试条件</font><font>来检查name 数据项是否已经存储在Web Storage 中</font><font>。</font><font>如果它已被存储,则该调用的返回值为</font></font><code>true</code><font><font>; </font><font>如果没有,它会是</font></font><code>false</code><font><font>。</font><font>如果是</font></font><code>true</code><font><font>,我们会显示个性化问候语,显示表格的“</font></font>forget<font><font>”部分,并隐藏表格的“</font></font>Say hello<font><font>”部分。</font><font>如果是</font></font><code>false</code><font><font>,我们会显示一个通用问候语,并做相反的事。</font><font>再次将下面的代码添到底部:</font></font></p>
+
+ <pre class="brush: js notranslate">// define the nameDisplayCheck() function
+function nameDisplayCheck() {
+ // check whether the 'name' data item is stored in web Storage
+ if(localStorage.getItem('name')) {
+ // If it is, display personalized greeting
+ let name = localStorage.getItem('name');
+ h1.textContent = 'Welcome, ' + name;
+ personalGreeting.textContent = 'Welcome to our website, ' + name + '! We hope you have fun while you are here.';
+ // hide the 'remember' part of the form and show the 'forget' part
+ forgetDiv.style.display = 'block';
+ rememberDiv.style.display = 'none';
+ } else {
+ // if not, display generic greeting
+ h1.textContent = 'Welcome to our website ';
+ personalGreeting.textContent = 'Welcome to our website. We hope you have fun while you are here.';
+ // hide the 'forget' part of the form and show the 'remember' part
+ forgetDiv.style.display = 'none';
+ rememberDiv.style.display = 'block';
+ }
+}</pre>
+ </li>
+ <li>
+ <p><font><font>最后但同样重要的是,我们需要在</font></font><font><font>每次加载页面时</font><font>运行</font></font><code>nameDisplayCheck()</code><font><font>函数。</font><font>如果我们不这样做,那么个性化问候不会在页面重新加载后保持。</font><font>将以下代码添加到代码的底部:</font></font></p>
+
+ <pre class="brush: js notranslate">document.body.onload = nameDisplayCheck;</pre>
+ </li>
+</ol>
+
+<p><font>你的例子完成了 - 做得好!</font><font>现在剩下的就是保存你的代码并在浏览器中测试你的HTML页面。你可以在这里看到我们的</font><a href="https://mdn.github.io/learning-area/javascript/apis/client-side-storage/web-storage/personal-greeting.html" rel="noopener"><font><font>完成版本并在线运行</font></font></a><font><font>。</font></font></p>
+
+<div class="note">
+<p><strong>注意</strong>: 在 <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API">Using the Web Storage API</a> 中还有一个稍微复杂点儿的示例。</p>
+</div>
+
+<div class="note">
+<p><strong>注意</strong>: 在完成版本的源代码中, <code>&lt;script src="index.js" defer&gt;&lt;/script&gt;</code> 一行里, <code>defer</code> 属性指明<font>在页面加载完成之前,</font>{{htmlelement("script")}}<font>元素的内容</font><font>不会执行。</font></p>
+</div>
+
+<h2 id="存储复杂数据_—_IndexedDB">存储复杂数据 — IndexedDB</h2>
+
+<p><a href="https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API"><font><font>IndexedDB API</font></font></a><font><font>(有时简称 IDB )是可以在浏览器中访问的一个完整的数据库系统,在这里,你可以存储复杂的关系数据。其种类不限于像字符串和数字这样的简单值。你可以在一个</font></font>IndexedDB中<font><font>存储视频,图像和许多其他的内容。</font></font></p>
+
+<p><font>但是,这确实是有代价的:使用IndexedDB要比Web Storage API复杂得多。</font><font>在本节中,我们仅仅只能浅尝辄止地一提它的能力,不过我们会给你足够基础知识以帮助你开始。</font></p>
+
+<h3 id="通过一个笔记存储示例演示">通过一个笔记存储示例演示</h3>
+
+<p>在这里,我们将向您介绍一个示例,该示例允许您在浏览器中存储笔记并随时查看和删除它们,在我们进行时,我们将解释IDB的最基本部分并让您自己构建注释。</p>
+
+<p>这个应用看起来像这样:</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/15744/idb-demo.png" style="border-style: solid; border-width: 1px; display: block; margin: 0px auto;"></p>
+
+<p>每个笔记都有一个标题和一些正文,每个都可以单独编辑。我们将在下面通过的JavaScript代码提供详细的注释,以帮助您了解正在发生的事情。</p>
+
+<h3 id="开始">开始</h3>
+
+<p>1、首先,将 <code><a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/client-side-storage/indexeddb/notes/index.html">index.html</a></code>, <code><a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/client-side-storage/indexeddb/notes/style.css">style.css</a></code>, 和 <code><a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/client-side-storage/indexeddb/notes/index-start.js">index-start.js</a></code> 文件的本地副本放入本地计算机上的新目录中。 </p>
+
+<p>2、浏览这些文件。 您将看到HTML非常简单:具有页眉和页脚的网站,以及包含显示注释的位置的主内容区域,以及用于在数据库中输入新注释的表单。 CSS提供了一些简单的样式,使其更清晰。 JavaScript文件包含五个声明的常量,其中包含对将显示注释的 {{htmlelement("ul")}} 元素的引用,标题和正文  {{htmlelement("input")}} 元素,{{htmlelement("form")}}本身,以及{{htmlelement("button")}}。</p>
+
+<p>3、将您的JavaScript文件重命名为 <code>index.js</code> 。 您现在可以开始向其添加代码了。</p>
+
+<h3 id="数据库初始设置">数据库初始设置</h3>
+
+<p>现在让我们来看看为了建立数据库必须首先要做什么。</p>
+
+<ol>
+ <li>
+ <p>在常量声明下,加入这几行:</p>
+
+ <pre class="brush: js notranslate">// Create an instance of a db object for us to store the open database in
+let db;</pre>
+
+ <p>这里我们声明了一个叫 <code>db</code> 的变量 — 这将在之后被用来存储一个代表数据库的对象。我们将在几个地方使用它,所以我们为了方便使用而在这里把它声明为全局的。</p>
+ </li>
+ <li>
+ <p>接着,在你的代码最后添加如下代码:</p>
+
+ <pre class="brush: js notranslate">window.onload = function() {
+
+};</pre>
+
+ <p>我们将把所有的后续代码写在这个 <code>window.onload</code> 事件处理函数内,这个函数将在window的{{event("load")}}事件被触发时调用,为了确保我们没有在应用完整加载前试图使用IndexedDB功能(如果我们不这么做,它会失败)。</p>
+ </li>
+ <li>
+ <p><font><font>在</font></font><code>window.onload</code><font><font>处理程序内,添加以下内容:</font></font></p>
+
+ <pre class="brush: js notranslate">// Open our database; it is created if it doesn't already exist
+// (see onupgradeneeded below)
+let request = window.indexedDB.open('notes', 1);</pre>
+
+ <p><font><font>此行创建一个</font></font> <code>request</code> <font><font>变量,目的是打开</font></font> <code>notes</code><font><font>数据库的</font></font> <code>1</code><font><font>版本</font></font><font><font>。</font><font>如果<code>notes</code>数据库不存在,则后续代码将为您创建。</font><font>您将在IndexedDB中经常看到此请求模式。</font><font>数据库操作需要时间。</font><font>您不希望在等待结果时挂起浏览器,因此数据库操作是</font></font><a href="https://developer.mozilla.org/en-US/docs/Glossary/asynchronous" title="异步:异步是指通信环境,其中每个方在方便或可能时而不是立即接收和处理消息。"><font><font>异步的</font></font></a><font><font>,这意味着它们不会立即发生,而是在将来的某个时刻发生,并且在完成后会收到通知。</font></font></p>
+
+ <p><font><font>要在IndexedDB中处理此问题,您需要创建一个请求对象(可以随意命名 - </font></font>命名为<code>request</code>,<font><font>可以表明它的用途)。</font><font>然后,在请求完成或者失败时,使用事件处理程序来运行代码,您将在下面看到这些代码。</font></font></p>
+
+ <div class="note">
+ <p><strong><font><font>注意</font></font></strong><font><font>:版本号很重要。</font><font>如果要升级数据库(例如:更改表结构),则必须使用增加的版本号或者</font></font><code>onupgradeneeded</code><font><font>处理程序</font><font>内指定的不同模式</font><font>(请参阅下文)等</font><font>再次运行代码</font><font>。在这个简单教程中,我们不讨论数据库升级。</font></font></p>
+ </div>
+
+ <ol>
+ <li>
+ <p><font><font>在之前添加的事件处理程序下方添加以下代码 - 在</font></font><code>window.onload</code><font><font>处理程序内:</font></font></p>
+
+ <pre class="brush: js notranslate">// onerror handler signifies that the database didn't open successfully
+request.onerror = function() {
+ console.log('Database failed to open');
+};
+
+// onsuccess handler signifies that the database opened successfully
+request.onsuccess = function() {
+ console.log('Database opened successfully');
+
+ // Store the opened database object in the db variable. This is used a lot below
+ db = request.result;
+
+ // Run the displayData() function to display the notes already in the IDB
+ displayData();
+};</pre>
+
+ <p><font><font>如果系统返回:请求失败,</font></font><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/IDBRequest/onerror" rel="nofollow" title="此页面仍未被本地化, 期待您的翻译!"><code>request.onerror</code></a><font><font>将会运行。</font><font>这将允许你对这个问题做出响应。</font><font>在我们的简单示例中,只是将消息打印到JavaScript控制台。</font></font></p>
+
+ <p>如果系统返回:请求成功,<font><font>表明成功打开数据库</font></font> ,<a href="https://developer.mozilla.org/zh-CN/docs/Web/API/IDBRequest/onsuccess" rel="nofollow" title="此页面仍未被本地化, 期待您的翻译!"><code>request.onsuccess</code></a><font><font>将会运行。如果是这种情况,则表示已打开数据库的对象在</font></font><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/IDBRequest/result" rel="nofollow" title="此页面仍未被本地化, 期待您的翻译!"><code>request.result</code></a><font><font>属性中</font><font>变为可用</font><font>,从而允许我们操作数据库。</font><font>我们将它存储在</font></font><code>db</code><font><font>我们之前创建</font><font>的</font><font>变量中供以后使用。</font><font>我们还运行一个名为</font></font><code>displayData()</code>的自定义函数<font><font>,它把数据库中的数据显示在</font></font><a href="https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/ul" title="The HTML &lt;ul> 元素 ( 或 HTML 无序列表元素) 代表多项的无序列表,即无数值排序项的集合,且它们在列表中的顺序是没有意义的。通常情况下,无序列表项的头部可以是几种形式,如一个点,一个圆形或方形。头部的风格并不是在页面的HTML描述定义, 但在其相关的CSS 可以用 list-style-type 属性。"><code>&lt;ul&gt;</code></a><font><font>。</font><font>我们现在运行它,以便在页面加载时显示数据库中已有的注释。</font><font>您将在稍后看到此定义。</font></font></p>
+ </li>
+ </ol>
+ </li>
+ <li>
+ <p><font><font>最后,对于本节,我们可能会添加最重要的事件处理程序来设置数据库:</font></font><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/IDBOpenDBRequest/onupgradeneeded" rel="nofollow" title="此页面仍未被本地化, 期待您的翻译!"><code>request.onupgradeneeded</code></a><font><font>。</font><font>如果尚未设置数据库,或者使用比现有存储数据库更大的版本号打开数据库(执行升级时),则运行此处理程序。</font><font>在上一个处理程序下面添加以下代码:</font></font></p>
+
+ <pre class="brush: js notranslate">// Setup the database tables if this has not already been done
+request.onupgradeneeded = function(e) {
+ // Grab a reference to the opened database
+ let db = e.target.result;
+
+ // Create an objectStore to store our notes in (basically like a single table)
+ // including a auto-incrementing key
+ let objectStore = db.createObjectStore('notes', { keyPath: 'id', autoIncrement:true });
+
+ // Define what data items the objectStore will contain
+ objectStore.createIndex('title', 'title', { unique: false });
+ objectStore.createIndex('body', 'body', { unique: false });
+
+ console.log('Database setup complete');
+};</pre>
+
+ <p><font><font>这是我们定义数据库的模式(结构)的地方; </font><font>也就是说,它包含的列(或字段)集。</font><font>这里我们首先从</font></font><code>e.target.result</code><font><font>(事件目标的</font></font><code>result</code><font><font>属性)中</font><font>获取对现有数据库的引用,该引用</font><font>是</font></font><code>request</code><font><font>对象。</font><font>这相当于</font><font>处理程序</font></font><code>db = request.result;</code><font><font>内部</font><font>的行</font></font><code>onsuccess</code><font><font>,但我们需要在此单独执行此操作,因为</font></font><code>onupgradeneeded</code><font><font>处理程序(如果需要)将在</font></font><code>onsuccess</code><font><font>处理程序</font><font>之前运行</font><font>,这意味着</font></font><code>db</code><font><font>如果我们不这样做</font><font>,该</font><font>值将不可用。</font></font></p>
+
+ <p><font><font>然后</font></font><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/IDBDatabase/createObjectStore" title="此方法接受一个参数作为 store 的名称,也接受一个可选的参数对象让你可以定义重要的可选属性。你可以用这个属性唯一的标识此 store 中的每个对象。因为参数是一个标识符,所以 store 中的每一个对象都应有此属性并保证此属性唯一。"><code>IDBDatabase.createObjectStore()</code></a><font><font>,</font><font>我们使用</font><font>在打开的数据库中创建一个新的对象库。</font><font>这相当于传统数据库系统中的单个表。</font><font>我们给它起了名称注释,并且还指定了一个</font></font><code>autoIncrement</code><font><font>名为</font><font>的</font><font>关键字段</font></font><code>id</code><font><font>- 在每个新记录中,这将自动赋予增量值 - 开发人员不需要明确地设置它。</font><font>作为密钥,该</font></font><code>id</code><font><font>字段将用于唯一标识记录,例如删除或显示记录时。</font></font></p>
+
+ <p><font><font>我们还使用以下</font></font><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/IDBObjectStore/createIndex" rel="nofollow" title="此页面仍未被本地化, 期待您的翻译!"><code>IDBObjectStore.createIndex()</code></a><font><font>方法</font><font>创建另外两个索引(字段)</font><font>:( </font></font><code>title</code><font><font>每个音符将包含一个标题),以及</font></font><code>body</code><font><font>(包含音符的正文)。</font></font></p>
+ </li>
+</ol>
+
+<p>因此,通过设置这个简单的数据库模式,当我们开始向数据库添加记录时,每个记录都会沿着这些行表示为一个对象:</p>
+
+<pre class="brush: js notranslate"><span class="message-body-wrapper"><span class="message-flex-body"><span class="devtools-monospace message-body"><span class="objectBox objectBox-object"><span class="objectLeftBrace">{
+ </span><span class="nodeName">title</span><span class="objectEqual">: </span><span class="objectBox objectBox-string">"Buy milk"</span>,
+ <span class="nodeName">body</span><span class="objectEqual">: </span><span class="objectBox objectBox-string">"Need both cows milk and soya."</span>,
+ <span class="nodeName">id</span><span class="objectEqual">: </span><span class="objectBox objectBox-number">8</span></span></span></span></span><span class="message-body-wrapper"><span class="message-flex-body"><span class="devtools-monospace message-body"><span class="objectBox objectBox-object"><span class="objectRightBrace">
+}</span></span></span></span></span></pre>
+
+<h3 id="添加数据到数据库">添加数据到数据库</h3>
+
+<p><font><font>现在让我们看一下如何将记录添加到数据库中。</font><font>这将使用我们页面上的表单完成。</font></font></p>
+
+<p><font><font>在您之前的事件处理程序下面(但仍在</font></font><code>window.onload</code><font><font>处理程序中),添加以下行,该行设置一个</font></font><code>onsubmit</code><font><font>处理程序,</font><font>该</font><font>处理程序运行</font></font><code>addData()</code><font><font>在提交表单时</font><font>调用的函数</font><font>(当</font></font><a href="https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/button" title="HTML &lt;button> 元素表示一个可点击的按钮,可以用在表单或文档其它需要使用简单标准按钮的地方。"><code>&lt;button&gt;</code></a><font><font>按下</font><font>提交时</font><font>导致成功提交表单):</font></font></p>
+
+<pre class="brush: js notranslate">// Create an onsubmit handler so that when the form is submitted the addData() function is run
+form.onsubmit = addData;</pre>
+
+<p><font><font>现在让我们定义一下这个</font></font><code>addData()</code><font><font>功能。</font><font>在上一行下面添加:</font></font></p>
+
+<pre class="brush: js notranslate">// Define the addData() function
+function addData(e) {
+ // prevent default - we don't want the form to submit in the conventional way
+ e.preventDefault();
+
+ // grab the values entered into the form fields and store them in an object ready for being inserted into the DB
+ let newItem = { title: titleInput.value, body: bodyInput.value };
+
+ // open a read/write db transaction, ready for adding the data
+ let transaction = db.transaction(['notes'], 'readwrite');
+
+ // call an object store that's already been added to the database
+ let objectStore = transaction.objectStore('notes');
+
+ // Make a request to add our newItem object to the object store
+ var request = objectStore.add(newItem);
+ request.onsuccess = function() {
+ // Clear the form, ready for adding the next entry
+ titleInput.value = '';
+ bodyInput.value = '';
+ };
+
+ // Report on the success of the transaction completing, when everything is done
+ transaction.oncomplete = function() {
+ console.log('Transaction completed: database modification finished.');
+
+ // update the display of data to show the newly added item, by running displayData() again.
+ displayData();
+ };
+
+ transaction.onerror = function() {
+ console.log('Transaction not opened due to error');
+ };
+}</pre>
+
+<p><font>这很复杂; </font><font>打破它,我们:</font></p>
+
+<ul>
+ <li><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/Event/preventDefault" title="Event 接口的 preventDefault( ) 方法,告诉user agent:如果此事件没有需要显式处理,那么它默认的动作也不要做(因为默认是要做的)。此事件还是继续传播,除非碰到事件侦听器调用stopPropagation() 或stopImmediatePropagation(),才停止传播。"><code>Event.preventDefault()</code></a><font><font>在事件对象上</font><font>运行</font><font>以停止以传统方式实际提交的表单(这将导致页面刷新并破坏体验)。</font></font></li>
+ <li><font><font>创建一个表示要输入数据库的记录的对象,并使用表单输入中的值填充它。</font><font>请注意,我们不必明确包含一个</font></font><code>id</code><font><font>值 - 正如我们提前详细说明的那样,这是自动填充的。</font></font></li>
+ <li><font><font>使用该</font><font>方法</font><font>打开</font><font>对象存储</font><font>的</font></font><code>readwrite</code><font><font>事务</font><font>。</font><font>此事务对象允许我们访问对象存储,以便我们可以对其执行某些操作,例如添加新记录。</font></font><code>notes</code><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/IDBDatabase/transaction" rel="nofollow" title="此页面仍未被本地化, 期待您的翻译!"><code>IDBDatabase.transaction()</code></a></li>
+ <li><font><font>使用该</font></font><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/IDBTransaction/objectStore" rel="nofollow" title="此页面仍未被本地化, 期待您的翻译!"><code>IDBTransaction.objectStore()</code></a><font><font>方法</font><font>访问对象库</font><font>,将结果保存在  </font></font><code>objectStore</code><font><font> 变量中。</font></font></li>
+ <li><font><font>使用添加新记录到数据库</font></font><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/IDBObjectStore/add" rel="nofollow" title="此页面仍未被本地化, 期待您的翻译!"><code>IDBObjectStore.add()</code></a><font><font>。</font><font>这创建了一个请求对象,与我们之前看到的方式相同。</font></font></li>
+ <li><font><font>在生命周期的关键点</font><font>添加一堆事件处理程序</font></font><code>request</code><font><font>以及</font></font><code>transaction</code><font><font>运行代码。</font><font>请求成功后,我们会清除表单输入,以便输入下一个注释。</font><font>交易完成后,我们</font></font><code>displayData()</code><font><font>再次</font><font>运行该</font><font>功能以更新页面上的注释显示。</font></font></li>
+</ul>
+
+<h3 id="显示数据">显示数据</h3>
+
+<p><font><font>我们已经</font></font><code>displayData()</code><font><font>在代码中</font><font>引用了</font><font>两次,所以我们可能更好地定义它。</font><font>将其添加到您的代码中,位于上一个函数定义之下:</font></font></p>
+
+<pre class="brush: js notranslate">// Define the displayData() function
+function displayData() {
+ // Here we empty the contents of the list element each time the display is updated
+ // If you ddn't do this, you'd get duplicates listed each time a new note is added
+ while (list.firstChild) {
+ list.removeChild(list.firstChild);
+ }
+
+ // Open our object store and then get a cursor - which iterates through all the
+ // different data items in the store
+ let objectStore = db.transaction('notes').objectStore('notes');
+ objectStore.openCursor().onsuccess = function(e) {
+ // Get a reference to the cursor
+ let cursor = e.target.result;
+
+ // If there is still another data item to iterate through, keep running this code
+ if(cursor) {
+ // Create a list item, h3, and p to put each data item inside when displaying it
+ // structure the HTML fragment, and append it inside the list
+ let listItem = document.createElement('li');
+ let h3 = document.createElement('h3');
+ let para = document.createElement('p');
+
+ listItem.appendChild(h3);
+ listItem.appendChild(para);
+ list.appendChild(listItem);
+
+ // Put the data from the cursor inside the h3 and para
+ h3.textContent = cursor.value.title;
+ para.textContent = cursor.value.body;
+
+ // Store the ID of the data item inside an attribute on the listItem, so we know
+ // which item it corresponds to. This will be useful later when we want to delete items
+ listItem.setAttribute('data-note-id', cursor.value.id);
+
+ // Create a button and place it inside each listItem
+ let deleteBtn = document.createElement('button');
+ listItem.appendChild(deleteBtn);
+ deleteBtn.textContent = 'Delete';
+
+ // Set an event handler so that when the button is clicked, the deleteItem()
+ // function is run
+ <font face="Consolas, Monaco, Andale Mono, Ubuntu Mono, monospace">deleteBtn.onclick = deleteItem;</font>
+
+ // Iterate to the next item in the cursor
+ cursor.continue();
+ } else {
+ // Again, if list item is empty, display a 'No notes stored' message
+ if(!list.firstChild) {
+ let listItem = document.createElement('li');
+ listItem.textContent = 'No notes stored.'
+ list.appendChild(listItem);
+ }
+ // if there are no more cursor items to iterate through, say so
+ console.log('Notes all displayed');
+ }
+ };
+}</pre>
+
+<p><font><font>再次,让我们打破这个:</font></font></p>
+
+<ul>
+ <li><font><font>首先,我们清空</font></font><a href="https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/ul" title="The HTML &lt;ul> 元素 ( 或 HTML 无序列表元素) 代表多项的无序列表,即无数值排序项的集合,且它们在列表中的顺序是没有意义的。通常情况下,无序列表项的头部可以是几种形式,如一个点,一个圆形或方形。头部的风格并不是在页面的HTML描述定义, 但在其相关的CSS 可以用 list-style-type 属性。"><code>&lt;ul&gt;</code></a><font><font>元素的内容,然后填充更新的内容。</font><font>如果您不这样做,那么每次更新时都会添加大量重复内容。</font></font></li>
+ <li><font><font>接下来,我们</font></font><code>notes</code><font><font>使用</font></font><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/IDBDatabase/transaction" rel="nofollow" title="此页面仍未被本地化, 期待您的翻译!"><code>IDBDatabase.transaction()</code></a><font><font>和</font></font><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/IDBTransaction/objectStore" rel="nofollow" title="此页面仍未被本地化, 期待您的翻译!"><code>IDBTransaction.objectStore()</code></a><font><font>我们一样</font><font>得到</font><font>对象存储</font><font>的引用</font></font><code>addData()</code><font><font>,除了这里我们将它们链接在一行中。</font></font></li>
+ <li><font><font>下一步是使用</font></font><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/IDBObjectStore/openCursor" title="要确定添加操作是否已成功完成,请侦听结果的成功事件。"><code>IDBObjectStore.openCursor()</code></a><font><font>方法打开对游标的请求 - 这是一个可用于迭代对象存储中的记录的构造。</font><font>我们将一个</font></font><code>onsuccess</code><font><font>处理程序链接到该行的末尾以使代码更简洁 - 当成功返回游标时,运行处理程序。</font></font></li>
+ <li><font><font>我们</font></font><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/IDBCursor" title="IndexedDB API的IDBCursor接口表示用于遍历或迭代数据库中的多个记录的游标。"><code>IDBCursor</code></a><font><font>使用let </font><font>获取对游标本身(</font><font>对象)</font><font>的引用</font></font><code>cursor = e.target.result</code><font><font>。</font></font></li>
+ <li><font><font>接下来,我们检查光标是否包含来自数据存储区(</font></font><code>if(cursor){ ... }</code><font><font>)</font><font>的记录</font><font>- 如果是这样,我们创建一个DOM片段,用记录中的数据填充它,然后将其插入页面(</font></font><code>&lt;ul&gt;</code><font><font>元素</font><font>内部</font><font>)。</font><font>我们还包括一个删除按钮,当单击该按钮时,将通过运行该</font></font><code>deleteItem()</code><font><font>功能</font><font>删除该注释</font><font>,我们将在下一节中查看。</font></font></li>
+ <li><font><font>在</font></font><code>if</code><font><font>块</font><font>结束时</font><font>,我们使用该</font></font><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/IDBCursor/continue" rel="nofollow" title="此页面仍未被本地化, 期待您的翻译!"><code>IDBCursor.continue()</code></a><font><font>方法将光标前进到数据存储区中的下</font><font>一条</font><font>记录,然后</font></font><code>if</code><font><font>再次</font><font>运行</font><font>块</font><font>的内容</font><font>。</font><font>如果有另一个要迭代的记录,这会导致它被插入到页面中,然后</font></font><code>continue()</code><font><font>再次运行,依此类推。</font></font></li>
+ <li><font><font>当没有更多记录要迭代时,</font></font><code>cursor</code><font><font>将返回</font></font><code>undefined</code><font><font>,因此</font></font><code>else</code><font><font>块将运行而不是</font></font><code>if</code><font><font>块。</font><font>此块检查是否有任何注释被插入</font></font><code>&lt;ul&gt;</code><font><font>- 如果没有,它会插入一条消息,说没有存储注释。</font></font></li>
+</ul>
+
+<h3 id="删除一条笔记">删除一条笔记</h3>
+
+<p><font><font>如上所述,当按下音符的删除按钮时,音符将被删除。</font><font>这是通过</font></font><code>deleteItem()</code><font><font>函数</font><font>实现的</font><font>,如下所示:</font></font></p>
+
+<pre class="brush: js notranslate">// Define the deleteItem() function
+function deleteItem(e) {
+ // retrieve the name of the task we want to delete. We need
+ // to convert it to a number before trying it use it with IDB; IDB key
+ // values are type-sensitive.
+ let noteId = Number(e.target.parentNode.getAttribute('data-note-id'));
+
+ // open a database transaction and delete the task, finding it using the id we retrieved above
+ let transaction = db.transaction(['notes'], 'readwrite');
+ let objectStore = transaction.objectStore('notes');
+ let request = objectStore.delete(noteId);
+
+ // report that the data item has been deleted
+ transaction.oncomplete = function() {
+ // delete the parent of the button
+ // which is the list item, so it is no longer displayed
+ e.target.parentNode.parentNode.removeChild(e.target.parentNode);
+ console.log('Note ' + noteId + ' deleted.');
+
+ // Again, if list item is empty, display a 'No notes stored' message
+ if(!list.firstChild) {
+ let listItem = document.createElement('li');
+ listItem.textContent = 'No notes stored.';
+ list.appendChild(listItem);
+ }
+ };
+}</pre>
+
+<ul>
+ <li><font><font>第一部分可以使用一些解释 - 我们检索要删除</font></font><code>Number(e.target.parentNode.getAttribute('data-note-id'))</code><font><font>的记录的ID - 回想一下记录的ID是在</font><font>第一次显示时</font><font>保存在</font></font><code>data-note-id</code><font><font>属性中的</font></font><code>&lt;li&gt;</code><font><font>。</font><font>但是,我们需要通过全局内置的</font></font><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number"><font><font>Number()</font></font></a><font><font>对象</font><font>传递属性</font><font>,因为它当前是一个字符串,否则将无法被数据库识别。</font></font></li>
+ <li><font><font>然后,我们使用我们之前看到的相同模式获取对对象存储的引用,并使用该</font></font><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/IDBObjectStore/delete" rel="nofollow" title="此页面仍未被本地化, 期待您的翻译!"><code>IDBObjectStore.delete()</code></a><font><font>方法从数据库中删除记录,并将ID传递给它。</font></font></li>
+ <li><font><font>当数据库事务完成后,我们</font></font><code>&lt;li&gt;</code><font><font>从DOM中</font><font>删除注释</font><font>,然后再次检查以查看它是否</font></font><code>&lt;ul&gt;</code><font><font>为空,并根据需要插入注释。</font></font></li>
+</ul>
+
+<p><font><font>就是这样了!</font><font>你的例子现在应该有效。</font></font></p>
+
+<p><font><font>如果您遇到问题,请随时</font></font><a href="https://mdn.github.io/learning-area/javascript/apis/client-side-storage/indexeddb/notes/" rel="noopener"><font><font>查看我们的实例</font></font></a><font><font>(请参阅</font></font><a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/client-side-storage/indexeddb/notes/index.js" rel="noopener"><font><font>源代码</font></font></a><font><font>)。</font></font></p>
+
+<h3 id="通过_IndexedDB_存储复杂数据">通过 IndexedDB 存储复杂数据</h3>
+
+<p><font><font>如上所述,IndexedDB可用于存储不仅仅是简单的文本字符串。</font><font>您可以存储任何您想要的东西,包括复杂的对象,如视频或图像blob。</font><font>并且它比任何其他类型的数据更难实现。</font></font></p>
+
+<p><font><font>为了演示如何操作,我们编写了另一个名为</font></font><a href="https://github.com/mdn/learning-area/tree/master/javascript/apis/client-side-storage/indexeddb/video-store" rel="noopener"><font><font>IndexedDB视频存储的</font></font></a><font><font>示例</font><font>(请参阅</font></font><a href="https://mdn.github.io/learning-area/javascript/apis/client-side-storage/indexeddb/video-store/" rel="noopener"><font><font>此处也可以在此处运行</font></font></a><font><font>)。</font><font>首次运行示例时,它会从网络下载所有视频,将它们存储在IndexedDB数据库中,然后在UI内部</font></font><a href="https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/video" title="HTML &lt;video> 元素 用于在HTML或者XHTML文档中嵌入视频内容。"><code>&lt;video&gt;</code></a><font><font>元素中</font><font>显示视频</font><font>。</font><font>第二次运行它时,它会在数据库中找到视频并从那里获取它们而不是显示它们 - 这使得后续加载更快,占用空间更少。</font></font></p>
+
+<p><font><font>让我们来看看这个例子中最有趣的部分。</font><font>我们不会全部看 - 它的很多内容与上一个示例类似,代码注释得很好。</font></font></p>
+
+<ol>
+ <li>
+ <p>对于这个简单的例子,我们已经存储了视频的名称以获取数组opf对象:</p>
+
+ <pre class="brush: js notranslate">const videos = [
+ { 'name' : 'crystal' },
+ { 'name' : 'elf' },
+ { 'name' : 'frog' },
+ { 'name' : 'monster' },
+ { 'name' : 'pig' },
+ { 'name' : 'rabbit' }
+];</pre>
+ </li>
+ <li>
+ <p><font><font>首先,一旦数据库成功打开,我们就运行一个</font></font><code>init()</code><font><font>函数。</font><font>这会遍历不同的视频名称,尝试加载由</font></font><code>videos</code><font><font>数据库中的</font><font>每个名称标识的记录</font><font>。</font></font></p>
+
+ <p><font><font>如果在数据库中找到每个视频(通过查看</font></font><code>request.result</code><font><font>评估</font><font>是否容易检查</font></font><code>true</code><font><font>- 如果记录不存在,那么</font></font><code>undefined</code><font><font>),视频文件(存储为blob)和视频名称将直接传递给</font></font><code>displayVideo()</code><font><font>函数以放置它们在用户界面中。</font><font>如果没有,视频名称将传递给</font></font><code>fetchVideoFromNetwork()</code><font><font>函数...你猜对了 - 从网络中获取视频。</font></font></p>
+
+ <pre class="brush: js notranslate">function init() {
+ // Loop through the video names one by one
+ for(let i = 0; i &lt; videos.length; i++) {
+ // Open transaction, get object store, and get() each video by name
+ let objectStore = db.transaction('videos').objectStore('videos');
+ let request = objectStore.get(videos[i].name);
+ request.onsuccess = function() {
+ // If the result exists in the database (is not undefined)
+ if(request.result) {
+ // Grab the videos from IDB and display them using displayVideo()
+ console.log('taking videos from IDB');
+ displayVideo(request.result.mp4, request.result.webm, request.result.name);
+ } else {
+ // Fetch the videos from the network
+ fetchVideoFromNetwork(videos[i]);
+ }
+ };
+ }
+}</pre>
+ </li>
+ <li>
+ <p><font><font>以下片段是从内部</font></font><code>fetchVideoFromNetwork()</code><font><font>获取的 - 这里我们使用两个单独的</font></font><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch" rel="nofollow" title="此页面仍未被本地化, 期待您的翻译!"><code>WindowOrWorkerGlobalScope.fetch()</code></a><font><font>请求</font><font>获取视频的MP4和WebM版本</font><font>。</font><font>然后,我们使用该</font></font><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/Blob" title="Blob 对象表示一个不可变、原始数据的类文件对象。Blob 表示的不一定是JavaScript原生格式的数据。File 接口基于Blob,继承了 blob 的功能并将其扩展使其支持用户系统上的文件。"><code>Body.blob()</code></a><font><font>方法将每个响应的主体提取为blob,为我们提供可以在以后存储和显示的视频的对象表示。</font></font></p>
+
+ <p><font><font>我们在这里遇到了一个问题 - 这两个请求都是异步的,但我们只想在两个promises都满足时尝试显示或存储视频。</font><font>幸运的是,有一种处理这种问题的内置方法 - </font></font><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/all" title="Promise.all(iterable) 方法返回一个 Promise 实例,此实例在 iterable 参数内所有的 promise 都“完成(resolved)”或参数中不包含 promise 时回调完成(resolve);如果参数中  promise 有一个失败(rejected),此实例回调失败(reject),失败原因的是第一个失败 promise 的结果。"><code>Promise.all()</code></a><font><font>。</font><font>这需要一个参数 - 引用您要检查放置在数组中的履行的所有单个承诺 - 并且本身是基于承诺的。</font></font></p>
+
+ <p><font><font>当所有这些承诺都履行完毕时,</font></font><code>all()</code><font><font>承诺将通过包含所有个人履行价值的数组来实现。</font><font>在</font></font><code>all()</code><font><font>块中,您可以看到我们</font></font><code>displayVideo()</code><font><font>之前</font><font>调用</font><font>函数,就像在UI中显示视频一样,然后我们也调用</font></font><code>storeVideo()</code><font><font>函数将这些视频存储在数据库中。</font></font></p>
+
+ <pre class="brush: js notranslate">let mp4Blob = fetch('videos/' + video.name + '.mp4').then(response =&gt;
+ response.blob()
+);
+let webmBlob = fetch('videos/' + video.name + '.webm').then(response =&gt;
+ response.blob()
+);;
+
+// Only run the next code when both promises have fulfilled
+Promise.all([mp4Blob, webmBlob]).then(function(values) {
+ // display the video fetched from the network with displayVideo()
+ displayVideo(values[0], values[1], video.name);
+ // store it in the IDB using storeVideo()
+ storeVideo(values[0], values[1], video.name);
+});</pre>
+ </li>
+ <li>
+ <p><font><font>我们</font></font><code>storeVideo()</code><font><font>先</font><font>来看看吧</font><font>。</font><font>这与您在上一个示例中看到的用于向数据库添加数据的模式非常相似 - 我们打开一个</font></font><code>readwrite</code><font><font>事务并获取对象存储引用</font></font><code>videos</code><font><font>,创建一个表示要添加到数据库的记录的对象,然后使用它添加它</font></font><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/IDBObjectStore/add" rel="nofollow" title="此页面仍未被本地化, 期待您的翻译!"><code>IDBObjectStore.add()</code></a><font><font>。</font></font></p>
+
+ <pre class="brush: js notranslate">function storeVideo(mp4Blob, webmBlob, name) {
+ // Open transaction, get object store; make it a readwrite so we can write to the IDB
+ let objectStore = db.transaction(['videos'], 'readwrite').objectStore('videos');
+ // Create a record to add to the IDB
+ let record = {
+ mp4 : mp4Blob,
+ webm : webmBlob,
+ name : name
+ }
+
+ // Add the record to the IDB using add()
+ let request = objectStore.add(record);
+
+ ...
+
+};</pre>
+ </li>
+ <li>
+ <p><font><font>最后但并非最不重要的是,我们</font></font><code>displayVideo()</code><font><font>创建了在UI中插入视频然后将它们附加到页面所需的DOM元素。</font><font>最有趣的部分如下所示 - 要在</font></font><code>&lt;video&gt;</code><font><font>元素中</font><font>实际显示我们的视频blob </font><font>,我们需要使用该</font></font><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/URL/createObjectURL" title="URL.createObjectURL() 静态方法会创建一个 DOMString,其中包含一个表示参数中给出的对象的URL。这个 URL 的生命周期和创建它的窗口中的 document 绑定。这个新的URL 对象表示指定的 File 对象或 Blob 对象。"><code>URL.createObjectURL()</code></a><font><font>方法</font><font>创建对象URL(指向存储在内存中的视频blob的内部URL)</font><font>。</font><font>完成后,我们可以将对象URL设置为</font></font><a href="https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/source" title="HTML &lt;source>元素为&lt;picture>,&lt;audio>或&lt;video>元素指定多个媒体资源。 这是一个空元素。 它通常用于以不同浏览器支持的多种格式提供相同的媒体内容。"><code>&lt;source&gt;</code></a><font><font>元素</font></font><code>src</code><font><font>属性的值,并且它可以正常工作。</font></font></p>
+
+ <pre class="brush: js notranslate">function displayVideo(mp4Blob, webmBlob, title) {
+ // Create object URLs out of the blobs
+ let mp4URL = URL.createObjectURL(mp4Blob);
+ let webmURL = URL.createObjectURL(webmBlob);
+
+ ...
+
+ let video = document.createElement('video');
+ video.controls = true;
+ let source1 = document.createElement('source');
+ source1.src = mp4URL;
+ source1.type = 'video/mp4';
+ let source2 = document.createElement('source');
+ source2.src = webmURL;
+ source2.type = 'video/webm';
+
+ ...
+}</pre>
+ </li>
+</ol>
+
+<h2 id="离线文件存储">离线文件存储</h2>
+
+<p><font>上面的示例已经说明了如何创建一个将大型资产存储在IndexedDB数据库中的应用程序,从而无需多次下载它们。</font><font>这已经是对用户体验的一个很大的改进,但仍然有一件事 - 每次访问网站时仍然需要下载主要的HTML,CSS和JavaScript文件,这意味着当没有网络连接时,它将无法工作。</font></p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/15759/ff-offline.png" style="border-style: solid; border-width: 1px; display: block; height: 307px; margin: 0px auto; width: 765px;"></p>
+
+<p><font><font>这就是</font></font><a href="https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API"><font><font>服务工作者</font></font></a><font><font>和密切相关的</font></font><a href="https://developer.mozilla.org/en-US/docs/Web/API/Cache"><font><font>Cache API的</font></font></a><font><font>用武之地。</font></font></p>
+
+<p><font><font>服务工作者是一个JavaScript文件,简单地说,它是在浏览器访问时针对特定来源(网站或某个域的网站的一部分)进行注册的。</font><font>注册后,它可以控制该来源的可用页面。</font><font>它通过坐在加载的页面和网络之间以及拦截针对该来源的网络请求来实现这一点。</font></font></p>
+
+<p><font><font>当它拦截一个请求时,它可以做任何你想做的事情(参见</font></font><a href="https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API#Other_use_case_ideas"><font><font>用例思路</font></font></a><font><font>),但经典的例子是离线保存网络响应,然后提供响应请求而不是来自网络的响应。</font><font>实际上,它允许您使网站完全脱机工作。</font></font></p>
+
+<p><font><font>Cache API是另一种客户端存储机制,略有不同 - 它旨在保存HTTP响应,因此与服务工作者一起工作得非常好。</font></font></p>
+
+<div class="note">
+<p><strong><font><font>注意</font></font></strong><font><font>:现在大多数现代浏览器都支持服务工作者和缓存。</font><font>在撰写本文时,Safari仍在忙着实施它,但它应该很快就会存在。</font></font></p>
+</div>
+
+<h3 id="一个_service_worker_例子">一个 service worker 例子</h3>
+
+<p><font><font>让我们看一个例子,让你对这可能是什么样子有所了解。</font><font>我们已经创建了另一个版本的视频存储示例,我们在上一节中看到了 - 这个功能完全相同,只是它还通过服务工作者将Cache,CSS和JavaScript保存在Cache API中,允许示例脱机运行!</font></font></p>
+
+<p><font><font>请参阅</font></font><a href="https://mdn.github.io/learning-area/javascript/apis/client-side-storage/cache-sw/video-store-offline/" rel="noopener"><font><font>IndexedDB视频存储,其中服务工作者正在运行</font></font></a><font><font>,并且还可以</font></font><a href="https://github.com/mdn/learning-area/tree/master/javascript/apis/client-side-storage/cache-sw/video-store-offline" rel="noopener"><font><font>查看源代码</font></font></a><font><font>。</font></font></p>
+
+<h4 id="注册服务工作者"><font><font>注册服务工作者</font></font></h4>
+
+<p><font><font>首先要注意的是,在主JavaScript文件中放置了一些额外的代码(请参阅</font></font><a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/client-side-storage/cache-sw/video-store-offline/index.js" rel="noopener"><font><font>index.js</font></font></a><font><font>)。</font><font>首先,我们进行特征检测测试,以查看</font></font><code>serviceWorker</code><font><font>该</font></font><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/Navigator" title="Navigator 接口表示用户代理的状态和标识。 它允许脚本查询它和注册自己进行一些活动。"><code>Navigator</code></a><font><font>对象中</font><font>是否有该</font><font>成员</font><font>。</font><font>如果返回true,那么我们知道至少支持服务工作者的基础知识。</font><font>在这里,我们使用该</font></font><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/ServiceWorkerContainer/register" title="ServiceWorkerContainer 接口的 register() 方法创建或更新一个给定scriptURL的ServiceWorkerRegistration 。"><code>ServiceWorkerContainer.register()</code></a><font><font>方法将</font></font><code>sw.js</code><font><font>文件中</font><font>包含的服务工作者注册到</font><font>它所驻留的源,因此它可以控制与它或子目录相同的目录中的页面。</font><font>当其承诺履行时,服务人员被视为已注册。</font></font></p>
+
+<pre class="brush: js notranslate"> // Register service worker to control making site work offline
+
+ if('serviceWorker' in navigator) {
+ navigator.serviceWorker
+ .register('/learning-area/javascript/apis/client-side-storage/cache-sw/video-store-offline/sw.js')
+ .then(function() { console.log('Service Worker Registered'); });
+ }</pre>
+
+<div class="note">
+<p><strong><font><font>注意</font></font></strong><font><font>:</font></font><code>sw.js</code><font><font>文件</font><font>的给定路径</font><font>是相对于站点源的,而不是包含代码的JavaScript文件。</font><font>服务人员在</font></font><code>https://mdn.github.io/learning-area/javascript/apis/client-side-storage/cache-sw/video-store-offline/sw.js</code><font><font>。</font><font>原点是</font></font><code>https://mdn.github.io</code><font><font>,因此给定的路径必须是</font></font><code>/learning-area/javascript/apis/client-side-storage/cache-sw/video-store-offline/sw.js</code><font><font>。</font><font>如果您想在自己的服务器上托管此示例,则必须相应地更改此示例。</font><font>这是相当令人困惑的,但出于安全原因,它必须以这种方式工作。</font></font></p>
+</div>
+
+<h4 id="安装_service_worker">安装 service worker</h4>
+
+<p><font><font>下次访问服务工作者控制下的任何页面时(例如,重新加载示例时),将针对该页面安装服务工作者,这意味着它将开始控制它。</font><font>发生这种情况时,</font></font><code>install</code><font><font>会向服务工作人员发起</font><font>一个</font><font>事件; </font><font>您可以在服务工作者本身内编写代码来响应安装。</font></font></p>
+
+<p><font><font>让我们看一下</font></font><a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/client-side-storage/cache-sw/video-store-offline/sw.js" rel="noopener"><font><font>sw.js</font></font></a><font><font>文件(服务工作者)</font><font>中的一个例子</font><font>。</font><font>您将看到安装侦听器已注册</font></font><code>self</code><font><font>。</font><font>此</font></font><code>self</code><font><font>关键字是一种从服务工作文件内部引用服务工作者的全局范围的方法。</font></font></p>
+
+<p><font><font>在</font></font><code>install</code><font><font> 处理程序</font><font>内部,  </font><font>我们使用</font></font><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/ExtendableEvent/waitUntil" title="ExtendableEvent.waitUntil() 方法扩展了事件的生命周期。在服务工作线程中,延长事件的寿命从而阻止浏览器在事件中的异步操作完成之前终止服务工作线程。"><code>ExtendableEvent.waitUntil()</code></a><font><font>事件对象上可用</font><font>的</font><font>方法来表示浏览器不应该完成服务工作者的安装,直到其中的promise成功完成。</font></font></p>
+
+<p><font><font>这是我们在运行中看到Cache API的地方。</font><font>我们使用该</font></font><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/CacheStorage/open" title="CacheStorage open() 方法返回一个resolve为匹配 cacheName 的 Cache 对象的 Promise ."><code>CacheStorage.open()</code></a><font><font>方法打开一个可以存储响应的新缓存对象(类似于IndexedDB对象存储)。</font><font>此承诺通过</font></font><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/Cache" title="Cache 接口为缓存的 Request / Response  对象对提供存储机制,例如,作为ServiceWorker 生命周期的一部分。请注意,Cache 接口像 workers 一样,是暴露在 window 作用域下的。尽管它被定义在 service worker 的标准中,  但是它不必一定要配合 service worker 使用."><code>Cache</code></a><font><font>表示</font></font><code>video-store</code><font><font>缓存</font><font>的</font><font>对象来</font><font>实现</font><font>。</font><font>然后,我们使用该</font></font><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/Cache/addAll" title="Cache 接口的 addAll() 方法接受一个URL数组,检索它们,并将生成的response对象添加到给定的缓存中。 在检索期间创建的request对象成为存储的response操作的key。"><code>Cache.addAll()</code></a><font><font>方法获取一系列资产并将其响应添加到缓存中。</font></font></p>
+
+<pre class="brush: js notranslate">self.addEventListener('install', function(e) {
+ e.waitUntil(
+ caches.open('video-store').then(function(cache) {
+ return cache.addAll([
+ '/learning-area/javascript/apis/client-side-storage/cache-sw/video-store-offline/',
+ '/learning-area/javascript/apis/client-side-storage/cache-sw/video-store-offline/index.html',
+ '/learning-area/javascript/apis/client-side-storage/cache-sw/video-store-offline/index.js',
+ '/learning-area/javascript/apis/client-side-storage/cache-sw/video-store-offline/style.css'
+ ]);
+ })
+ );
+});</pre>
+
+<p>这就是现在,安装完成。</p>
+
+<h4 id="响应未来的请求">响应未来的请求</h4>
+
+<p><font><font>在我们的HTML页面上注册并安装了服务工作者,并且所有相关资产都添加到我们的缓存中,我们几乎准备好了。</font><font>还有一件事要做,写一些代码来响应进一步的网络请求。</font></font></p>
+
+<p><font><font>这就是第二位代码的</font></font><code>sw.js</code><font><font>作用。</font><font>我们向服务工作者全局范围添加另一个侦听器,该范围在</font></font><code>fetch</code><font><font>引发事件</font><font>时运行处理函数</font><font>。</font><font>只要浏览器在服务工作者注册的目录中请求资产,就会发生这种情况。</font></font></p>
+
+<p><font><font>在处理程序内部,我们首先记录所请求资产的URL。</font><font>然后,我们使用该</font></font><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/FetchEvent/respondWith" title="FetchEvent 接口的 respondWith() 方法旨在包裹代码,这些代码为来自受控页面的request生成自定义的response。这些代码通过返回一个 Response 、 network error 或者 Fetch的方式resolve。"><code>FetchEvent.respondWith()</code></a><font><font>方法</font><font>为请求提供自定义响应</font><font>。</font></font></p>
+
+<p><font><font>在这个块中,我们</font></font><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/CacheStorage/match" title="CacheStorage 接口(可适用于全局性caches) 的 match() 方法检查给定的Request 对象或url字符串是否是一个已存储的 Response 对象的key. 这个方法针对 Response 返回一个 Promise ,如果没有匹配则返回 undefined 。"><code>CacheStorage.match()</code></a><font><font>用来检查是否可以在任何缓存中找到匹配的请求(即匹配URL)。</font><font>如果未找到匹配,或者</font></font><code>undefined</code><font><font>如果</font><font>未找到匹配,则此承诺将满足匹配的响应</font><font>。</font></font></p>
+
+<p><font><font>如果找到匹配项,我们只需将其作为自定义响应返回。</font><font>如果没有,我们</font><font>从网络中</font></font><a href="https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch"><font><font>获取()</font></font></a><font><font>响应并返回该响应。</font></font></p>
+
+<pre class="brush: js notranslate">self.addEventListener('fetch', function(e) {
+ console.log(e.request.url);
+ e.respondWith(
+ caches.match(e.request).then(function(response) {
+ return response || fetch(e.request);
+ })
+ );
+});</pre>
+
+<p><font><font>这就是我们简单的服务工作者。</font><font>您可以使用它们进行更多的负载 - 有关详细信息,请参阅</font></font><a href="https://serviceworke.rs/" rel="noopener"><font><font>服务工作者手册</font></font></a><font><font>。</font><font>感谢Paul Kinlan在他的文章中</font></font><a href="https://developers.google.com/web/fundamentals/codelabs/offline/" rel="noopener"><font><font>添加服务工作者和离线到您的Web应用程序</font></font></a><font><font>,这启发了这个简单的例子。</font></font></p>
+
+<h4 id="测试离线示例">测试离线示例</h4>
+
+<p><font><font>要测试我们的</font></font><a href="https://mdn.github.io/learning-area/javascript/apis/client-side-storage/cache-sw/video-store-offline/" rel="noopener"><font><font>服务工作者示例</font></font></a><font><font>,您需要加载它几次以确保它已安装。</font><font>完成后,您可以:</font></font></p>
+
+<ul>
+ <li><font><font>尝试拔掉网络连接/关闭Wifi。</font></font></li>
+ <li><font><font>如果您使用的是Firefox,请</font><font>选择</font></font><em><font><font>文件&gt;脱机工作</font></font></em><font><font>。</font></font></li>
+ <li><font><font>转到devtools,然后选择</font></font><em><font><font>Application&gt; Service Workers</font></font></em><font><font>,</font><font>如果您使用的是Chrome </font><font>,请选中</font></font><em><font><font>Offline</font></font></em><font><font>选中。</font></font></li>
+</ul>
+
+<p><font><font>如果再次刷新示例页面,您仍应该看到它加载得很好。</font><font>所有内容都是脱机存储的 - 缓存中的页面资源以及IndexedDB数据库中的视频。</font></font></p>
+
+<h2 id="总结">总结</h2>
+
+<p><font>现在就是这些。</font><font>我们希望你能感觉到我们对客户端存储技术的介绍很有用。</font></p>
+
+<h2 id="相关链接">相关链接</h2>
+
+<ul>
+ <li><a href="/en-US/docs/Web/API/Web_Storage_API">网页存储 API</a></li>
+ <li><a href="/en-US/docs/Web/API/IndexedDB_API">IndexedDB API</a></li>
+ <li><a href="/en-US/docs/Web/HTTP/Cookies">Cookies</a></li>
+ <li><a href="/en-US/docs/Web/API/Service_Worker_API">Service worker API</a></li>
+</ul>
+
+<p>{{PreviousMenu("Learn/JavaScript/Client-side_web_APIs/Video_and_audio_APIs", "Learn/JavaScript/Client-side_web_APIs")}}</p>
+
+<h2 id="在本单元中">在本单元中</h2>
+
+<ul>
+ <li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Introduction">网页端 API介绍 </a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Manipulating_documents">Manipulating documents</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Fetching_data">从服务器获取数据(fetch)</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Third_party_APIs">第三方 API</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Drawing_graphics">Drawing graphics</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Video_and_audio_APIs">视频和音频 API</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Client-side_storage">客户端存储</a></li>
+</ul>
diff --git a/files/zh-cn/learn/javascript/client-side_web_apis/drawing_graphics/index.html b/files/zh-cn/learn/javascript/client-side_web_apis/drawing_graphics/index.html
new file mode 100644
index 0000000000..e39694743d
--- /dev/null
+++ b/files/zh-cn/learn/javascript/client-side_web_apis/drawing_graphics/index.html
@@ -0,0 +1,881 @@
+---
+title: 绘图
+slug: Learn/JavaScript/Client-side_web_APIs/Drawing_graphics
+tags:
+ - API
+ - Canvas
+ - CodingScripting
+ - JavaScript
+ - WebGL
+ - 初学者
+ - 图形
+ - 学习
+ - 文章
+translation_of: Learn/JavaScript/Client-side_web_APIs/Drawing_graphics
+---
+<div>{{LearnSidebar}}{{PreviousMenuNext("Learn/JavaScript/Client-side_web_APIs/Third_party_APIs", "Learn/JavaScript/Client-side_web_APIs/Video_and_audio_APIs", "Learn/JavaScript/Client-side_web_APIs")}}</div>
+
+<p class="summary">浏览器包含一些非常强大的图形编程工具,从可缩放矢量图形(Scalable Vector Graphics,简称 <a href="https://developer.mozilla.org/zh-CN/docs/Web/SVG">SVG</a>)语言到用于在 HTML {{htmlelement("canvas")}} 元素上绘制图形的API(参阅 <a href="/zh-CN/docs/Web/API/Canvas_API">Canvas API</a> 和 <a href="/zh-CN/docs/Web/API/WebGL_API">WebGL</a>)。本文对 {{htmlelement("canvas")}} 进行介绍,并提供更多的学习资源。</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">预备知识:</th>
+ <td>JavaScript基础(见 <a href="/zh-CN/docs/Learn/JavaScript/First_steps">JavaScript 第一步</a>,<a href="/zh-CN/docs/learn/JavaScript/Building_blocks">创建 JavaScript 代码块</a>,<a href="/zh-CN/docs/Learn/JavaScript/Objects">JavaScript 对象入门</a>),<a href="/zh-CN/docs/Learn/JavaScript/Client-side_web_APIs/Introduction">Web API 基础知识</a>。</td>
+ </tr>
+ <tr>
+ <th scope="row">目标:</th>
+ <td>学习 JavaScript 在 <code>&lt;canvas&gt;</code> 元素中绘图的基础知识。</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="网络图形">网络图形</h2>
+
+<p>我们来讨论 HTML 的 <a href="/zh-CN/docs/Learn/HTML/Multimedia_and_embedding">多媒体和嵌入式</a> 模块,早先的网页只有单调的文字,后来才引入了图像,起初是通过 {{htmlelement("img")}} 元素的方式,后来出现了类似于 {{cssxref("background-image")}} 的 CSS 属性和 <a href="/zh-CN/docs/Web/SVG">SVG</a> 图像等方式。</p>
+
+<p>然而,这还不够好。当你能够使用 <a href="/zh-CN/docs/Learn/CSS">CSS</a> 和 <a href="/zh-CN/docs/Learn/JavaScript">JavaScript </a>让 SVG 矢量图动起来时,位图却依然没有相应的支持。同时 SVG 动画的可用工具也少得可怜。有效地生成动画、游戏画面、3D场景和其他的需求依然没有满足,而这些在诸如 C++ 或者 Java 等底层语言中却司空见惯。</p>
+
+<p>当浏览器开始支持 HTML 画布元素 {{htmlelement("canvas")}} 和相关的 <a href="/zh-CN/docs/Web/API/Canvas_API">Canvas API</a>(由苹果公司在 2004 年前后发明,后来其他的浏览器开始跟进)时,形势开始改善。下面你会看到,canvas 提供了许多有用的工具,特别是当捆绑了由网络平台提供的一些其他的 API 时。它们用来生成 2D 动画、游戏画面和数据分析图,以及其他类型的 app。</p>
+
+<p>下面的例子显示的是一个基于 canvas 的简单的 2D 弹跳球动画,前面我们在 <a href="/zh-CN/docs/Learn/JavaScript/Objects/Object_building_practice">JavaScript 对象入门 </a>的章节中见到过。</p>
+
+<p>{{EmbedGHLiveSample("learning-area/javascript/oojs/bouncing-balls/index-finished.html", '100%', 500)}}</p>
+
+<p>大约在 2006 - 2007 年,Mozilla 开始测试 3D 画布。后来演化为 <a href="/zh-CN/docs/Web/API/WebGL_API">WebGL</a>,它获得了各大浏览器厂商的认可,于是大约在 2009 - 2010 年间得到了标准化。WebGL 可以让你在 web 浏览器中生成真正的 3D 图形。下面的例子显示了一个简单的旋转的 WebGL 立方体。</p>
+
+<p>{{EmbedGHLiveSample("learning-area/javascript/apis/drawing-graphics/threejs-cube/index.html", '100%', 500)}}</p>
+
+<p>由于原生的 WebGL 代码非常复杂,本文主要针对 2D 画布。然而,你也可以通过 <a href="/zh-CN/docs/Web/API/WebGL_API">WebGL介绍页面</a> 找到 WebGL 原生代码的教程,来学习如何更容易地使用 WebGL 库来创建一个 3D 场景。</p>
+
+<div class="note">
+<p><strong>注:</strong>画布的基本功能有良好的跨浏览器支持。但存在例外:IE 8 及以下不支持 2D 画布,IE 11 及以下不支持WebGL。</p>
+</div>
+
+<h2 id="主动学习:开始使用_&lt;canvas>">主动学习:开始使用 &lt;canvas&gt;</h2>
+
+<p>要在网页中创建 2D 或者 3D 场景,必须在 HTML 文件中插入一个 {{htmlelement("canvas")}} 元素,以界定网页中的绘图区域。这很简单,如下所示:</p>
+
+<pre class="brush: html notranslate">&lt;canvas width="320" height="240"&gt;&lt;/canvas&gt;</pre>
+
+<p>网页中会生成一块 320 × 240 像素的画布。</p>
+
+<p>在 canvas 标签内,你可以放置一些反馈信息,如果用户的浏览器不支持画布功能,这些内容就会显示出来。</p>
+
+<pre class="brush: html notranslate">&lt;canvas width="320" height="240"&gt;
+ &lt;p&gt;卧槽你的浏览器竟然不支持 canvas!&lt;/p&gt;
+&lt;/canvas&gt;</pre>
+
+<p>当然这条信息实在是没什么用!在实际情况中最好提供与画布内容相关的反馈信息。比如,如果无法渲染实时更新的股价曲线图,就应提供一幅静态的、最近的股价曲线图,并用 alt 显示出价格信息。</p>
+
+<h3 id="创建画布并确定尺寸">创建画布并确定尺寸</h3>
+
+<p>让我们开始吧:创建画布,准备尝试绘制图形。</p>
+
+<ol>
+ <li>
+ <p>首先下载 <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/drawing-graphics/getting-started/0_canvas_start.html">0_canvas_start.html</a> 文件, 用文本编辑器打开。</p>
+ </li>
+ <li>
+ <p>在 {{htmlelement("body")}} 标签下面填加以下代码。</p>
+
+ <pre class="brush: html notranslate">&lt;canvas class="myCanvas"&gt;
+ &lt;p&gt;添加恰当的反馈信息。&lt;/p&gt;
+&lt;/canvas&gt;</pre>
+
+ <p>我们为 <code>&lt;canvas&gt;</code> 元素添加了一个 <code>class</code>,使得在网页中选择多个画布时会容易些。这里我们移除了 <code>width</code> 和 <code>height</code> 属性 (你可以随时添上。但是我们会在下方用 JavaScript 把它们添加回来)。不明确指定宽高时,画布默认尺寸为 300 × 150 像素。</p>
+ </li>
+ <li>
+ <p>现在,请在 {{htmlelement("script")}} 元素内添加以下 JavaScript 代码:</p>
+
+ <pre class="brush: js notranslate">var canvas = document.querySelector('.myCanvas');
+var width = canvas.width = window.innerWidth;
+var height = canvas.height = window.innerHeight;</pre>
+
+ <p>这里我们用 <code>canvas</code> 变量来存储画布的引用。第二行中我们将  {{domxref("Window.innerWidth")}}(可视区域宽度)赋值给一个新变量 <code>width</code> 和画布的 <code>canvas.width</code> 变量,(第三行同理)。然后我们就得到了一个充满窗口的画布。<br>
+ <br>
+ 你还可以看到我们使用了多个等号为多个变量进行连续赋值,这在 JavaScript 中是允许的,很适合为多个变量同时赋一个相同的值。后文中你会发现,使用 <code>width</code> 和 <code>height</code> 变量可以更快捷地访问画布的长宽(比如在画布正中央绘制一条垂直线)。 </p>
+ </li>
+ <li>
+ <p>如果现在保存文件,浏览器中什么也不会显示,这并没有问题,但是滚动条还是可见的,这就是问题了。原因是我们的“全窗尺寸画布”包含 {{htmlelement("body")}} 元素的外边距({{cssxref("margin")}}),使得文档比窗口略宽。 为使滚动条消失,需要删除 {{htmlelement("body")}} 元素的 {{cssxref("margin")}} 并将 {{cssxref("overflow")}} 设置为 <code>hidden</code>。在文档的 {{htmlelement("head")}} 中添加以下代码即可:</p>
+
+ <pre class="brush: html notranslate">&lt;style&gt;
+ body {
+ margin: 0;
+ overflow: hidden;
+ }
+&lt;/style&gt;</pre>
+
+ <p>此时滚动条就消失了。</p>
+ </li>
+</ol>
+
+<div class="note">
+<p><strong>注:</strong>如上文所讲,一般情况下图片的尺寸可以通过 HTML 属性或 DOM 属性来设定。你也可以使用 CSS,但问题是画布在渲染完毕后其尺寸就是固定的了,如果试图调整,就会与其他图象一样(其实渲染好的画布就是一副图片),所显示的内容将变得像素化或扭曲变形。</p>
+</div>
+
+<h3 id="获取画布上下文(canvas_context)并完成设置">获取画布上下文(canvas context)并完成设置</h3>
+
+<p>画布模板设置还有最后一步。我们需要获得一个对绘画区域的特殊的引用(称为“上下文”)用来在画布上绘图。可通过 {{domxref("HTMLCanvasElement.getContext()")}} 方法获得基础的绘画功能,需要提供一个字符串参数来表示所需上下文的类型。</p>
+
+<p>这里我们需要一个 2d 画布,在 <code>&lt;script&gt;</code> 元素内添加以下 JS 代码即可:</p>
+
+<pre class="brush: js notranslate">var ctx = canvas.getContext('2d');</pre>
+
+<div class="note">
+<p><strong>注:</strong>可选上下文还包括 WebGL(<code>webgl</code>)、WebGL 2(<code>webgl2</code>)等等,但本文暂不涉及。</p>
+</div>
+
+<p>好啦,现已万事具备!<code>ctx</code> 变量包含一个 {{domxref("CanvasRenderingContext2D")}} 对象,画布上所有绘画操作都会涉及到这个对象。</p>
+
+<p>开始前我们先初尝一下 canvas API。在 JS 代码中添加以下两行,将画布背景涂成黑色:</p>
+
+<pre class="brush: js notranslate">ctx.fillStyle = 'rgb(0, 0, 0)';
+ctx.fillRect(0, 0, width, height);</pre>
+
+<p>这里我们使用画布的 {{domxref("CanvasRenderingContext2D.fillStyle", "fillStyle")}} 属性(和CSS属性 <a href="/zh-CN/docs/Learn/CSS/Introduction_to_CSS/Values_and_units#Colors">色值</a> 一致)设置填充色,然后使用 {{domxref("CanvasRenderingContext2D.fillRect", "fillRect")}} 方法绘制一个覆盖整个区域的矩形(前两个参数是矩形左上顶点的坐标,后两个参数是矩形的长宽,现在你知道 <code>width</code> 和 <code>height</code> 的作用了吧)。</p>
+
+<p>好的,模板已经就位,我们要开始了。</p>
+
+<h2 id="2D_画布基础">2D 画布基础</h2>
+
+<p>如上文所讲,所有绘画操作都离不开 {{domxref("CanvasRenderingContext2D")}} 对象(这里叫做 <code>ctx</code>)。许多操作都需要提供坐标来指示绘图的确切位置。画布左上角的坐标是(0, 0),横坐标(x)轴向右延伸,纵坐标(y)轴向下延伸。</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/224/Canvas_default_grid.png" style="display: block; margin: 0 auto;"></p>
+
+<p>绘图操作可基于原始矩形模型实现,也可通过追踪一个特定路径后填充颜色实现。下面分别讲解。</p>
+
+<h3 id="简单矩形">简单矩形</h3>
+
+<p>让我们从简单矩形开始。</p>
+
+<ol>
+ <li>
+ <p>首先,复制一份刚才创建的画布模板 (如果你没有严格按上述步骤进行,请下载 <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/drawing-graphics/getting-started/1_canvas_template.html">1_canvas_template.html</a>)。</p>
+ </li>
+ <li>
+ <p>然后在 JS 代码末尾添加下面两行:</p>
+
+ <pre class="brush: js notranslate">ctx.fillStyle = 'rgb(255, 0, 0)';
+ctx.fillRect(50, 50, 100, 150);</pre>
+
+ <p>保存并刷新,画布上将出现一个红色的矩形。其左边和顶边与画布边缘距离均为 50 像素(由前两个参数指定),宽 100 像素、高 150 像素(由后两个参数指定)。</p>
+ </li>
+ <li>
+ <p>然后再添加一个绿色矩形。在 JS 代码末尾添加下面两行:</p>
+
+ <pre class="brush: js notranslate">ctx.fillStyle = 'rgb(0, 255, 0)';
+ctx.fillRect(75, 75, 100, 100);</pre>
+
+ <p>保存并刷新,新的矩形就会出现。这里引出了一个新问题:绘制矩形、线等操作按出现的顺序依次进行。就像粉刷墙面时,两层重叠时新层总会覆盖旧层。这一点是无法改变的,因此在绘制图形时一定要慎重考虑顺序问题。</p>
+ </li>
+ <li>
+ <p>你还可以通过指定半透明的颜色来绘制半透明的图形,比如使用 <code>rgba()</code>。 <code>a</code> 指定了“α 通道”的值,也就是颜色的透明度。值越高透明度越高,底层的内容就越清晰。在代码中添加以下两行:</p>
+
+ <pre class="brush: js notranslate"><code>ctx.fillStyle = 'rgba(255, 0, 255, 0.75)';
+ctx.fillRect(25, 100, 175, 50);</code></pre>
+ </li>
+ <li>
+ <p>现在你可以自己尝试绘制一些矩形了,玩得开心!</p>
+ </li>
+</ol>
+
+<h3 id="描边(stroke)和线条宽度">描边(stroke)和线条宽度</h3>
+
+<p>目前我们绘制的矩形都是填充颜色的,我们也可以绘制仅包含外部框线(图形设计中称为<strong>描边</strong>)的矩形。你可以使用 {{domxref("CanvasRenderingContext2D.strokeStyle", "strokeStyle")}} 属性来设置描边颜色,使用  {{domxref("CanvasRenderingContext2D.strokeRect", "strokeRect")}} 来绘制一个矩形的轮廓。</p>
+
+<ol>
+ <li>
+ <p>在上文的 JS 代码的末尾添加以下代码:</p>
+
+ <pre class="brush: js notranslate">ctx.strokeStyle = 'rgb(255, 255, 255)';
+ctx.strokeRect(25, 25, 175, 200);</pre>
+ </li>
+ <li>
+ <p>默认的描边宽度是 1 像素,可以通过调整 {{domxref("CanvasRenderingContext2D.lineWidth", "lineWidth")}} 属性(接受一个表示描边宽度像素值的数字)的值来修改。在上文两行后添加以下代码(读者注:此代码要放到两行代码中间才有效):</p>
+
+ <pre class="brush: js notranslate">ctx.lineWidth = 5;</pre>
+ </li>
+</ol>
+
+<p>现在可以看到白色的外边框变得更粗了。就这么简单,示例看上去像这样:</p>
+
+<p>{{EmbedGHLiveSample("learning-area/javascript/apis/drawing-graphics/getting-started/2_canvas_rectangles.html", '100%', 250)}}</p>
+
+<div class="note">
+<p><strong>注</strong>: 完整代码请访问 GitHub: <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/drawing-graphics/getting-started/2_canvas_rectangles.html">2_canvas_rectangles.html</a>。</p>
+</div>
+
+<h3 id="绘制路径">绘制路径</h3>
+
+<p>可以通过绘制路径来绘制比矩形更复杂的图形。路径中至少要包含钢笔运行精确路径的代码以确定图形的形状。画布提供了许多函数用来绘制直线、圆、贝塞尔曲线,等等。</p>
+
+<p>重新复制一份(<a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/drawing-graphics/getting-started/1_canvas_template.html">1_canvas_template.html</a>),然后在其中绘制新的示例。</p>
+
+<p>一些通用的方法和属性将贯穿以下全部内容:</p>
+
+<ul>
+ <li>{{domxref("CanvasRenderingContext2D.beginPath", "beginPath()")}}:在钢笔当前所在位置开始绘制一条路径。在新的画布中,钢笔起始位置为 (0, 0)。</li>
+ <li>{{domxref("CanvasRenderingContext2D.moveTo", "moveTo()")}}:将钢笔移动至另一个坐标点,不记录、不留痕迹,只将钢笔“跳”至新位置。</li>
+ <li>{{domxref("CanvasRenderingContext2D.fill", "fill()")}}:通过为当前所绘制路径的区域填充颜色来绘制一个新的填充形状。</li>
+ <li>{{domxref("CanvasRenderingContext2D.stroke", "stroke()")}}:通过为当前绘制路径的区域描边,来绘制一个只有边框的形状。</li>
+ <li>路径也可和矩形一样使用 <code>lineWidth</code> 和 <code>fillStyle</code> / <code>strokeStyle</code> 等功能。</li>
+</ul>
+
+<p>以下是一个典型的简单路径绘制选项:</p>
+
+<pre class="brush: js notranslate">ctx.fillStyle = 'rgb(255, 0, 0)';
+ctx.beginPath();
+ctx.moveTo(50, 50);
+// 绘制路径
+ctx.fill();</pre>
+
+<h4 id="画线">画线</h4>
+
+<p>我们来在画布上绘制一个等边三角形。</p>
+
+<ol>
+ <li>
+ <p>首先,在代码底部添加下面的辅助函数。它可以将角度换算为弧度,在为 JavaScript 提供角度值时非常实用,JS 基本上只接受弧度值,而人类更习惯用角度值。</p>
+
+ <pre class="brush: js notranslate">function degToRad(degrees) {
+ return degrees * Math.PI / 180;
+};</pre>
+ </li>
+ <li>
+ <p>然后,在刚才复制好的文件中添加下面的内容,以开始路径的绘制。此处为我们为三角形设置了颜色,准备绘制,然后将钢笔移动至 (50, 50)(没有绘制任何内容)。然后准备在新的坐标开始绘制三角形。</p>
+
+ <pre class="brush: js notranslate">ctx.fillStyle = 'rgb(255, 0, 0)';
+ctx.beginPath();
+ctx.moveTo(50, 50);</pre>
+ </li>
+ <li>
+ <p>接下来在脚本中添加以下代码:</p>
+
+ <pre class="brush: js notranslate">ctx.lineTo(150, 50);
+var triHeight = 50 * Math.tan(degToRad(60));
+ctx.lineTo(100, 50+triHeight);
+ctx.lineTo(50, 50);
+ctx.fill();</pre>
+
+ <p>我们来逐行解释:</p>
+
+ <p>首先绘制一条直线,终点坐标为 (150, 50)。此时路径沿 x 轴向右行走 100 像素。</p>
+
+ <p>然后利用三角函数来计算等边三角形的高。这里我们要绘制的三角形是朝下的。等边三角形每个角均为 60°,为计算高的值,我们可以将三角形从正中心分割为两个直角三角形,每个直角三角形的三个角分别为 90°、60°、30°。对于边:</p>
+
+ <ul>
+ <li>最长的边称为 <strong>斜边</strong>。</li>
+ <li>紧挨 60° 角的边称为 <strong>临边</strong>,显然地,它的长度是刚才绘制的线的一半,即 50 像素。</li>
+ <li>60° 角对面的边称为 <strong>对边</strong>,即三角形的高,需要计算得到。</li>
+ </ul>
+
+ <p><img alt="" src="https://mdn.mozillademos.org/files/16289/%E4%B8%89%E8%A7%92%E5%87%BD%E6%95%B0.png" style="display: block; margin: 0 auto;"></p>
+
+ <p>通过基本三角函数可得:临边长度乘以角的正切等于对边长度。于是可得三角形的高为 <code>50 * Math.tan(degToRad(60))</code>。由于 {{jsxref("Math.tan()")}} 接受数值的单位为弧度,于是我们用刚才的 <code>degToRad()</code> 函数将 60° 换算为弧度。</p>
+ </li>
+ <li>
+ <p>有了三角形的高,我们来绘制另一条线,终点坐标为 <code>(100, 50+triHeight)</code>。X 坐标值很简单,应在刚才绘制的水平线两顶点正中间位置。Y 值应为 50 加上三角形的高,因为高即三角形底边到顶点的距离。</p>
+ </li>
+ <li>
+ <p>下一条线的终点坐标为绘制整个三角形的起点坐标。</p>
+ </li>
+ <li>
+ <p>最后,运行 <code>ctx.fill()</code> 来终止路径,并为图形填充颜色。</p>
+ </li>
+</ol>
+
+<h4 id="画圆">画圆</h4>
+
+<p>下面来看可在画布中绘制圆的方法—— {{domxref("CanvasRenderingContext2D.arc", "arc()")}} ,通过连续的点来绘制整个圆或者弧(arc,即局部的圆)。</p>
+
+<ol>
+ <li>
+ <p>在代码中添加以下几行,以向画布中添加一条弧。</p>
+
+ <pre class="notranslate">ctx.fillStyle = 'rgb(0, 0, 255)';
+ctx.beginPath();
+ctx.arc(150, 106, 50, degToRad(0), degToRad(360), false);
+ctx.fill();</pre>
+
+ <p><code>arc()</code> 函数有六个参数。前两个指定圆心的位置坐标,第三个是圆的半径,第四、五个是绘制弧的起、止角度(给定 0° 和 360° 便能绘制一个完整的圆),第六个是绘制方向(<code>false</code> 是顺时针,<code>true</code> 是逆时针)。</p>
+
+ <div class="note">
+ <p><strong>注:</strong>0° 设定为水平向右。</p>
+ </div>
+ </li>
+ <li>
+ <p>我们再来画一条弧:</p>
+
+ <pre class="notranslate">ctx.fillStyle = 'yellow';
+ctx.beginPath();
+ctx.arc(200, 106, 50, degToRad(-45), degToRad(45), true);
+ctx.lineTo(200, 106);
+ctx.fill();</pre>
+
+ <p>模式基本一样,但有两点不同:</p>
+
+ <ul>
+ <li>将 <code>arc()</code> 的最后一个参数设置为 <code>true</code>,意味着弧将逆时针绘制,也就意味着即使起、止角度分别设置为 -45°、45°,我们还是得到了区域外的一条 270° 的弧。如果把 <code>true</code> 改为<code>false</code> 重新运行,将得到 90° 的弧。</li>
+ <li>在调用 <code>fill()</code> 前,我们绘制了一条终点为圆心的直线。然后我们就渲染出一个惟妙惟肖的吃豆人模型。如果删除这条线(试试呗)再重新运行代码,你只能得到一个起止点间被砍掉一块的圆。这向我们展示了画布的另一个重要事项:如果要填充一个未完成(也就是没有首尾相接)的路径,浏览器将在起、止点件绘制一条直线,然后直接填充。</li>
+ </ul>
+ </li>
+</ol>
+
+<p>示例现在应该跟下图一致:</p>
+
+<p>{{EmbedGHLiveSample("learning-area/javascript/apis/drawing-graphics/getting-started/3_canvas_paths.html", '100%', 200)}}</p>
+
+<div class="note">
+<p><strong>注:</strong>完整代码可到 GitHub 下载:<a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/drawing-graphics/getting-started/3_canvas_paths.html">3_canvas_paths.html</a>。</p>
+</div>
+
+<div class="note">
+<p><strong>注:</strong>请访问我们的 <a href="/zh-CN/docs/Web/API/Canvas_API/Tutorial/Drawing_shapes">用画布绘图</a> 入门课程来学习更多高级的路径绘制功能,比如贝叶斯曲线。</p>
+</div>
+
+<h3 id="文本">文本</h3>
+
+<p>画布可用于绘制文本。我们简要学习一下。首先再次下载一份新的画布模板(<a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/drawing-graphics/getting-started/1_canvas_template.html">1_canvas_template.htm</a>),我们用它来绘制新的示例。</p>
+
+<p>以下两个函数用于绘制文本:</p>
+
+<ul>
+ <li>{{domxref("CanvasRenderingContext2D.fillText", "fillText()")}} :绘制有填充色的文本。</li>
+ <li>{{domxref("CanvasRenderingContext2D.strokeText", "strokeText()")}}:绘制文本外边框(描边)。</li>
+</ul>
+
+<p>这两个函数有三个基本的参数:需要绘制的文字、<strong>文本框</strong>(顾名思义,围绕着需要绘制文字的方框)左上顶点的X、Y坐标。</p>
+
+<p>还有一系列帮助控制文本渲染的属性:比如用于指定字体族、字号的 {{domxref("CanvasRenderingContext2D.font", "font")}},它的值和语法与 CSS 的 {{cssxref("font")}} 属性一致。</p>
+
+<p>在 JS 代码底部添加以下内容:</p>
+
+<pre class="brush: js notranslate">ctx.strokeStyle = 'white';
+ctx.lineWidth = 1;
+ctx.font = '36px arial';
+ctx.strokeText('Canvas text', 50, 50);
+
+ctx.fillStyle = 'red';
+ctx.font = '48px georgia';
+ctx.fillText('Canvas text', 50, 150);</pre>
+
+<p>将绘制两行文字,一行描边文字一行填充颜色的文字。示例最终如下所示:</p>
+
+<p>{{EmbedGHLiveSample("learning-area/javascript/apis/drawing-graphics/getting-started/4_canvas_text.html", '100%', 180)}}</p>
+
+<div class="note">
+<p><strong>注:</strong>完整代码可到 GitHub 下载:<a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/drawing-graphics/getting-started/4_canvas_text.html">4_canvas_text.html</a>.</p>
+</div>
+
+<p>可以自己尝试一下。访问 <a href="/zh-CN/docs/Web/API/Canvas_API/Tutorial/Drawing_text">绘制文本</a> 获得关于画布文本选项的更多信息。</p>
+
+<h3 id="在画布上绘制图片">在画布上绘制图片</h3>
+
+<p>可在画布上渲染外部图片,简单图片文件、视频帧、其他画布内容都可以。这里我们只考虑简单图片文件的情况:</p>
+
+<ol>
+ <li>
+ <p>同上,下载画布模板(<a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/drawing-graphics/getting-started/1_canvas_template.html">1_canvas_template.html</a>)以绘制新的示例。这里还需要在同一目录下保存一个示例图片文件:<a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/drawing-graphics/getting-started/firefox.png">firefox.png</a>。</p>
+
+ <p>{{domxref("CanvasRenderingContext2D.drawImage", "drawImage()")}} 方法可将图片绘制在画布上。 最简单的版本需要三个参数:需要渲染的图片、图片左上角的X、Y坐标。</p>
+ </li>
+ <li>
+ <p>将图片源嵌入画布中,代码如下:</p>
+
+ <pre class="brush: js notranslate">var image = new Image();
+image.src = 'firefox.png';</pre>
+
+ <p>这里使用 {{domxref("HTMLImageElement.Image()", "Image()")}} 构造器创建了一个新的 {{domxref("HTMLImageElement")}} 对象。返回对象的类型与非空 {{htmlelement("img")}} 元素的引用是一致的。然后将它的 {{htmlattrxref("src", "img")}} 属性设置为 Firefox 的图标。此时浏览器将开始载入这张图片。</p>
+ </li>
+ <li>
+ <p>这次我们尝试用 <code>drawImage()</code> 函数来嵌入图片,应确保图片先载入完毕,否则运行会出错。可以通过 <code>onload</code> 事件处理器来达成,该函数只在图片调用完毕后才会调用。在上文代码末尾添加以下内容:</p>
+
+ <pre class="brush: js notranslate">image.onload = function() {
+ ctx.drawImage(image, 50, 50);
+}</pre>
+
+ <p>保存刷新,可以看到图片成功嵌入画布中。</p>
+ </li>
+ <li>
+ <p>还有更多方式。如果仅需要显示图片的某一部分,或者需要改变尺寸,该怎么做呢?复杂版本的 <code>drawImage()</code> 可解决这两个问题。请更新 <code>ctx.drawImage()</code> 一行代码为:</p>
+
+ <pre class="brush: js notranslate">ctx.drawImage(image, 20, 20, 185, 175, 50, 50, 185, 175);</pre>
+
+ <ul>
+ <li>第一个参数不变,为图片引用。</li>
+ <li>参数 2、3 表示裁切部分左上顶点的坐标,参考原点为原图片本身左上角的坐标。原图片在该坐标左、上的部分均不会绘制出来。</li>
+ <li>参数 4、5 表示裁切部分的长、宽。</li>
+ <li>参数 6、7 表示裁切部分左上顶点在画布中的位置坐标,参考原点为画布左上顶点。</li>
+ <li>参数 8、9 表示裁切部分在画布中绘制的长、宽。本例中绘制时与裁切时面积相同,你也可以定制绘制的尺寸。</li>
+ </ul>
+ </li>
+</ol>
+
+<p>最终结果如下所示:</p>
+
+<p>{{EmbedGHLiveSample("learning-area/javascript/apis/drawing-graphics/getting-started/5_canvas_images.html", '100%', 260)}}</p>
+
+<div class="note">
+<p><strong>注:</strong>完整代码可到 GitHub 下载:<a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/drawing-graphics/getting-started/5_canvas_images.html">5_canvas_images.html</a>.</p>
+</div>
+
+<h2 id="循环和动画">循环和动画</h2>
+
+<p>目前我们学习了关于 2D 画布一些非常基础的用法,但是不学习动画你就无法体会画布的强大。画布是提供可编程图形的。如果你的作品不需要改变,那么你就只能永远面对那些静态图片了。</p>
+
+<h3 id="创建一个循环">创建一个循环</h3>
+
+<p>在画布中使用循环是件有趣的事,你可以在 <code><a href="/en-US/docs/Web/JavaScript/Reference/Statements/for">for</a></code> 循环中运行画布命令,和其他 JS 代码一样。</p>
+
+<p>我们来创建一个简单的示例。</p>
+
+<ol>
+ <li>
+ <p>继续复制一份画布模板(<a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/drawing-graphics/getting-started/1_canvas_template.html">1_canvas_template.html</a>)在代码编辑器中打开。</p>
+ </li>
+ <li>
+ <p>在 JS 代码末尾添加以下一行。这将创建一个新方法——{{domxref("CanvasRenderingContext2D.translate", "translate()")}},可用于移动画布的原点。</p>
+
+ <pre class="brush: js notranslate">ctx.translate(width/2, height/2);</pre>
+
+ <p>这会使原点 (0, 0) 从画布左上顶点移动至画布正中心。这个功能在许多场合非常实用,就像本示例,我们的绘制操作都是围绕着画布的中心点展开的。</p>
+ </li>
+ <li>
+ <p>在 JS 代码末尾添加以下内容:</p>
+
+ <pre class="brush: js notranslate">function degToRad(degrees) {
+ return degrees * Math.PI / 180;
+};
+
+function rand(min, max) {
+ return Math.floor(Math.random() * (max-min+1)) + (min);
+}
+
+var length = 250;
+var moveOffset = 20;
+
+for(var i = 0; i &lt; length; i++) {
+
+}</pre>
+
+ <p>这里我们实现了一个与上文三角形示例中相同的 <code>degToRad()</code> 函数、一个返回给定范围内随机数 <code>rand()</code> 函数、<code>length</code> 和 <code>moveOffset</code> 变量(见下文),以及一个空的 <code>for</code> 循环。</p>
+ </li>
+ <li>
+ <p>此处的理念是利用 <code>for</code> 循环在画布上循环迭代绘制好玩儿的内容。请将以下代码添加进 <code>for</code> 循环中:</p>
+
+ <pre class="brush: js notranslate">ctx.fillStyle = 'rgba(' + (255-length) + ', 0, ' + (255-length) + ', 0.9)';
+ctx.beginPath();
+ctx.moveTo(moveOffset, moveOffset);
+ctx.lineTo(moveOffset+length, moveOffset);
+var triHeight = length/2 * Math.tan(degToRad(60));
+ctx.lineTo(moveOffset+(length/2), moveOffset+triHeight);
+ctx.lineTo(moveOffset, moveOffset);
+ctx.fill();
+
+length--;
+moveOffset += 0.7;
+ctx.rotate(degToRad(5));</pre>
+
+ <p>在每次迭代中:</p>
+
+ <ul>
+ <li>设置 <code>fillStyle</code> 为略透明的紫色渐变色。渐变由每次迭代时 <code>length</code> 值的改变实现。随着循环的运行, <code>length</code> 值逐渐变小,从而使连续的三角形颜色逐渐变亮。</li>
+ <li>开始路径。</li>
+ <li>将钢笔移动至坐标 <code>(moveOffset, moveOffset)</code>;该变量定义了每次要绘制新三角形时需要移动的距离。</li>
+ <li>画一条直线,终点坐标为 <code>(moveOffset+length, moveOffset)</code>。即一条长度为 <code>length</code> 与 X 轴平行的线。</li>
+ <li>计算三角形的高,方法同上。</li>
+ <li>向三角形底部顶点方向绘制一条直线,然后向三角形的起始点绘制一条直线。</li>
+ <li>调用 <code>fill()</code> 为三角形填充颜色。</li>
+ <li>更新次序变量,准备绘制下一个三角形。<code>length</code> 的值减一,使三角形每次迭代都变小一些;小幅增加 <code>moveOffset</code> 的值,使得下一个三角形略微错位;用一个新函数 {{domxref("CanvasRenderingContext2D.rotate", "rotate()")}} 来旋转整块画布,在绘制下个三角形前画布旋转 5°。</li>
+ </ul>
+ </li>
+</ol>
+
+<p>好了,最终结果如下:</p>
+
+<p>{{EmbedGHLiveSample("learning-area/javascript/apis/drawing-graphics/loops_animation/6_canvas_for_loop.html", '100%', 550)}}</p>
+
+<p>现在,你可以尝试这个示例,可以加一些创新哦。比如:</p>
+
+<ul>
+ <li>把三角形换成矩形、弧,甚至内嵌的图片。</li>
+ <li>修改 <code>length</code> 和 <code>moveOffset</code> 的值。</li>
+ <li>我们引入了 <code>rand()</code> 函数但是没有使用,你可以试着用它引入一些随机数。</li>
+</ul>
+
+<div class="note">
+<p><strong>注:</strong>完整代码可到 GitHub 下载:<a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/drawing-graphics/loops_animation/6_canvas_for_loop.html">6_canvas_for_loop.html</a>.</p>
+</div>
+
+<h3 id="动画">动画</h3>
+
+<p>上文的循环示例很有趣,其实在重度画布应用(比如游戏或实时可视化)中恒定循环是至关重要的支持组件。如果期望画布显示的内容像一部电影,屏幕最好能够以 60 帧每秒的刷新率实时更新,这样人眼看到的动作才更真实、更平滑。</p>
+
+<p>一些 JavaScript 函数可以让函数在一秒内重复运行多次,这里最适合的就是 {{domxref("window.requestAnimationFrame()")}}。它只取一个参数,即每帧要运行的函数名。下一次浏览器准备好更新屏幕时,将会调用你的函数。如果你的函数向动画中绘制了更新内容,则在函数结束前再次调用 <code>requestAnimationFrame()</code>,动画循环得以保留。只有在停止调用 <code>requestAnimationFrame()</code> 时,或 <code>requestAnimationFrame()</code> 调用后、帧调用前调用了 {{domxref("window.cancelAnimationFrame()")}} 时,循环才会停止。</p>
+
+<p><strong>注:</strong>动画结束后在主代码中调用 <code>cancelAnimationFrame()</code> 是良好习惯,可以确保不再有等待运行的更新。</p>
+
+<p>浏览器自行处理诸如“使动画匀速运行”、“避免在不可见的内容浪费资源”等复杂细节问题。</p>
+
+<p>我们简单回顾一下“弹球”示例(<a href="https://mdn.github.io/learning-area/javascript/oojs/bouncing-balls/index-finished.html">在线运行</a> 或查看 <a href="https://github.com/mdn/learning-area/tree/master/javascript/oojs/bouncing-balls">源代码</a>),来探究一下原理。以下是让弹球持续运行的循环代码:</p>
+
+<pre class="brush: js notranslate">function loop() {
+ ctx.fillStyle = 'rgba(0, 0, 0, 0.25)';
+ ctx.fillRect(0, 0, width, height);
+
+ while(balls.length &lt; 25) {
+ var ball = new Ball();
+ balls.push(ball);
+ }
+
+ for(i = 0; i &lt; balls.length; i++) {
+ balls[i].draw();
+ balls[i].update();
+ balls[i].collisionDetect();
+ }
+
+ requestAnimationFrame(loop);
+}
+
+loop();</pre>
+
+<p>我们在代码底部运行了一次 <code>loop()</code> 函数,它启动了整个循环,绘制了第一帧动画。接着 <code>loop()</code> 函数接管了<code>requestAnimationFrame(loop)</code> 的调用工作,即运行下一帧、再下一帧……的动画。</p>
+
+<p>请注意每一帧我们都整体清除画布并重新渲染所有内容。(每帧创建一个新球(25 个封顶),然后绘制每个球,更新它们的位置,检查是否撞到了其它球。)向画布中绘制的新图形不能像DOM 元素那样单独操作。你无法再画布中单独操作某一个球,因为只要绘制完毕了,它就是画布的一部分,而不是一个单独的球。你需要擦除再重画,可以将整帧擦除再重画整个画面,也可通过编程选择最小的部分进行擦除和重画。</p>
+
+<p>优化图形动画是另一个编程主题,需要好多奇技淫巧。这超出我们的讨论范围啦。</p>
+
+<p>一般地,在画布上制作动画需要以下步骤:</p>
+
+<ol>
+ <li>清除画布内容(可用 {{domxref("CanvasRenderingContext2D.fillRect", "fillRect()")}} 或 {{domxref("CanvasRenderingContext2D.clearRect", "clearRect()")}})。</li>
+ <li>(在需要时)用 {{domxref("CanvasRenderingContext2D.save", "save()")}} 保存状态。(在进行下一步前保存所更新的设置,一般在复杂环境中用到)</li>
+ <li>绘制动画图形。</li>
+ <li>使用 {{domxref("CanvasRenderingContext2D.restore", "restore()")}} 恢复第 2 步中保存的状态。</li>
+ <li>调用 <code>requestAnimationFrame()</code> 准备下一帧动画。</li>
+</ol>
+
+<div class="note">
+<p><strong>注:</strong><code>save()<font face="x-locale-heading-primary, zillaslab, Palatino, Palatino Linotype, x-locale-heading-secondary, serif"><span style="background-color: #fff3d4;"> 和 </span></font></code><code>restore()</code> 这里暂不展开,可以访问 <a href="/en-US/docs/Web/API/Canvas_API/Tutorial/Transformations">变形</a> 教程(及后续内容)来获取详细信息。</p>
+</div>
+
+<h3 id="一个简单的人物动画">一个简单的人物动画</h3>
+
+<p>现在我们来创建一个简单的动画,我们找来一个复古的电脑游戏的主角制作一个在屏幕上行走的动画。</p>
+
+<ol>
+ <li>
+ <p>继续复制一份画布模板(<a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/drawing-graphics/getting-started/1_canvas_template.html">1_canvas_template.html</a>)在代码编辑器中打开。下载 <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/drawing-graphics/loops_animation/walk-right.png">walk-right.png</a> 并放在同一文件夹。</p>
+ </li>
+ <li>
+ <p>在 JS 代码末尾添加下面一行,再次将画布的原点设置为中心点。</p>
+
+ <pre class="brush: js notranslate">ctx.translate(width/2, height/2);</pre>
+ </li>
+ <li>
+ <p>创建一个新的 {{domxref("HTMLImageElement")}} 对象,把它的 {{htmlattrxref("src", "img")}} 设置为所需图片,添加一个 <code>onload</code> 事件处理器,使 <code>draw()</code> 函数在图片载入后触发。</p>
+
+ <pre class="brush: js notranslate">var image = new Image();
+image.src = 'walk-right.png';
+image.onload = draw;</pre>
+ </li>
+ <li>
+ <p>添加一些变量,来追踪精灵图在屏幕上的位置,以及当前需要显示的精灵图的序号。</p>
+
+ <pre class="brush: js notranslate">var sprite = 0;
+var posX = 0;</pre>
+
+ <p>我们来解释一下“精灵图序列”(我们借鉴了麦克托马斯的 <a href="http://atomicrobotdesign.com/blog/htmlcss/create-a-sprite-sheet-walk-cycle-using-using-css-animation/" rel="bookmark" title="Permanent Link to Create a sprite sheet walk cycle using using CSS animation">使用 CSS 动画创建人物行走的精灵图</a>)。图片如下:</p>
+
+ <p><img alt="" src="https://mdn.mozillademos.org/files/14847/walk-right.png" style="display: block; margin: 0 auto;"></p>
+
+ <p>图中包含六个精灵,它们组成了一趟完整的行走序列。每个精灵的尺寸为 102 × 148 像素。为了整齐的显示一个精灵,可以通过 <code>drawImage()</code> 来从序列中裁切出单独的精灵并隐藏其他部分,就像上文中操作 Firefox 图标的方法。切片的 X 坐标应为 102 的倍数,Y 坐标恒为 0。切片尺寸恒为 102 × 148 像素。</p>
+ </li>
+ <li>
+ <p>在代码末尾添加一个空的 <code>draw()</code> 函数,用来添加一些代码:</p>
+
+ <pre class="brush: js notranslate">function draw() {
+
+};</pre>
+ </li>
+ <li>
+ <p>本节剩余部分都在这个 <code>draw()</code> 中展开。首先,添加以下代码,清除画布,准备绘制新的帧。注意由于我们刚才将原点设置为 <code>width/2, height/2</code>,这里需要将矩形左上顶点的坐标设置为 <code>-(width/2), -(height/2)</code>。</p>
+
+ <pre class="brush: js notranslate">ctx.fillRect(-(width/2), -(height/2), width, height);</pre>
+ </li>
+ <li>
+ <p>下一步,我们使用 <code>drawImage()</code>(9参数版本)来绘制图形,添加以下代码:</p>
+
+ <pre class="brush: js notranslate">ctx.drawImage(image, (sprite*102), 0, 102, 148, 0+posX, -74, 102, 148);</pre>
+
+ <p>如你所见:</p>
+
+ <ul>
+ <li><code>image</code> 指定需要嵌入的图片。</li>
+ <li>参数 2、3 指定切片左上顶点在原图的位置坐标,X 值为 <code>sprite</code>(精灵序列 0 - 5)乘 102,Y 值恒为 0。</li>
+ <li>参数 4、5 指定切片尺寸:102 × 148 像素。</li>
+ <li>参数 6、7 指定切片在画布绘制区域的坐上顶点坐标。X 坐标位置为 0 + <code>posX</code>,意味着我们可以通过修改 <code>posX</code> 的值来修改绘制的位置。</li>
+ <li>参数 8、9 指定图片在画布中的尺寸。这里需要图片保持原始尺寸,因此我们指定宽、高值为 102、148。</li>
+ </ul>
+ </li>
+ <li>
+ <p>现在,我们在每帧绘制完毕(部分完毕)后修改 <code>sprite</code> 的值。在 <code>draw()</code> 函数底部添加以下内容:</p>
+
+ <pre class="brush: js notranslate"> if (posX % 13 === 0) {
+ if (sprite === 5) {
+ sprite = 0;
+ } else {
+ sprite++;
+ }
+ }</pre>
+
+ <p>将整个功能块放置在 <code>if (posX % 13 === 0) { ... }</code> 内。用“模(<code>%</code>)运算符”(即 <a href="/en-US/docs/Web/JavaScript/Reference/Operators/Arithmetic_Operators#Remainder_()">求余运算符</a>)来检测 <code>posX</code> 是否可以被 13 整除。如果整除,则通过增加 <code>sprite</code> 的值转至下一个精灵(到 5 号精灵时归零)。这实际上意味着每隔 13 帧才更新一次精灵,每秒大约更新 5 帧(<code>requestAnimationFrame()</code> 每秒最多调用 60 帧)。我们故意放慢了帧率,因为精灵图只有六个,且如果每秒显示 60 帧的话,这个角色就会快到起飞。</p>
+
+ <p>外部程序块中用一个 <code><a href="/en-US/docs/Web/JavaScript/Reference/Statements/if...else">if ... else</a></code> 语句来检测 <code>sprite</code> 的值是否为 5(精灵序号在 0 - 5 间循环,因此 5 代表最后一个精灵)。 如果最后一个精灵已经显示,就把 <code>sprite</code> 重置为 0,否则加 1。</p>
+ </li>
+ <li>
+ <p>下一步要算出每帧 <code>posX</code> 的值,在上文代码末尾添加以下内容:</p>
+
+ <pre class="brush: js notranslate"> if(posX &gt; width/2) {
+ newStartPos = -((width/2) + 102);
+ posX = Math.ceil(newStartPos / 13) * 13;
+ console.log(posX);
+ } else {
+ posX += 2;
+ }</pre>
+
+ <p>用另一个 <code>if ... else</code> 来检测 <code>posX</code> 的值是否超出了 <code>width/2</code>,那意味着角色走到了屏幕右侧边缘。如果这样就计算出一个让角色出现在屏幕左侧边缘的 X 坐标,然后将 <code>posX</code> 设置为最接近这个数的 13 的倍数。这里必须限定 13 的倍数这个条件,这是因为 <code>posX</code> 不可能是 13 的倍数,若不限定的话上一段代码就不会运行了。</p>
+
+ <p>如果角色没有走到屏幕边缘,只需为 <code>posX</code> 加 2。这将让他在下次绘制时更靠右些。</p>
+ </li>
+ <li>
+ <p>最后,通过在 <code>draw()</code> 函数末尾添加 {{domxref("window.requestAnimationFrame", "requestAnimationFrame()")}} 调用以实现动画的循环。</p>
+
+ <pre class="brush: js notranslate">window.requestAnimationFrame(draw);</pre>
+ </li>
+</ol>
+
+<p>成功了!最终效果如下所示:</p>
+
+<p>{{EmbedGHLiveSample("learning-area/javascript/apis/drawing-graphics/loops_animation/7_canvas_walking_animation.html", '100%', 260)}}</p>
+
+<div class="note">
+<p><strong>注:</strong>完整代码可到 GitHub 下载:<a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/drawing-graphics/loops_animation/7_canvas_walking_animation.html">7_canvas_walking_animation.html</a>.</p>
+</div>
+
+<h3 id="简单的绘图应用">简单的绘图应用</h3>
+
+<p>下面来演示一个简单的绘图应用,作为最后一个绘画示例,它将向你展示动画循环如果与用户输入(本例中为鼠标移动)结合起来。我们不会带你一步一步来实现本示例,只对代码中最有趣的部分进行探究。</p>
+
+<p>示例代码可到GitHub下载:<a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/drawing-graphics/loops_animation/8_canvas_drawing_app.html">8_canvas_drawing_app.html</a>,也可在线试玩:</p>
+
+<p>{{EmbedGHLiveSample("learning-area/javascript/apis/drawing-graphics/loops_animation/8_canvas_drawing_app.html", '100%', 600)}}</p>
+
+<p>下面我们就来看看代码的精华部分。首先,用 <code>curX</code>、<code>curY</code> 和 <code>pressed</code> 这三个变量来跟踪鼠标的 X、Y 坐标和点击状态。当鼠标移动时,触发一个函数作为 <code>onmousemove</code> 事件处理器,其应捕获当前的 X 和 Y 值。再用 <code>onmousedown</code> 和 <code>onmouseup</code> 事件处理器来修改鼠标键按下时 <code>pressed</code> 的值(按下为 <code>true</code>,释放为  <code>false</code>)。</p>
+
+<pre class="brush: js notranslate">var curX;
+var curY;
+var pressed = false;
+
+document.onmousemove = function(e) {
+ curX = (window.Event) ? e.pageX : e.clientX + (document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft);
+ curY = (window.Event) ? e.pageY : e.clientY + (document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop);
+}
+
+canvas.onmousedown = function() {
+ pressed = true;
+};
+
+canvas.onmouseup = function() {
+ pressed = false;
+}</pre>
+
+<p>在按下“Clear canvas”(清除画布)按钮时,我们运行一个简单的函数来清除整个画布的内容至纯黑色,和刚才的方法一致:</p>
+
+<pre class="brush: js notranslate">clearBtn.onclick = function() {
+ ctx.fillStyle = 'rgb(0, 0, 0)';
+ ctx.fillRect(0, 0, width, height);
+}</pre>
+
+<p>这次的绘图循环非常简单,如果 <code>pressed</code> 为 <code>true</code>,则绘制一个圆,该圆以颜色选择器中设定的颜色为背景,以滑动选择器设定的数值为半径。</p>
+
+<pre class="brush: js notranslate">function draw() {
+ if(pressed) {
+ ctx.fillStyle = colorPicker.value;
+ ctx.beginPath();
+ ctx.arc(curX, curY-85, sizePicker.value, degToRad(0), degToRad(360), false);
+ ctx.fill();
+ }
+
+ requestAnimationFrame(draw);
+}
+
+draw();</pre>
+
+<div class="note">
+<p><strong>注:</strong><code>range</code> 和 <code>color</code> 两个 {{htmlelement("input")}} 的类型有良好的跨浏览器支持,但 Safari 暂不支持 <code>color</code>,IE 10 以下版本两者均不支持。如果你的浏览器不支持这些输入类型,它们将降格为简单文字输入区域,可以直接输入合法的数字来表示半径和颜色的值。</p>
+</div>
+
+<h2 id="WebGL">WebGL</h2>
+
+<p>2D 内容告一段落,现在简单了解一下 3D 画布。3D 画布内容可通过的 <a href="/zh-CN/docs/Web/API/WebGL_API">WebGL</a> API 实现,尽管它和 2D canvas API 都可在 {{htmlelement("canvas")}} 元素上进行渲染,但两者是彼此独立的。</p>
+
+<p>WebGL 基于 <a href="/en-US/docs/Glossary/OpenGL">OpenGL</a> 图形编程语言实现,可直接与 <a href="/en-US/docs/Glossary/GPU">GPU</a> 通信,基于此,编写纯 WebGL 代码与常规的 JavaScript 不尽相同,更像 C++ 那样的底层语言,更加复杂,但无比强大。</p>
+
+<h3 id="使用库">使用库</h3>
+
+<p>由于 3D 绘图的复杂性,大多数人写代码时会使用第三方 JavaScript 库(比如 <a href="/en-US/docs/Games/Techniques/3D_on_the_web/Building_up_a_basic_demo_with_Three.js">Three.js</a>、<a href="/en-US/docs/Games/Techniques/3D_on_the_web/Building_up_a_basic_demo_with_PlayCanvas">PlayCanvas</a> 或 <a href="/en-US/docs/Games/Techniques/3D_on_the_web/Building_up_a_basic_demo_with_Babylon.js">Babylon.js</a>)。大多数库的原理都基本类似,提供创建基本的、自定义性状的功能、视图定位摄影和光效、表面纹理覆盖,等等。库负责 与 WebGL 通信,你只需完成更高阶工作。</p>
+
+<p>接触任何一个库都意味着要学一套全新的API(这里是第三方的版本),但与纯 WebGL 编程都大同小异。</p>
+
+<h3 id="创建魔方">创建魔方</h3>
+
+<p>我们来看一个简单的示例,用一套 WebGL 库(这里我们选择 <a href="/en-US/docs/Games/Techniques/3D_on_the_web/Building_up_a_basic_demo_with_Three.js">Three.js</a>,最流行的 3D 绘图库之一)来创建我们在本文开头看到的旋转魔方。</p>
+
+<ol>
+ <li>
+ <p>首先,下载 <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/drawing-graphics/threejs-cube/index.html">index.html</a>、<a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/drawing-graphics/threejs-cube/metal003.png">metal003.png</a> 并保存在同一个文件夹。图片将用于魔方的表面纹理。</p>
+ </li>
+ <li>
+ <p>然后,继续在同一个文件夹内创建 <code>main.js</code> 文件。</p>
+ </li>
+ <li>
+ <p>在编辑器中打开 <code>index.html</code> 可以看到其中有两个 {{htmlelement("script")}} 元素,第一个将 <code>three.min.js</code> 嵌入页面,第二个将我们的 <code>main.js</code> 嵌入页面。需要自行 <a href="https://raw.githubusercontent.com/mrdoob/three.js/dev/build/three.min.js">下载 three.min.js 库</a> 并保存到同一个文件夹中。</p>
+ </li>
+ <li>
+ <p>将 <code>three.js</code> 嵌入页面后,就可以在 <code>main.js</code> 中添加新的代码对其加以应用了。请添加下面一行:</p>
+
+ <pre class="brush: js notranslate">var scene = new THREE.Scene();</pre>
+
+ <p><code><a href="https://threejs.org/docs/index.html#Reference/Scenes/Scene">Scene()</a></code> 构造器创建一个新的场景,表示即将显示的整个 3D 世界。</p>
+ </li>
+ <li>
+ <p>下一步,我们需要一部<strong>摄影机</strong>来看到整个场景。在 3D 绘图语境中,摄影机表示观察者在世界里的位置,可通过下面代码创建一部摄影机:</p>
+
+ <pre class="brush: js notranslate">var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
+camera.position.z = 5;
+</pre>
+
+ <p><code><a href="https://threejs.org/docs/index.html#Reference/Cameras/PerspectiveCamera">PerspectiveCamera()</a></code> 构造器有四个参数:</p>
+
+ <ul>
+ <li>观察区域:镜头视角大小,用角度表示。</li>
+ <li>屏幕宽高比:一般情况下,宽高比等于屏幕的宽比上屏幕的高。使用其他的值会使场景扭曲(也许正是你需要的,但一般都不是)。</li>
+ <li>近裁切面:停止渲染前对象离摄影机的最近距离。设想一下,举起一个手指,逐渐移近双眼,某个点后就在也看不到这根手指了。</li>
+ <li>远裁切面:停止渲染前离摄像机最远的对象的距离。</li>
+ </ul>
+
+ <p>将摄像机的位置设定为距 Z 轴 5 个距离单位的位置。与 CSS 类似,在屏幕之外你(观察者)的位置。</p>
+ </li>
+ <li>
+ <p>第三个重要参数是渲染器。我们用它来渲染给定的场景,可通过给定位值得摄影机观察。现在我们使用 <code><a href="https://threejs.org/docs/index.html#Reference/Renderers/WebGLRenderer">WebGLRenderer()</a></code> 构造器创建一个渲染器供稍后使用。添加以下代码:</p>
+
+ <pre class="brush: js notranslate">var renderer = new THREE.WebGLRenderer();
+renderer.setSize(window.innerWidth, window.innerHeight);
+document.body.appendChild(renderer.domElement);</pre>
+
+ <p>第一行创建一个新的渲染器,第二行设定渲染器在当前摄影机视角下的尺寸,第三行将渲染好的 {{htmlelement("canvas")}} 对象加入HTML的 {{htmlelement("body")}} 中。现在渲染器绘制的内容将在窗口中显示出来。</p>
+ </li>
+ <li>
+ <p>下一步,在画布中创建魔方。把以下代码添加到 JS 文件中:</p>
+
+ <pre class="brush: js notranslate">var cube;
+
+var loader = new THREE.TextureLoader();
+
+loader.load( 'metal003.png', function (texture) {
+ texture.wrapS = THREE.RepeatWrapping;
+ texture.wrapT = THREE.RepeatWrapping;
+ texture.repeat.set(2, 2);
+
+ var geometry = new THREE.BoxGeometry(2.4, 2.4, 2.4);
+ var material = new THREE.MeshLambertMaterial( { map: texture, shading: THREE.FlatShading } );
+ cube = new THREE.Mesh(geometry, material);
+ scene.add(cube);
+
+ draw();
+});</pre>
+
+ <p>内容很多,我们来剥丝抽茧:</p>
+
+ <ul>
+ <li>首先,创建一个全局变量 <code>cube</code>,这样就可以在代码任意位置访问我们的魔方。</li>
+ <li>然后,创建一个 <code><a href="https://threejs.org/docs/index.html#Reference/Loaders/TextureLoader">TextureLoader</a></code> 对象,并调用 <code>load()</code>。 这里 <code>load()</code> 包含两个参数(其它情况可以有更多参数):需要调用的纹理图(PNG 文件)和纹理加载成功后调用的函数。</li>
+ <li>函数内部,我们用 <code><a href="https://threejs.org/docs/index.html#Reference/Textures/Texture">texture</a></code> 对象的属性指明我们要在魔方的每个面渲染 2 × 2 的图片,然后创建一个 <code><a href="https://threejs.org/docs/index.html#Reference/Geometries/BoxGeometry">BoxGeometry</a></code> 对象和一个 <code><a href="https://threejs.org/docs/index.html#Reference/Materials/MeshLambertMaterial">MeshLambertMaterial</a></code> 对象,将两者作为 <code><a href="https://threejs.org/docs/index.html#Reference/Objects/Mesh">Mesh</a></code> 的参数来创建我们的魔方。 <code><a href="https://threejs.org/docs/index.html#Reference/Objects/Mesh">Mesh</a></code> 一般就需要两个参数:一个几何(形状)和一个素材(形状表面外观)。</li>
+ <li>最后,将魔方添加进场景中,调用我们的 <code>draw()</code> 函数开始动画。</li>
+ </ul>
+ </li>
+ <li>
+ <p>定义 <code>draw()</code> 函数前,我们需要先为场景打光,以照亮场景中的物体。请添加以下代码:</p>
+
+ <pre class="brush: js notranslate">var light = new THREE.AmbientLight('rgb(255, 255, 255)'); // soft white light
+scene.add(light);
+
+var spotLight = new THREE.SpotLight('rgb(255, 255, 255)');
+spotLight.position.set( 100, 1000, 1000 );
+spotLight.castShadow = true;
+scene.add(spotLight);</pre>
+
+ <p><code><a href="https://threejs.org/docs/index.html#Reference/Lights/AmbientLight">AmbientLight</a></code> 对象是可以轻度照亮整个场景的柔光,就像户外的阳光。而 <code><a href="https://threejs.org/docs/index.html#Reference/Lights/SpotLight">SpotLight</a></code> 对象是直射的硬光,就像闪光灯和手电筒(或者它的英文字面意思——聚光灯)。</p>
+ </li>
+ <li>
+ <p>最后,在代码末尾添加我们的 <code>draw()</code> 函数:</p>
+
+ <pre class="brush: js notranslate">function draw() {
+ cube.rotation.x += 0.01;
+ cube.rotation.y += 0.01;
+ renderer.render(scene, camera);
+
+ requestAnimationFrame(draw);
+}</pre>
+
+ <p>这段代码很直观,每一帧我们都沿 X 轴 和 Y 轴将魔方轻微转动,然后按摄像机视角渲染场景,最后调用 <code>requestAnimationFrame()</code> 来准备下一帧。</p>
+ </li>
+</ol>
+
+<p>回顾一下最终效果:</p>
+
+<p>{{EmbedGHLiveSample("learning-area/javascript/apis/drawing-graphics/threejs-cube/index.html", '100%', 500)}}</p>
+
+<p>你可以 <a href="https://github.com/mdn/learning-area/tree/master/javascript/apis/drawing-graphics/threejs-cube">到 Github 下载最终代码</a>。</p>
+
+<div class="note">
+<p><strong>注:</strong>在我们的 GitHub repo 还有另一个趣味 3D 魔方示例——<a href="https://github.com/mdn/learning-area/tree/master/javascript/apis/drawing-graphics/threejs-video-cube">Three.js Video Cube</a>(<a>在线查看</a>)。其中通过 {{domxref("MediaDevices.getUserMedia", "getUserMedia()")}} 来从电脑摄像头获取一段视频,将其投影到魔方上作为纹理。</p>
+</div>
+
+<h2 id="小结">小结</h2>
+
+<p>此刻你以经了解了一些 Canvas 和 WebGL 图形编程的基本理念和简单应用,你一定产生了不少创作灵感,玩得开心!</p>
+
+<h2 id="另请参阅">另请参阅</h2>
+
+<p>本文我们只涉及到画布最为基本的内容,以下内容帮你探索更多:</p>
+
+<ul>
+ <li><a href="/zh-CN/docs/Web/API/Canvas_API/Tutorial">Canvas 教程</a>:一个详尽的教程系列,更细致深入地讲解了 2D 画布所需的知识。必读。</li>
+ <li><a href="/zh-CN/docs/Web/API/WebGL_API/Tutorial">WebGL 教程</a>:纯 WebGL 编程教程系列。</li>
+ <li><a href="/zh-CN/docs/Games/Techniques/3D_on_the_web/Building_up_a_basic_demo_with_Three.js">用 Three.js 创建一个简单的示例</a>:Three.js 基础教程。我们还提供 <a href="/en-US/docs/Games/Techniques/3D_on_the_web/Building_up_a_basic_demo_with_PlayCanvas">PlayCanvas</a> 和 <a href="/en-US/docs/Games/Techniques/3D_on_the_web/Building_up_a_basic_demo_with_Babylon.js">Babylon.js</a> 的基础教程。</li>
+ <li><a href="/zh-CN/docs/Games">游戏开发</a>:MDN web 游戏开发目录页。提供与 2D、3D画布相关的实用教程和技术,可参考“技术”和“教程”菜单项。</li>
+</ul>
+
+<h2 id="示例">示例</h2>
+
+<ul>
+ <li><a href="https://github.com/mdn/violent-theremin">Violent theramin</a>:用 Web 音频 API 创建声音,用画布显示漂亮的视觉效果以配合音乐。</li>
+ <li><a href="https://github.com/mdn/voice-change-o-matic">Voice change-o-matic</a>:用画布为 Web 音频 API 产生的音效提供实时的视觉效果。</li>
+</ul>
+
+<p>{{PreviousMenuNext("Learn/JavaScript/Client-side_web_APIs/Third_party_APIs", "Learn/JavaScript/Client-side_web_APIs/Video_and_audio_APIs", "Learn/JavaScript/Client-side_web_APIs")}}</p>
+
+<h2 id="本章目录">本章目录</h2>
+
+<ul>
+ <li><a href="https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Client-side_web_APIs/Introduction">web API 简介</a></li>
+ <li><a href="https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Client-side_web_APIs/Manipulating_documents">文档操作</a></li>
+ <li><a href="https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Client-side_web_APIs/Fetching_data">从服务器获得数据</a></li>
+ <li><a href="https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Client-side_web_APIs/Third_party_APIs">第三方 API</a></li>
+ <li><a href="https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Client-side_web_APIs/Drawing_graphics">绘图</a></li>
+ <li><a href="https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Client-side_web_APIs/Video_and_audio_APIs">视频API 和 音频 API</a></li>
+ <li><a href="https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Client-side_web_APIs/Client-side_storage">客户端存储</a></li>
+</ul>
diff --git a/files/zh-cn/learn/javascript/client-side_web_apis/fetching_data/index.html b/files/zh-cn/learn/javascript/client-side_web_apis/fetching_data/index.html
new file mode 100644
index 0000000000..5ea749c536
--- /dev/null
+++ b/files/zh-cn/learn/javascript/client-side_web_apis/fetching_data/index.html
@@ -0,0 +1,395 @@
+---
+title: 从服务器获取数据
+slug: Learn/JavaScript/Client-side_web_APIs/Fetching_data
+tags:
+ - API
+ - Fetch
+ - JSON
+ - JavaScript
+ - Promises
+ - XHR
+ - XML
+ - XMLHttpRequest
+ - 初学者
+ - 学习
+ - 数据
+ - 服务器
+ - 脚本编程
+ - 请求
+translation_of: Learn/JavaScript/Client-side_web_APIs/Fetching_data
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/JavaScript/Client-side_web_APIs/Manipulating_documents", "Learn/JavaScript/Client-side_web_APIs/Third_party_APIs", "Learn/JavaScript/Client-side_web_APIs")}}</div>
+
+<p class="summary">在现代网站和应用中另一个常见的任务是从服务端获取个别数据来更新部分网页而不用加载整个页面。 这看起来是小细节却对网站性能和行为产生巨大的影响。所以我们将在这篇文章介绍概念和技术使它成为可能,例如: XMLHttpRequest 和 Fetch API.</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">先决条件:</th>
+ <td>JavaScript 基础(查看 <a href="/zh-CN/docs/Learn/JavaScript/First_steps">第一步</a>, <a href="/zh-CN/docs/Learn/JavaScript/Building_blocks">基础要件</a>, <a href="/zh-CN/docs/Learn/JavaScript/Objects">JavaScript对象</a>), 和<a href="/zh-CN/docs/Learn/JavaScript/Client-side_web_APIs/Introduction">客户端API基础</a></td>
+ </tr>
+ <tr>
+ <th scope="row">目标:</th>
+ <td>学会如何从服务器获取数据并使用它更新网页内容.</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="这里有什么问题">这里有什么问题?</h2>
+
+<p>最初加载页面很简单  -- 你为网站发送一个请求到服务器, 只要没有出错你将会获取资源并显示网页到你的电脑上。</p>
+
+<p><img alt="A basic representation of a web site architecture" src="https://mdn.mozillademos.org/files/6475/web-site-architechture@2x.png" style="display: block; height: 134px; margin: 0px auto; width: 484px;"></p>
+
+<p>这个模型的问题是当你想更新网页的任何部分,例如显示一套新的产品或者加载一个新的页面,你需要再一次加载整个页面。这是非常浪费的并且导致了差的用户体验尤其是现在的页面越来越大且越来越复杂。</p>
+
+<h3 id="Ajax开始">Ajax开始</h3>
+
+<p>这导致了创建允许网页请求小块数据(例如 <a href="/en-US/docs/Web/HTML">HTML</a>, {{glossary("XML")}}, <a href="/en-US/docs/Learn/JavaScript/Objects/JSON">JSON</a>, 或纯文本) 和 仅在需要时显示它们的技术,从而帮助解决上述问题。</p>
+
+<p>这是通过使用诸如 {{domxref("XMLHttpRequest")}} 之类的API或者 — 最近以来的 <a href="/en-US/docs/Web/API/Fetch_API">Fetch API</a> 来实现. 这些技术允许网页直接处理对服务器上可用的特定资源的 <a href="/en-US/docs/Web/HTTP">HTTP</a> 请求,并在显示之前根据需要对结果数据进行格式化。</p>
+
+<div class="note">
+<p><strong>注意:在早期,这种通用技术被称为</strong>Asynchronous JavaScript and XML<strong>(Ajax),</strong> 因为它倾向于使用{{domxref("XMLHttpRequest")}} 来请求XML数据。 但通常不是这种情况 (你更有可能使用 <code>XMLHttpRequest</code> 或 Fetch 来请求JSON), 但结果仍然是一样的,术语“Ajax”仍然常用于描述这种技术。</p>
+</div>
+
+<p><img alt="A simple modern architecture for web sites" src="https://mdn.mozillademos.org/files/6477/moderne-web-site-architechture@2x.png" style="display: block; height: 235px; margin: 0px auto; width: 559px;"></p>
+
+<p>Ajax模型包括使用Web API作为代理来更智能地请求数据,而不仅仅是让浏览器重新加载整个页面。让我们来思考这个意义:</p>
+
+<ol>
+ <li><font><font>去你最喜欢的信息丰富的网站之一,如亚马逊,油管,CNN等,并加载它。</font></font></li>
+ <li>现在搜索一些东西,比如一个新产品。 主要内容将会改变,但大部分周围的信息,如页眉,页脚,导航菜单等都将保持不变。</li>
+</ol>
+
+<p>这是一件非常好的事情,因为:</p>
+
+<ul>
+ <li><font><font>页面更新速度更快,您不必等待页面刷新,这意味着该网站体验感觉更快,响应更快。</font></font></li>
+ <li><font><font>每次更新都会下载更少的数据,这意味着更少地浪费带宽。</font><font>在宽带连接的桌面上这可能不是一个大问题,但是在移动设备和发展中国家没有无处不在的快速互联网服务是一个大问题。</font></font></li>
+</ul>
+
+<p>为了进一步提高速度,有些网站还会在首次请求时将资产和数据存储在用户的计算机上,这意味着在后续访问中,他们将使用本地版本,而不是在首次加载页面时下载新副本。 内容仅在更新后从服务器重新加载。</p>
+
+<p><img alt="A basic web app data flow architecture" src="https://mdn.mozillademos.org/files/6479/web-app-architecture@2x.png" style="display: block; height: 383px; margin: 0px auto; width: 562px;"></p>
+
+<p><font>本文不会涉及这种存储技术。</font><font>我们稍后会在模块中讨论它。</font></p>
+
+<h2 id="基本的Ajax请求">基本的Ajax请求</h2>
+
+<p>让我们看看使用{{domxref("XMLHttpRequest")}} 和 <a href="/en-US/docs/Web/API/Fetch_API">Fetch</a>如何处理这样的请求. 对于这些例子,我们将从几个不同的文本文件中请求数据,并使用它们来填充内容区域。</p>
+
+<p>这一系列文件将作为我们的假数据库; 在真正的应用程序中,我们更可能使用服务器端语言(如PHP,Python或Node)从数据库请求我们的数据。 不过,我们要保持简单,并专注于客户端部分。</p>
+
+<h3 id="XMLHttpRequest">XMLHttpRequest</h3>
+
+<p><code>XMLHttpRequest</code> (通常缩写为XHR)现在是一个相当古老的技术 - 它是在20世纪90年代后期由微软发明的,并且已经在相当长的时间内跨浏览器进行了标准化。</p>
+
+<ol>
+ <li>
+ <p>为例子做些准备, 将 <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/ajax-start.html">ajax-start.html</a> 和四个文本文件 — <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/verse1.txt">verse1.txt</a>, <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/verse2.txt">verse2.txt</a>, <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/verse3.txt">verse3.txt</a>,  <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/verse4.txt">verse4.txt</a> — 复制到你计算机上的一个新目录. 在这个例子中,我们将通过XHR在下拉菜单中选择一首诗(您可能会认识 — "如果谷歌翻译可以翻译的话")加载另一首诗。</p>
+ </li>
+ <li>
+ <p>在 {{htmlelement("script")}} 的内部, 添加下面的代码. 将 {{htmlelement("select")}} 和 {{htmlelement("pre")}} 元素的引用存储到变量中, 并定义一个 {{domxref("GlobalEventHandlers.onchange","onchange")}} 事件处理函数,可以在select的值改变时, 将其值传递给 <code>updateDisplay()</code> 函数作为参数。 </p>
+
+ <pre class="brush: js">const verseChoose = document.querySelector('select');
+const poemDisplay = document.querySelector('pre');
+
+verseChoose.onchange = function() {
+ const verse = verseChoose.value;
+ updateDisplay(verse);
+};</pre>
+ </li>
+ <li>
+ <p>定义 <code>updateDisplay()</code> 函数. 首先,将下面的代码块放在之前代码块的下面 - 这是函数的空壳:</p>
+
+ <pre class="brush: js">function updateDisplay(verse) {
+
+}</pre>
+ </li>
+ <li>
+ <p>我们将通过构造一个  指向我们要加载的文本文件的相对URL  来启动我们的函数, 因为我们稍后需要它. 任何时候 {{htmlelement("select")}} 元素的值都与所选的 {{htmlelement("option")}} 内的文本相同 (除非在值属性中指定了不同的值) — 例如 "Verse 1". 相应的诗歌文本文件是 "verse1.txt", 并与HTML文件位于同一目录中, 因此只需要文件名即可。</p>
+
+ <p>但是,Web服务器往往是区分大小写的,文件名没有空格。 要将“Verse 1”转换为“verse1.txt”,我们需要将V转换为小写,删除空格,并在末尾添加.txt。  这可以通过 {{jsxref("String.replace", "replace()")}}, {{jsxref("String.toLowerCase", "toLowerCase()")}}, 和 简单的 <a href="/en-US/docs/Learn/JavaScript/First_steps/Strings#Concatenating_strings">string concatenation</a> 来完成. 在 <code>updateDisplay()</code> 函数中添加以下代码:</p>
+
+ <pre class="brush: js">verse = verse.replace(" ", "");
+verse = verse.toLowerCase();
+let url = verse + '.txt';</pre>
+ </li>
+ <li>
+ <p>要开始创建XHR请求,您需要使用 {{domxref("XMLHttpRequest.XMLHttpRequest", "XMLHttpRequest()")}} 的构造函数创建一个新的请求对象。 你可以把这个对象叫做你喜欢的任何东西, 但是我们会把它叫做 <code>request</code> 来保持简单. 在之前的代码中添加以下内容:</p>
+
+ <pre class="brush: js">let request = new XMLHttpRequest();</pre>
+ </li>
+ <li>
+ <p>接下来,您需要使用 {{domxref("XMLHttpRequest.open","open()")}} 方法来指定用于从网络请求资源的 <a href="/en-US/docs/Web/HTTP/Methods">HTTP request method</a> , 以及它的URL是什么。我们将在这里使用 <code><a href="/en-US/docs/Web/HTTP/Methods/GET">GET</a></code> 方法, 并将URL设置为我们的 <code>url</code> 变量. 在你上面的代码中添加以下代码:</p>
+
+ <pre class="brush: js">request.open('GET', url);</pre>
+ </li>
+ <li>
+ <p>接下来,我们将设置我们期待的响应类型 —  这是由请求的 {{domxref("XMLHttpRequest.responseType", "responseType")}} 属性定义的 — 作为 <code>text</code>.  这并不是绝对必要的 — XHR默认返回文本 —但如果你想在以后获取其他类型的数据,养成这样的习惯是一个好习惯. 接下来添加:</p>
+
+ <pre class="brush: js">request.responseType = 'text';</pre>
+ </li>
+ <li>
+ <p>从网络获取资源是一个 {{glossary("asynchronous")}} "异步" 操作, 这意味着您必须等待该操作完成(例如,资源从网络返回),然后才能对该响应执行任何操作,否则会出错,将被抛出错误。 XHR允许你使用它的 {{domxref("XMLHttpRequest.onload", "onload")}} 事件处理器来处理这个事件 — 当{{event("onload")}} 事件触发时(当响应已经返回时)这个事件会被运行。 发生这种情况时, <code>response</code> 数据将在XHR请求对象的响应属性中可用。</p>
+
+ <p>在后面添加以下内容.  你会看到,在 <code>onload</code> 事件处理程序中,我们将 <code>poemDisplay</code>  ( {{htmlelement("pre")}} 元素 ) 的  <code><a href="/en-US/docs/Web/API/Node/textContent">textContent</a></code> 设置为 {{domxref("XMLHttpRequest.response", "request.response")}} 属性的值。</p>
+
+ <pre class="brush: js">request.onload = function() {
+ poemDisplay.textContent = request.response;
+};</pre>
+ </li>
+ <li>
+ <p>以上都是XHR请求的设置 — 在我们告诉它之前,它不会真正运行,这是通过 {{domxref("XMLHttpRequest.send","send()")}} 完成的. 在你之前的代码下添加以下内容完成该函数:</p>
+
+ <pre class="brush: js">request.send();</pre>
+ </li>
+ <li>
+ <p>这个例子中的一个问题就是它首次加载时不会显示任何诗。 为了解决这个问题,在代码的底部添加以下两行 (正好在关闭的 <code>&lt;/script&gt;</code> 标签之上) 默认加载第1节,并确保 {{htmlelement("select")}} 元素始终显示正确的值:</p>
+
+ <pre class="brush: js">updateDisplay('Verse 1');
+verseChoose.value = 'Verse 1';</pre>
+ </li>
+</ol>
+
+<h3 id="在server端运行例子">在server端运行例子</h3>
+
+<p>如果只是从本地文件运行示例,一些浏览器(包括Chrome)将不会运行XHR请求。这是因为安全限制(更多关于web安全性的限制,请参阅<a href="/en-US/docs/Learn/Server-side/First_steps/Website_security">Website security</a>)。</p>
+
+<p>为了解决这个问题,我们需要通过在本地web服务器上运行它来测试这个示例。要了解如何实现这一点,请阅读<a href="/en-US/docs/Learn/Common_questions/set_up_a_local_testing_server">How do you set up a local testing server?</a></p>
+
+<h3 id="Fetch">Fetch</h3>
+
+<p>Fetch API基本上是XHR的一个现代替代品——它是最近在浏览器中引入的,它使异步HTTP请求在JavaScript中更容易实现,对于开发人员和在Fetch之上构建的其他API来说都是如此。</p>
+
+<p>让我们将最后一个示例转换为使用Fetch !</p>
+
+<ol>
+ <li>
+ <p>复制您之前完成的示例目录. (如果您没有通过以前的练习,创建一个新的目录。, 然后复制 <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/xhr-basic.html">xhr-basic.html</a>和这四个文件 — <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/verse1.txt">verse1.txt</a>, <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/verse2.txt">verse2.txt</a>, <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/verse3.txt">verse3.txt</a>, and <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/verse4.txt">verse4.txt</a>.)</p>
+ </li>
+ <li>
+ <p>在 <code>updateDisplay()</code> 里找到 XHR 那段代码:</p>
+
+ <pre class="brush: js">let request = new XMLHttpRequest();
+request.open('GET', url);
+request.responseType = 'text';
+
+request.onload = function() {
+ poemDisplay.textContent = request.response;
+};
+
+request.send();</pre>
+ </li>
+ <li>
+ <p>像这样替换掉所有关于XHR的代码:</p>
+
+ <pre class="brush: js">fetch(url).then(function(response) {
+ response.text().then(function(text) {
+ poemDisplay.textContent = text;
+ });
+});</pre>
+ </li>
+ <li>
+ <p>在浏览器中加载示例(通过web服务器运行),它应该与XHR版本相同,前提是您运行的是一个现代浏览器。</p>
+ </li>
+</ol>
+
+<h4 id="那么Fetch代码中发生了什么呢">那么Fetch代码中发生了什么呢?</h4>
+
+<p>首先,我们调用了<code><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/WorkerOrWindowGlobalScope/fetch" rel="nofollow" title="此页面仍未被本地化, 期待您的翻译!">fetch()</a></code>方法,将我们要获取的资源的URL传递给它。这相当于现代版的XHR中的<code>request.open()</code>,另外,您不需要任何等效的<code>send()</code>方法。</p>
+
+<p>然后,你可以看到<code>.then()</code>方法连接到了<code>fetch()</code>末尾-这个方法是<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise" title="Promise 对象用于一个异步操作的最终完成(或失败)及其结果值的表示。(简单点说就是处理异步请求。我们经常会做些承诺,如果我赢了你就嫁给我,如果输了我就嫁给你之类的诺言。这就是promise的中文含义:诺言,一个成功,一个失败。)"><code>Promises</code></a>的一部分,是一个用于执行异步操作的现代JavaScript特性。<code>fetch()</code>返回一个promise,它将解析从服务器发回的响应。我们使用<code>then()</code>来运行一些后续代码,这是我们在其内部定义的函数。这相当于XHR版本中的<code>onload</code>事件处理程序。</p>
+
+<p>当<code>fetch()</code> promise 解析时,这个函数会自动将响应从服务器传递给参数。在函数内部,我们获取响应并运行其<code>text()</code>方法。这基本上将响应作为原始文本返回,这相当于在XHR版本中的<code>responseType = 'text'</code>。</p>
+
+<p>你会看到 <code>text()</code> 也返回了一个 promise, 所以我们连接另外一个 <code>.then()</code> 到它上面, 在其中我们定义了一个函数来接收 <code>text()</code> promise解析的生文本。</p>
+
+<p>在promise的函数内部,我们做的和在XHR版本中差不多— 设置 <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/pre" title="The HTML &lt;pre> element represents preformatted text which is to be presented exactly as written in the HTML file."><code>&lt;pre&gt;</code></a> 元素的文本内容为text的值。</p>
+
+<h3 id="关于promises">关于promises </h3>
+
+<p>当你第一次见到它们的时候,promises会让你有点困惑,但现在不要太担心这个。在一段时间之后,您将会习惯它们,特别是当您了解更多关于现代JavaScript api的时候——大多数现代的JavaScript api都是基于promises的。</p>
+
+<p>让我们再看看上面的promises结构,看看我们是否能更清楚地理解它:</p>
+
+<pre class="brush: js">fetch(url).then(function(response) {
+ response.text().then(function(text) {
+ poemDisplay.textContent = text;
+ });
+});</pre>
+
+<p>第一行是说‘’获取位于url里的资源(<code>fetch(url)</code>)‘’和“然后当promise解析后运行指定的函数(<code>.then(function() { ... })</code>)”。"解析"的意思是"在将来某一时刻完成指定的操作"。在本例中,指定的操作是从指定的URL(使用HTTP请求)获取资源,并返回对我们执行某些操作的响应。</p>
+
+<p>实际上,传递给 <code>then()</code> 是一段不会立即执行的代码 — 而是当返回响应时代码会被运行。注意,你还可以选择把你的 promise 保存到一个变量里, 链接 <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then" title="The then() method returns a Promise. It takes up to two arguments: callback functions for the success and failure cases of the Promise."><code>.then()</code></a> 在相同的位置。下面的代码会做相同的事情。</p>
+
+<pre class="brush: js">let myFetch = fetch(url);
+
+myFetch.then(function(response) {
+ response.text().then(function(text) {
+ poemDisplay.textContent = text;
+ });
+});</pre>
+
+<p>因为方法 fetch() 返回一个解析HTTP响应的promise, 你在 .then() 中定义的任何函数会被自动给与一个响应作为一个参数。你可以给这个参数取任何名字,以下的例子依然可以实现:(例子里把response参数叫做狗饼干---'dogBiscuits'=狗饼干)</p>
+
+<pre class="brush: js">fetch(url).then(function(dogBiscuits) {
+ dogBiscuits.text().then(function(text) {
+ poemDisplay.textContent = text;
+ });
+});</pre>
+
+<p>但是把参数叫做描述其内容的名字更有意义。</p>
+
+<p>现在让我们来单独看一下函数:</p>
+
+<pre class="brush: js">function(response) {
+ response.text().then(function(text) {
+ poemDisplay.textContent = text;
+ });
+}</pre>
+
+<p>response 对象有个 <a href="https://developer.mozilla.org/en-US/docs/Web/API/Body/text" title="The text() method of the Body mixin takes a Response stream and reads it to completion. It returns a promise that resolves with a USVString object (text). The response is always decoded using UTF-8."><code>text()</code></a>方法, 获取响应主体中的原始数据a并把它转换成纯文本, 那是我们想要的格式。它也返回一个promise (解析结果文本字符串), 所以这里我们再使用 <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then" title="The then() method returns a Promise. It takes up to two arguments: callback functions for the success and failure cases of the Promise."><code>.then()</code></a>, 在里面我们再定义一个操作文本字符串的函数。我们设置诗歌的 <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/pre" title="The HTML &lt;pre> element represents preformatted text which is to be presented exactly as written in the HTML file."><code>&lt;pre&gt;</code></a> 元素的 <code><a href="https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent">textContent</a></code> 属性和这个文本字符串相同, 这样就非常简单地解决了。</p>
+
+<p>值得注意的是你可以直接将promise块 (<code>.then()</code>块, 但也有其他类型) 链接到另一个的尾部, 顺着链条将每个块的结果传到下一个块。 这使得promises非常强大。</p>
+
+<p>下面的代码块和我们原始的例子做的是相同的事, 但它是不同的写法:</p>
+
+<pre class="brush: js">fetch(url).then(function(response) {
+ return response.text()
+}).then(function(text) {
+ poemDisplay.textContent = text;
+});</pre>
+
+<p>很多开发者更喜欢这种样式, 因为它更扁平并且按理说对于更长的promise链它更容易读 — 每一个promise(承诺)接续上一个promise,而不是在上一个promise的里面(会使得整个代码笨重起来,难以理解)。以上两种写法还有一个不同的地方是我们在<code>response.text()</code>  语句之前得包含一个 <code><a href="/en-US/docs/Learn/JavaScript/Building_blocks/Return_values">return</a></code>  语句, 用来把这一部分的结果传向promise链的下一段。</p>
+
+<h3 id="你应该用哪种方法呢">你应该用哪种方法呢?</h3>
+
+<p>  这完全取决于你正在干的项目是啥样。XHR已经面世非常之久,现在已经有了相当棒的跨浏览器支持。然而对于网页平台来说,Fetch和Promise是新近的产物,除了IE和Safari浏览器不支持,别的浏览器大多提供了支持。(现在Safari也即将为fetch和promise提供支持)。</p>
+
+<p>  如果你的项目需要支持年代久远的浏览器,那么使用XHR可能会更爽一些。如果你的项目比较激进而且你根本不管老版的浏览器吃不吃这套,那就选择Fetch吧老铁。</p>
+
+<p>  话说回来,咱倒真应该两者都学学——因为使用IE浏览器的人们在变少,Fetch会变得越来越流行(事实上IE已经没人管了,因为微软Edge浏览器的受宠),但在所有浏览器彻底支持Fetch之前,你可能还得和XHR纠缠一阵子。</p>
+
+<h2 id="一个更复杂的示例">一个更复杂的示例</h2>
+
+<p>为了使本文更详尽,我们将看一个稍微复杂一点的示例,它展示了Fetch的一些更有趣的用法。我们创建了一个名为Can Store的示例站点——它是一个虚构的超市,只出售罐头食品。你可以找到 <a href="https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store/">example live on GitHub</a>,并且 <a href="https://github.com/mdn/learning-area/tree/master/javascript/apis/fetching-data/can-store">see the source code</a> 。</p>
+
+<p><img alt="A fake ecommerce site showing search options in the left hand column, and product search results in the right hand column." src="https://mdn.mozillademos.org/files/14779/can-store.png" style="display: block; margin: 0 auto;"></p>
+
+<p>默认情况下,站点会显示所有产品,但您可以使用左手列中的表单控件按类别或搜索词或两者进行筛选。</p>
+
+<p>有很多复杂的代码处理按类别和搜索词过滤产品、操作字符串以便数据在UI中正确显示等等。我们不会在本文中讨论所有这些,但是您可以在代码中找到大量的注释 (see <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/can-store/can-script.js">can-script.js</a>)。</p>
+
+<p>但是,我们会解释Fetch代码的含义。</p>
+
+<p>第一个使用Fetch的块可以在JavaScript的开头找到:</p>
+
+<pre class="brush: js">fetch('products.json').then(function(response) {
+ if(response.ok) {
+ response.json().then(function(json) {
+ products = json;
+ initialize();
+ });
+ } else {
+ console.log('Network request for products.json failed with response ' + response.status + ': ' + response.statusText);
+ }
+});</pre>
+
+<p>这看起来和我们之前看到的相似,只是第二个promise是在一个条件语句中。在条件下,我们检查返回的response是否成功 —  {{domxref("response.ok")}} 属性包含一个布尔变量,如果response是成功的 (e.g. <a href="/en-US/docs/Web/HTTP/Status/200">200 meaning "OK"</a>),则是<code>true</code>;如果失败了,则是<code>false</code>。</p>
+
+<p>如果response成功,我们运行第二个promise — 然而,这次我们使用 {{domxref("Body.json","json()")}},而不是{{domxref("Body.text","text()")}}, 因为我们想要response返回的是一个结构化的JSON数据,而不是纯文本。</p>
+
+<p>如果response失败,我们将输出一个错误到控制台,指出网络请求失败,该控制台将报告响应的网络状态和描述性消息(分别包含在{{domxref("response.status")}}和{{domxref("response.statusText")}}属性中)。 当然,一个完整的web站点可以通过在用户的屏幕上显示一条消息来更优雅地处理这个错误,也许还可以提供一些选项来补救这种情况。</p>
+
+<p>您可以自己测试失败案例:</p>
+
+<ol>
+ <li>制作示例文件的本地副本(下载并解压<a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/can-store/can-store.zip?raw=true">the can-store ZIP file</a>)</li>
+ <li>通过web服务器运行代码(如上所述,在 {{anch("Serving your example from a server")}})</li>
+ <li>修改要获取的文件的路径,比如“produc.json'(确保你拼写的是错误的)</li>
+ <li>现在在你的浏览器上加载索引文件 (通过 <code>localhost:8000</code>) 然后查看你的开发者控制台。 你将看到一条类似于“网络请求products.json失败,404:找不到文件”的消息</li>
+</ol>
+
+<p>第二个Fetch块可以在<code>fetchBlob()</code> 找到:</p>
+
+<pre class="brush: js">fetch(url).then(function(response) {
+ if(response.ok) {
+ response.blob().then(function(blob) {
+ objectURL = URL.createObjectURL(blob);
+ showProduct(objectURL, product);
+ });
+ } else {
+ console.log('Network request for "' + product.name + '" image failed with response ' + response.status + ': ' + response.statusText);
+ }
+});</pre>
+
+<p>它的工作原理和前一个差不多, 除了我们放弃{{domxref("Body.json","json()")}}而使用{{domxref("Body.blob","blob()")}} — 在本例中,我们希望以图像文件的形式返回响应,为此使用的数据格式是<a href="/en-US/docs/Web/API/Blob">Blob</a> — 这个词是“二进制大对象”的缩写,基本上可以用来表示大型文件类对象——比如图像或视频文件。</p>
+
+<p>一旦我们成功地接收到我们的blob,我们就会使用它创建一个对象URL {{domxref("URL.createObjectURL()", "createObjectURL()")}}. 它返回一个指向浏览器中引用的对象的临时内部URL。这些不是很容易读懂,但是你可以通过打开Can Store看到,按Ctrl-/右键单击一个图像,然后选择“View Image(查看图像)”选项(根据您使用的浏览器可能略有不同)。 对象URL将在地址栏中可见,应该是这样的:</p>
+
+<pre>blob:http://localhost:7800/9b75250e-5279-e249-884f-d03eb1fd84f4</pre>
+
+<h3 id="挑战:一个XHR版本的Can_Store">挑战:一个XHR版本的Can Store</h3>
+
+<p>我们希望您能尝试将Fetch版本转换为使用XHR,作为一个有用的实践。下载一份 <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/can-store/can-store.zip?raw=true">copy of the ZIP file</a>, 并适当修改下JavaScript。</p>
+
+<p>一些有用的提示:</p>
+
+<ul>
+ <li>你可能会发现 {{domxref("XMLHttpRequest")}} 作参考材料非常有用。</li>
+ <li>你基本上需要使用和你在前面的文章中看到的<a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/xhr-basic.html">XHR-basic.html</a>例子相同的模式。</li>
+ <li>但是,您将需要添加我们在Can Store的Fetch版本中显示的错误处理:
+ <ul>
+ <li>在<code>load</code>事件完毕后,response被用于<code>request.response</code>而不是promise的<code>then()</code>。</li>
+ <li>Fetch的 <code>response.ok</code>在XHR中的最佳等效为检查 {{domxref("XMLHttpRequest.status","request.status")}} 是否等于200或者 {{domxref("XMLHttpRequest.readyState","request.readyState")}} 是否等于4。</li>
+ <li>获取状态和状态信息的内容是一样的, 但是它们在 <code>request</code> (XHR) 对象,而不是<code>response</code> 对象。</li>
+ </ul>
+ </li>
+</ul>
+
+<div class="note">
+<p><strong>注意</strong>: 如果您在这方面有问题,请到Github将您的代码和完成的版本进行对比 ( <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/can-store-xhr/can-script.js">see the source here</a>, see also <a href="https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store-xhr/">see it running live</a> )。</p>
+</div>
+
+<h2 id="概述">概述</h2>
+
+<p>我们这篇关于从服务器那儿抓取数据的文章就到此为止了。现在你应该对如何使用XHR和Fetch有一些了解了吧?</p>
+
+<h2 id="请参阅">请参阅</h2>
+
+<p>虽然本文中讨论了许多不同的主题,但是这些主题仅仅只是触及了表面。有关这些主题的更多详细信息,请尝试以下文章:</p>
+
+<ul>
+ <li><a href="/en-US/docs/AJAX/Getting_Started">Ajax — Getting started</a></li>
+ <li><a href="/en-US/docs/Web/API/Fetch_API/Using_Fetch">Using Fetch</a></li>
+ <li><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise">Promises</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Objects/JSON">Working with JSON data</a></li>
+ <li><a href="/en-US/docs/Web/HTTP/Overview">An overview of HTTP</a></li>
+ <li><a href="/en-US/docs/Learn/Server-side">Server-side website programming</a></li>
+ <li>
+ <p><a href="https://jsperf.com/xhr-vs-jquery-ajax-vs-get-vs-fetch">https://jsperf.com/xhr-vs-jquery-ajax-vs-get-vs-fetch</a></p>
+ </li>
+ <li>
+ <p><a href="https://xhr.spec.whatwg.org/#example-xhr">https://xhr.spec.whatwg.org/#example-xhr</a></p>
+ </li>
+</ul>
+
+<div>{{PreviousMenuNext("Learn/JavaScript/Client-side_web_APIs/Manipulating_documents", "Learn/JavaScript/Client-side_web_APIs/Third_party_APIs", "Learn/JavaScript/Client-side_web_APIs")}}</div>
+
+<div></div>
+
+<h2 id="模块大纲">模块大纲</h2>
+
+<ul>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Introduction">介绍web APIs</a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Manipulating_documents">操作DOM documents</a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Fetching_data">从服务器获取数据</a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Third_party_APIs">第三方APIs</a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Drawing_graphics">绘图</a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Video_and_audio_APIs">视频音频APIs</a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Client-side_storage">客户端存储</a></li>
+</ul>
diff --git a/files/zh-cn/learn/javascript/client-side_web_apis/index.html b/files/zh-cn/learn/javascript/client-side_web_apis/index.html
new file mode 100644
index 0000000000..30bec1c50f
--- /dev/null
+++ b/files/zh-cn/learn/javascript/client-side_web_apis/index.html
@@ -0,0 +1,47 @@
+---
+title: 客户端 Web API
+slug: Learn/JavaScript/Client-side_web_APIs
+tags:
+ - API(应用程序编程接口)
+ - DOM(文档对象模型)
+ - JavaScript
+ - WebAPI(网页接口)
+ - 初学者
+ - 图形学
+ - 数据
+ - 文章
+ - 脚本编程
+translation_of: Learn/JavaScript/Client-side_web_APIs
+---
+<div>{{LearnSidebar}}</div>
+
+<p class="summary">当你给网页或者网页应用编写客户端的JavaScript时, 你很快会遇上应用程序接口(API )—— 这些编程特性可用来操控网站所基于的浏览器与操作系统的不同方面,或是操控由其他网站或服务端传来的数据。在这个单元里,我们将一同探索什么是API,以及如何使用一些在你开发中将经常遇见的API。</p>
+
+<h2 id="预备知识">预备知识</h2>
+
+<p>若想深入理解这个单元的内容, 你必须能够以自己的能力较好地学完之前的几个章节 (<a href="/zh-CN/docs/Learn/JavaScript/First_steps">JavaScript第一步</a>, <a href="/zh-CN/docs/Learn/JavaScript/First_steps">JavaScript</a><a href="/zh-CN/docs/Learn/JavaScript/Building_blocks">基础要件</a>, 和<a href="/zh-CN/docs/Learn/JavaScript/First_steps">JavaScript</a><a href="/zh-CN/docs/Learn/JavaScript/Objects">对象介绍</a>). 这几部分涉及到了许多简单的API的使用, 如果没有它们我们将很难做一些实际的事情。在这个教程中,我们会认为你懂得JavaScript的核心知识,而且我们将更深入地探索常见的网页API。</p>
+
+<p>若你知道 <a href="https://developer.mozilla.org/en-US/docs/Learn/HTML">HTML</a> 和 <a href="https://developer.mozilla.org/en-US/docs/Learn/CSS">CSS</a> 的基本知识,也会对理解这个单元的内容大有裨益。</p>
+
+<div class="note">
+<p>注意:如果你正在使用一台无法创建你自身文件的电脑、平板或其他设备,你可以尝试使用一些在线网页编辑器如<a href="http://jsbin.com/">JSBin</a>或者<a href="https://thimble.mozilla.org/">Thimble</a>,来尝试编辑一些代码示例。</p>
+</div>
+
+<h2 id="向导">向导</h2>
+
+<dl>
+ <dt><a href="/zh-CN/docs/Learn/JavaScript/Client-side_web_APIs/Introduction">Web API简介</a></dt>
+ <dd>首先, 我们将从一个更高的角度来看这些API —它们是什么,它们怎么起作用的,你该怎么在自己的代码中使用它们以及他们是怎么构成的? 我们依旧会再来看一看这些API有哪些主要的种类和他们会有哪些用处。</dd>
+ <dt><a href="/zh-CN/docs/Learn/JavaScript/Client-side_web_APIs/Manipulating_documents">操作文档</a></dt>
+ <dd>当你在制作WEB页面和APP时,一个你最经常想要做的事就是通过一些方法来操作WEB文档。这其中最常见的方法就是使用文档对象模型Document Object Model (DOM),它是一系列大量使用了 {{domxref("Document")}} object的API来控制HTML和样式信息。通过这篇文章,我们来看看使用DOM方面的一些细节, 以及其他一些有趣的API能够通过一些有趣的方式改变你的环境。</dd>
+ <dt><a href="https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Client-side_web_APIs/Fetching_data">从服务器获取数据</a></dt>
+ <dd>在现代网页及其APP中另外一个很常见的任务就是与服务器进行数据交互时不再刷新整个页面,这看起来微不足道,但却对一个网页的展现和交互上起到了很大的作用,在这篇文章里,我们将阐述这个概念,然后来了解实现这个功能的技术,例如 {{domxref("XMLHttpRequest")}} 和 <a href="/en-US/docs/Web/API/Fetch_API">Fetch API</a>.(抓取API)。</dd>
+ <dt><a href="/zh-CN/docs/Learn/JavaScript/Client-side_web_APIs/Third_party_APIs">第三方 API</a></dt>
+ <dd>到目前为止我们所涉及的API都是浏览器内置的,但并不代表所有。许多大网站如Google Maps, Twitter, Facebook, PayPal等,都提供他们的API给开发者们去使用他们的数据(比如在你的博客里展示你分享的推特内容)或者服务(如在你的网页里展示定制的谷歌地图或接入Facebook登录功能)。这篇文章介绍了浏览器API和第三方API 的差别以及一些最新的典型应用。</dd>
+ <dt><a href="/zh-CN/docs/Learn/JavaScript/Client-side_web_APIs/Drawing_graphics">绘制图形</a></dt>
+ <dd>浏览器包含多种强大的图形编程工具,从可缩放矢量图形语言Scalable Vector Graphics (<a href="/zh-CN/docs/Web/SVG">SVG</a>) language,到HTML绘制元素 {{htmlelement("canvas")}} 元素(<a href="/zh-CN/docs/Web/API/Canvas_API">The Canvas API</a> and <a href="/zh-CN/docs/Web/API/WebGL_API">WebGL</a>). 这篇文章提供了部分canvas的简介,以及让你更深入学习的资源。</dd>
+ <dt><a href="/zh-CN/docs/Learn/JavaScript/Client-side_web_APIs/Video_and_audio_APIs">视频和音频 API</a></dt>
+ <dd>HTML5能够通过元素标签嵌入富媒体——{{htmlelement("video")}} and {{htmlelement("audio")}}——而将有自己的API来控制回放,搜索等功能。本文向您展示了如何创建自定义播放控制等常见的任务。</dd>
+ <dt><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Client-side_storage">客户端存储</a></dt>
+ <dd>现代web浏览器拥有很多不同的技术,能够让你存储与网站相关的数据,并在需要时调用它们,能够让你长期保存数据、保存离线网站及其他实现其他功能。本文解释了这些功能的基本原理。</dd>
+</dl>
diff --git a/files/zh-cn/learn/javascript/client-side_web_apis/introduction/index.html b/files/zh-cn/learn/javascript/client-side_web_apis/introduction/index.html
new file mode 100644
index 0000000000..f549f9919d
--- /dev/null
+++ b/files/zh-cn/learn/javascript/client-side_web_apis/introduction/index.html
@@ -0,0 +1,275 @@
+---
+title: Web API简介
+slug: Learn/JavaScript/Client-side_web_APIs/Introduction
+tags:
+ - API
+ - CodingScripting
+ - WebAPI
+ - 初学者
+ - 学习
+ - 客户机端
+ - 对象
+ - 文章
+ - 浏览器
+ - 第三方
+translation_of: Learn/JavaScript/Client-side_web_APIs/Introduction
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{NextMenu("Learn/JavaScript/Client-side_Web_APIs/Manipulating_documents", "Learn/JavaScript/Client-side_Web_API")}}</div>
+
+<p class="summary">首先,我们将从一个高层次看看API - 它们是什么;他们如何工作;如何在代码中使用它们,以及它们是如何组织的。我们也将看看不同主要类别的API以及它们的用途。</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">预备知识</th>
+ <td>基本计算机知识,对于HTML和CSS的基本理解(见<a href="https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/First_steps">JavaScript 第一步</a>,<a href="/zh-CN/docs/learn/JavaScript/Building_blocks">创建JavaScript代码块</a>,<a href="/zh-CN/docs/Learn/JavaScript/Objects">JavaScript 对象入门</a>)。</td>
+ </tr>
+ <tr>
+ <th scope="row">目标</th>
+ <td>熟悉API,他们可以做什么,以及如何在代码中使用它们。</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="什么是API">什么是API?</h2>
+
+<p>应用程序接口(API)是基于编程语言构建的结构,使开发人员更容易地创建复杂的功能。它们抽象了复杂的代码,并提供一些简单的接口规则直接使用。</p>
+
+<p>来看一个现实中的例子:想想您的房子、公寓或其他住宅的供电方式,如果您想在您的房子里用电,只要把电器的插头插入插座就可以,而不是直接把它连接到电线上——这样做非常低效,而且对于不是电工的人会是困难和危险的。</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/14317/plug-socket.png" style="display: block; height: 472px; margin: 0px auto; width: 700px;"></p>
+
+<p><em>图片来自:<a href="https://www.flickr.com/photos/easy-pics/9518184890/in/photostream/lightbox/">Overloaded plug socket</a> 提供者: <a href="https://www.flickr.com/photos/easy-pics/">The Clear Communication People</a>于Flickr。</em></p>
+
+<p>同样,比如说,编程来显示一些3D图形,使用以更高级语言编写的API(例如JavaScript或Python)将会比直接编写直接控制计算机的GPU或其他图形功能的低级代码(比如C或C++)来执行操作要容易得多。</p>
+
+<div class="note">
+<p><strong>注:</strong>详细说明请见<a href="https://developer.mozilla.org/en-US/docs/Glossary/API">API - Glossary</a>。</p>
+</div>
+
+<h3 id="客户端JavaScript中的API">客户端JavaScript中的API</h3>
+
+<p>客户端JavaScript中有很多可用的API — 他们本身并不是JavaScript语言的一部分,却建立在JavaScript语言核心的顶部,为使用JavaScript代码提供额外的超强能力。他们通常分为两类:</p>
+
+<ul>
+ <li><strong>浏览器API</strong>内置于Web浏览器中,能从浏览器和电脑周边环境中提取数据,并用来做有用的复杂的事情 。例如<a href="/en-US/docs/Web/API/Geolocation/Using_geolocation">Geolocation API</a>提供了一些简单的JavaScript结构以获得位置数据,因此您可以在Google地图上标示您的位置。在后台,浏览器确实使用一些复杂的低级代码(例如C++)与设备的GPS硬件(或可以决定位置数据的任何设施)通信来获取位置数据并把这些数据返回给您的代码中使用浏览器环境;但是,这种复杂性通过API抽象出来,因而与您无关。</li>
+ <li><strong>第三方API</strong>缺省情况下不会内置于浏览器中,通常必须在Web中的某个地方获取代码和信息。例如<a href="https://dev.twitter.com/overview/documentation">Twitter API</a> 使您能做一些显示最新推文这样的事情,它提供一系列特殊的结构,可以用来请求Twitter服务并返回特殊的信息。</li>
+</ul>
+
+
+
+
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/13508/browser.png" style="display: block; height: 511px; margin: 0px auto; width: 815px;"></p>
+
+
+
+<h3 id="JavaScript,API和其他JavaScript工具之间的关系">JavaScript,API和其他JavaScript工具之间的关系</h3>
+
+<p>如上所述,我们讨论了什么是客户端JavaScript API,以及它们与JavaScript语言的关系。让我们回顾一下,使其更清晰,并提及其他JavaScript工具的适用位置:</p>
+
+<ul>
+ <li>JavaScript — 一种内置于浏览器的高级脚本语言,您可以用来实现Web页面/应用中的功能。注意JavaScript也可用于其他象<a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Introduction">Node</a>这样的的编程环境。但现在您不必考虑这些。</li>
+ <li>客户端API — 内置于浏览器的结构程序,位于JavaScript语言顶部,使您可以更容易的实现功能。</li>
+ <li>第三方API — 置于第三方普通的结构程序(例如Twitter,Facebook),使您可以在自己的Web页面中使用那些平台的某些功能(例如在您的Web页面显示最新的Tweets)。</li>
+ <li>JavaScript库 — 通常是包含具有<a href="/en-US/docs/Learn/JavaScript/Building_blocks/Functions#Custom_functions">特定功能</a>的一个或多个JavaScript文件,把这些文件关联到您的Web页以快速或授权编写常见的功能。例如包含jQuery和Mootools</li>
+ <li>JavaScript框架 — 从库开始的下一步,JavaScript框架视图把HTML、CSS、JavaScript和其他安装的技术打包在一起,然后用来从头编写一个完整的Web应用。</li>
+</ul>
+
+<h2 id="API可以做什么?">API可以做什么?</h2>
+
+<p>在主流浏览器中有大量的可用API,您可以在代码中做许多的事情,对此可以查看<a href="https://developer.mozilla.org/en-US/docs/Web/API">MDN API index page</a>。</p>
+
+<h3 id="常见浏览器API">常见浏览器API</h3>
+
+<p>特别地,您将使用的最常见的浏览器API类别(以及我们将更详细地介绍的)是:</p>
+
+<ul>
+ <li><strong>操作文档的API</strong>内置于浏览器中。最明显的例子是<a href="https://developer.mozilla.org/zh-CN/docs/Web/API/Document_Object_Model">DOM(文档对象模型)</a>API,它允许您操作HTML和CSS — 创建、移除以及修改HTML,动态地将新样式应用到您的页面,等等。每当您看到一个弹出窗口出现在一个页面上,或者显示一些新的内容时,这都是DOM的行为。 您可以在在<a href="https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Client-side_web_APIs/Manipulating_documents">Manipulating documents</a>中找到关于这些类型的API的更多信息。</li>
+ <li><strong>从服务器获取数据的API </strong>用于更新网页的一小部分是相当好用的。这个看似很小的细节能对网站的性能和行为产生巨大的影响 — 如果您只是更新一个股票列表或者一些可用的新故事而不需要从服务器重新加载整个页面将使网站或应用程序感觉更加敏感和“活泼”。使这成为可能的API包括<a href="https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest" title="XMLHttpRequest is an API that provides client functionality for transferring data between a client and a server. It provides an easy way to retrieve data from a URL without having to do a full page refresh. This enables a Web page to update just a part of the page without disrupting what the user is doing."><code>XMLHttpRequest</code></a>和<a href="https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API">Fetch API</a>。您也可能会遇到描述这种技术的术语<strong>Ajax</strong>。您可以在<a href="/zh-CN/docs/Learn/JavaScript/Client-side_web_APIs/Fetching_data">Fetching data from the server</a>找到关于类似的API的更多信息。</li>
+ <li><strong>用于绘制和操作图形的API</strong>目前已被浏览器广泛支持 — 最流行的是允许您以编程方式更新包含在HTML {{htmlelement("canvas")}} 元素中的像素数据以创建2D和3D场景的<a href="https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API">Canvas</a>和<a href="https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API">WebGL</a>。例如,您可以绘制矩形或圆形等形状,将图像导入到画布上,然后使用Canvas API对其应用滤镜(如棕褐色滤镜或灰度滤镜),或使用WebGL创建具有光照和纹理的复杂3D场景。这些API经常与用于创建动画循环的API(例如{{domxref("window.requestAnimationFrame()")}})和其他API一起不断更新诸如动画和游戏之类的场景。</li>
+ <li><strong>音频和视频API</strong>例如{{domxref("HTMLMediaElement")}},<a href="/zh-CN/docs/Web/API/Web_Audio_API">Web Audio API</a>和<a href="/zh-CN/docs/MDN/Doc_status/API/WebRTC">WebRTC</a>允许您使用多媒体来做一些非常有趣的事情,比如创建用于播放音频和视频的自定义UI控件,显示字幕字幕和您的视频,从网络摄像机抓取视频,通过画布操纵(见上),或在网络会议中显示在别人的电脑上,或者添加效果到音轨(如增益,失真,平移等) 。</li>
+ <li><strong>设备API</strong>基本上是以对网络应用程序有用的方式操作和检索现代设备硬件中的数据的API。我们已经讨论过访问设备位置数据的地理定位API,因此您可以在地图上标注您的位置。其他示例还包括通过系统通知(参见<a href="/zh-CN/docs/Web/API/Notifications_API">Notifications API</a>)或振动硬件(参见<a href="/zh-CN/docs/Web/API/Vibration_API">Vibration API</a>)告诉用户Web应用程序有用的更新可用。</li>
+ <li><strong>客户端存储API</strong>在Web浏览器中的使用变得越来越普遍 - 如果您想创建一个应用程序来保存页面加载之间的状态,甚至让设备在处于脱机状态时可用,那么在客户端存储数据将会是非常有用的。例如使用<a href="/zh-CN/docs/Web/API/Web_Storage_API">Web Storage API</a>的简单的键 - 值存储以及使用<a href="/zh-CN/docs/Web/API/IndexedDB_API">IndexedDB API</a>的更复杂的表格数据存储。</li>
+</ul>
+
+<h3 id="常见第三方API">常见第三方API</h3>
+
+<p>第三方API种类繁多; 下列是一些比较流行的你可能迟早会用到的第三方API:</p>
+
+<ul>
+ <li>The <a href="https://dev.twitter.com/overview/documentation">Twitter API</a>, 允许您在您的网站上展示您最近的推文等。</li>
+ <li>The <a href="https://developers.google.com/maps/">Google Maps API</a> 允许你在网页上对地图进行很多操作(这很有趣,它也是Google地图的驱动器)。现在它是一整套完整的,能够胜任广泛任务的API。其能力已经被<a href="https://developers.google.com/maps/documentation/api-picker">Google Maps API Picker</a>见证。</li>
+ <li>The <a href="https://developers.facebook.com/docs/">Facebook suite of API</a> 允许你将很多Facebook生态系统中的功能应用到你的app,使之受益,比如说它提供了通过Facebook账户登录、接受应用内支付、推送有针对性的广告活动等功能。</li>
+ <li>The <a href="https://developers.google.com/youtube/">YouTube API</a>, 允许你将Youtube上的视频嵌入到网站中去,同时提供搜索Youtube,创建播放列表等众多功能。</li>
+ <li>The <a href="https://www.twilio.com/">Twilio API</a>, 其为您的app提供了针对语音通话和视频聊天的框架,以及从您的app发送短信息或多媒体信息等诸多功能。</li>
+</ul>
+
+<div class="note">
+<p><strong>注</strong>: 你可以在 <a href="http://www.programmableweb.com/category/all/apis">Programmable Web API directory</a>.上发现更多关于第三方API的信息。</p>
+</div>
+
+<h2 id="API如何工作?">API如何工作?</h2>
+
+<p>不同的JavaScript API以稍微不同的方式工作,但通常它们具有共同的特征和相似的主题。</p>
+
+<h3 id="它们是基于对象的">它们是基于对象的</h3>
+
+<p> API使用一个或多个 <a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects">JavaScript objects</a> 在您的代码中进行交互,这些对象用作API使用的数据(包含在对象属性中)的容器以及API提供的功能(包含在对象方法中)。</p>
+
+<div class="note">
+<p> 注意:如果您不熟悉对象如何工作,则应继续学习 <a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects">JavaScript objects</a> 模块。</p>
+</div>
+
+<p>让我们回到Geolocation API的例子 - 这是一个非常简单的API,由几个简单的对象组成:</p>
+
+<ul>
+ <li>{{domxref("Geolocation")}}, 其中包含三种控制地理数据检索的方法</li>
+ <li>{{domxref("Position")}}, 表示在给定的时间的相关设备的位置。 — 它包含一个当前位置的 {{domxref("Coordinates")}} 对象。还包含了一个时间戳,这个时间戳表示获取到位置的时间。</li>
+ <li>{{domxref("Coordinates")}}, 其中包含有关设备位置的大量有用数据,包括经纬度,高度,运动速度和运动方向等。</li>
+</ul>
+
+<pre class="brush: js notranslate">navigator.geolocation.getCurrentPosition(function(position) {
+ var latlng = new google.maps.LatLng(position.coords.latitude,position.coords.longitude);
+ var myOptions = {
+ zoom: 8,
+ center: latlng,
+ mapTypeId: google.maps.MapTypeId.TERRAIN,
+ disableDefaultUI: true
+ }
+ var map = new google.maps.Map(document.querySelector("#map_canvas"), myOptions);
+});</pre>
+
+<div class="note">
+<p><strong>Note</strong>: 当您第一次加载上述实例,应当出现一个对话框询问您是否乐意对此应用共享位置信息(参见 {{anch("They have additional security mechanisms where appropriate")}} 这一稍后将会提到的部分)。 您需要同意这项询问以将您的位置于地图上绘制。如果您始终无法看见地图,您可能需要手动修改许可项。修改许可项的方法取决于您使用何种浏览器,对于Firefox浏览器来说,在页面信息 &gt; 权限 中修改位置权限,在Chrome浏览器中则进入 设置 &gt; 隐私 &gt; 显示高级设置 &gt; 内容设置,其后修改位置设定。</p>
+</div>
+
+<p>我们首先要使用 {{domxref("Geolocation.getCurrentPosition()")}} <font>方法返回设备的当前位置。</font><font>浏览器的 </font>{{domxref("Geolocation")}} 对象通过调用 {{domxref("Navigator.geolocation")}} <font>属性</font><font>来访问.</font></p>
+
+<pre class="brush: js notranslate">navigator.geolocation.getCurrentPosition(function(position) { ... });</pre>
+
+<p>这相当于做同样的事情</p>
+
+<pre class="brush: js notranslate">var myGeo = navigator.geolocation;
+myGeo.getCurrentPosition(function(position) { ... });</pre>
+
+<p>但是我们可以使用 "点运算符" 将我们的属性和方法的访问链接在一起,减少了我们必须写的行数。</p>
+
+<p>{{domxref("Geolocation.getCurrentPosition()")}} 方法只有一个必须的参数,这个参数是一个匿名函数,当设备的当前位置被成功取到时,这个函数会运行。  这个函数本身有一个参数,它包含一个表示当前位置数据的 {{domxref("Position")}} 对象。</p>
+
+<div class="note">
+<p><strong>注意:由另一个函数作为参数的函数称为</strong> (<a href="https://developer.mozilla.org/en-US/docs/Glossary/Callback_function">callback function</a> "回调函数").</p>
+</div>
+
+<p>仅在操作完成时调用函数的模式在JavaScript API中非常常见 - 确保一个操作已经完成,然后在另一个操作中尝试使用该操作返回的数据。这些被称为 <strong><a href="https://developer.mozilla.org/en-US/docs/Glossary/Asynchronous">asynchronous</a> </strong> “异步”操作。由于获取设备的当前位置依赖于外部组件(设备的GPS或其他地理定位硬件), 我们不能保证会立即使用返回的数据。 因此,这样子是行不通的:</p>
+
+<pre class="brush: js notranslate">var position = navigator.geolocation.getCurrentPosition();
+var myLatitude = position.coords.latitude;</pre>
+
+<p>如果第一行还没有返回结果,则第二行将会出现错误,因为位置数据还不可用。 出于这个原因,涉及同步操作的API被设计为使用 {{glossary("callback function")}}s “回调函数”,或更现代的 <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise">Promises</a> 系统,这些系统在ECMAScript 6中可用,并被广泛用于较新的API。</p>
+
+<p>我们将Geolocation API与第三方API(Google Maps API)相结合, — 我们正在使用它来绘制Google地图上由 <code>getCurrentPosition()</code>返回的位置。 我们通过链接到页面上使这个API可用。 — 你会在HTML中找到这一行:</p>
+
+<pre class="brush: html notranslate">&lt;script type="text/javascript" src="https://maps.google.com/maps/API/js?key=AIzaSyDDuGt0E5IEGkcE6ZfrKfUtE9Ko_de66pA"&gt;&lt;/script&gt;</pre>
+
+<p>要使用该API, 我们首先使用<code>google.maps.LatLng()</code>构造函数创建一个<code>LatLng</code>对象实例, 该构造函数需要我们的地理定位 {{domxref("Coordinates.latitude")}} 和 {{domxref("Coordinates.longitude")}}值作为参数:</p>
+
+<pre class="brush: js notranslate">var latlng = new google.maps.LatLng(position.coords.latitude,position.coords.longitude);</pre>
+
+<p>该对象实例被设置为 <code>myOptions</code>对象的<code>center</code>属性的值。然后我们通过调用<code>google.maps.Map()</code>构造函数创建一个对象实例来表示我们的地图, 并传递它两个参数 — 一个参数是我们要渲染地图的 {{htmlelement("div")}} 元素的引用 (ID为 <code>map_canvas</code>), 以及另一个参数是我们在上面定义的<code>myOptions</code>对象</p>
+
+<pre class="brush: js notranslate">var myOptions = {
+ zoom: 8,
+ center: latlng,
+ mapTypeId: google.maps.MapTypeId.TERRAIN,
+ disableDefaultUI: true
+}
+
+var map = new google.maps.Map(document.querySelector("#map_canvas"), myOptions);</pre>
+
+<p>这样做一来,我们的地图呈现了。</p>
+
+<p>最后一块代码突出显示了您将在许多API中看到的两种常见模式。 首先,API对象通常包含构造函数,可以调用这些构造函数来创建用于编写程序的对象的实例。 其次,API对象通常有几个可用的options(如上面的<code>myOptions</code>对象),可以调整以获得您的程序所需的确切环境(根据不同的环境,编写不同的<code>Options</code>对象)。 API构造函数通常接受options对象作为参数,这是您设置这些options的地方。</p>
+
+<div class="note">
+<p><strong>注意:如果您不能立即理解这个例子的细节,请不要担心。 我们将在未来的文章中详细介绍第三方API。</strong></p>
+</div>
+
+<h3 id="它们有可识别的入口点">它们有可识别的入口点</h3>
+
+<p>使用API时,应确保知道API入口点的位置。 在Geolocation API中,这非常简单 - 它是 {{domxref("Navigator.geolocation")}} 属性, 它返回浏览器的 {{domxref("Geolocation")}} 对象,所有有用的地理定位方法都可用。</p>
+
+<p>文档对象模型 (DOM) API有一个更简单的入口点 —它的功能往往被发现挂在 {{domxref("Document")}} 对象, 或任何你想影响的HTML元素的实例,例如:</p>
+
+<pre class="brush: js notranslate">var em = document.createElement('em'); // create a new em element
+var para = document.querySelector('p'); // reference an existing p element
+em.textContent = 'Hello there!'; // give em some text content
+para.appendChild(em); // embed em inside para</pre>
+
+<p>其他API具有稍微复杂的入口点,通常涉及为要编写的API代码创建特定的上下文。例如,Canvas API的上下文对象是通过获取要绘制的 {{htmlelement("canvas")}} 元素的引用来创建的,然后调用它的{{domxref("HTMLCanvasElement.getContext()")}}方法:</p>
+
+<pre class="brush: js notranslate">var canvas = document.querySelector('canvas');
+var ctx = canvas.getContext('2d');</pre>
+
+<p>然后,我们想通过调用内容对象 (它是{{domxref("CanvasRenderingContext2D")}}的一个实例)的属性和方法来实现我们想要对画布进行的任何操作, 例如:</p>
+
+<pre class="brush: js notranslate">Ball.prototype.draw = function() {
+ ctx.beginPath();
+ ctx.fillStyle = this.color;
+ ctx.arc(this.x, this.y, this.size, 0, 2 * Math.PI);
+ ctx.fill();
+};</pre>
+
+<div class="note">
+<p><strong><font><font>注意</font></font></strong><font><font>:您可以在我们的</font></font><a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/introduction/bouncing-balls.html" rel="noopener"><font><font>弹跳球演示中</font></font></a><font><font>看到此代码的实际  </font></font><a href="http://mdn.github.io/learning-area/javascript/apis/introduction/bouncing-balls.html" rel="noopener"><font><font>运行情况</font></font></a><font><font>  (</font><font> 也可以</font><font>参阅它  </font><a href="http://mdn.github.io/learning-area/javascript/apis/introduction/bouncing-balls.html" rel="noopener"><font>现场运行</font></a><font>)。</font></font></p>
+</div>
+
+<h3 id="它们使用事件来处理状态的变化">它们使用事件来处理状态的变化</h3>
+
+<p><font><font>我们之前已经在课程中讨论了事件,在我们的  </font></font><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events"><font><font>事件介绍</font></font></a><font><font>文章中 - 详细介绍了客户端Web事件是什么以及它们在代码中的用法。</font><font>如果您还不熟悉客户端Web API事件的工作方式,则应继续阅读。</font></font></p>
+
+<p><font><font>一些Web API不包含事件,但有些包含一些事件。</font><font>当事件触发时,允许我们运行函数的处理程序属性通常在单独的 “</font></font>Event handlers<font><font>”(事件处理程序) 部分的参考资料中列出。作为一个简单的例子,</font></font><code><a href="https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest">XMLHttpRequest</a></code><font><font> 对象的</font><font>实例  </font><font>(每一个实例都代表一个到服务器的HTTP请求,来取得某种新的资源)都有很多事件可用,例如  </font></font><code>onload</code><font><font> 事件在成功返回时就触发包含请求的资源,并且现在就可用。</font></font></p>
+
+<p>下面的代码提供了一个简单的例子来说明如何使用它:</p>
+
+<pre class="brush: js notranslate">var requestURL = 'https://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json';
+var request = new XMLHttpRequest();
+request.open('GET', requestURL);
+request.responseType = 'json';
+request.send();
+
+request.onload = function() {
+ var superHeroes = request.response;
+ populateHeader(superHeroes);
+ showHeroes(superHeroes);
+}</pre>
+
+<div class="note">
+<p><strong>注意:您可以在我们的</strong><a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/introduction/ajax.html">ajax.html</a><strong>示例中看到此代码</strong>  (或者 在线运行版本 <a href="http://mdn.github.io/learning-area/javascript/apis/introduction/ajax.html">see it live</a> also).</p>
+</div>
+
+<p><font><font>前五行指定了我们要获取的资源的位置,使用</font></font><code>XMLHttpRequest()</code><font><font> 构造函数</font><font>创建请求对象的新实例  </font><font>,打开HTTP 的 </font></font><code>GET</code><font><font> 请求以取得指定资源,指定响应以JSON格式发送,然后发送请求。</font></font></p>
+
+<p><font><font>然后 </font></font><code>onload</code> 处理函数指定我们如何处理响应。 我们知道请求会成功返回,并在需要加载事件(如<code>onload</code><font><font> 事件</font></font>)之后可用(除非发生错误),所以我们将包含返回的JSON的响应保存在<code>superHeroes</code>变量中,然后将其传递给两个不同的函数以供进一步处理。</p>
+
+<h3 id="它们在适当的地方有额外的安全机制">它们在适当的地方有额外的安全机制</h3>
+
+<p><font><font>WebAPI功能受到与JavaScript和其他Web技术(例如</font></font><a href="https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy"><font><font>同源政策</font></font></a><font><font>)</font><font>相同的安全考虑</font></font> 但是他们有时会有额外的安全机制。<font><font>例如,一些更现代的WebAPI将只能在通过HTTPS提供的页面上工作,因为它们正在传输潜在的敏感数据(例如  </font></font><a href="https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API"><font><font>服务工作者</font></font></a><font><font>  和  </font></font><a href="https://developer.mozilla.org/en-US/docs/Web/API/Push_API"><font><font>推送</font></font></a><font><font>)。</font></font></p>
+
+<p><font><font>另外,一旦调用WebAPI请求,用户就可以在您的代码中启用一些WebAPI请求权限。</font><font>作为一个例子,在加载我们之前的</font></font><a href="https://developer.mozilla.org/en-US/docs/Web/API/Geolocation"><font><font>Geolocation</font></font></a><font><font>  示例</font><font>时,您可能注意到了类似下面的对话框  </font><font>:</font></font></p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/14313/location-permission.png"></p>
+
+<p><font><font>该  </font></font><a href="https://developer.mozilla.org/en-US/docs/Web/API/Notifications_API"><font><font>通知API</font></font></a><font><font>  请求以类似的方式许可:</font></font></p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/14315/notification-permission.png"></p>
+
+<p>这些许可提示会被提供给用户以确保安全 - 如果这些提示不在适当位置,那么网站可能会在您不知情的情况下开始秘密跟踪您的位置,或者通过大量恼人的通知向您发送垃圾邮件。</p>
+
+<h2 id="概要">概要</h2>
+
+<p>在这一点上,您应该对API是什么,它们是如何工作的以及在JavaScript代码中可以对它们做什么有一个很好的了解。你可能很兴奋开始用特定的API来做一些有趣的事情,so let's go!  接下来,我们将看到使用文档对象模型(DOM)处理文档。</p>
+
+<p>{{NextMenu("Learn/JavaScript/Client-side_Web_APIs/Manipulating_documents", "Learn/JavaScript/Client-side_Web_API")}}</p>
diff --git a/files/zh-cn/learn/javascript/client-side_web_apis/manipulating_documents/index.html b/files/zh-cn/learn/javascript/client-side_web_apis/manipulating_documents/index.html
new file mode 100644
index 0000000000..ceca6b3b57
--- /dev/null
+++ b/files/zh-cn/learn/javascript/client-side_web_apis/manipulating_documents/index.html
@@ -0,0 +1,300 @@
+---
+title: 操作文档
+slug: Learn/JavaScript/Client-side_web_APIs/Manipulating_documents
+translation_of: Learn/JavaScript/Client-side_web_APIs/Manipulating_documents
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/JavaScript/Client-side_web_APIs/Introduction", "Learn/JavaScript/Client-side_web_APIs/Fetching_data", "Learn/JavaScript/Client-side_web_APIs")}}</div>
+
+<p class="summary">在编写web页面或应用时,你最想做的事情之一就是以某种方式操作文档结构。这通常使用一套大量使用{{domxref("Document")}}对象来控制HTML和样式信息的文档对象模型(DOM)来实现,在本文中,我们可以更详细的看到怎样使用DOM,连同一些其他有趣的API以有趣的方式改变你的环境</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">前提:</th>
+ <td>基础的计算机常识,基本了解HTML、CSS和JavaScript - 包括JavaScript对象。</td>
+ </tr>
+ <tr>
+ <th scope="row">目标:</th>
+ <td>熟悉核心DOM API, 以及其他和DOM与文档操作相关的常见API。</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="web浏览器的重要部分">web浏览器的重要部分</h2>
+
+<p>web浏览器的软件中有很多活动的程序片段,而许多片段web开发人员无法使用JavaScript来控制或操作,因此Web浏览器是一个很复杂的软件组合。你可能认为这样的限制是不是好事,但是浏览器被锁定是有充分理由的,主要集中在安全方面。如果一个网站可以访问您存储的密码或其他敏感信息,犹如你一样登录到网站,试想会发生什么?</p>
+
+<p>尽管有局限性,Web API仍然允许我们访问许多的功能,使我们用web页做很多事情。有几个在代码中经常引用的非常明显的部位 - 下面的图表表示了直接出现在web页面视图中的浏览器的主要部分:</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/14557/document-window-navigator.png" style="display: block; margin: 0 auto;"></p>
+
+<ul>
+ <li>window是载入浏览器的标签,在JavaScript中用{{domxref("Window")}}对象来表示,使用这个对象的可用方法,你可以返回窗口的大小(参见{{domxref("Window.innerWidth")}}和{{domxref("Window.innerHeight")}}),操作载入窗口的文档,存储客户端上文档的特殊数据(例如使用本地数据库或其他存储设备),为当前窗口绑定<a href="/en-US/docs/Learn/JavaScript/Building_blocks/Events#A_series_of_fortunate_events">event handler</a>,等等。</li>
+ <li>navigator表示浏览器存在于web上的状态和标识(即用户代理)。在JavaScript中,用{{domxref("Navigator")}}来表示。你可以用这个对象获取一些信息,比如来自用户摄像头的地理信息、用户偏爱的语言、多媒体流等等。</li>
+ <li>document(在浏览器中用DOM表示)是载入窗口的实际页面,在JavaScript中用{{domxref("Document")}} 对象表示,你可以用这个对象来返回和操作文档中HTML和CSS上的信息。例如获取DOM中一个元素的引用,修改其文本内容,并应用新的样式,创建新的元素并添加为当前元素的子元素,甚至把他们一起删除。</li>
+</ul>
+
+<p>在本文中,我们主要关注操作文档,但是也会稍微关注一下其他有用的部位。</p>
+
+<h2 id="文档对象模型">文档对象模型</h2>
+
+<p>在浏览器标签中当前载入的文档用文档对象模型来表示。这是一个由浏览器生成的“树结构”,使编程语言可以很容易的访问HTML结构 — 例如浏览器自己在呈现页面时,使用它将样式和其他信息应用于正确的元素,而页面呈现完成以后,开发人员可以用JavaScript操作DOM。</p>
+
+<p>我们已经创建一个简单的例子<a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/document-manipulation/dom-example.html">dom-example.html</a> (<a href="http://mdn.github.io/learning-area/javascript/apis/document-manipulation/dom-example.html">see it live also</a>). 在你的浏览器中打开它 — 这是一个很简单的页面,包含了一个{{htmlelement("section")}} 元素,里面有一个图像和有链接的段落。HTML源码如下:</p>
+
+<pre class="brush: html">&lt;!DOCTYPE html&gt;
+&lt;html&gt;
+ &lt;head&gt;
+ &lt;meta charset="utf-8"&gt;
+ &lt;title&gt;Simple DOM example&lt;/title&gt;
+ &lt;/head&gt;
+ &lt;body&gt;
+ &lt;section&gt;
+ &lt;img src="dinosaur.png" alt="A red Tyrannosaurus Rex: A two legged dinosaur standing upright like a human, with small arms, and a large head with lots of sharp teeth."&gt;
+ &lt;p&gt;Here we will add a link to the &lt;a href="https://www.mozilla.org/"&gt;Mozilla homepage&lt;/a&gt;&lt;/p&gt;
+ &lt;/section&gt;
+ &lt;/body&gt;
+&lt;/html&gt;</pre>
+
+<p>另一方面,DOM树如下所示:</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/14559/dom-screenshot.png" style="border-style: solid; border-width: 1px; display: block; margin: 0px auto;"></p>
+
+<div class="note">
+<p><strong>注意</strong>: 这个DOM树状图是用Ian Hickson的<a href="https://software.hixie.ch/utilities/js/live-dom-viewer/">Live DOM viewer</a>生成的</p>
+</div>
+
+<p>这里你可以看到,文档中每个元素和文本在树中都有它们自己的入口 — 称之为<strong>节点</strong>。你将用不同的术语来描述节点的类型和它们相对于其他节点的位置:</p>
+
+<ul>
+ <li><strong>元素节点</strong>: 一个元素,存在于DOM中。</li>
+ <li><strong>根节点</strong>: 树中顶层节点,在HTML的情况下,总是一个<code>HTML</code>节点(其他标记词汇,如SVG和定制XML将有不同的根元素)。</li>
+ <li><strong>子节点</strong>: <em>直接</em>位于另一个节点内的节点。例如上面例子中,<code>IMG</code>是<code>SECTION</code>的子节点。</li>
+ <li><strong>后代节点</strong>: 位于另一个节点内<em>任意位置</em>的节点。例如 上面例子中,<code>IMG</code>是<code>SECTION</code>的子节点,也是一个后代节点。<code>IMG</code>不是<code>BODY</code>的子节点,因为它在树中低了<code>BODY</code>两级,但它是<code>BODY</code>的后代之一。</li>
+ <li><strong>父节点</strong>: 里面有另一个节点的节点。例如上面的例子中<code>BODY</code>是<code>SECTION</code>的父节点。</li>
+ <li><strong>兄弟节点</strong>: DOM树中位于同一等级的节点。例如上面例子中,<code>IMG</code>和<code>P</code>是兄弟。</li>
+ <li><strong>文本节点</strong>: 包含文字串的节点</li>
+</ul>
+
+<p>在用DOM工作之前,熟悉这些术语是很有用的,因为你将会遇到大量代码术语的使用。你在研究CSS时也会遇到这些术语(例如后代选择器、子选择器)</p>
+
+<h2 id="主动学习_基本的DOM_操作">主动学习: 基本的DOM 操作</h2>
+
+<p>要开始学习DOM操作,我们先做一个实际的例子。</p>
+
+<ol>
+ <li>本地备份<a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/document-manipulation/dom-example.html">dom-example.html page</a>和与之相关的<a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/document-manipulation/dinosaur.png">image</a>。</li>
+ <li>在闭合的<code>&lt;/body&gt;</code>标签上面加入<code>&lt;script&gt;&lt;/script&gt;</code>元素。</li>
+ <li>要操作DOM内的元素,首先需要选择它,并将它的引用存储在一个变量中。在script元素中,添加下列代码行:
+ <pre class="brush: js">var link = document.querySelector('a');</pre>
+ </li>
+ <li>现在你有了一个存储在变量中的元素引用,你可以使用它的可用属性和方法来操作它(在{{htmlelement("a")}}元素的情况下定义为接口{{domxref("HTMLAnchorElement")}},它更常常用的父接口是{{domxref("HTMLElement")}}和表示DOM中所有节点的{{domxref("Node")}})。首先,更新 {{domxref("Node.textContent")}}属性的值来修改链接中的文字。在上面的代码后面加入一行代码:
+ <pre class="brush: js">link.textContent = 'Mozilla Developer Network';</pre>
+ </li>
+ <li>我们也能修改链接指向的URL,使得它被点击时不会走向错误的位置。在底部再加入下列代码:
+ <pre class="brush: js">link.href = 'https://developer.mozilla.org';</pre>
+ </li>
+</ol>
+
+<div>
+<p>注意,和JavaScript中的许多事情一样,有很多方法可以选择一个元素,并在一个变量中存储一个引用。{{domxref("Document.querySelector()")}}是推荐的主流方法,它允许你使用CSS选择器选择元素,使用很方便。上面的<code>querySelector()</code>调用会匹配它在文档中遇到的第一个{{htmlelement("a")}}元素。如果想对多个元素进行匹配和操作,你可以使用{{domxref("Document.querySelectorAll()")}},这个方法匹配文档中每个匹配选择器的元素,并把它们的引用存储在一个<a href="/en-US/docs/Learn/JavaScript/First_steps/Arrays">array</a>中。</p>
+
+<p>对于获取元素引用,还有一些更旧的方法,如:</p>
+
+<ul>
+ <li>{{domxref("Document.getElementById()")}},选择一个<code>id</code>属性值已知的元素,例如<code>&lt;p id="myId"&gt;My paragraph&lt;/p&gt;</code>。ID作为参数传递给函数,即 <code>var elementRef = document.getElementById('myId')</code>。</li>
+ <li>{{domxref("Document.getElementsByTagName()")}},返回页面中包含的所有已知类型元素的数组。如<code>&lt;p&gt;</code>s, <code>&lt;a&gt;</code>。元素类型作为参数传递给函数,即<code>var elementRefArray = document.getElementsByTagName('p')</code>.</li>
+</ul>
+
+<p>在较老的浏览器中使用这两种方法而不是流行的<code>querySelector()</code>方法,但这样并不方便。看一看你还可以发现别的什么!</p>
+</div>
+
+<h3 id="创建并放置新的节点">创建并放置新的节点</h3>
+
+<p>以上只是让你稍微尝试一下你可以做的事情,让我们进一步看看我们可以怎样来创建新的元素。</p>
+
+<ol>
+ <li>回到当前的例子,我们先获取到{{htmlelement("section")}}元素的引用 — 在已有script中添加下列代码(其他代码也同样处理):
+ <pre class="brush: js">var sect = document.querySelector('section');</pre>
+ </li>
+ <li>现在用{{domxref("Document.createElement()")}}创建一个新的段落,用与之前相同的方法赋予相同的文本:
+ <pre class="brush: js">var para = document.createElement('p');
+para.textContent = 'We hope you enjoyed the ride.';</pre>
+ </li>
+ <li>现在可以用{{domxref("Node.appendChild()")}}方法在后面追加新的段落:
+ <pre class="brush: js">sect.appendChild(para);</pre>
+ </li>
+ <li>最后,在内部链接的段落中添加文本节点,完美的结束句子。首先我们要使用{{domxref("Document.createTextNode()")}}创建一个文本节点:
+ <pre class="brush: js">var text = document.createTextNode(' — the premier source for web development knowledge.');</pre>
+ </li>
+ <li>现在获取内部连接的段落的引用,并把文本节点绑定到这个节点上:
+ <pre class="brush: js">var linkPara = document.querySelector('p');
+linkPara.appendChild(text);</pre>
+ </li>
+</ol>
+
+<p>这是给DOM添加节点要做的大部分工作 — 在构建动态接口时,你将做大量使用这些方法(我们在后面可以看到一些例子)。</p>
+
+<h3 id="移动和删除元素">移动和删除元素</h3>
+
+<p>也许有时候你想移动或从DOM中删除节点,这是完全可能的。</p>
+
+<p>如果你想把具有内部链接的段落移到sectioin的底部,简单的做法是:</p>
+
+<pre class="brush: js">sect.appendChild(linkPara);</pre>
+
+<p>这样可以把段落下移到section的底部。你可能想过要做第二个副本,但是情况并非如此 — <code>linkPara</code>是指向该段落唯一副本的引用。如果你想做一个副本并也把它添加进去,只能用{{domxref("Node.cloneNode()")}} 方法来替代。</p>
+
+<p>删除节点也非常的简单,至少,你拥有要删除的节点和其父节点的引用。在当前情况下,我们只要使用{{domxref("Node.removeChild()")}}即可,如下:</p>
+
+<pre>sect.removeChild(linkPara);</pre>
+
+<p>要删除一个仅基于自身引用的节点可能稍微有点复杂,这也是很常见的。没有方法会告诉节点删除自己,所以你必须像下面这样操作。</p>
+
+<pre class="brush: js">linkPara.parentNode.removeChild(linkPara);</pre>
+
+<p>把上述代码行加到你的代码中去</p>
+
+<h3 id="操作样式">操作样式</h3>
+
+<p>通过JavaScript以不同的方式来操作CSS样式是可能的。</p>
+
+<p>首先,使用 {{domxref("Document.stylesheets")}}返回{{domxref("CSSStyleSheet")}}数组,获取绑定到文档的所有样式表的序列。然后添加/删除想要的样式。然而,我们并不想扩展这些特性,因此它们在操作样式方面有点陈旧和困难,而现在有了更容易的方法。</p>
+
+<p>第一种方法是直接在想要动态设置样式的元素内部添加内联样式。这是用{{domxref("HTMLElement.style")}}属性来实现。这个属性包含了文档中每个元素的内联样式信息。你可以设置这个对象的属性直接修改元素样式。</p>
+
+<ol>
+ <li>要做个例子,把下面的代码行加到我们的例子中:
+ <pre class="brush: js">para.style.color = 'white';
+para.style.backgroundColor = 'black';
+para.style.padding = '10px';
+para.style.width = '250px';
+para.style.textAlign = 'center';</pre>
+ </li>
+ <li>重新载入页面,你将看到样式已经应用到段落中。如果在浏览器的<a href="/en-US/docs/Tools/Page_Inspector">Page Inspector/DOM inspector</a>中查看段落,你会看到这些代码的确为文档添加了内联样式:
+ <pre class="brush: html">&lt;p style="color: white; background-color: black; padding: 10px; width: 250px; text-align: center;"&gt;We hope you enjoyed the ride.&lt;/p&gt;</pre>
+ </li>
+</ol>
+
+<div class="note">
+<p><strong>注意</strong>: CSS样式的JavaSript属性版本以小驼峰式命名法书写,而CSS版本带连接符号(<code>backgroundColor</code> 对 <code>background-color</code>)。确保你不会混淆,否则就不能工作。</p>
+</div>
+
+<p>现在我们来看看另一个操作文档样式的常用方法。</p>
+
+<ol>
+ <li>删除之前添加到JavaScript中的五行代码。</li>
+ <li>在HTML的{{htmlelement("head")}}中添加下列代码 :
+ <pre>&lt;style&gt;
+.highlight {
+ color: white;
+ background-color: black;
+ padding: 10px;
+ width: 250px;
+ text-align: center;
+}
+&lt;/style&gt;</pre>
+ </li>
+ <li>现在我们改为使用HTML操作的常用方法 — {{domxref("Element.setAttribute()")}} — 这里有两个参数,你想在元素上设置的属性,你要为它设置的值。在这种情况下,我们在段落中设置类名为highlight:
+ <pre class="brush: js">para.setAttribute('class', 'highlight');</pre>
+ </li>
+ <li>刷新页面,看不到改变 — CSS仍然应用到段落,但是这次给出CSS规则选择的类不是内联CSS样式。</li>
+</ol>
+
+<p>两种方式各有优缺点,选择哪种取决于你自己。第一种方式无需安装,适合简单应用,第二种方式更加正统(没有CSS和JavaScript的混合,没有内联样式,而这些被认为是不好的体验)。当你开始构建更大更具吸引力的应用时,你可能会更多地使用第二种方法,但这完全取决于你自己。</p>
+
+<p>在这一点上,我们还没有做任何有用的事!使用JavaScript创建静态内容是毫无意义的 — 最好将其写入HTML,而不使用JavaScript。用JavaScript创建内容也有其他问题(如不能被搜索引擎读取),比HTML复杂得多。</p>
+
+<p>在接下来的几节中我们将看看DOM API一些更实际的用途。</p>
+
+<div class="note">
+<p><strong>注意</strong>: 你可以在GitHub上找到演示程序<a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/document-manipulation/dom-example-manipulated.html">finished version of the dom-example.html</a> (<a href="http://mdn.github.io/learning-area/javascript/apis/document-manipulation/dom-example-manipulated.html">see it live also</a>).</p>
+</div>
+
+<h2 id="主动学习:从Window对象中获取有用的信息">主动学习:从Window对象中获取有用的信息</h2>
+
+<p>到目前为止,我们只真正看到使用{{domxref("Node")}}和{{domxref("Document")}} 特性来操纵文档,但是没有理由不能从其他来源获取数据并在UI中使用它。想想我们最新文件中的演示例子 <a href="http://mdn.github.io/learning-area/javascript/apis/introduction/maps-example.html">maps-example.html</a> — 那里我们获取一些位置数据并用来显示你所在区域的地图。你只要确保你的数据格式正确;使用JavaScript比其他许多语言更容易,因为它是弱类型的——例如,当你想把它们打印到屏幕上时,数字会自动转换成字符串。</p>
+
+<p>在这个例子中,我们解决了一个常见的问题 — 不管窗口的大小是多少,确保应用程序和它所在的窗口视图一样大。在玩游戏的情况下,想在游戏中尽可能多地使用屏幕区域,这种方法是很有用的。</p>
+
+<p>一开始,要做一个<a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/document-manipulation/window-resize-example.html">window-resize-example.html</a>和<a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/document-manipulation/bgtile.png">bgtile.png</a>文件的本地拷贝。打开文件看一看 — 你可以看到我们用一个{{htmlelement("div")}}元素包裹屏幕的小部分,用来获得应用的background tile。我们也用它来表示应用的UI区域。</p>
+
+<ol>
+ <li>首先,获取这个div的引用,然后获取视窗(显示文档的内部窗口)的宽度和高度, 并存入变量中 — 这两个值包含在{{domxref("Window.innerWidth")}} 和 {{domxref("Window.innerHeight")}}属性中。在已存在的{{htmlelement("script")}}元素中加入下列代码:
+ <pre class="brush: js">var div = document.querySelector('div');
+var WIDTH = window.innerWidth;
+var HEIGHT = window.innerHeight;</pre>
+ </li>
+ <li>接下来,我们将动态地改变div的宽度和高度,使其等于视窗的宽度和高度。 在您的代码下面添加以下两行:
+ <pre class="brush: js">div.style.width = WIDTH + 'px';
+div.style.height = HEIGHT + 'px';</pre>
+ </li>
+ <li>保存并刷新浏览器 — 现在可以看到不管你使用什么大小的屏幕,div变得和视窗一样大。如果要调整窗口大小使其更大,你可以看到div会保持相同大小 — 因为我们只能设置一次。</li>
+ <li>在我们调整窗口时,我们怎样用事件来调整div的大小? {{domxref("Window")}}对象有一个称为resize的可用事件。每次窗口调整大小时都会触发该事件 — 我们可以通过{{domxref("Window.onresize")}} 事件处理程序来访问它,并返回每次改变大小的代码。在代码底部添加下列程序:
+ <pre class="brush: js">window.onresize = function() {
+ WIDTH = window.innerWidth;
+ HEIGHT = window.innerHeight;
+ div.style.width = WIDTH + 'px';
+ div.style.height = HEIGHT + 'px';
+}</pre>
+ </li>
+</ol>
+
+<div class="note">
+<p><strong>注意</strong>: 如果你被卡住了,可查看<a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/document-manipulation/window-resize-example-finished.html">finished window resize example</a> (<a href="http://mdn.github.io/learning-area/javascript/apis/document-manipulation/window-resize-example-finished.html">see it live also</a>).</p>
+</div>
+
+<h2 id="主动学习_一个动态的购物单">主动学习: 一个动态的购物单</h2>
+
+<p>作为文章的收尾,我们想给你一个小小的挑战 — 我们要做一个简单的购物单的例子,使用表单的输入框和按钮动态的向购物单中添加购物项。在输入中添加项,然后按下按钮:</p>
+
+<ul>
+ <li>购物项应该出现在清单中。</li>
+ <li>每个购物项都应该给出一个按钮,可以按下按钮从清单中删除该项。</li>
+ <li>输入框应该是空白的并已聚焦,为你准备好输入另一个项。</li>
+</ul>
+
+<p>完成后的演示程序看起来有点像这样的:</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/14563/shopping-list.png" style="border-style: solid; border-width: 1px; display: block; height: 225px; margin: 0px auto; width: 369px;"></p>
+
+<p>要完成实验,要按照下面的步骤,确保购物单的行为如上所述。</p>
+
+<ol>
+ <li>首先,下载<a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/document-manipulation/shopping-list.html">shopping-list.html</a>文件,并存入本地。你会看到它有一些极小的CSS,一个带有label、input和button的list和一个空的list以及{{htmlelement("script")}} 元素。要添加的所有程序都在script里面。</li>
+ <li>创建三个变量来保存list({{htmlelement("ul")}})、{{htmlelement("input")}}和{{htmlelement("button")}}元素的引用。</li>
+ <li>创建一个<a href="/en-US/docs/Learn/JavaScript/Building_blocks/Functions">函数</a>响应点击按钮。</li>
+ <li>在函数体内,开始要在一个变量中存储输入框的当前<a href="/en-US/docs/Web/API/HTMLInputElement#Properties">值</a>。</li>
+ <li>然后,为输入框元素设置空字符 - <code>''</code>使其为空</li>
+ <li>创建三个新元素 — 一个list项({{htmlelement('li')}}),{{htmlelement('span')}}和 {{htmlelement('button')}},并把它们存入变量之中。</li>
+ <li>把span和button作为list项的子节点。</li>
+ <li>把之前保存的输入框元素的值设置为span的文本内容,按钮的文本内容设置为'Delete'</li>
+ <li>把list项设置为list的子节点。</li>
+ <li>为删除按钮绑定事件处理程序。当点击按钮时,删除它所在的整个list项。</li>
+ <li>最后,使用<code><a href="/en-US/docs/Web/API/HTMLElement/focus">focus()</a></code>方法聚焦输入框准备输入下一个购物项。</li>
+</ol>
+
+<div class="note">
+<p><strong>注意</strong>: 如果你卡住了,请查看<a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/document-manipulation/shopping-list-finished.html">finished shopping list</a> (<a href="http://mdn.github.io/learning-area/javascript/apis/document-manipulation/shopping-list-finished.html">see it running live also</a>.)</p>
+</div>
+
+<h2 id="总结">总结</h2>
+
+<p>我们已经结束了对文档和DOM操作的研究。在这一点上,你应该了解Web浏览器在控制文档和用户Web体验的其他方面方面的重要部分。更重要的是,你应该了解什么是文档对象模型,怎样操作它来创建有用的功能。</p>
+
+<h2 id="另见">另见</h2>
+
+<p>你还可以使用更多的特性来操作文档,检查一些参考,看看你能发现些什么?</p>
+
+<ul>
+ <li>{{domxref("Document")}}</li>
+ <li>{{domxref("Window")}}</li>
+ <li>{{domxref("Node")}}</li>
+ <li>{{domxref("HTMLElement")}}, {{domxref("HTMLInputElement")}}, {{domxref("HTMLImageElement")}}, etc.</li>
+</ul>
+
+<p>(MDN上有完整的Web API 列表,参见<a href="https://developer.mozilla.org/zh-CN/docs/Web/API">Web API接口参考</a> !)</p>
+
+<div>{{PreviousMenuNext("Learn/JavaScript/Client-side_web_APIs/Introduction", "Learn/JavaScript/Client-side_web_APIs/Fetching_data", "Learn/JavaScript/Client-side_web_APIs")}}</div>
diff --git a/files/zh-cn/learn/javascript/client-side_web_apis/third_party_apis/index.html b/files/zh-cn/learn/javascript/client-side_web_apis/third_party_apis/index.html
new file mode 100644
index 0000000000..edaeb3acde
--- /dev/null
+++ b/files/zh-cn/learn/javascript/client-side_web_apis/third_party_apis/index.html
@@ -0,0 +1,484 @@
+---
+title: 第三方 API
+slug: Learn/JavaScript/Client-side_web_APIs/Third_party_APIs
+tags:
+ - API
+ - youtube
+ - 初学者
+ - 第三方
+ - 谷歌地图
+translation_of: Learn/JavaScript/Client-side_web_APIs/Third_party_APIs
+---
+<div>{{LearnSidebar}}{{PreviousMenuNext("Learn/JavaScript/Client-side_web_APIs/Fetching_data", "Learn/JavaScript/Client-side_web_APIs/Drawing_graphics", "Learn/JavaScript/Client-side_web_APIs")}}</div>
+
+<p class="summary">到目前为止我们已经介绍的API是内置在浏览器中的,但并不是所有的API都是。许多大型网站和服务(例如Google地图,Twitter,Facebook,PayPal等)提供的API允许开发者使用他们的数据(例如在博客上显示您的Twitter流)或服务(例如在您的网站上显示自定义Google地图,或者使用Facebook登录来登录你的用户)。本文着眼于浏览器API和第三方API的区别,并展示了后者的一些典型用途。</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">先决条件:</th>
+ <td>JavaScript 基础知识 (see <a href="/en-US/docs/Learn/JavaScript/First_steps">first steps</a>, <a href="/en-US/docs/Learn/JavaScript/Building_blocks">building blocks</a>, <a href="/en-US/docs/Learn/JavaScript/Objects">JavaScript objects</a>), the <a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Introduction">basics of Client-side APIs</a></td>
+ </tr>
+ <tr>
+ <th scope="row">目的:</th>
+ <td> 学习了解第三方API的运作方式,以及如何运用它们来提高你的网站性能</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="什么是第三方API">什么是第三方API?</h2>
+
+<p>第三方API是由第三方(通常是Facebook,Twitter或Google等公司)提供的API,允许您通过JavaScript访问其功能,并在您自己的站点上使用它。 正如我们在 <a href="/ch-ZN/docs/Learn/JavaScript/Client-side_web_APIs/Introduction">API介绍章节</a> 所展示的, 最显著的例子就是运用 <a href="https://developers.google.com/maps/">Google Maps APIs</a> 在你的网页上展示自定义地图。</p>
+
+<p>让我们再来瞧一眼这个地图的例子 (see the <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/introduction/maps-example.html">source code on GitHub</a>; <a href="https://mdn.github.io/learning-area/javascript/apis/introduction/maps-example.html">see it live also</a>), 从这里可以知道第三方API和浏览器API的区别是怎么样的。</p>
+
+<div class="note">
+<p><strong>Note</strong>: 您可能想要一次获得所有的代码示例,在这种情况下,您可以搜索repo来获取每个部分中需要的示例文件。</p>
+</div>
+
+<h3 id="它们植根于第三方服务器">它们植根于第三方服务器</h3>
+
+<p>浏览器API在浏览器构建之初就存在 — 用JavaScript就可以立即访问它们。例如, 例子中所使用的 <a href="/en-US/docs/Web/API/Geolocation/Using_geolocation">Geolocation API</a> 就是通过使用 <code><a href="/en-US/docs/Web/API/Navigator">Navigator</a></code> 对象的属性 geolocation 来访问的, 它返回一个名为 <code><a href="/en-US/docs/Web/API/Geolocation">Geolocation</a></code> 的对象。 这个例子使用了这个对象的方法 <code><a href="/en-US/docs/Web/API/Geolocation/getCurrentPosition">getCurrentPosition()</a></code> 来请求当前设备所处的地点:</p>
+
+<pre class="brush: js notranslate">navigator.geolocation.getCurrentPosition(function(position) { ... });</pre>
+
+<p>第三方API,从某种角度讲,植根于第三方服务器上。要通过 JavaScript获取它们,您首先需要链接到其功能接口上并使其在您的页面上生效。通常来说,这首先需要您通过一个 {{htmlelement("script")}} 元素连接到第三方服务器所开放的JavaScript库,如下所示:</p>
+
+<pre class="brush: js notranslate">&lt;script type="text/javascript" src="https://maps.google.com/maps/api/js?key=AIzaSyDDuGt0E5IEGkcE6ZfrKfUtE9Ko_de66pA"&gt;&lt;/script&gt;</pre>
+
+<p>然后您便可使用该库中可用的对象了,如:</p>
+
+<pre class="brush: js notranslate">var latlng = new google.maps.LatLng(position.coords.latitude,position.coords.longitude);
+var myOptions = {
+ zoom: 8,
+ center: latlng,
+ mapTypeId: google.maps.MapTypeId.TERRAIN,
+ disableDefaultUI: true
+}
+
+var map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);</pre>
+
+<p>代码中我们用 <code>google.maps.LatLng()</code> 构造器创建了一个新的 <code>LatLng</code> 对象,它包含了我们想展示的地址的纬度和经度,作为一个Geolocation API返回。然后,我们创建了包含这个对象,和其他有关地图显示信息的选项对象(<code>myOptions</code>) 。最后,用 <code>google.maps.Map()</code> 构造器创建了<code>map</code>对象,它接受网页元素(地图展示处)和选项对象两个参数。</p>
+
+<p>以上就是用 Google Maps API 建立一个简单地图所需要的所有信息。所有复杂的工作都全由你所连接的第三方服务器处理,包括展示正确地理位置的地图块,等等。</p>
+
+<div class="note">
+<p><strong>Note</strong>: 一些api处理对其功能的访问略有不同,相反,它们要求开发人员向特定的URL模式发出HTTP请求(参见从服务器获取数据),以检索特定的数据。这些被称为RESTful api,稍后我们将在文章中展示这个示例。</p>
+</div>
+
+<h3 id="权限的不同处理方式">权限的不同处理方式</h3>
+
+<p>正如我们在第一篇文章中所讨论的,浏览器api的安全性通常是通过权限提示。这样做的目的是为了让用户知道他们访问的网站上发生了什么,并且不太可能成为恶意使用API的人的受害者。</p>
+
+<p>第三方API有一个稍微不同的权限系统——它们倾向于使用关键代码来允许开发人员访问API功能。再次查看我们链接到的谷歌地图API库的URL:</p>
+
+<pre class="notranslate">https://maps.google.com/maps/api/js?key=AIzaSyDDuGt0E5IEGkcE6ZfrKfUtE9Ko_de66pA</pre>
+
+<p>URL末尾提供的URL参数是一个开发人员密钥—应用程序的开发人员必须应用它来获取一个密钥,然后以一种特定的方式将其包含在代码中,才能允许访问API的功能。对于谷歌映射(以及其他谷歌API),可以在谷歌云平台上申请一个密钥。</p>
+
+<p>其他API的包含方式稍微不同,但是大多数API的模式都非常相似。</p>
+
+<p>需要密钥的原因是:所用使用API功能的人都需要承担责任。当开发者注册一个密钥之后,如果他们开始恶意使用API(例如跟踪位置,试图用大量的垃圾邮件干扰用户正常工作),此时API的提供者可以采取措施。最简单的措施是撤销开发者的API的使用权。</p>
+
+<h2 id="扩展the_Google_Maps示例">扩展the Google Maps示例</h2>
+
+<p>现在我们已经检验了Google Maps API示例以及它的运作方式,让我们添加一些更多的功能来展示如何使用API的其他特性。</p>
+
+<ol>
+ <li>
+ <p>要开始这个部分, 确保你已经在一个新的目录复制 <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/third-party-apis/google-maps/maps_start.html">Google Maps启动文件</a>。 如果你已经 <a href="/en-US/docs/Learn#Getting_our_code_examples">克隆了示例存储库</a>,那么你已经拥有了一个这个文件的拷贝,你可以在<em>javascript/apis/third-party-apis/google-maps目录中找到该文件。</em></p>
+ </li>
+ <li>
+ <p>接下来,用以下步骤获取你自己的开发者密钥:</p>
+
+ <ol>
+ <li>跳转到 <a href="https://console.cloud.google.com/apis/dashboard">Google Cloud Platform API Manager dashboard</a>.</li>
+ <li>如果你还没有新项目,请创建一个新项目。</li>
+ <li>单击“启用 API”按钮。</li>
+ <li>选择<em>Google Maps JavaScript API</em>.</li>
+ <li>单击“启用”按钮。</li>
+ <li>单击创建凭据,然后选择API密钥。</li>
+ <li>复制你的API密钥并将示例中的第一个{{htmlelement("script")}}元素中的现有密钥替换为你自己的密钥。(位于<code>?key=</code>和属性结束引号标记 (<code>"</code>)之间的位置。)</li>
+ </ol>
+
+ <div class="note">
+ <p><strong>注意:获取</strong>Google相关API密钥可能会有一点困难——Google Cloud Platform API Manager有许多不同的屏幕 ,并且工作流程可能因您是否设置账户而变得细微的不同。如果您在执行此步骤时遇到了困难,我们将很乐意为您提供帮助——<a href="/en-US/docs/Learn#Contact_us">联系我们</a>。</p>
+ </div>
+ </li>
+ <li>打开你的Google Maps起始文件,找到<code>INSERT-YOUR-API-KEY-HERE</code>字符串,然后将其替换为你从Google Cloud Platform API Manager dashboard获取的实际API密钥。</li>
+</ol>
+
+<h3 id="添加自定义标记">添加自定义标记</h3>
+
+<p>添加一个标记在地图上(icon)在某种程度上是很容易的,你只需要创建一个新的标记使用google.maps.Marker()构造函数,传递给它一个包含位置显示标记的选择对象(如LatLng对象),和Map对象来显示它。</p>
+
+<ol>
+ <li>
+ <p>在 <code>var map ...</code> 行下面添加下列代码:</p>
+
+ <pre class="brush: js notranslate">var marker = new google.maps.Marker({
+ position: latlng,
+ map: map
+});</pre>
+
+ <p>现在如果你刷新你的页面, 你会看到地图中间弹出了一个小小的漂亮标记。这很酷, 但是这并不是一个定制的标记图标 — 它使用了默认的标记图标。</p>
+ </li>
+ <li>
+ <p>如果要使用定制化的图标,我们需要在创建标记时通过URL来明确说明。首先,在刚才添加的代码块之后添加下面的代码:</p>
+
+ <pre class="brush: js notranslate">var iconBase = 'https://maps.google.com/mapfiles/kml/shapes/';</pre>
+
+ <p>这定义了所有Google Maps官方图标存储的URL(如果你想的话你也可以使用你自己的图标存储位置)。</p>
+ </li>
+ <li>
+ <p>图标的位置应当在选项对象的<code>icon</code> 属性中说明。更新Constructor并添加icon属性,如下:</p>
+
+ <pre class="brush: js notranslate">var marker = new google.maps.Marker({
+ position: latlng,
+ icon: iconBase + 'flag_maps.png',
+ map: map
+});</pre>
+
+ <p>这里我们用<code>iconBase</code> 加上图标的文件名,从而创建完整URL的方式阐明了icon属性。现在尝试重新加载你的例子,你会看到你的地图上显示了一个定制的标记!</p>
+ </li>
+</ol>
+
+<div class="note">
+<p><strong>Note</strong>: 访问 <a href="https://developers.google.com/maps/documentation/javascript/custom-markers">Customizing a Google Map: Custom Markers</a> 以查看更多信息。</p>
+</div>
+
+<div class="note">
+<p><strong>Note</strong>: 访问 <a href="https://fusiontables.google.com/DataSource?dsrcid=308519#map:id=3">Map marker or Icon names</a> 以找出还有哪些可以的图标,并查看它们的引用名称是什么。它们的文件名应当是当你点击它们时显示出的图标名,最后带有".png"。</p>
+</div>
+
+<h3 id="单击标记时显示弹出窗口"><span class="tlid-translation translation" lang="zh-CN"><span title="">单击标记时显示弹出窗口</span></span></h3>
+
+<p><span class="tlid-translation translation" lang="zh-CN"><span title="">Google地图的另一个常见用例是在点击其名称或标记时显示有关某个地点的更多信息(弹出式广告在Google Maps API中称为信息窗口)。</span> <span title="">这也很容易实现,所以让我们来看看它。</span></span></p>
+
+<ol>
+ <li>
+ <p><span class="tlid-translation translation" lang="zh-CN"><span title="">首先,您需要指定一个包含HTML的JavaScript字符串,该字符串将定义弹出窗口的内容。</span> <span title="">这将由API注入弹出窗口,并且可以包含您想要的任何内容。</span> <span title="">在<code>google.maps.Marker()</code>构造函数定义下面添加以下行:</span></span></p>
+
+ <pre class="brush: js notranslate">var contentString = '&lt;div id="content"&gt;&lt;h2 id="firstHeading" class="firstHeading"&gt;Custom info window&lt;/h2&gt;&lt;p&gt;This is a cool custom info window.&lt;/p&gt;&lt;/div&gt;';</pre>
+ </li>
+ <li>
+ <p>然后,你需要使用<code>google.maps.InfoWindow()</code> 构造器,创建一个新的info window object。在之前的代码下面,添加以下代码:</p>
+
+ <pre class="brush: js notranslate">var infowindow = new google.maps.InfoWindow({
+ content: contentString
+});</pre>
+
+ <p>还有其他可用的属性 (查看 <a href="https://developers.google.com/maps/documentation/javascript/infowindows">Info Windows</a>), 但是在这里我们只具体说明指向内容源的<code>content</code> 属性。</p>
+ </li>
+ <li>
+ <p>最后,为了在单击标记(marker)时显示弹出窗口,我们使用了一个简单的点击事件处理器。在<code>google.maps.InfoWindow()</code>构造器代码下面,添加以下代码:</p>
+
+ <pre class="brush: js notranslate">marker.addListener('click', function() {
+ infowindow.open(map, marker);
+});</pre>
+
+ <p>在函数中,我们只需调用 infowindow 的 <code>open()</code> 函数,该函数将要显示它的地图和希望它显示在旁边的标记作为参数。</p>
+ </li>
+ <li>
+ <p>现在尝试重新加载示例,然后单击标记!</p>
+ </li>
+</ol>
+
+<h3 id="Controlling_what_map_controls_are_displayed">Controlling what map controls are displayed</h3>
+
+<p>在原始 <code>google.maps.Map()</code>构造函数中,将看到 <code>disableDefaultUI: true</code> 。这将禁用您通常在 Google 地图上获得的所有标准 UI 控件。</p>
+
+<ol>
+ <li>
+ <p>将其值设置为 <code>false</code> (或完全删除此属性),重新加载示例,将看到地图缩放按钮、 scale indicator等等。</p>
+ </li>
+ <li>
+ <p>现在撤销上一次更改。</p>
+ </li>
+ <li>
+ <p>通过使用指定单个 UI 功能的其他属性,可以更精细地显示或隐藏控件。尝试在 <code>disableDefaultUI: true</code> 的下面添加代码(请记住在 <code>disableDefaultUI: true</code> 之后输入逗号,否则将收到错误):</p>
+
+ <pre class="brush: js notranslate">zoomControl: true,
+mapTypeControl: true,
+scaleControl: true,</pre>
+ </li>
+ <li>
+ <p>现在尝试重新加载示例以查看这些属性的效果。您可以在 <a href="https://developers.google.com/maps/documentation/javascript/3.exp/reference#MapOptions">MapOptions object reference page</a>找到更多属性。</p>
+ </li>
+</ol>
+
+<p>就是现在 - 看看 <a href="https://developers.google.com/maps/documentation/javascript/">Google Maps APIs documentation</a>,发现更多乐趣!</p>
+
+<h2 id="一个RESTful_API_—_NYTimes">一个RESTful API — NYTimes</h2>
+
+<p><span class="tlid-translation translation" lang="zh-CN"><span title="">现在让我们看看另一个API示例</span></span> - <a href="https://developer.nytimes.com">New York Times API</a><span class="tlid-translation translation" lang="zh-CN"><span title="">。</span> <span title="">此API允许您检索纽约时报的新闻故事信息并将其显示在您的网站上。</span> <span title="">这种类型的API称为RESTful API - 我们不像使用Google地图那样使用JavaScript库的功能获取数据,而是通过向特定网址发出HTTP请求来获取数据,其中包含搜索术语和其他属性编码的数据</span> <span title="">URL(通常作为URL参数)。</span> <span title="">这是您在API中遇到的常见模式。</span></span></p>
+
+<h2 id="使用第三方API的方法">使用第三方API的方法</h2>
+
+<p><span class="tlid-translation translation" lang="zh-CN"><span title="">下面我们将带您完成练习,向您展示如何使用NYTimes API,它还提供了一组更为通用的步骤,您可以将其用作处理新API的方法。</span></span></p>
+
+<h3 id="查找文档"><span class="tlid-translation translation" lang="zh-CN"><span title="">查找文档</span></span></h3>
+
+<p><span class="tlid-translation translation" lang="zh-CN"><span title="">当您想要使用第三方API时,必须找出文档的位置,以便了解API具有哪些功能,以及如何使用它们等等。NYTimes API文档位于</span></span> <a href="https://developer.nytimes.com/">https://developer.nytimes.com/</a>。</p>
+
+<h3 id="获取一个开发者密钥">获取一个开发者密钥</h3>
+
+<p><span class="tlid-translation translation" lang="zh-CN"><span title="">出于安全性和问责制的原因,大多数API都要求您使用某种开发人员密钥。</span> <span title="">要注册NYTimes API密钥,您需要访问</span></span> <a href="https://developer.nytimes.com/signup">https://developer.nytimes.com/signup</a>。</p>
+
+<ol>
+ <li>
+ <p>申请 "Article Search API" 的 API key ——新建一个应用,选择这个 API ,(填写名称和描述,打开 "Article Search API" 下面的开关,然后点击 “创建(Create)”)</p>
+ </li>
+ <li>
+ <p>从结果页面获取 API 。</p>
+ </li>
+ <li>
+ <p>现在开始构建这个应用,下载 <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/third-party-apis/nytimes/nytimes_start.html">nytimes_start.html</a> 和 <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/third-party-apis/nytimes/nytimes.css">nytimes.css</a> 到一个新的本地目录。如果已经克隆过这个仓库 <a href="/en-US/docs/Learn#Getting_our_code_examples">cloned the examples repository</a>, 里面就已经有这 2 个文件了,它们存放在 <em>javascript/apis/third-party-apis/nytimes</em> 目录下。HTML 文件里的 <code>&lt;script&gt;</code> 标签下已经包含了构建这个应用需要用到的变量;下面我们来填写函数。</p>
+ </li>
+</ol>
+
+<p>下面是这个应用最终的样子,可以在搜索框里填写条目、起始日期和结束日期,作为参数向 Article Search API 接口发起查询,然后显示查询结果。</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/14821/nytimes-search.png" style="border-style: solid; border-width: 1px; display: block; height: 374px; margin: 0px auto; width: 700px;"></p>
+
+<h3 id="将API连接到应用">将API连接到应用</h3>
+
+<p>首先,建立 API 和本地应用的连接。对于这个 API ,每次向服务器对应的 URL 发起 get 请求,都需要把 API key 作为 get 请求的参数。</p>
+
+<ol>
+ <li>
+ <p>在代码中找到下面这行:</p>
+
+ <pre class="brush: js notranslate">var key = 'INSERT-YOUR-API-KEY-HERE';</pre>
+
+ <p>把 <code>INSERT-YOUR-API-KEY-HERE</code> 替换为在上一节中获得的 API key.</p>
+ </li>
+ <li>
+ <p>添加下面这行代码到 JavaScript 代码中。添加到注释 "<code>// Event listeners to control the functionality</code>" 的下面。当表单提交时 (按钮按下时) 运行这个函数  <code>submitSearch()</code> .</p>
+
+ <pre class="brush: js notranslate">searchForm.addEventListener('submit', <code>submitSearch</code>);</pre>
+ </li>
+ <li>
+ <p>添加 <code>submitSearch()</code> 和 <code>fetchResults()</code> 函数如下:</p>
+
+ <pre class="notranslate">function submitSearch(e) {
+ pageNumber = 0;
+ fetchResults(e);
+}
+
+function fetchResults(e) {
+ // Use preventDefault() to stop the form submitting
+ e.preventDefault();
+
+ // Assemble the full URL
+ url = baseURL + '?api-key=' + key + '&amp;page=' + pageNumber + '&amp;q=' + searchTerm.value + '&amp;fq=document_type:("article")';
+
+ if(startDate.value !== '') {
+ url += '&amp;begin_date=' + startDate.value;
+ };
+
+ if(endDate.value !== '') {
+ url += '&amp;end_date=' + endDate.value;
+ };
+
+}</pre>
+ </li>
+</ol>
+
+<p><code>submitSearch()</code> 设置起始页码为 0 ,然后调用 <code>fetchResults()</code> 函数。其中,先调用事件对象的 <code><a href="/en-US/docs/Web/API/Event/preventDefault">preventDefault()</a></code> 函数,阻止实际的表单提交事件 (会破坏应用,至于为什么可以自己试一试)。然后,处理字符串,构建完整的请求 URL 。以下是几个必要的步骤:</p>
+
+<ul>
+ <li>基本的 URL ( <code>baseURL</code> 变量).</li>
+ <li> API key ,需要放到 <code>api-key</code> URL 参数里面去 ( <code>key</code> 变量).</li>
+ <li>页码,需要放到 <code>page</code> URL 参数里面去 ( <code>pageNumber</code> 变量).</li>
+ <li>搜索项,需要放到 <code>q</code> URL 参数里面去 ( <code>searchTerm</code> 输入框的文字 {{htmlelement("input")}}).</li>
+</ul>
+
+<p>然后,用几个 <code><a href="/en-US/docs/Web/JavaScript/Reference/Statements/if...else">if()</a></code> 代码块检测 <code>startDate</code> 和 <code>endDate</code> <code>&lt;input&gt;</code> 输入框里面是否有文字内容。如果有,再把输入框里的内容分别填写到 <code>begin_date</code> 和 <code>end_date</code> URL 参数里面去。</p>
+
+<p>最后,完整的 URL 如下所示:</p>
+
+<pre class="notranslate">https://api.nytimes.com/svc/search/v2/articlesearch.json?api-key=4f3c267e125943d79b0a3e679f608a78&amp;page=0&amp;q=cats
+&amp;begin_date=20170301&amp;end_date=20170312</pre>
+
+<div class="note">
+<p><strong>Note</strong>: 更多 URL 参数的说明参考 <a href="https://developer.nytimes.com/">NYTimes developer docs</a>.</p>
+</div>
+
+<div class="note">
+<p><strong>Note</strong>: 示例包含了基本的表单数据验证操作 — 表项提交之前必须有内容 (用 <code>required</code> 属性实现),此外,日期字段有确定的 <code>pattern</code> 属性,它们的值必须由 8 个数字组成 (<code>pattern="[0-9]{8}"</code>),否则不能提交。更多细节参考 <a href="/en-US/docs/Learn/HTML/Forms/Form_validation">Form data validation</a> </p>
+</div>
+
+<h3 id="从api请求数据">从api请求数据</h3>
+
+<p>现在 URL 已经构造好了,下面向它发起请求。本例使用 <a href="/ch-CN/docs/Web/API/Fetch_API/Using_Fetch">Fetch API </a></p>
+
+<p>把下面的代码块添加到 <code>fetchResults()</code> 函数末尾的大括号里面:</p>
+
+<pre class="brush: js notranslate">// Use fetch() to make the request to the API
+fetch(url).then(function(result) {
+ return result.json();
+}).then(function(json) {
+ displayResults(json);
+});</pre>
+
+<p>这段代码用于发起请求,把变量 <code>url</code> 作为 <code><a href="/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch">fetch()</a></code> 函数的参数,用 <code><a href="/en-US/docs/Web/API/Body/json">json()</a></code> 函数把响应的结果转换为 JSON 格式,然后把 JSON 数据传递给 <code>displayResults()</code> 函数,数据就可以在 UI 中显示出来了。</p>
+
+<h3 id="显示数据">显示数据</h3>
+
+<p>OK,下面是显示数据的部分。把下面的函数添加到 <code>fetchResults()</code> 函数下面。</p>
+
+<pre class="brush: js notranslate">function displayResults(json) {
+ while (section.firstChild) {
+ section.removeChild(section.firstChild);
+ }
+
+ var articles = json.response.docs;
+
+ if(articles.length === 10) {
+ nav.style.display = 'block';
+ } else {
+ nav.style.display = 'none';
+ }
+
+ if(articles.length === 0) {
+ var para = document.createElement('p');
+ para.textContent = 'No results returned.'
+ section.appendChild(para);
+ } else {
+ for(var i = 0; i &lt; articles.length; i++) {
+ var article = document.createElement('article');
+ var heading = document.createElement('h2');
+ var link = document.createElement('a');
+ var img = document.createElement('img');
+ var para1 = document.createElement('p');
+ var para2 = document.createElement('p');
+ var clearfix = document.createElement('div');
+
+ var current = articles[i];
+ console.log(current);
+
+ link.href = current.web_url;
+ link.textContent = current.headline.main;
+ para1.textContent = current.lead_paragraph;
+ para2.textContent = 'Keywords: ';
+ for(var j = 0; j &lt; current.keywords.length; j++) {
+ var span = document.createElement('span');
+ span.textContent += current.keywords[j].value + ' ';
+ para2.appendChild(span);
+ }
+
+ if(current.multimedia.length &gt; 0) {
+ img.src = 'http://www.nytimes.com/' + current.multimedia[0].url;
+ img.alt = current.headline.main;
+ }
+
+ clearfix.setAttribute('class','clearfix');
+
+ article.appendChild(heading);
+ heading.appendChild(link);
+ article.appendChild(img);
+ article.appendChild(para1);
+ article.appendChild(para2);
+ article.appendChild(clearfix);
+ section.appendChild(article);
+ }
+ }
+};</pre>
+
+<p>这一段有好多代码;下面一一解释:</p>
+
+<ul>
+ <li>第一个 <code><a href="/en-US/docs/Web/JavaScript/Reference/Statements/while">while</a></code> 循环是通用的操作,用于删除 DOM 结点里面的所有内容,在本例中是 {{htmlelement("section")}} 元素。不断查询 <code>&lt;section&gt;</code> 结点是否有子节点,如果有,就删除。当 <code>&lt;section&gt;</code> 没有子节点时退出循环。</li>
+ <li>然后,把 <code>articles</code> 变量赋值为 <code>json.response.docs</code> — 它来自查询结果,是所有文章对象构成的数组。单独写出这一行来,下面的代码会清晰一些。</li>
+ <li>第一个 <code><a href="/en-US/docs/Web/JavaScript/Reference/Statements/if...else">if()</a></code> 检测查询到的文章数量是否是 10  (该 API 一次最多返回 10 篇文章) 如果是,就把 {{htmlelement("nav")}} 显示出来,里面包含有 <em>前 10/后 10(Previous 10/Next 10) </em>翻页按钮。如果返回的文章数量不足 10,就直接显示在 1 页里,不需要显示翻页按钮。在下一节中将展示翻页功能的实现。</li>
+ <li>下一个 <code>if()</code> 检测是否没有文章返回。如果是,就什么都不显示 — 创建一个 {{htmlelement("p")}} 结点,填写提示 "无结果。" 然后把该结点插入到 <code>&lt;section&gt;</code> 结点中。</li>
+ <li>如果有一些文章返回,第一步,创建所有用于展示新闻信息的结点,依次填写对应的文章内容,再把这些结点插入到合适的 DOM 结点下。至于文章对象里面的哪个属性包含需要展示的信息,查询 <a href="https://developer.nytimes.com/article_search_v2.json">Article Search API reference</a>. 大部分操作都很简单,但是个别需要提示一下:
+ <ul>
+ <li>使用 <a href="/en-US/docs/Web/JavaScript/Reference/Statements/for">for loop</a> (<code>for(var j = 0; j &lt; current.keywords.length; j++) { ... }</code> ) 遍历每篇文章关联的关键字,再把它们插入到各自的 {{htmlelement("span")}} 标签,在 <code>&lt;p&gt;</code> 标签里面,这样做可以方便样式表的编辑。</li>
+ <li>使用 <code>if()</code> 代码块 (<code>if(current.multimedia.length &gt; 0) { ... }</code>) 检测每篇文章是否有对应的图片 (有些没有) 。只有当检测到有时,显示首张图片 (否则抛出异常)。</li>
+ <li>把 &lt;div&gt; 结点设置一个 class 属性 "clearfix",这样清理操作 (apply clearing to it) 就很容易了。</li>
+ </ul>
+ </li>
+</ul>
+
+<h3 id="添加分页按钮">添加分页按钮</h3>
+
+<p>为了使分页按钮工作,我们将增加(或减少)<code>pageNumber</code> 变量的值,然后用页面 url 参数中包含的新值重新运行 fetch 请求。这么做是因为 NYTimes API 每次最多只返回 10 篇文章 — 如果查询结果超过 10 篇,而 <code>page</code> URL 参数设为 0 (或者忽略这个参数 — 0 是默认值),只返回前 10 篇 (0-9) ,后续的 10 篇 (10-19) 对应参数值 1 ,以此类推。</p>
+
+<p>根据这个特性就可以轻松实现一个简单的翻页函数。</p>
+
+<ol>
+ <li>
+ <p>下面的代码中,在 <code><a href="/en-US/docs/Web/API/EventTarget/addEventListener">addEventListener()</a></code> 函数中添加这 2 个新事件,即把 <code>nextPage()</code> 和 <code>previousPage()</code> 函数链接到相应的按键点击事件上:</p>
+
+ <pre class="brush: js notranslate">nextBtn.addEventListener('click', nextPage);
+previousBtn.addEventListener('click', previousPage);</pre>
+ </li>
+ <li>
+ <p>在上边代码的基础上,定义这 2 个函数 — 添加下面代码:</p>
+
+ <pre class="brush: js notranslate">function nextPage(e) {
+ pageNumber++;
+ fetchResults(e);
+};
+
+function previousPage(e) {
+ if(pageNumber &gt; 0) {
+ pageNumber--;
+ } else {
+ return;
+ }
+ fetchResults(e);
+};</pre>
+
+ <p>第一个函数很简单 — 增加 <code>pageNumber</code> 变量,然后再次运行 <code>fetchResults()</code> 函数 展示下一页的结果。</p>
+
+ <p>第二个函数基本上执行相反的操作,不过有个额外的步骤是检测 <code>pageNumber</code> 在 -1 之前是否已经是 0 — 如果 fetch 请求的 <code>page</code> URL 参数是负数,会导致错误。如果 <code>pageNumber</code> 已经是 0 ,则直接执行 <code><a href="/en-US/docs/Web/JavaScript/Reference/Statements/return">return</a></code> 退出函数,避免多余的计算。 (如果当前页面已经是首页,就不需要重新加载)。</p>
+ </li>
+</ol>
+
+<div class="blockIndicator note">
+<p><strong>Note</strong>: 完整代码参考 <a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/third-party-apis/nytimes/index.html">finished nytimes API example code on GitHub</a> (实例参考 <a href="https://mdn.github.io/learning-area/javascript/apis/third-party-apis/nytimes/">see it running live here</a>).</p>
+</div>
+
+<h2 id="YouTube示例">YouTube示例</h2>
+
+<p>我们还做了另外一个示例用来学习 — 参考 <a href="https://mdn.github.io/learning-area/javascript/apis/third-party-apis/youtube/">YouTube video search example</a>. 这个示例用了下面 2 个 API:</p>
+
+<ul>
+ <li><a href="https://developers.google.com/youtube/v3/docs/">YouTube Data API</a> ,搜索 YouTube 视频并返回结果。</li>
+ <li><a href="https://developers.google.com/youtube/iframe_api_reference">YouTube IFrame Player API</a> ,把返回的视频查询结果展示到 IFrame 视频播放器里,然后就可以播放了。</li>
+</ul>
+
+<p>这个示例的有趣之处在于,它把两个第三方 API 结合起来做了一个应用。第一个 API 是 RESTful API ,而第二个 API 更像是 Mapquest (有 API 专用方法 (API-specific methods), etc.)。但是,值得注意的是,这两个 API 均需要将 JavaScript 库应用于该页面。RESTful API 自己有用于处理 HTTP 请求并返回结果的函数。</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/14823/youtube-example.png" style="border-style: solid; border-width: 1px; display: block; height: 389px; margin: 0px auto; width: 700px;"></p>
+
+<p>本文不会对该示例做过多的叙述 — <a href="https://github.com/mdn/learning-area/tree/master/javascript/apis/third-party-apis/youtube">源码</a> 中有详细的注释。</p>
+
+<p>运行源码需要:</p>
+
+<ul>
+ <li>从 <a href="https://cloud.google.com/">Google Cloud</a> 获取 API key.</li>
+ <li>找到源码中的 <code>ENTER-API-KEY-HERE</code> 字符串,替换为 API key.</li>
+ <li>把示例运行在 web 服务器上。只是用浏览器打开文件是不会工作的 (例如,通过 <code>file://</code> URL 打开).</li>
+</ul>
+
+<h2 id="小结">小结</h2>
+
+<p>本文介绍了如何使用第三方 API 给网页添加功能。</p>
+
+<p>{{PreviousMenuNext("Learn/JavaScript/Client-side_web_APIs/Fetching_data", "Learn/JavaScript/Client-side_web_APIs/Drawing_graphics", "Learn/JavaScript/Client-side_web_APIs")}}</p>
+
+<h2 id="本章目录">本章目录</h2>
+
+<ul>
+ <li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Introduction">Introduction to web APIs</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Manipulating_documents">Manipulating documents</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Fetching_data">Fetching data from the server</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Third_party_APIs">Third party APIs</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Drawing_graphics">Drawing graphics</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Video_and_audio_APIs">Video and audio APIs</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Client-side_storage">Client-side storage</a></li>
+</ul>
diff --git a/files/zh-cn/learn/javascript/client-side_web_apis/video_and_audio_apis/index.html b/files/zh-cn/learn/javascript/client-side_web_apis/video_and_audio_apis/index.html
new file mode 100644
index 0000000000..45d379f4b4
--- /dev/null
+++ b/files/zh-cn/learn/javascript/client-side_web_apis/video_and_audio_apis/index.html
@@ -0,0 +1,499 @@
+---
+title: 视频和音频API
+slug: Learn/JavaScript/Client-side_web_APIs/Video_and_audio_APIs
+translation_of: Learn/JavaScript/Client-side_web_APIs/Video_and_audio_APIs
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/JavaScript/Client-side_web_APIs/Drawing_graphics", "Learn/JavaScript/Client-side_web_APIs/Client-side_storage", "Learn/JavaScript/Client-side_web_APIs")}}</div>
+
+<p class="summary">HTML5提供了用于在文档中嵌入富媒体的元素 — {{htmlelement("video")}}和{{htmlelement("audio")}} — 这些元素通过自带的API来控制视频或音频的播放,定位进度等。本文将向你展示如何执行一些常见的任务,如创建自定义播放控件。</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">前提:</th>
+ <td>JavaScript基础(见<a href="/zh-CN/docs/Learn/JavaScript/First_steps">JavaScript第一步</a>,<a href="/zh-CN/docs/learn/JavaScript/Building_blocks">创建JavaScript代码块</a>,<a href="/zh-CN/docs/Learn/JavaScript/Objects">JavaScript对象入门</a>),<a href="/zh-CN/docs/Learn/JavaScript/Client-side_web_APIs/Introduction">Web API简介</a></td>
+ </tr>
+ <tr>
+ <th scope="row">目标:</th>
+ <td>学习如何通过浏览器API来控制视频和音频的播放。</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="HTML5视频和音频">HTML5视频和音频</h2>
+
+<p>{{htmlelement("video")}}和{{htmlelement("audio")}}元素允许我们把视频和音频嵌入到网页当中。就像我们在<a href="/zh-CN/docs/Learn/HTML/Multimedia_and_embedding/Video_and_audio_content">音频和视频内容</a>文中展示的一样,一个典型的实现如下所示:</p>
+
+<pre class="brush: html">&lt;video controls&gt;
+ &lt;source src="rabbit320.mp4" type="video/mp4"&gt;
+ &lt;source src="rabbit320.webm" type="video/webm"&gt;
+ &lt;p&gt;Your browser doesn't support HTML5 video. Here is a &lt;a href="rabbit320.mp4"&gt;link to the video&lt;/a&gt; instead.&lt;/p&gt;
+&lt;/video&gt;</pre>
+
+<p>上述代码将会在浏览器内部创建一个如下图所示的视频播放器:</p>
+
+<p>{{EmbedGHLiveSample("learning-area/html/multimedia-and-embedding/video-and-audio-content/multiple-video-formats.html", '100%', 380)}}</p>
+
+<p>你可以点击上面的文章链接来查看相关HTML元素的所有特性;但在这篇文章中,主要目的是学习我们最感兴趣的{{htmlattrxref("controls", "video")}}属性,它会启用默认的播放设置。如果没有指定该属性,则播放器中不会显示相关控件:</p>
+
+<p>{{EmbedGHLiveSample("learning-area/html/multimedia-and-embedding/video-and-audio-content/multiple-video-formats-no-controls.html", '100%', 380)}}</p>
+
+<p>至此,你可能会觉得这个属性作用不大,但是它确实很有优势。使用原生浏览器控件的一个很大的问题在于,它们在各个浏览器中都不相同 — 对于跨浏览器的支持并不是很好!另一个问题是,在大多数浏览器中原生控件难以通过键盘来操作。</p>
+
+<p>你可以通过隐藏本地控件(通过删除controls属性),然后使用HTML,CSS和JavaScript编写自己的代码来解决这两个问题。 在下一节中,我们将看到如何通过一些可用的工具来实现。</p>
+
+<h2 id="HTMLMediaElement_API">HTMLMediaElement API</h2>
+
+<p>作为HTML5规范的一部分,{{domxref("HTMLMediaElement")}} API提供允许你以编程方式来控制视频和音频播放的功能—例如 {{domxref("HTMLMediaElement.play()")}}, {{domxref("HTMLMediaElement.pause()")}},等。该接口对{{htmlelement("audio")}}和{{htmlelement("video")}}两个元素都是可用的,因为在这两个元素中要实现的功能几乎是相同的。让我们通过一个例子来一步步演示一些功能。</p>
+
+<p>我们最终的示例(和功能)将会如下所示:</p>
+
+<p>{{EmbedGHLiveSample("learning-area/javascript/apis/video-audio/finished/", '100%', 360)}}</p>
+
+<h3 id="入门">入门</h3>
+
+<p>想要使用这个示例的代码来入门,请下载<a href="https://github.com/mdn/learning-area/raw/master/javascript/apis/video-audio/start/media-player-start.zip">media-player-start.zip</a> 并解压到您的硬盘上的一个新建目录里。如果想要下载<a href="">examples repo</a>,它位于<code>javascript/apis/video-audio/start/ </code>路径下。</p>
+
+<p>下载并解压之后,如果您加载这个HTML,你将会看到一个通过浏览器原生播放控件渲染的非常一般的HTML5视频播放器。</p>
+
+<h4 id="探索_HTML">探索 HTML</h4>
+
+<p>打开HTML index文件。你将看到一些功能;HTML由视频播放器和它的控件所控制:</p>
+
+<pre>&lt;div class="player"&gt;
+  &lt;video controls&gt;
+    &lt;source src="video/sintel-short.mp4" type="video/mp4"&gt;
+    &lt;source src="video/sintel-short.mp4" type="video/webm"&gt;
+    &lt;!-- fallback content here --&gt;
+  &lt;/video&gt;
+  &lt;div class="controls"&gt;
+    &lt;button class="play" data-icon="P" aria-label="play pause toggle"&gt;&lt;/button&gt;
+    &lt;button class="stop" data-icon="S" aria-label="stop"&gt;&lt;/button&gt;
+    &lt;div class="timer"&gt;
+ &lt;div&gt;&lt;/div&gt;
+ &lt;span aria-label="timer"&gt;00:00&lt;/span&gt;
+ &lt;/div&gt;
+    &lt;button class="rwd" data-icon="B" aria-label="rewind"&gt;&lt;/button&gt;
+    &lt;button class="fwd" data-icon="F" aria-label="fast forward"&gt;&lt;/button&gt;
+  &lt;/div&gt;
+&lt;/div&gt;
+</pre>
+
+<ul>
+ <li>整个播放器被包装在一个{{htmlelement("div")}}元素之中,所以如果有必要的话,可以把它作为一个单元整体来设置其样式。</li>
+ <li>{{htmlelement("video")}}元素层包含两个{{htmlelement("source")}}元素,这样可以根据浏览器来加载其所支持的不同视频格式。</li>
+ <li>控件 HTML 大概是最有趣的:
+ <ul>
+ <li>我们有四个 {{htmlelement("button")}} — play/pause, stop, rewind, and fast forward.</li>
+ <li>每个<code>&lt;button&gt;</code> 都有一个<code>class名</code> , 一个<code>data-icon</code> 属性来决定在每个按钮上显示什么图标 (在下一节讲述它是如何工作的), 和一个<code>aria-label</code> 属性为每一个按钮提供容易理解的描述, 即使我们没有在tags内提供可读的标签。当用户关注这些元素时含有<code>aria-label</code> 属性的内容也会被讲述人读出来。</li>
+ <li>有一个设定的计时器 {{htmlelement("div")}}用来报告已经播放的时长。为了好玩,我们提供了两种报告机制 — 一个 {{htmlelement("span")}} 包含了流逝时间的分钟和秒,和一个额外的<code>&lt;div&gt;</code> 用来创建一个水平的随着时间增加而增长的进度条。要想了解完成版本看上去是咋样的, <a href="https://mdn.github.io/learning-area/javascript/apis/video-audio/finished/">点击查看完成版本</a>.</li>
+ </ul>
+ </li>
+</ul>
+
+<h4 id="探索_CSS">探索 CSS</h4>
+
+<p>现在打开CSS文件来查看里面的内容。例子中的CSS样式并不是很复杂, 我们突出了最主要的一部分。首先注意<code>.controls</code> 的样式:</p>
+
+<pre class="brush: css">.controls {
+ visibility: hidden;
+ opacity: 0.5;
+ width: 400px;
+ border-radius: 10px;
+ position: absolute;
+ bottom: 20px;
+ left: 50%;
+ margin-left: -200px;
+ background-color: black;
+ box-shadow: 3px 3px 5px black;
+ transition: 1s all;
+ display: flex;
+}
+
+.player:hover .controls, player:focus .controls {
+ opacity: 1;
+}
+</pre>
+
+<ul>
+ <li>我们从设置为<code>hidden</code>的自定义控件{{cssxref("visibility")}}开始。稍后在我们的JavaScript中, 我们将控件设置为 <code>visible</code>, 并且从<code>&lt;video&gt;</code> 元素中移除<code>controls</code> 属性。这是因为, 如果JavaScript由于某种原因没有加载, 用户依然可以使用原生的控件播放视频。</li>
+ <li>默认情况下,我们将控件的<code>opacity</code>设置为0.5 {{cssxref("opacity")}},这样当您尝试观看视频时,它们就不会分散注意力。 只有当您将鼠标悬停/聚焦在播放器上时,控件才会完全不透明。</li>
+ <li>我们使用Flexbox({{cssxref("display")}}: flex)布置控制栏内的按钮,以简化操作。</li>
+</ul>
+
+<p>接下来,让我们看看我们的按钮图标:</p>
+
+<pre class="brush: css">@font-face {
+ font-family: 'HeydingsControlsRegular';
+ src: url('fonts/heydings_controls-webfont.eot');
+ src: url('fonts/heydings_controls-webfont.eot?#iefix') format('embedded-opentype'),
+ url('fonts/heydings_controls-webfont.woff') format('woff'),
+ url('fonts/heydings_controls-webfont.ttf') format('truetype');
+ font-weight: normal;
+ font-style: normal;
+}
+
+button:before {
+ font-family: HeydingsControlsRegular;
+ font-size: 20px;
+ position: relative;
+ content: attr(data-icon);
+ color: #aaa;
+ text-shadow: 1px 1px 0px black;
+}</pre>
+
+<p>首先在CSS的最上方我们使用 {{cssxref("@font-face")}} 块来导入自定义Web字体。这是一种图标字体 —— 字母表中的所有字符都是各种常用图标,你可以尝试在程序中调用。</p>
+
+<p>接下来,我们使用这些内容来显示每个按钮上的图标:</p>
+
+<ul>
+ <li>我们使用 {{cssxref("::before")}} 选择器在每个 {{htmlelement("button")}} 元素之前显示内容。</li>
+ <li>我们使用 {{cssxref("content")}} 属性将各情况下要显示的内容设置为 <code><a href="/en-US/docs/Learn/HTML/Howto/Use_data_attributes">data-icon</a></code> 属性的内容。例如在播放按钮的情况下,<code><a href="/en-US/docs/Learn/HTML/Howto/Use_data_attributes">data-icon </a></code>包含大写的“P”。</li>
+ <li>我们使用 {{cssxref("font-family")}} 将自定义Web字体应用于我们的按钮上。在该字体中“P”对应的是“播放”图标,因此播放按钮上显示“播放”图标。</li>
+</ul>
+
+<p>图标字体非常酷有很多原因 —— 减少HTTP请求,因为您不需要将这些图标作为图像文件下载。同时具有出色的可扩展性,以及您可以使用文本属性来设置它们的样式 —— 例如 {{cssxref("color")}} 和 {{cssxref("text-shadow")}}。</p>
+
+<p>最后让我们来看看进度条的 CSS:</p>
+
+<pre class="brush: css">.timer {
+ line-height: 38px;
+ font-size: 10px;
+ font-family: monospace;
+ text-shadow: 1px 1px 0px black;
+ color: white;
+ flex: 5;
+ position: relative;
+}
+
+.timer div {
+ position: absolute;
+ background-color: rgba(255,255,255,0.2);
+ left: 0;
+ top: 0;
+ width: 0;
+ height: 38px;
+ z-index: 2;
+}
+
+.timer span {
+ position: absolute;
+ z-index: 3;
+ left: 19px;
+}</pre>
+
+<ul>
+ <li>我们将外部 <code>.timer</code>  <code>&lt;div&gt;</code> 设为flex:5,这样它占据了控件栏的大部分宽度。 我们还设置 {{cssxref("position")}}<code>: relative</code>,这样我们就可以根据它的边界方便地定位元素,而不是{{htmlelement("body")}} 元素的边界。</li>
+ <li>内部<code> &lt;div&gt; </code> <code>position:absolute</code> 绝对定位于外部 <code>&lt;div&gt;</code> 的顶部。 它的初始宽度为0,因此根本无法看到它。随着视频的播放,JavaScript将动态的增加其宽度。</li>
+ <li><code>&lt;span&gt;</code> 也绝对位于计时器/进度条 <code>timer</code> 栏的左侧附近。</li>
+ <li>我们还对内部 <code>&lt;div&gt;</code> 和 <code>&lt;span&gt;</code> 定义适当数值的 {{cssxref("z-index")}} ,以便进度条显示在最上层,内部<code> &lt;div&gt;</code> 显示在下层。 这样,我们确保我们可以看到所有信息 —— 一个box不会遮挡另一个。</li>
+</ul>
+
+<h3 id="实现_JavaScript">实现 JavaScript</h3>
+
+<p>我们已经有了一个相当完整的 HTML 和CSS 接口;现在我们只需要调通所有按钮以使控件正常工作。</p>
+
+<ol>
+ <li>
+ <p>在与 index.html 文件相同的目录下创建新的JavaScript文件。命名为 <code>custom-player.js</code>。</p>
+ </li>
+ <li>
+ <p>在此文件的顶部,插入以下代码:</p>
+
+ <pre class="brush: js">var media = document.querySelector('video');
+var controls = document.querySelector('.controls');
+
+var play = document.querySelector('.play');
+var stop = document.querySelector('.stop');
+var rwd = document.querySelector('.rwd');
+var fwd = document.querySelector('.fwd');
+
+var timerWrapper = document.querySelector('.timer');
+var timer = document.querySelector('.timer span');
+var timerBar = document.querySelector('.timer div');
+</pre>
+
+ <p>这里我们创建变量来保存对我们想要操作的所有对象的引用。有如下三组:</p>
+
+ <ul>
+ <li> <code>&lt;video&gt;</code> 元素,和控制栏。</li>
+ <li>播放/暂停,停止,快退,和快进按钮。</li>
+ <li>进度条外面的 <code>&lt;div&gt;</code>,数字计时器的 <code>&lt;span&gt;</code>,以及内部的 <code>&lt;div&gt;</code> 会随着视频播放逐渐变宽。</li>
+ </ul>
+ </li>
+ <li>
+ <p>接下来,在代码的底部插入以下内容:</p>
+
+ <pre class="brush: js">media.removeAttribute('controls');
+controls.style.visibility = 'visible';</pre>
+
+ <p>这两行从视频中删除默认浏览器控件,并使自定义控件可见。</p>
+ </li>
+</ol>
+
+<h4 id="播放和暂停视频">播放和暂停视频</h4>
+
+<p>让我们实现或许是最重要的控制——播放/暂停按钮。</p>
+
+<ol>
+ <li>
+ <p>首先,将以下内容添加到您代码的底部,以便于在单击播放按钮时调用 <code>playPauseMedia()</code>函数:</p>
+
+ <pre class="brush: js">play.addEventListener('click', playPauseMedia);
+</pre>
+ </li>
+ <li>
+ <p>现在定义 <code>playPauseMedia()</code> 函数——再次添加以下内容到您代码底部:</p>
+
+ <pre class="brush: js">function playPauseMedia() {
+ if(media.paused) {
+ play.setAttribute('data-icon','u');
+ media.play();
+ } else {
+ play.setAttribute('data-icon','P');
+ media.pause();
+ }
+}</pre>
+
+ <p><span class="tlid-translation translation" lang="zh-CN"><span title="">我们使用if语句来检查</span></span>视频<span class="tlid-translation translation" lang="zh-CN"><span title="">是否暂停。如果</span></span>视频<span class="tlid-translation translation" lang="zh-CN"><span title="">已暂停,</span></span>{{domxref("HTMLMediaElement.paused")}} 属性将返回 true,任何视频没有播放的时间,包括第一次加载时处于0的时间段都是视频暂停状态。如果已暂停,我们把play按钮的 <code>data-icon</code> 属性值设置成"u", 用以表示 "暂停" 按钮图标,并且调用{{domxref("HTMLMediaElement.play()")}} 函数播放视频。</p>
+
+ <p>点击第二次,按钮将会切换回去——"播放"按钮图标将会再次显示,并且视频将会被{{domxref("HTMLMediaElement.paused()")}} 函数暂停。</p>
+ </li>
+</ol>
+
+<h4 id="暂停视频">暂停视频</h4>
+
+<ol>
+ <li>
+ <p>接下来,让我们添加处理视频停止的方法。添加以下的 <code><a href="/en-US/docs/Web/API/EventTarget/addEventListener">addEventListener() </a></code>行在你之前添加的内容的下面:</p>
+
+ <pre class="brush: js">stop.addEventListener('click', stopMedia);
+media.addEventListener('ended', stopMedia);
+</pre>
+
+ <p>{{event("click")}} 事件很明显——我们想要在点击停止按钮的时候停止视频通过运行我们的 <code>stopMedia()</code> 函数。然而我们也希望停止视频当视频播放完成时——由{{event("ended")}} 事件标记,所以我们也会设置一个监听器在此事件触发时运行函数。</p>
+ </li>
+ <li>
+ <p>接下来,让我们定义 <code>stopMedia()</code>—— 在 <code>playPauseMedia() 后面</code>添加以下函数:</p>
+
+ <pre>function stopMedia() {
+ media.pause();
+ media.currentTime = 0;
+ play.setAttribute('data-icon','P');
+}
+</pre>
+
+ <p>在 HTMLMediaElement API 中没有 <code>stop()</code> 方法——等效的办法是先用 <code>pause()</code> 暂停视频,然后设置{{domxref("HTMLMediaElement.currentTime","currentTime")}} 属性为0。设置 <code>currentTime</code> 的值(单位:秒)将会立刻使视频跳到该位置。</p>
+
+ <p>之后要做的事是把显示的图标设置成“播放”图标。无论视频使暂停还是正在播放,您都希望它随后可以播放。</p>
+ </li>
+</ol>
+
+<h4 id="探索快进和快退">探索快进和快退</h4>
+
+<p>有许多方法可以实现快退和快进功能;在这里,我们向您展示了一种相对复杂的方式,当按意外顺序按下不同的按钮时,它不会中断。</p>
+
+<ol>
+ <li>
+ <p>首先,在前面的代码之下添加以下两个<code><a href="/en-US/docs/Web/API/EventTarget/addEventListener">addEventListener()</a></code>:</p>
+
+ <pre class="brush: js">rwd.addEventListener('click', mediaBackward);
+fwd.addEventListener('click', mediaForward);
+</pre>
+ </li>
+ <li>
+ <p>现在转到事件处理函数 - 在以前的函数下面添加以下代码来定义<code>mediaBackward()</code>和<code>mediaForward()</code>:</p>
+
+ <pre class="brush: js">var intervalFwd;
+var intervalRwd;
+
+function mediaBackward() {
+ clearInterval(intervalFwd);
+ fwd.classList.remove('active');
+
+ if(rwd.classList.contains('active')) {
+ rwd.classList.remove('active');
+ clearInterval(intervalRwd);
+ media.play();
+ } else {
+ rwd.classList.add('active');
+ media.pause();
+ intervalRwd = setInterval(windBackward, 200);
+ }
+}
+
+function mediaForward() {
+ clearInterval(intervalRwd);
+ rwd.classList.remove('active');
+
+ if(fwd.classList.contains('active')) {
+ fwd.classList.remove('active');
+ clearInterval(intervalFwd);
+ media.play();
+ } else {
+ fwd.classList.add('active');
+ media.pause();
+ intervalFwd = setInterval(windForward, 200);
+ }
+}
+</pre>
+
+ <p>您会注意到,首先我们初始化两个变量 -  intervalFwd和intervalRwd  - 您将在后面发现它们的用途。</p>
+
+ <p>让我们逐步浏览<code>mediaBackward()</code>(<code>mediaForward()</code>的功能性完全相同,但效果相反):</p>
+
+ <ol>
+ <li>我们清除在快进功能上设置的所有classes和intervals ––这样做是因为如果我们在按下<code>fwd</code>(快进)按钮后再按下<code>rwd</code>(快退)按钮,就可以取消任何快进的功能并将其替换为快退功能。如果我们试图同时做到这两点,播放器就会暂停。</li>
+ <li>使用<code>if</code>语句检查是否已在<code>rwd</code>按钮上设置了用来指示它已被按下的<code>active</code>类。{{domxref("classList")}}是一个非常方便的属性,存在于每个元素上 ––它包含元素上设置的所有类的列表,以及添加/删除类的方法等。使用<code>classList.contains()</code>方法检查列表是否包含<code>active</code>类。这将返回布尔值<code>true/false</code>结果。</li>
+ <li>如果在<code>rwd</code>按钮上设置了<code>active</code>,我们使用<code>classList.remove()</code>删除它,清除第一次按下按钮时设置的间隔(参见下面的更多解释),并使用{{domxref("HTMLMediaElement.play()")}}取消快退并开始正常播放视频。</li>
+ <li>如果尚未设置,使用<code>classList.add()</code>将<code>active</code>类添加到<code>rwd</code>按钮,使用{{domxref("HTMLMediaElement.pause()")}}暂停视频,然后设置<code>intervalRwd</code>变量为{{domxref("WindowOrWorkerGlobalScope.setInterval", "setInterval()")}}的调用。调用时,<code>setInterval()</code>会创建一个活动间隔,这意味着它每隔x毫秒运行一个作为第一个参数给出的函数,其中x是第二个参数的值。 所以这里我们每200毫秒运行一次<code>windBackward()</code>函数 ––我们将使用此函数不断向后滚动(快退动作)视频。要停止{{domxref("WindowOrWorkerGlobalScope.setInterval", "setInterval()")}}运行,你必须调用{{domxref("WindowOrWorkerGlobalScope.clearInterval", "clearInterval()")}},给它识别要清除的间隔的名称,在本例中是变量名称intervalRwd(请参阅函数中较早的clearInterval()调用)。</li>
+ </ol>
+ </li>
+ <li>
+ <p>最后,对于本节,定义在setInterval()调用中需要调用的windBackward()和windForward()函数。在以上两个函数下面添加以下内容:</p>
+
+ <pre class="brush: js">function windBackward() {
+ if(media.currentTime &lt;= 3) {
+ rwd.classList.remove('active');
+ clearInterval(intervalRwd);
+ stopMedia();
+ } else {
+ media.currentTime -= 3;
+ }
+}
+
+function windForward() {
+ if(media.currentTime &gt;= media.duration - 3) {
+ fwd.classList.remove('active');
+ clearInterval(intervalFwd);
+ stopMedia();
+ } else {
+ media.currentTime += 3;
+ }
+}</pre>
+
+ <p>同样,我们将完成这些功能中的第一个,因为它们几乎完全相同,但彼此相反。在<code>windBackward()</code>中,我们执行以下操作 ––请记住,当间隔处于活动状态时,此函数每200毫秒运行一次。</p>
+
+ <ol>
+ <li>我们从一个<code>if</code>语句开始,该语句检查当前时间是否小于3秒,即,如果再倒退三秒将使其超过视频的开始。这会导致奇怪的行为,所以如果是这种情况,我们通过调用<code>stopMedia()</code>来停止视频播放,从倒带按钮中删除<code>active</code>类,并清除<code>intervalRwd</code>间隔以停止快退功能。如果我们没有做到最后一步,视频将永远保持快退。</li>
+ <li>如果当前时间不在视频开始的3秒内,我们只需通过执行<code>media.currentTime-=3</code>从当前时间中删除三秒。因此,实际上,我们将视频快退3秒,每200毫秒一次。</li>
+ </ol>
+ </li>
+</ol>
+
+<h4 id="更新已用时间">更新已用时间</h4>
+
+<p>我们要实施的媒体播放器的最后一块是显示的时间。为此,我们将运行一个函数,以便每次在&lt;video&gt;元素上触发 {{event("timeupdate")}}事件时更新时间显示。此事件触发的频率取决于您的浏览器,CPU电源等(<a href="http://stackoverflow.com/questions/9678177/how-often-does-the-timeupdate-event-fire-for-an-html5-video">see this stackoverflow post</a>)。</p>
+
+<p>在代码下方添加<code>addEventListener()</code>行:</p>
+
+<pre class="brush: js">media.addEventListener('timeupdate', setTime);</pre>
+
+<p>现在定义<code>setTime()</code>函数。在文件底部添加以下内容:</p>
+
+<pre class="brush: js">function setTime() {
+ var minutes = Math.floor(media.currentTime / 60);
+ var seconds = Math.floor(media.currentTime - minutes * 60);
+ var minuteValue;
+ var secondValue;
+
+ if (minutes &lt; 10) {
+ minuteValue = '0' + minutes;
+ } else {
+ minuteValue = minutes;
+ }
+
+ if (seconds &lt; 10) {
+ secondValue = '0' + seconds;
+ } else {
+ secondValue = seconds;
+ }
+
+ var mediaTime = minuteValue + ':' + secondValue;
+ timer.textContent = mediaTime;
+
+ var barLength = timerWrapper.clientWidth * (media.currentTime/media.duration);
+ timerBar.style.width = barLength + 'px';
+}
+</pre>
+
+<p>这是一个相当长的函数,所以让我们一步一步地完成它:</p>
+
+<ol>
+ <li>首先,我们计算{{domxref("HTMLMediaElement.currentTime")}} 值中的分钟数和秒数。</li>
+ <li>然后我们初始化另外两个变量 ––<code>minuteValue</code>和<code>secondValue</code>。</li>
+ <li>两个<code>if</code>语句计算出分钟数和秒数是否小于10.如果是这样,它们会在类似数值时钟显示工作的方式上为值添加前置的零。</li>
+ <li>要显示的实际时间值设置为<code>minuteValue</code>加上冒号字符加<code>secondValue</code>。</li>
+ <li>计时器的{{domxref("Node.textContent")}}值设置为时间值,因此它显示在UI中。</li>
+ <li>我们应该设置内部<code>&lt;div&gt;</code>的长度是通过首先计算外部<code>&lt;div&gt;</code>的宽度来计算出来的(任何元素的{{domxref("HTMLElement.clientWidth", "clientWidth")}} 属性将包含它的长度),然后乘以{{domxref("HTMLMediaElement.currentTime")}}除以媒体的总{{domxref("HTMLMediaElement.duration")}}。</li>
+ <li>我们将内部<code>&lt;div&gt;</code>的宽度设置为等于计算的条形长度加上“px”,因此它将设置为该像素数</li>
+</ol>
+
+<h4 id="修复播放和暂停">修复播放和暂停</h4>
+
+<p>还有一个问题需要修复。如果在快退或快进功能激活时按下播放/暂停或停止按钮,它们就不起作用。我们如何修复它以便取消<code>rwd / fwd</code>按钮功能并按照您的预期播放/停止视频?这很容易解决。</p>
+
+<p>首先,在<code>stopMedia()</code>函数中添加以下行 ––任何地方都可以:</p>
+
+<pre class="brush: js">rwd.classList.remove('active');
+fwd.classList.remove('active');
+clearInterval(intervalRwd);
+clearInterval(intervalFwd);
+</pre>
+
+<p>现在再次添加相同的行(上面的四行)到<code>playPauseMedia()</code>函数的最开头(就在<code>if</code>语句的开始之前)。</p>
+
+<p>此时,您可以删除<code>windBackward()</code>和<code>windForward()</code>函数中的等效行,因为该函数已在<code>stopMedia()</code>函数中实现。</p>
+
+<div class="blockIndicator note">
+<p><strong>注意</strong>:您还可以通过创建运行这些行的单独函数来进一步提高代码的效率,然后在需要的任何地方调用它,而不是在代码中多次重复这些行。但是我们会把这些留给你自己。</p>
+</div>
+
+<h2 id="小结">小结</h2>
+
+<p><span class="tlid-translation translation" lang="zh-CN"><span title="">我想我们已经在这篇文章中教过你足够多了。使用</span></span>{{domxref("HTMLMediaElement")}} <span class="tlid-translation translation" lang="zh-CN"><span title="">API可以为创建简单的视频和音频播放器提供丰富的可用功能,然而这只是冰山一角。</span> <span title="">有关更复杂和有趣功能的链接,请参阅下面的“另请参阅”部分。</span></span></p>
+
+<p><span class="tlid-translation translation" lang="zh-CN"><span title="">以下是一些有关如何增强我们构建的现有示例的建议:</span></span></p>
+
+<ol>
+ <li><span class="tlid-translation translation" lang="zh-CN"><span title="">如果视频是一小时或更长时间(嗯,它不会显示小时;只有几分钟和几秒),当前显示时间会中断。</span> <span title="">你能弄清楚如何更改示例以使其显示小时数吗?</span></span></li>
+ <li>
+ <p><span class="tlid-translation translation" lang="zh-CN"><span title="">由于 <code>&lt;audio&gt; </code>元素具有相同的{{domxref("HTMLMediaElement")}}功能,因此您可以轻松地将此播放器用于<code> &lt;audio&gt; </code>元素。</span> <span title="">试着这样做。</span></span></p>
+ </li>
+ <li>
+ <p><span class="tlid-translation translation" lang="zh-CN"><span title="">你能找到一种方法将计时器内部的<code> &lt;div&gt; </code>元素转换为真正的搜索条/ 滑动条- 也就是说,当你点击条形图上的某个位置时,它会跳转到视频播放中的相对位置吗?</span></span> <span class="tlid-translation translation" lang="zh-CN"><span title="">作为提示,您可以通过</span></span><code><a href="/ch-ZN/docs/Web/API/Element/getBoundingClientRect">getBoundingClientRect()</a></code> <span class="tlid-translation translation" lang="zh-CN"><span title="">方法找出元素左/右和上/下侧的X和Y值</span></span> , 而且你可以通过 {{domxref("Document")}} 对象调用的click事件的事件对象找到鼠标单击的坐标。举个栗子:</p>
+
+ <pre class="brush: js">document.onclick = function(e) {
+ console.log(e.x) + ',' + console.log(e.y)
+}</pre>
+ </li>
+</ol>
+
+<h2 id="另请参阅">另请参阅</h2>
+
+<ul>
+ <li>{{domxref("HTMLMediaElement")}}</li>
+ <li><a href="/zh-CN/docs/Learn/HTML/Multimedia_and_embedding/Video_and_audio_content">视频和音频内容</a>— &lt;video&gt;和&lt;audio&gt;的简单指南.</li>
+ <li><a href="/en-US/docs/Web/Apps/Fundamentals/Audio_and_video_delivery">Audio and video delivery</a> — detailed guide to delivering media inside the browser, with many tips, tricks, and links to further more advanced tutorials.</li>
+ <li><a href="/en-US/docs/Web/Apps/Fundamentals/Audio_and_video_manipulation">Audio 与 video 操作</a> — 操作audio 和 video 的详细指南,例如:使用 <a href="/en-US/docs/Web/API/Canvas_API">Canvas API</a>, <a href="/en-US/docs/Web/API/Web_Audio_API">Web Audio API</a>, 等等。</li>
+ <li>{{htmlelement("video")}} and {{htmlelement("audio")}} reference pages.</li>
+ <li>
+ <p><a href="/en-US/docs/Web/HTML/Supported_media_formats">Media formats supported by the HTML audio and video elements</a>.</p>
+ </li>
+</ul>
+
+<p>{{PreviousMenuNext("Learn/JavaScript/Client-side_web_APIs/Drawing_graphics", "Learn/JavaScript/Client-side_web_APIs/Client-side_storage", "Learn/JavaScript/Client-side_web_APIs")}}</p>
+
+<h2 id="本章目录">本章目录</h2>
+
+<ul>
+ <li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Introduction">Introduction to web APIs</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Manipulating_documents">Manipulating documents</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Fetching_data">Fetching data from the server</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Third_party_APIs">Third party APIs</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Drawing_graphics">Drawing graphics</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Video_and_audio_APIs">Video and audio APIs</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Client-side_storage">Client-side storage</a></li>
+</ul>
diff --git a/files/zh-cn/learn/javascript/first_steps/a_first_splash/index.html b/files/zh-cn/learn/javascript/first_steps/a_first_splash/index.html
new file mode 100644
index 0000000000..d54ec0c4ed
--- /dev/null
+++ b/files/zh-cn/learn/javascript/first_steps/a_first_splash/index.html
@@ -0,0 +1,744 @@
+---
+title: JavaScript 初体验
+slug: Learn/JavaScript/First_steps/A_first_splash
+tags:
+ - JavaScript
+ - 事件
+ - 函数
+ - 初学者
+ - 变量
+ - 学习
+ - 操作符
+ - 教程
+ - 添加
+translation_of: Learn/JavaScript/First_steps/A_first_splash
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/JavaScript/First_steps/What_is_JavaScript", "Learn/JavaScript/First_steps/What_went_wrong", "Learn/JavaScript/First_steps")}}</div>
+
+<p class="summary">现在,你已经学到了一些 JavaScript 的理论知识,以及用 JavaScript 能够做些什么。下面我们会提供一个讲解 Javascript 基本特性的 Crash Course,课程是一个全面的实践性项目——循序渐进地构建一个简易版“猜数字”游戏。</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">预备知识:</th>
+ <td>计算机基础知识,初步理解HTML和CSS,了解JavaScript。</td>
+ </tr>
+ <tr>
+ <th scope="row">目标:</th>
+ <td>获得编写JavaScript的初体验,初步了解编写JavaScript时会涉及哪些内容。</td>
+ </tr>
+ </tbody>
+</table>
+
+<p>我们并不要求你立刻完整理解所有代码,当前只是概括地介绍一些抽象的概念,并且让你对JavaScript(以及其他编程语言)工作原理有一定的认知。所有具体特性将在后续文章中详细介绍。</p>
+
+<div class="note">
+<p>注: 可以看到,JavaScript 中许多代码特性和其他编程语言是一致的( 函数、循环,等等)。尽管代码语法不尽相同,但概念基本类似。</p>
+</div>
+
+<h2 id="像程序员一样思考">像程序员一样思考</h2>
+
+<p><span lang="zh-CN">学习编程,语法本身并不</span><span lang="zh-CN">难,真正困难的是如何应用它来解决现实世界的问题。 你要开始像程序员那样思考。一般来讲,这种思考包括了解你程序运行的目的,为达到该目的应选定的代码类型,以及如何使这些代码协同运行。</span></p>
+
+<p><span id="result_box" lang="zh-CN">为达成这一点,我们需要努力编程,获取语法经验,注重实践,再加一点创造力,几项缺一不可。代码写的越多,就会完成的越优秀。虽然我们不能保证你在5分钟内拥有“程序员大脑”,但是整个课程中你将得到大量机会来训练程序员思维。</span></p>
+
+<p><span id="result_box" lang="zh-CN">请牢记这一点,然后开始观察本文的示例,体会一下将其分解为可操作任务的大体过程。</span></p>
+
+<h2 id="示例——猜数字游戏">示例——猜数字游戏</h2>
+
+<p>本文将向你演示如何构建下面的小游戏:</p>
+
+<div class="hidden">
+<h6 id="Top_hidden_code">Top hidden code</h6>
+
+<pre class="brush: html">&lt;!DOCTYPE html&gt;
+&lt;html&gt;
+
+&lt;head&gt;
+ &lt;meta charset="utf-8"&gt;
+ &lt;title&gt;猜数字游戏&lt;/title&gt;
+ &lt;style&gt;
+ html {
+ font-family: sans-serif;
+ }
+
+ body {
+ width: 50%;
+ max-width: 800px;
+ min-width: 480px;
+ margin: 0 auto;
+ }
+
+ .lastResult {
+ color: white;
+ padding: 3px;
+ }
+ &lt;/style&gt;
+&lt;/head&gt;
+
+&lt;body&gt;
+ &lt;h1&gt;猜数字游戏&lt;/h1&gt;
+ &lt;p&gt;我刚才随机选定了一个100以内的自然数。看你能否在 10 次以内猜中它。每次我都会告诉你所猜的结果是高了还是低了。&lt;/p&gt;
+ &lt;div class="form"&gt; &lt;label for="guessField"&gt;请猜数: &lt;/label&gt;&lt;input type="text" id="guessField" class="guessField"&gt; &lt;input type="submit" value="确定" class="guessSubmit"&gt; &lt;/div&gt;
+ &lt;div class="resultParas"&gt;
+ &lt;p class="guesses"&gt;&lt;/p&gt;
+ &lt;p class="lastResult"&gt;&lt;/p&gt;
+ &lt;p class="lowOrHi"&gt;&lt;/p&gt;
+ &lt;/div&gt;
+&lt;/body&gt;
+&lt;script&gt;
+ // 在此放置你的 JavaScript 代码
+ let randomNumber = Math.floor(Math.random() * 100) + 1;
+ const guesses = document.querySelector('.guesses');
+ const lastResult = document.querySelector('.lastResult');
+ const lowOrHi = document.querySelector('.lowOrHi');
+ const guessSubmit = document.querySelector('.guessSubmit');
+ const guessField = document.querySelector('.guessField');
+ let guessCount = 1;
+ let resetButton;
+
+ function checkGuess() {
+ let userGuess = Number(guessField.value);
+ if (guessCount === 1) {
+ guesses.textContent = '上次猜的数: ';
+ }
+
+ guesses.textContent += userGuess + ' ';
+
+ if (userGuess === randomNumber) {
+ lastResult.textContent = '恭喜你!猜对了!';
+ lastResult.style.backgroundColor = 'green';
+ lowOrHi.textContent = '';
+ setGameOver();
+ } else if (guessCount === 10) {
+ lastResult.textContent = '!!!GAME OVER!!!';
+ lowOrHi.textContent = '';
+ setGameOver();
+ } else {
+ lastResult.textContent = '你猜错了!';
+ lastResult.style.backgroundColor = 'red';
+ if(userGuess &lt; randomNumber) {
+ lowOrHi.textContent='刚才你猜低了!' ;
+ } else if(userGuess &gt; randomNumber) {
+ lowOrHi.textContent = '刚才你猜高了!';
+ }
+ }
+
+ guessCount++;
+ guessField.value = '';
+ }
+
+ guessSubmit.addEventListener('click', checkGuess);
+
+ function setGameOver() {
+ guessField.disabled = true;
+ guessSubmit.disabled = true;
+ resetButton = document.createElement('button');
+ resetButton.textContent = '开始新游戏';
+ document.body.appendChild(resetButton);
+ resetButton.addEventListener('click', resetGame);
+ }
+
+ function resetGame() {
+ guessCount = 1;
+ const resetParas = document.querySelectorAll('.resultParas p');
+ for(var i = 0 ; i &lt; resetParas.length ; i++) {
+ resetParas[i].textContent='';
+ }
+
+ resetButton.parentNode.removeChild(resetButton);
+ guessField.disabled = false;
+ guessSubmit.disabled = false;
+ guessField.value='';
+ guessField.focus();
+ lastResult.style.backgroundColor='white';
+ randomNumber=Math.floor(Math.random() * 100) + 1;
+ }
+&lt;/script&gt;
+
+&lt;/html&gt;
+</pre>
+</div>
+
+<p>{{ EmbedLiveSample('Top_hidden_code', '100%', 320, "", "", "hide-codepen-jsfiddle") }}</p>
+
+<p>先玩上几盘,在继续之前先熟悉一下这个游戏。</p>
+
+<p><span class="short_text" id="result_box" lang="zh-CN"><span>假设你的老板给你布置了以下游戏设计任务要求:</span></span></p>
+
+<blockquote>
+<p>我想让你开发一个猜数字游戏。游戏应随机选择一个 100 以内的自然数, 然后邀请玩家在 10 轮以内猜出这个数字。每轮后都应告知玩家的答案正确与否,如果出错了,则告诉他数字是低了还是高了。并且应显示出玩家前一轮所猜的数字。一旦玩家猜对,或者用尽所有机会,游戏将结束。游戏结束后,可以让玩家选择再次开始。</p>
+</blockquote>
+
+<p><span id="result_box" lang="zh-CN"><span title="Upon looking at this brief, the first thing we can do is to start breaking it down into simple actionable tasks, in as much of a programmer mindset as possible:
+
+    ">看到这个要求,首先我们要做的是将其分解成简单的可操作的任务,尽可能从程序员的思维去思考:</span></span></p>
+
+<ol>
+ <li>随机<span id="result_box" lang="zh-CN"><span title="Generate a random number between 1 and 100.
+    ">生成一个 100 以内的自然数。</span></span></li>
+ <li><span id="result_box" lang="zh-CN"><span title="Record the turn number the player is on.">记录玩家当前的轮数。</span><span title="Start it on 1.
+    ">从 1 开始。</span></span></li>
+ <li><span id="result_box" lang="zh-CN"><span title="Provide the player with a way to guess what the number is.
+    ">为玩家提供一种猜测数字的方法。</span></span></li>
+ <li><span id="result_box" lang="zh-CN"><span title="Once a guess has been submitted first record it somewhere so the user can see their previous guesses.
+    ">一旦有结果提交,先将其记录下来,以便用户可以看到他们先前的猜测。</span></span></li>
+ <li><span id="result_box" lang="zh-CN"><span title="Next, check whether it is the correct number.
+    ">然后检查它是否正确。</span></span></li>
+ <li><span id="result_box" lang="zh-CN"><span title="If it is correct:
+        ">如果正确:</span></span>
+ <ol>
+ <li><span id="result_box" lang="zh-CN"><span title="Display congratulations message.
+        ">显示祝贺消息。</span></span></li>
+ <li><span id="result_box" lang="zh-CN"><span title="Stop the player from being able to enter more guesses (this would mess the game up).
+        ">阻止玩家继续猜测(这会使游戏混乱)。</span></span></li>
+ <li><span id="result_box" lang="zh-CN"><span title="Display control allowing the player to restart the game.
+    ">显示控件允许玩家重新开始游戏。</span></span></li>
+ </ol>
+ </li>
+ <li><span id="result_box" lang="zh-CN"><span title="If it is wrong and the player has turns left:
+        ">如果出错,并且玩家有剩余轮次:</span></span>
+ <ol>
+ <li><span id="result_box" lang="zh-CN"><span title="Tell the player they are wrong.
+        ">告诉玩家他们错了。</span></span></li>
+ <li><span id="result_box" lang="zh-CN"><span title="Allow them to enter another guess.
+        ">允许他们输入另一个猜测。</span></span></li>
+ <li><span id="result_box" lang="zh-CN"><span title="Increment the turn number by 1.
+    ">轮数加 1。</span></span></li>
+ </ol>
+ </li>
+ <li><span id="result_box" lang="zh-CN"><span title="If it is wrong and the player has no turns left:
+        ">如果出错,并且玩家没有剩余轮次:</span></span>
+ <ol>
+ <li><span id="result_box" lang="zh-CN"><span title="Tell the player it is game over.
+        ">告诉玩家游戏结束。</span></span></li>
+ <li><span id="result_box" lang="zh-CN"><span title="Stop the player from being able to enter more guesses (this would mess the game up).
+        ">阻止玩家继续猜测(这会使游戏混乱)。</span></span></li>
+ <li><span id="result_box" lang="zh-CN"><span title="Display control allowing the player to restart the game.
+    ">显示控件允许玩家重新开始游戏。</span></span></li>
+ </ol>
+ </li>
+ <li><span id="result_box" lang="zh-CN"><span title="Once the game restarts, make sure the game logic and UI are completely reset, then go back to step 1.
+
+">一旦游戏重启,确保游戏的逻辑和UI完全重置,然后返回步骤1。</span></span></li>
+</ol>
+
+<p><span id="result_box" lang="zh-CN"><span title="Let's now move forward, looking at how we can turn these steps into code, building up the example, and exploring JavaScript features as we go.">让我们继续,看看我们如何将这些步骤转换为代码,构建这个示例,从而探索 JavaScript 的功能。</span></span></p>
+
+<h3 id="初始设置">初始设置</h3>
+
+<p><span class="short_text" id="result_box" lang="zh-CN">本教程开始前,请将</span> <a class="external external-icon" href="https://github.com/roy-tian/learning-area/blob/master/javascript/introduction-to-js-1/first-splash/number-guessing-game-start.html">number-guessing-game-start.html</a> 文件保存下来。同时<span id="result_box" lang="zh-CN">在文本编辑器和Web浏览器中将其打开,可以看到一个简单的标题,一段游戏说明,和一个用于输入猜测的表单,此时表单不会执行任何操作。</span></p>
+
+<p><span class="short_text" id="result_box" lang="zh-CN">我们将在 HTML 底部的</span><span class="short_text" lang="zh-CN"> </span> {{htmlelement("script")}} <span class="short_text" lang="zh-CN"> 元素中添加新的代码:</span></p>
+
+<pre class="brush: html">&lt;script&gt;
+
+ <span class="pl-s1"><span class="pl-c"><span class="pl-c">//</span> 开始编写 JavaScript 代码</span></span>
+
+&lt;/script&gt;
+</pre>
+
+<h3 id="添加变量以保存数据">添加变量以保存数据</h3>
+
+<p><span class="short_text" id="result_box" lang="zh-CN">让我们开始吧。首先,在 </span> {{htmlelement("script")}} <span class="short_text" lang="zh-CN"> 元素中添加以下代码:</span></p>
+
+<pre class="brush: js">let randomNumber = Math.floor(Math.random() * 100) + 1;
+
+const guesses = document.querySelector('.guesses');
+const lastResult = document.querySelector('.lastResult');
+const lowOrHi = document.querySelector('.lowOrHi');
+
+const guessSubmit = document.querySelector('.guessSubmit');
+const guessField = document.querySelector('.guessField');
+
+let guessCount = 1;
+let resetButton;</pre>
+
+<p><span id="result_box" lang="zh-CN">这段代码设置了存储数据的变量和常量以供程序使用。变量本质上是值(例如数字或字符串)</span><span lang="zh-CN">的容器。 你可以使用关键字 <code>let</code> (旧代码中使用 <code>var</code>)和一个名字来创建变量(请参阅 <a href="/zh-CN/docs/Learn/JavaScript/First_steps/Variables#var_与_let_的区别">let 和 var 之间的区别</a>)。常量用于存储不希望更改的数据,用关键字 <code>const</code> 创建,本例中用常量来保存对界面元素的引用。界面元素的文字可能会改变,但引用是不变的。</span></p>
+
+<p><span lang="zh-CN">可以使用等号(=)和一个值来为变量赋值。</span></p>
+
+<p><span id="result_box" lang="zh-CN">在我们的示例中:</span></p>
+
+<ul>
+ <li><span lang="zh-CN">我们用数学算法得出一个 1 到 100 之间的随机数,并赋值给第一个变量(<code>randomNumber</code>)。</span></li>
+ <li><span lang="zh-CN">接下来的三个常量均存储着一个引用,分别指向HTML结果段落中某个元素,用于在代码后面段落中插入值:</span> </li>
+ <li>
+ <pre class="brush: html" dir="rtl">&lt;p class="guesses"&gt;&lt;/p&gt;
+&lt;p class="lastResult"&gt;&lt;/p&gt;
+&lt;p class="lowOrHi"&gt;&lt;/p&gt;</pre>
+ </li>
+ <li><span class="short_text" id="result_box" lang="zh-CN">接下来的两个常量存储对表单文本输入和提交按钮的引用,并用于控制以后提交猜测:</span>
+ <pre class="brush: html">&lt;label for="guessField"&gt;请猜数:&lt;/label&gt;
+&lt;input type="text" id="guessField" class="guessField"&gt;
+&lt;input type="submit" value="确定" class="guessSubmit"&gt;</pre>
+ </li>
+ <li><span class="short_text" id="result_box" lang="zh-CN">倒数第二个变量存储一个计数器并初始化为 1(用于跟踪玩家猜测的次数),最后一个变量存储对</span><span class="short_text" lang="zh-CN">重置按钮的引用,这个按钮</span><span class="short_text" id="result_box" lang="zh-CN">尚不存在</span><span class="short_text" lang="zh-CN">(但稍后就有了)。</span></li>
+</ul>
+
+<div class="note">
+<p><strong>注</strong>: <span class="short_text" id="result_box" lang="zh-CN">稍后将讲解更多关于变量/常量的信息。<a href="https://developer.mozilla.org/en-US/docs/user:chrisdavidmills/variables">参见下节</a>。</span></p>
+</div>
+
+<h3 id="函数(Function)">函数(Function)</h3>
+
+<p><span class="short_text" id="result_box" lang="zh-CN">下面,在之前的代码中添加以下内容:</span></p>
+
+<pre class="brush: js">function checkGuess() {
+ alert('我是一个占位符');
+}</pre>
+
+<div id="gt-src-tools">
+<div id="tts_button"><span id="result_box" lang="zh-CN">函数是可复用的代码块,可以一次</span><span lang="zh-CN">编写,反复运行,从而节省了大量的重复代码。它们真的很有用。定义函数的方法很多,但现在我们先集中考虑当前这个简单的方式。 这里我们使用关键字 <code>function</code> 、</span>一个函数名、一对小括号<span lang="zh-CN">定义了一个函数。 随后是一对花括号(<code>{}</code>)。花括号内部是调用函数时要运行的所有代码。</span></div>
+</div>
+
+<div class="g-unit" id="gt-res-c">
+<div id="gt-res-p">
+<div id="gt-res-data">
+<div id="gt-res-wrap">
+<div id="gt-res-content">
+<div dir="ltr"><br>
+要运行一个函数代码时,可以输入函数名加一对小括号。</div>
+
+<div dir="ltr"></div>
+
+<div dir="ltr"><span lang="zh-CN">让我们尝试一下。保存你的代码并刷新浏览器页面 。然后进入</span><br>
+<a href="https://developer.mozilla.org/zh-CN/docs/Learn/Common_questions/What_are_browser_developer_tools">开发者工具 JavaScript 控制台</a>,并输入<span class="short_text" id="result_box" lang="zh-CN">以下代码:</span></div>
+
+<div dir="ltr"></div>
+</div>
+</div>
+</div>
+</div>
+</div>
+
+<pre class="brush: js">checkGuess();</pre>
+
+<p>在按下 <kbd>Return</kbd>/<kbd>Enter</kbd> 之后,你应该会看到一个告警窗口,显示 "<code><font face="monospace">我是一个占位符</font></code>";<span lang="zh-CN">我们在代码中定义了一个函数,当我们调用它时,函数创建了一个</span><span id="result_box" lang="zh-CN">告警窗口</span><span lang="zh-CN">。</span></p>
+
+<div class="note">
+<p><strong>注</strong>: <a href="/zh-CN/docs/learn/JavaScript/Building_blocks/Functions">后续课程 </a>将讲解更多有关函数的知识。</p>
+</div>
+
+<h3 id="运算符(Operator)">运算符(Operator)</h3>
+
+<p><span class="short_text" id="result_box" lang="zh-CN">JavaScript 运算符允许我们执行比较,做数学运算,连接字符串,以及其他类似的事情。</span></p>
+
+<p>请保存代码以免丢失,然后刷新浏览器页面,打开 <a href="https://developer.mozilla.org/zh-CN/docs/Learn/Common_questions/What_are_browser_developer_tools">开发者工具 JavaScript 控制台</a>。然后我们就可以尝试下文中的示例了:把下表中“示例”一列中的每一项都原封不动输入进来<span lang="zh-CN">,每次输入完毕后都按下 </span> <kbd>Return</kbd>/<kbd>Enter</kbd> <span lang="zh-CN">,可以看到返回的结果。如果你</span>无法访问浏览器开发者工具<span lang="zh-CN">,你随时都</span>可以使用下面的简易版内置控制台。</p>
+
+<div class="hidden">
+<h6 id="Hidden_code">Hidden code</h6>
+
+<pre class="brush: html">&lt;!DOCTYPE html&gt;
+&lt;html&gt;
+ &lt;head&gt;
+ &lt;meta charset="utf-8"&gt;
+ &lt;title&gt;JavaScript console&lt;/title&gt;
+ &lt;style&gt;
+ * { box-sizing: border-box; }
+
+ html {
+ background-color: #0C323D;
+ color: #809089;
+ font-family: monospace;
+ }
+
+ body {
+ max-width: 700px;
+ }
+
+ p {
+ margin: 0;
+ width: 1%;
+ padding: 0 1%;
+ font-size: 16px;
+ line-height: 1.5;
+ float: left;
+ }
+
+ .input p {
+ margin-right: 1%;
+ }
+
+ .output p {
+ width: 100%;
+ }
+
+ .input input {
+ width: 96%;
+ float: left;
+ border: none;
+ font-size: 16px;
+ line-height: 1.5;
+ font-family: monospace;
+ padding: 0;
+ background: #0C323D;
+ color: #809089;
+ }
+
+ div {
+ clear: both;
+ }
+
+ &lt;/style&gt;
+ &lt;/head&gt;
+ &lt;body&gt;
+ &lt;/body&gt;
+
+ &lt;script&gt;
+ var geval = eval;
+
+ function createInput() {
+ var inputDiv = document.createElement('div');
+ var inputPara = document.createElement('p');
+ var inputForm = document.createElement('input');
+
+ inputDiv.setAttribute('class','input');
+ inputPara.textContent = '&gt;';
+ inputDiv.appendChild(inputPara);
+ inputDiv.appendChild(inputForm);
+ document.body.appendChild(inputDiv);
+ inputDiv.focus();
+
+ inputForm.addEventListener('change', executeCode);
+ }
+
+ function executeCode(e) {
+ try {
+ var result = geval(e.target.value);
+ } catch(e) {
+ var result = 'error — ' + e.message;
+ }
+
+ var outputDiv = document.createElement('div');
+ var outputPara = document.createElement('p');
+
+ outputDiv.setAttribute('class','output');
+ outputPara.textContent = 'Result: ' + result;
+ outputDiv.appendChild(outputPara);
+ document.body.appendChild(outputDiv);
+
+ e.target.disabled = true;
+ e.target.parentNode.style.opacity = '0.5';
+
+ createInput()
+ }
+
+ createInput();
+
+ &lt;/script&gt;
+&lt;/html&gt;</pre>
+</div>
+
+<p>{{ EmbedLiveSample('Hidden_code', '100%', 300, "", "", "hide-codepen-jsfiddle") }}</p>
+
+<p><span class="short_text" id="result_box" lang="zh-CN">首先让我们来看看算术运算符,例如:</span></p>
+
+<table class="standard-table">
+ <thead>
+ <tr>
+ <th scope="col">运算符</th>
+ <th scope="col">名称</th>
+ <th scope="col">示例</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td><code>+</code></td>
+ <td>加</td>
+ <td><code>6 + 9</code></td>
+ </tr>
+ <tr>
+ <td><code>-</code></td>
+ <td>减</td>
+ <td><code>20 - 15</code></td>
+ </tr>
+ <tr>
+ <td><code>*</code></td>
+ <td>乘</td>
+ <td><code>3 * 7</code></td>
+ </tr>
+ <tr>
+ <td><code>/</code></td>
+ <td>除</td>
+ <td><code>10 / 5</code></td>
+ </tr>
+ </tbody>
+</table>
+
+<p><span class="short_text" id="result_box" lang="zh-CN">您也可以使用 </span><span class="short_text" lang="zh-CN"><code>+</code> 运算符将文本字符串连接在一起(术语“串联”(</span><em>concatenation</em><span class="short_text" lang="zh-CN">))。 尝试依次输入以下行:</span></p>
+
+<pre class="brush: js">let name = 'Bingo';
+name;
+let hello = ' says hello!';
+hello;
+let greeting = name + hello;
+greeting;</pre>
+
+<p><span id="result_box" lang="zh-CN">还有一些快捷操作符可用,称为 <a href="/zh-CN/docs/Web/JavaScript/Reference/Operators/Assignment_Operators">复合赋值操作符</a>。 例如,如果你只希望在现有字符串末尾添加一个新串,可以这样做:</span></p>
+
+<pre class="brush: js">name += ' says hello!';</pre>
+
+<p><span class="short_text" id="result_box" lang="zh-CN">这等价于:</span></p>
+
+<pre class="brush: js">name = name + ' says hello!';</pre>
+
+<p><span class="short_text" id="result_box" lang="zh-CN">在执行真/假比较时(例如在条件语句中,见 </span>{{anch("Conditionals", "下表")}}<span class="short_text" lang="zh-CN">),我们使用 <a href="zh-CN/docs/Web/JavaScript/Reference/Operators/Comparison_Operators">比较运算符</a>,例如:</span></p>
+
+<table class="standard-table">
+ <thead>
+ <tr>
+ <th scope="col">运算符</th>
+ <th scope="col">名称</th>
+ <th scope="col">示例</th>
+ </tr>
+ <tr>
+ <td><code>===</code></td>
+ <td>严格等于(它们是否完全一样?)</td>
+ <td><code>5 === 2 + 4</code></td>
+ </tr>
+ <tr>
+ <td><code>!==</code></td>
+ <td>不等于(它们究竟哪里不一样?)</td>
+ <td><code>'Chris' !== 'Ch' + 'ris'</code></td>
+ </tr>
+ <tr>
+ <td><code>&lt;</code></td>
+ <td>小于</td>
+ <td><code>10 &lt; 6</code></td>
+ </tr>
+ <tr>
+ <td><code>&gt;</code></td>
+ <td>大于</td>
+ <td><code>10 &gt; 20</code></td>
+ </tr>
+ </thead>
+</table>
+
+<h3 id="条件语句(Conditional)">条件语句(Conditional)</h3>
+
+<p><span id="result_box" lang="zh-CN">回到我们的 <code>checkGuess()</code> 函数,我们希望它不仅能够给出一个占位符消息,同时还能检查玩家是否猜对,并做出适当的反应。我想这并不难吧。</span></p>
+
+<p><span class="short_text" id="result_box" lang="zh-CN">现在,将当前的 <code>checkGuess()</code> 函数替换为此版本:</span></p>
+
+<pre class="brush: js">function checkGuess() {
+ let userGuess = Number(guessField.value);
+ if (guessCount === 1) {
+ guesses.textContent = '上次猜的数:';
+ }
+ guesses.textContent += userGuess + ' ';
+
+ if (userGuess === randomNumber) {
+ lastResult.textContent = '恭喜你!猜对了';
+ lastResult.style.backgroundColor = 'green';
+ lowOrHi.textContent = '';
+ setGameOver();
+ } else if (guessCount === 10) {
+ lastResult.textContent = '!!!GAME OVER!!!';
+ setGameOver();
+ } else {
+ lastResult.textContent = '你猜错了!';
+ lastResult.style.backgroundColor = 'red';
+ if(userGuess &lt; randomNumber) {
+ lowOrHi.textContent = '你猜低了!';
+ } else if(userGuess &gt; randomNumber) {
+ lowOrHi.textContent = '你猜高了';
+ }
+ }
+
+ guessCount++;
+ guessField.value = '';
+ guessField.focus();
+}</pre>
+
+<p>唷——好多的<span class="short_text" lang="zh-CN">代码!让我们来逐段探究。</span></p>
+
+<ul>
+ <li><span id="result_box" lang="zh-CN">第一行(行标 2)声明了一个名为 <code>userGuess</code> 的变量,并将其设置为在文本字段中输入的值。 我们还对这个值应用了内置的 <code>Number()</code> 方法,只是为了确保该值是一个数字。</span></li>
+ <li><span id="result_box" lang="zh-CN">接下来,我们遇到我们的第一个条件代码块(3 - 5 行)。 条件代码块让你能够根据某个条件的真假来选择性地运行代码。 虽然看起来有点像一个函数,但它不是。 条件块的最简单形式是从关键字 <code>if</code> 开始,然后是一些括号,然后是一些花括号。 括号内包含一个比较。 如果比较结果为 <code>true</code></span><span lang="zh-CN">,就会执行花括号内的代码。 反之,花括号中的代码就会被跳过,从而执行下面的代码。 本文的示例中,比较测试的是 <code>guessCount</code> 变量是否等于1,即玩家是不是第一次猜数字:</span></li>
+ <li>
+ <pre class="brush: js">guessCount === 1</pre>
+ </li>
+</ul>
+
+<p>如果是, 我们让 <code>guesses</code> 段落的文本内容等于“<code><font face="monospace">上次猜的数:</font></code>”。如果不是就不用了。</p>
+
+<ul>
+ <li><span class="short_text" id="result_box" lang="zh-CN">第 6 行将当前 <code>userGuess</code> 值附加到 </span><code>guesses</code> <span class="short_text" lang="zh-CN">段落的末尾,并加上一个空格,以使每两个猜测值之间有一个空格。</span></li>
+ <li><span class="short_text" id="result_box" lang="zh-CN">下一个代码块中(8 - 24 行)做了几个检查:</span>
+ <ul>
+ <li>第一个 <code>if(){ }</code> 检查用户的猜测是否等于在代码顶端设置的 <code>randomNumber</code> 值。如果是,则玩家猜对了,游戏胜利,我们将向玩家显示一个漂亮的绿色的祝贺信息,并清除“高了 / 低了”信息框的内容,调用 <code>setGameOver()</code> 方法。</li>
+ <li><font face="Open Sans, arial, sans-serif">紧接着是一个 </font><code>else if(){ }</code> 结构。它会检查这个回合是否是玩家的最后一个回合。如果是,程序将做与前一个程序块相同的事情,只是这次它显示的是 Game Over 而不是祝贺消息。</li>
+ <li>最后的一个块是 <code>else { }</code>,前两个比较都不返回 <span id="result_box" lang="zh-CN"><code>true</code></span> 时(也就是玩家尚未猜对,但是还有机会)才会执行这里的代码。在这个情况下,我们会告诉玩家他们猜错了,并执行另一个条件测试,判断并告诉玩家猜测的数字是高了还是低了。</li>
+ </ul>
+ </li>
+ <li>函数最后三行(26 - 28 行)是为下次猜测值提交做准备的。我们把 <code>guessCount</code> 变量的值加 1,以使玩家消耗一次机会 (<code>++</code> 是自增操作符,为自身加 1),然后我们把表单中文本域的值清空,重新聚焦于此,准备下一轮游戏。</li>
+</ul>
+
+<h3 id="事件(Event)">事件(Event)</h3>
+
+<p><span id="result_box" lang="zh-CN">现在,我们有一个实现比较不错的 <code>checkGuess()</code> 函数了,但它现在什么事情也做不了,因为我们还没有调用它。 理想中,我们希望在点击“</span>确定<span lang="zh-CN">”按钮时调用它,为此,我们需要使用事件。 事件就是浏览器中发生的事儿,比如点击按钮、加载页面、播放视频,等等,我们可以通过调用代码来响应事件。 侦听事件发生的结构称为<strong>事件监听器(Event Listener)</strong>,响应事件触发而运行的代码块被称为<strong>事件处理器(Event Handler)</strong>。</span></p>
+
+<p><span class="short_text" id="result_box" lang="zh-CN">在 <code>checkGuess()</code> 函数后添加以下代码:</span></p>
+
+<pre class="brush: js">guessSubmit.addEventListener('click', checkGuess);</pre>
+
+<p>这里为 <code>guessSubmit</code> 按钮添加了一个事件监听器。<code>addEventListener()</code> 方法包含两个可输入值(称为“<em>参数”(argument)</em>),监听事件的类型(本例中为“<code>click</code>”),和当事件发生时我们想要执行的代码(本例中为 <code>checkGuess()</code> 函数)。注意,<code>addEventListener()</code> 中作为参数的函数名不加括号。</p>
+
+<p><span id="result_box" lang="zh-CN">现在,保存代码并</span><span lang="zh-CN">刷新页面,示例应该能够工作了,但还不够完善。 现在唯一的问题是,如果玩家猜对或游戏次数用完,游戏将出错,因为我们尚未定义游戏结束时应运行的<code>setGameOver()</code> 函数。 现在,让我们补全所缺代码,并完善示例功能。</span></p>
+
+<h3 id="补全游戏功能">补全游戏功能</h3>
+
+<p><span id="result_box" lang="zh-CN">在代码最后添加一个 <code>setGameOver()</code> 函数,然后我们一起来看看它:</span></p>
+
+<pre class="brush: js">function setGameOver() {
+ guessField.disabled = true;
+ guessSubmit.disabled = true;
+ resetButton = document.createElement('button');
+ resetButton.textContent = '开始新游戏';
+ document.body.appendChild(resetButton);
+ resetButton.addEventListener('click', resetGame);
+}</pre>
+
+<ul>
+ <li><span id="result_box" lang="zh-CN">前两行通过将 <code>disable</code> 属性设置为 <code>true</code></span><span lang="zh-CN"> 来禁用表单文本输入和按钮。 这样做是必须的,否则用户就可以在游戏结束后提交更多的猜测,游戏的规则将遭到破坏。</span></li>
+ <li>接下来的三行创建一个新的  {{htmlelement("button")}} 元素,设置它的文本为“开始新游戏”,并把它添加到当前 HTML 的底部。</li>
+ <li><span id="result_box" lang="zh-CN">最后一行在新按钮上设置了一个事件监听器,当它被点击时,一个名为 <code>resetGame()</code> 的函数被将被调用。</span></li>
+</ul>
+
+<p><span class="short_text" id="result_box" lang="zh-CN">现在我们需要定义 </span><code><span id="result_box" lang="zh-CN">resetGame()</span></code><span lang="zh-CN"> </span><span class="short_text" lang="zh-CN">这个函数,依然放到代码底部:</span></p>
+
+<pre class="brush: js">function resetGame() {
+ guessCount = 1;
+
+ const resetParas = document.querySelectorAll('.resultParas p');
+ for (let i = 0 ; i &lt; resetParas.length; i++) {
+ resetParas[i].textContent = '';
+ }
+
+ resetButton.parentNode.removeChild(resetButton);
+
+ guessField.disabled = false;
+ guessSubmit.disabled = false;
+ guessField.value = '';
+ guessField.focus();
+
+ lastResult.style.backgroundColor = 'white';
+
+ randomNumber = Math.floor(Math.random() * 100) + 1;
+}</pre>
+
+<p><span class="short_text" id="result_box" lang="zh-CN">这段较长的代码将游戏中的一切重置为初始状态,然后玩家就可以开始新一轮的游戏了。此段代码:</span></p>
+
+<ul>
+ <li><span id="result_box" lang="zh-CN">将 <code>guessCount</code> 重置为 1。</span></li>
+ <li><span class="short_text" id="result_box" lang="zh-CN"><span>清除所有信息段落。</span></span></li>
+ <li><span lang="zh-CN">删除重置按钮</span><span lang="zh-CN">。</span></li>
+ <li><span id="result_box" lang="zh-CN">启用表单元素,清空文本域并</span><span lang="zh-CN">聚焦于此,准备接受新猜测的数字。</span></li>
+ <li><span id="result_box" lang="zh-CN">删除 </span><span lang="zh-CN"><code>lastResult</code> 段落的背景颜色。</span></li>
+ <li><span id="result_box" lang="zh-CN">生成一个新的随机数,这样就可以猜测新的数字了!</span></li>
+</ul>
+
+<p><strong><span class="short_text" id="result_box" lang="zh-CN">此刻一个能功能完善的(简易版)游戏就完成了。恭喜!</span></strong></p>
+
+<p><span class="short_text" id="result_box" lang="zh-CN">我们现在来讨论下其他很重要的代码功能,你可能已经看到过,但是你可能没有意识到这一点。</span></p>
+
+<h3 id="循环(Loop)">循环(Loop)</h3>
+
+<p><span id="result_box" lang="zh-CN">上面代码中有一部分需要我们仔细研读,那就是</span><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/for"> for</a> 循环。 <span id="result_box" lang="zh-CN"> 循环是一个非常重要的编程</span><span lang="zh-CN">概念,它让你能够重复运行一段代码,直到满足某个条件为止。</span></p>
+
+<p><span class="short_text" id="result_box" lang="zh-CN">首先,请再次转到</span> <a href="https://developer.mozilla.org/zh-CN/docs/Learn/Common_questions/What_are_browser_developer_tools">浏览器开发工具 JavaScript 控制台</a><span class="short_text" id="result_box" lang="zh-CN"> 然后输入以下内容:</span></p>
+
+<pre class="brush: js">for (let i = 1; i &lt; 21; i++) { console.log(i); }</pre>
+
+<p><span class="short_text" id="result_box" lang="zh-CN">发生了什么? 控制台中打印出了</span><span class="short_text" lang="zh-CN">数字 1 到 20。 这正是循环所为。一个 for 循环需要三个输入值(参数):</span></p>
+
+<ol>
+ <li><span id="result_box" lang="zh-CN"><strong>起始值</strong>:本例中我们从 1 开始计数,但其他任意数字也可以。 </span><span class="short_text" id="result_box" lang="zh-CN"><code>i</code></span><span lang="zh-CN">也可以用任何你喜欢的名字替换</span><span lang="zh-CN">,但一般约定用 </span><span class="short_text" id="result_box" lang="zh-CN"><code>i</code></span><span lang="zh-CN">,因为它很短,且易于记忆。</span></li>
+ <li><span class="short_text" id="result_box" lang="zh-CN"><strong>退出条件</strong>:这里我们指定 <code>i &lt; 21</code>,直到<code>i</code>不再小于 21 前</span><span class="short_text" lang="zh-CN">循环将继续。当 <code>i</code> 达到 21 时,循环将退出</span>。</li>
+ <li><strong>增加器</strong>:我们指定 <code>i++</code>,意思是向 <code>i</code> 加 1。<code>i</code> 值的每次变动都会引发循环的执行,直到 <code>i</code> 值等于 21(如前文所讲)。为简化问题,在本例中,我们使用<code>console.log()</code>在控制台打印出每次迭代时变量 <code>i</code> 的值。</li>
+</ol>
+
+<p><span class="short_text" id="result_box" lang="zh-CN">现在让我们来观察猜数字游戏中的循环 —— <code>resetGame()</code> 函数中可以找到以下内容:</span></p>
+
+<pre class="brush: js">let resetParas = document.querySelectorAll('.resultParas p');
+for (let i = 0 ; i &lt; resetParas.length ; i++) {
+ resetParas[i].textContent = '';
+}</pre>
+
+<p>这段代码通过 {{domxref("Document.querySelectorAll", "querySelectorAll()")}} 方法创建了一个包含 <code>&lt;div class="resultParas"&gt;</code> 内所有段落的变量,然后通过循环迭代,删除每个段落的文本内容。</p>
+
+<h3 id="浅谈对象(Object)">浅谈对象(Object)</h3>
+
+<p><span id="result_box" lang="zh-CN">在讨论前最后</span><span lang="zh-CN">再改进一波。 在 <code>let resetButton</code>(脚本顶端部分)下方添加下面一行内容,然后保存文件:</span></p>
+
+<pre class="brush: js">guessField.focus();</pre>
+
+<p>这一行通过 {{domxref("HTMLElement.focus", "focus()")}} 方法让光标在页面加载完毕时自动放置于 {{htmlelement("input")}}  输入框内,这意味着玩家可以马上开始第一次猜测,而无需点击输入框。 这只是一个小的改进,却提高了可用性——为使用户能投入游戏提供一个良好的视觉线索。</p>
+
+<p><span id="result_box" lang="zh-CN"><span title="Let's analyze what's going on here in a bit more detail.">深入分析一下。</span><span title="In JavaScript, everything is an object.">JavaScript 中一切都是对象。</span><span title="An object is a collection of related functionality stored in a single grouping.">对象是存储在单个分组中的相关功能的集合。</span><span title="You can create your own objects, but that is quite advanced and we won't be covering it until much later in the course.">可以创建自己的对象,但这是较高阶的知识,我们今后才会谈及。</span><span title="For now, we'll just briefly discuss the built-in objects that your browser contains, which allow you to do lots of useful things.
+
+">现在,仅需简要讨论浏览器内置的</span></span><span lang="zh-CN"><span title="For now, we'll just briefly discuss the built-in objects that your browser contains, which allow you to do lots of useful things.
+
+">对象,它们已经能够做许多有用的事情。</span></span></p>
+
+<p><span id="result_box" lang="zh-CN"><span title="In this particular case, we first created a guessField variable that stores a reference to the text input form field in our HTML — the following line can be found amongst our variable declarations near the top:">在本示例的特定情况下,我们首先创建一个 <code>guessField</code> 常量来存储对 HTML 中的文本输入表单域的引用,在文档顶部的声明区域中可以找到以下行:</span></span></p>
+
+<pre class="brush: js">const guessField = document.querySelector('.guessField');</pre>
+
+<p>使用 {{domxref("document")}} 对象的 {{domxref("document.querySelector", "querySelector()")}} 方法可以获得这个引用。<code>querySelector()</code> 需要一个信息——用一个 <a href="https://developer.mozilla.org/zh-CN/docs/Learn/CSS/Introduction_to_CSS/Selectors">CSS选择器</a> 可以选中需要引用的元素。</p>
+
+<p>因为 <code>guessField</code> 现在包含一个指向 {{htmlelement("input")}} 元素的引用,它现在就能够访问一系列的属性(存储于对象内部的基础变量,其中一些的值无法改变)和方法(存储在对象内部的基础函数)。<code>focus()</code> 是 {{htmlelement("input")}} 元素可用方法之一,因此我们可以使用这行代码将光标聚焦于此文本框上︰</p>
+
+<pre class="brush: js">guessField.focus();</pre>
+
+<p>不包含对表单元素引用的变量不提供 <code>focus()</code>方法。例如,引用 {{htmlelement("p")}} 元素的<code>guesses</code> 常量,包含一个数字的 <code>guessCount</code> 变量。</p>
+
+<h3 id="操作浏览器对象">操作浏览器对象</h3>
+
+<p>浏览器对象如何使用呢,下面我们来小试牛刀。</p>
+
+<ol>
+ <li>首先在浏览器中打开你的程序。</li>
+ <li>接下来打开 <a href="/zh-CN/docs/Learn/Common_questions/What_are_browser_developer_tools">浏览器开发者工具</a>, 并且切换到 JavaScript 控制台的标签页。</li>
+ <li><font face="Open Sans, arial, sans-serif">输入 </font><code>guessField</code> ,控制台将会显示此变量包含一个 {{htmlelement("input")}} 元素。同时控制台还能自动补全运行环境中对象的名字,包括你的变量!</li>
+ <li>现在输入下面的代码:
+ <pre class="brush: js">guessField.value = 'Hello';</pre>
+ <code>value</code> 属性表示当前文本区域中输入的值。 在输入这条指令后,你将看到文本域区中的文本被我们修改了!</li>
+ <li>现在试试输入 <code>guesses</code> 然后回车。控制台会显示一个包含 {{htmlelement("p")}} 元素的变量。</li>
+ <li>现在试试输入下面这一行:
+ <pre class="brush: js">guesses.value</pre>
+ 浏览器会返回 <code>undefined</code>,因为段落中并不存在 <code>value</code>。</li>
+ <li>为了改变段落中的文本内容, 你需要用 {{domxref("Node.textContent", "textContent")}} 属性来代替 <code>value</code>。试试这个:
+ <pre class="brush: js">guesses.textContent = '我的段落在哪里?';</pre>
+ </li>
+ <li>下面是一些有趣的东西。 尝试依次输入下面几行:
+ <pre class="brush: js">guesses.style.backgroundColor = 'yellow';
+guesses.style.fontSize = '200%';
+guesses.style.padding = '10px';
+guesses.style.boxShadow = '3px 3px 6px black';</pre>
+ </li>
+ <li><span id="result_box" lang="zh-CN">页面上的每个元素都有一个 <code>style</code> 属性,它本身包含一个对象,其属性包含应用于该元素的所有内联 CSS 样式。 让我们可以使用 JavaScript 在元素上动态设置新的 CSS 样式。</span></li>
+</ol>
+
+<h2 id="大功告成...">大功告成...</h2>
+
+<p><span id="result_box" lang="zh-CN">这个示例已经构建完毕,做得好!来尝试运行一下最终的代码,</span>或者 <a class="external external-icon" href="https://github.com/roy-tian/learning-area/blob/master/javascript/introduction-to-js-1/first-splash/number-guessing-game.html">看看我们的最终版本</a>。<span id="result_box" lang="zh-CN">如果你的版本无法正常工作,请对照</span> <a class="external external-icon" href="https://roy-tian.github.io/learning-area/javascript/introduction-to-js-1/first-splash/number-guessing-game.html">源代码</a> 进行检查。</p>
+
+<p>{{PreviousMenuNext("Learn/JavaScript/First_steps/What_is_JavaScript", "Learn/JavaScript/First_steps/What_went_wrong", "Learn/JavaScript/First_steps")}}</p>
+
+<h2 id="本章目录">本章目录</h2>
+
+<ul>
+ <li><a href="https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/First_steps/What_is_JavaScript">JavaScript 是什么?</a></li>
+ <li><a href="https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/First_steps/A_first_splash">JavaScript 初体验</a></li>
+ <li><a href="https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/First_steps/What_went_wrong">查找并解决 JavaScript 代码的错误 </a></li>
+ <li><a href="https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/First_steps/Variables">变量:储存所需信息</a></li>
+ <li><a href="https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/First_steps/Math">数字和运算符:JavaScript 的基本算数</a></li>
+ <li><a href="https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/First_steps/Strings">字符串:JavaScript 文本的处理</a></li>
+ <li><a href="https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/First_steps/Useful_string_methods">字符串的一些实用方法</a></li>
+ <li><a href="https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/First_steps/Arrays">数组</a></li>
+ <li><a href="https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/First_steps/Silly_story_generator">课程评估:笑话机</a></li>
+</ul>
diff --git a/files/zh-cn/learn/javascript/first_steps/arrays/index.html b/files/zh-cn/learn/javascript/first_steps/arrays/index.html
new file mode 100644
index 0000000000..e3cc48d6d5
--- /dev/null
+++ b/files/zh-cn/learn/javascript/first_steps/arrays/index.html
@@ -0,0 +1,522 @@
+---
+title: 数组
+slug: Learn/JavaScript/First_steps/Arrays
+tags:
+ - JavaScript
+ - Join
+ - Pop
+ - Push
+ - shift
+ - split
+ - unshift
+ - 初学者
+ - 数组
+translation_of: Learn/JavaScript/First_steps/Arrays
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/JavaScript/First_steps/Useful_string_methods", "Learn/JavaScript/First_steps/Silly_story_generator", "Learn/JavaScript/First_steps")}}</div>
+
+<p class="summary">在本模块的最后一篇文章中, 我们将看看数组 —— 一种将一组数据存储在单个变量名下的优雅方式。 现在我们看看它有什么用,然后探索如何来创建一个数组,检索、添加和删除存储在数组中的元素,以及其他更多的功能。</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">前提:</th>
+ <td>基本的电脑知识,对 HTML、CSS 语法有基础的理解,能理解什么是 JavaScript。</td>
+ </tr>
+ <tr>
+ <th scope="row">目的:</th>
+ <td>理解什么是数组,和如何在 JavaScript 中操作数组。</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="数组是什么">数组是什么?</h2>
+
+<p>数组通常被描述为“像列表一样的对象”; 简单来说,数组是一个包含了多个值的对象。数组对象可以存储在变量中,并且能用和其他任何类型的值完全相同的方式处理,区别在于我们可以单独访问列表中的每个值,并使用列表执行一些有用和高效的操作,如循环 - 它对数组中的每个元素都执行相同的操作。 也许我们有一系列产品和价格存储在一个数组中,我们想循环遍历所有这些产品,并将它们打印在发票上,同时将所有产品的价格统计在一起,然后将总价格打印在底部。<br>
+  </p>
+
+<p>如果我们没有数组,我们必须将每个产品存储在一个单独的变量中,然后调用打印的代码,并为每个产品单独添加。 花费的时间要长得多,效率很低,而且也容易出错。 如果我们有 10 个产品需要添加发票,那就只是有点麻烦而已,但是 100 个,或者 1000 个呢? 我们稍后将在文章中使用这个例子。</p>
+
+<p>像以前的文章一样,我们通过在 JavaScript 控制台中输入一些示例来了解数组的基础知识。 我们在下面提供了一个(您也可以在单独的选项卡或窗口中打开此控制台,或者如果您愿意,请使用<a href="/zh-CN/docs/Learn/Discover_browser_developer_tools">浏览器的开发者工具控制台</a>)。</p>
+
+<div class="hidden">
+<h6 id="Hidden_code">Hidden code</h6>
+
+<pre class="brush: html">&lt;!DOCTYPE html&gt;
+&lt;html&gt;
+ &lt;head&gt;
+ &lt;meta charset="utf-8"&gt;
+ &lt;title&gt;JavaScript console&lt;/title&gt;
+ &lt;style&gt;
+ * {
+ box-sizing: border-box;
+ }
+
+ html {
+ background-color: #0C323D;
+ color: #809089;
+ font-family: monospace;
+ }
+
+ body {
+ max-width: 700px;
+ }
+
+ p {
+ margin: 0;
+ width: 1%;
+ padding: 0 1%;
+ font-size: 16px;
+ line-height: 1.5;
+ float: left;
+ }
+
+ .input p {
+ margin-right: 1%;
+ }
+
+ .output p {
+ width: 100%;
+ }
+
+ .input input {
+ width: 96%;
+ float: left;
+ border: none;
+ font-size: 16px;
+ line-height: 1.5;
+ font-family: monospace;
+ padding: 0;
+ background: #0C323D;
+ color: #809089;
+ }
+
+ div {
+ clear: both;
+ }
+
+ &lt;/style&gt;
+ &lt;/head&gt;
+ &lt;body&gt;
+
+
+ &lt;/body&gt;
+
+ &lt;script&gt;
+ var geval = eval;
+ function createInput() {
+ var inputDiv = document.createElement('div');
+ var inputPara = document.createElement('p');
+ var inputForm = document.createElement('input');
+
+ inputDiv.setAttribute('class','input');
+ inputPara.textContent = '&gt;';
+ inputDiv.appendChild(inputPara);
+ inputDiv.appendChild(inputForm);
+ document.body.appendChild(inputDiv);
+
+ if(document.querySelectorAll('div').length &gt; 1) {
+        inputForm.focus();
+      }
+
+ inputForm.addEventListener('change', executeCode);
+ }
+
+ function executeCode(e) {
+ try {
+ var result = geval(e.target.value);
+ } catch(e) {
+ var result = 'error — ' + e.message;
+ }
+
+ var outputDiv = document.createElement('div');
+ var outputPara = document.createElement('p');
+
+ outputDiv.setAttribute('class','output');
+ outputPara.textContent = 'Result: ' + result;
+ outputDiv.appendChild(outputPara);
+ document.body.appendChild(outputDiv);
+
+ e.target.disabled = true;
+ e.target.parentNode.style.opacity = '0.5';
+
+ createInput()
+ }
+
+ createInput();
+
+ &lt;/script&gt;
+&lt;/html&gt;</pre>
+</div>
+
+<p>{{ EmbedLiveSample('Hidden_code', '100%', 300) }}</p>
+
+<h3 id="创建数组">创建数组</h3>
+
+<p>数组由方括号构成,其中包含用逗号分隔的元素列表。</p>
+
+<ol>
+ <li>假设我们想在一个数组中存储一个购物清单 - 我们会做一些像下面这样的事情。 在您的控制台中输入以下行:
+ <pre class="brush: js">let shopping = ['bread', 'milk', 'cheese', 'hummus', 'noodles'];
+shopping;</pre>
+ </li>
+ <li>在这种情况下,数组中的每个项目都是一个字符串,但请记住,您可以将任何类型的元素存储在数组中 - 字符串,数字,对象,另一个变量,甚至另一个数组。 您也可以混合和匹配项目类型 - 它们并不都是数字,字符串等。尝试下面这些:
+ <pre class="brush: js">let sequence = [1, 1, 2, 3, 5, 8, 13];
+let random = ['tree', 795, [0, 1, 2]];</pre>
+ </li>
+ <li>尝试创建您自己的几个数组,然后再继续往下看。</li>
+</ol>
+
+<h3 id="访问和修改数组元素">访问和修改数组元素</h3>
+
+<p>然后,您可以使用括号表示法访问数组中的元素,与 <a href="/zh-CN/docs/Learn/JavaScript/First_steps/Useful_string_methods#检索特定字符串字符">检索特定字符串字符</a> 的方法相同。</p>
+
+<ol>
+ <li>在您的控制台中输入以下内容:
+ <pre class="brush: js">shopping[0];
+// returns "bread"</pre>
+ </li>
+ <li>您还可以简单地为单个数组元素提供新值来修改数组中的元素。 例如:
+ <pre class="brush: js">shopping[0] = 'tahini';
+shopping;
+// shopping will now return [ "tahini", "milk", "cheese", "hummus", "noodles" ]</pre>
+
+ <div class="note"><strong>Note</strong>: 我们以前说过,但还是提醒一下 —— 电脑从 0 开始计数!</div>
+ </li>
+ <li>
+ <p>请注意,数组中包含数组的话称之为多维数组。 您可以通过将两组方括号链接在一起来访问数组内的另一个数组。 例如,要访问数组内部的一个项目,即 <code>random</code> 数组中的第三个项目(参见上一节),我们可以这样做:</p>
+
+ <pre class="brush: js">random[2][2];</pre>
+ </li>
+ <li>在继续之前,尝试对您的数组示例进行一些修改。 玩一玩,看看哪些有效,哪些无效。</li>
+</ol>
+
+<h3 id="获取数组长度">获取数组长度</h3>
+
+<p>你可以通过使用 {{jsxref("Array.prototype.length","length")}} 属性获取数组的长度(数组中有多少项元素),这与查找字符串的长度(以字符为单位)完全相同 。 尝试以下代码:</p>
+
+<pre class="brush: js">sequence.length;
+// should return 7</pre>
+
+<p>虽然 length 属性也有其他用途,但最常用于循环(循环遍历数组中的所有项)。 例如:</p>
+
+<pre class="brush: js">let sequence = [1, 1, 2, 3, 5, 8, 13];
+for (let i = 0; i &lt; sequence.length; i++) {
+ console.log(sequence[i]);
+}</pre>
+
+<p>您将在以后的文章中正确地了解循环,但简而言之,这段代码的意思是:</p>
+
+<ol>
+ <li>在数组中的元素编号 0 开始循环。</li>
+ <li>在元素编号等于数组长度的时候停止循环。 这适用于任何长度的数组,但在这种情况下,它将在编号 7 的时候终止循环(这很好,因为我们希望最后一位元素的编号是 6)。</li>
+ <li>对于每个元素,使用 <a href="https://developer.mozilla.org/en-US/docs/Web/API/Console/log">console.log()</a> 将其打印到浏览器控制台。</li>
+</ol>
+
+<h2 id="一些有用的数组方法">一些有用的数组方法</h2>
+
+<p>在本节中,我们将介绍一些相当有用的数组方法,这些方法允许我们将字符串拆分为字符串数组,反之亦然,以及添加或删除元素。</p>
+
+<h3 id="字符串和数组之间的转换">字符串和数组之间的转换</h3>
+
+<p>通常,您会看到一个包含在一个长长的字符串中的原始数据,您可能希望将有用的项目分成更有用的表单,然后对它们进行处理,例如将它们显示在数据表中。 为此,我们可以使用 {{jsxref("String.prototype.split()","split()")}} 方法。 在其最简单的形式中,这需要一个参数,您要将字符串分隔的字符,并返回分隔符之间的子串,作为数组中的项。</p>
+
+<div class="note">
+<p><strong>Note</strong>:  好吧,从技术上讲,这是一个字符串方法,而不是一个数组方法,但是我们把它放在数组中,因为它在这里很合适。</p>
+</div>
+
+<ol>
+ <li>我们来玩一下这个方法,看看它是如何工作的。 首先,在控制台中创建一个字符串:
+ <pre class="brush: js">let myData = 'Manchester,London,Liverpool,Birmingham,Leeds,Carlisle';</pre>
+ </li>
+ <li>现在我们用每个逗号分隔它:
+ <pre class="brush: js">let myArray = myData.split(',');
+myArray;</pre>
+ </li>
+ <li>最后,尝试找到新数组的长度,并从中检索一些项目:
+ <pre class="brush: js">myArray.length;
+myArray[0]; // the first item in the array
+myArray[1]; // the second item in the array
+myArray[myArray.length-1]; // the last item in the array</pre>
+ </li>
+ <li>您也可以使用 {{jsxref("Array.prototype.join()","join()")}} 方法进行相反的操作。 尝试以下:
+ <pre class="brush: js">let myNewString = myArray.join(',');
+myNewString;</pre>
+ </li>
+ <li>将数组转换为字符串的另一种方法是使用 {{jsxref("Array.prototype.toString()","toString()")}} 方法。 <code>toString()</code> 可以比 <code>join()</code> 更简单,因为它不需要一个参数,但更有限制。 使用 <code>join()</code> 可以指定不同的分隔符(尝试使用与逗号不同的字符运行步骤4)。
+ <pre class="brush: js">let dogNames = ["Rocket","Flash","Bella","Slugger"];
+dogNames.toString(); //Rocket,Flash,Bella,Slugger</pre>
+ </li>
+</ol>
+
+<h3 id="添加和删除数组项">添加和删除数组项</h3>
+
+<p>我们还没有涵盖添加和删除数组元素,现在让我们来看看。 我们将使用在上一节中最后提到的  <code>myArray</code> 数组。 如果您尚未遵循该部分,请先在控制台中创建数组:</p>
+
+<pre class="brush: js">let myArray = ['Manchester', 'London', 'Liverpool', 'Birmingham', 'Leeds', 'Carlisle'];</pre>
+
+<p>首先,要在数组末尾添加或删除一个项目,我们可以使用  {{jsxref("Array.prototype.push()","push()")}} 和 {{jsxref("Array.prototype.pop()","pop()")}}。</p>
+
+<ol>
+ <li>让我们先使用 <code>push()</code> —— 注意,你需要添加一个或多个要添加到数组末尾的元素。 尝试下面的代码:
+
+ <pre class="brush: js">myArray.push('Cardiff');
+myArray;
+myArray.push('Bradford', 'Brighton');
+myArray;
+</pre>
+ </li>
+ <li>当方法调用完成时,将返回数组的新长度。 如果要将新数组长度存储在变量中。例如:
+ <pre class="brush: js">var newLength = myArray.push('Bristol');
+myArray;
+newLength;</pre>
+ </li>
+ <li>从数组中删除最后一个元素的话直接使用 <code>pop()</code> 就可以。 例如:
+ <pre class="brush: js">myArray.pop();</pre>
+ </li>
+ <li>当方法调用完成时,将返回已删除的项目。 你也可以这样做:
+ <pre class="brush: js">let removedItem = myArray.pop();
+myArray;
+removedItem;</pre>
+ </li>
+</ol>
+
+<p>{{jsxref("Array.prototype.unshift()","unshift()")}} 和 {{jsxref("Array.prototype.shift()","shift()")}} 从功能上与 {{jsxref("Array.prototype.push()","push()")}} 和 {{jsxref("Array.prototype.pop()","pop()")}} 完全相同,只是它们分别作用于数组的开始,而不是结尾。</p>
+
+<ol>
+ <li><font face="Open Sans, arial, sans-serif">首先 </font><code>unshift()</code> ——尝试一下这个命令:
+
+ <pre class="brush: js">myArray.unshift('Edinburgh');
+myArray;</pre>
+ </li>
+ <li>现在 <code>shift()</code> —— 尝试一下!
+ <pre class="brush: js">let removedItem = myArray.shift();
+myArray;
+removedItem;</pre>
+ </li>
+</ol>
+
+<h2 id="积极学习:打印这些产品">积极学习:打印这些产品</h2>
+
+<p>我们回到前面描述的例子 —— 打印出发票上的产品名称和价格,然后计算总价格并将其印在底部。 在下面的可编辑示例中,包含数字的注释 —— 每个注释标记都是您必须向代码添加内容的地方。 它们如下:</p>
+
+<ol>
+ <li>在 <code>// number 1</code> 注释下面是一些字符串,每个字符串包含一个产品名称和一个冒号分隔的价格。 我们希望您将其转换为一个数组,并将其存储在名为 <code>products</code> 的数组中。</li>
+ <li>与 <code>// number 2</code> 注释同一行的是 for 循环的开头。 在这行中,我们目前有 <code>i &lt;= 0</code>,这是一个条件测试,导致 <a href="/zh-CN/docs/Learn/JavaScript/First_steps/A_first_splash#循环(Loop)">for循环</a> 立即停止,因为它说“当 <code>i</code> 不再小于或等于0”时停止,而 <code>i</code> 从0开始。 我们希望您使用条件测试来替换它,当 <code>i</code> 不再小于 <code>products</code> 数组的长度时,该条件测试会停止循环。</li>
+ <li>就在 <code>// number 3</code> 注释的下方,我们希望您编写一行代码,将当前数组项目(名称:价格)分成两个独立的项目,一个只包含该名称,一个只包含该价格。 如果您不确定如何执行此操作,请参阅<a href="/zh-CN/docs/Learn/JavaScript/First_steps/Useful_string_methods">有用的字符串方法</a>文章以获得一些帮助,甚至更好的看看本文中的{{anch("转换字符串和数组")}}部分。</li>
+ <li>作为上述代码行的一部分,您还需要将价格从字符串转换为数字。 如果你不记得如何做,请查看<a href="/zh-CN/docs/Learn/JavaScript/First_steps/Strings#创建一个字符串">第一个字符串</a>文章。</li>
+ <li>有一个名为 <code>total</code> 的变量被创建,并在代码的顶部赋值为 0。 在循环内(在 <code>// number 4</code> 注释下面),我们希望您添加一行,将当前项目价格添加到循环中的迭代变量,以便在代码结尾处将正确的总数打印到发票上。 您可能需要一个赋值运算符来执行此操作。</li>
+ <li>我们希望您改变 <code>// number 5</code> 注释的行,以便使  <code>itemText</code> 变量等于“当前项目名称 - $ 当前项目价格”,例如“Shoes - $ 23.99”,以此将每个项目的正确信息都印在发票上。 这只是简单的字符串连接,您应该对此很熟悉。</li>
+</ol>
+
+<div class="hidden">
+<h6 id="Playable_code">Playable code</h6>
+
+<pre class="brush: html">&lt;div class="output" style="min-height: 150px;"&gt;
+
+&lt;ul&gt;
+
+&lt;/ul&gt;
+
+&lt;p&gt;&lt;/p&gt;
+
+&lt;/div&gt;
+
+&lt;textarea id="code" class="playable-code" style="height: 370px;"&gt;
+var list = document.querySelector('.output ul');
+var totalBox = document.querySelector('.output p');
+var total = 0;
+list.innerHTML = '';
+totalBox.textContent = '';
+// number 1
+ 'Underpants:6.99'
+ 'Socks:5.99'
+ 'T-shirt:14.99'
+ 'Trousers:31.99'
+ 'Shoes:23.99';
+
+for (var i = 0; i &lt;= 0; i++) { // number 2
+ // number 3
+
+ // number 4
+
+ // number 5
+ itemText = 0;
+
+ var listItem = document.createElement('li');
+ listItem.textContent = itemText;
+ list.appendChild(listItem);
+}
+
+totalBox.textContent = 'Total: $' + total.toFixed(2);
+&lt;/textarea&gt;
+
+&lt;div class="playable-buttons"&gt;
+ &lt;input id="reset" type="button" value="Reset"&gt;
+ &lt;input id="solution" type="button" value="Show solution"&gt;
+&lt;/div&gt;
+</pre>
+
+<pre class="brush: js">var textarea = document.getElementById('code');
+var reset = document.getElementById('reset');
+var solution = document.getElementById('solution');
+var code = textarea.value;
+
+function updateCode() {
+ eval(textarea.value);
+}
+
+reset.addEventListener('click', function() {
+ textarea.value = code;
+ updateCode();
+});
+
+solution.addEventListener('click', function() {
+ textarea.value = jsSolution;
+ updateCode();
+});
+
+var jsSolution = 'var list = document.querySelector(\'.output ul\');\nvar totalBox = document.querySelector(\'.output p\');\nvar total = 0;\nlist.innerHTML = \'\';\ntotalBox.textContent = \'\';\n\nvar products = [\'Underpants:6.99\',\n \'Socks:5.99\',\n \'T-shirt:14.99\',\n \'Trousers:31.99\',\n \'Shoes:23.99\'];\n\nfor(var i = 0; i &lt; products.length; i++) {\n var subArray = products[i].split(\':\');\n var name = subArray[0];\n var price = Number(subArray[1]);\n total += price;\n itemText = name + \' — $\' + price;\n\n var listItem = document.createElement(\'li\');\n listItem.textContent = itemText;\n list.appendChild(listItem);\n}\n\ntotalBox.textContent = \'Total: $\' + total.toFixed(2);';
+
+textarea.addEventListener('input', updateCode);
+window.addEventListener('load', updateCode);
+</pre>
+</div>
+
+<p>{{ EmbedLiveSample('Playable_code', '100%', 600) }}</p>
+
+<h2 id="积极学习:前5个搜索">积极学习:前5个搜索</h2>
+
+<p>当您在 Web 程序中维护当前活动元素的记录时,{{jsxref("Array.prototype.push()","push()")}} 和 {{jsxref("Array.prototype.pop()","pop()")}} 是不错的方法。 例如,在动画场景中,您可能会有一系列表示当前显示的背景图像的对象,并且由于性能或混乱原因,您可能只需要一次显示 50 个。 当创建新对象并将其添加到数组中时,可以从数组中删除较旧的对象以保持所需的数量。</p>
+
+<p>在这个例子中,我们将展示一种更简单的使用方法 - 在这里我们给你一个假的搜索网站,一个搜索框。 这个想法是,当在搜索框中输入时,列表中会显示5个先前的搜索字词。 当列表项目数量超过 5 时,每当新项目被添加到顶部时,最后一个项目开始被删除,因此总是显示5个以前的搜索字词。</p>
+
+<div class="note">
+<p><strong>Note</strong>: 在真正的搜索应用中,您可能可以点击先前的搜索字词返回上一次搜索,并显示实际的搜索结果! 我们现在只是保持简单的逻辑。</p>
+</div>
+
+<p>要完成该应用程序,我们需要您:</p>
+
+<ol>
+ <li>在 <code>//number 1</code> 注释下面添加一行,将输入到搜索框中的当前值添加到数组的开头。 这可以使用 <code>searchInput.value</code> 检索。</li>
+ <li>在 <code>// number 2</code> 注释下方添加一行,该行删除数组末尾的当前值。</li>
+</ol>
+
+<div class="hidden">
+<h6 id="Playable_code_2">Playable code 2</h6>
+
+<pre class="brush: html">&lt;div class="output" style="min-height: 150px;"&gt;
+
+&lt;input type="text"&gt;&lt;button&gt;Search&lt;/button&gt;
+
+&lt;ul&gt;
+
+&lt;/ul&gt;
+
+&lt;/div&gt;
+
+&lt;textarea id="code" class="playable-code" style="height: 370px;"&gt;
+var list = document.querySelector('.output ul');
+var searchInput = document.querySelector('.output input');
+var searchBtn = document.querySelector('.output button');
+
+list.innerHTML = '';
+
+var myHistory = [];
+
+searchBtn.onclick = function() {
+ // 如果搜索框不为空,我们则将搜索词添加到开头
+ if (searchInput.value !== '') {
+ // number 1
+
+ // 清空显示的搜索关键词列表,防止显示
+ // 每次输入搜索词都会重新生成显示的内容
+ list.innerHTML = '';
+
+ // 通过循环遍历,显示所有的搜索关键词
+ for (var i = 0; i &lt; myHistory.length; i++) {
+ var itemText = myHistory[i];
+ var listItem = document.createElement('li');
+ listItem.textContent = itemText;
+ list.appendChild(listItem);
+ }
+
+ // 如果数组的长度大于 5,那么便移除旧的搜索关键词
+ if (myHistory.length &gt;= 5) {
+ // number 2
+
+ }
+
+ // 清空并聚焦到搜索框,准备下一次的搜索
+ searchInput.value = '';
+ searchInput.focus();
+ }
+}
+&lt;/textarea&gt;
+
+&lt;div class="playable-buttons"&gt;
+ &lt;input id="reset" type="button" value="Reset"&gt;
+ &lt;input id="solution" type="button" value="Show solution"&gt;
+&lt;/div&gt;
+</pre>
+
+<pre class="brush: js">var textarea = document.getElementById('code');
+var reset = document.getElementById('reset');
+var solution = document.getElementById('solution');
+var code = textarea.value;
+
+function updateCode() {
+ eval(textarea.value);
+}
+
+reset.addEventListener('click', function() {
+ textarea.value = code;
+ updateCode();
+});
+
+solution.addEventListener('click', function() {
+ textarea.value = jsSolution;
+ updateCode();
+});
+
+var jsSolution = 'var list = document.querySelector(\'.output ul\');\nvar searchInput = document.querySelector(\'.output input\');\nvar searchBtn = document.querySelector(\'.output button\');\n\nlist.innerHTML = \'\';\n\nvar myHistory= [];\n\nsearchBtn.onclick = function() {\n if(searchInput.value !== \'\') {\n myHistory.unshift(searchInput.value);\n\n list.innerHTML = \'\';\n\n for(var i = 0; i &lt; myHistory.length; i++) {\n var itemText = myHistory[i];\n var listItem = document.createElement(\'li\');\n listItem.textContent = itemText;\n list.appendChild(listItem);\n }\n\n if(myHistory.length &gt;= 5) {\n myHistory.pop();\n }\n\n searchInput.value = \'\';\n searchInput.focus();\n }\n}';
+
+textarea.addEventListener('input', updateCode);
+window.addEventListener('load', updateCode);
+</pre>
+</div>
+
+<p>{{ EmbedLiveSample('Playable_code_2', '100%', 600) }}</p>
+
+<h2 id="总结">总结</h2>
+
+<p>阅读本文后,我们相信您会明白数组是很有用的; 你会看到它们在 JavaScript 中随处可见,通常与循环相关,以便对数组中的每个元素做同样的事情。 我们将教你所有有用的基础知识,了解下一个模块中的循环,但现在你应该给自己鼓掌,并稍加休息; 您已经完成了本单元中的所有文章!</p>
+
+<p>唯一需要做的就是通过这个模块的评估,这将测试你对之前的文章的理解。</p>
+
+<h2 id="相关链接">相关链接</h2>
+
+<ul>
+ <li><a href="/en-US/docs/Web/JavaScript/Guide/Indexed_collections">Indexed collections</a> — 数组及其表兄弟类型阵列的高级指导。</li>
+ <li>{{jsxref("Array")}} — Array 对象引用页面 - 有关此页面中讨论功能的详细参考指南等。</li>
+</ul>
+
+<p>{{PreviousMenuNext("Learn/JavaScript/First_steps/Useful_string_methods", "Learn/JavaScript/First_steps/Silly_story_generator", "Learn/JavaScript/First_steps")}}</p>
+
+
+
+<h2 id="在本单元中">在本单元中</h2>
+
+<ul>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps/What_is_JavaScript">What is JavaScript?</a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps/A_first_splash">A first splash into JavaScript</a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps/What_went_wrong">What went wrong? Troubleshooting JavaScript</a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps/Variables">Storing the information you need — Variables</a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps/Math">Basic math in JavaScript — numbers and operators</a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps/Strings">Handling text — strings in JavaScript</a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps/Useful_string_methods">Useful string methods</a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps/Arrays">Arrays</a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps/Silly_story_generator">Assessment: Silly story generator</a></li>
+</ul>
diff --git a/files/zh-cn/learn/javascript/first_steps/index.html b/files/zh-cn/learn/javascript/first_steps/index.html
new file mode 100644
index 0000000000..e53fc2761f
--- /dev/null
+++ b/files/zh-cn/learn/javascript/first_steps/index.html
@@ -0,0 +1,57 @@
+---
+title: JavaScript 第一步
+slug: Learn/JavaScript/First_steps
+tags:
+ - JavaScript
+ - 入门
+ - 指南
+ - 新手
+translation_of: Learn/JavaScript/First_steps
+---
+<div>{{LearnSidebar}}</div>
+
+<p class="summary">在第一个 JavaScript 板块,带领各位体验编写 JavaScript 程序前,首先回答一些基本问题:「什么是 JavaScript?」,「它看上去是什么样的?」,「它能做什么?」。此后,我们将详细讨论一些关键构件,例如变量、字符串、数值和数组。</p>
+
+<h2 id="事前准备">事前准备</h2>
+
+<p>学习这个板块前,你不需要预先了解任何 JavaScript 知识,但你应当对 HTML 和 CSS 有所熟悉。我们建议学习 JavaScript 前,先完成以下板块阅读:</p>
+
+<ul>
+ <li><a href="/zh-CN/docs/Learn/Getting_started_with_the_web">开始使用 Web</a>(包括 <a href="/zh-CN/docs/Learn/Getting_started_with_the_web/JavaScript_basics">JavaScript 基础简介</a>)。</li>
+ <li><a href="/zh-CN/docs/Learn/HTML/Introduction_to_HTML">HTML 简介</a>。</li>
+ <li><a href="/zh-CN/docs/Learn/CSS/Introduction_to_CSS">CSS 简介</a>。</li>
+</ul>
+
+<div class="note">
+<p><strong>注意:</strong>如果你无法在你使用的电脑/平板/其他设备上创建自己的文件,尝试使用在线编程应用来运行(大部分)代码示例,例如 <a href="http://jsbin.com/">JSBin</a> 和 <a href="https://thimble.mozilla.org/">Thimble</a>。</p>
+</div>
+
+<h2 id="学习指南">学习指南</h2>
+
+<dl>
+ <dt><a href="/zh-CN/docs/Learn/JavaScript/First_steps/What_is_JavaScript">什么是 JavaScript?</a></dt>
+ <dd>欢迎来到 MDN JavaScript 新手课程!第一篇文章,我们将从较高层次审视 JavaScript,解答诸如「它是什么?」、「它在做些什么?」的问题,确保你理解 JavaScript 的用途。</dd>
+ <dt><a href="/zh-CN/docs/Learn/JavaScript/First_steps/A_first_splash">JavaScript 初体验</a></dt>
+ <dd>你已经学习了一些 JavaScript 理论,了解了可以用它做什么,接下来我们将通过一个非常实用的教程,带给你一节 JavaScript 基本特性速成课。在这里你将一步一步构建出一个「猜数字」游戏。</dd>
+ <dt><a href="/zh-CN/docs/Learn/JavaScript/First_steps/What_went_wrong">哪里出错了?JavaScript 疑难解答</a></dt>
+ <dd>构建上篇文章中的「猜数字」游戏时,你可能发现它没法运行。不要害怕——这篇文章来解救正为问题挠头的你,它为你提供一些从 JavaScript 程序中发现并解决问题的思路。</dd>
+ <dt><a href="/zh-CN/docs/Learn/JavaScript/First_steps/Variables">存储你需要的信息——变量</a></dt>
+ <dd>读过最近几篇文章,你应当了解 JavaScript 是什么,它能为你做什么,如何搭配其他 web 技术使用,以及从较高层次来看,它的主要特性是什么样的。这篇文章,我们将深入真正的基础,看看如何使用 JavaScript 的基本构件——变量。</dd>
+ <dt><a href="/zh-CN/docs/Learn/JavaScript/First_steps/Math">JavaScript 中的基本运算——数值和运算符</a></dt>
+ <dd>这一课我们讨论 JavaScript 的运算——如何将运算符与其他特性结合使用,完美操作数值来实现需求。</dd>
+ <dt><a href="/zh-CN/docs/Learn/JavaScript/First_steps/Strings">文字处理——JavaScript 字符串</a></dt>
+ <dd>接下来将目光转向字符串——编程世界中文字片段被称为字符串。本文涉及你在学习 JavaScript 时应当了解的有关字符串的所有常见情形,例如生成字符串、字符串中的引用以及合并字符串。</dd>
+ <dt><a href="/zh-CN/docs/Learn/JavaScript/First_steps/Useful_string_methods">实用字符串方法</a></dt>
+ <dd>我们已经了解字符串的基本内容,接下来再上一个台阶,思考利用字符串内建方法实现的实用操作,例如获取字符串长度、字符串合并与分割、替换字符串中特定字符,等等。</dd>
+ <dt><a href="/zh-CN/docs/Learn/JavaScript/First_steps/Arrays">数组</a></dt>
+ <dd>这个版块最后一篇文章,我们来看数组——干净整洁地把一连串数据项存储在同一个变量名下。这里我们要理解为什么说它实用,探索如何创建数组,如何获取、添加、删除数组中的项目等等内容。</dd>
+</dl>
+
+<h2 id="自我评估">自我评估</h2>
+
+<p>下面的评估会检验你对上述学习指南中 JavaScript 基础的理解。</p>
+
+<dl>
+ <dt><a href="/zh-CN/docs/Learn/JavaScript/First_steps/Silly_story_generator">笑话生成器</a></dt>
+ <dd>这项评估中,你将运用你在这个版块的文章中学到的知识,构建一个生成随机笑话的好玩 app。玩得开心!</dd>
+</dl>
diff --git a/files/zh-cn/learn/javascript/first_steps/math/index.html b/files/zh-cn/learn/javascript/first_steps/math/index.html
new file mode 100644
index 0000000000..b9bb3e012f
--- /dev/null
+++ b/files/zh-cn/learn/javascript/first_steps/math/index.html
@@ -0,0 +1,429 @@
+---
+title: JavaScript中的基础数学 — 数字和操作符
+slug: Learn/JavaScript/First_steps/Math
+translation_of: Learn/JavaScript/First_steps/Math
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/JavaScript/First_steps/Variables", "Learn/JavaScript/First_steps/Strings", "Learn/JavaScript/First_steps")}}</div>
+
+<p class="summary">在本次课程中,我们讨论 JavaScript 中的数学 — 我们如何使用 {{Glossary("Operator","运算符")}} 和其他功能来成功地操作数字以完成我们的请求。</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">前提:</th>
+ <td>基本的计算机知识,对HTML和CSS初步了解,知道JavaScript是什么。</td>
+ </tr>
+ <tr>
+ <th scope="row">目标:</th>
+ <td>熟悉 JavaScript 中 Math 的基础知识。</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="人人都爱数学">人人都爱数学</h2>
+
+<p>好吧,可能不是。有些人喜欢数学,有些人可能从在学校必须学习乘法表和长除法时就讨厌数学,还有人介于两者之间。但我们都不能否认,数学是生活的基本组成部分,我们离不了它。尤其如此,当我们学习编写 JavaScript 程序(或任何其他语言),我们所做的很多事情都依赖于处理数值数据,计算新值等。你将不会惊讶地认识到 JavaScript 有一套可用的全功能的数学功能。</p>
+
+<p>本文仅讨论您现在需要了解的基本部分。</p>
+
+<h3 id="数字类型">数字类型</h3>
+
+<p>在编程中,即使是人人熟知的最普遍的十进制数,也比你想象的要复杂的多。我们使用不同的术语来描述不同类型的十进制数,例如:</p>
+
+<ul>
+ <li><strong>整数</strong> 就是整数,例如 10, 400, 或者 -5.</li>
+ <li><strong>浮点数</strong> (浮点) 有小数点或小数位,例如 12.5,和 56.7786543。</li>
+ <li><strong>双精度</strong>双精度是一种特定类型的浮点数,它们具有比标准浮点数更高的精度(这意味着它们精确到更大的小数位数)。</li>
+</ul>
+
+<p>我们甚至有不同类型的数字系统! 十进制是基数10(意味着它在每列使用0-9),但是我们也有这样的东西:</p>
+
+<ul>
+ <li><strong>二进制</strong> — 计算机的最基础语言; 0s and 1s</li>
+ <li><strong>八进制</strong> — 基数8,每列使用0-7</li>
+ <li><strong>十六进制</strong> — 基数16,每列使用0-9,然后使用a-f。 在CSS中设置颜色时,可能会遇到这些数字。</li>
+</ul>
+
+<p>在你开始担心你的大脑混乱之前,先停下来吧! 一开始,我们将在本课程中坚持使用十进制数; 你很少会遇到需要开始考虑其他类型的情况,如果有的话。</p>
+
+<p>第二个好消息是,与其他一些编程语言不同,JavaScript只有一个数据类型用来表示数字(包括 integers 和 decimals ),您猜对了,{{jsxref("Number")}}。 这意味着,你在JavaScript中处理的任何类型的数字,都以完全相同的方式处理它们。</p>
+
+<h3 id="这是我们的全部数字">这是我们的全部数字</h3>
+
+<p>让我们快点玩一些数字,以重新定义我们所需要的基本语法。 在您的<a href="/zh-CN/docs/Learn/Discover_browser_developer_tools">开发工具JavaScript控制台</a>中输入下面列出的命令。</p>
+
+<p><strong><a href="https://mdn.github.io/learning-area/javascript/introduction-to-js-1/variables/">在新窗口中打开</a></strong></p>
+
+<ol>
+ <li>首先,我们先声明一对变量,并分别用一个整数和一个浮点数来初始化它们,然后再输入变量名来检查是否正确:
+ <pre class="brush: js notranslate">let myInt = 5;
+let myFloat = 6.667;
+myInt;
+myFloat;</pre>
+ </li>
+ <li>数值键入不带引号 —— 在继续之前尝试声明和初始化更多包含数字的变量。</li>
+ <li>现在我们来看看我们的原始变量是否是相同的数据类型。 在JavaScript中有一个称为{{jsxref("Operators / typeof", "typeof")}} 的运算符。 输入如下所示的两行:
+ <pre class="brush: js notranslate">typeof myInt;
+typeof myFloat;</pre>
+ 在这两种情况下,都应该返回 <code>"number"</code> —— 这使得事情变得更简单,因为若是不同的数字具有不同的数据类型,那么我们还须以不同的方式处理它们。呦!</li>
+</ol>
+
+<h2 id="算术运算符">算术运算符</h2>
+
+<p>算术运算符是我们用来做和的基本运算符:</p>
+
+<table class="standard-table" style="height: 321px; width: 852px;">
+ <thead>
+ <tr>
+ <th scope="col">运算符</th>
+ <th scope="col">名称</th>
+ <th scope="col">作用</th>
+ <th scope="col">示例</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td><code>+</code></td>
+ <td>加法</td>
+ <td>两个数相加。</td>
+ <td><code>6 + 9</code></td>
+ </tr>
+ <tr>
+ <td><code>-</code></td>
+ <td>减法</td>
+ <td>从左边减去右边的数。</td>
+ <td><code>20 - 15</code></td>
+ </tr>
+ <tr>
+ <td><code>*</code></td>
+ <td>乘法</td>
+ <td>两个数相乘。</td>
+ <td><code>3 * 7</code></td>
+ </tr>
+ <tr>
+ <td><code>/</code></td>
+ <td>除法</td>
+ <td>用右边的数除左边的数</td>
+ <td><code>10 / 5</code></td>
+ </tr>
+ <tr>
+ <td><code>%</code></td>
+ <td>求余(有时候也叫取模)</td>
+ <td>
+ <p>在你将左边的数分成同右边数字相同的若干整数部分后,返回剩下的余数</p>
+ </td>
+ <td><code>8 % 3</code> (返回 2,8除以3的倍数,余下2 。)</td>
+ </tr>
+ <tr>
+ <td><code>**</code></td>
+ <td>幂</td>
+ <td>
+ <p>取底数的指数次方,即指数所指定的底数相乘。它在EcmaScript 2016 中首次引入。</p>
+ </td>
+ <td><code>5 ** 5</code> (返回 3125,相当于 <code>5 * 5 * 5 * 5 * 5</code> 。)</td>
+ </tr>
+ </tbody>
+</table>
+
+<div class="note">
+<p><strong>Note</strong>: 你以后有时候会看到参与算术计算的数字被称为 操作数({{Glossary("Operand", "operands")}})。</p>
+</div>
+
+<div class="blockIndicator note">
+<p>Note: 有时你可能会看到使用较旧的 {{jsxref("Math.pow()")}} 方法表达的指数,该方法的工作方式非常相似。 例如,在 <code>Math.pow(7, 3)</code>  中,<code>7</code> 是基数,<code>3</code> 是指数,因此表达式的结果是 <code>343</code>。 <code>Math.pow(7, 3)</code> 相当于 <code>7 ** 3</code>。</p>
+</div>
+
+<p>我们可能不需要教你如何做基础数学,但我们想测试你对所涉及的语法的理解。 尝试将下面的示例输入到<a href="/zh-CN/docs/Learn/Discover_browser_developer_tools">开发者工具JavaScript控制台</a>中。</p>
+
+<ol>
+ <li>首先尝试输入一些简单的例子,例如
+ <pre class="brush: js notranslate">10 + 7
+9 * 8
+60 % 3</pre>
+ </li>
+ <li>您还可以尝试声明变量并用数字初始化变量,然后尝试使用这些变量来求和 - 求和中变量的行为与直接用其持有的数来求和完全一样。 例如:
+ <pre class="brush: js notranslate">let num1 = 10;
+let num2 = 50;
+9 * num1;
+num1 ** 3;
+num2 / num1;</pre>
+ </li>
+ <li>最后在本节中,尝试输入一些更复杂的表达式,如:
+ <pre class="brush: js notranslate">5 + 10 * 3;
+num2 % 9 * num1;
+num2 + num1 / 8 + 2;</pre>
+ </li>
+</ol>
+
+<p>这最后的一组计算中可能没有给出你期望的结果; 下面的部分也许能告诉你为什么。</p>
+
+<h3 id="运算符优先级">运算符优先级</h3>
+
+<p>我们来看看上面的最后一个例子,假设num2的值为50,num1的值为10(如上所述):</p>
+
+<pre class="brush: js notranslate">num2 + num1 / 8 + 2;</pre>
+
+<p>一般人,你会将它看作“50加10等于60”,然后“8加2等于10”,最后“60除以10等于6”。</p>
+
+<p>但浏览器的“10除以8等于1.25”,那么“50加1.25加2等于53.25”。</p>
+
+<p>这是因为<strong>运算符优先级</strong> —— 一些运算符将在计算算式(在编程中称为表达式)的结果时先于其他运算符被执行。 JavaScript中的运算符优先级与学校的数学课程相同 - 乘法和除法总是先完成,然后是加法和减法(总是从左到右进行计算)。</p>
+
+<p>如果想要改变计算优先级,可以把想要优先计算的部分用括号围住。 所以要得到结果为6,我们可以这样做:</p>
+
+<pre class="brush: js notranslate">(num2 + num1) / (8 + 2);</pre>
+
+<p>尝试看看。</p>
+
+<div class="note">
+<p><strong>Note</strong>: 注意:可以在<a href="/zh-CN/docs/Web/JavaScript/Guide/Expressions_and_Operators#运算符优先级">表达式和运算符</a>中找到所有JavaScript运算符的完整列表及其优先级。</p>
+</div>
+
+<h2 id="自增和自减运算符">自增和自减运算符</h2>
+
+<p>有时候,您需要反复把一个变量加1或减1。 这可以方便地使用增量(<code>++</code>)和递减( <code>--</code> )运算符来完成。 我们在<a href="/zh-CN/docs/Learn/JavaScript/First_steps/A_first_splash">JavaScript 初体验</a>文章的“猜数字”游戏中使用了++,当我们添加1到我们的guessCount变量来跟踪用户在每次转动之后剩下的猜测时。</p>
+
+<pre class="brush: js notranslate">guessCount++;</pre>
+
+<div class="note">
+<p><strong>Note</strong>: 它们最常用于 <a href="/zh-CN/docs/Web/JavaScript/Guide/Loops_and_iteration">循环</a> 中,您将在以后的课程中了解。 例如,假设您想循环查看价格表,并为每个价格增加销售税。 您可以使用循环依次查看每个值,并在每种情况下进行必要的计算,以添加销售税。 当需要时,增量器用于移动到下一个值。 我们实际上提供了一个简单的例子,显示了如何完成 ——  <a href="https://mdn.github.io/learning-area/javascript/introduction-to-js-1/maths/loop.html">在线查看效果</a>,并 <a href="https://github.com/mdn/learning-area/blob/master/javascript/introduction-to-js-1/maths/loop.html">查看源代码</a>,看看是否可以发现增量器! 我们稍后将会详细介绍循环。</p>
+</div>
+
+<p>我们来试试看你们的控制台。 首先,请注意,您不能将这些直接应用于一个数字,这可能看起来很奇怪,但是我们为变量赋值一个新的更新值,而不是对该值进行操作。 以下将返回错误:</p>
+
+<pre class="brush: js notranslate">3++;</pre>
+
+<p>所以,你只能增加一个现有的变量。 尝试这个:</p>
+
+<pre class="brush: js notranslate">let num1 = 4;
+num1++;</pre>
+
+<p>好的,第二个奇怪的东西! 执行此操作时,您会看到返回值为4,这是因为浏览器返回当前值,然后增加变量。 如果您再次返回变量值,则可以看到它已经递增:</p>
+
+<pre class="brush: js notranslate">num1;</pre>
+
+<p>递减 <code>--</code> 同样如此,尝试以下操作:</p>
+
+<pre class="brush: js notranslate">let num2 = 6;
+num2--;
+num2;</pre>
+
+<div class="note">
+<p><strong>Note</strong>: 您可以使浏览器以其他方式进行操作 - 递增/递减变量,然后返回值 - 将操作符放在变量的开头,而不是结束。 再次尝试上面的例子,但这次使用 <code>++num1</code> 和 <code>--num2</code>。</p>
+</div>
+
+<h2 id="赋值运算符">赋值运算符</h2>
+
+<p>赋值运算符是向变量分配值的运算符。 我们已经使用了最基本的一个很多次了:<code>=</code>, 它只是将右边的值赋给左边的变量 ,即:</p>
+
+<pre class="brush: js notranslate">let x = 3; // x 的值是 3
+let y = 4; // y 的值是 4
+x = y; // x 和 y 有相同的值, 4</pre>
+
+<p>但是还有一些更复杂的类型,它们提供了有用的快捷方式,可以使您的代码更加清洁和高效。 最常见的如下:</p>
+
+<table class="standard-table">
+ <thead>
+ <tr>
+ <th scope="col">运算符</th>
+ <th scope="col">名称</th>
+ <th scope="col">作用</th>
+ <th scope="col">示例</th>
+ <th scope="col">等价于</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td><code>+=</code></td>
+ <td>加法赋值</td>
+ <td>右边的数值加上左边的变量,然后再返回新的变量。</td>
+ <td><code>x = 3;<br>
+ x += 4;</code></td>
+ <td><code>x = 3;<br>
+ x = x + 4;</code></td>
+ </tr>
+ <tr>
+ <td><code>-=</code></td>
+ <td>减法赋值</td>
+ <td>左边的变量减去右边的数值,然后再返回新的变量。</td>
+ <td><code>x = 6;<br>
+ x -= 3;</code></td>
+ <td><code>x = 6;<br>
+ x = x - 3;</code></td>
+ </tr>
+ <tr>
+ <td><code>*=</code></td>
+ <td>乘法赋值</td>
+ <td>左边的变量乘以右边的数值,然后再返回新的变量。</td>
+ <td><code>x = 2;<br>
+ x *= 3;</code></td>
+ <td><code>x = 2;<br>
+ x = x * 3;</code></td>
+ </tr>
+ <tr>
+ <td><code>/=</code></td>
+ <td>除法赋值</td>
+ <td>左边的变量除以右边的数值,然后再返回新的变量。</td>
+ <td><code>x = 10;<br>
+ x /= 5;</code></td>
+ <td><code>x = 10;<br>
+ x = x / 5;</code></td>
+ </tr>
+ </tbody>
+</table>
+
+<p>尝试在你的控制台中输入上面的一些示例,以了解它们的工作原理。 在每种情况下,你是否可以猜出在输入第二行之前的值。</p>
+
+<p>请注意,您可以愉快地使用每个表达式右侧的其他变量,例如:</p>
+
+<pre class="brush: js notranslate">let x = 3; // x 包含值 3
+let y = 4; // y 包含值 4
+x *= y; // x 现在包含值 12</pre>
+
+<div class="note">
+<p><strong>Note</strong>: 虽然有很多可用的<a href="/zh-CN/docs/Web/JavaScript/Guide/Expressions_and_Operators#Assignment_operators">赋值运算符</a>, 但是这些是你现在应该学习的基本的一类。</p>
+</div>
+
+<h2 id="主动学习:调整画布框的大小">主动学习:调整画布框的大小</h2>
+
+<p>在这个练习中,我们将让你填写一些数字和操作符来操纵一个框的大小。 该框使用称为{{domxref("Canvas API", "", "", "true")}}的浏览器API绘制。 没有必要担心这是如何工作的 - 现在只关注数学。 盒子的宽度和高度(以像素为单位)由变量 <code>x</code> 和 <code>y</code> 定义,变量 <code>x</code> 和 <code>y</code> 最初都被赋值为50。</p>
+
+<p>{{EmbedGHLiveSample("learning-area/javascript/introduction-to-js-1/maths/editable_canvas.html", '100%', 620)}}</p>
+
+<p><strong><a href="https://mdn.github.io/learning-area/javascript/introduction-to-js-1/maths/editable_canvas.html">新窗口打开</a></strong></p>
+
+<p>在上面的可编辑代码框中,有两行标有清晰的注释,我们希望你更新以使框增长/缩小到某些大小,在每种情况下使用某些操作符和/或值。 我们希望你回答以下问题:</p>
+
+<ul>
+ <li>更改计算x的行,使框仍然是50px宽,并且使用数字43和7以及算术运算符计算50。</li>
+ <li>更改计算y的行,使框为高75像素,使用数字25和3计算75,以及算术运算符。</li>
+ <li>更改计算x的行,使框为250px宽,250是使用两个数字和余数(模)运算符计算的。</li>
+ <li>更改计算y的行,使框为150px高,150是使用三个数字计算的,以及减法和除数运算符。</li>
+ <li>更改计算x的行,因此该框为200px宽,并且使用数字4和赋值运算符计算200。</li>
+ <li>更改计算y的行,使框为200px高,使用数字50和3,乘法运算符和加法运算符计算200。</li>
+</ul>
+
+<p>如果你完全混淆了代码,别担心。 您可以随时按“重置”按钮,使事情恢复正常。 在您正确回答了上述所有问题后,可以自由地使用代码或创建自己的挑战。</p>
+
+<h2 id="比较运算符">比较运算符</h2>
+
+<p>有时,我们将要运行真/假测试,然后根据该测试的结果进行相应的操作 - 为此,我们使用比较运算符。</p>
+
+<table class="standard-table">
+ <thead>
+ <tr>
+ <th scope="col">运算符</th>
+ <th scope="col">名称</th>
+ <th scope="col">作用</th>
+ <th scope="col">示例</th>
+ </tr>
+ <tr>
+ <td><code>===</code></td>
+ <td>严格等于</td>
+ <td>测试左右值是否相同</td>
+ <td><code>5 === 2 + 4</code></td>
+ </tr>
+ <tr>
+ <td><code>!==</code></td>
+ <td>严格不等于</td>
+ <td>测试左右值是否<strong>不</strong>相同</td>
+ <td><code>5 !== 2 + 3</code></td>
+ </tr>
+ <tr>
+ <td><code>&lt;</code></td>
+ <td>小于</td>
+ <td>测试左值是否小于右值。</td>
+ <td><code>10 &lt; 6</code></td>
+ </tr>
+ <tr>
+ <td><code>&gt;</code></td>
+ <td>大于</td>
+ <td>测试左值是否大于右值</td>
+ <td><code>10 &gt; 20</code></td>
+ </tr>
+ <tr>
+ <td>&lt;=</td>
+ <td>小于或等于</td>
+ <td>测试左值是否小于或等于右值。</td>
+ <td><code>3 &lt;= 2</code></td>
+ </tr>
+ <tr>
+ <td>&gt;=</td>
+ <td>大于或等于</td>
+ <td>测试左值是否大于或等于正确值。</td>
+ <td><code>5 &gt;= 4</code></td>
+ </tr>
+ </thead>
+</table>
+
+<div class="note">
+<p><strong>Note</strong>: 您可能会看到有些人在他们的代码中使用<code>==</code>和<code>!=</code>来判断相等和不相等,这些都是JavaScript中的有效运算符,但它们与<code>===</code>/<code>!==</code>不同,前者测试值是否相同, 但是数据类型可能不同,而后者的严格版本测试值和数据类型是否相同。 严格的版本往往导致更少的错误,所以我们建议您使用这些严格的版本。</p>
+</div>
+
+<p>如果您尝试在控制台中输入这些值,您将看到它们都返回 <code>true</code>/<code>false</code> 值 - 我们在上一篇文章中提到的那些布尔值。 这些是非常有用的,因为它们允许我们在我们的代码中做出决定 - 每次我们想要选择某种类型时,都会使用这些代码,例如:</p>
+
+<ul>
+ <li>根据功能是打开还是关闭,在按钮上显示正确的文本标签。</li>
+ <li>如果游戏结束,则显示游戏消息,或者如果游戏已经获胜,则显示胜利消息。</li>
+ <li>显示正确的季节性问候,取决于假期季节。</li>
+ <li>根据选择的缩放级别缩小或放大地图。</li>
+</ul>
+
+<p>当我们在以后的的文章中查看条件语句时,我们将介绍如何编写这样的逻辑。 现在,我们来看一个简单的例子:</p>
+
+<pre class="brush: html notranslate">&lt;button&gt;Start machine&lt;/button&gt;
+&lt;p&gt;The machine is stopped.&lt;/p&gt;
+</pre>
+
+<pre class="brush: js notranslate">const btn = document.querySelector('button');
+const txt = document.querySelector('p');
+
+btn.addEventListener('click', updateBtn);
+
+function updateBtn() {
+ if (btn.textContent === 'Start machine') {
+ btn.textContent = 'Stop machine';
+ txt.textContent = 'The machine has started!';
+ } else {
+ btn.textContent = 'Start machine';
+ txt.textContent = 'The machine is stopped.';
+ }
+}</pre>
+
+<p>{{EmbedGHLiveSample("learning-area/javascript/introduction-to-js-1/maths/conditional.html", '100%', 100)}}</p>
+
+<p><strong><a href="https://mdn.github.io/learning-area/javascript/introduction-to-js-1/maths/conditional.html">新窗口打开</a></strong></p>
+
+<p>您可以在updateBtn()函数内看到正在使用的等号运算符。 在这种情况下,我们不会测试两个数学表达式是否具有相同的值 - 我们正在测试一个按钮的文本内容是否包含某个字符串 - 但它仍然是工作原理。 如果按钮当前按“启动机”,则将其标签更改为“停机”,并根据需要更新标签。 如果按下按钮当前正在说“停机”,我们再次将显示器交换回来。</p>
+
+<div class="note">
+<p><strong>Note</strong>: 这种在两个状态之间来回交换的行为通常被称为<strong> </strong><strong>切换 (toggle)</strong>。它在一个状态和另一个状态之间切换 - 点亮,熄灭等</p>
+</div>
+
+<h2 id="总结">总结</h2>
+
+<p>在本文中,我们已经介绍了现在需要了解JavaScript中数字的基本信息。 你会发现,在你学习JavaScript过程中,num类型的数据一直都在被使用,所以熟练的掌握它是一个不错的选择。 如果你是那些不喜欢数学的人之一,你应该庆幸这一章内容很简短。</p>
+
+<p>在下一篇文章中,我们将探讨文本,以及JavaScript如何让我们操纵它。</p>
+
+<div class="note">
+<p><strong>Note</strong>:  如果您喜欢数学,并希望阅读更多关于它如何在JavaScript中实现的, 那么你可以在 MDN's main JavaScript 部分读到更多关于它的内容。对于学习<a href="/zh-CN/docs/Web/JavaScript/Guide/Numbers_and_dates">数字与日期</a> 和 <a href="/zh-CN/docs/Web/JavaScript/Guide/Expressions_and_Operators#运算符优先级">表达式与运算符</a> 来说,那是一个不错的地方。</p>
+</div>
+
+<p>{{PreviousMenuNext("Learn/JavaScript/First_steps/Variables", "Learn/JavaScript/First_steps/Strings", "Learn/JavaScript/First_steps")}}</p>
+
+<h2 id="在这个模块">在这个模块</h2>
+
+<ul>
+ <li><a href="/zh-CN/docs/Learn/JavaScript/First_steps/What_is_JavaScript">什么是 JavaScript?</a></li>
+ <li><a href="/zh-CN/docs/Learn/JavaScript/First_steps/A_first_splash">JavaScript 初体验</a></li>
+ <li><a href="/zh-CN/docs/Learn/JavaScript/First_steps/What_went_wrong">哪里出错了</a><a href="/zh-CN/docs/Learn/JavaScript/First_steps/What_went_wrong">? JavaScript 故障排除</a></li>
+ <li><a href="/zh-CN/docs/Learn/JavaScript/First_steps/Variables">存储你需要的信息——变量</a></li>
+ <li><a href="/zh-CN/docs/Learn/JavaScript/First_steps/Math">JavaScript 中的基本运算——数值和运算符</a></li>
+ <li><a href="/zh-CN/docs/Learn/JavaScript/First_steps/Strings">文字处理——JavaScript 字符串</a></li>
+ <li><a href="/zh-CN/docs/Learn/JavaScript/First_steps/Useful_string_methods">实用的strings方法</a></li>
+ <li><a href="/zh-CN/docs/Learn/JavaScript/First_steps/Arrays">Arrays</a></li>
+ <li><a href="/zh-CN/docs/Learn/JavaScript/First_steps/Silly_story_generator">Assessment: Silly story generator</a></li>
+</ul>
diff --git a/files/zh-cn/learn/javascript/first_steps/silly_story_generator/index.html b/files/zh-cn/learn/javascript/first_steps/silly_story_generator/index.html
new file mode 100644
index 0000000000..f99ff16949
--- /dev/null
+++ b/files/zh-cn/learn/javascript/first_steps/silly_story_generator/index.html
@@ -0,0 +1,239 @@
+---
+title: 笑话生成器
+slug: Learn/JavaScript/First_steps/Silly_story_generator
+tags:
+ - JavaScript
+ - 初学者
+ - 变量
+ - 字符串
+ - 学习
+ - 操作符
+ - 数字
+ - 数组
+ - 测试
+ - 脚本代码
+ - 课程设计
+translation_of: Learn/JavaScript/First_steps/Silly_story_generator
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenu("Learn/JavaScript/First_steps/Arrays", "Learn/JavaScript/First_steps")}}</div>
+
+<p class="summary">本节是一个小测验,要求你运用所学知识制作一个笑话生成器。祝玩的愉快!</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">预备知识:</th>
+ <td>请读完本章所有小节的内容后再开始这个测验。</td>
+ </tr>
+ <tr>
+ <th scope="row">目标:</th>
+ <td>测试你对变量、数字、操作符、字符串和数组等 Javascript 基本概念的理解程度。</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="起点">起点</h2>
+
+<p>测验开始之前需要下载并保存 <a class="external external-icon" href="https://github.com/roy-tian/learning-area/blob/master/javascript/introduction-to-js-1/assessment-start/index.html">index.html</a>、<a class="external external-icon" href="https://github.com/roy-tian/learning-area/blob/master/javascript/introduction-to-js-1/assessment-start/style.css">style.css</a>、<a class="external external-icon" href="https://github.com/roy-tian/learning-area/blob/master/javascript/introduction-to-js-1/assessment-start/raw-text.txt">raw-text.txt</a>。</p>
+
+<div class="note">
+<p><strong>注:</strong> 你还可以用类似 <a class="external external-icon" href="http://jsbin.com/">JSBin</a> 或 <a class="external external-icon" href="https://thimble.mozilla.org/">Thimble</a> 这些在线编辑器来完成测验。你可以把 HTML、CSS 及 JavaScript 代码复制过去。如果你选的工具没有独立的 JavaScript 版面,可以随时在 HTML 页面中添加 <code>&lt;script&gt;</code> 元素。</p>
+</div>
+
+<h2 id="项目简介">项目简介</h2>
+
+<p>我们提供了一些原始的 HTML / CSS,以及若干字符串和 JavaScript 函数,还需要你来编写一些 JavaScript 代码让项目运行起来:</p>
+
+<ul>
+ <li>点击“随机生成笑话”按钮时生成一则笑话。</li>
+ <li>若“生成”按钮按下之前,你在“输入自定义的名字”文字框中输入了一个自定义名字,那么生成的笑话中原有的名字(李雷 / Bob)将被取代。</li>
+ <li>通过选择国家名称的单选按钮来确定界面语言以及笑话中温度和重量的制式。</li>
+ <li>点一次按钮,生成一个新故事。点一次生成一个……</li>
+</ul>
+
+<p>可以尝试操作一下:(别偷看源代码哦!)</p>
+
+<div class="hidden">
+<h6 id="Silly_story_generator">Silly story generator</h6>
+
+<pre class="brush: html notranslate">&lt;!DOCTYPE html&gt;
+&lt;html&gt;
+&lt;head&gt;
+ &lt;meta charset="utf-8"&gt;
+ &lt;style&gt;
+ body {
+ font-family: helvetica, sans-serif;
+ width: 350px;
+ border: 1px solid;
+ padding: 1em;
+ }
+ label {
+ font-weight: bold;
+ }
+ div {
+ padding-bottom: 20px;
+ }
+ input[type="text"] {
+ padding: 5px;
+ width: 150px;
+ }
+ p {
+ background: #FFC125;
+ color: #5E2612;
+ padding: 10px;
+ visibility: hidden;
+ }
+ &lt;/style&gt;
+&lt;/head&gt;
+
+&lt;body&gt;
+&lt;div&gt;
+ &lt;label for="customname" id="lbl-customname"&gt;请输入自定义的名字:&lt;/label&gt;
+ &lt;input id="customname" type="text" placeholder="李雷"&gt;
+&lt;/div&gt;
+&lt;div&gt;
+      &lt;label for="metric"&gt;公制&lt;/label&gt;&lt;input id="metric" type="radio" name="measure" value="metric" checked&gt;
+      &lt;label for="american"&gt;美制&lt;/label&gt;&lt;input id="american" type="radio" name="measure" value="american"&gt;
+&lt;/div&gt;
+&lt;div&gt;
+ &lt;button class="randomize"&gt;随机生成笑话&lt;/button&gt;
+&lt;/div&gt;
+&lt;!-- Thanks a lot to Willy Aguirre for his help with the code for this assessment --&gt;
+&lt;p class="story"&gt;&lt;/p&gt;
+
+&lt;script&gt;
+const customName = document.getElementById('customname');
+const randomize = document.querySelector('.randomize');
+const story = document.querySelector('.story');
+
+function randomValueFromArray(array){
+  const random = Math.floor(Math.random() * array.length);
+  return array[random];
+}
+
+let storyText = '今天气温 35 摄氏度,:insertx:出门散步。当走到:inserty:门前时,突然就:insertz:。人们都惊呆了,李雷全程目睹但并没有慌,因为:insertx:是一个 140 公斤的胖子,天气又辣么热。';
+let insertX = ['怪兽威利', '大老爹', '圣诞老人'];
+let insertY = ['肯德基', '迪士尼乐园', '白宫'];
+let insertZ = ['自燃了', '在人行道化成了一坨泥', '变成一只鼻涕虫爬走了'];
+
+randomize.addEventListener('click', result);
+
+function result() {
+  let newStory = storyText;
+
+  let xItem = randomValueFromArray(insertX);
+  let yItem = randomValueFromArray(insertY);
+  let zItem = randomValueFromArray(insertZ);
+
+  newStory = newStory.replace(':insertx:', xItem);
+  newStory = newStory.replace(':insertx:', xItem);
+  newStory = newStory.replace(':inserty:', yItem);
+  newStory = newStory.replace(':insertz:', zItem);
+
+  if(customName.value !== '') {
+    const name = customName.value;
+    newStory = newStory.replace('李雷', name);
+  }
+
+  if(document.getElementById("american").checked) {
+    const weight = Math.round(140 * 2.20462) + ' 磅';
+    const temperature =  Math.round(35 * 9 / 5 + 32) + ' 华氏度';
+    newStory = newStory.replace('35 摄氏度', temperature);
+    newStory = newStory.replace('140 公斤', weight);
+  }
+
+  story.textContent = newStory;
+  story.style.visibility = 'visible';
+}
+&lt;/script&gt;
+
+&lt;/body&gt;
+&lt;/html&gt;</pre>
+</div>
+
+<p>{{ EmbedLiveSample('Silly_story_generator', '100%', 350, "", "", "hide-codepen-jsfiddle") }}</p>
+
+<div class="note">
+<p><strong>译注: </strong><a class="external external-icon">点击在线试用</a> 汉化版本。有兴趣还可以在本节结束后回来 <a class="external external-icon" href="https://github.com/roy-tian/learning-area/tree/master/javascript/introduction-to-js-1/assessment-finished">看看代码</a>。(没兴趣就跳过吧 :)</p>
+</div>
+
+<h2 id="步骤">步骤</h2>
+
+<p>以下是你的工作。</p>
+
+<p>初始化变量和函数:</p>
+
+<ol>
+ <li>将刚下载的文本文件中的“1. 定义变量和函数”标题项下所有代码复制粘贴至 main.js 中。此时你就有了三个变量(<code>customName</code> 是对“输入自定义的名字”文本框的引用,<code>randomize</code> 是对“随机生成笑话”按钮的引用,<code>story</code> 是对 HTML 底部的、准备存放笑话的 {{htmlelement("p")}} 元素的引用)和一个函数(<code>randomValueFromArray()</code> 取一个数组作参数,随机返回数组中的一个元素)。</li>
+ <li>然后是文本文件的第二节——“2. 纯文本字符串”。这里包含了一些字符串,这些字符串是项目的输入信息。你应该在 main.js 文件中用变量来保存它们。
+ <ol>
+ <li>用 <code>storyText</code> 变量保存第一个长字符串,“今天气温……”。</li>
+ <li>用 <code>insertX</code> 数组保存第一组三个字符串,“怪兽威利……”。</li>
+ <li>用 <code>insertY</code> 数组保存第二组三个字符串。“肯德基……”。</li>
+ <li>用 <code>insertZ</code> 数组保存第三组三个字符串。“自燃了……”。</li>
+ </ol>
+ </li>
+</ol>
+
+<p>放置事件处理器并补全:</p>
+
+<ol>
+ <li>返回刚才的文本文件。</li>
+ <li>将“3. 事件监听器和未完成的函数定义”标题项下的代码复制粘贴至 <code>main.js</code> 文件。这将:
+ <ul>
+ <li>为 <code>randomize</code> 变量增加一个点击事件的监听器。于是当所引用的按钮被点击时,<code>result()</code> 函数就会运行。</li>
+ <li>为代码添加一个未完成的 <code>result()</code> 函数定义。本测验剩下的工作就是完成这个函数,让程序正常运行起来。</li>
+ </ul>
+ </li>
+</ol>
+
+<p>补全 <code>result()</code> 函数:</p>
+
+<ol>
+ <li>将 <code>newStory</code> 的值设置为 <code>storyText</code>。声明新变量有必要的,只有这样才能在每次按下按钮后、在函数运行时生成一个新的随机笑话。如果直接操作 <code>storyText</code> 则只能生成一次新故事</li>
+ <li>将 <code>xItem</code>、<code>yItem</code> 和 <code>zItem</code> 分别设置为 <code>randomValueFromArray(insertX)</code>、<code>randomValueFromArray(insertY)</code> 和 <code>randomValueFromArray(insertZ)</code>。</li>
+ <li>接下来将 <code>newStory</code> 中的占位符(<code>:inserta:</code>、<code>:insertb:</code> 和 <code>:insertc:</code> )替换为 <code>xItem</code>、<code>yItem</code> 和 <code>zItem</code>。有专用的字符串方法可供使用,并用该方法的返回值为 <code>newStory</code> 赋值。每当按下按钮时,这些占位符都会替换为随机的笑话字符串。再给你一点提示,我们所说的这种方法每次只会将所找到的首个子字符串进行替换,因此该方法对某个占位符需要执行两次。</li>
+ <li>在第一个 <code>if</code> 块中再次调用这个字符串替换方法,以使 <code>newStory</code> 字符串中的名字“李雷”替换为变量 <code>name</code> 的值 。这里我们说:“如果 <code>customName</code> 中有值,就把故事里的 “李雷”替换成它。” 如果是汉化版将newStory中的“李雷”替换成 <code>name</code> 的值;</li>
+ <li>在第二个 <code>if</code> 块中检查 <code>american</code> 单选按钮是否被选中。如果选中,就要将故事中的重量和温度值从公斤和摄氏度转换为磅和华氏度,具体事项如下:
+ <ol>
+ <li>确定英美单位的转换公式。</li>
+ <li>定义变量 <code>weight</code>、<code>temperature</code> 的行中,分别将美制单位按公式转化为英制,用 <code>Math.round()</code> 对计算结果取整。然后将英式单位连接到末尾。</li>
+ <li>就在上述两个变量的定义下方增加两个字符串置换操作,将“35 摄氏度”替换为 <code>temperature</code> 的值,将“140 公斤”替换为 <code>weight</code> 的值。</li>
+ </ol>
+ </li>
+ <li>最后,在函数倒数第二行,将 <code>story.textContent</code>(程序中显示笑话结果的段落) 赋值为 <code>newStory</code>。</li>
+</ol>
+
+<h2 id="提示">提示</h2>
+
+<ul>
+ <li>除了在 HTML 文件中引入这个 JavaScript 文件之外,完全不需要编辑 HTML。</li>
+ <li>如果你不确定当前 JavaScript 是否正确添加到了你的 HTML 中,可以尝试暂时删除 JavaScript 文件的所有内容,然后加上一些简单但效果显著的 JavaScript 代码,保存文件并刷新浏览器。下面的示例会让 {{htmlelement("html")}} 背景变为红色,如果 JavaScript 成功加载,那么整个浏览器窗口将变红:</li>
+ <li>
+ <pre class="brush: js line-numbers language-js notranslate"><code class="language-js">document<span class="punctuation token">.</span><span class="function token">querySelector</span><span class="punctuation token">(</span><span class="string token">'html'</span><span class="punctuation token">)</span><span class="punctuation token">.</span>style<span class="punctuation token">.</span>backgroundColor <span class="operator token">=</span> <span class="string token">'red'</span><span class="punctuation token">;</span></code></pre>
+ </li>
+ <li><code class="language-js"><span class="punctuation token"><a href="/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Math/round">Math.round()</a></span></code> 是 Javascript 的内建函数,可取得与传入小数最接近的整数。</li>
+ <li>本示例中有三类字符串需要替换。可以多次重复 <code>replace()</code> 方法,也可使用正则表达式。例如:<code>var text = 'I am the biggest lover, I love my love';</code> 或 <code>text.replace(/love/g,'like');</code> 会将所有的“love”替换为“like”。记住,字符串本身是不可修改的!</li>
+</ul>
+
+<h2 id="测验">测验</h2>
+
+<p>如果你是在课堂上进行这个测验,你可以把作品交给导师或教授去打分了。如果你是在自学,也可以在 <a href="https://discourse.mozilla.org/t/silly-story-generator-assessment/24686">本节测验的讨论页</a> 或者 <a href="https://wiki.mozilla.org/IRC">Mozilla 聊天室 </a>的 <a href="irc://irc.mozilla.org/mdn">#mdn</a> 频道取得帮助。要自己先尝试,作弊是不会有收获的!</p>
+
+<p>{{PreviousMenu("Learn/JavaScript/First_steps/Arrays", "Learn/JavaScript/First_steps")}}</p>
+
+<h2 id="本章目录">本章目录</h2>
+
+<ul>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps/What_is_JavaScript">JavaScript 是什么?</a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps/A_first_splash">JavaScript 初体验</a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps/What_went_wrong">查找并解决 JavaScript 代码的错误 </a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps/Variables">变量:储存所需信息</a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps/Math">数字和运算符:JavaScript 的基本算数</a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps/Strings">字符串:JavaScript 文本的处理</a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps/Useful_string_methods">字符串的一些实用方法</a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps/Arrays">数组</a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps/Silly_story_generator">课程评估:笑话机</a></li>
+</ul>
diff --git a/files/zh-cn/learn/javascript/first_steps/strings/index.html b/files/zh-cn/learn/javascript/first_steps/strings/index.html
new file mode 100644
index 0000000000..36352b5f48
--- /dev/null
+++ b/files/zh-cn/learn/javascript/first_steps/strings/index.html
@@ -0,0 +1,288 @@
+---
+title: 文本处理 — JavaScript中的字符串
+slug: Learn/JavaScript/First_steps/Strings
+tags:
+ - JavaScript
+ - Join
+ - 初学者
+ - 字符串
+ - 指南
+ - 文章
+ - 脚本编写
+translation_of: Learn/JavaScript/First_steps/Strings
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/JavaScript/First_steps/Math", "Learn/JavaScript/First_steps/Useful_string_methods", "Learn/JavaScript/First_steps")}}</div>
+
+<p class="summary">接下来,我们将把注意力转向文本片段——也就是编程中所说的<strong>字符串</strong>。在本文中,我们将了解在学习JavaScript时,您应该了解的关于字符串的所有常见事项,例如创建字符串、在字符串中转义引号,和连接字符串。</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">预备知识:</th>
+ <td>基本的计算机读写能力,对HTML和CSS的基本理解,对JavaScript的理解。</td>
+ </tr>
+ <tr>
+ <th scope="row">目标:</th>
+ <td>要熟悉JavaScript中字符串的基本知识。</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="语言的力量">语言的力量</h2>
+
+<p>语言对人类非常重要——它们是我们交流的重要组成部分。由于Web是一种主要基于文本的媒介,旨在让人们进行交流和分享信息,因此对我们来说,掌握它所出现的单词是很有用的。{{glossary("HTML")}}为我们的文本提供了结构和意义, {{glossary("CSS")}} 允许我们精确地设计它的样式,JavaScript包含许多操作字符串的特性,创建定制的欢迎消息,在需要时显示正确的文本标签,将术语排序到所需的顺序,等等。</p>
+
+<p>到目前为止,我们在课程中展示的所有程序都涉及到一些字符串操作。</p>
+
+<h2 id="字符串_—_基本知识">字符串 — 基本知识</h2>
+
+<p>字符串与数字的处理方式第一眼看上去十分相似,但是当您深入挖掘时,您将会看到一些显著的差异。让我们首先在一个控制台输入一些基本的行来熟悉一下。<br>
+ 我们在下面提供了一个(您也可以在一个单独的选项卡或窗口中<a href="https://mdn.github.io/learning-area/javascript/introduction-to-js-1/variables/index.html">打开这个控制台</a>,或者如果您愿意使用<a href="https://developer.mozilla.org/en-US/docs/Learn/Common_questions/What_are_browser_developer_tools">浏览器开发人员控制台</a>)。</p>
+
+<div class="hidden">
+<h6 id="Hidden_code">Hidden code</h6>
+
+<pre class="brush: html">&lt;!DOCTYPE html&gt;
+&lt;html&gt;
+ &lt;head&gt;
+ &lt;meta charset="utf-8"&gt;
+ &lt;title&gt;JavaScript console&lt;/title&gt;
+ &lt;style&gt;
+ * {
+ box-sizing: border-box;
+ }
+
+ html {
+ background-color: #0C323D;
+ color: #809089;
+ font-family: monospace;
+ }
+
+ body {
+ max-width: 700px;
+ }
+
+ p {
+ margin: 0;
+ width: 1%;
+ padding: 0 1%;
+ font-size: 16px;
+ line-height: 1.5;
+ float: left;
+ }
+
+ .input p {
+ margin-right: 1%;
+ }
+
+ .output p {
+ width: 100%;
+ }
+
+ .input input {
+ width: 96%;
+ float: left;
+ border: none;
+ font-size: 16px;
+ line-height: 1.5;
+ font-family: monospace;
+ padding: 0;
+ background: #0C323D;
+ color: #809089;
+ }
+
+ div {
+ clear: both;
+ }
+
+ &lt;/style&gt;
+ &lt;/head&gt;
+ &lt;body&gt;
+
+
+ &lt;/body&gt;
+
+ &lt;script&gt;
+ var geval = eval;
+ function createInput() {
+ var inputDiv = document.createElement('div');
+ var inputPara = document.createElement('p');
+ var inputForm = document.createElement('input');
+
+ inputDiv.setAttribute('class','input');
+ inputPara.textContent = '&gt;';
+ inputDiv.appendChild(inputPara);
+ inputDiv.appendChild(inputForm);
+ document.body.appendChild(inputDiv);
+
+ inputForm.addEventListener('change', executeCode);
+ }
+
+ function executeCode(e) {
+ try {
+ var result = geval(e.target.value);
+ } catch(e) {
+ var result = 'error — ' + e.message;
+ }
+
+ var outputDiv = document.createElement('div');
+ var outputPara = document.createElement('p');
+
+ outputDiv.setAttribute('class','output');
+ outputPara.textContent = 'Result: ' + result;
+ outputDiv.appendChild(outputPara);
+ document.body.appendChild(outputDiv);
+
+ e.target.disabled = true;
+ e.target.parentNode.style.opacity = '0.5';
+
+ createInput()
+ }
+
+ createInput();
+
+ &lt;/script&gt;
+&lt;/html&gt;</pre>
+</div>
+
+<p>{{ EmbedLiveSample('Hidden_code', '100%', 300) }}</p>
+
+<h3 id="创建一个字符串">创建一个字符串</h3>
+
+<ol>
+ <li>首先, 输入下面的代码:
+ <pre class="brush: js">let string = 'The revolution will not be televised.';
+string;</pre>
+ 就像我们处理数字一样,我们声明一个变量,用一个字符串值初始化它,然后返回值。这里惟一的区别是,在编写字符串时,我们需要在字符串上加上引号。</li>
+ <li>如果你不这样做,或者在书写过程中,漏掉其中一个引号,你就会得到一个错误。<br>
+ 试着输入以下几行:
+ <pre class="brush: js example-bad">let badString = This is a test;
+let badString = 'This is a test;
+let badString = This is a test';</pre>
+ 这些行不起作用,因为没有引号的任何文本字符串都被假定为变量名、属性名、保留字或类似。如果浏览器不能找到它,那么将会引发语法错误(例如:"missing ; before statement")。<br>
+ 如果浏览器能够识别字符串从哪里开始,但是不能找到字符串的结尾符,如第二行所示,那么它则会提示这样的错误(“unterminated string literal”)。如果您写的程序目前也引发这样的错误,那么请你回过头来仔细检查你的代码,看是否漏写了引号。</li>
+ <li>如果您之前定义了变量字符串,下面的操作将会起作用 — 现在来试一试:
+ <pre class="brush: js">let badString = string;
+badString;</pre>
+
+ <p>现在将 <code>string</code> 的值赋值给 <code>badString</code>,赋值之后,两个字符串的值相等。</p>
+ </li>
+</ol>
+
+<h3 id="单引号和双引号">单引号和双引号</h3>
+
+<ol>
+ <li>在JavaScript中,您可以选择单引号或双引号来包裹字符串。<br>
+ 下面两种方式都可以:
+ <pre class="brush: js">let sgl = 'Single quotes.';
+let dbl = "Double quotes";
+sgl;
+dbl;</pre>
+ </li>
+ <li>两者之间几乎没有什么区别,根据个人偏好来使用。但是,您应该选择一个并坚持使用它,不一致的引号混用代码可能会让人很迷惑,特别是如果您在同一个字符串中使用不同的引号!<br>
+ 下面将返回一个错误:
+ <pre class="brush: js example-bad">let badQuotes = 'What on earth?";</pre>
+ </li>
+ <li>浏览器会认为字符串没有被关闭,因为在字符串中您没有使用其他类型的引号。<br>
+ 例如,这两种情况都是可以的:
+ <pre class="brush: js">let sglDbl = 'Would you eat a "fish supper"?';
+let dblSgl = "I'm feeling blue.";
+sglDbl;
+dblSgl;</pre>
+ </li>
+ <li>但是,您不能在字符串中包含相同的引号,因为它是用来包含它们的。下面将会出现错误,因为它会混淆浏览器和字符串的结束位置:
+ <pre class="brush: js example-bad">let bigmouth = 'I've got no right to take my place...';</pre>
+ 这个指引将会让我们很好地进入下一个主题。</li>
+</ol>
+
+<h3 id="转义字符串中的字符">转义字符串中的字符</h3>
+
+<p>要修复我们之前的问题代码行,我们需要避免引号的问题。转义字符意味着我们对它们做一些事情,以确保它们被识别成文本,而不是代码的一部分。在JavaScript中,我们通过在字符之前放一个反斜杠来实现这一点。试试这个:</p>
+
+<pre class="brush: js">let bigmouth = 'I\'ve got no right to take my place...';
+bigmouth;</pre>
+
+<p>这回正常了。你可以用别的方式来达到一样的目的, 例如. <code>\",</code> 除此之外有一些特殊的代码 。更多细节请参见<a href="/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String#Parameters">转义符号</a>。</p>
+
+<h2 id="连接字符串">连接字符串</h2>
+
+<ol>
+ <li>连接是一个很花哨的编程词,意思是“连接在一起”。在JavaScript中连接字符串使用加号(+)操作符,我们也用它来将数字加在一起,但是在这种情况下,它做了一些不同的事情。让我们在控制台中尝试一个例子。
+ <pre class="brush: js">let one = 'Hello, ';
+let two = 'how are you?';
+let joined = one + two;
+joined;</pre>
+ 变量 <code>joined</code> 的值的结果,它包含的值为 "Hello, how are you?"。</li>
+ <li>最后一个例子中, 我们只是把两个字符串连接在一起,但是你可以喜欢连接多少就多少个, 只需要在它们之间加上 + 操作符。试试这个:
+ <pre class="brush: js">let multiple = one + one + one + one + two;
+multiple;</pre>
+ </li>
+ <li>你还能用真实的字符串和变量来混合。试试这个:
+ <pre class="brush: js">let response = one + 'I am fine — ' + two;
+response;</pre>
+ </li>
+</ol>
+
+<div class="note">
+<p><strong>注意</strong>: 当您在您的代码中输入一个实际的字符串时,用单引号或双引号括起来,它被称为字符串文字。</p>
+</div>
+
+<h3 id="上下文中的串联">上下文中的串联</h3>
+
+<p>让我们看一下在操作中使用的连接——这是本课程早些时候的一个例子:</p>
+
+<pre class="brush: html">&lt;button&gt;Press me&lt;/button&gt;</pre>
+
+<pre class="brush: js">const button = document.querySelector('button');
+
+button.onclick = function() {
+ let name = prompt('What is your name?');
+ alert('Hello ' + name + ', nice to see you!');
+}</pre>
+
+<p>{{ EmbedLiveSample('上下文中的串联', '100%', 50, "", "", "hide-codepen-jsfiddle") }}</p>
+
+<p>这里我们使用的是第4行中的 {{domxref("window.prompt()", "window.prompt()")}} 函数, 它要求用户通过一个弹出对话框回答一个问题然后将他们输入的文本存储在一个给定的变量中 — 在这个例子中是就是 <code>name</code> 变量。然后,我们在第5行中使用  {{domxref("window.alert()","window.alert()")}}  函数来显示另一个弹出窗口,其中包含一个字符串,我们用两个字符串常量和name变量通过连接进行组合。</p>
+
+<h3 id="数字与字符">数字与字符</h3>
+
+<ol>
+ <li>当我们尝试添加(或连接)一个字符串和一个数字时,会发生什么?<br>
+ 让我们在我们的控制台中尝试一下:
+ <pre class="brush: js">'Front ' + 242;
+</pre>
+ 您可能会认为这会抛出一个错误,但它运行得很好。<br>
+ 试图将字符串表示为一个数字并不是很讲的通,但是用数字表示一个字符串则不然,因此浏览器很聪明地将数字转换为字符串,并将这两个字符串连接在一起。</li>
+ <li>你甚至可以用两个数字来这么操作——你可以通过用引号将数字包装成一个字符串。尝试以下方法(我们使用typeof操作符来检查变量是一个数字还是一个字符串):
+ <pre class="brush: js">let myDate = '19' + '67';
+typeof myDate;</pre>
+ </li>
+ <li>如果您有一个数值变量,您想要将其转换为字符串,并且不改变其他地方,或者您想将一个字符串转换为一个数字而不改变其其他地方,那么您可以使用以下两个构造:
+ <ul>
+ <li>如果可以的话, {{jsxref("Number")}} 对象将把传递给它的任何东西转换成一个数字。<br>
+ 试一试:
+ <pre class="brush: js">let myString = '123';
+let myNum = Number(myString);
+typeof myNum;</pre>
+ </li>
+ <li>另一方面,每个数字都有一个名为 <a href="/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Number/toString">toString()</a> 的方法,它将把它转换成等价的字符串。<br>
+ 试试这个:
+ <pre class="brush: js">let myNum = 123;
+let myString = myNum.toString();
+typeof myString;</pre>
+ </li>
+ </ul>
+ 这些结构在某些情况下是非常有用的,例如,如果一个用户将一个数字输入到一个表单文本字段中,这将是一个字符串,但是如果你想要将这个数字添加到某些东西中时,你需要它是一个数字,所以你可以通过 <code>Number()</code> 来处理这个问题。我们在<a href="https://github.com/mdn/learning-area/blob/master/javascript/introduction-to-js-1/first-splash/number-guessing-game.html#L54">数字猜谜游戏中第54行</a>就是这么做的。</li>
+</ol>
+
+<h2 id="结论">结论</h2>
+
+<p>这就是JavaScript中所涉及的字符串的基本内容。在下一篇文章中,我们将在此基础上,研究JavaScript中字符串的一些内置方法,以及如何使用它们来操纵我们的字符串,使之成为我们想要的形式。</p>
+
+<p>{{PreviousMenuNext("Learn/JavaScript/First_steps/Math", "Learn/JavaScript/First_steps/Useful_string_methods", "Learn/JavaScript/First_steps")}}</p>
+
+<p>
+ <audio style="display: none;"></audio>
+</p>
diff --git a/files/zh-cn/learn/javascript/first_steps/test_your_skills_colon__variables/index.html b/files/zh-cn/learn/javascript/first_steps/test_your_skills_colon__variables/index.html
new file mode 100644
index 0000000000..af9b56b9ae
--- /dev/null
+++ b/files/zh-cn/learn/javascript/first_steps/test_your_skills_colon__variables/index.html
@@ -0,0 +1,78 @@
+---
+title: 'Test your skills: variables'
+slug: 'Learn/JavaScript/First_steps/Test_your_skills:_variables'
+translation_of: 'Learn/JavaScript/First_steps/Test_your_skills:_variables'
+---
+<div>{{learnsidebar}}</div>
+
+<p>这个能力测试用于评估你自己是否理解了 <a href="/zh-CN/docs/Learn/JavaScript/First_steps/Variables">如何存储你需要的信息 — 变量</a> 这篇文章.</p>
+
+<div class="blockIndicator note">
+<p><strong>Note</strong>: You can try out solutions in the interactive editors below, however it may be helpful to download the code and use an online tool such as <a href="https://codepen.io/">CodePen</a>, <a href="https://jsfiddle.net/">jsFiddle</a>, or <a href="https://glitch.com/">Glitch</a> to work on the tasks.<br>
+ <br>
+ 如果你遇到问题需要我们帮助 — 请前往在这一页的底部的 {{anch("Assessment or further help")}} 部分.</p>
+</div>
+
+<div class="blockIndicator note">
+<p><strong>Note</strong>: In the examples below, if there is an error in your code it will be outputted into the results panel on the page, to help you try to figure out the answer (or into the browser's JavaScript console, in the case of the downloadable version).</p>
+</div>
+
+<h2 id="Variables_1">Variables 1</h2>
+
+<p>In this task we want you to:</p>
+
+<ul>
+ <li>Declare a variable called <code>myName</code>.</li>
+ <li>Initialize <code>myName</code> with a suitable value, on a separate line (you can use your actual name, or something else).</li>
+ <li>Declare a variable called <code>myAge</code> and initialize it with a value, on the same line.</li>
+</ul>
+
+<p>Try updating the live code below to recreate the finished example:</p>
+
+<p>{{EmbedGHLiveSample("learning-area/javascript/introduction-to-js-1/tasks/variables/variables1.html", '100%', 400)}}</p>
+
+<div class="blockIndicator note">
+<p><a href="https://github.com/mdn/learning-area/blob/master/javascript/introduction-to-js-1/tasks/variables/variables1-download.html">Download the starting point for this task</a> to work in your own editor or in an online editor.</p>
+</div>
+
+<h2 id="Variables_2">Variables 2</h2>
+
+<p>In this task you need to add a new line to correct the value stored in the existing <code>myName</code> variable to your own name.</p>
+
+<p>Try updating the live code below to recreate the finished example:</p>
+
+<p>{{EmbedGHLiveSample("learning-area/javascript/introduction-to-js-1/tasks/variables/variables2.html", '100%', 400)}}</p>
+
+<div class="blockIndicator note">
+<p><a href="https://github.com/mdn/learning-area/blob/master/javascript/introduction-to-js-1/tasks/variables/variables2-download.html">Download the starting point for this task</a> to work in your own editor or in an online editor.</p>
+</div>
+
+<h2 id="Variables_3">Variables 3</h2>
+
+<p>The final task for now — in this case you are provided with some existing code, which has two errors present in it. The results panel should be outputting the name <code>Chris</code>, and a statement about how old Chris will be in 20 years' time. How can you fix the problem and correct the output?</p>
+
+<p>Try updating the live code below to recreate the finished example:</p>
+
+<p>{{EmbedGHLiveSample("learning-area/javascript/introduction-to-js-1/tasks/variables/variables3.html", '100%', 400)}}</p>
+
+<div class="blockIndicator note">
+<p><a href="https://github.com/mdn/learning-area/blob/master/javascript/introduction-to-js-1/tasks/variables/variables3-download.html">Download the starting point for this task</a> to work in your own editor or in an online editor.</p>
+</div>
+
+<h2 id="Assessment_or_further_help">Assessment or further help</h2>
+
+<p>You can practice these examples in the Interactive Editors above.</p>
+
+<p>If you would like your work assessed, or are stuck and want to ask for help:</p>
+
+<ol>
+ <li>Put your work into an online shareable editor such as <a href="https://codepen.io/">CodePen</a>, <a href="https://jsfiddle.net/">jsFiddle</a>, or <a href="https://glitch.com/">Glitch</a>. You can write the code yourself, or use the starting point files linked to in the above sections.</li>
+ <li>Write a post asking for assessment and/or help at the <a href="https://discourse.mozilla.org/c/mdn/learn">MDN Discourse forum Learning category</a>. Your post should include:
+ <ul>
+ <li>A descriptive title such as "Assessment wanted for Variables 1 skill test".</li>
+ <li>Details of what you have already tried, and what you would like us to do, e.g. if you are stuck and need help, or want an assessment.</li>
+ <li>A link to the example you want assessed or need help with, in an online shareable editor (as mentioned in step 1 above). This is a good practice to get into — it's very hard to help someone with a coding problem if you can't see their code.</li>
+ <li>A link to the actual task or assessment page, so we can find the question you want help with.</li>
+ </ul>
+ </li>
+</ol>
diff --git a/files/zh-cn/learn/javascript/first_steps/useful_string_methods/index.html b/files/zh-cn/learn/javascript/first_steps/useful_string_methods/index.html
new file mode 100644
index 0000000000..0dda086985
--- /dev/null
+++ b/files/zh-cn/learn/javascript/first_steps/useful_string_methods/index.html
@@ -0,0 +1,474 @@
+---
+title: 有用的字符串方法
+slug: Learn/JavaScript/First_steps/Useful_string_methods
+tags:
+ - JavaScript
+ - String
+ - 大写
+ - 字符串
+ - 小写
+ - 替换
+ - 格式
+ - 长度
+translation_of: Learn/JavaScript/First_steps/Useful_string_methods
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/JavaScript/First_steps/Strings", "Learn/JavaScript/First_steps/Arrays", "Learn/JavaScript/First_steps")}}</div>
+
+<p class="summary">现在我们学习基本的字符串语法, 让我们开始思考一下我们可以对内置方法的字符串做什么有用的操作,例如查找文本字符串的长度,加入和分割字符串, 将字符串中的一个字符替换为另一个字符。</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">前提:</th>
+ <td>基本的电脑知识, 对HTML和CSS有一定的了解,最好是懂一点javascript知识。</td>
+ </tr>
+ <tr>
+ <th scope="row">目的:</th>
+ <td>明白字符串这个对象,学会使用字符串的基本方法去处理字符串</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="把字符串当作对象">把字符串当作对象</h2>
+
+<p id="Useful_string_methods">我们曾经说过,现在我们重申一遍—在javascript中,一切东西都可以被当作对象。例如我们创建一个字符串。</p>
+
+<pre class="brush: js">let string = 'This is my string';</pre>
+
+<p>一旦你的变量成为字符串对象实例, 你就可以有大量的原型和方法编辑它. 如果你进入{{jsxref("String")}}对象页并观察页面旁边的列表你就会明白这一点。</p>
+
+<p><strong>可能现在你的大脑开始迷糊了,不要担心!</strong> 在你的学习进程中你真的不需要过早地理解大部分这方面知识,但是接下来我们这儿要看的是你要经常使用的一些知识。</p>
+
+<p>现在我们在控制台中加些示例 ,我们已经提供了以下示例(你可在单独打开控制台标签或窗口,或者选择使用<a href="/zh-CN/docs/Learn/Discover_browser_developer_tools">浏览器开发者控制台</a>)</p>
+
+<div class="hidden">
+<h6 id="Hidden_code">Hidden code</h6>
+
+<pre class="brush: html">&lt;!DOCTYPE html&gt;
+&lt;html&gt;
+ &lt;head&gt;
+ &lt;meta charset="utf-8"&gt;
+ &lt;title&gt;JavaScript console&lt;/title&gt;
+ &lt;style&gt;
+ * {
+ box-sizing: border-box;
+ }
+
+ html {
+ background-color: #0C323D;
+ color: #809089;
+ font-family: monospace;
+ }
+
+ body {
+ max-width: 700px;
+ }
+
+ p {
+ margin: 0;
+ width: 1%;
+ padding: 0 1%;
+ font-size: 16px;
+ line-height: 1.5;
+ float: left;
+ }
+
+ .input p {
+ margin-right: 1%;
+ }
+
+ .output p {
+ width: 100%;
+ }
+
+ .input input {
+ width: 96%;
+ float: left;
+ border: none;
+ font-size: 16px;
+ line-height: 1.5;
+ font-family: monospace;
+ padding: 0;
+ background: #0C323D;
+ color: #809089;
+ }
+
+ div {
+ clear: both;
+ }
+
+ &lt;/style&gt;
+ &lt;/head&gt;
+ &lt;body&gt;
+
+
+ &lt;/body&gt;
+
+ &lt;script&gt;
+ var geval = eval;
+ function createInput() {
+ var inputDiv = document.createElement('div');
+ var inputPara = document.createElement('p');
+ var inputForm = document.createElement('input');
+
+ inputDiv.setAttribute('class', 'input');
+ inputPara.textContent = '&gt;';
+ inputDiv.appendChild(inputPara);
+ inputDiv.appendChild(inputForm);
+ document.body.appendChild(inputDiv);
+
+ inputForm.addEventListener('change', executeCode);
+ }
+
+ function executeCode(e) {
+ try {
+ var result = geval(e.target.value);
+ } catch(e) {
+ var result = 'error — ' + e.message;
+ }
+
+ var outputDiv = document.createElement('div');
+ var outputPara = document.createElement('p');
+
+ outputDiv.setAttribute('class','output');
+ outputPara.textContent = 'Result: ' + result;
+ outputDiv.appendChild(outputPara);
+ document.body.appendChild(outputDiv);
+
+ e.target.disabled = true;
+ e.target.parentNode.style.opacity = '0.5';
+
+ createInput()
+ }
+
+ createInput();
+
+ &lt;/script&gt;
+&lt;/html&gt;</pre>
+</div>
+
+<p>{{ EmbedLiveSample('Hidden_code', '100%', 300) }}</p>
+
+<h3 id="获得字符串的长度">获得字符串的长度</h3>
+
+<p>这很简单 — 你可以很轻松的使用 {{jsxref("String.prototype.length", "length")}} 属性. 尝试输入以下的两行代码:</p>
+
+<pre class="brush: js">let browserType = 'mozilla';
+browserType.length;</pre>
+
+<p>这个结果应该返回一个数字:7,因为"mozilla"的长度为7个字符. 说字符串的长度有用是有很多原因的, 例如,你可能想算出一连串名字的长度,并用名字长度来作为名字排序的依据,亦或让一个用户知道他输入的用户名太长,已经超出了输入的字符串长度限制。</p>
+
+<h3 id="检索特定字符串字符">检索特定字符串字符</h3>
+
+<p>在相关的注释中,您可以使用方括号表示法返回字符串中的任何字符 - 这意味着您可以在变量名的末尾包含方括号([ ])。 在方括号内,您可以包含要返回的字符的编号,例如,您要检索第一个字母,可以这样做:</p>
+
+<pre class="brush: js">browserType[0];</pre>
+
+<p>电脑从0开始,不是1! 要检索任何字符串的最后一个字符,我们可以使用下面这行,将这种技术与我们上面看到的length属性相结合起来:</p>
+
+<pre class="brush: js">browserType[browserType.length-1];</pre>
+
+<p>“mozilla”的长度为7,但由于计数从0开始,所以字符位置为6,因此需要长度为<strong>length-1</strong>。 例如,您可以使用它来查找一系列字符串的第一个字母,并按字母顺序排列。</p>
+
+<h3 id="在字符串中查找子字符串并提取它">在字符串中查找子字符串并提取它</h3>
+
+<ol>
+ <li>有时候你会想要找出一个较小的字符串是否存在于一个较大的字符串中(我们通常会说一个字符串中存在一个子字符串)。 这可以使用{{jsxref("String.prototype.indexOf()","indexOf()")}}方法来完成,该方法需要一个{{glossary("parameter")}} — 你想要搜索的子字符串。 尝试以下:
+ <pre class="brush: js">browserType.indexOf('zilla');</pre>
+ 结果是2,因为子字符串“zilla”从“mozilla”内的位置2(0,1,2 —— 所以从第3个字符)开始。 这样的代码可以用来过滤字符串。 例如,假设我们有一个Web地址列表,但我们只想打印出包含“mozilla”的那些地址。</li>
+</ol>
+
+<ol start="2">
+ <li>这可以用另一种可能更有效的方式来实现。 尝试以下:
+ <pre class="brush: js">browserType.indexOf('vanilla');</pre>
+ 这应该会得到 <code>-1</code> 的结果 —— 当在主字符串中找不到子字符串(在本例中为“vanilla”)时将返回 <code>-1</code>。<br>
+ <br>
+ 您可以使用它来查找不包含子串“mozilla”的所有字符串实例,或者如果使用否定运算符,请执行以下操作。 你可以这样做:
+ <pre class="brush: js">if(browserType.indexOf('mozilla') !== -1) {
+ // do stuff with the string
+}</pre>
+ </li>
+ <li>当你知道字符串中的子字符串开始的位置,以及想要结束的字符时,{{jsxref("String.prototype.slice()", "slice()")}}可以用来提取 它。 尝试以下:
+ <pre class="brush: js">browserType.slice(0,3);</pre>
+ 这时返回"moz"——第一个参数是开始提取的字符位置,第二个参数是提取的最后一个字符的后一个位置。所以提取从第一个位置开始,直到但不包括最后一个位置。(此例中)你也可以说第二个参数等于被返回的字符串的长度。<br>
+  </li>
+ <li>此外,如果您知道要在某个字符之后提取字符串中的所有剩余字符,则不必包含第二个参数,而只需要包含要从中提取的字符位置 字符串中的其余字符。 尝试以下:
+ <pre class="brush: js">browserType.slice(2);</pre>
+ 这返回“zilla” —— 这是因为2的字符位置是字母z,并且因为没有包含第二个参数,所以返回的子字符串是字符串中的所有剩余字符。</li>
+</ol>
+
+<div class="note">
+<p><strong>Note: </strong><code>slice()</code>的第二个参数是可选的 :如果没有传入这个参数,分片结束位置会在原始字符串的末尾。这个方法也有其他的选项;学习{{jsxref("String.prototype.slice()", "slice()")}}这页,来看看你还能发现什么其他作用。</p>
+</div>
+
+<h3 id="转换大小写">转换大小写</h3>
+
+<p>字符串方法{{jsxref("String.prototype.toLowerCase()","toLowerCase()")}}和{{jsxref("String.prototype.toUpperCase()","toUpperCase()")}}字符串并将所有字符分别转换为小写或大写。 例如,如果要在将数据存储在数据库中之前对所有用户输入的数据进行规范化,这可能非常有用。</p>
+
+<p>让我们尝试输入以下几行来看看会发生什么:</p>
+
+<pre class="brush: js">let radData = 'My NaMe Is MuD';
+radData.toLowerCase();
+radData.toUpperCase();</pre>
+
+<h3 id="替换字符串的某部分">替换字符串的某部分</h3>
+
+<p>您可以使用{{jsxref("String.prototype.replace()","replace()")}}方法将字符串中的一个子字符串替换为另一个子字符串。在基础的层面上, 这个工作非常简单。你当然可以用它做一些更高级的事情,但目前我们不会涉及到。</p>
+
+<p>它需要两个参数 - 要被替换下的字符串和要被替换上的字符串。 尝试这个例子:</p>
+
+<pre class="brush: js">browserType.replace('moz','van');</pre>
+
+<p>注意,在实际程序中,想要真正更新 <code>browserType</code> 变量的值,您需要设置变量的值等于刚才的操作结果;它不会自动更新子串的值。所以事实上你需要这样写:<code>browserType = browserType.replace('moz','van');</code>。</p>
+
+<h2 id="主动学习的示例">主动学习的示例</h2>
+
+<p>在本节中,我们会让您尽力编写一些字符串操作代码。 在下面的每个练习中,我们有一个字符串数组,一个循环,用于处理数组中的每个值,并将其显示在项目符号列表中。 您现在不需要了解数组或循环 - 这些将在以后的文章中解释。 所有你需要做的每一种情况都是写出将以我们想要的格式输出字符串的代码。</p>
+
+<p>每个示例都附带一个“重置”按钮,您可以使用该按钮重置代码,如果您犯了错误,并且无法再次工作,并且显示解决方案按钮,您可以按下来看到一个潜在的答案,如果你真的卡住了。</p>
+
+<h3 id="过滤问候留言">过滤问候留言</h3>
+
+<p>在第一个练习中,我们将简单介绍一下 - 我们有一系列的问候卡片消息,但我们希望对它们进行排序,以列出圣诞消息。 我们希望您在 <code>if(...)</code> 结构中填写条件测试,以测试每个字符串,并将其打印在列表中,如果它是圣诞消息。</p>
+
+<ol>
+ <li>首先考虑一下如何测试每种情况下的消息是否为圣诞消息。 所有这些消息中都有什么字符串,您可以使用什么方法来测试是否存在?</li>
+ <li>然后,您需要编写<em> 操作数1 操作符 操作数2</em> 形式的条件测试。 左边的东西等于右边的东西吗? 或者在这种情况下,方法调用在左边返回的结果在右边?</li>
+ <li>提示:在这种情况下,测试方法调用是否不等于某个结果可能更有用。</li>
+</ol>
+
+<div class="hidden">
+<h6 id="Playable_code">Playable code</h6>
+
+<pre class="brush: html">&lt;div class="output" style="min-height: 125px;"&gt;
+
+&lt;ul&gt;
+
+&lt;/ul&gt;
+
+&lt;/div&gt;
+
+&lt;textarea id="code" class="playable-code" style="height: 290px;"&gt;
+var list = document.querySelector('.output ul');
+list.innerHTML = '';
+var greetings = ['Happy Birthday!',
+ 'Merry Christmas my love',
+ 'A happy Christmas to all the family',
+ 'You\'re all I want for Christmas',
+ 'Get well soon'];
+
+for (var i = 0; i &lt; greetings.length; i++) {
+ var input = greetings[i];
+ // Your conditional test needs to go inside the parentheses
+ // in the line below, replacing what's currently there
+ if (greetings[i]) {
+ var result = input;
+ var listItem = document.createElement('li');
+ listItem.textContent = result;
+ list.appendChild(listItem);
+ }
+}
+&lt;/textarea&gt;
+
+&lt;div class="playable-buttons"&gt;
+ &lt;input id="reset" type="button" value="Reset"&gt;
+ &lt;input id="solution" type="button" value="Show solution"&gt;
+&lt;/div&gt;
+</pre>
+
+<pre class="brush: js">var textarea = document.getElementById('code');
+var reset = document.getElementById('reset');
+var solution = document.getElementById('solution');
+var code = textarea.value;
+
+function updateCode() {
+ eval(textarea.value);
+}
+
+reset.addEventListener('click', function() {
+ textarea.value = code;
+ updateCode();
+});
+
+solution.addEventListener('click', function() {
+ textarea.value = jsSolution;
+ updateCode();
+});
+
+var jsSolution = 'var list = document.querySelector(\'.output ul\');\nlist.innerHTML = \'\';\nvar greetings = [\'Happy Birthday!\',\n \'Merry Christmas my love\',\n \'A happy Christmas to all the family\',\n \'You\\\'re all I want for Christmas\',\n \'Get well soon\'];\n\nfor(var i = 0; i &lt; greetings.length; i++) {\n var input = greetings[i];\n if(greetings[i].indexOf(\'Christmas\') !== -1) {\n var result = input;\n var listItem = document.createElement(\'li\');\n listItem.textContent = result;\n list.appendChild(listItem);\n }\n}';
+
+textarea.addEventListener('input', updateCode);
+window.addEventListener('load', updateCode);
+</pre>
+</div>
+
+<p>{{ EmbedLiveSample('Playable_code', '100%', 490) }}</p>
+
+<h3 id="大写修正">大写修正</h3>
+
+<p>在这个练习中,我们有英国城市的名字,但是这个大写字母都搞砸了。 我们希望你改变它们,使它们都是小写字母,除了首字母。 一个很好的方法是:</p>
+
+<ol>
+ <li>将输入变量中包含的整个字符串转换为小写,并将其存储在新变量中。</li>
+ <li>在此新变量中获取字符串的第一个字母并将其存储在另一个变量中。</li>
+ <li>将此最新变量用作子字符串,将小写字符串的第一个字母从小写更改为大写。 将此替换过程的结果存储在另一个新变量中。</li>
+ <li>让 <code>result</code> 变量的值与最终结果相等,而不是使用 <code>input</code> 变量。</li>
+</ol>
+
+<div class="note">
+<p><strong>Note</strong>: 一个提示 - 字符串方法的参数不必是字符串文字; 它们也可以是变量,甚至是在其上调用方法的变量。</p>
+</div>
+
+<div class="hidden">
+<h6 id="Playable_code_2">Playable code 2</h6>
+
+<pre class="brush: html">&lt;div class="output" style="min-height: 125px;"&gt;
+
+&lt;ul&gt;
+
+&lt;/ul&gt;
+
+&lt;/div&gt;
+
+&lt;textarea id="code" class="playable-code" style="height: 250px;"&gt;
+var list = document.querySelector('.output ul');
+list.innerHTML = '';
+var cities = ['lonDon', 'ManCHESTer', 'BiRmiNGHAM', 'liVERpoOL'];
+for(var i = 0; i &lt; cities.length; i++) {
+ var input = cities[i];
+ // write your code just below here
+
+ var result = input;
+ var listItem = document.createElement('li');
+ listItem.textContent = result;
+ list.appendChild(listItem);
+}
+&lt;/textarea&gt;
+
+&lt;div class="playable-buttons"&gt;
+ &lt;input id="reset" type="button" value="Reset"&gt;
+ &lt;input id="solution" type="button" value="Show solution"&gt;
+&lt;/div&gt;
+</pre>
+
+<pre class="brush: js">var textarea = document.getElementById('code');
+var reset = document.getElementById('reset');
+var solution = document.getElementById('solution');
+var code = textarea.value;
+
+function updateCode() {
+ eval(textarea.value);
+}
+
+reset.addEventListener('click', function() {
+ textarea.value = code;
+ updateCode();
+});
+
+solution.addEventListener('click', function() {
+ textarea.value = jsSolution;
+ updateCode();
+});
+
+var jsSolution = 'var list = document.querySelector(\'.output ul\');\nlist.innerHTML = \'\';\nvar cities = [\'lonDon\', \'ManCHESTer\', \'BiRmiNGHAM\', \'liVERpoOL\'];\n\nfor(var i = 0; i &lt; cities.length; i++) {\n var input = cities[i];\n var lower = input.toLowerCase();\n var firstLetter = lower.slice(0,1);\n var capitalized = lower.replace(firstLetter,firstLetter.toUpperCase());\n var result = capitalized;\n var listItem = document.createElement(\'li\');\n listItem.textContent = result;\n list.appendChild(listItem);\n\n}';
+
+textarea.addEventListener('input', updateCode);
+window.addEventListener('load', updateCode);
+</pre>
+</div>
+
+<p>{{ EmbedLiveSample('Playable_code_2', '100%', 450) }}</p>
+
+<h3 id="从原先部分得到新字符串">从原先部分得到新字符串</h3>
+
+<p>在最后一个练习中,阵列包含一堆字符串,其中包含有关英格兰北部火车站的信息。 字符串是包含三字母站代码的数据项,后面是一些机器可读数据,后跟分号,后跟可读站名。 例如:</p>
+
+<pre>MAN675847583748sjt567654;Manchester Piccadilly</pre>
+
+<p>我们要提取站点代码和名称,并将它们放在一起,具有以下结构的字符串:</p>
+
+<pre>MAN: Manchester Piccadilly</pre>
+
+<p>我们建议这样做:</p>
+
+<ol>
+ <li>提取三个字母的站代码并将其存储在一个新的变量中。</li>
+ <li>查找分号的字符索引号。</li>
+ <li>使用分号字符索引号作为参考点提取人可读的站名,并将其存储在新变量中。</li>
+ <li>连接两个新的变量和一个字符串文字,使最终的字符串。</li>
+ <li>将 <code>result</code> 变量的值更改为等于最终的字符串,而不是 <code>input</code>。</li>
+</ol>
+
+<div class="hidden">
+<h6 id="Playable_code_3">Playable code 3</h6>
+
+<pre class="brush: html">&lt;div class="output" style="min-height: 125px;"&gt;
+
+&lt;ul&gt;
+
+&lt;/ul&gt;
+
+&lt;/div&gt;
+
+&lt;textarea id="code" class="playable-code" style="height: 285px;"&gt;
+var list = document.querySelector('.output ul');
+list.innerHTML = '';
+var stations = ['MAN675847583748sjt567654;Manchester Piccadilly',
+ 'GNF576746573fhdg4737dh4;Greenfield',
+ 'LIV5hg65hd737456236dch46dg4;Liverpool Lime Street',
+ 'SYB4f65hf75f736463;Stalybridge',
+ 'HUD5767ghtyfyr4536dh45dg45dg3;Huddersfield'];
+
+for (var i = 0; i &lt; stations.length; i++) {
+ var input = stations[i];
+ // write your code just below here
+
+ var result = input;
+ var listItem = document.createElement('li');
+ listItem.textContent = result;
+ list.appendChild(listItem);
+}
+&lt;/textarea&gt;
+
+&lt;div class="playable-buttons"&gt;
+ &lt;input id="reset" type="button" value="Reset"&gt;
+ &lt;input id="solution" type="button" value="Show solution"&gt;
+&lt;/div&gt;
+</pre>
+
+<pre class="brush: js">var textarea = document.getElementById('code');
+var reset = document.getElementById('reset');
+var solution = document.getElementById('solution');
+var code = textarea.value;
+
+function updateCode() {
+ eval(textarea.value);
+}
+
+reset.addEventListener('click', function() {
+ textarea.value = code;
+ updateCode();
+});
+
+solution.addEventListener('click', function() {
+ textarea.value = jsSolution;
+ updateCode();
+});
+
+var jsSolution = 'var list = document.querySelector(\'.output ul\');\nlist.innerHTML = \'\';\nvar stations = [\'MAN675847583748sjt567654;Manchester Piccadilly\',\n \'GNF576746573fhdg4737dh4;Greenfield\',\n \'LIV5hg65hd737456236dch46dg4;Liverpool Lime Street\',\n \'SYB4f65hf75f736463;Stalybridge\',\n \'HUD5767ghtyfyr4536dh45dg45dg3;Huddersfield\'];\n\nfor(var i = 0; i &lt; stations.length; i++) {\n var input = stations[i];\n var code = input.slice(0,3);\n var semiC = input.indexOf(\';\');\n var name = input.slice(semiC + 1);\n var final = code + \': \' + name;\n var result = final;\n var listItem = document.createElement(\'li\');\n listItem.textContent = result;\n list.appendChild(listItem);\n}';
+
+
+textarea.addEventListener('input', updateCode);
+window.addEventListener('load', updateCode);
+</pre>
+</div>
+
+<p>{{ EmbedLiveSample('Playable_code_3', '100%', 485) }}</p>
+
+<h2 id="结论">结论</h2>
+
+<p>你不能逃避的事实是在编程中处理单词和句子是非常重要的——特别是在JavaScript中,因为网站都是关于与人沟通的。 本文已经给出了您现在需要了解的关于操作字符串的基础知识。这将在未来进入更复杂的主题时为您服务。 接下来,我们将在短期内研究我们需要关注的最后一个主要的数据类型——数组。</p>
+
+<p>{{PreviousMenuNext("Learn/JavaScript/First_steps/Strings", "Learn/JavaScript/First_steps/Arrays", "Learn/JavaScript/First_steps")}}</p>
diff --git a/files/zh-cn/learn/javascript/first_steps/variables/index.html b/files/zh-cn/learn/javascript/first_steps/variables/index.html
new file mode 100644
index 0000000000..3ba4e02f60
--- /dev/null
+++ b/files/zh-cn/learn/javascript/first_steps/variables/index.html
@@ -0,0 +1,431 @@
+---
+title: 如何存储你需要的信息 — 变量
+slug: Learn/JavaScript/First_steps/Variables
+tags:
+ - 初始化
+ - 变量
+ - 声明
+ - 字符串
+ - 数组
+translation_of: Learn/JavaScript/First_steps/Variables
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/JavaScript/First_steps/What_went_wrong", "Learn/JavaScript/First_steps/Math", "Learn/JavaScript/First_steps")}}</div>
+
+<p class="summary">在读完之前的一些文章之后,你现在应该大概知道了JavaScript是什么,你能用它干什么,它是怎么跟其他web技术(HTML,CSS)协同工作的,以及它有哪些主要特性。在本章节,我们将开始学习JavaScript的基础,了解如何使用 -- 变量。</p>
+
+<table class="learn-box">
+ <tbody>
+ <tr>
+ <th scope="row">预备知识:</th>
+ <td>电脑基础知识,了解基本的 HTML 和 CSS,了解JavaScript是什么。</td>
+ </tr>
+ <tr>
+ <th scope="row">目标:</th>
+ <td>熟悉JavaScript的变量使用</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="需要的工具">需要的工具</h2>
+
+<p>在本章中,你将要输入一些代码来测试你对相关内容的理解。如果你是用的桌面浏览器,输入这些代码最好的地方是浏览器的JavaScript终端,(参考 <a href="/en-US/docs/Learn/Common_questions/What_are_browser_developer_tools">什么是浏览器开发工具</a> 查看如何使用这些工具)。</p>
+
+<p>当然,我们也提供了一个简单的JavaScript终端,嵌入在下文的页面中,以便你更容易的输入和测试这些代码。</p>
+
+<h2 id="变量是什么">变量是什么?</h2>
+
+<p>一个变量,就是一个用于存放数值的容器。这个数值可能是一个用于累加计算的数字,或者是一个句子中的字符串。变量的独特之处在于它存放的数值是可以改变的。让我们看一个简单的例子:</p>
+
+<pre class="brush: html notranslate">&lt;button&gt;Press me&lt;/button&gt;</pre>
+
+<pre class="brush: js notranslate">const button = document.querySelector('button');
+
+button.onclick = function() {
+ let name = prompt('What is your name?');
+ alert('Hello ' + name + ', nice to see you!');
+}</pre>
+
+<p>{{ EmbedLiveSample('变量是什么', '100%', 50, "", "", "hide-codepen-jsfiddle") }}</p>
+
+<p>在上面的例子中,点击按钮之后,第一行代码会在屏幕上弹出一个对话框,让你输入名字,然后存储输入的名字到一个变量。第二行代码将会显示包含你名字的欢迎信息,你的名字就是从之前的变量里面读取的。</p>
+
+<p>为了理解变量的作用,我们可以思考一下,如果不使用变量,我们实现上述功能的代码将是这样的:</p>
+
+<pre class="example-bad notranslate">let name = prompt('What is your name?');
+
+if (name === 'Adam') {
+ alert('Hello Adam, nice to see you!');
+} else if (name === 'Alan') {
+ alert('Hello Alan, nice to see you!');
+} else if (name === 'Bella') {
+ alert('Hello Bella, nice to see you!');
+} else if (name === 'Bianca') {
+ alert('Hello Bianca, nice to see you!');
+} else if (name === 'Chris') {
+ alert('Hello Chris, nice to see you!');
+}
+
+// ... and so on ...</pre>
+
+<p>你可能暂时还没有完全理解这些代码和语法,但是你应该能够理解到 -- 如果我们没有变量,我们就不得不写大量的代码去枚举和检查输入的名字,然后去显示它们。这样做显然是低效率和不可行的 -- 你没有办法列举出所有可能的输入。</p>
+
+<p>变量的另一个特性就是它们能够存储任何的东西 -- 不只是字符串和数字。变量可以存储更复杂的数据,甚至是函数。你将在后续的内容中体验到这些用法。</p>
+
+<p><u><strong>我们说,变量是用来存储数值的,那么有一个重要的概念需要区分。变量不是数值本身,它们仅仅是一个用于存储数值的容器。你可以把变量想象成一个个用来装东西的纸箱子。</strong></u></p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/13506/boxes.png" style="display: block; height: 436px; margin: 0px auto; width: 1052px;"></p>
+
+<h2 id="声明变量">声明变量</h2>
+
+<p>要想使用变量,你需要做的第一步就是创建它 -- 更准确的说,是声明一个变量。声明一个变量的语法是在 <code>var</code> 或 <code>let</code> 关键字之后加上这个变量的名字:</p>
+
+<pre class="brush: js notranslate">let myName;
+let myAge;</pre>
+
+<p>在这里我们声明了两个变量 <code>myName</code> 和 <code>myAge</code>. 那么现在尝试输入这些代码到你的浏览器终端或者下面提供的JavaScript终端 (你也可以在另一个独立的标签页或窗口 <a href="https://mdn.github.io/learning-area/javascript/introduction-to-js-1/variables/index.html">open this consol</a> ). 此外,你也可以多声明几个变量.</p>
+
+<div class="hidden">
+<h6 id="Hidden_code">Hidden code</h6>
+
+<pre class="brush: html notranslate">&lt;!DOCTYPE html&gt;
+&lt;html&gt;
+ &lt;head&gt;
+ &lt;meta charset="utf-8"&gt;
+ &lt;title&gt;JavaScript console&lt;/title&gt;
+ &lt;style&gt;
+ * {
+ box-sizing: border-box;
+ }
+
+ html {
+ background-color: #0C323D;
+ color: #809089;
+ font-family: monospace;
+ }
+
+ body {
+ max-width: 700px;
+ }
+
+ p {
+ margin: 0;
+ width: 1%;
+ padding: 0 1%;
+ font-size: 16px;
+ line-height: 1.5;
+ float: left;
+ }
+
+ .input p {
+ margin-right: 1%;
+ }
+
+ .output p {
+ width: 100%;
+ }
+
+ .input input {
+ width: 96%;
+ float: left;
+ border: none;
+ font-size: 16px;
+ line-height: 1.5;
+ font-family: monospace;
+ padding: 0;
+ background: #0C323D;
+ color: #809089;
+ }
+
+ div {
+ clear: both;
+ }
+
+ &lt;/style&gt;
+ &lt;/head&gt;
+ &lt;body&gt;
+
+
+ &lt;/body&gt;
+
+ &lt;script&gt;
+ let geval = eval;
+ function createInput() {
+ let inputDiv = document.createElement('div');
+ let inputPara = document.createElement('p');
+ let inputForm = document.createElement('input');
+
+ inputDiv.setAttribute('class','input');
+ inputPara.textContent = '&gt;';
+ inputDiv.appendChild(inputPara);
+ inputDiv.appendChild(inputForm);
+ document.body.appendChild(inputDiv);
+
+ inputForm.addEventListener('change', executeCode);
+ }
+
+ function executeCode(e) {
+ try {
+ let result = geval(e.target.value);
+ } catch(e) {
+ let result = 'error — ' + e.message;
+ }
+
+ let outputDiv = document.createElement('div');
+ let outputPara = document.createElement('p');
+
+ outputDiv.setAttribute('class','output');
+ outputPara.textContent = 'Result: ' + result;
+ outputDiv.appendChild(outputPara);
+ document.body.appendChild(outputDiv);
+
+ e.target.disabled = true;
+ e.target.parentNode.style.opacity = '0.5';
+
+ createInput()
+ }
+
+ createInput();
+
+ &lt;/script&gt;
+&lt;/html&gt;</pre>
+</div>
+
+<p>{{ EmbedLiveSample('Hidden_code', '100%', 300, "", "", "hide-codepen-jsfiddle") }}</p>
+
+<div class="note">
+<p><strong>提示</strong>: 在JavaScript中,所有代码指令都会以分号结尾 (<code>;</code>) — 如果忘记加分号,你的单行代码可能执行正常,但是在多行代码在一起的时候就可能出错。所以,最好是养成主动以分号作为代码结尾的习惯。</p>
+</div>
+
+<p>你可以通过使用变量的方式来验证这个变量的数值是否在执行环境中已经存在。例如,</p>
+
+<pre class="brush: js notranslate">myName;
+myAge;</pre>
+
+<p>以上这两个变量并没有数值,他们是空的容器。当你输入变量名并回车后,你会得到一个<code>undefined</code>的返回值。如果他们并不存在的话,那你就会得到一个报错信息。不信的话,可以尝试输入:</p>
+
+<pre class="brush: js notranslate">scoobyDoo;</pre>
+
+<div class="note">
+<p><strong>提示</strong>: 千万不要把两个概念弄混淆了,“一个变量存在,但是没有数值”和“一个变量并不存在” — 他们完全是两回事 — 在前面你看到的盒子的类比中,不存在意味着没有可以存放变量的“盒子”。没有定义的值意味着有一个“盒子”,但是它里面没有任何值。</p>
+</div>
+
+<h2 id="初始化变量">初始化变量</h2>
+
+<p>一旦你定义了一个变量,你就能够初始化它. 方法如下,在变量名之后跟上一个“=”,然后是数值:</p>
+
+<pre class="brush: js notranslate">myName = 'Chris';
+myAge = 37;</pre>
+
+<p>现在回到控制台并且尝试输入这几行。每输入一条你都应该确认一下控制台输出的变量是不是你刚赋的值。 同样,你可以通过在控制台中输入变量的名称来返回该变量的值 — 再次尝试下这些:</p>
+
+<pre class="brush: js notranslate">myName;
+myAge;</pre>
+
+<p>你可以像这样在声明变量的时候给变量初始化:</p>
+
+<pre class="brush: js notranslate">let myName = 'Chris';</pre>
+
+<p>这可能是大多数时间你都会使用的方式, 因为它要比在单独的两行上做两次操作要快。</p>
+
+<div class="note">
+<p><strong>Note</strong>: 如果你写一个声明和初始化变量的多行JavaScript代码的程序,你可以在初始化变量之后再实际声明它,并且它仍然可以工作。这是因为变量的声明通常在其余的代码执行之前完成。这叫做<strong>顶置</strong>—阅读<a href="/en-US/docs/Web/JavaScript/Reference/Statements/var#var_hoisting">var hoisting</a>来了解更多细节。</p>
+</div>
+
+<h2 id="var_与_let_的区别">var 与 let 的区别</h2>
+
+<p>此时,您可能会想:“为什么我们需要两个关键字来定义变量?”,“为什么有 <code>var</code> 和 <code>let</code> 呢?"。</p>
+
+<p>原因是有些历史性的。 回到最初创建 JavaScript 时,是只有 <code>var</code> 的。 在大多数情况下,这种方法可以接受, 但有时在工作方式上会有一些问题——它的设计会令人困惑或令人讨厌 。 因此,<code>let</code> 是在现代版本中的 JavaScript 创建的一个新的关键字,用于创建与 <code>var</code> 工作方式有些不同的变量,解决了过程中的问题。</p>
+
+<p>下面解释几个简单的差异。 我们现在不会讨论所有的差异,但是当您了解有关 JavaScript 的更多信息时,您将开始发现它们(如果您现在真的想要阅读它们,请随时查看我们的<a href="/en-US/docs/Web/JavaScript/Reference/Statements/let">参考页面</a>)。</p>
+
+<p>首先,如果你编写一个声明并初始化变量的多行 JavaScript 程序,你可以在初始化一个变量之后用 <code>var</code> 声明它,它仍然可以工作。 例如:</p>
+
+<pre class="brush: js line-numbers language-js notranslate"><code class="language-js">myName <span class="operator token">=</span> <span class="string token">'Chris'</span><span class="punctuation token">;</span>
+
+<span class="keyword token">function</span> <span class="function token">logName</span><span class="punctuation token">(</span><span class="punctuation token">)</span> <span class="punctuation token">{</span>
+ console<span class="punctuation token">.</span><span class="function token">log</span><span class="punctuation token">(</span>myName<span class="punctuation token">)</span><span class="punctuation token">;</span>
+<span class="punctuation token">}</span>
+
+<span class="function token">logName</span><span class="punctuation token">(</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
+
+var myName<span class="punctuation token">;</span></code></pre>
+
+<div class="blockIndicator note">
+<p>Note: 只有在 web 文档中运行多行 JavaScript 时才会有这种效果,当在 JavaScript 控制台中键入单独的行,这将不起作用。</p>
+</div>
+
+<p>这是由于变量的<strong>提升</strong>,更多细节请阅读<a href="/zh-CN/docs/Web/JavaScript/Reference/Statements/var#变量提升">变量提升</a>。</p>
+
+<p>但提升操作不再适用于 <code>let</code> 。如果将上面例子中的 <code>var</code> 替换成 <code>let</code> 将不起作用并引起一个错误。 这是一件好事——因为初始化后再声明一个变量会使代码变得混乱和难以理解。</p>
+
+<p>其次,当你使用 <code>var</code> 时,可以根据需要多次声明相同名称的变量,但是 <code>let</code> 不能。 以下将有效:</p>
+
+<pre class="brush: js line-numbers language-js notranslate"><code class="language-js"><span class="keyword token">var</span> myName <span class="operator token">=</span> <span class="string token">'Chris'</span><span class="punctuation token">;</span>
+<span class="keyword token">var</span> myName <span class="operator token">=</span> <span class="string token">'Bob'</span><span class="punctuation token">;</span></code></pre>
+
+<p>但是以下内容会在第二行引发错误:</p>
+
+<pre class="brush: js example-bad notranslate"><code class="language-js"><span class="keyword token">let</span> myName <span class="operator token">=</span> <span class="string token">'Chris'</span><span class="punctuation token">;</span>
+<span class="keyword token">let</span> myName <span class="operator token">=</span> <span class="string token">'Bob'</span><span class="punctuation token">;</span></code></pre>
+
+<p>你必须这样做:</p>
+
+<pre class="brush: js line-numbers language-js notranslate"><code class="language-js"><span class="keyword token">let</span> myName <span class="operator token">=</span> <span class="string token">'Chris'</span><span class="punctuation token">;</span>
+myName <span class="operator token">=</span> <span class="string token">'Bob'</span><span class="punctuation token">;</span></code></pre>
+
+<p>同样,这是一个明智的语言决定。没有理由重新声明变量——这只会让事情变得更加混乱。</p>
+
+<p>出于这些以及其他原因,我们建议您在代码中尽可能多地使用 <code>let</code>,而不是 <code>var</code>。因为没有理由使用 <code>var</code>,除非您需要用代码支持旧版本的 Internet Explorer (它直到第 11 版才支持 <code>let</code> ,现代的 Windows Edge 浏览器支持的很好)。</p>
+
+<div class="blockIndicator note">
+<p>  <strong>Note: </strong>我们目前正在更新课程以使用let而不是var。 忍耐一下!</p>
+</div>
+
+<h2 id="更新变量">更新变量</h2>
+
+<p>一旦变量赋值,您可以通过简单地给它一个不同的值来更新它。试试在你的控制台中输入下面几行:</p>
+
+<pre class="brush: js notranslate">myName = 'Bob';
+myAge = 40;</pre>
+
+<h3 id="关于变量命名的规则">关于变量命名的规则</h3>
+
+<p>你可以给你的变量赋任何你喜欢的名字,但有一些限制。 一般你应当坚持使用拉丁字符(0-9,a-z,A-Z)和下划线字符。</p>
+
+<ul>
+ <li>你不应当使用规则之外的其他字符,因为它们可能引发错误,或对国际用户来说难以理解。</li>
+ <li>变量名不要以下划线开头—— 以下划线开头的被某些JavaScript设计为特殊的含义,因此可能让人迷惑。</li>
+ <li>变量名不要以数字开头。这种行为是不被允许的,并且将引发一个错误。</li>
+ <li>一个可靠的命名约定叫做 <a href="https://en.wikipedia.org/wiki/CamelCase#Variations_and_synonyms">"小写驼峰命名法"</a>,用来将多个单词组在一起,小写整个命名的第一个字母然后大写剩下单词的首字符。我们已经在文章中使用了这种命名方法。</li>
+ <li>让变量名直观,它们描述了所包含的数据。不要只使用单一的字母/数字,或者长句。</li>
+ <li>变量名大小写敏感——因此<code>myage</code>与<code>myAge</code>是2个不同的变量。</li>
+ <li>最后也是最重要的一点—— 你应当避免使用JavaScript的保留字给变量命名。保留字,即是组成JavaScript的实际语法的单词!因此诸如 <code>var</code>, <code>function</code>, <code>let和</code> <code>for</code>等,都不能被作为变量名使用。浏览器将把它们识别为不同的代码项,因此你将得到错误。</li>
+</ul>
+
+<div class="note">
+<p><strong>Note</strong>: 你能从<a href="/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#Keywords">词汇语法—关键字</a>找到一个相当完整的保留关键字列表来避免使用关键字当作变量。</p>
+</div>
+
+<p>好的命名示例:</p>
+
+<pre class="example-good notranslate">age
+myAge
+init
+initialColor
+finalOutputValue
+audio1
+audio2</pre>
+
+<p>错误与差的命名示例:</p>
+
+<pre class="example-bad notranslate">1
+a
+_12
+myage
+MYAGE
+var
+Document
+skjfndskjfnbdskjfb
+thisisareallylongstupidvariablenameman</pre>
+
+<p>现在尝试创建更多的变量,请将上面的指导铭记于心。</p>
+
+<h2 id="变量类型">变量类型</h2>
+
+<p>可以为变量设置不同的数据类型。本节我们将对其进行简短的介绍,在以后的文章中,你会更详细地了解它们。</p>
+
+<p>到目前为止我们已经认识了前2个,但是还有其他的。</p>
+
+<h3 id="Number">Number</h3>
+
+<p>你可以在变量中存储数字,不论这些数字是像30(也叫整数)这样,或者像2.456这样的小数(也叫做浮点数)。与其他编程语言不同,在 JavaScript 中你不需要声明一个变量的类型。当你给一个变量数字赋值时,不需要用引号括起来。 </p>
+
+<pre class="brush: js notranslate">let myAge = 17;</pre>
+
+<h3 id="String">String</h3>
+
+<p>字符串是文本的一部分。当你给一个变量赋值为字符串时,你需要用单引号或者双引号把值给包起来,否则JavaScript将会把这个字符串值理解成别的变量名。</p>
+
+<pre class="brush: js notranslate">let dolphinGoodbye = 'So long and thanks for all the fish';</pre>
+
+<h3 id="Boolean">Boolean</h3>
+
+<p>Boolean 的值有2种:true或false。它们通常被用于在适当的代码之后,测试条件是否成立。举个例子,一个简单的示例如下: </p>
+
+<pre class="brush: js notranslate">let iAmAlive = true;</pre>
+
+<p>然而实际上通常是以下用法:</p>
+
+<pre class="brush: js notranslate">let test = 6 &lt; 3;</pre>
+
+<p>这是使用“小于”操作符(&lt;)来测试6小于3。正如你所料的,将会返回<code>false</code>,因为6并不小于3!在这个课程中,以后你将会学到许多有关操作符的知识。</p>
+
+<h3 id="Array">Array</h3>
+
+<p>数组是一个单个对象,其中包含很多值,方括号括起来,并用逗号分隔。尝试在您的控制台输入以下行:</p>
+
+<pre class="brush: js notranslate">let myNameArray = ['Chris', 'Bob', 'Jim'];
+let myNumberArray = [10,15,40];</pre>
+
+<p>当数组被定义后,您可以使用如下所示的语法来访问各自的值,例如下行:</p>
+
+<pre class="brush: js notranslate">myNameArray[0]; // should return 'Chris'
+myNumberArray[2]; // should return 40</pre>
+
+<p>此处的方括号包含一个索引值,该值指定要返回的值的位置。 您可能已经注意到,计算机从0开始计数,而不是像我们人类那样的1。</p>
+
+<p>在以后的文章,你将更多地了解数组。</p>
+
+<h3 id="Object">Object</h3>
+
+<p>在编程中,对象是现实生活中的模型的一种代码结构。您可以有一个简单的对象,代表一个停车场,并包含有关其宽度和长度的信息,或者您可以有一个代表一个人的对象,并包含有关他们的名字,身高,体重,他们说什么语言,如何说 你好,他们,等等。</p>
+
+<p>尝试在您的控制台输入以下行:</p>
+
+<pre class="brush: js notranslate">let dog = { name : 'Spot', breed : 'Dalmatian' };</pre>
+
+<p>要检索存储在对象中的信息,可以使用以下语法:</p>
+
+<pre class="brush: js notranslate">dog.name</pre>
+
+<p>我们现在不会看对象了 - 您可以在将来的模块中了解更多关于这些对象的信息.</p>
+
+<h2 id="动态类型">动态类型</h2>
+
+<p>JavaScript是一种“动态类型语言”,这意味着不同于其他一些语言(译者注:如C、JAVA),您不需要指定变量将包含什么数据类型(例如number或string)</p>
+
+<p>例如,如果你声明一个变量并给它一个带引号的值,浏览器就会知道它是一个字符串:</p>
+
+<pre class="brush: js notranslate">let myString = 'Hello';</pre>
+
+<p>即使它包含数字,但它仍然是一个字符串,所以要小心:</p>
+
+<pre class="brush: js notranslate">let myNumber = '500'; // oops, this is still a string
+typeof myNumber;
+myNumber = 500; // much better — now this is a number
+typeof myNumber</pre>
+
+<p>尝试依次将上述代码输入您的控制台,看看结果是什么(无须输入//之后的注释)。 我们使用了一个名为<code>typeof</code>的特殊的操作符 ——它会返回所传递给它的变量的数据类型。 第一次在上面的代码中调用它,它应该返回string,因为此时myNumber变量包含一个字符串'500'。 看看它第二次将返回什么。</p>
+
+<h2 id="总结">总结</h2>
+
+<p>到目前为止,您应该了解了一些JavaScript变量以及如何创建它们。 在下一篇文章中,我们将更详细地关注数字,了解如何在JavaScript中使用基础数学。</p>
+
+<p>{{PreviousMenuNext("Learn/JavaScript/First_steps/What_went_wrong", "Learn/JavaScript/First_steps/Math", "Learn/JavaScript/First_steps")}}</p>
+
+<h2 id="在此模块内">在此模块内</h2>
+
+<ul>
+ <li><a href="/zh-CN/docs/Learn/JavaScript/First_steps/What_is_JavaScript">What is JavaScript?</a></li>
+ <li><a href="/zh-CN/docs/Learn/JavaScript/First_steps/A_first_splash">A first splash into JavaScript</a></li>
+ <li><a href="/zh-CN/docs/Learn/JavaScript/First_steps/What_went_wrong">What went wrong? Troubleshooting JavaScript</a></li>
+ <li><a href="/zh-CN/docs/Learn/JavaScript/First_steps/Variables">Storing the information you need — Variables</a></li>
+ <li><a href="/zh-CN/docs/Learn/JavaScript/First_steps/Math">Basic math in JavaScript — numbers and operators</a></li>
+ <li><a href="/zh-CN/docs/Learn/JavaScript/First_steps/Strings">Handling text — strings in JavaScript</a></li>
+ <li><a href="/zh-CN/docs/Learn/JavaScript/First_steps/Useful_string_methods">Useful string methods</a></li>
+ <li><a href="/zh-CN/docs/Learn/JavaScript/First_steps/Arrays">Arrays</a></li>
+ <li><a href="/zh-CN/docs/Learn/JavaScript/First_steps/Silly_story_generator">Assessment: Silly story generator</a></li>
+</ul>
diff --git a/files/zh-cn/learn/javascript/first_steps/what_is_javascript/index.html b/files/zh-cn/learn/javascript/first_steps/what_is_javascript/index.html
new file mode 100644
index 0000000000..b62daa5a1d
--- /dev/null
+++ b/files/zh-cn/learn/javascript/first_steps/what_is_javascript/index.html
@@ -0,0 +1,501 @@
+---
+title: 什么是 JavaScript?
+slug: Learn/JavaScript/First_steps/What_is_JavaScript
+tags:
+ - API
+ - JavaScript
+ - 初学者
+ - 核心
+ - 浏览器
+translation_of: Learn/JavaScript/First_steps/What_is_JavaScript
+---
+<div dir="ltr">{{LearnSidebar}}</div>
+
+<div dir="ltr">{{NextMenu("Learn/JavaScript/First_steps/A_first_splash", "Learn/JavaScript/First_steps")}}</div>
+
+<p class="summary" dir="ltr">欢迎来到 MDN 的 JavaScript 初学者课程!本节将在一定高度俯瞰 JavaScript,回答一些诸如“它是什么?”和“它能做什么?”的问题 。并使你熟悉 JavaScript 的用途。</p>
+
+<table class="learn-box standard-table" dir="ltr">
+ <tbody>
+ <tr>
+ <th scope="row">预备知识:</th>
+ <td>计算机基础知识,初步理解 HTML 和 CSS 。</td>
+ </tr>
+ <tr>
+ <th scope="row">目标:</th>
+ <td>初步了解 JavaScript,包括一些概念、用途、嵌入网站的方法。</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 dir="ltr" id="广义的定义">广义的定义</h2>
+
+<p dir="ltr">JavaScript 是一种脚本,一门编程语言,它可以在网页上实现复杂的功能,网页展现给你的不再是简单的静态信息,而是实时的内容更新,交互式的地图,2D/3D 动画,滚动播放的视频等等。JavaScript 怎能缺席。它是标准 Web 技术蛋糕的第三层,其中 <a href="/en-US/docs/Learn/HTML">HTML </a>和 <a href="/en-US/docs/Learn/CSS">CSS </a>我们已经在学习中心的其他部分进行了详细的讲解。</p>
+
+<p><img alt="" dir="ltr" src="https://mdn.mozillademos.org/files/13502/cake.png" style="display: block; height: 291px; margin: 0px auto; width: 300px;"></p>
+
+<ul dir="ltr">
+ <li>{{glossary("HTML")}}是一种标记语言,用来结构化我们的网页内容并赋予内容含义,例如定义段落、标题和数据表,或在页面中嵌入图片和视频。</li>
+ <li>{{glossary("CSS")}} 是一种样式规则语言,可将样式应用于 HTML 内容, 例如设置背景颜色和字体,在多个列中布局内容。</li>
+ <li>{{glossary("JavaScript")}} 是一种脚本语言,可以用来创建动态更新的内容,控制多媒体,制作图像动画,还有很多。(好吧,虽然它不是万能的,但可以通过简短的代码来实现神奇的功能。)</li>
+</ul>
+
+<p dir="ltr">这三层依次建立,秩序井然。以文本标签(text label)的简单示例。首先用 HTML 将文本标记起来,从而赋予它结构和目的:</p>
+
+<pre class="brush: html notranslate" dir="rtl">&lt;p&gt;玩家1:小明&lt;/p&gt;</pre>
+
+<p dir="ltr">玩家1:小明</p>
+
+<p dir="ltr">然后我们可以为它加一点 CSS 让它更好看:</p>
+
+<pre class="brush: css notranslate" dir="rtl">p {
+ font-family: sans-serif, '黑体';
+ letter-spacing: 1px;
+ text-transform: uppercase;
+ text-align: center;
+ border: 2px solid rgba(0, 0, 200, 0.6);
+ background: rgba(0, 0, 200, 0.3);
+ color: rgba(0, 0, 200, 0.6);
+ box-shadow: 1px 1px 2px rgba(0, 0, 200, 0.4);
+ border-radius: 10px;
+ padding: 3px 10px;
+ display: inline-block;
+ cursor: pointer;
+}</pre>
+
+<div class="hidden" dir="ltr">
+<h6 id="Beautiful_name_label">Beautiful name label</h6>
+
+<pre class="brush: html notranslate">&lt;!DOCTYPE html&gt;
+&lt;html&gt;&lt;head&gt;&lt;style&gt;
+p {
+ font-family: sans-serif, '黑体';
+ letter-spacing: 1px;
+ text-transform: uppercase;
+ text-align: center;
+ border: 2px solid rgba(0, 0, 200, 0.6);
+ background: rgba(0, 0, 200, 0.3);
+ color: rgba(0, 0, 200, 0.6);
+ box-shadow: 1px 1px 2px rgba(0, 0, 200, 0.4);
+ border-radius: 10px;
+ padding: 3px 10px;
+ display: inline-block;
+ cursor: pointer;
+}
+&lt;/style&gt;&lt;/head&gt;
+&lt;body&gt;
+&lt;p&gt;玩家1:小明&lt;/p&gt;
+&lt;/body&gt;&lt;/html&gt;</pre>
+</div>
+
+<p dir="ltr">{{ EmbedLiveSample('Beautiful_name_label', '100%', 80, "", "", "hide-codepen-jsfiddle") }}</p>
+
+<p dir="ltr">最后,我们可以再加上一些 JavaScript 来实现动态行为:</p>
+
+<pre class="brush: js notranslate" dir="rtl">const para = document.querySelector('p');
+
+para.addEventListener('click', updateName);
+
+function updateName() {
+ let name = prompt('输入一个新的名字:');
+ para.textContent = '玩家1:' + name;
+}
+</pre>
+
+<div class="hidden" dir="ltr">
+<h6 id="Customizable_name_label">Customizable name label</h6>
+
+<pre class="brush: html notranslate">&lt;!DOCTYPE html&gt;
+&lt;html&gt;&lt;head&gt;&lt;style&gt;
+p {
+font-family: sans-serif, '黑体';
+letter-spacing: 1px;
+text-transform: uppercase;
+text-align: center;
+border: 2px solid rgba(0,0,200,0.6);
+background: rgba(0,0,200,0.3);
+color: rgba(0,0,200,0.6);
+box-shadow: 1px 1px 2px rgba(0,0,200,0.4);
+border-radius: 10px;
+padding: 3px 10px;
+display: inline-block;
+cursor: pointer;
+}
+&lt;/style&gt;&lt;/head&gt;
+&lt;body&gt;
+&lt;p&gt;玩家1:小明&lt;/p&gt;
+&lt;script&gt;
+const para = document.querySelector('p');
+para.addEventListener('click', updateName);
+function updateName() {
+ const name = prompt('输入一个新的名字:');
+ para.textContent = '玩家1:' + name;
+}
+&lt;/script&gt;
+&lt;/body&gt;&lt;/html&gt;</pre>
+</div>
+
+<p dir="ltr">{{ EmbedLiveSample('Customizable_name_label', '100%', 80, "", "", "hide-codepen-jsfiddle") }}</p>
+
+<p dir="ltr">尝试点击最后一个版本的文本标签,观察会发生什么(也可在 GitHub 上查看这个 demo 的 <a class="external external-icon" href="https://github.com/roy-tian/learning-area/blob/master/javascript/introduction-to-js-1/what-is-js/javascript-label.html">源代码</a> 或 <a class="external external-icon" href="https://roy-tian.github.io/learning-area/javascript/introduction-to-js-1/what-is-js/javascript-label.html">在线运行</a>。)</p>
+
+<p dir="ltr">JavaScript 能做的远不止这些。让我们来仔细探索。</p>
+
+<h2 dir="ltr" id="它到底可以做什么?">它到底可以做什么?</h2>
+
+<p dir="ltr">客户端(client-side)JavaScript 语言的核心包含一些普遍的编程特性,以让你可以做到如下的事情:</p>
+
+<ul dir="ltr">
+ <li>在变量中储存有用的值。比如上文的示例中,我们请求客户输入一个新名字,然后将其储存到 <code>name</code> 变量中。</li>
+ <li>操作一段文本(在编程中称为“字符串”(string))。上文的示例中,我们取字符串 "玩家1:",然后把它和 <code>name</code> 变量连结起来,创造出完整的文本标签,比如:"玩家1:小明"。</li>
+ <li>运行代码以响应网页中发生的特定事件。上文的示例中,我们用一个 {{Event("click")}} 事件来检测按钮什么时候被点击,然后运行代码更新文本标签。</li>
+ <li>以及更多!</li>
+</ul>
+
+<p dir="ltr">JavaScript 语言核心之上所构建的功能更令人兴奋。<strong>应用程序接口(Application Programming Interfaces</strong>(<strong>API</strong>))将为你的代码提供额外的超能力。</p>
+
+<p dir="ltr">API 是已经建立好的一套代码组件,可以让开发者实现原本很难甚至无法实现的程序。就像现成的家具套件之于家居建设,用一些已经切好的木板组装一个书柜,显然比自己设计,寻找合适的木材,裁切至合适的尺寸和形状,找到正确尺寸的螺钉,再组装成书柜要简单得多。</p>
+
+<p dir="ltr">API 通常分为两类。</p>
+
+<p><img alt="" dir="ltr" src="https://mdn.mozillademos.org/files/13508/browser.png" style="display: block; height: 511px; margin: 0px auto; width: 815px;"></p>
+
+<p dir="ltr"><strong>浏览器 API</strong> 内建于 web 浏览器中,它们可以将数据从周边计算机环境中筛选出来,还可以做实用的复杂工作。例如:</p>
+
+<ul dir="ltr">
+ <li>{{domxref("Document_Object_Model","文档对象模型 API(DOM(Document Object Model)API)")}} 能通过创建、移除和修改 HTML,为页面动态应用新样式等手段来操作 HTML 和 CSS。比如当某个页面出现了一个弹窗,或者显示了一些新内容(像上文小 demo 中看到那样),这就是 DOM 在运行。</li>
+ <li>{{domxref("Geolocation","地理位置 API(Geolocation API)")}} 获取地理信息。这就是为什么 <a href="https://www.google.cn/maps">谷歌地图</a> 可以找到你的位置,而且标示在地图上。</li>
+ <li>{{domxref("Canvas_API","画布(Canvas)")}} 和 {{domxref("WebGL_API","WebGL")}} API 可以创建生动的 2D 和 3D 图像。人们正运用这些 web 技术制作令人惊叹的作品。参见 <a href="https://www.chromeexperiments.com/webgl">Chrome Experiments</a> 以及 <a href="http://webglsamples.org/">webglsamples</a>。</li>
+ <li>诸如 {{domxref("HTMLMediaElement")}} 和 {{domxref("WebRTC API", "WebRTC")}} 等 <a href="https://developer.mozilla.org/en-US/Apps/Fundamentals/Audio_and_video_delivery">影音类 API</a> 让你可以利用多媒体做一些非常有趣的事,比如在网页中直接播放音乐和影片,或用自己的网络摄像头获取录像,然后在其他人的电脑上展示(试用简易版 <a href="http://chrisdavidmills.github.io/snapshot/">截图 demo</a> 以理解这个概念)。</li>
+</ul>
+
+<div class="note" dir="ltr">
+<p><strong>注</strong>: 上述很多演示都不能在旧浏览器中运行。推荐你在测试代码时使用诸如 Firefox, Chrome, Edge 或者 Opera 等现代浏览器。当代码即将交付生产环境时(也就是真实的客户即将使用真实的代码时),你还需要深入考虑 <a href="https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing">跨平台测试</a>。</p>
+</div>
+
+<p dir="ltr"><strong>第三方 API </strong>并没有默认嵌入浏览器中,一般要从网上取得它们的代码和信息。比如:</p>
+
+<ul dir="ltr">
+ <li><a href="https://dev.twitter.com/overview/documentation">Twitter API</a>、<a href="https://open.weibo.com/">新浪微博 API</a> 可以在网站上展示最新推文之类。</li>
+ <li><a href="https://developers.google.com/maps/">谷歌地图 API</a>、<a href="https://lbs.amap.com/">高德地图 API</a> 可以在网站嵌入定制的地图等等。</li>
+</ul>
+
+<div class="note" dir="ltr">
+<p><strong>注</strong>:这些 API 较为高级,我们的课程中不会涉及,更多信息请参考:<a href="https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Client-side_web_APIs">客户端 web API 模块</a>.</p>
+</div>
+
+<p dir="ltr">先稳住!你看到的只是冰山一角。你不可能学一天 JavaScript 就能构建下一个Facebook, 谷歌地图, 或者 Instagram。敬请「牢记初心,砥砺前行」。</p>
+
+<h2 dir="ltr" id="JavaScript_在页面上做了什么?">JavaScript 在页面上做了什么?</h2>
+
+<p dir="ltr">现在我们实实在在的学习一些代码,与此同时,探索 JavaScript 运行时背后发生的事情。</p>
+
+<p dir="ltr">让我们简单回顾一下,浏览器在读取一个网页时都发生什么(<a href="/zh-CN/Learn/CSS/Introduction_to_CSS/How_CSS_works#How_does_CSS_actually_work">CSS 如何工作</a> 一文中首次谈及)。浏览器在读取一个网页时,代码(HTML, CSS 和 JavaScript)将在一个运行环境(浏览器标签页)中得到执行。就像一间工厂,将原材料(代码)加工为一件产品(网页)。</p>
+
+<p><img alt="" dir="ltr" src="https://mdn.mozillademos.org/files/13504/execution.png" style="display: block; margin: 0 auto;"></p>
+
+<p dir="ltr">在 HTML 和 CSS 集合组装成一个网页后,浏览器的 JavaScript 引擎将执行 JavaScript 代码。这保证了当 JavaScript 开始运行之前,网页的结构和样式已经就位。</p>
+
+<p dir="ltr">这样很好,因为JavaScript 最普遍的用处是通过 DOM API(见上文)动态修改 HTML 和 CSS 来更新用户界面 (user interface)。如果 JavaScript 在 HTML 和 CSS 就位之前加载运行,就会引发错误。</p>
+
+<h3 dir="ltr" id="浏览器安全">浏览器安全</h3>
+
+<p dir="ltr">每个浏览器标签页就是其自身用来运行代码的独立容器(这些容器用专业术语称为“运行环境”)。大多数情况下,每个标签页中的代码完全独立运行,而且一个标签页中的代码不能直接影响另一个标签页(或者另一个网站)中的代码。这是一个好的安全措施,如果不这样,黑客就可以从其他网站盗取信息,等等。</p>
+
+<div class="note" dir="ltr">
+<p><strong>注</strong>:以安全的方式在不同网站/标签页中传送代码和数据的方法是存在的,但这些技术较为高级,本课程不会涉及。</p>
+</div>
+
+<h3 dir="ltr" id="JavaScript_运行次序">JavaScript 运行次序</h3>
+
+<p dir="ltr">当浏览器执行到一段 JavaScript 代码时,通常会按从上往下的顺序执行这段代码。这意味着你需要注意代码书写的顺序。比如,我们回到第一个例子中的 JavaScript 代码:</p>
+
+<pre class="brush: js notranslate" dir="rtl">const para = document.querySelector('p');
+
+para.addEventListener('click', updateName);
+
+function updateName() {
+ let name = prompt('输入一个新的名字:');
+ para.textContent = '玩家1:' + name;
+}</pre>
+
+<p dir="ltr">这里我们选定一个文本段落(第 1 行),然后给它附上一个事件监听器(第 3 行),使得在它被点击时,<code>updateName()</code> 代码块(code block) (5 – 8 行)便会运行。<code>updateName()</code> (这类可以重复使用的代码块称为“函数”)向用户请求一个新名字,然后把这个名字插入到段落中以更新显示。</p>
+
+<p dir="ltr">如果你互换了代码里最初两行的顺序,会导致问题。浏览器<a href="zh-CN/docs/Learn/Discover_browser_developer_tools">开发者控制台</a>将返回一个错误: <code>TypeError: para is undefined</code>。这意味着 <code>para</code> 对象还不存在,所以我们不能为它增添一个事件监听器。</p>
+
+<div class="note" dir="ltr">
+<p><strong>注</strong>:这是一个很常见的错误,在引用对象之前必须确保该对象已经存在。</p>
+</div>
+
+<h3 dir="ltr" id="解释代码_vs_编译代码">解释代码 vs 编译代码</h3>
+
+<p dir="ltr">作为程序员,你或许听说过这两个术语:<strong>解释(</strong><strong>interpret)</strong>和 <strong>编译(compile)</strong>。在解释型语言中,代码自上而下运行,且实时返回运行结果。代码在由浏览器执行前,不需要将其转化为其他形式。代码将直接以文本格式(text form)被接收和处理。</p>
+
+<p dir="ltr">相对的,编译型语言需要先将代码转化(编译)成另一种形式才能运行。比如 C/C++ 先被编译成汇编语言,然后才能由计算机运行。程序将以二进制的格式运行,这些二进制内容是由程序源代码产生的。</p>
+
+<p dir="ltr">JavaScript 是轻量级解释型语言。浏览器接受到JavaScript代码,并以代码自身的文本格式运行它。技术上,几乎所有 JavaScript 转换器都运用了一种叫做即时编译(just-in-time compiling)的技术;当 JavaScript 源代码被执行时,它会被编译成二进制的格式,使代码运行速度更快。尽管如此,JavaScript 仍然是一门解释型语言,因为编译过程发生在代码运行中,而非之前。</p>
+
+<p dir="ltr">两种类型的语言各有优势,这个问题我们暂且不谈。</p>
+
+<h3 dir="ltr" id="服务器端代码_vs_客户端代码">服务器端代码 vs 客户端代码</h3>
+
+<p dir="ltr">你或许还听说过<strong>服务器端(server-side)</strong>和 <strong>客户端(client-side)</strong>代码这两个术语,尤其是在web开发时。客户端代码是在用户的电脑上运行的代码,在浏览一个网页时,它的客户端代码就会被下载,然后由浏览器来运行并展示。这就是<strong>客户端 JavaScript</strong>。</p>
+
+<p dir="ltr">而服务器端代码在服务器上运行,接着运行结果才由浏览器下载并展示出来。流行的服务器端 web 语言包括:PHP、Python、Ruby、ASP.NET 以及...... JavaScript!JavaScript 也可用作服务器端语言,比如现在流行的 Node.js 环境,你可以在我们的 <a href="/zh-CN/docs/Learn/Server-side">动态网页 - 服务器端编程</a> 主题中找到更多关于服务器端 JavaScript 的知识。</p>
+
+<h3 dir="ltr" id="动态代码_vs_静态代码">动态代码 vs 静态代码</h3>
+
+<p dir="ltr">“<strong>动态</strong>”一词既适用于客户端 JavaScript,又适用于描述服务器端语言。是指通过按需生成新内容来更新 web 页面 / 应用,使得不同环境下显示不同内容。服务器端代码会在服务器上动态生成新内容,例如从数据库中提取信息。而客户端 JavaScript 则在用户端浏览器中动态生成新内容,比如说创建一个新的 HTML 表格,用从服务器请求到的数据填充,然后在网页中向用户展示这个表格。两种情况的意义略有不同,但又有所关联,且两者(服务器端和客户端)经常协同作战。</p>
+
+<p dir="ltr">没有动态更新内容的网页叫做“<strong>静态</strong>”页面<strong>,</strong>所显示的内容不会改变。</p>
+
+<h2 dir="ltr" id="怎样向页面添加_JavaScript?">怎样向页面添加 JavaScript?</h2>
+
+<p dir="ltr">可以像添加 CSS 那样将 JavaScript 添加到 HTML 页面中。CSS 使用 {{htmlelement("link")}} 元素链接外部样式表,使用 {{htmlelement("style")}} 元素向 HTML 嵌入内部样式表,JavaScript 这里只需一个元素——{{htmlelement("script")}}。我们来看看它是怎么工作的。</p>
+
+<h3 dir="ltr" id="内部_JavaScript">内部 JavaScript</h3>
+
+<ol dir="ltr">
+ <li>首先,下载示例文件 <a href="https://github.com/roy-tian/learning-area/blob/master/javascript/introduction-to-js-1/what-is-js/apply-javascript.html" id="92a6bf0d0009a65dbe8e2051e58c9e9c-69e65808c197b337a320d1613964f0ea3e3ba5f1" title="apply-javascript.html">apply-javascript.html</a>。放在一个好记的文件夹里。</li>
+ <li>分别在浏览器和文本编辑器中打开这个文件。你会看到这个 HTML 文件创建了一个简单的网页,其中有一个可点击按钮。</li>
+ <li><font face="Open Sans, Arial, sans-serif">然后转到文本编辑器,在 </font><code>&lt;/body&gt;</code> 标签结束前插入以下代码:
+ <pre class="brush: html notranslate">&lt;script&gt;
+
+ // 在此编写 JavaScript 代码
+
+&lt;/script&gt;</pre>
+ </li>
+ <li>下面,在 {{htmlelement("script")}} 元素中添加一些 JavaScript 代码,这个页面就能做一些更有趣的事。在“/ /在此编写 JavaScript 代码”一行下方添加以下代码:
+ <pre class="brush: js notranslate">document.addEventListener("DOMContentLoaded", function() {
+ function createParagraph() {
+ let para = document.createElement('p');
+ para.textContent = '你点击了这个按钮!';
+ document.body.appendChild(para);
+ }
+
+ const buttons = document.querySelectorAll('button');
+
+ for(let i = 0; i &lt; buttons.length ; i++) {
+ buttons[i].addEventListener('click', createParagraph);
+ }
+});</pre>
+ </li>
+ <li>保存文件并刷新浏览器,然后你会发现,点击按钮文档下方将会添加一个新段落。</li>
+</ol>
+
+<div class="note" dir="ltr">
+<p><strong>注</strong>: 如果示例不能正常工作,请依次检查所有步骤,并保证没有纰漏。原始文件是否以 <code>.html</code> 为扩展名保存到本地了?<code>&lt;/body&gt;</code> 标签前是否添加了 {{htmlelement("script")}} 元素?JavaScript 代码输入是否正确 ? <strong>JavaScript 是区分大小写的,而且非常精确,所以你需要准确无误地输入所示的句法,否则可能会出错。</strong></p>
+</div>
+
+<div class="note" dir="ltr">
+<p><strong>注</strong>: 你可以在 GitHub 上查看此版本 <a class="external external-icon" href="https://github.com/roy-tian/learning-area/blob/master/javascript/introduction-to-js-1/what-is-js/apply-javascript-internal.html">apply-internal.html</a> (<a class="external external-icon" href="https://roy-tian.github.io/learning-area/javascript/introduction-to-js-1/what-is-js/apply-javascript-internal.html">也可在线查看</a>)。</p>
+</div>
+
+<h3 dir="ltr" id="外部_JavaScript">外部 JavaScript</h3>
+
+<p dir="ltr">这很不错,但是能不能把 JavaScript 代码放置在一个外部文件中呢?现在我们来研究一下。</p>
+
+<ol dir="ltr">
+ <li>首先,在刚才的 HTML 文件所在的目录下创建一个名为 <code>script.js</code> 的新文件。请确保扩展名为 <code>.js</code>,只有这样才能被识别为 JavaScript 代码。</li>
+ <li>将 {{htmlelement("script")}} 元素替换为:
+ <pre class="brush: html notranslate">&lt;script src="script.js" async&gt;&lt;/script&gt;</pre>
+ </li>
+ <li>在 <code>script.js</code> 文件中,添加下面的脚本:
+ <pre class="brush: js notranslate"><code class="language-js"><span class="keyword token">function</span> <span class="function token">createParagraph</span><span class="punctuation token">(</span><span class="punctuation token">)</span> <span class="punctuation token">{</span>
+ let para <span class="operator token">=</span> document<span class="punctuation token">.</span><span class="function token">createElement</span><span class="punctuation token">(</span><span class="string token">'p'</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
+ para<span class="punctuation token">.</span>textContent <span class="operator token">=</span> <span class="string token">'你点击了这个按钮!'</span><span class="punctuation token">;</span>
+ document<span class="punctuation token">.</span>body<span class="punctuation token">.</span><span class="function token">appendChild</span><span class="punctuation token">(</span>para<span class="punctuation token">)</span><span class="punctuation token">;</span>
+<span class="punctuation token">}</span></code>
+
+const buttons <span class="operator token">=</span> document<span class="punctuation token">.</span><span class="function token">querySelectorAll</span><span class="punctuation token">(</span><span class="string token">'button'</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
+
+<span class="keyword token">for</span><span class="punctuation token">(let</span> i <span class="operator token">=</span> <span class="number token">0</span><span class="punctuation token">;</span> i <span class="operator token">&lt;</span> buttons<span class="punctuation token">.</span>length <span class="punctuation token">;</span> i<span class="operator token">++</span><span class="punctuation token">)</span> <span class="punctuation token">{</span>
+ buttons<span class="punctuation token">[</span>i<span class="punctuation token">]</span><span class="punctuation token">.</span><span class="function token">addEventListener</span><span class="punctuation token">(</span><span class="string token">'click'</span><span class="punctuation token">,</span> createParagraph<span class="punctuation token">)</span><span class="punctuation token">;</span>
+<span class="punctuation token">}</span> </pre>
+ </li>
+ <li>保存并刷新浏览器,你会发现二者完全一样。但是现在我们把 JavaScript 写进了一个外部文件。这样做一般会使代码更加有序,更易于复用,且没有了脚本的混合,HTML 也会更加易读,因此这是个好的习惯。</li>
+</ol>
+
+<div class="note" dir="ltr">
+<p><strong>注</strong>:你可以在 GitHub 上查看这个版本 <a class="external external-icon" href="https://github.com/roy-tian/learning-area/blob/master/javascript/introduction-to-js-1/what-is-js/apply-javascript-external.html">apply-external.html</a> 以及 <a class="external external-icon" href="https://github.com/roy-tian/learning-area/blob/master/javascript/introduction-to-js-1/what-is-js/script.js">script.js</a> (<a class="external external-icon" href="https://roy-tian.github.io/learning-area/javascript/introduction-to-js-1/what-is-js/apply-javascript-external.html">也可在线查看</a>).</p>
+</div>
+
+<h3 dir="ltr" id="内联_JavaScript_处理器">内联 JavaScript 处理器</h3>
+
+<p dir="ltr">注意,有时候你会遇到在 HTML 中存在着一丝真实的 JavaScript 代码。它或许看上去像这样:</p>
+
+<pre class="brush: js example-bad line-numbers language-js notranslate" dir="rtl"><code class="language-js"><span class="keyword token">function</span> <span class="function token">createParagraph</span><span class="punctuation token">(</span><span class="punctuation token">)</span> <span class="punctuation token">{</span>
+ const para <span class="operator token">=</span> document<span class="punctuation token">.</span><span class="function token">createElement</span><span class="punctuation token">(</span><span class="string token">'p'</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
+ para<span class="punctuation token">.</span>textContent <span class="operator token">=</span> <span class="string token">'你点击了这个按钮!'</span><span class="punctuation token">;</span>
+ document<span class="punctuation token">.</span>body<span class="punctuation token">.</span><span class="function token">appendChild</span><span class="punctuation token">(</span>para<span class="punctuation token">)</span><span class="punctuation token">;</span>
+<span class="punctuation token">}</span></code></pre>
+
+<pre class="brush: html example-bad notranslate" dir="rtl">&lt;button onclick="createParagraph()"&gt;点我呀&lt;/button&gt;</pre>
+
+<p dir="ltr">你可以在下面尝试这个版本的 demo。</p>
+
+<div class="hidden" dir="ltr">
+<h6 id="Inline_JavaScript">Inline JavaScript</h6>
+
+<pre class="brush: html notranslate"><code>&lt;!DOCTYPE html&gt;</code>
+<code>&lt;html lang="zh-CN"&gt;
+ &lt;head&gt;
+ &lt;meta charset="utf-8"&gt;
+ &lt;title&gt;JavaScript 示例&lt;/title&gt;
+ &lt;script&gt;
+ </code><code class="language-js"><span class="keyword token">function</span> <span class="function token">createParagraph</span><span class="punctuation token">(</span><span class="punctuation token">)</span> <span class="punctuation token">{</span>
+ const para <span class="operator token">=</span> document<span class="punctuation token">.</span><span class="function token">createElement</span><span class="punctuation token">(</span><span class="string token">'p'</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
+ para<span class="punctuation token">.</span>textContent <span class="operator token">=</span> <span class="string token">'你点击了这个按钮!'</span><span class="punctuation token">;</span>
+ document<span class="punctuation token">.</span>body<span class="punctuation token">.</span><span class="function token">appendChild</span><span class="punctuation token">(</span>para<span class="punctuation token">)</span><span class="punctuation token">;</span>
+<span class="punctuation token"> }</span></code>
+<code> &lt;/script&gt;
+ &lt;/head&gt;
+ &lt;body&gt;
+ &lt;button onclick="createParagraph()"&gt;点我呀&lt;/button&gt;
+ &lt;/body&gt;
+&lt;/html&gt;</code>
+
+</pre>
+</div>
+
+<p dir="ltr">{{ EmbedLiveSample('Inline_JavaScript', '100%', 150, "", "", "hide-codepen-jsfiddle") }}</p>
+
+<p dir="ltr">这个 demo 与之前的两个功能完全一致,只是在 {{htmlelement("button")}} 元素中包含了一个内联的 <code>onclick</code> 处理器,使得函数在按钮被按下时运行。</p>
+
+<p dir="ltr"><strong>然而请不要这样做。</strong> 这将使 JavaScript 污染到 HTML,而且效率低下。对于每个需要应用 JavaScript 的按钮,你都得手动添加 <code>onclick="createParagraph()"</code> 属性。</p>
+
+<p dir="ltr">可以使用纯 JavaScript 结构来通过一个指令选取所有按钮。下文的这段代码即实现了这一目的:</p>
+
+<pre class="brush: js notranslate" dir="rtl">const buttons = document.querySelectorAll('button');
+
+for(let i = 0; i &lt; buttons.length ; i++) {
+ buttons[i].addEventListener('click', createParagraph);
+}</pre>
+
+<p dir="ltr">这样写乍看去比 <code>onclick</code> 属性要长一些,但是这样写会对页面上所有按钮生效,无论多少个,或添加或删除,完全无需修改 JavaScript 代码。</p>
+
+<div class="note" dir="ltr">
+<p><strong>注</strong>:请尝试修改 <code>apply-javascript.html</code> 以添加更多按钮。刷新后可发现按下任一按钮时都会创建一个段落。很高效吧。</p>
+</div>
+
+<h3 dir="ltr" id="脚本调用策略">脚本调用策略</h3>
+
+<p dir="ltr">要让脚本调用的时机符合预期,需要解决一系列的问题。这里看似简单,实际大有文章。最常见的问题就是:HTML 元素是按其在页面中出现的次序调用的,如果用 JavaScript 来管理页面上的元素(更精确的说法是使用 <a href="/zh-CN/docs/Web/API/Document_Object_Model">文档对象模型</a> DOM),若 JavaScript 加载于欲操作的 HTML 元素之前,则代码将出错。</p>
+
+<p dir="ltr">在上文的“内部”、“外部”示例中,JavaScript 调用于文档头处,解析 HTML 文档体之前。这样做是有隐患的,需要使用一些结构来避免错误发生。</p>
+
+<p dir="ltr">“内部”示例使用了以下结构:</p>
+
+<pre class="brush: js notranslate" dir="rtl">document.addEventListener("DOMContentLoaded", function() {
+ . . .
+});</pre>
+
+<p dir="ltr">这是一个事件监听器,它监听浏览器的 "<code>DOMContentLoaded</code>" 事件,即 HTML 文档体加载、解释完毕事件。事件触发时将调用 " <code>. . .</code>" 处的代码,从而避免了错误发生(<a href="/zh-CN/docs/Learn/JavaScript/Building_blocks/Events">事件</a> 的概念稍后学习)。</p>
+
+<p dir="ltr">“外部”示例中使用了 JavaScript 的一项现代技术(<code>async</code> “异步”属性)来解决这一问题,它告知浏览器在遇到 <code>&lt;script&gt;</code> 元素时不要中断后续 HTML 内容的加载。</p>
+
+<pre class="brush: html notranslate" dir="rtl">&lt;script src="script.js" async&gt;&lt;/script&gt;</pre>
+
+<p dir="ltr">上述情况下,脚本和 HTML 将一并加载,代码将顺利运行。</p>
+
+<div class="blockIndicator note" dir="ltr">
+<p><strong>注</strong>:“外部”示例中 <code>async</code> 属性可以解决调用顺序问题,因此无需使用 <code>DOMContentLoaded</code> 事件。而 <code>async</code> 只能用于外部脚本,因此不适用于“内部”示例。</p>
+</div>
+
+<p dir="ltr">解决此问题的旧方法是:把脚本元素放在文档体的底端(<code>&lt;/body&gt;</code> 标签之前,与之相邻),这样脚本就可以在 HTML 解析完毕后加载了。此方案(以及上述的 <code>DOMContentLoaded</code> 方案)的问题是:只有在所有 HTML DOM 加载完成后才开始脚本的加载/解析过程。对于有大量 JavaScript 代码的大型网站,可能会带来显著的性能损耗。这也是 <code>async</code> 属性诞生的初衷。</p>
+
+<h4 dir="ltr" id="async_和_defer"><code>async</code> 和 <code>defer</code></h4>
+
+<p dir="ltr">上述的脚本阻塞问题实际有两种解决方案 —— <code>async</code> 和 <code>defer</code>。我们来依次讲解。</p>
+
+<p dir="ltr">浏览器遇到 <code>async</code> 脚本时不会阻塞页面渲染,而是直接下载然后运行。这样脚本的运行次序就无法控制,只是脚本不会阻止剩余页面的显示。当页面的脚本之间彼此独立,且不依赖于本页面的其它任何脚本时,<code>async</code> 是最理想的选择。</p>
+
+<p dir="ltr">比如,如果你的页面要加载以下三个脚本:</p>
+
+<pre class="brush: html notranslate" dir="rtl">&lt;script async src="js/vendor/jquery.js"&gt;&lt;/script&gt;
+
+&lt;script async src="js/script2.js"&gt;&lt;/script&gt;
+
+&lt;script async src="js/script3.js"&gt;&lt;/script&gt;</pre>
+
+<p dir="ltr">三者的调用顺序是不确定的。<code>jquery.js</code> 可能在 <code>script2</code> 和 <code>script3</code> 之前或之后调用,如果这样,后两个脚本中依赖 <code>jquery</code> 的函数将产生错误,因为脚本运行时 <code>jquery</code> 尚未加载。</p>
+
+<p dir="ltr">解决这一问题可使用 <code>defer</code> 属性,脚本将按照在页面中出现的顺序加载和运行:</p>
+
+<pre class="brush: html notranslate" dir="ltr">&lt;script defer src="js/vendor/jquery.js"&gt;&lt;/script&gt;
+
+&lt;script defer src="js/script2.js"&gt;&lt;/script&gt;
+
+&lt;script defer src="js/script3.js"&gt;&lt;/script&gt;</pre>
+
+<p dir="ltr">添加 <code>defer</code> 属性的脚本将按照在页面中出现的顺序加载,因此第二个示例可确保 <code>jquery.js</code> 必定加载于 <code>script2.js</code> 和 <code>script3.js</code> 之前,同时 <code>script2.js</code> 必定加载于 <code>script3.js</code> 之前。</p>
+
+<p dir="ltr">脚本调用策略小结:</p>
+
+<ul dir="ltr">
+ <li>如果脚本无需等待页面解析,且无依赖独立运行,那么应使用 <code>async</code>。</li>
+ <li>如果脚本需要等待页面解析,且依赖于其它脚本,调用这些脚本时应使用 <code>defer</code>,将关联的脚本按所需顺序置于 HTML 中。</li>
+</ul>
+
+<h2 dir="ltr" id="注释">注释</h2>
+
+<p dir="ltr">就像 HTML 和 CSS,JavaScript 代码中也可以添加注释,浏览器会忽略它们,注释只是为你的同事(还有你,如果半年后再看自己写的代码你会说,这是什么垃圾玩意。)提供关于代码如何工作的指引。注释非常有用,而且应该经常使用,尤其在大型应用中。注释分为两类:</p>
+
+<ul dir="ltr">
+ <li>在双斜杠后添加单行注释,比如:
+ <pre class="brush: js notranslate">// 我是一条注释</pre>
+ </li>
+ <li>在 <code>/*</code> 和 <code>*/</code> 之间添加多行注释,比如:
+ <pre class="brush: js notranslate">/*
+ 我也是
+ 一条注释
+*/</pre>
+ </li>
+</ul>
+
+<p dir="ltr">比如说,我们可以这样为上一个 demo 添加注释:</p>
+
+<pre class="brush: js notranslate" dir="rtl">// 函数:创建一个新的段落并添加至 HTML body 底部。
+function createParagraph() {
+ let para = document.createElement('p');
+ para.textContent = '你点了这个按钮!';
+ document.body.appendChild(para);
+}
+
+/*
+ 1. 取得页面上所有按钮的引用并将它们置于一个数组中。
+ 2. 通过一个循环为每个按钮添加一个点击事件的监听器。
+ 当按钮被点击时,调用 createParagraph() 函数。
+*/
+
+const buttons = document.querySelectorAll('button');
+
+for (let i = 0; i &lt; buttons.length; i++) {
+ buttons[i].addEventListener('click', createParagraph);
+}</pre>
+
+<h2 dir="ltr" id="小结">小结</h2>
+
+<p dir="ltr">恭喜你,迈出了探索 JavaScript 世界的第一步。我们从理论开始,介绍为什么要使用 JavaScript,以及用它能做什么事情。过程中穿插了一些代码示例并讲解了 JavaScript 如何与网站中其他代码适配,等等。</p>
+
+<p dir="ltr">现在 JavaScript 或许还有些令人生畏,但不用担心。在课程中我们会循序渐进。下一节将<a href="/en-US/docs/Learn/JavaScript/Introduction_to_JavaScript_1/A_first_splash"> 全力投入实战</a>,让你专注其中,并建立自己的 JavaScript 示例。</p>
+
+<p dir="ltr">{{NextMenu("Learn/JavaScript/First_steps/A_first_splash", "Learn/JavaScript/First_steps")}}</p>
+
+<h2 dir="ltr" id="本章目录">本章目录</h2>
+
+<ul dir="ltr">
+ <li><a href="https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/First_steps/What_is_JavaScript">JavaScript 是什么?</a></li>
+ <li><a href="https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/First_steps/A_first_splash">JavaScript 初体验</a></li>
+ <li><a href="https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/First_steps/What_went_wrong">查找并解决 JavaScript 代码的错误 </a></li>
+ <li><a href="https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/First_steps/Variables">变量:储存所需信息</a></li>
+ <li><a href="https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/First_steps/Math">数字和运算符:JavaScript 的基本算数</a></li>
+ <li><a href="https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/First_steps/Strings">字符串:JavaScript 文本的处理</a></li>
+ <li><a href="https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/First_steps/Useful_string_methods">字符串的一些实用方法</a></li>
+ <li><a href="https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/First_steps/Arrays">数组</a></li>
+ <li><a href="https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/First_steps/Silly_story_generator">课程评估:笑话机</a></li>
+</ul>
+
+<p>
+ <audio style="display: none;"></audio>
+</p>
diff --git a/files/zh-cn/learn/javascript/first_steps/what_went_wrong/index.html b/files/zh-cn/learn/javascript/first_steps/what_went_wrong/index.html
new file mode 100644
index 0000000000..b36d8be3df
--- /dev/null
+++ b/files/zh-cn/learn/javascript/first_steps/what_went_wrong/index.html
@@ -0,0 +1,284 @@
+---
+title: 查找并解决 JavaScript 代码的错误
+slug: Learn/JavaScript/First_steps/What_went_wrong
+tags:
+ - JavaScript
+ - 初学者
+ - 开发者工具
+ - 指导教程
+ - 文章
+ - 调试
+ - 错误
+translation_of: Learn/JavaScript/First_steps/What_went_wrong
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/JavaScript/First_steps/A_first_splash", "Learn/JavaScript/First_steps/Variables", "Learn/JavaScript/First_steps")}}</div>
+
+<p class="summary">上一节中你创建了“猜数字”游戏,但它可能没有正常工作。别担心,本节将为你提供一些简单的提示,来帮助你查找并修复JavaScript程序中的错误,从而让你远离困扰。</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">预备知识:</th>
+ <td>计算机基础知识,初步理解HTML和CSS,了解JavaScript。</td>
+ </tr>
+ <tr>
+ <th scope="row">目标:</th>
+ <td>获得独立修复简单问题的能力和信心。</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="错误类型">错误类型</h2>
+
+<p>一般来说,代码错误主要分为两种:</p>
+
+<ul>
+ <li><strong>语法错误</strong>:代码中存在拼写错误,将导致程序完全或部分不能运行,通常你会收到一些出错信息。只要熟悉语言并了解出错信息的含义,你就能够顺利修复它们。</li>
+ <li><strong>逻辑错误</strong>:有些代码语法虽正确,但执行结果和预期相悖,这里便存在着逻辑错误。这意味着程序虽能运行,但会给出错误的结果。由于一般你不会收到来自这些错误的提示,它们通常比语法错误更难修复。</li>
+</ul>
+
+<p>事情远没有你想的那么简单,随着探究的深入,会有更多差异因素浮出水面。但在编程生涯的初级阶段上述分类方法已足矣。下面我们将依次分析。</p>
+
+<h2 id="一个出错的示例">一个出错的示例</h2>
+
+<p>让我们重回猜数字游戏,这次我们将故意引入一些错误。请到Github下载一份 <a href="https://github.com/roy-tian/learning-area/blob/master/javascript/introduction-to-js-1/troubleshooting/number-game-errors.html">number-game-errors.html</a> (或 <a class="external external-icon" href="https://roy-tian.github.io/learning-area/javascript/introduction-to-js-1/troubleshooting/number-game-errors.html">在线运行</a>)。</p>
+
+<ol>
+ <li>请分别在你的文本编辑器和浏览器中打开刚下载的文件。</li>
+ <li>先试玩游戏,你会发现在点击“确定”按钮时,游戏并没有响应。</li>
+</ol>
+
+<div class="note">
+<p><strong>注</strong>:你可能还在为修复你自己版本的游戏头疼,但我们仍然希望你先用我们的版本来完成这一节,这样你才能学到本节中的技术。然后再去修复自己的游戏也不晚。</p>
+</div>
+
+<p>首先查看开发者控制台,看是否存在语法错误,然后尝试修复。详见下文。</p>
+
+<h2 id="修复语法错误">修复语法错误</h2>
+
+<p>以前的课程中,你学会了在 <a href="/zh-CN/docs/Learn/Common_questions/What_are_browser_developer_tools">开发工具 JavaScript 控制台</a> 中输入一些简单的 JavaScript 命令。(如果你忘记了如何在浏览器中打开它,可以直接打开上面的链接)。更实用的是,当 JavaScript 代码进入浏览器的 JavaScript 引擎时,如果存在语法错误,控制台会提供出错信息。现在我们去看一看。</p>
+
+<ol>
+ <li>
+ <p class="brush: bash">打开 <code>number-game-errors.html</code> 所在的标签页,然后打开 JavaScript 控制台。你将看到以下出错信息:</p>
+ <img alt="不是函数" src="https://mdn.mozillademos.org/files/16256/%E5%B1%8F%E5%B9%95%E5%BF%AB%E7%85%A7_2018-10-17_20.27.08.png" style="display: block; height: 1116px; margin: 0px auto; width: 1564px;"></li>
+ <li>这个错误很容易跟踪,浏览器为你提供了几条有用的信息(截图来自 Firefox,其他浏览器也提供类似信息)。从左到右依次为:</li>
+</ol>
+
+<ul>
+ <li>红色 “!” 表示这是一个错误。</li>
+ <li>一条出错信息,表示问题出在哪儿:“TypeError:<strong>guessSubmit</strong>.addeventListener is not a function”(类型错误:<strong>guessSubmit</strong>.addeventListener 不是函数)</li>
+ <li>点击[详细了解]将跳转到一个 MDN 页面,其中包含了此类错误超详细的解释。</li>
+ <li>JavaScript 文件名,点击将跳转到开发者工具的“调试器”标签页。 如果你按照这个链接,你会看到错误突出显示的确切行。</li>
+ <li>出错的行,以及导致错误的首个字符号。这里错误来自 86 行,第 3 个字符。</li>
+</ul>
+
+<ol start="3">
+ <li>我们在代码编辑器中找到第 86 行:</li>
+</ol>
+
+<pre class="brush: js notranslate">guessSubmit.addeventListener('click', checkGuess);</pre>
+
+<ol start="4">
+ <li>出错信息显示“guessSubmit.addeventListener 不是一个函数”,说明这里可能存在拼写错误。如果你不确定某语法的拼写是否正确,可以到 MDN 上去查找,目前最简便的方法就是去你喜欢的搜索引擎搜索“MDN + 语言<em>特性”。就本文当前内容你可以点击</em>:<code><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/EventTarget/addEventListener">addEventListener()</a></code>。</li>
+ <li>因此这里错误显然是我们把函数名写错造成的。请记住,JavaScript 区分大小写,所以任何轻微的不同或大小写问题都会导致出错。将 <code>addeventListener</code> 改为 <code>addEventListener</code> 便可解决。</li>
+</ol>
+
+<div class="note">
+<p><strong>注:</strong>更多信息请参考 <a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Errors/Not_a_function">类型错误:“x”不是一个函数</a>。</p>
+</div>
+
+<h3 id="语法错误:第二轮">语法错误:第二轮</h3>
+
+<ol>
+ <li>保存页面并刷新,可以看到出错信息不见了。</li>
+ <li>现在,如果尝试输入一个数字并按确定按钮,你会看到...另一个错误! <img alt="" src="https://mdn.mozillademos.org/files/16264/%E5%B1%8F%E5%B9%95%E5%BF%AB%E7%85%A7_2018-10-17_21.56.00.png" style="display: block; height: 1266px; margin: 0px auto; width: 1646px;"></li>
+ <li>此次出错信息为“TypeError:lowOrHi is null”(“类型错误:lowOrHi为null”),在第 78 行。
+ <div class="note"><strong>注</strong>:<code><a href="https://developer.mozilla.org/zh-CN/docs/Glossary/Null">Null</a></code>是一个特殊值,意思是“什么也没有”,或者“没有值”。这表示 <code>lowOrHi</code> 已声明并初始化,但没有任何有意义的值,可以说:它没有类型没有值。</div>
+
+ <div class="note"><strong>注</strong>:这条错误没有在页面加载时立即发生,是因为它发生在函数内部(<code>checkGuess() { ... }</code>块中)。函数内部的代码运行于一个外部代码相互独立的域内,后面函数的文章中将更详细地讲解。此时此刻,只有当代码运行至86行并调用 <code>checkGuess()</code> 函数时,代码才会抛出出错信息。</div>
+ </li>
+ <li>请观察第 78 行代码:
+ <pre class="brush: js notranslate">lowOrHi.textContent = '你猜高了!';</pre>
+ </li>
+ <li>该行试图将 <code>lowOrHi</code> 变量中的<code> textContent</code> 属性设置为一个字符串,但是失败了,这是因为 <code>lowOrHi</code> 并不包含预期的内容。为了一探究竟,你可以在代码中查找一下该变量的的其他实例。<code>lowOrHi</code> 最早出现于第 48 行:
+ <pre class="brush: js notranslate">const lowOrHi = document.querySelector('lowOrHi');</pre>
+ </li>
+ <li>此处,我们试图让该变量包含一个指向文档 HTML 中特定元素的引用。我们来检查一下在该行代码执行后变量的值是否为 <code>null</code>。在第 49 行添加以下代码:
+ <pre class="brush: js notranslate">console.log(lowOrHi);</pre>
+
+ <div class="note">
+ <p><strong>注</strong>:<code><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/Console/log">console.log()</a></code> 是一个非常实用的调试功能,它可以把值打印到控制台。因此我们将其置于代码第 48 行时,它会将 <code>lowOrHi</code> 的值打印至控制台。</p>
+ </div>
+ </li>
+ <li>保存并刷新,你将在控制台看到 <code>console.log()</code> 的执行结果:<img alt="" src="https://mdn.mozillademos.org/files/16275/%E5%B1%8F%E5%B9%95%E5%BF%AB%E7%85%A7_2018-10-18_16.43.40.png" style="display: block; height: 1246px; margin: 0px auto; width: 1606px;"> 显然,此处 <code>lowOrHi</code> 的值为 <code>null</code>,所以第 48 行肯定有问题。</li>
+ <li>我们来思考问题有哪些可能。第 48 行使用 <code><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/Document/querySelector">document.querySelector()</a></code> 方法和一个 CSS 选择器来取得一个元素的引用。进一步查看我们的文件,我们可以找到有问题的段落:
+ <pre class="brush: js notranslate">&lt;p class="lowOrHi"&gt;&lt;/p&gt;</pre>
+ </li>
+ <li>这里我们需要一个类选择器,它应以一个点开头(<code>.</code>),但被传递到第 48 行的<code>querySelector()</code>方法中的选择器没有点。这可能是问题所在!尝试将第 48 行中的<code> lowOrHi</code> 改成 <code>.lowOrHi</code>。</li>
+ <li>再次保存并刷新,此时 <code>console.log()</code> 语句应该返回我们想要的 <code>&lt;p&gt;</code> 元素。终于把错误搞定了!此时你可以把 <code>console.log()</code> 一行删除,或保留它以便随后参考。选择权在你。</li>
+</ol>
+
+<div class="note">
+<p><strong>注</strong>:此错误的更多详细信息请参阅:<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Errors/Unexpected_type">类型错误:“x”(不)是“y”</a>。</p>
+</div>
+
+<h3 id="语法错误:第三轮">语法错误:第三轮</h3>
+
+<ol>
+ <li>现在,如果你再次试玩,你离成功更进了一步。游戏过程按部就班,直到猜测正确或机会用完,游戏结束。</li>
+ <li>此时如果点击“开始新游戏”,游戏将再次出错,抛出与开始时同样的错误——“TypeError:resetButton.addeventListener is not a function”!这次它来自第 94 行。</li>
+ <li>查看第 94 行,很容易看到我们犯了同样的错误。我们只需要再次将 <code>addeventListener </code>改为 <code>addEventListener</code>。现在就改吧。</li>
+</ol>
+
+<h2 id="逻辑错误">逻辑错误</h2>
+
+<p>此时,游戏应该可以顺利进行了。但经过几次试玩后你一定会注意到要猜的随机数不是 0 就是 1。这可不是我们期望的!</p>
+
+<p>游戏的逻辑肯定是哪里出现了问题,因为游戏并没有返回错误,只是不能正确运行。</p>
+
+<ol>
+ <li>寻找<code> randomNumber</code> 变量和首次设定随机数的代码。保存着游戏开始时玩家要猜的随机数的实例大约在 44 行:
+
+ <pre class="brush: js notranslate">let randomNumber = Math.floor(Math.random()) + 1;</pre>
+
+ <p>重新开始游戏产生随机数的设定语句大约在 113 行:</p>
+
+ <pre class="brush: js notranslate">randomNumber = Math.floor(Math.random()) + 1;</pre>
+ </li>
+ <li>为了检查问题是否来自这两行,我们要再次转到我们的朋友-控制台:在两行代码之后各插入下面的代码:
+ <pre class="brush: js notranslate">console.log(randomNumber);</pre>
+ </li>
+ <li>保存并刷新,然后试玩,你会看到在控制台显示的随机数总是等于1。</li>
+</ol>
+
+<h3 id="修正逻辑错误">修正逻辑错误</h3>
+
+<p>为了解决这个问题,让我们来思考这行代码如何工作。首先,我们调用 <code><a href="/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Math/random">Math.random()</a></code>,它生成一个在 0 和 1 之间的十进制随机数,例如 0.5675493843。</p>
+
+<pre class="brush: js notranslate">Math.random()
+</pre>
+
+<p>接下来,我们把调用 <code>Math.random()</code> 的结果作为参数传递给 <code><a href="/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Math/floor">Math.floor()</a></code>,它会舍弃小数部分返回与之最接近的整数。然后我们给这个结果加上1:</p>
+
+<pre class="notranslate">Math.floor(Math.random()) + 1
+</pre>
+
+<p>由于将一个 0 和 1 之间的随机小数的小数部分舍弃,返回的整数一定为 0,因此在此基础上加 1 之后返回值一定为 1。要在舍弃小数部分之前将它乘以100。便可得到 0 到 99 之间的随机数:</p>
+
+<pre class="brush: js notranslate">Math.floor(Math.random() * 100);
+</pre>
+
+<p>然后再加 1,便可得到一个 100 以内随机的自然数:</p>
+
+<pre class="brush: js notranslate">Math.floor(Math.random() * 100) + 1;</pre>
+
+<p>将上述两行内容替换为此,然后保存刷新,游戏终于如期运行了!</p>
+
+<h2 id="其它常见错误">其它常见错误</h2>
+
+<p>代码中还会遇到其他常见错误。 本节将指出其中的大部分。</p>
+
+<h3 id="SyntaxError_missing_before_statement_(语法错误:语句缺少分号)">SyntaxError: missing ; before statement<br>
+ (语法错误:语句缺少分号)</h3>
+
+<p>这个错误通常意味着你漏写了一行代码最后的分号,但是此类错误有时候会更加隐蔽。例如如果我们把 <code>checkGuess()</code> 函数中的这一行 :</p>
+
+<pre class="brush: js notranslate">let userGuess = Number(guessField.value);
+</pre>
+
+<p>改成</p>
+
+<pre class="brush: js notranslate">let userGuess === Number(guessField.value);
+</pre>
+
+<p>将抛出一个错误。因为系统认为你在做其他事情。请不要把赋值运算符(<code>=</code>,为一个变量赋值)和严格等于运算符(<code>===</code>,比较两个值是否相等,返回 <code>true</code>/<code>false</code>)弄混淆。</p>
+
+<div class="note">
+<p><strong>注:</strong>此错误的更多详细信息请参考 <a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements">SyntaxError: missing ; before statement</a> 。</p>
+</div>
+
+<h3 id="不管输入什么程序总说“你猜对了!”">不管输入什么程序总说“你猜对了!”</h3>
+
+<p>这是混淆赋值和严格等于运算符的又一症状。例如我们把 <code>checkGuess()</code> 里的:</p>
+
+<pre class="brush: js notranslate">if (userGuess === randomNumber) {</pre>
+
+<p>改成</p>
+
+<pre class="brush: js notranslate">if (userGuess = randomNumber) {
+</pre>
+
+<p>因为条件永远返回 <code>true</code>,使得程序报告你猜对了。小心哦!</p>
+
+<h3 id="SyntaxError_missing_after_argument_list_(语法错误:参数表末尾缺少括号)">SyntaxError: missing ) after argument list<br>
+ (语法错误:参数表末尾缺少括号)</h3>
+
+<p>这个很简单。通常意味着函数/方法调用后的结束括号忘写了。</p>
+
+<div class="note">
+<p><strong>注:</strong>有关此错误的更多详细信息请参考: <a href="/zh-CN/docs/Web/JavaScript/Reference/Errors/Missing_parenthesis_after_argument_list">SyntaxError: missing ) after argument list</a>。</p>
+</div>
+
+<h3 id="SyntaxError_missing_after_property_id_(语法错误:属性ID后缺少冒号)">SyntaxError: missing : after property id<br>
+ (语法错误:属性ID后缺少冒号)</h3>
+
+<p>JavaScript 对象的形式有错时通常会导致此类错误,如果把</p>
+
+<pre class="brush: js notranslate">function checkGuess() {</pre>
+
+<p>写成了</p>
+
+<pre class="brush: js notranslate">function checkGuess( {</pre>
+
+<p>浏览器会认为我们试图将函数的内容当作参数传回函数。写圆括号时要小心!</p>
+
+<h3 id="SystaxError_missing_after_function_body_(语法错误:函数体末尾缺少花括号)">SystaxError: missing } after function body<br>
+ (语法错误:函数体末尾缺少花括号)</h3>
+
+<p>这个简单。通常意味着函数或条件结构中丢失了一个花括号。如果我们将 <code>checkGuess()</code> 函数末尾的花括号删除,就会得到这个错误。</p>
+
+<h3 id="SyntaxError_expected_expression_got_string_(语法错误:得到一个_string_而非表达式)">SyntaxError: expected expression, got '<em>string</em>'<br>
+ (语法错误:得到一个 '<em>string</em>' 而非表达式)</h3>
+
+<p>或者</p>
+
+<h3 id="SyntaxError_unterminated_string_literal_(语法错误:字符串字面量未正常结束)">SyntaxError: unterminated string literal<br>
+ (语法错误:字符串字面量未正常结束)</h3>
+
+<p>这个错误通常意味着字符串两端的引号漏写了一个。如果你漏写了字符串开始的引号,将得到第一条出错信息,这里的 '<em>string' </em>将被替换为浏览器发现的意外字符。如果漏写了末尾的引号将得到第二条。</p>
+
+<p>对于所有的这些错误,想想我们在实例中是如何逐步解决的。错误出现时,转到错误所在的行观察是否能发现问题所在。记住,错误不一定在那一行,错误的原因也可能和我们在上面所说的不同!</p>
+
+<div class="note">
+<p><strong>注:</strong> 有关这些错误的更多详细信息请参考:<a href="/zh-CN/docs/Web/JavaScript/Reference/Errors/Unexpected_token">SyntaxError: Unexpected token</a> 以及 <a href="/zh-CN/docs/Web/JavaScript/Reference/Errors/Unterminated_string_literal">SyntaxError: unterminated string literal</a>。</p>
+</div>
+
+<h2 id="小结">小结</h2>
+
+<p>我们有了能够在简单的 JavaScript 程序中除错的基础知识。解决代码中的错误并不总是那么简单,但至少本节内容可以为刚刚踏上学习之路的你节省出几个小时来补觉,同时让问题更快速得到解决。</p>
+
+<h2 id="另请参阅">另请参阅</h2>
+
+<ul>
+ <li>许多错误不能一一的在这里列出来,我们正在编写一个参考文档来详细说明它们的含义。请参阅 <a href="/zh-CN/docs/Web/JavaScript/Reference/Errors">JavaScript 出错信息参考</a>.</li>
+ <li>如果你在阅读了本文之后遇到了一些错误但不知如何解决,你能够得到别人的帮助! <span class="short_text" id="result_box" lang="zh-CN"><span>可以到<a href="https://discourse.mozilla-community.org/t/learning-web-development-marking-guides-and-questions/16294"> 学习区</a></span></span> 或者 <a href="https://wiki.mozilla.org/IRC">Mozilla IRC</a> 的 <a href="irc://irc.mozilla.org/mdn">#mdn</a> 聊天室来提问。告诉我们你遇到的错误是什么,我们会尽量帮助你。附加一段你的代码也是很有用的。</li>
+</ul>
+
+<p>{{PreviousMenuNext("Learn/JavaScript/First_steps/A_first_splash", "Learn/JavaScript/First_steps/Variables", "Learn/JavaScript/First_steps")}}</p>
+
+<h2 id="本章目录">本章目录</h2>
+
+<ul>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps/What_is_JavaScript">JavaScript 是什么?</a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps/A_first_splash">JavaScript 初体验</a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps/What_went_wrong">查找并解决 JavaScript 代码的错误 </a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps/Variables">变量:储存所需信息</a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps/Math">数字和运算符:JavaScript 的基本算数</a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps/Strings">字符串:JavaScript 文本的处理</a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps/Useful_string_methods">字符串的一些实用方法</a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps/Arrays">数组</a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps/Silly_story_generator">课程评估:笑话机</a></li>
+</ul>
diff --git a/files/zh-cn/learn/javascript/howto/index.html b/files/zh-cn/learn/javascript/howto/index.html
new file mode 100644
index 0000000000..0aaa32f374
--- /dev/null
+++ b/files/zh-cn/learn/javascript/howto/index.html
@@ -0,0 +1,294 @@
+---
+title: 在JavaSctript中解决问题
+slug: learn/JavaScript/Howto
+tags:
+ - JavaScript
+ - 初学者
+ - 学习
+translation_of: Learn/JavaScript/Howto
+---
+<div>{{LearnSidebar}}</div>
+
+<p class="summary">以下链接指向您需要修复的日常常见问题的解决方案,以使您的JavaScript代码正确运行。</p>
+
+<h2 id="初学者常见的错误">初学者常见的错误</h2>
+
+<h3 id="正确的拼写和使用">正确的拼写和使用</h3>
+
+<p>如果你的代码不工作或浏览器抱怨某些东西是未定义的,请检查你是否正确拼写了所有的变量名称,函数名称等。 <br>
+  <br>
+ 导致问题的一些常见的内置浏览器函数有:</p>
+
+<table class="standard-table">
+ <thead>
+ <tr>
+ <th scope="col">正确</th>
+ <th scope="col">错误</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td><code>getElementsByTagName()</code></td>
+ <td><code>getElementbyTagName()</code></td>
+ </tr>
+ <tr>
+ <td><code>getElementsByName()</code></td>
+ <td><code>getElementByName()</code></td>
+ </tr>
+ <tr>
+ <td><code>getElementsByClassName()</code></td>
+ <td><code>getElementByClassName()</code></td>
+ </tr>
+ <tr>
+ <td><code>getElementById()</code></td>
+ <td><code>getElementsById()</code></td>
+ </tr>
+ </tbody>
+</table>
+
+<h3 id="分号位置">分号位置</h3>
+
+<p>必须确保没有错误的放置分号,例如:</p>
+
+<table class="standard-table">
+ <thead>
+ <tr>
+ <th scope="col">正确</th>
+ <th scope="col">错误</th>
+ </tr>
+ <tr>
+ <td><code>elem.style.color = 'red';</code></td>
+ <td><code>elem.style.color = 'red;'</code></td>
+ </tr>
+ </thead>
+</table>
+
+<h3 id="函数">函数</h3>
+
+<p>函数有很多容易出错的地方。</p>
+
+<p>最常见的错误之一是函数被声明了却没有被调用。例如:</p>
+
+<pre class="brush: js">function myFunction() {
+ alert('This is my function.');
+};</pre>
+
+<p>这个函数不会执行,除非你调用它,例如:</p>
+
+<pre class="brush: js">myFunction();</pre>
+
+<h4 id="函数作用域">函数作用域</h4>
+
+<p>记住<a href="/en-US/docs/Learn/JavaScript/Building_blocks/Functions#Function_scope_and_conflicts">函数拥有自己的作用域</a>——你不能从函数外部访问一个函数内的变量值,除非你在全局声明了该变量(即不在任何函数内),或者从函数外部获得它的<a href="/en-US/docs/Learn/JavaScript/Building_blocks/Return_values">返回值</a>。</p>
+
+<h4 id="在return语句之后运行代码">在return语句之后运行代码</h4>
+
+<p>还要记住,当你向一个函数外部返回一个值时,JavaScript解释器会退出这个函数——在return语句运行之后,没有声明任何代码。</p>
+
+<p>事实上,如果您在返回语句之后有代码,某些浏览器(如Firefox)会在开发人员控制台中给您一条错误消息。 Firefox在返回语句后给你提示“无法访问的代码”。</p>
+
+<h3 id="对象标记法与正常赋值">对象标记法与正常赋值</h3>
+
+<p>当你在JavaScript中正常赋值时,使用等号:</p>
+
+<pre class="brush: js">var myNumber = 0;</pre>
+
+<p>但是在<a href="/en-US/docs/Learn/JavaScript/Objects">对象</a>中,你需要使用冒号来分隔成员名称和值,并用逗号分隔每个成员,例如:</p>
+
+<pre class="brush: js">var myObject = {
+ name : 'Chris',
+ age : 38
+}</pre>
+
+<h2 id="基本定义">基本定义</h2>
+
+<div class="column-container">
+<div class="column-half">
+<ul>
+ <li><a href="/en-US/docs/Learn/JavaScript/First_steps/What_is_JavaScript#A_high-level_definition">JavaScript是什么?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/First_steps/Variables#What_is_a_variable">变量是什么?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/First_steps/Strings">字符串是什么?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/First_steps/Arrays#What_is_an_Array">数组是什么?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Building_blocks/Looping_code">循环是什么?</a></li>
+</ul>
+</div>
+
+<div class="column-half">
+<ul>
+ <li><a href="/en-US/docs/Learn/JavaScript/Building_blocks/Functions">函数是什么?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Building_blocks/Events">事件是什么?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Objects/Basics#Object_basics">对象是什么?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Objects/JSON#No_really_what_is_JSON">JSON是什么?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Introduction#What_are_APIs">web API是什么?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Manipulating_documents#The_document_object_model">DOM是什么?</a></li>
+</ul>
+</div>
+</div>
+
+<h2 id="基本用例">基本用例</h2>
+
+<div class="column-container">
+<div class="column-half">
+<h3 id="常见">常见</h3>
+
+<ul>
+ <li><a href="/en-US/docs/Learn/JavaScript/First_steps/What_is_JavaScript#How_do_you_add_JavaScript_to_your_page">怎么在页面中添加JavaScript?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/First_steps/What_is_JavaScript#Comments">怎么在JavaScript中添加注释?</a></li>
+</ul>
+
+<h3 id="变量">变量</h3>
+
+<ul>
+ <li><a href="/en-US/docs/Learn/JavaScript/First_steps/Variables#Declaring_a_variable">如何声明一个变量?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/First_steps/Variables#Initializing_a_variable">如何初始化一个变量的值?</a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps/Variables#Updating_a_variable">如何更新变量的值?</a> (参见 <a href="/en-US/docs/Learn/JavaScript/First_steps/Math#Assignment_operators">赋值操作符</a>)</li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps/Variables#Variable_types">JavaScript中有哪些数据类型?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/First_steps/Variables#Loose_typing">'弱类型'是什么意思?</a></li>
+</ul>
+
+<h3 id="数字">数字</h3>
+
+<ul>
+ <li><a href="/en-US/docs/Learn/JavaScript/First_steps/Math#Types_of_numbers">What types of number do you have to deal with in web development?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/First_steps/Math#Arithmetic_operators">How do you do basic math in JavaScript?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/First_steps/Math#Operator_precedence">What is operator precedence, and how is it handled in JavaScript?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/First_steps/Math#Increment_and_decrement_operators">How do you increment and decrement values in JavaScript?</a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps/Math#Comparison_operators">How do you compare values in JavaScript?</a> (e.g. to see which one is bigger, or to see if one value is equal to another).</li>
+</ul>
+
+<h3 id="字符串">字符串</h3>
+
+<ul>
+ <li><a href="/en-US/docs/Learn/JavaScript/First_steps/Strings#Creating_a_string">How do you create a string in JavaScript?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/First_steps/Strings#Single_quotes_versus_double_quotes">Do you have to use single quotes or double quotes?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/First_steps/Strings#Escaping_characters_in_a_string">How do you escape characters in strings?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/First_steps/Strings#Concatenating_strings">How do you join strings together?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/First_steps/Strings#Numbers_versus_strings">Can you join strings and numbers together?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/First_steps/Useful_string_methods#Finding_the_length_of_a_string">How do you find the length of a string?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/First_steps/Useful_string_methods#Retrieving_a_specific_string_character">How you find what character is at a certain position in a string?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/First_steps/Useful_string_methods#Finding_a_substring_inside_a_string_and_extracting_it">How do you find and extract a specific substring from a string?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/First_steps/Useful_string_methods#Changing_case">How do you change the case of a string?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/First_steps/Useful_string_methods#Updating_parts_of_a_string">How do you replace one specific substring with another?</a></li>
+</ul>
+</div>
+
+<div class="column-half">
+<h3 id="数组">数组</h3>
+
+<ul>
+ <li><a href="/en-US/docs/Learn/JavaScript/First_steps/Arrays#Creating_an_array">怎么创建数组?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/First_steps/Arrays#Accessing_and_modifying_array_items">如何访问和修改数组中的元素?</a> (包括多维数组)</li>
+ <li><a href="/en-US/docs/Learn/JavaScript/First_steps/Arrays#Finding_the_length_of_an_array">怎么获取数组的长度?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/First_steps/Arrays#Adding_and_removing_array_items">怎么添加和移除数组中的元素?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/First_steps/Arrays#Converting_between_strings_and_arrays">如何将一个字符串拆分为数组,或将数组拼接成一个字符串?</a></li>
+</ul>
+
+<h3 id="JavaScript_调试">JavaScript 调试</h3>
+
+<ul>
+ <li><a href="/en-US/docs/Learn/JavaScript/First_steps/What_went_wrong#Types_of_error">什么是错误的基本类型?</a></li>
+ <li><a href="/en-US/docs/Learn/Common_questions/What_are_browser_developer_tools">什么是浏览器开发工具,如何使用它?</a></li>
+ <li><a href="/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/JavaScript#The_Console_API">怎么在JavaScript控制台打印值?</a></li>
+ <li><a href="/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/JavaScript#Using_the_JavaScript_debugger">怎么使用断点和其他JavaScript调试工具?</a></li>
+</ul>
+
+<p>For more information on JavaScript debugging, see <a href="/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/JavaScript">Handling common JavaScript problems</a>; also see <a href="/en-US/docs/Learn/JavaScript/First_steps/What_went_wrong#Other_common_errors">Other common errors</a> for a description of common errors.</p>
+
+<h3 id="Making_decisions_in_code">Making decisions in code</h3>
+
+<ul>
+ <li><a href="/en-US/docs/Learn/JavaScript/Building_blocks/conditionals">How do you execute different blocks of code, depending on a variable's value or other condition?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Building_blocks/conditionals#if_..._else_statements">How do you use if ...else statements?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Building_blocks/conditionals#Nesting_if_..._else">How do nest one decision block inside another?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Building_blocks/conditionals#Logical_operators_AND_OR_and_NOT">How do you use AND, OR, and NOT operators in JavaScript?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Building_blocks/conditionals#switch_statements">How do you conveniently handle a large number of choices for one condition?</a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/conditionals#Ternary_operator">How do you use a ternary operator to make a quick choice between two options based on a true or false test?</a></li>
+</ul>
+
+<h3 id="循环迭代">循环/迭代</h3>
+
+<ul>
+ <li><a href="/en-US/docs/Learn/JavaScript/Building_blocks/Looping_code">How do you run the same bit of code over and over again?</a></li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Looping_code#Exiting_loops_with_break">How do you exit a loop before the end if a certain condition is met?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Building_blocks/Looping_code#Skipping_iterations_with_continue">How do you skip to the next iteration of a loop if a certain condition is met?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Building_blocks/Looping_code#while_and_do_..._while">How do you use while and do ... while loops?</a></li>
+ <li>How to iterate over the elements in an array</li>
+ <li>How to iterate over the elements in a multidimensional array</li>
+ <li>How to iterate over the members in an object</li>
+ <li>How to iterate over the members of an object nested inside an array</li>
+</ul>
+</div>
+</div>
+
+<h2 id="中级用例">中级用例</h2>
+
+<div class="column-container">
+<div class="column-half">
+<h3 id="函数_2">函数</h3>
+
+<ul>
+ <li><a href="/en-US/docs/Learn/JavaScript/Building_blocks/Functions#Built-in_browser_functions">How do you find functions in the browser?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Building_blocks/Functions#Functions_versus_methods">What is the difference between a function and a method?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Building_blocks/Build_your_own_function">How do you create your own functions?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Building_blocks/Functions#Invoking_functions">How do you run (call, or invoke) a function?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Building_blocks/Functions#Anonymous_functions">What is an anonymous function?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Building_blocks/Functions#Function_parameters">How do you specify parameters (or arguments) when invoking a function?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Building_blocks/Functions#Function_scope_and_conflicts">What is function scope?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Building_blocks/Return_values">What are return values, and how do you use them?</a></li>
+</ul>
+
+<h3 id="对象">对象</h3>
+
+<ul>
+ <li><a href="/en-US/docs/Learn/JavaScript/Objects/Basics#Object_basics">How do you create an object?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Objects/Basics#Dot_notation">What is dot notation?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Objects/Basics#Bracket_notation">What is bracket notation?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Objects/Basics#Setting_object_members">How do you get and set the methods and properties of an object?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Objects/Basics#What_is_this">What is <code>this</code>, in the context of an object?</a></li>
+ <li><a href="/docs/Learn/JavaScript/Objects/Object-oriented_JS#Object-oriented_programming_from_10000_meters">What is object-oriented programming?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Objects/Object-oriented_JS#Constructors_and_object_instances">What are constructors and instances, and how do you create them?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Objects/Object-oriented_JS#Other_ways_to_create_object_instances">What different ways are there to create objects in JavaScript?</a></li>
+</ul>
+
+<h3 id="JSON">JSON</h3>
+
+<ul>
+ <li><a href="/en-US/docs/Learn/JavaScript/Objects/JSON#JSON_structure">How do you structure JSON data, and read it from JavaScript?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Objects/JSON#Loading_our_JSON">How can you load a JSON file into a page?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Objects/JSON#Converting_between_objects_and_text">How do you convert a JSON object to a text string, and back again?</a></li>
+</ul>
+</div>
+
+<div class="column-half">
+<h3 id="事件">事件</h3>
+
+<ul>
+ <li><a href="/en-US/docs/Learn/JavaScript/Building_blocks/Events#Event_handler_properties">What are event handlers and how do you use them?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Building_blocks/Events#Inline_event_handlers_%E2%80%94_don%27t_use_these">What are inline event handlers?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Building_blocks/Events#addEventListener()_and_removeEventListener()">What does the <code>addEventListener()</code> function do, and how do you use it?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Building_blocks/Events#What_mechanism_should_I_use">Which mechanism should I use to add event code to my web pages?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Building_blocks/Events#Event_objects">What are event objects, and how do you use them?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Building_blocks/Events#Preventing_default_behaviour">How do you prevent default event behaviour?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Building_blocks/Events#Event_bubbling_and_capture">How do events fire on nested elements? (event propagation, also related — event bubbling and capturing)</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Building_blocks/Events#Event_delegation">What is event delegation, and how does it work?</a></li>
+</ul>
+
+<h3 id="面向对象的JavaScript">面向对象的JavaScript</h3>
+
+<ul>
+ <li><a href="/en-US/docs/Learn/JavaScript/Objects/Object_prototypes">What are object prototypes?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Objects/Object_prototypes#The_constructor_property">What is the constructor property, and how can you use it?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Objects/Object_prototypes#Modifying_prototypes">How do you add methods to the constructor?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Objects/Inheritance">How do you create a new constructor that inherits its members from a parent constructor?</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Objects/Inheritance#Object_member_summary">When should you use inheritance in JavaScript?</a></li>
+</ul>
+
+<h3 id="Web_APIs">Web APIs</h3>
+
+<ul>
+ <li><a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Manipulating_documents#Active_learning_Basic_DOM_manipulation">How do you manipulate the DOM (e.g. adding or removing elements) using JavaScript?</a></li>
+</ul>
+
+<p> </p>
+</div>
+</div>
diff --git a/files/zh-cn/learn/javascript/index.html b/files/zh-cn/learn/javascript/index.html
new file mode 100644
index 0000000000..35969ae0a7
--- /dev/null
+++ b/files/zh-cn/learn/javascript/index.html
@@ -0,0 +1,61 @@
+---
+title: JavaScript
+slug: learn/JavaScript
+tags:
+ - JavaScript
+ - 初学者
+ - 编写脚本
+translation_of: Learn/JavaScript
+---
+<div>{{LearnSidebar}}</div>
+
+<p class="summary">{{Glossary('JavaScript')}} 编程语言允许你在 Web 页面上实现复杂的功能。如果你看到一个网页不仅仅显示静态的信息,而是显示依时间更新的内容,或者交互式地图,或者 2D/3D 动画图像,或者滚动的视频播放器,等等——你基本可以确定,这需要 JavaScript 的参与。</p>
+
+<h2 id="学习路线">学习路线</h2>
+
+<p>很多人认为,与相关技术如 <a href="https://developer.mozilla.org/en-US/docs/Learn/HTML">HTML</a> 和 <a href="https://developer.mozilla.org/en-US/docs/Learn/CSS">CSS</a> 相比,学习 JavaScript 更为困难。在尝试学习 JavaScript 之前,我们强烈建议你首先至少熟悉上述这两种技术,一些其他知识可能也会有帮助。你可以从以下模块开始学习之旅:</p>
+
+<ul>
+ <li><a href="/zh-CN/docs/Learn/Getting_started_with_the_web">开始了解 Web</a></li>
+ <li><a href="/zh-CN/docs/Web/Guide/HTML/Introduction">HTML 入门</a></li>
+ <li><a href="/zh-CN/docs/Learn/CSS/Introduction_to_CSS">CSS 入门</a></li>
+</ul>
+
+<p>拥有其他编程语言的经验也许会有帮助。</p>
+
+<p>熟悉 JavaScript 的基本概念之后,你将具备学习更多高级主题的能力,比如这些:</p>
+
+<ul>
+ <li>深入理解 JavaScript,如 <a href="/zh-CN/docs/Web/JavaScript/Guide">JavaScript 指南</a>中的内容</li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Web/API">Web APIs</a></li>
+</ul>
+
+<h2 id="模块">模块</h2>
+
+<p>本主题包含以下模块,我们建议你按照下列顺序阅读。</p>
+
+<dl>
+ <dt><a href="/zh-CN/docs/Learn/JavaScript/First_steps">JavaScript 第一步</a></dt>
+ <dd>作为 JavaScript 学习的第一个模块,在开始编写第一段代码之前,我们首先回答一些基础的问题,比如“JavaScript 是什么?”、“它的代码长什么样?”、以及“它能做什么?”。之后我们会详细讨论一些 JavaScript 的关键功能,比如变量、字符串、数字、数组等。</dd>
+ <dt><a href="/zh-CN/docs/Learn/JavaScript/Building_blocks">构成 JavaScript 的“砖块”</a></dt>
+ <dd>在这个模块中,我们继续介绍 JavaScript 的关键的基础功能,并逐渐将注意力转移到常见类型的代码块,比如条件语句、循环、函数、以及事件等。你应该已经遇到过这些概念,而这里我们将正式学习。</dd>
+ <dt><a href="/zh-CN/docs/Learn/JavaScript/Objects">JavaScript 对象初识</a></dt>
+ <dd>在 JavaScript 中,绝大多数东西都是对象;从作为 JavaScript 核心功能的字符串和数组,到建立在 JavaScript 之上的浏览器 API,无一不是对象。你甚至可以自己创建对象,将相关的函数和变量封装打包。想要进一步学习 JavaScript 语言知识、写出高效的代码的话,理解这种面向对象的特性是必不可少的。这个模块将帮助你了解“对象”,我们将详细介绍对象的设计思想和语法、如何创建对象,并解释 JSON 数据是什么、如何使用。</dd>
+ <dt><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous">异步JavaScript</a></dt>
+ <dd>
+ <p>这个模块介绍异步JavaScript: 为什么重要,如何用它来处理 可能引起阻塞的操作(比如从服务器获取资源)</p>
+ </dd>
+ <dt><a href="/zh-CN/docs/Learn/JavaScript/Client-side_web_APIs">客户端 Web API</a></dt>
+ <dd>为网站或应用编写客户端 JavaScript 脚本时,你很难不用到 Web API 接口。这些接口允许你一定程度上操纵网页所运行在的浏览器和操作系统、甚至来自其他网站和服务的数据。在这个模块中,我们将了解有哪些 API,并学习使用开发过程中最常见的 API。</dd>
+</dl>
+
+<h2 id="解决常见的JavaScript问题">解决常见的JavaScript问题</h2>
+
+<p><a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Howto">解决常见的JavaScript问题</a> 提供一些链接,解释如何使用JavaScript来解决创建网页时非常常见的问题。</p>
+
+<h2 id="另见">另见</h2>
+
+<dl>
+ <dt><a href="https://www.youtube.com/user/codingmath">Coding math</a></dt>
+ <dd>由 <a href="https://twitter.com/bit101">Keith Peters</a> 制作的一个优秀的视频教程系列,向你传授高效编程所需的必备技能。</dd>
+</dl>
diff --git a/files/zh-cn/learn/javascript/objects/basics/index.html b/files/zh-cn/learn/javascript/objects/basics/index.html
new file mode 100644
index 0000000000..48c8646a07
--- /dev/null
+++ b/files/zh-cn/learn/javascript/objects/basics/index.html
@@ -0,0 +1,243 @@
+---
+title: JavaScript 对象基础
+slug: Learn/JavaScript/Objects/Basics
+translation_of: Learn/JavaScript/Objects/Basics
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{NextMenu("Learn/JavaScript/Objects/Object-oriented_JS", "Learn/JavaScript/Objects")}}</div>
+
+<p class="summary">在这学习JavaScript的对象的首篇文章中,我们将会学习有关对象基础的语法,并且回顾一些之前学过的JavaScript的一些特点,使你明白你所使用过的一些功能实际上是由对象提供的。</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">前提:</th>
+ <td>基础计算机基础, 了解基础的HTML 和 CSS, 熟悉 JavaScript 基础 (基础知识看这里 <a href="/en-US/docs/Learn/JavaScript/First_steps">First steps</a> 和这里 <a href="/en-US/docs/Learn/JavaScript/Building_blocks">Building blocks</a>).</td>
+ </tr>
+ <tr>
+ <th scope="row">目标:</th>
+ <td>理解面向对象编程背后的基础理论, 怎样理解 JavaScript ("一切皆对象most things are objects"), 如何开始使用JavaScript对象.</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="对象基础">对象基础</h2>
+
+<p>对象是一个包含相关数据和方法的集合(通常由一些变量和函数组成,我们称之为对象里面的属性和方法),让我们通过一个例子来了解它们。</p>
+
+<p>首先, 将 <a href="https://github.com/mdn/learning-area/blob/master/javascript/oojs/introduction/oojs.html">oojs.html</a> 文件复制到本地. 此文件包含非常少 — 一个供我们写源代码的 {{HTMLElement("script")}} 标签, 一个供我们输入示例指令的 {{HTMLElement("input")}} 标签,当页面被渲染时, 一些变量定义, 一个输出任何输入到{{HTMLElement("input")}}的内容输出到{{HTMLElement("p")}}标签的函数。我们用这个文件做为基础探索对象的基础语法.</p>
+
+<p>如同Javascript中的很多东西一样,创建一个对象通常先定义初始化变量。 尝试在您已有的文件中JavaScript代码下面输入以下内容, 保存刷新页面:</p>
+
+<pre class="brush: js">var person = {};</pre>
+
+<p>如果你在浏览器控制台输入person,然后按下Enter(确认)键,你会得到如下结果:</p>
+
+<pre class="brush: js">[object Object]</pre>
+
+<p>恭喜, 你刚创建了你的第一个对象. 干的漂亮! 但这是一个空对象,所以我们做不了更多的事情。像下面一样更新下我们的对象:</p>
+
+<pre class="brush: js">var person = {
+ name : ['Bob', 'Smith'],
+ age : 32,
+ gender : 'male',
+ interests : ['music', 'skiing'],
+ bio : function() {
+ alert(this.name[0] + ' ' + this.name[1] + ' is ' + this.age + ' years old. He likes ' + this.interests[0] + ' and ' + this.interests[1] + '.');
+ },
+ greeting: function() {
+ alert('Hi! I\'m ' + this.name[0] + '.');
+ }
+};
+</pre>
+
+<p>保存刷新后, 尝试在你的浏览器控制台输入下面的内容:</p>
+
+<pre class="brush: js">person.name[0]
+person.age
+person.interests[1]
+person.bio()
+person.greeting()</pre>
+
+<p>现在在你的对象里得到了一些数据和功能(functionality),现在可以通过简单的语法访问他们了!</p>
+
+<div class="note">
+<p><strong>Note</strong>:如果做上面的东西遇到了麻烦,尝试拿你的代码与我们的版本做对比——对比 <a href="https://github.com/mdn/learning-area/blob/master/javascript/oojs/introduction/oojs-finished.html">oojs-finished.html</a> (也可以 <a href="http://mdn.github.io/learning-area/javascript/oojs/introduction/oojs-finished.html">看实际效果</a>)。一个对于初学者很常见的错误是在最后一个成员后面多了一个逗号,这会引发错误。</p>
+</div>
+
+<p>所以发生了什么?一个对象由许多的成员组成,每一个成员都拥有一个名字(像上面的name、age),和一个值(如['Bob', 'Smith']、32)。每一个名字/值(name/value)对被逗号分隔开,并且名字和值之间由冒号(:)分隔,语法规则如下所示:</p>
+
+<pre class="brush: js">var objectName = {
+ member1Name : member1Value,
+ member2Name : member2Value,
+ member3Name : member3Value
+}</pre>
+
+<p>对象成员的值可以是任意的,在我们的person对象里有字符串(string),数字(number),两个数组(array),两个函数(function)。前4个成员是资料项目,被称为对象的属性(property),后两个成员是函数,允许对象对资料做一些操作,被称为对象的方法(method)</p>
+
+<p>一个如上所示的对象被称之为对象的字面量(literal)——手动的写出对象的内容来创建一个对象。不同于从类实例化一个对象,我们会在后面学习这种方式。</p>
+
+<p>当你想要传输一些有结构和关联的资料时常见的方式是使用字面量来创建一个对象,举例来说,发起一个请求到服务器以存储一些数据到数据库,发送一个对象要比分别发送这些数据更有效率,而且比起数组更为易用,因为你使用名字(name)来标识这些资料。</p>
+
+<h2 id="点表示法">点表示法</h2>
+
+<p>在上面的例子中,你使用了点表示法(dot notation)来访问对象的属性和方法。对象的名字表现为一个命名空间(namespace),它必须写在第一位——当你想访问对象内部的属性或方法时,然后是一个点(.),紧接着是你想要访问的项目,标识可以是简单属性的名字(name),或者是数组属性的一个子元素,又或者是对象的方法调用。如下所示:</p>
+
+<pre class="brush: js">person.age
+person.interests[1]
+person.bio()</pre>
+
+<h3 id="子命名空间">子命名空间</h3>
+
+<p>可以用一个对象来做另一个对象成员的值。例如将name成员</p>
+
+<pre class="brush: js">name : ['Bob', 'Smith'],</pre>
+
+<p>改成</p>
+
+<pre class="brush: js">name : {
+ first : 'Bob',
+ last : 'Smith'
+},</pre>
+
+<p>这样,我们实际上创建了一个子命名空间,听起来有点复杂,但用起来很简单,你只需要链式的再使用一次点表示法,像这样:</p>
+
+<pre class="brush: js">person.name.first
+person.name.last</pre>
+
+<p><strong>注意</strong>:你需要改变你之前的代码,从</p>
+
+<pre class="brush: js">name[0]
+name[1]</pre>
+
+<p>改成</p>
+
+<pre class="brush: js">name.first
+name.last</pre>
+
+<p>否则,你的方法不再有效。</p>
+
+<h2 id="括号表示法">括号表示法</h2>
+
+<p>另外一种访问属性的方式是使用括号表示法(bracket notation),替代这样的代码</p>
+
+<pre class="brush: js">person.age
+person.name.first</pre>
+
+<p>使用如下所示的代码:</p>
+
+<pre class="brush: js">person['age']
+person['name']['first']</pre>
+
+<p>这看起来很像访问一个数组的元素,从根本上来说是一回事儿,你使用了关联了值的名字,而不是索引去选择元素。难怪对象有时被称之为关联数组(associative array)了——对象做了字符串到值的映射,而数组做的是数字到值的映射。</p>
+
+<h2 id="设置对象成员">设置对象成员</h2>
+
+<p>目前我们仅仅看到了如何访问对象的成员,而你其实也可以设置对象成员的值,通过声明你要设置的成员,像这样:</p>
+
+<pre class="brush: js">person.age = 45
+person['name']['last'] = 'Cratchit'</pre>
+
+<p>尝试这些代码,然后再查看这些成员是否已经被改变了</p>
+
+<pre class="brush: js">person.age
+person['name']['last']</pre>
+
+<p>设置成员并不意味着你只能更新已经存在的属性的值,你完全可以创建新的成员,尝试以下代码:</p>
+
+<pre class="brush: js">person['eyes'] = 'hazel'
+person.farewell = function() { alert("Bye everybody!") }</pre>
+
+<p>现在你可以测试你新创建的成员</p>
+
+<pre class="brush: js">person['eyes']
+person.farewell()</pre>
+
+<p>括号表示法一个有用的地方是它不仅可以动态的去设置对象成员的值,还可以动态的去设置成员的名字。</p>
+
+<p>比如说,我们想让用户能够在他们的数据里存储自己定义的值类型,通过两个input框来输入成员的名字和值,通过以下代码获取用户输入的值:</p>
+
+<pre class="brush: js">var myDataName = nameInput.value
+var myDataValue = nameValue.value</pre>
+
+<p>我们可以这样把这个新的成员的名字和值加到person对象里:</p>
+
+<pre class="brush: js">person[myDataName] = myDataValue</pre>
+
+<p>为了测试这个功能,尝试在你的代码里添加以下几行,就在person对象的右花括号的下面:</p>
+
+<pre class="brush: js">var myDataName = 'height'
+var myDataValue = '1.75m'
+person[myDataName] = myDataValue</pre>
+
+<p>现在,保存并刷新,在输入框里输入以下代码:</p>
+
+<pre class="brush: js">person.height</pre>
+
+<p>这是使用点表示法无法做到的,点表示法只能接受字面量的成员的名字,不接受变量作为名字。</p>
+
+<h2 id="this的含义">"this"的含义</h2>
+
+<p>你也许在我们的方法里注意到了一些奇怪的地方,看这个例子:</p>
+
+<pre class="brush: js">greeting: function() {
+ alert('Hi! I\'m ' + this.name.first + '.');
+}</pre>
+
+<p>你也许想知道"this"是什么,关键字"this"指向了当前代码运行时的对象( 原文:the current object the code is being written inside )——这里即指person对象,为什么不直接写person呢?当你学到下一篇<a href="/en-US/docs/Learn/JavaScript/Objects/Object-oriented_JS">Object-oriented JavaScript for beginners</a>文章时,我们开始使用构造器(constructor)时,"this"是非常有用的——它保证了当代码的上下文(context)改变时变量的值的正确性(比如:不同的person对象拥有不同的name这个属性,很明显greeting这个方法需要使用的是它们自己的name)。</p>
+
+<p>让我们以两个简单的person对象来说明:</p>
+
+<pre class="brush: js">var person1 = {
+ name : 'Chris',
+ greeting: function() {
+ alert('Hi! I\'m ' + this.name + '.');
+ }
+}
+
+var person2 = {
+ name : 'Brian',
+ greeting: function() {
+ alert('Hi! I\'m ' + this.name + '.');
+ }
+}</pre>
+
+<p>在这里,person1.greeting()会输出:"Hi! I'm Chris.";person2.greeting()会输出:"Hi! I'm Brain.",即使greeting这个方法的代码是一样的。就像我们之前说的,this 指向了代码所在的对象(其实代码运行时所在的对象)。在字面量的对象里this看起来不是很有用,但是当你动态创建一个对象(例如使用构造器)时它是非常有用的,之后你会更清楚它的用途。</p>
+
+<h2 id="你一直在使用对象">你一直在使用对象</h2>
+
+<p>当你使用过这些例子之后,你可能会发现你对点表示法并不陌生,这是因为我们在这个课程里一直在使用它,每次我们学习的示例使用浏览器内建的API和JavaScript的一些对象时,我们就在使用对象,因为,这些功能是由跟我们所看到的对象同样的结构来构建的,虽然比我们自己定义的要复杂许多。</p>
+
+<p>所以当我们这样使用字符串的方法时:</p>
+
+<pre class="brush: js">myString.split(',');</pre>
+
+<p>你正在使用一个字符串实例上可用的方法,你随时都可以在代码里使用字面量创建一个字符串,字符串会自动的被创建为字符串(<code><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/String">String</a></code>)的实例,因此会有一些常见的方法和属性可用。</p>
+
+<p>当你这样访问document对象时:</p>
+
+<pre class="brush: js">var myDiv = document.createElement('div');
+var myVideo = document.querySelector('video');</pre>
+
+<p>你正在使用<code><a href="/en-US/docs/Web/API/Document">Document</a></code>实例上可用的方法。每个页面在加载完毕后,会有一个Document的实例被创建,叫做document,它代表了整个页面的结构,内容和一些功能,比如页面的URL。同样的,这意味document有一些可用的方法和属性。</p>
+
+<p>这同样适用许多其他内建的对象或API,你使用过有—— <code><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array">Array</a></code>,<code><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math">Math</a></code>, 等。</p>
+
+<p>请注意内建的对象或API不会总是自动地创建对象的实例,举例来说,这个 <a href="/en-US/docs/Web/API/Notifications_API">Notifications API</a>——允许浏览器发起系统通知,需要你为每一个你想发起的通知都使用构造器进行实例化。尝试在JavaScript终端里输入以下代码</p>
+
+<pre class="brush: js">var myNotification = new Notification('Hello!');</pre>
+
+<p>我们会在之后的文章里学习到构造器。</p>
+
+<div class="note">
+<p><strong>Note</strong>: 这样来理解对象之间通过消息传递来通信是很有用的——当一个对象想要另一个执行某种动作时,它通常会通过那个对象的方法给其发送一些信息,并且等待回应,即我们所知的返回值。</p>
+</div>
+
+<h2 id="总结">总结</h2>
+
+<p>恭喜,你已经阅读到了我们有关JavaScript对象的第一篇文章的末尾,你现在应该对如何在JavaScript中使用对象有了很好的认识,包括你自己创建一个简单的对象。你应该清楚对象有利于存储一些相关联的数据和函数,如果你尝试以分开的方式去保存person对象包含的所有的属性和方法,这是令人沮丧且效率低下的,而且会有很多的变量和函数之间同名的风险。对象使我们将一些信息安全地锁在了它们自己的包内,防止它们被损坏。</p>
+
+<p>在下一篇文章,我们将会了解面对对象编程(OOP)理论,和许多在JavaScript中使用的技巧。</p>
+
+<p>{{NextMenu("Learn/JavaScript/Objects/Object-oriented_JS", "Learn/JavaScript/Objects")}}</p>
diff --git a/files/zh-cn/learn/javascript/objects/index.html b/files/zh-cn/learn/javascript/objects/index.html
new file mode 100644
index 0000000000..cb1c75af18
--- /dev/null
+++ b/files/zh-cn/learn/javascript/objects/index.html
@@ -0,0 +1,51 @@
+---
+title: JavaScript 对象入门
+slug: Learn/JavaScript/Objects
+tags:
+ - CodingScripting
+ - JavaScript
+ - 初学者
+ - 学习
+ - 对象
+ - 指南
+ - 教程
+ - 评估
+translation_of: Learn/JavaScript/Objects
+---
+<div>{{LearnSidebar}}</div>
+
+<p class="summary">在 JavaScript 中,大多数事物都是对象, 从作为核心功能的字符串和数组,到建立在 JavaScript 之上的浏览器 {{Glossary("API", "API")}} 。你甚至可以自己创建对象,将相关的函数和变量高效地封装打包成便捷的数据容器。对于进一步学习 JavaScript 语言知识而言,理解这种面向对象(object-oriented, OO)的特性是必不可少的,所以,我们提供了这个模块来帮助你了解这一切。这里我们会先详细介绍对象的理论和语法,再介绍如何创建对象。</p>
+
+<h2 id="预备知识">预备知识</h2>
+
+<p>开始这个模块之前,你应当已经对 HTML 和 CSS 有所了解。我们建议你通读 <a href="/zh-CN/docs/Web/Guide/HTML/Introduction">HTML 入门</a>和 <a href="/zh-CN/docs/Learn/CSS/Introduction_to_CSS">CSS 入门</a>模块,再开始了解 JavaScript。</p>
+
+<p>详细了解 JavaScript 对象之前,你应当已经对 JavaScript 基础有所熟悉。尝试这个模块之前,请通读 <a href="/zh-CN/docs/Learn/JavaScript/First_steps">JavaScript 第一步</a> 和 <a href="/zh-CN/docs/Learn/JavaScript/Building_blocks">JavaScript基础要件 </a></p>
+
+<div class="note">
+<p><strong>注意</strong>:如果您无法在当前使用的电脑/平板/其他设备上创建自己的文件,可以使用在线编程网站如 <a href="http://jsbin.com/">JSBin</a> 或 <a href="https://thimble.mozilla.org/">Thimble</a>,来试验文章中的(大多数)代码。</p>
+</div>
+
+<h2 id="指南">指南</h2>
+
+<dl>
+ <dt><a href="/zh-CN/docs/Learn/JavaScript/Objects/Basics">对象基础</a></dt>
+ <dd>在了解 JavaScript 对象的第一篇文章中,我们将介绍 JavaScript 对象的语法,并回顾先前课程中讲过的某些 JavaScript 功能。你会发现,你已经在使用的很多功能本质上都是对象。</dd>
+ <dt><a href="/zh-CN/docs/Learn/JavaScript/Objects/Object-oriented_JS">适合初学者的面向对象 JavaScript</a></dt>
+ <dd>了解基础后,我们将关注面向对象 JavaScript (OOJS)。本文将介绍面向对象编程 (OOP) 的基本理论,然后讲解 JavaScript 如何通过构造器 (constructor) 函数模拟对象类别 (class)、如何创建对象实例 (instance)。</dd>
+ <dt><a href="/zh-CN/docs/Learn/JavaScript/Objects/Object_prototypes">对象原型</a></dt>
+ <dd>通过原型 (prototype) 这种机制,JavaScript 中的对象从其他对象继承功能特性;这种继承机制与经典的面向对象编程语言不同。本文将探讨这些差别,解释原型链如何工作,并了解如何通过 <code>prototype</code> 属性向已有的构造器添加方法。</dd>
+ <dt><a href="/zh-CN/docs/Learn/JavaScript/Objects/Inheritance">JavaScript 中的继承</a></dt>
+ <dd>了解了 OOJS 的大多数细节之后,本文将介绍如何创建“子”对象类别(构造器)并从“父”类别中继承功能。此外,我们还会针对何时何处使用 OOJS 给出建议。</dd>
+ <dt><a href="/zh-CN/docs/Learn/JavaScript/Objects/JSON">使用 JSON 数据</a></dt>
+ <dd>JavaScript Object Notation (JSON) 是一种将结构化数据表达为 JavaScript 对象的标准格式,其常用于在网站上表达或传输数据(比如:从服务器向客户端发送数据,使之显示在网页上)。你会经常遇到它,因此本文将告诉你如何在 JavaScript 中使用 JSON 数据,包括访问 JSON 对象中的数据条目、编写自己的 JSON 数据等等。</dd>
+ <dt><a href="/zh-CN/docs/Learn/JavaScript/Objects/Object_building_practice">构建对象实战</a></dt>
+ <dd>在前面的文章中我们了解了 JavaScript 对象基本理论和语法,为你打下坚实的基础。本文中你需要进行实战练习,通过构建自定义 JavaScript 对象的实践过程,编写一个有趣而又多彩的程序——“彩色弹跳球”。</dd>
+</dl>
+
+<h2 id="学习评估">学习评估</h2>
+
+<dl>
+ <dt><a href="/zh-CN/docs/Learn/JavaScript/Objects/Adding_bouncing_balls_features">向“弹跳球”演示程序添加新功能</a></dt>
+ <dd>在这个评估中,你需要以上一篇文章中的“弹跳球”演示为起点,向这个演示程序新增一些有趣的功能。</dd>
+</dl>
diff --git a/files/zh-cn/learn/javascript/objects/inheritance/index.html b/files/zh-cn/learn/javascript/objects/inheritance/index.html
new file mode 100644
index 0000000000..c7b564d978
--- /dev/null
+++ b/files/zh-cn/learn/javascript/objects/inheritance/index.html
@@ -0,0 +1,251 @@
+---
+title: JavaScript 中的继承
+slug: Learn/JavaScript/Objects/Inheritance
+tags:
+ - JavaScript
+ - OOJS
+ - 原型
+ - 对象
+ - 继承
+ - 面向对象JS
+translation_of: Learn/JavaScript/Objects/Inheritance
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects")}}</div>
+
+<p class="summary">了解了 OOJS 的大多数细节之后,本文将介绍如何创建“子”对象类别(构造器)并从“父”类别中继承功能。此外,我们还会针对何时何处使用 OOJS 给出建议。</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">预备知识:</th>
+ <td>基本的计算机素养,对 HTML 和 CSS 有基本的理解,熟悉 JavaScript 基础(参见 <a href="https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/First_steps">First steps</a> 和 <a href="https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Building_blocks">Building blocks</a>)以及面向对象的JavaScript (OOJS) 基础(参见 <a href="https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Object-oriented/Introduction">Introduction to objects</a>)。</td>
+ </tr>
+ <tr>
+ <th scope="row">目标:</th>
+ <td>理解在 JavaScript 中如何实现继承。</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="原型式的继承">原型式的继承</h2>
+
+<p>到目前为止我们已经了解了一些关于原型链的实现方式以及成员变量是如何通过它来实现继承,但是之前涉及到的大部分都是浏览器内置函数(比如 <code><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String">String</a></code>、<code><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Date">Date</a></code>、<code><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Number">Number</a></code> 和 <code><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array">Array</a></code>),那么我们如何创建一个继承自另一对象的JavaScript对象呢?</p>
+
+<p>正如前面课程所提到的,有些人认为JavaScript并不是真正的面向对象语言,在经典的面向对象语言中,您可能倾向于定义类对象,然后您可以简单地定义哪些类继承哪些类(参考<a href="http://www.tutorialspoint.com/cplusplus/cpp_inheritance.htm">C++ inheritance</a>里的一些简单的例子),JavaScript使用了另一套实现方式,继承的对象函数并不是通过复制而来,而是通过原型链继承(通常被称为 <strong>原型式继承 —— </strong><strong>prototypal inheritance<font face="Consolas, Liberation Mono, Courier, monospace">)</font></strong>。</p>
+
+<p>让我们通过具体的例子来解释上述概念</p>
+
+<h2 id="开始">开始</h2>
+
+<p>首先,将<a href="https://github.com/mdn/learning-area/blob/master/javascript/oojs/advanced/oojs-class-inheritance-start.html">oojs-class-inheritance-start.html</a>文件复制到您本地(也可以 <a href="http://mdn.github.io/learning-area/javascript/oojs/advanced/oojs-class-inheritance-start.html">在线运行</a> ),其中您能看到一个只定义了一些属性的<code>Person()</code>构造器,与之前通过模块来实现所有功能的Person的构造器类似。</p>
+
+<pre class="brush: js notranslate">function Person(first, last, age, gender, interests) {
+ this.name = {
+ first,
+ last
+ };
+ this.age = age;
+ this.gender = gender;
+ this.interests = interests;
+};
+</pre>
+
+<p><em>所有</em>的方法都定义在构造器的原型上,比如:</p>
+
+<pre class="brush: js notranslate">Person.prototype.greeting = function() {
+ alert('Hi! I\'m ' + this.name.first + '.');
+};
+</pre>
+
+<div class="note">
+<p>注意:在源代码中,你可以看到已定义的<code>bio()</code>和<code>farewell()</code>方法。随后,你将看到它们被其他的构造器所继承。</p>
+</div>
+
+<p>比如我们想要创建一个<code>Teacher</code>类,就像我们前面在面向对象概念解释时用的那个一样。这个类会继承<code>Person</code>的所有成员,同时也包括:</p>
+
+<ol>
+ <li>一个新的属性,<code>subject</code>——这个属性包含了教师教授的学科。</li>
+ <li>一个被更新的<code>greeting()</code>方法,这个方法打招呼听起来比一般的<code>greeting()</code>方法更正式一点——对于一个教授一些学生的老师来说。</li>
+</ol>
+
+<h2 id="定义_Teacher_构造器函数">定义 Teacher() 构造器函数</h2>
+
+<p>我们要做的第一件事是创建一个<code>Teacher()</code>构造器——将下面的代码加入到现有代码之下:</p>
+
+<pre class="brush: js notranslate">function Teacher(first, last, age, gender, interests, subject) {
+ Person.call(this, first, last, age, gender, interests);
+
+ this.subject = subject;
+}
+</pre>
+
+<p>这在很多方面看起来都和Person的构造器很像,但是这里有一些我们从没见过的奇怪玩意——<code><a href="/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/call">call()</a></code>函数。基本上,这个函数允许您调用一个在这个文件里别处定义的函数。第一个参数指明了在您运行这个函数时想对“<code>this</code>”指定的值,也就是说,您可以重新指定您调用的函数里所有“<code>this</code>”指向的对象。其他的变量指明了所有目标函数运行时接受的参数。</p>
+
+<div class="note">
+<p><strong>注:</strong>在这个例子里我们在创建一个新的对象实例时同时指派了继承的所有属性,但是注意您需要在构造器里将它们作为参数来指派,即使实例不要求它们被作为参数指派(比如也许您在创建对象的时候已经得到了一个设置为任意值的属性)</p>
+</div>
+
+<p>所以在这个例子里,我们很有效的在<code>Teacher()</code>构造函数里运行了<code>Person()</code>构造函数(见上文),得到了和在<code>Teacher()</code>里定义的一样的属性,但是用的是传送给<code>Teacher()</code>,而不是<code>Person()</code>的值(我们简单使用这里的<code>this</code>作为传给<code>call()</code>的<code>this</code>,意味着<code>this</code>指向<code>Teacher()</code>函数)。</p>
+
+<p>在构造器里的最后一行代码简单地定义了一个新的<code>subject</code>属性,这将是教师会有的,而一般人没有的属性。</p>
+
+<p>顺便提一下,我们本也可以这么做:</p>
+
+<pre class="brush: js notranslate">function Teacher(first, last, age, gender, interests, subject) {
+ this.name = {
+ first,
+ last
+ };
+ this.age = age;
+ this.gender = gender;
+ this.interests = interests;
+ this.subject = subject;
+}
+</pre>
+
+<p>但是这只是重新定义了一遍属性,并不是将他们从Person()中继承过来的,所以这违背了我们的初衷。这样写也会需要更长的代码。</p>
+
+<h3 id="从无参构造函数继承">从无参构造函数继承</h3>
+
+<p>请注意,如果您继承的构造函数不从传入的参数中获取其属性值,则不需要在<code>call()</code>中为其指定其他参数。所以,例如,如果您有一些相当简单的东西:</p>
+
+<pre class="brush: js notranslate">function Brick() {
+ this.width = 10;
+ this.height = 20;
+}</pre>
+
+<p>您可以这样继承<code>width</code>和<code>height</code>属性(以及下面描述的其他步骤):</p>
+
+<pre class="brush: js notranslate">function BlueGlassBrick() {
+ Brick.call(this);
+
+ this.opacity = 0.5;
+ this.color = 'blue';
+}</pre>
+
+<p>请注意,我们仅传入了<code>this</code>到<code>call()</code>中 - 不需要其他参数,因为我们不会继承通过参数设置的父级的任何属性。</p>
+
+<h2 id="设置_Teacher_的原型和构造器引用">设置 Teacher() 的原型和构造器引用</h2>
+
+<p>到目前为止一切看起来都还行,但是我们遇到问题了。我们已经定义了一个新的构造器,这个构造器默认有一个空的原型属性。我们需要让<code>Teacher()</code>从<code>Person()</code>的原型对象里继承方法。我们要怎么做呢?</p>
+
+<ol>
+ <li>在您先前添加的代码的下面增加以下这一行:
+ <pre class="brush: js notranslate">Teacher.prototype = Object.create(Person.prototype);</pre>
+ 这里我们的老朋友<code><a href="/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/create">create()</a></code>又来帮忙了——在这个例子里我们用这个函数来创建一个和<code>Person.prototype</code>一样的新的原型属性值(这个属性指向一个包括属性和方法的对象),然后将其作为<code>Teacher.prototype</code>的属性值。这意味着<code>Teacher.prototype</code>现在会继承<code>Person.prototype</code>的所有属性和方法。</li>
+ <li>接下来,在我们动工之前,还需要完成一件事 — 现在<code>Teacher()</code>的<code>prototype</code>的<code>constructor</code>属性指向的是<code>Person()</code>, 这是由我们生成<code>Teacher()</code>的方式决定的。(这篇 <a href="https://stackoverflow.com/questions/8453887/why-is-it-necessary-to-set-the-prototype-constructor">Stack Overflow post</a> 文章会告诉您详细的原理) — 将您写的页面在浏览器中打开,进入JavaScript控制台,输入以下代码来确认:
+ <pre class="brush: js notranslate">Teacher.prototype.constructor</pre>
+ </li>
+ <li>这或许会成为很大的问题,所以我们需要将其正确设置——您可以回到源代码,在底下加上这一行代码来解决:
+ <pre class="brush: js notranslate">Teacher.prototype.constructor = Teacher;</pre>
+ </li>
+ <li>当您保存并刷新页面以后,输入<code>Teacher.prototype.constructor</code>就会得到<code>Teacher()</code>。</li>
+</ol>
+
+<div class="note">
+<p><strong>注:</strong>每一个函数对象(<code>Function</code>)都有一个<code>prototype</code>属性,并且<em>只有</em>函数对象有<code>prototype</code>属性,因为<code>prototype</code>本身就是定义在<code>Function</code>对象下的属性。当我们输入类似<code>var person1=new Person(...)</code>来构造对象时,JavaScript实际上参考的是<code>Person.prototype</code>指向的对象来生成<code>person1</code>。另一方面,<code>Person()</code>函数是<code>Person.prototype</code>的构造函数,也就是说<code>Person===Person.prototype.constructor</code>(不信的话可以试试)。</p>
+
+<p>在定义新的构造函数<code>Teacher</code>时,我们通过<code>function.call</code>来调用父类的构造函数,但是这样无法自动指定<code>Teacher.prototype</code>的值,这样<code>Teacher.prototype</code>就只能包含在构造函数里构造的属性,而没有方法。因此我们利用<code>Object.create()</code>方法将<code>Person.prototype</code>作为<code>Teacher.prototype</code>的原型对象,并改变其构造器指向,使之与<code>Teacher</code>关联。</p>
+
+<p><em>任何</em>您想要被继承的方法都应该定义在构造函数的<code>prototype</code>对象里,并且<em>永远</em>使用父类的<code>prototype</code>来创造子类的<code>prototype</code>,这样才不会打乱类继承结构。</p>
+</div>
+
+<h2 id="向_Teacher_添加一个新的greeting函数">向 Teacher() 添加一个新的greeting()函数</h2>
+
+<p>为了完善代码,您还需在构造函数<code>Teacher()</code>上定义一个新的函数<code>greeting()</code>。最简单的方法是在Teacher的原型上定义它—把以下代码添加到您代码的底部:</p>
+
+<pre class="brush: js notranslate">Teacher.prototype.greeting = function() {
+ var prefix;
+
+ if(this.gender === 'male' || this.gender === 'Male' || this.gender === 'm' || this.gender === 'M') {
+ prefix = 'Mr.';
+ } else if(this.gender === 'female' || this.gender === 'Female' || this.gender === 'f' || this.gender === 'F') {
+ prefix = 'Mrs.';
+ } else {
+ prefix = 'Mx.';
+ }
+
+ alert('Hello. My name is ' + prefix + ' ' + this.name.last + ', and I teach ' + this.subject + '.');
+};</pre>
+
+<p>这样就会出现老师打招呼的弹窗,老师打招呼会使用条件结构判断性别从而使用正确的称呼。</p>
+
+<h2 id="范例尝试">范例尝试</h2>
+
+<p>现在我们来键入代码,将下面的代码放到您的 JavaScript 代码下面从而来创建一个 <code>Teacher()</code> 对象实例。</p>
+
+<pre class="brush: js notranslate">var teacher1 = new Teacher('Dave', 'Griffiths', 31, 'male', ['football', 'cookery'], 'mathematics');</pre>
+
+<p>当您保存代码并刷新的时候,试一下您的老师实例的属性和方法:</p>
+
+<pre class="brush: js notranslate">teacher1.name.first;
+teacher1.interests[0];
+teacher1.bio();
+teacher1.subject;
+teacher1.greeting();</pre>
+
+<p>前面三个进入到从<code>Person()</code>的构造器 继承的属性和方法,后面两个则是只有<code>Teacher()</code>的构造器才有的属性和方法。</p>
+
+<div class="note">
+<p><strong>注:</strong>如果您在这里遇到了问题,请对比您的代码与我们的<a href="https://github.com/mdn/learning-area/blob/master/javascript/oojs/advanced/oojs-class-inheritance-finished.html">完成版本</a>(或查看<a href="http://mdn.github.io/learning-area/javascript/oojs/advanced/oojs-class-inheritance-finished.html">可运行的在线示例</a>)。</p>
+</div>
+
+<p>我们在这里讲述的技巧并不是 JavaScript 中创建继承类的唯一方式,但是这个技巧也还不错,非常好地告诉了您如何在 JavaScript 中实行继承操作。</p>
+
+<p>您可能对在 JavaScript中使用其他方法来实行继承会感兴趣(参见 <a href="/en-US/docs/Web/JavaScript/Reference/Classes">Classes</a>)。我们没有覆盖那些内容,因为并不是每种浏览器都会支持这些方法。我们在这一系列文章中介绍的所有其他方法都会被 IE9 支持或者更老的浏览器支持,也有一些方法可以支持更老的浏览器。</p>
+
+<p>一个常用的方法是使用 JavaScript 语言库——最热门的一些库提供一些方法让我们更快更好地实行继承。比如 <a href="http://coffeescript.org/#classes">CoffeeScript</a> 就提供一些类和扩展。</p>
+
+<h2 id="更多练习">更多练习</h2>
+
+<p>在我们的 <a href="/zh-CN/docs/Learn/JavaScript/Objects/Object-oriented_JS#Object-oriented_programming_from_10000_meters">OOP theory section</a> 模块中, 我们也将学生类作为一个概念,继承了 Person 所有的属性和方法,也有一个不同的打招呼的方法(比老师的打招呼轻松随意一些)。您可以自己尝试一下如何实现。</p>
+
+<div class="note">
+<p><strong>注:</strong>如果你编写时遇到困难,代码无法运行,那么可以查看我们的<a href="https://github.com/mdn/learning-area/blob/master/javascript/oojs/advanced/oojs-class-inheritance-student.html">完成版本</a>(也可查看 <a href="http://mdn.github.io/learning-area/javascript/oojs/advanced/oojs-class-inheritance-student.html">可运行的在线示例</a>)。</p>
+</div>
+
+<h2 id="对象成员总结">对象成员总结</h2>
+
+<p>总结一下,您应该基本了解了以下三种属性或者方法:</p>
+
+<ol>
+ <li>那些定义在构造器函数中的、用于给予对象实例的。这些都很容易发现 - 在您自己的代码中,它们是构造函数中使用<code>this.x = x</code>类型的行;在内置的浏览器代码中,它们是可用于对象实例的成员(通常通过使用<code>new</code>关键字调用构造函数来创建,例如<code>var myInstance = new myConstructor()</code>)。</li>
+ <li>那些直接在构造函数上定义、仅在构造函数上可用的。这些通常仅在内置的浏览器对象中可用,并通过被直接链接到构造函数而不是实例来识别。 例如<code><a href="/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/keys">Object.keys()</a></code>。</li>
+ <li>那些在构造函数原型上定义、由所有实例和对象类继承的。这些包括在构造函数的原型属性上定义的任何成员,如<code>myConstructor.prototype.x()</code>。</li>
+</ol>
+
+<p>如果您现在觉得一团浆糊,别担心——您现在还处于学习阶段,不断练习才会慢慢熟悉这些知识。</p>
+
+<h2 id="何时在_JavaScript_中使用继承?">何时在 JavaScript 中使用继承?</h2>
+
+<p>特别是在读完这段文章内容之后,您也许会想 "天啊,这实在是太复杂了". 是的,您是对的,原型和继承代表了JavaScript这门语言里最复杂的一些方面,但是JavaScript的强大和灵活性正是来自于它的对象体系和继承方式,这很值得花时间去好好理解下它是如何工作的。</p>
+
+<p>在某种程度上来说,您一直都在使用继承 - 无论您是使用WebAPI的不同特性还是调用字符串、数组等浏览器内置对象的方法和属性的时候,您都在隐式地使用继承。</p>
+
+<p>就在自己代码中使用继承而言,您可能不会使用的非常频繁,特别是在小型项目中或者刚开始学习时 - 因为当您不需要对象和继承的时候,仅仅为了使用而使用它们只是在浪费时间而已。但是随着您的代码量的增大,您会越来越发现它的必要性。如果您开始创建一系列拥有相似特性的对象时,那么创建一个包含所有共有功能的通用对象,然后在更特殊的对象类型中继承这些特性,将会变得更加方便有用。</p>
+
+<div class="note">
+<p><strong>注: </strong>考虑到JavaScript的工作方式,由于原型链等特性的存在,在不同对象之间功能的共享通常被叫做 <strong>委托</strong> - 特殊的对象将功能委托给通用的对象类型完成。这也许比将其称之为继承更为贴切,因为“被继承”了的功能并没有被拷贝到正在“进行继承”的对象中,相反它仍存在于通用的对象中。</p>
+</div>
+
+<p>在使用继承时,建议您不要使用过多层次的继承,并仔细追踪定义方法和属性的位置。很有可能您的代码会临时修改了浏览器内置对象的原型,但您不应该这么做,除非您有足够充分的理由。过多的继承会在调试代码时给您带来无尽的混乱和痛苦。</p>
+
+<p>总之,对象是另一种形式的代码重用,就像函数和循环一样,有他们特定的角色和优点。如果您发现自己创建了一堆相关的变量和函数,还想一起追踪它们并将其灵活打包的话,对象是个不错的主意。对象在您打算把一个数据集合从一个地方传递到另一个地方的时候非常有用。这些都可以在不使用构造器和继承的情况下完成。如果您只是需要一个单一的对象实例,也许使用对象常量会好些,您当然不需要使用继承。</p>
+
+<h2 id="总结">总结</h2>
+
+<p>这篇文章覆盖了剩余的 OOJS 理论的核心知识和我们认为您应该知道的语法,这个时候您应该理解了 JavaScript 中的对象和 OOP 基础,原型和原型继承机制,如何创建类(constructors)和对象实例,为类增加功能,通过从其他类继承而创建新的子类。</p>
+
+<p>下一篇文章我们将学习如何运用 JavaScript Object Notation (JSON), 一种使用 JavaScript 对象写的数据传输格式。</p>
+
+<h2 id="参见">参见</h2>
+
+<ul>
+ <li><a href="http://www.objectplayground.com/">ObjectPlayground.com</a> - 一个非常有用的、用于了解对象的交互式学习网站。</li>
+ <li><a href="https://www.amazon.com/gp/product/193398869X/">Secrets of the JavaScript Ninja</a>, 第6章 - 由John Resig和Bear Bibeault撰写的关于高级JavaScript概念和技术的好书。第6章很好地介绍了原型和继承的相关方面;您可以很容易地找到打印版本或在线副本。</li>
+ <li><a href="https://github.com/getify/You-Dont-Know-JS/blob/1ed-zh-CN/this%20%26%20object%20prototypes/ch5.md">You Don't Know JS: this &amp; Object Prototypes</a> - 凯尔·辛普森(Kyle Simpson)的一系列优秀的JavaScript手册,第5章对原型的解释比我们在这里做的更详细。我们在本系列针对初学者的文章中提出了简化的观点,而凯尔深入学习,并提供了更为复杂但更准确的图景。</li>
+</ul>
+
+<p>{{PreviousMenuNext("Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects")}}</p>
diff --git a/files/zh-cn/learn/javascript/objects/json/index.html b/files/zh-cn/learn/javascript/objects/json/index.html
new file mode 100644
index 0000000000..c6963d261f
--- /dev/null
+++ b/files/zh-cn/learn/javascript/objects/json/index.html
@@ -0,0 +1,327 @@
+---
+title: 使用JSON
+slug: Learn/JavaScript/Objects/JSON
+tags:
+ - Working with JSON data
+translation_of: Learn/JavaScript/Objects/JSON
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/JavaScript/Objects/Inheritance", "Learn/JavaScript/Objects/Object_building_practice", "Learn/JavaScript/Objects")}}</div>
+
+<p class="summary">JavaScript对象表示法(JSON)是用于将结构化数据表示为JavaScript对象的标准格式,通常用于在网站上表示和传输数据(例如从服务器向客户端发送一些数据,因此可以将其显示在网页上)。您会经常遇到它,所以在本文中,我们向您提供使用JavaScript处理JSON的所有工作,包括访问JSON对象中的数据项并编写自己的JSON。</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">前提:</th>
+ <td>计算机基础知识,HTML 和 CSS 基础 (see <a href="/en-US/docs/Learn/JavaScript/First_steps">First steps</a> and <a href="/en-US/docs/Learn/JavaScript/Building_blocks">Building blocks</a>) 和 JS 面向对象基础(see <a href="/en-US/docs/Learn/JavaScript/Object-oriented/Introduction">Introduction to objects</a>)。</td>
+ </tr>
+ <tr>
+ <th scope="row">目标:</th>
+ <td>理解 JSON 的数据储存工作原理,创建您的 JSON 对象。</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="什么是_JSON">什么是 JSON?</h2>
+
+<p>{{glossary("JSON")}} 是一种按照JavaScript对象语法的数据格式,这是 <a href="https://en.wikipedia.org/wiki/Douglas_Crockford">Douglas Crockford</a> 推广的。虽然它是基于 JavaScript 语法,但它独立于JavaScript,这也是为什么许多程序环境能够读取(解读)和生成 JSON。 </p>
+
+<p>JSON可以作为一个对象或者字符串存在,前者用于解读 JSON 中的数据,后者用于通过网络传输 JSON 数据。 这不是一个大事件——JavaScript 提供一个全局的 可访问的 <a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON">JSON</a> 对象来对这两种数据进行转换。</p>
+
+<p>一个 JSON 对象可以被储存在它自己的文件中,这基本上就是一个文本文件,扩展名为 <code>.json</code>, 还有 {{glossary("MIME type")}} 用于 <code>application/json</code>.</p>
+
+<h3 id="JSON_结构">JSON 结构</h3>
+
+<p>我们已经可以推测出 JSON 对象就是基于 JavaScript 对象,而且这几乎是正确的。您可以把 JavaScript 对象原原本本的写入 JSON 数据——字符串,数字,数组,布尔还有其它的字面值对象。这允许您构造出一个对象树,如下:</p>
+
+<pre class="brush: json notranslate">{
+  "squadName" : "Super hero squad",
+  "homeTown" : "Metro City",
+  "formed" : 2016,
+  "secretBase" : "Super tower",
+ "active" : true,
+  "members" : [
+    {
+      "name" : "Molecule Man",
+      "age" : 29,
+      "secretIdentity" : "Dan Jukes",
+      "powers" : [
+        "Radiation resistance",
+        "Turning tiny",
+        "Radiation blast"
+      ]
+    },
+    {
+      "name" : "Madame Uppercut",
+      "age" : 39,
+      "secretIdentity" : "Jane Wilson",
+      "powers" : [
+        "Million tonne punch",
+        "Damage resistance",
+        "Superhuman reflexes"
+      ]
+    },
+    {
+      "name" : "Eternal Flame",
+      "age" : 1000000,
+      "secretIdentity" : "Unknown",
+      "powers" : [
+        "Immortality",
+        "Heat Immunity",
+        "Inferno",
+        "Teleportation",
+        "Interdimensional travel"
+      ]
+    }
+  ]
+}</pre>
+
+<p>如果我们要加载对象进入 JavaScript 程序,以保存为一个名为 <code>superHeroes </code>对象为例,我们使用 . 或 [] 访问对象内的数据(关于. 和 []概念,见 <a href="/en-US/docs/Learn/JavaScript/Objects/Basics">对象基础</a> )。如:</p>
+
+<pre class="brush: js notranslate">superHeroes.hometown
+superHeroes["active"]</pre>
+
+<p>为了访问对象中的对象,您只需简单地链式访问(通过属性名和数组索引)。例如,访问 superHeroes 对象中的 members 数组对象的第二个元素的 powers 数组对象的第三个元素,您可以这样做:</p>
+
+<pre class="brush: js notranslate">superHeroes["members"][1]["powers"][2]</pre>
+
+<ol>
+ <li>首先我们有变量名 <code>superHeroes</code>,储存对象 。</li>
+ <li>在对象中我们想访问 <code>members</code> 属性,所以我们使用 <code>["members"]</code>。</li>
+ <li><code>members </code>包含有对象数组,我们想要访问第二个元素,所以我们使用<code>[1]</code>。</li>
+ <li>在对象内,我们想访问 <code>powers</code> 属性,所以我们使用 <code>["powers"]</code>。</li>
+ <li><code>powers</code> 属性是一个包含英雄技能的数组。我们想要第三个,所以我们使用<code>[2]</code>。</li>
+</ol>
+
+<div class="note">
+<p><strong>注:</strong>我们已经在 <a href="http://mdn.github.io/learning-area/javascript/oojs/json/JSONTest.html">JSONText.html</a> 实例中让JSON 对象进入变量中使其可访问(见<a href="https://github.com/mdn/learning-area/blob/master/javascript/oojs/json/JSONTest.html">源代码</a>)。尝试加载它并且在您的浏览器上访问对象数据。</p>
+</div>
+
+<h3 id="JSON_数组">JSON 数组</h3>
+
+<p>前面我们已经说过,”我们已经可以推测出 JSON 对象就是基于 JavaScript 对象,而且这几乎是正确的“——我们说几乎正确的原因是数组对象也是一种合法的 JSON 对象,例如:</p>
+
+<pre class="brush: json notranslate">[
+ {
+ "name" : "Molecule Man",
+ "age" : 29,
+ "secretIdentity" : "Dan Jukes",
+ "powers" : [
+ "Radiation resistance",
+ "Turning tiny",
+ "Radiation blast"
+ ]
+ },
+ {
+    "name" : "Madame Uppercut",
+    "age" : 39,
+    "secretIdentity" : "Jane Wilson",
+    "powers" : [
+      "Million tonne punch",
+      "Damage resistance",
+      "Superhuman reflexes"
+    ]
+  }
+]</pre>
+
+<p>上面是完全合法的 JSON。您只需要通过数组索引就可以访问数组元素,如<code>[0]["powers"][0]。</code></p>
+
+<h3 id="其他注意事项">其他注意事项</h3>
+
+<ul>
+ <li>JSON 是一种纯数据格式,它只包含属性,没有方法。</li>
+ <li>JSON要求在字符串和属性名称周围使用双引号。 单引号无效。</li>
+ <li>甚至一个错位的逗号或分号就可以导致  JSON 文件出错。您应该小心的检查您想使用的数据(虽然计算机生成的 JSON 很少出错,只要生成程序正常工作)。您可以通过像 <a href="http://jsonlint.com/">JSONLint</a> 的应用程序来检验 JSON。</li>
+ <li>JSON 可以将任何标准合法的 JSON 数据格式化保存,不只是数组和对象。比如,一个单一的字符串或者数字可以是合法的 JSON 对象。虽然不是特别有用处……</li>
+ <li>与 JavaScript 代码中对象属性可以不加引号不同,JSON 中只有带引号的字符串可以用作属性。</li>
+</ul>
+
+<h2 id="主动学习_一个JSON_示例">主动学习 : 一个JSON 示例</h2>
+
+<p>好了,让我们通过运行这个示例来展示我们如何利用JSON数据。</p>
+
+<h3 id="开始吧">开始吧</h3>
+
+<p>首先,拷贝我们的 <a href="https://github.com/mdn/learning-area/blob/master/javascript/oojs/json/heroes.html">heroes.html</a> 和 <a href="https://github.com/mdn/learning-area/blob/master/javascript/oojs/json/style.css">style.css</a> 文件。后者包含了用于页面的简单的 CSS ,前者包含了简单的 HTML  body。</p>
+
+<pre class="brush: html notranslate">&lt;header&gt;
+&lt;/header&gt;
+
+&lt;section&gt;
+&lt;/section&gt;</pre>
+
+<p>添加 <code>&lt;script&gt;</code>元素来包含我们的 JavaScript 代码。当前它只有两行,获得了<code>&lt;header&gt;</code>和<code>&lt;section&gt;</code>的引用,保存在变量中。</p>
+
+<pre class="brush: js notranslate">var header = document.querySelector('header');
+var section = document.querySelector('section');
+</pre>
+
+<p>我们已经把 JSON 数据放在了GitHub 上面:<a href="https://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json" rel="noopener">https://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json</a></p>
+
+<p>我们准备把它加载到我们的页面中,然后使用漂亮的 DOM 操作来展示它,就像这样:</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/13857/json-superheroes.png" style="display: block; margin: 0 auto;"></p>
+
+<h3 id="加载我们的JSON">加载我们的JSON</h3>
+
+<p>为了载入 JSON 到页面中,我们将使用 一个名为<code>XMLHTTPRequest</code>的API(常称为XHR)。这是一个非常有用的 JavaScript 对象,使我们能够通过代码来向服务器请求资源文件(如:图片,文本,JSON,甚至HTML片段),意味着我们可以更新小段内容而不用重新加载整个页面。这将有更多响应页面,听起来让人兴奋,但是这部分超出我们本部分的文章,所以就不多详述了。</p>
+
+<ol>
+ <li>首先,我们将保存一个即将访问的 URL 作为变量。在您的 JavaScript 代码的底部添加下面的代码:
+ <pre class="brush: js notranslate">var requestURL = '<a href="https://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json" rel="noopener">https://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json</a>';</pre>
+ </li>
+ <li>为了创建一个HTTP请求,我们需要创建一个HTTP请求对象,通过 new 构造函数的形式。在您最下面的代码中写入:
+ <pre class="brush: js notranslate">var request = new XMLHttpRequest();</pre>
+ </li>
+ <li>现在我们需要使用 <code><a href="/en-US/docs/Web/API/XMLHttpRequest/open">open()</a></code> 函数打开一个新的请求,添加如下代码:
+ <pre class="brush: js notranslate">request.open('GET', requestURL);</pre>
+
+ <p>这个函数至少含有两个参数,其它的是可选参数。对于示例我们只需要两个强制参数</p>
+
+ <ul>
+ <li>HTTP 方法,网络连接时使用。这个示例中 <code><a href="/en-US/docs/Web/HTTP/Methods/GET">GET</a></code> 就可以了,因为我们只要获得简单的数据。</li>
+ <li>URL,用于指向请求的地址。我们使用之前保存的变量。</li>
+ </ul>
+ </li>
+ <li>接下来,添加,两行代码,我们设定 <code><a href="/en-US/docs/Web/API/XMLHttpRequest/responseType">responseType</a></code> 为 JSON,所以服务器将知道我们想要返回一个 JSON 对象,然后发送请求 :
+ <pre class="brush: js notranslate">request.responseType = 'json';
+request.send();</pre>
+ </li>
+ <li>最后一点内容涉及相应来自服务器的返回数据,然后处理它,添加如下代码在您先前的代码下方:
+ <pre class="brush: js notranslate">request.onload = function() {
+ var superHeroes = request.response;
+ populateHeader(superHeroes);
+ showHeroes(superHeroes);
+}</pre>
+ </li>
+</ol>
+
+<p>这儿我们保存了相应我们请求的数据(访问 <code><a href="/en-US/docs/Web/API/XMLHttpRequest/response">response</a></code> 属性) 于变量 <code>superHeroes</code> ;这个变量现在含有 JSON!我们现在把<code>superHeroes</code>传给两个函数,第一个函数将会用正确的数据填充<code>&lt;header&gt;</code>,同时第二个函数将创建一个信息卡片,然后把它插入<code>&lt;section&gt;</code>中。</p>
+
+<p>我们把代码包在事件处理函数中,当请求对象<code>load</code>事件触发时执行代码(<code>见<a href="/en-US/docs/Web/API/XMLHttpRequestEventTarget/onload">onload</a></code>),这是因为请求对象<code>load</code>事件只有在请求成功时触发;这种方式可以保证事件触发时 <code>request.response </code>是绝对可以访问的。</p>
+
+<h3 id="定位_header">定位 header</h3>
+
+<p>现在我们已经获得我们的JSON数据,让我们利用它来写两个我们使用的函数。首先,添加下面的代码于之前的代码下方:</p>
+
+<pre class="brush: js notranslate">function populateHeader(jsonObj) {
+ var myH1 = document.createElement('h1');
+ myH1.textContent = jsonObj['squadName'];
+ header.appendChild(myH1);
+
+ var myPara = document.createElement('p');
+ myPara.textContent = 'Hometown: ' + jsonObj['homeTown'] + ' // Formed: ' + jsonObj['formed'];
+ header.appendChild(myPara);
+}</pre>
+
+<p>我们称参数为 <code>jsonObj</code>,那也是为什么我们要在其中调用 JSON 对象。这儿我们首先使用 <code><a href="/en-US/docs/Web/API/Document/createElement">createElement()</a> </code>创建了一个 <code>&lt;h1&gt;</code> 节点,将它的 <code><a href="/en-US/docs/Web/API/Node/textContent">textContent</a></code> 设为 JSON 对象的 <code>squadName</code> 属性,然后通过 <code><a href="/en-US/docs/Web/API/Node/appendChild">appendChild()</a> </code>把它加入 <code>&lt;header&gt;</code>中。然后我们对段落做了相同的一件事情:创建,设置内容,追加到 <code>&lt;header&gt;</code>。唯一的不同在于它的内容设为一个与 JSON 内属性 <code>homeTown </code>和<code>formed</code>相关联的字符串。</p>
+
+<h3 id="创建英雄信息卡片">创建英雄信息卡片</h3>
+
+<p>接下来,添加如下的函数到脚本代码底部,这个函数创建和展示了<code>superhero cards</code>:</p>
+
+<pre class="brush: js notranslate">function showHeroes(jsonObj) {
+ var heroes = jsonObj['members'];
+
+ for(i = 0; i &lt; heroes.length; i++) {
+ var myArticle = document.createElement('article');
+ var myH2 = document.createElement('h2');
+ var myPara1 = document.createElement('p');
+ var myPara2 = document.createElement('p');
+ var myPara3 = document.createElement('p');
+ var myList = document.createElement('ul');
+
+ myH2.textContent = heroes[i].name;
+ myPara1.textContent = 'Secret identity: ' + heroes[i].secretIdentity;
+ myPara2.textContent = 'Age: ' + heroes[i].age;
+ myPara3.textContent = 'Superpowers:';
+
+ var superPowers = heroes[i].powers;
+ for(j = 0; j &lt; superPowers.length; j++) {
+ var listItem = document.createElement('li');
+ listItem.textContent = superPowers[j];
+ myList.appendChild(listItem);
+ }
+
+ myArticle.appendChild(myH2);
+ myArticle.appendChild(myPara1);
+ myArticle.appendChild(myPara2);
+ myArticle.appendChild(myPara3);
+ myArticle.appendChild(myList);
+
+ section.appendChild(myArticle);
+ }
+}</pre>
+
+<p>首先,我们保存了 JSON 的 <code>members </code>属性作为一个变量。这个数组含有多个带有英雄信息的对象。</p>
+
+<p>接下来,我们使用一个循环来,遍历每个元素。对于每一个元素,我们:</p>
+
+<ol>
+ <li>创建几个元素: 一个 <code>&lt;article&gt;</code>,一个 <code>&lt;h2&gt;</code>, 三个 <code>&lt;p&gt;</code>s, 和一个 <code>&lt;ul&gt;。</code></li>
+ <li>设置 <code>&lt;h2&gt;</code> 为当前英雄的 <code>name</code>。</li>
+ <li>使用他们的<code>secretIdentity</code>, <code>age</code>, "Superpowers:" 介绍信息列表 填充三个段落来。</li>
+ <li>保存 <code>powers</code> 属性于另一个变量 <code>superPowers</code>,包含英雄的<code>superpowers</code>列表。</li>
+ <li>使用另一个循环来遍历当前的英雄的 <code>superpowers</code> ,对于每一个元素我们创建<code>&lt;li&gt;</code>元素,把<code>superpower</code>放进去,然后使用<code>appendChild()</code>把 <code>listItem</code> 放入<code>&lt;ul&gt;</code> 元素中。</li>
+ <li>最后一件事情是追加<code>&lt;h2&gt;,&lt;p&gt;<font face="Open Sans, arial, x-locale-body, sans-serif">,还有</font></code><code>&lt;ul&gt;进入</code> <code>&lt;article&gt;</code> (<code>myArticle</code>)。然后将<code>&lt;article&gt;</code> 追加到 <code>&lt;section&gt;</code>。追加的顺序很重要,因为他们将被展示在 HTML 中。</li>
+</ol>
+
+<div class="note">
+<p><strong>Note</strong>: 如有疑难,试试引用我们的 <a href="https://github.com/mdn/learning-area/blob/master/javascript/oojs/json/heroes-finished.html">heroes-finished.html</a> 代码(也可见 <a href="http://mdn.github.io/learning-area/javascript/oojs/json/heroes-finished.html">running live</a> )。</p>
+</div>
+
+<div class="note">
+<p><strong>Note</strong>: 如果您对访问 JSON对象的 点/括号标记 有困扰。获得文件 <a href="http://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json">superheroes.json</a> 并在您的编辑器中打开参考我们的 JS 代码将会有帮助。您还应该参考我们的 <a href="/en-US/docs/Learn/JavaScript/Objects/Basics">JavaScript object basics</a>文章,了解关于点和括号符号的更多信息。</p>
+</div>
+
+<h2 id="对象和文本间的转换">对象和文本间的转换</h2>
+
+<p>上述示例就访问 JSON 而言是简单的,因为我们设置了 XHR 来访问 JSON 格式数据: </p>
+
+<pre class="brush: js notranslate">request.responseType = 'json';</pre>
+
+<p>但是有时候我们没有那么幸运,我们接收到一些 字符串作为 JSON 数据,然后我们想要将它转换为对象。当我们想要发送 JSON 数据作为信息,我们将需要转换它为字符串,我们经常需要正确的转换数据,幸运的是,这两个问题在web环境中是那么普遍以至于浏览器拥有一个内建的 JSON,包含以下两个方法。</p>
+
+<ul>
+ <li><code><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse">parse()</a></code>: 以文本字符串形式接受JSON对象作为参数,并返回相应的对象。</li>
+ <li><code><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify">stringify()</a></code>: 接收一个对象作为参数,返回一个对应的JSON字符串。</li>
+</ul>
+
+<p>您可以看看我们 <a href="http://mdn.github.io/learning-area/javascript/oojs/json/heroes-finished-json-parse.html">heroes-finished-json-parse.html</a> 示例的第一个操作 (见 <a href="https://github.com/mdn/learning-area/blob/master/javascript/oojs/json/heroes-finished-json-parse.html">source code</a>) ,除了返回的是 text,这做了一件与我们之前一模一样的事情,然后使用 <code>parse()</code> 来将他转换成为 JavaScript 对象。关键片段如下:</p>
+
+<pre class="brush: js notranslate">request.open('GET', requestURL);
+request.responseType = 'text'; // now we're getting a string!
+request.send();
+
+request.onload = function() {
+ var superHeroesText = request.response; // get the string from the response
+ var superHeroes = JSON.parse(superHeroesText); // convert it to an object
+ populateHeader(superHeroes);
+ showHeroes(superHeroes);
+}</pre>
+
+<p>正如您所想, <code>stringify()</code> 做相反的事情. 尝试将下面的代码输入您的浏览器 JS 控制台来看看会发生什么:</p>
+
+<pre class="brush: js notranslate">var myJSON = { "name" : "Chris", "age" : "38" };
+myJSON
+var myString = JSON.stringify(myJSON);
+myString</pre>
+
+<p>这儿我们创建了一个JavaScript 对象,然后检查了它包含了什么,然后用<code>stringify()</code> 将它转换成JSON字符串,最后保存返回值作为变量。然后再一次检查。</p>
+
+<h2 id="总结">总结</h2>
+
+<p>在这个文章中,我们给了您一个简单的示例来在自己的程序中使用 JSON,包括创建和处理 JSON,还有如何访问 JSON 内的数据。在下一篇文章中我们将开始关注JS中的面向对象内容。</p>
+
+<h2 id="参见">参见</h2>
+
+<ul>
+ <li><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON">JSON object reference page</a></li>
+ <li><a href="/en-US/docs/Web/API/XMLHttpRequest">XMLHTTPRequest object reference page</a></li>
+ <li><a href="/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest">Using XMLHTTPRequest</a></li>
+ <li><a href="/en-US/docs/Web/HTTP/Methods">HTTP request methods</a></li>
+</ul>
+
+<p>{{PreviousMenuNext("Learn/JavaScript/Objects/Inheritance", "Learn/JavaScript/Objects/Object_building_practice", "Learn/JavaScript/Objects")}}</p>
diff --git a/files/zh-cn/learn/javascript/objects/object-oriented_js/index.html b/files/zh-cn/learn/javascript/objects/object-oriented_js/index.html
new file mode 100644
index 0000000000..e20c345337
--- /dev/null
+++ b/files/zh-cn/learn/javascript/objects/object-oriented_js/index.html
@@ -0,0 +1,267 @@
+---
+title: 适合初学者的JavaScript面向对象
+slug: Learn/JavaScript/Objects/Object-oriented_JS
+translation_of: Learn/JavaScript/Objects/Object-oriented_JS
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/JavaScript/Objects/Basics", "Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects")}}</div>
+
+<p class="summary">学完基础后, 现在我们集中于面向对象的 JavaScript (OOJS) — 本文首先提出了面向对象编程(OOP) 理论的基本观点,然后探索如何通过构造函数模拟对象类,以及如何创建对象.</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">预备知识:</th>
+ <td>计算机基础知识, 了解 HTML 和 CSS, 熟悉 JavaScrpit 基础知识 (查看 <a href="/en-US/docs/Learn/JavaScript/First_steps">First steps</a> 和 <a href="/en-US/docs/Learn/JavaScript/Building_blocks">Building blocks</a>) 和 OOJS 基础 (查看 <a href="/en-US/docs/Learn/JavaScript/Object-oriented/Introduction">Introduction to objects</a>).</td>
+ </tr>
+ <tr>
+ <th scope="row">目标:</th>
+ <td>掌握面向对象程序的基本理论, 这涉及到 JavaScript  的("万物皆对象"), 以及如何创建构造器和对象.</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="从零开始面向对象的程序设计">从零开始面向对象的程序设计</h2>
+
+<p>首先,我们从高维度且简化的角度看看 面向对象的程序(Object-oriented programming ,OOP)是什么。我们将简单描述OOP,因为OOP该概念已变得很复杂,如果完整地描述OOP将使读者难以理解。OOP 的基本思想是:在程序里,我们通过使用对象去构建现实世界的模型,把原本很难(或不可)能被使用的功能,简单化并提供出来,以供访问。</p>
+
+<p>对象可以包含相关的数据和代码,这些数据和代码用于表示 你所建造的模型是什么样子,以及拥有什么样的行为或功能。<span>对象包(</span>object package<span>,或者叫命名空间 </span>namespace<span>)存储(官方用语:</span><strong>封装</strong><span>)着对象的数据(常常还包括函数),使数据的组织和访问变得更容易了;对象也常用作 数据存储体(</span>data stores<span>),用于在网络上运输数据,十分便捷。</span></p>
+
+<h3 id="定义一个对象模板">定义一个对象模板</h3>
+
+<p>让我们来考虑一个简单的程序,它可以显示一个学校的学生和老师的信息.在这里我们不讨论任何程序语言,我们只讨论 OOP 思想.</p>
+
+<p>首先,我们可以回到上一节拿到定义好属性和方法的Person对象。对于一个人(person)来说,我们能在他们身上获取到很多信息(他们的住址,身高,鞋码,基因图谱,护照信息,显著的性格特征等等),然而,我们仅仅需要他们的名字,年龄,性别,兴趣 这些信息,然后,我们会基于他们的这些信息写一个简短的介绍关于他们自己,在最后我们还需要教会他们打招呼。以上的方式被称为抽象-为了我们编程的目标而利用事物的一些重要特性去把复杂的事物简单化</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/13889/person-diagram.png" style="display: block; height: 219px; margin: 0px auto; width: 610px;"></p>
+
+<p>在一些面向对象的语言中,我们用类(class)的概念去描述一个对象(您在下面就能看到JavaScript使用了一个完全不同的术语)-类并不完全是一个对象,它更像是一个定义对象特质的模板。</p>
+
+<h3 id="创造一个真正的对象">创造一个真正的对象</h3>
+
+<p>从上面我们创建的class中, 我们能够基于它创建出一些对象 - 一些拥有class中属性及方法的对象。基于我们的Person类,我们可以创建出许许多多的真实的人:</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/13883/MDN-Graphics-instantiation-2.png" style="display: block; height: 743px; margin: 0px auto; width: 700px;"></p>
+
+<p>当一个对象需要从类中创建出来时,类的构造函数就会运行来创建这个实例。这种创建对象的过程我们称之为实例化-实例对象被类实例化。</p>
+
+<h3 id="具体的对象">具体的对象</h3>
+
+<p>在这个例子里,我们不想要泛指的人,我们想要像老师和学生这样类型更为具体的人。在 OOP 里,我们可以创建基于其它类的新类,这些新的子类可以继承它们父类的数据和功能。比起复制来说这样能够使用父对象共有的功能。如果类之间的功能不同,你可以根据需要定义专用的特征。</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/13881/MDN-Graphics-inherited-3.png" style="display: block; height: 743px; margin: 0px auto; width: 700px;"></p>
+
+<p>这是非常有用的,老师和学生具有一些相同的特征比如姓名、性别、年龄,因此只需要定义这些特征一次就可以了。您可以在不同的类里分开定义这些相同的特征,这样该特征会有一个不同的命名空间。比如,一个学生的 greeting 可以是 "Yo, I'm [firstName]" (例子 <em>Yo, I'm Sam</em>),老师的可能会正式一些,比如"Hello, my name is [Prefix] [lastName]" (例子 <em>Hello, My name is Mr Griffiths</em>)。</p>
+
+<div class="note">
+<p><strong>注:</strong><strong>多态</strong>——这个高大上的词正是用来描述多个对象拥有实现共同方法的能力。</p>
+</div>
+
+<p>现在可以根据子类创建对象。如:</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/13885/MDN-Graphics-instantiation-teacher-3.png" style="display: block; height: 743px; margin: 0px auto; width: 700px;">下面我们来看看 OOP 理论如何应用到 JavaScript 实践中去的。</p>
+
+<h2 id="构建函数和对象">构建函数和对象</h2>
+
+<p>有些人认为 JavaScript 不是真正的面向对象的语言,比如它没有像许多面向对象的语言一样有用于创建class类的声明。JavaScript 用一种称为<strong>构建函数</strong>的特殊函数来定义对象和它们的特征。构建函数非常有用,因为很多情况下您不知道实际需要多少个对象(实例)。<strong>构建函数</strong>提供了创建您所需对象(实例)的有效方法,将对象的数据和特征函数按需联结至相应对象。</p>
+
+<p>不像“经典”的面向对象的语言,从构建函数创建的新实例的特征并非全盘复制,而是通过一个叫做原形链的参考链链接过去的。(参见 <a href="/en-US/docs/Learn/JavaScript/Objects/Object_prototypes">Object prototypes</a>),所以这并非真正的实例,严格的讲, JavaScript 在对象间使用和其它语言的共享机制不同。</p>
+
+<div class="note">
+<p><strong>注:</strong> “经典”的面向对象的语言并非好事,就像上面提到的,OOP 可能很快就变得非常复杂,JavaScript 找到了在不变的特别复杂的情况下利用面向对象的优点的方法。</p>
+</div>
+
+<p>让我们来看看 JavaScript 如何通过构建函数对象来创建类。首先,请先复制一个新的前文提到的<a href="https://github.com/mdn/learning-area/blob/master/javascript/oojs/introduction/oojs.html">oojs.html</a> 。</p>
+
+<h3 id="一个简单的例子">一个简单的例子</h3>
+
+<ol>
+ <li>让我们看看如何通过一个普通的函数定义一个”人“。在您的文件中添加以下代码:
+ <pre class="brush: js notranslate">function createNewPerson(name) {
+ var obj = {};
+ obj.name = name;
+ obj.greeting = function () {
+ alert('Hi! I\'m ' + this.name + '.');
+ }
+ return obj;
+}</pre>
+ </li>
+ <li>您现在可以通过调用这个函数创建一个新的叫 salva 的人,在您浏览器的JavaScript console 试试 :
+ <pre class="brush: js notranslate">var salva = createNewPerson('salva');
+salva.name;
+salva.greeting();</pre>
+ 上述代码运行良好,但是有点冗长;如果我们知道如何创建一个对象,就没有必要创建一个新的空对象并且返回它。幸好 JavaScript 通过构建函数提供了一个便捷的方法,方法如下:</li>
+ <li>将之前的代码用如下代码代替:
+ <pre class="brush: js notranslate">function Person(name) {
+ this.name = name;
+ this.greeting = function() {
+ alert('Hi! I\'m ' + this.name + '.');
+ };
+}</pre>
+ </li>
+</ol>
+
+<p>这个构建函数是 JavaScript 版本的类。您会发现,它只定义了对象的属性和方法,除了没有明确创建一个对象和返回任何值和之外,它有了您期待的函数所拥有的全部功能。这里使用了<code>this</code>关键词,即无论是该对象的哪个实例被这个构建函数创建,它的 <code>name</code> 属性就是传递到构建函数形参<code>name</code>的值,它的 <code>greeting()</code> 方法中也将使用相同的传递到构建函数形参<code>name</code>的值。</p>
+
+<div class="note">
+<p><strong>注:</strong> 一个构建函数通常是大写字母开头,这样便于区分构建函数和普通函数。</p>
+</div>
+
+<p>那如何调用构建函数创建新的实例呢?</p>
+
+<ol>
+ <li>将下面的代码加在您之前的代码下面:
+ <pre class="brush: js notranslate">var person1 = new Person('Bob');
+var person2 = new Person('Sarah');</pre>
+ </li>
+ <li>保存并刷新浏览器,在 console 里输入如下代码:
+ <pre class="brush: js notranslate">person1.name
+person1.greeting()
+person2.name
+person2.greeting()</pre>
+ </li>
+</ol>
+
+<p>酷!您现在看到页面上有两个对象,每一个保存在不同的命名空间里,当您访问它们的属性和方法时,您需要使用<code>person1</code>或者<code>person2</code>来调用它们。尽管它们有着相同的<code>name</code>属性和 <code>greeting()</code>方法它们是各自独立的,所以相互的功能不会冲突。注意它们使用的是自己的 name 值,这也是使用 this 关键字的原因,它们使用的从实参传入形参的自己的值,而不是其它的什么值。</p>
+
+<p>再看看这个构造对象的语法:</p>
+
+<pre class="brush: js notranslate">var person1 = new Person('Bob');
+var person2 = new Person('Sarah');</pre>
+
+<p>上述代码中,关键字 <code>new</code> 跟着一个含参函数,用于告知浏览器我们想要创建一个对象,非常类似函数调用,并把结果保存到变量中。每个示例类都是根据下面的方式定义的。</p>
+
+<pre class="brush: js notranslate">function Person(name) {
+ this.name = name;
+ this.greeting = function() {
+ alert('Hi! I\'m ' + this.name + '.');
+ };
+}
+</pre>
+
+<p>当新的对象被创立, 变量<code>person1</code>与<code>person2</code>有效地包含了以下值:</p>
+
+<pre class="brush: js notranslate">{
+ name : 'Bob',
+ greeting : function() {
+ alert('Hi! I\'m ' + this.name + '.');
+ }
+}
+
+{
+ name : 'Sarah',
+ greeting : function() {
+ alert('Hi! I\'m ' + this.name + '.');
+ }
+}</pre>
+
+<p>值得注意的是每次当我们调用构造函数时,我们都会重新定义一遍 greeting(),这不是个理想的方法。为了避免这样,我们可以在原型里定义函数,接下来我们会讲到。</p>
+
+<h3 id="创建我们最终的构造函数">创建我们最终的构造函数</h3>
+
+<p>上面的例子仅仅是简单地介绍如何开始。让我们现在开始创建<code>Person()</code>构造函数。</p>
+
+<ol>
+ <li>移除掉您之前写的所有代码, 用如下构造函数替代 —— 实现原理上,这与我们之前的例子并无二致, 只是变得稍稍复杂了些:
+ <pre class="brush: js notranslate">function Person(first, last, age, gender, interests) {
+ this.name = {
+ 'first': first,
+ 'last': last
+ };
+ this.age = age;
+ this.gender = gender;
+ this.interests = interests;
+ this.bio = function() {
+ alert(this.name.first + ' ' + this.name.last + ' is ' + this.age + ' years old. He likes ' + this.interests[0] + ' and ' + this.interests[1] + '.');
+ };
+ this.greeting = function() {
+ alert('Hi! I\'m ' + this.name.first + '.');
+ };
+};</pre>
+ </li>
+ <li>接下来加上这样一行代码, 用来创建它的一个对象:
+ <pre class="brush: js notranslate">var person1 = new Person('Bob', 'Smith', 32, 'male', ['music', 'skiing']);</pre>
+ </li>
+</ol>
+
+<p>这样,您就可以像我们定义第一个对象一样访问它的属性和方法了:</p>
+
+<pre class="brush: js notranslate">person1['age']
+person1.interests[1]
+person1.bio()
+// etc.</pre>
+
+<div class="note">
+<p><strong>注:</strong> 如果您对这一部分有疑问, 尝试将您的代码与我们的版本做比较 —— 戳链接: <a href="https://github.com/mdn/learning-area/blob/master/javascript/oojs/introduction/oojs-class-finished.html">oojs-class-finished.html</a> (或者: <a href="http://mdn.github.io/learning-area/javascript/oojs/introduction/oojs-class-further-exercises.html">查看它的实现</a>).</p>
+</div>
+
+<h3 id="进一步的练习">进一步的练习</h3>
+
+<p>首先, 尝试着写几行代码创建您自己的对象, 接着,尝试getting与setting对象中的成员。</p>
+
+<p>此外, 我们的<code>bio()</code>方法里仍有一些问题 —— 尽管您创建的Person是女性,或者是些别的性别类型,输出里的代词都总是 "He"。 而且, 纵然您有更多的兴趣列举在<code>interests</code>数组中, bio只会展示您的两个兴趣。 您能想出如何在类型定义(构造函数)中解决这个问题吗? 您可以按照您喜欢的方式编写构造函数(您可能需要一些条件判断和循环)。 考虑下语句如何根据性别、兴趣列表中兴趣的数目异构。</p>
+
+<div class="note">
+<p><strong>注:</strong>如果您觉得困难, 我们在我们的<a href="https://github.com/mdn/learning-area/blob/master/javascript/oojs/introduction/oojs-class-further-exercises.html">GitHub仓库</a>里作了回答(<a href="http://mdn.github.io/learning-area/javascript/oojs/introduction/oojs-class-further-exercises.html">查看它的实现</a>) ——但首先请您尝试着自己写出来。</p>
+</div>
+
+<h2 id="创建对象的其他方式">创建对象的其他方式</h2>
+
+<p>到现在为止,我们了解到了两种不同的创建对象的方式 —— <a href="/en-US/docs/Learn/JavaScript/Objects/Basics#Object_basics">声明一个对象的语法</a>, 与使用构造函数(回顾上面)。</p>
+
+<p>这些方法都是很有用的, 但仍有其他的方法 —— 我们希望您能熟悉这些,以免您在Web世界的旅行中碰到它们。</p>
+
+<h3 id="Object构造函数">Object()构造函数</h3>
+
+<p>首先, 您能使用<code><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object">Object()</a></code>构造函数来创建一个新对象。 是的, 一般对象都有构造函数,它创建了一个空的对象。</p>
+
+<ol>
+ <li>尝试在您浏览器中的Javascript控制台中输入以下代码:
+ <pre class="brush: js notranslate">var person1 = new Object();</pre>
+ </li>
+ <li>这样就在<code>person1</code>变量中存储了一个空对象。然后, 可以根据需要, 使用点或括号表示法向此对象添加属性和方法;试试这个例子:
+ <pre class="brush: js notranslate">person1.name = 'Chris';
+person1['age'] = 38;
+person1.greeting = function() {
+ alert('Hi! I\'m ' + this.name + '.');
+}</pre>
+ </li>
+ <li>还可以将对象文本传递给Object() 构造函数作为参数, 以便用属性/方法填充它。请尝试以下操作:
+ <pre class="brush: js notranslate">var person1 = new Object({
+ name : 'Chris',
+ age : 38,
+ greeting : function() {
+ alert('Hi! I\'m ' + this.name + '.');
+ }
+});</pre>
+ </li>
+</ol>
+
+<h3 id="使用create方法">使用create()方法</h3>
+
+<p>JavaScript有个内嵌的方法<code><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create">create()</a></code>, 它允许您基于现有对象创建新的对象。</p>
+
+<ol>
+ <li>在 JavaScript 控制台中尝试此操作:
+ <pre class="brush: js notranslate">var person2 = Object.create(person1);</pre>
+ </li>
+ <li>现在尝试这个:
+ <pre class="brush: js notranslate">person2.name
+person2.greeting()</pre>
+ </li>
+</ol>
+
+<p>您可以看到,<code>person2</code>是基于<code>person1</code>创建的, 它们具有相同的属性和方法。这非常有用, 因为它允许您创建新的对象而无需定义构造函数。缺点是比起构造函数,浏览器在更晚的时候才支持create()方法(IE9,  IE8 或甚至以前相比), 加上一些人认为构造函数让您的代码看上去更整洁 —— 您可以在一个地方创建您的构造函数, 然后根据需要创建实例, 这让您能很清楚地知道它们来自哪里。</p>
+
+<p>但是, 如果您不太担心对旧浏览器的支持, 并且您只需要一个对象的一些副本, 那么创建一个构造函数可能会让您的代码显得过度繁杂。这取决于您的个人爱好。有些人发现create() 更容易理解和使用。</p>
+
+<p>稍后我们将更详细地探讨create() 的效果。</p>
+
+<h2 id="总结">总结</h2>
+
+<p>这篇文章简单地介绍了一些面向对象原理 —— 这些描述还不够完整, 但它让您知道我们在这里处理什么。此外, 我们已经开始研究 javascript与 "经典 OOP"的关联与区别, 如何使用构造函数实现 javascript 中的类, 以及生成对象的不同方法。</p>
+
+<p>在下一篇文章中, 我们将探讨 JavaScript 对象原型。</p>
+
+<p>{{PreviousMenuNext("Learn/JavaScript/Objects/Basics", "Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects")}}</p>
diff --git a/files/zh-cn/learn/javascript/objects/object_building_practice/index.html b/files/zh-cn/learn/javascript/objects/object_building_practice/index.html
new file mode 100644
index 0000000000..a5e19db541
--- /dev/null
+++ b/files/zh-cn/learn/javascript/objects/object_building_practice/index.html
@@ -0,0 +1,454 @@
+---
+title: 实践对象构造
+slug: Learn/JavaScript/Objects/Object_building_practice
+tags:
+ - JavaScript
+ - 初学者
+ - 学习
+ - 对象
+ - 指南
+ - 画布
+translation_of: Learn/JavaScript/Objects/Object_building_practice
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects/Adding_bouncing_balls_features", "Learn/JavaScript/Objects")}}</div>
+
+<p class="summary">在前面的文章中,我们学习了 JavaScript 的面向对象理论和基本的语法知识,为之后的学习建立了良好的基础。这篇文章中我们将进行一次实战演练,通过构造 JavaScript 对象得到生动有趣的成果!</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">预备知识:</th>
+ <td>基本的计算机知识,了解HTML与CSS的基本概念,熟悉JavaScript基本知识 (请参阅 <a href="/zh-CN/docs/Learn/JavaScript/First_steps">入门</a> 和 <a href="/zh-CN/docs/Learn/JavaScript/Building_blocks">构建块结构</a>)和面向对象的JavaScript (OOJS) 基础 (请参阅 <a href="/zh-CN/docs/Learn/JavaScript/Objects/Basics">对象基础</a>)。</td>
+ </tr>
+ <tr>
+ <th scope="row">目标:</th>
+ <td>练习使用对象,在真实环境中应用面向对象开发技术。</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="弹跳吧!小彩球!">弹跳吧!小彩球!</h2>
+
+<p>本文通过编写一个弹球 demo 来展示 JavaScript 中对象的重要性。我们的小球会在屏幕上弹跳,当它们碰到彼此时会变色。最终会像这样:</p>
+
+<div class="hidden">
+<h6 id="Bouncing_balls">Bouncing balls</h6>
+
+<pre class="brush: html">&lt;!DOCTYPE html&gt;
+&lt;html lang="zh-CN"&gt;
+ &lt;head&gt;
+ &lt;meta charset="utf-8"&gt;
+ &lt;style&gt;
+ body {
+     margin: 0;
+     overflow: hidden;
+     font-family: 'PingFangSC-Regular', '微软雅黑', sans-serif;
+     height: 100%;
+ }
+ h1 {
+    font-size: 2rem;
+    letter-spacing: -1px;
+    position: absolute;
+    margin: 0;
+    top: -4px;
+    right: 5px;
+       color: transparent;
+    text-shadow: 0 0 4px white;
+  }
+ &lt;/style&gt;
+ &lt;/head&gt;
+
+ &lt;body&gt;
+ &lt;h1&gt;弹球&lt;/h1&gt;
+ &lt;canvas&gt;&lt;/canvas&gt;
+
+ &lt;script&gt;
+const canvas = document.querySelector('canvas');
+const ctx = canvas.getContext('2d');
+
+const width = canvas.width = window.innerWidth;
+const height = canvas.height = window.innerHeight;
+
+function random(min,max) {
+  const num = Math.floor(Math.random() * (max - min)) + min;
+  return num;
+}
+
+function randomColor() {
+  const color = 'rgb(' +
+                random(0, 255) + ',' +
+                random(0, 255) + ',' +
+                random(0, 255) + ')';
+  return color;
+}
+
+function Ball(x, y, velX, velY, color, size) {
+  this.x = x;
+  this.y = y;
+  this.velX = velX;
+  this.velY = velY;
+  this.color = color;
+  this.size = size;
+}
+
+Ball.prototype.draw = function() {
+  ctx.beginPath();
+  ctx.fillStyle = this.color;
+  ctx.arc(this.x, this.y, this.size, 0, 2 * Math.PI);
+  ctx.fill();
+};
+
+Ball.prototype.update = function() {
+  if((this.x + this.size) &gt;= width) {
+    this.velX = -(this.velX);
+  }
+
+  if((this.x - this.size) &lt;= 0) {
+    this.velX = -(this.velX);
+  }
+
+  if((this.y + this.size) &gt;= height) {
+    this.velY = -(this.velY);
+  }
+
+  if((this.y - this.size) &lt;= 0) {
+    this.velY = -(this.velY);
+  }
+
+  this.x += this.velX;
+  this.y += this.velY;
+};
+
+Ball.prototype.collisionDetect = function() {
+  for(let j = 0; j &lt; balls.length; j++) {
+    if(this !== balls[j]) {
+      const dx = this.x - balls[j].x;
+      const dy = this.y - balls[j].y;
+      const distance = Math.sqrt(dx * dx + dy * dy);
+
+      if (distance &lt; this.size + balls[j].size) {
+        balls[j].color = this.color = randomColor();
+      }
+    }
+  }
+};
+
+let balls = [];
+
+while(balls.length &lt; 25) {
+  const size = random(10,20);
+  let ball = new Ball(
+    random(0 + size, width - size),
+    random(0 + size, height - size),
+    random(-7, 7),
+    random(-7, 7),
+    randomColor(),
+    size
+  );
+  balls.push(ball);
+}
+
+function loop() {
+  ctx.fillStyle = 'rgba(0,0,0,0.25)';
+  ctx.fillRect(0,0,width,height);
+
+  for(let i = 0; i &lt; balls.length; i++) {
+    balls[i].draw();
+    balls[i].update();
+    balls[i].collisionDetect();
+  }
+
+  requestAnimationFrame(loop);
+}
+
+loop();
+
+  &lt;/script&gt;
+ &lt;/body&gt;
+&lt;/html&gt;
+</pre>
+</div>
+
+<ol>
+</ol>
+
+<p>{{ EmbedLiveSample('Bouncing_balls', '100%', 480, "", "", "hide-codepen-jsfiddle") }}</p>
+
+<p>这个实例将会利用 <a href="/zh-CN/docs/Learn/JavaScript/Client-side_web_APIs/Drawing_graphics">Canvas API</a> 来在屏幕上画小球, 还会用到 <a href="/zh-CN/docs/Web/API/window/requestAnimationFrame">requestAnimationFrame</a> API 来使整个画面动起来 —— 我们并不要求你事先学习过这些 API 的相关知识,希望你完成这个练习之后会想去探索更多。这个过程中我们会用到一些漂亮的小东西并向你展示一些技巧,比如小球从墙上反弹,检查它们是否撞到了对方 (也就是碰撞检测)。</p>
+
+<h2 id="让我们开始吧">让我们开始吧</h2>
+
+<p>首先下载 <a class="external external-icon" href="https://raw.githubusercontent.com/roy-tian/learning-area/master/javascript/oojs/bouncing-balls/bouncing-balls-start.zip">bouncing-balls-start.zip</a>,其中包含以下三个文件:index.html、style.css 和 main.js。它们分别包含以下内容:</p>
+
+<ol>
+ <li>一个非常简单的 HTML 文档,包括一个 <code>&lt;h1&gt;</code> 元素、一个{{HTMLElement("canvas")}} 元素来画小球,还有一些元素将 CSS 和 JavaScript 运用到我们的 HTML 中。</li>
+ <li>一些非常简单的样式,主要是 <code>&lt;h1&gt;</code> 元素的样式和定位,另外还能使画面填充整个页面从而摆脱滚动条和边缘的空白(这样看起来非常简洁)</li>
+ <li>一些 JavaScript 用来设置 <code>&lt;canvas&gt;</code> 元素,并提供我们要用到的基本函数。</li>
+</ol>
+
+<p>脚本的第一部分是这样的:</p>
+
+<pre class="brush: js">const canvas = document.querySelector('canvas');
+
+const ctx = canvas.getContext('2d');
+
+const width = canvas.width = window.innerWidth;
+const height = canvas.height = window.innerHeight;</pre>
+
+<p>这个脚本使用变量代指了 <code>&lt;canvas&gt;</code> 元素, 然后对其调用 <code><a href="/en-US/docs/Web/API/HTMLCanvasElement/getContext">getContext()</a></code> 从而我们获得一个开始画画的环境。存储以上操作结果的变量(<code>ctx</code>)是一个对象,直接代指画布上的一块允许我们绘制 2D 图形的区域。</p>
+
+<p>接下来,我们设置 <code>width</code> 和 <code>height</code> 变量,并且让画布元素的宽和高(分别使用 <code>canvas.width</code> 和 <code>canvas.height</code> 表示)等于浏览器的宽和高(也就是网页显示的区域 — 可以从 {{domxref("Window.innerWidth")}} 和 {{domxref("Window.innerHeight")}}参数获得)。</p>
+
+<p>你会看到我们在这里串联了多个赋值表达式在一起,这样能更快地设置变量——这是完全正确的。</p>
+
+<p>原始脚本最后的部分如下:</p>
+
+<pre class="brush: js">function random(min,max) {
+  return Math.floor(Math.random()*(max-min)) + min;
+}
+
+function randomColor() {
+  return 'rgb(' +
+         random(0, 255) + ', ' +
+         random(0, 255) + ', ' +
+         random(0, 255) + ')';
+}</pre>
+
+<p>第一个函数为我们生成一个 <code>min</code> 至 <code>max</code> 之间的随机整数,第二个函数为我们生成一个随机的颜色值。</p>
+
+<h2 id="为程序中的小球建立模型">为程序中的小球建立模型</h2>
+
+<p>我们的项目中会有很多小球在屏幕上跳来跳去。因此这些小球会以相同的方式运作,从而我们可以通过一个对象实例化它们。首先,我们将下面的构造器加入到代码的底部。</p>
+
+<pre class="brush: js">function Ball(x, y, velX, velY, color, size) {
+  this.x = x;
+  this.y = y;
+  this.velX = velX;
+  this.velY = velY;
+  this.color = color;
+  this.size = size;
+}</pre>
+
+<p>这个构造器中定义了每个小球需要的参数:</p>
+
+<ul>
+ <li><code>x</code> 和 <code>y</code> 坐标 —— 小球在屏幕上最开始时候的坐标。坐标的范围从 0 (左上角)到浏览器视口的宽和高(右下角)。</li>
+ <li>水平和竖直速度(<code>velX</code> 和 <code>velY</code>)—— 我们会给每个小球一个水平和竖直速度。实际上,当我们让这些球开始运动时候,每过一帧都会给小球的 <code>x</code> 和 <code>y</code> 坐标加一次这些值。</li>
+ <li><code>color</code> —— 每一个小球会有自己的颜色。</li>
+ <li><code>size</code> —— 每一个小球会有自己的大小 — 也就是小球的半径,以像素为单位。</li>
+</ul>
+
+<p>这里说明了小球的属性,那么方法呢?别忘了我们要让小球动起来。</p>
+
+<h3 id="画小球">画小球</h3>
+
+<p>首先给小球的原型加上 <code>draw()</code> 方法:</p>
+
+<pre class="brush: js">Ball.prototype.draw = function() {
+ ctx.beginPath();
+ ctx.fillStyle = this.color;
+ ctx.arc(this.x, this.y, this.size, 0, 2 * Math.PI);
+ ctx.fill();
+}</pre>
+
+<p>通过使用这个函数,通过使用我们之前定义的 <code>ctx</code>对象 的方法,我们就可以让在屏幕上画出小球了。<code>ctx</code> 的内容区域就像是一张纸,现在我们就可以命令我们的笔画一点东西。</p>
+
+<ul>
+ <li>首先,我们使用 <code><a href="/zh-CN/docs/Web/API/CanvasRenderingContext2D/beginPath">beginPath()</a></code> 来声明我们现在要开始在纸上画一个图形了。</li>
+ <li>然后,我们使用 <code><a href="/zh-CN/docs/Web/API/CanvasRenderingContext2D/fillStyle">fillStyle</a></code> 来定义这个图形的颜色 — 这个值正是小球的颜色属性。</li>
+ <li>接下来,我们使用 <code><a href="/zh-CN/docs/Web/API/CanvasRenderingContext2D/arc">arc()</a></code> 方法来在纸上画出一段圆弧。有这些参数:
+ <ul>
+ <li><code>x</code> 和 <code>y</code> 是圆弧的中心的坐标 —— 也就是小球的中心坐标。</li>
+ <li>圆弧的半径 —— 小球的半径。</li>
+ <li>最后两个参数是开始和结束,也就是圆弧对应的夹角,单位以弧度表示。这里我们用的是 0 和 <code>2 * PI</code>,也就是 360 度(如果你设置成 0 和 <code>1 * PI</code>,则只会出现一个半圆,也就是 180 度)</li>
+ </ul>
+ </li>
+ <li>最后,我们使用 <code><a href="/zh-CN/docs/Web/API/CanvasRenderingContext2D/fill">fill()</a></code> 方法,也就是声明我们结束了以 <code>beginPath()</code> 开始的绘画,并且使用我们之前设置的颜色进行填充。 </li>
+</ul>
+
+<p>现在你已经可以测试你的对象了。</p>
+
+<ol>
+ <li>保存代码,将 HTML 加载到浏览器中。</li>
+ <li>打开浏览器中的 JavaScript 控制台,刷新页面,从而画布可以根据可视的区域调整自己的大小。</li>
+ <li>通过下面的代码创建一个小球实例。
+ <pre class="brush: js">let testBall = new Ball(50, 100, 4, 4, 'blue', 10);</pre>
+ </li>
+ <li>你可以调用实例的这些属性。
+ <pre class="brush: js">testBall.x
+testBall.size
+testBall.color
+testBall.draw()</pre>
+ </li>
+ <li>当你键入最后一行的时候,你会在你的画布上看到一个小球被画出来了。</li>
+</ol>
+
+<h3 id="更新小球的数据">更新小球的数据</h3>
+
+<p>我们可以在一个固定位置画出小球,但是他们不会动,我们需要一个函数来更新一些东西。在 JavaScript 文件底部加上下面的代码,也就是给小球原型加上一个 update() 方法。</p>
+
+<pre class="brush: js">Ball.prototype.update = function() {
+ if ((this.x + this.size) &gt;= width) {
+ this.velX = -(this.velX);
+ }
+
+ if ((this.x - this.size) &lt;= 0) {
+ this.velX = -(this.velX);
+ }
+
+ if ((this.y + this.size) &gt;= height) {
+ this.velY = -(this.velY);
+ }
+
+ if ((this.y - this.size) &lt;= 0) {
+ this.velY = -(this.velY);
+ }
+
+ this.x += this.velX;
+ this.y += this.velY;
+}</pre>
+
+<p>函数的前四个部分用来检查小球是否碰到画布的边缘。如果碰到,我们反转小球的速度方向来让它向反方向移动。就比如说,如果小球正向上移动 (正 <code>velY</code>),然后垂直速度发生改变,小球就向下移动 (负 <code>velY</code>)。</p>
+
+<p>在这四部分中,我们:</p>
+
+<ul>
+ <li>检查小球的 <code>x</code> 坐标是否大于画布的宽度(小球会从右边缘离开)。</li>
+ <li>检查小球的 <code>x</code> 坐标是否小于0(小球会从左边缘离开)。</li>
+ <li>检查小球的 <code>y</code> 坐标是否大于画布的高度(小球会从下边缘离开)。</li>
+ <li>检查小球的 <code>y</code> 坐标是否小于0(小球会从上边缘离开)。</li>
+</ul>
+
+<p>在每种情况下,我们都会加上小球的半径,因为 <code>x</code>/<code>y</code> 坐标是小球中心的坐标,我们希望小球在其边界接触浏览器窗口的边界时反弹,而不是小球的一部分都不见了再返回。</p>
+
+<p>最后两行,我们将 <code>velX</code> 的值加到 <code>x</code> 的坐标上,将 <code>velY</code> 的值加到 <code>y</code> 坐标上 —— 每次调用这个方法的时候小球就移动这么多。</p>
+
+<p>暂时先这样做; 让我们继续做一些动画!</p>
+
+<h2 id="让球动起来">让球动起来 </h2>
+
+<p>现在就变得非常有趣了。我们在画布上加上一些小球,并且让他们动起来。</p>
+
+<ol>
+ <li>首先我们需要一个地方储存小球,下面的数组会干这件事 —— 现在将它添加到你的代码底部:
+ <pre class="brush: js">let balls = [];
+
+while (balls.length &lt; 25) {
+  let size = random(10, 20);
+    let ball = new Ball(
+  // 为避免绘制错误,球至少离画布边缘球本身一倍宽度的距离
+    random(0 + size, width - size),
+    random(0 + size, height - size),
+    random(-7, 7),
+    random(-7, 7),
+    randomColor(),
+    size
+    );
+ balls.push(ball);
+ }
+</pre>
+ </li>
+ <li>
+ <p>几乎所有的动画效果都会用到一个运动循环,也就是每一帧都自动更新视图。这是大多数游戏或者其他类似项目的基础。</p>
+ </li>
+ <li>现在将它添加到你的代码底部:
+ <pre class="brush: js">function loop() {
+ ctx.fillStyle = 'rgba(0, 0, 0, 0.25)';
+ ctx.fillRect(0, 0, width, height);
+
+ for (let i = 0; i &lt; balls.length; i++) {
+ balls[i].draw();
+ balls[i].update();
+ }
+
+ requestAnimationFrame(loop);
+}</pre>
+
+ <p><code>loop()</code> 函数做了下面的事情:</p>
+
+ <ul>
+ <li>将整个画布的颜色设置成半透明的黑色。然后使用  <code>fillRect()</code>(这四个参数分别是起始的坐标、绘制的矩形的宽和高)画出一个填充满整个画布的矩形。这是在下一个视图画出来时用来遮住之前的视图的。如果不这样做得话,你就会在屏幕上看到一条蛇的形状而不是小球的运动了。用来填充的颜色设置成半透明的<code>rgba(0,0,0,0.25)</code>,也就是让之前的视图留下来一点点,从而你可以看到小球运动时的轨迹。如果你将 0.25 设置成 1 时,你就完全看不到了。试着改变其中的值查看造成的影响。</li>
+ <li>当且仅当小球数量小于 25 时,将 <code>random()</code> 函数产生的数字传入新的小球实例从而创建一个新的小球,并且加入到数组中。因此当屏幕上有 25 个小球时,不会再出现更多小球。你可以改变这个值,从而看到不同小球个数造成的影响。如果你的电脑或者浏览器性能不怎么样的话,几千个小球的速度就会明显慢下来。</li>
+ <li>遍历数组中的所有小球,并且让每个小球都调用 <code>draw()</code> 和 <code>update()</code> 函数来将自己画出来,并且再接下来的每一帧都按照其速度进行位置的更新。</li>
+ <li>使用 <code>requestAnimationFrame()</code> 方法再运行一次函数 —— 当一个函数正在运行时传递相同的函数名,从而每隔一小段时间都会运行一次这个函数,这样我们可以得到一个平滑的动画效果。这主要是通过递归完成的 —— 也就是说函数每次运行的时候都会调用自己,从而可以一遍又一遍得运行。</li>
+ </ul>
+ </li>
+ <li>最后但是非常重要的是,加上下面这一行 —— 让动画开始运行的话我们需要调用这个函数。
+ <pre class="brush: js">loop();</pre>
+ </li>
+</ol>
+
+<p>完成这些基础的之后在浏览器打开测试一下!</p>
+
+<h2 id="添加碰撞检测">添加碰撞检测</h2>
+
+<p>现在会更加有趣,给我们的项目加上碰撞检测,从而小球会知道他们正在撞击其他的球。</p>
+
+<ol>
+ <li>首先在 <code>update()</code> 方法后添加以下方法 (即 <code>Ball.prototype.update</code> 的下面)。
+
+ <pre class="brush: js">Ball.prototype.collisionDetect = function() {
+ for (let j = 0; j &lt; balls.length; j++) {
+ if (this !== balls[j]) {
+ const dx = this.x - balls[j].x;
+ const dy = this.y - balls[j].y;
+ const distance = Math.sqrt(dx * dx + dy * dy);
+
+ if (distance &lt; this.size + balls[j].size) {
+ balls[j].color = this.color = randomColor();
+ }
+ }
+ }
+}</pre>
+
+ <p>这个方法有一点点复杂,如果不理解的话不必过分担心,下面是对它的解释:</p>
+
+ <ul>
+ <li>对于每个小球,我们都要检查其他的小球是否和当前这个小球相撞了。为了达到此目的,我们构造另外一个 <code>for</code> 循环来遍历 <code>balls[]</code> 数组中的小球。</li>
+ <li>在循环里面,我们使用一个 <code>if</code> 语句来检查遍历的小球是否是当前的小球。我们不希望检测到一个小球撞到了自己!为了达到这个目的,我们需要检查当前小球 (即正在调用 <code>collisionDetect</code> 方法的球) 是否和被循环到的小球 (<code>for</code> 循环检测中的当前遍历所引用的球) 是不是同一个。我们使用 <code>!</code> 来否定判断,因此只有两个小球<strong>不是</strong>同一个时,条件判断中的代码才会运行。</li>
+ <li>我们使用了一个常见的算法来检测两个小球是否相撞了,两个小球中心的距离是否小于两个小球的半径之和。这些会在 <a href="/zh-CN/docs/Games/Techniques/2D_collision_detection">2D 碰撞检测</a> 介绍地更加详细。</li>
+ <li>如果检测到了碰撞,会运行 <code>if</code> 语句中的代码。我们会将两个小球的颜色都设置成随机的一种。我们也可以将这步操作变得复杂一点,比如让两个小球弹开,那样需要植入更加复杂的代码。像这样的物理场景,有以下专门的库比如 <a href="http://wellcaffeinated.net/PhysicsJS/">PhysicsJS</a>,<a href="http://brm.io/matter-js/">matter.js</a>,<a href="http://phaser.io/">Phaser</a> 等。</li>
+ </ul>
+ </li>
+ <li>我们也需要在每一帧动画中都调用这个函数,因此在 <code>balls[i].update()</code> 加上下面的代码:
+ <pre class="brush: js">balls[i].collisionDetect();</pre>
+ </li>
+ <li>保存文件,刷新浏览器,你就会看到小球在撞击时会变色!</li>
+</ol>
+
+<div class="note">
+<p><strong>注:</strong>如果示例无法顺利执行,可参考我们的 <a class="external external-icon" href="https://github.com/roy-tian/learning-area/tree/master/javascript/oojs/bouncing-balls">最终版本</a>,或者 <a class="external external-icon" href="https://roy-tian.github.io/learning-area/javascript/oojs/bouncing-balls/">在线试用</a>。</p>
+</div>
+
+<h2 id="概要">概要</h2>
+
+<p>我们希望你玩得开心,编写出你自己的随机弹跳球的例子,在整个程序中使用各种对象和面向对象的技术! 在你实际运用对象中能提供一些有用的帮助。</p>
+
+<p>对象文章就到这里了。现在剩下的就是在下一节的对象评估中测试你的技能。</p>
+
+<h2 id="另请参阅">另请参阅</h2>
+
+<ul>
+ <li><a href="/zh-CN/docs/Web/API/Canvas_API/Tutorial">Canvas tutorial</a> —— 2D canvas 初学者指南.</li>
+ <li><a href="/zh-CN/docs/Web/API/window/requestAnimationFrame">requestAnimationFrame()</a></li>
+ <li><a href="zh-CN/docs/Games/Techniques/2D_collision_detection">2D 碰撞检测</a></li>
+ <li><a href="/zh-CN/docs/Games/Techniques/3D_collision_detection">3D 碰撞检测</a></li>
+ <li><a href="/zh-CN/docs/Games/Tutorials/2D_Breakout_game_pure_JavaScript">纯 JavaScript 编写的 2D 消除游戏</a> —— 一个很好的2D游戏开发初学者教程.</li>
+ <li><a href="/zh-CN/docs/Games/Tutorials/2D_breakout_game_Phaser">Phaser 编写的 2D 消除游戏</a> —— JavaScript游戏库构建2D游戏的基础知识。</li>
+</ul>
+
+<p>{{PreviousMenuNext("Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects/Adding_bouncing_balls_features", "Learn/JavaScript/Objects")}}</p>
+
+<h2 id="本章目录">本章目录</h2>
+
+<ul>
+ <li><a href="/zh-CN/docs/Learn/JavaScript/Objects/Basics">对象基础</a></li>
+ <li><a href="/zh-CN/docs/Learn/JavaScript/Objects/Object-oriented_JS">适合初学者的 JavaScript 面向对象</a></li>
+ <li><a href="/zh-CN/docs/Learn/JavaScript/Objects/Object_prototypes">对象原型</a></li>
+ <li><a href="/zh-CN/docs/Learn/JavaScript/Objects/Inheritance">JavaScript 中的继承</a></li>
+ <li><a href="/zh-CN/docs/Learn/JavaScript/Objects/JSON">使用 JSON 数据</a></li>
+ <li><a href="/zh-CN/docs/Learn/JavaScript/Objects/Object_building_practice">构建对象实战</a></li>
+ <li><a href="/zh-CN/docs/Learn/JavaScript/Objects/Adding_bouncing_balls_features">向“弹跳球”演示程序添加新功能</a></li>
+</ul>
diff --git a/files/zh-cn/learn/javascript/objects/object_prototypes/index.html b/files/zh-cn/learn/javascript/objects/object_prototypes/index.html
new file mode 100644
index 0000000000..89028e5e17
--- /dev/null
+++ b/files/zh-cn/learn/javascript/objects/object_prototypes/index.html
@@ -0,0 +1,357 @@
+---
+title: 对象原型
+slug: Learn/JavaScript/Objects/Object_prototypes
+tags:
+ - JavaScript
+ - 初学者
+ - 原型
+ - 对象
+translation_of: Learn/JavaScript/Objects/Object_prototypes
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/JavaScript/Objects/Object-oriented_JS", "Learn/JavaScript/Objects/Inheritance", "Learn/JavaScript/Objects")}}</div>
+
+<p class="summary">通过原型这种机制,JavaScript 中的对象从其他对象继承功能特性;这种继承机制与经典的面向对象编程语言的继承机制不同。本文将探讨这些差别,解释原型链如何工作,并了解如何通过 <code>prototype</code> 属性向已有的构造器添加方法</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">预备知识:</th>
+ <td>基本的计算机素养,对 HTML 和 CSS 有基本的理解,熟悉 JavaScript 基础(参见 <a href="/zh-CN/docs/Learn/JavaScript/First_steps">First steps</a> 和 <a href="/zh-CN/docs/Learn/JavaScript/Building_blocks">Building blocks</a>)以及面向对象的JavaScript (OOJS) 基础(参见 <a href="/zh-CN/docs/Learn/JavaScript/Object-oriented/Introduction">Introduction to objects</a>)。</td>
+ </tr>
+ <tr>
+ <th scope="row">目标:</th>
+ <td>理解 JavaScript 对象原型、原型链如何工作、如何向 <code>prototype</code> 属性添加新的方法。</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="基于原型的语言?">基于原型的语言?</h2>
+
+<p>JavaScript 常被描述为一种<strong>基于原型的语言 (prototype-based language)</strong>——每个对象拥有一个<strong>原型对象</strong>,对象以其原型为模板、从原型继承方法和属性。原型对象也可能拥有原型,并从中继承方法和属性,一层一层、以此类推。这种关系常被称为<strong>原型链 (prototype chain)</strong>,它解释了为何一个对象会拥有定义在其他对象中的属性和方法。</p>
+
+<p>准确地说,这些属性和方法定义在Object的构造器函数(constructor functions)之上的<code>prototype</code>属性上,而非对象实例本身。</p>
+
+<p>在传统的 OOP 中,首先定义“类”,此后创建对象实例时,类中定义的所有属性和方法都被复制到实例中。在 JavaScript 中并不如此复制——而是在对象实例和它的构造器之间建立一个链接(它是__proto__属性,是从构造函数的<code>prototype</code>属性派生的),之后通过上溯原型链,在构造器中找到这些属性和方法。</p>
+
+<div class="note">
+<p><strong>注意: </strong>理解对象的原型(可以通过<code><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getPrototypeOf">Object.getPrototypeOf(obj)</a></code>或者已被弃用的<code><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto">__proto__</a></code>属性获得)与构造函数的<code>prototype</code>属性之间的区别是很重要的。前者是每个实例上都有的属性,后者是构造函数的属性。也就是说,<code>Object.getPrototypeOf(new Foobar())</code>和<code>Foobar.prototype</code>指向着同一个对象。</p>
+</div>
+
+<p>以上描述很抽象;我们先看一个例子。</p>
+
+<h2 id="使用Javascript中的原型">使用Javascript中的原型</h2>
+
+<p>在javascript中,函数可以有属性。 每个函数都有一个特殊的属性叫作<code>原型(prototype)</code> ,正如下面所展示的。请注意,下面的代码是独立的一段(在网页中没有其他代码的情况下,这段代码是安全的)。为了最好的学习体验,你最好打开一个控制台 (在Chrome和Firefox中,可以按Ctrl+Shift+I来打开)切换到"控制台" 选项卡, 复制粘贴下面的JavaScript代码,然后按回车来运行.</p>
+
+<pre class="brush: js">function doSomething(){}
+console.log( doSomething.prototype );
+// It does not matter how you declare the function, a
+// function in javascript will always have a default
+// prototype property.
+var doSomething = function(){};
+console.log( doSomething.prototype );
+</pre>
+
+<p>正如上面所看到的, <code>doSomething</code> 函数有一个默认的原型属性,它在控制台上面呈现了出来. 运行这段代码之后,控制台上面应该出现了像这样的一个对象.</p>
+
+<pre class="brush: js">{
+ constructor: ƒ doSomething(),
+ __proto__: {
+ constructor: ƒ Object(),
+ hasOwnProperty: ƒ hasOwnProperty(),
+ isPrototypeOf: ƒ isPrototypeOf(),
+ propertyIsEnumerable: ƒ propertyIsEnumerable(),
+ toLocaleString: ƒ toLocaleString(),
+ toString: ƒ toString(),
+ valueOf: ƒ valueOf()
+ }
+}</pre>
+
+<p>现在,我们可以添加一些属性到 doSomething 的原型上面,如下所示.</p>
+
+<pre class="brush: js">function doSomething(){}
+doSomething.prototype.foo = "bar";
+console.log( doSomething.prototype );
+</pre>
+
+<p>结果:</p>
+
+<pre class="brush: js">{
+ foo: "bar",
+ constructor: ƒ doSomething(),
+ __proto__: {
+ constructor: ƒ Object(),
+ hasOwnProperty: ƒ hasOwnProperty(),
+ isPrototypeOf: ƒ isPrototypeOf(),
+ propertyIsEnumerable: ƒ propertyIsEnumerable(),
+ toLocaleString: ƒ toLocaleString(),
+ toString: ƒ toString(),
+ valueOf: ƒ valueOf()
+ }
+}
+</pre>
+
+<p> </p>
+
+<p>然后,我们可以使用 new 运算符来在现在的这个原型基础之上,创建一个 <code>doSomething</code> 的实例。正确使用 new 运算符的方法就是在正常调用函数时,在函数名的前面加上一个 <code>new</code> 前缀. 通过这种方法,在调用函数前加一个 <code>new</code> ,它就会返回一个这个函数的实例化对象. 然后,就可以在这个对象上面添加一些属性. 看.</p>
+
+<pre class="brush: js">function doSomething(){}
+doSomething.prototype.foo = "bar"; // add a property onto the prototype
+var doSomeInstancing = new doSomething();
+doSomeInstancing.prop = "some value"; // add a property onto the object
+console.log( doSomeInstancing );</pre>
+
+<p> </p>
+
+<p>结果:</p>
+
+<p> </p>
+
+<pre class="brush: js">{
+ prop: "some value",
+ __proto__: {
+ foo: "bar",
+ constructor: ƒ doSomething(),
+ __proto__: {
+ constructor: ƒ Object(),
+ hasOwnProperty: ƒ hasOwnProperty(),
+ isPrototypeOf: ƒ isPrototypeOf(),
+ propertyIsEnumerable: ƒ propertyIsEnumerable(),
+ toLocaleString: ƒ toLocaleString(),
+ toString: ƒ toString(),
+ valueOf: ƒ valueOf()
+ }
+ }
+}</pre>
+
+<p>就像上面看到的, <code>doSomeInstancing</code> 的 <code>__proto__</code> 属性就是<code>doSomething.prototype</code>. 但是这又有什么用呢? 好吧,当你访问 <code>doSomeInstancing</code> 的一个属性, 浏览器首先查找 <code>doSomeInstancing</code> 是否有这个属性. 如果 <code>doSomeInstancing</code> 没有这个属性, 然后浏览器就会在 <code>doSomeInstancing</code> 的 <code>__proto__</code> 中查找这个属性(也就是 doSomething.prototype). 如果 doSomeInstancing 的 <code>__proto__</code> 有这个属性, 那么 doSomeInstancing 的 <code>__proto__</code> 上的这个属性就会被使用. 否则, 如果 doSomeInstancing 的 <code>__proto__</code> 没有这个属性, 浏览器就会去查找 doSomeInstancing 的 <code>__proto__</code> 的 <code>__proto__</code> ,看它是否有这个属性. 默认情况下, 所有函数的原型属性的 <code>__proto__</code> 就是 <code>window.Object.prototype</code>. 所以 doSomeInstancing 的 <code>__proto__</code> 的 <code>__proto__</code> (也就是 doSomething.prototype 的 <code>__proto__</code> (也就是 <code>Object.prototype</code>)) 会被查找是否有这个属性. 如果没有在它里面找到这个属性, 然后就会在 doSomeInstancing 的 <code>__proto__</code> 的 <code>__proto__</code> 的 <code>__proto__</code> 里面查找. 然而这有一个问题: doSomeInstancing 的 <code>__proto__</code> 的 <code>__proto__</code> 的 <code>__proto__</code> 不存在. 最后, 原型链上面的所有的 <code>__proto__</code> 都被找完了, 浏览器所有已经声明了的 <code>__proto__</code> 上都不存在这个属性,然后就得出结论,这个属性是 <code>undefined</code>.</p>
+
+<pre class="brush: js">function doSomething(){}
+doSomething.prototype.foo = "bar";
+var doSomeInstancing = new doSomething();
+doSomeInstancing.prop = "some value";
+console.log("doSomeInstancing.prop: " + doSomeInstancing.prop);
+console.log("doSomeInstancing.foo: " + doSomeInstancing.foo);
+console.log("doSomething.prop: " + doSomething.prop);
+console.log("doSomething.foo: " + doSomething.foo);
+console.log("doSomething.prototype.prop: " + doSomething.prototype.prop);
+console.log("doSomething.prototype.foo: " + doSomething.prototype.foo);</pre>
+
+<p>结果:</p>
+
+<pre class="brush: js">doSomeInstancing.prop: some value
+doSomeInstancing.foo: bar
+doSomething.prop: undefined
+doSomething.foo: undefined
+doSomething.prototype.prop: undefined
+doSomething.prototype.foo: bar</pre>
+
+<h2 id="理解原型对象">理解原型对象</h2>
+
+<p>让我们回到 <code>Person()</code> 构造器的例子。请把这个例子载入浏览器。如果你还没有看完上一篇文章并写好这个例子,也可以使用 <a href="http://mdn.github.io/learning-area/javascript/oojs/introduction/oojs-class-further-exercises.html">oojs-class-further-exercises.html</a> 中的例子(亦可参考<a href="https://github.com/mdn/learning-area/blob/master/javascript/oojs/introduction/oojs-class-further-exercises.html">源代码</a>)。</p>
+
+<p>本例中我们将定义一个构造器函数:</p>
+
+<pre class="brush: js">function Person(first, last, age, gender, interests) {
+
+ // 属性与方法定义
+
+};</pre>
+
+<p>然后创建一个对象实例:</p>
+
+<pre class="brush: js">var person1 = new Person('Bob', 'Smith', 32, 'male', ['music', 'skiing']);</pre>
+
+<p>在 JavaScript 控制台输入 "<code>person1.</code>",你会看到,浏览器将根据这个对象的可用的成员名称进行自动补全:</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/13853/object-available-members.png" style="display: block; margin: 0 auto;"></p>
+
+<p>在这个列表中,你可以看到定义在 <code>person1</code> 的原型对象、即 <code>Person()</code> 构造器中的成员—— <code>name</code>、<code>age</code>、<code>gender</code>、<code>interests</code>、<code>bio</code>、<code>greeting</code>。同时也有一些其他成员—— <code>watch</code>、<code>valueOf</code> 等等——这些成员定义在 <code>Person()</code> 构造器的原型对象、即 <code><a href="/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object">Object</a></code> 之上。下图展示了原型链的运作机制。</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/13891/MDN-Graphics-person-person-object-2.png" style="display: block; height: 150px; margin: 0px auto; width: 700px;"></p>
+
+<p>那么,调用 <code>person1</code> 的“实际定义在 <code>Object</code> 上”的方法时,会发生什么?比如:</p>
+
+<pre class="brush: js">person1.valueOf()</pre>
+
+<p>这个方法仅仅返回了被调用对象的值。在这个例子中发生了如下过程:</p>
+
+<ul>
+ <li>浏览器首先检查,<code>person1</code> 对象是否具有可用的 <code>valueOf()</code> 方法。</li>
+ <li>如果没有,则浏览器检查 <code>person1</code> 对象的原型对象(即 <code>Person</code>构造函数的prototype属性所指向的对象)是否具有可用的 <code>valueof()</code> 方法。</li>
+ <li>如果也没有,则浏览器检查 <code>Person()</code> 构造函数的prototype属性所指向的对象的原型对象(即 <code>Object</code>构造函数的prototype属性所指向的对象)是否具有可用的 <code>valueOf()</code> 方法。这里有这个方法,于是该方法被调用。</li>
+</ul>
+
+<div class="note">
+<p><strong>注意</strong>:必须重申,原型链中的方法和属性<strong>没有</strong>被复制到其他对象——它们被访问需要通过前面所说的“原型链”的方式。</p>
+</div>
+
+<div class="note">
+<p><strong>注意</strong>:没有官方的方法用于直接访问一个对象的原型对象——原型链中的“连接”被定义在一个内部属性中,在 JavaScript 语言标准中用 <code>[[prototype]]</code> 表示(参见 {{glossary("ECMAScript")}})。然而,大多数现代浏览器还是提供了一个名为 <code><a href="/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/proto">__proto__</a></code> (前后各有2个下划线)的属性,其包含了对象的原型。你可以尝试输入 <code>person1.__proto__</code> 和 <code>person1.__proto__.__proto__</code>,看看代码中的原型链是什么样的!</p>
+</div>
+
+<h2 id="prototype_属性:继承成员被定义的地方">prototype 属性:继承成员被定义的地方</h2>
+
+<p>那么,那些继承的属性和方法在哪儿定义呢?如果你查看 <code><a href="/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object">Object</a></code> 参考页,会发现左侧列出许多属性和方法——大大超过我们在 <code>person1</code> 对象中看到的继承成员的数量。某些属性或方法被继承了,而另一些没有——为什么呢?</p>
+
+<p>原因在于,继承的属性和方法是定义在 <code>prototype</code> 属性之上的(你可以称之为子命名空间 (sub namespace) )——那些以 <code>Object.prototype.</code> 开头的属性,而非仅仅以 <code>Object.</code> 开头的属性。<code>prototype</code> 属性的值是一个对象,我们希望被原型链下游的对象继承的属性和方法,都被储存在其中。</p>
+
+<p>于是 <code><a href="/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/watch">Object.prototype.watch()</a>、</code><code><a href="/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/valueOf">Object.prototype.valueOf()</a></code> 等等成员,适用于任何继承自 <code>Object()</code> 的对象类型,包括使用构造器创建的新的对象实例。</p>
+
+<p><code><a href="/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/is">Object.is()</a></code>、<code><a href="zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/keys">Object.keys()</a></code>,以及其他不在 <code>prototype</code> 对象内的成员,不会被“对象实例”或“继承自 <code>Object()</code> 的对象类型”所继承。这些方法/属性仅能被 <code>Object()</code> 构造器自身使用。</p>
+
+<div class="note">
+<p><strong>注意</strong>:这看起来很奇怪——构造器本身就是函数,你怎么可能在构造器这个函数中定义一个方法呢?其实函数也是一个对象类型,你可以查阅 <code><a href="/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function">Function()</a></code> 构造器的参考文档以确认这一点。</p>
+</div>
+
+<ol>
+ <li>你可以检查已有的 <code>prototype</code> 属性。回到先前的例子,在 JavaScript 控制台输入:
+
+ <pre class="brush: js">Person.prototype</pre>
+ </li>
+ <li>输出并不多,毕竟我们没有为自定义构造器的原型定义任何成员。缺省状态下,构造器的 <code>prototype</code> 属性初始为空白。现在尝试:
+ <pre class="brush: js">Object.prototype</pre>
+ </li>
+</ol>
+
+<p>你会看到 <code>Object</code> 的 <code>prototype</code> 属性上定义了大量的方法;如前所示,继承自 <code>Object</code> 的对象都可以使用这些方法。</p>
+
+<p>JavaScript 中到处都是通过原型链继承的例子。比如,你可以尝试从 <code><a href="/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String">String</a></code>、<code><a href="/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Date">Date</a></code>、<code><a href="/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Number">Number</a></code> 和 <code><a href="/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array">Array</a></code> 全局对象的原型中寻找方法和属性。它们都在原型上定义了一些方法,因此当你创建一个字符串时:</p>
+
+<pre class="brush: js">var myString = 'This is my string.';</pre>
+
+<p><code>myString</code> 立即具有了一些有用的方法,如 <code><a href="/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String/split">split()</a></code>、<code><a href="/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String/indexOf">indexOf()</a></code>、<code><a href="/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String/replace">replace()</a></code> 等。</p>
+
+<div class="warning">
+<p><strong>重要</strong>:<code>prototype</code> 属性大概是 JavaScript 中最容易混淆的名称之一。你可能会认为,<code>this</code> 关键字指向当前对象的原型对象,其实不是(还记得么?原型对象是一个内部对象,应当使用<code> __proto__</code> 访问)。<code>prototype</code> 属性包含(指向)一个对象,你在这个对象中定义需要被继承的成员。</p>
+</div>
+
+<p><strong style="color: #4d4e53; font-size: 2.14286rem; font-weight: 700; letter-spacing: -1px;">create()</strong></p>
+
+<p>我们曾经讲过如何用 <code><a href="/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/create">Object.create()</a></code> 方法创建新的对象实例。</p>
+
+<ol>
+ <li>例如,在上个例子的 JavaScript 控制台中输入:
+ <pre class="brush: js">var person2 = Object.create(person1);</pre>
+ </li>
+ <li><code>create()</code> 实际做的是从指定原型对象创建一个新的对象。这里以 <code>person1</code> 为原型对象创建了 <code>person2</code> 对象。在控制台输入:
+ <pre class="brush: js">person2.__proto__</pre>
+ </li>
+</ol>
+
+<p>结果返回对象<code>person1</code>。</p>
+
+<h2 id="constructor_属性">constructor 属性</h2>
+
+<p>每个实例对象都从原型中继承了一个constructor属性,该属性指向了用于构造此实例对象的构造函数。</p>
+
+<ol>
+ <li>例如,在控制台中尝试下面的指令:
+ <pre class="brush: js">person1.constructor
+person2.constructor</pre>
+
+ <p>都将返回 <code>Person()</code> 构造器,因为该构造器包含这些实例的原始定义。</p>
+
+ <p>一个小技巧是,你可以在 <code>constructor</code> 属性的末尾添加一对圆括号(括号中包含所需的参数),从而用这个构造器创建另一个对象实例。毕竟构造器是一个函数,故可以通过圆括号调用;只需在前面添加 <code>new</code> 关键字,便能将此函数作为构造器使用。</p>
+ </li>
+ <li>在控制台中输入:
+ <pre class="brush: js">var person3 = new person1.constructor('Karen', 'Stephenson', 26, 'female', ['playing drums', 'mountain climbing']);</pre>
+ </li>
+ <li>现在尝试访问新建对象的属性,例如:
+ <pre class="brush: js">person3.name.first
+person3.age
+person3.bio()</pre>
+ </li>
+</ol>
+
+<p>正常工作。通常你不会去用这种方法创建新的实例;但如果你刚好因为某些原因没有原始构造器的引用,那么这种方法就很有用了。</p>
+
+<p>此外,<code><a href="/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor">constructor</a></code> 属性还有其他用途。比如,想要获得某个对象实例的构造器的名字,可以这么用:</p>
+
+<pre class="brush: js">instanceName.constructor.name</pre>
+
+<p>具体地,像这样:</p>
+
+<pre class="brush: js">person1.constructor.name</pre>
+
+<h2 id="修改原型">修改原型</h2>
+
+<p>从我们从下面这个例子来看一下如何修改构造器的 <code>prototype</code> 属性。</p>
+
+<ol>
+ <li>回到 <a href="http://mdn.github.io/learning-area/javascript/oojs/introduction/oojs-class-further-exercises.html">oojs-class-further-exercises.html</a> 的例子,在本地为<a href="https://github.com/mdn/learning-area/blob/master/javascript/oojs/introduction/oojs-class-further-exercises.html">源代码</a>创建一个副本。在已有的 JavaScript 的末尾添加如下代码,这段代码将为构造器的 <code>prototype</code> 属性添加一个新的方法:
+
+ <pre class="brush: js">Person.prototype.farewell = function() {
+ alert(this.name.first + ' has left the building. Bye for now!');
+}</pre>
+ </li>
+ <li>保存代码,在浏览器中加载页面,然后在控制台输入:
+ <pre class="brush: js">person1.farewell();</pre>
+ </li>
+</ol>
+
+<p>你会看到一条警告信息,其中还显示了构造器中定义的人名;这很有用。但更关键的是,整条继承链动态地更新了,任何由此构造器创建的对象实例都自动获得了这个方法。</p>
+
+<p>再想一想这个过程。我们的代码中定义了构造器,然后用这个构造器创建了一个对象实例,<em>此后</em>向构造器的 <code>prototype</code> 添加了一个新的方法:</p>
+
+<pre class="brush: js">function Person(first, last, age, gender, interests) {
+
+ // 属性与方法定义
+
+};
+
+var person1 = new Person('Tammi', 'Smith', 32, 'neutral', ['music', 'skiing', 'kickboxing']);
+
+Person.prototype.farewell = function() {
+ alert(this.name.first + ' has left the building. Bye for now!');
+}</pre>
+
+<p>但是 <code>farewell()</code> 方法<em>仍然</em>可用于 <code>person1</code> 对象实例——旧有对象实例的可用功能被自动更新了。这证明了先前描述的原型链模型。这种继承模型下,上游对象的方法不会复制到下游的对象实例中;下游对象本身虽然没有定义这些方法,但浏览器会通过上溯原型链、从上游对象中找到它们。这种继承模型提供了一个强大而可扩展的功能系统。</p>
+
+<div class="note">
+<p><strong>注意</strong>:如果运行样例时遇到问题,请参阅 <a href="https://github.com/mdn/learning-area/blob/master/javascript/oojs/advanced/oojs-class-prototype.html">oojs-class-prototype.html</a> 样例(也可查看<a href="http://mdn.github.io/learning-area/javascript/oojs/advanced/oojs-class-prototype.html">即时运行</a>)。</p>
+</div>
+
+<p>你很少看到属性定义在 prototype 属性中,因为如此定义不够灵活。比如,你可以添加一个属性:</p>
+
+<pre class="brush: js">Person.prototype.fullName = 'Bob Smith';</pre>
+
+<p>但这不够灵活,因为人们可能不叫这个名字。用 <code>name.first</code> 和 <code>name.last</code> 组成 <code>fullName</code> 会好很多:</p>
+
+<pre class="brush: js">Person.prototype.fullName = this.name.first + ' ' + this.name.last;</pre>
+
+<p>然而,这么做是无效的,因为本例中 <code>this</code> 引用全局范围,而非函数范围。访问这个属性只会得到 <code>undefined undefined</code>。但这个语句若放在 先前定义在 <code>prototype</code> 上的方法中则有效,因为此时语句位于函数范围内,从而能够成功地转换为对象实例范围。你可能会在 <code>prototype</code> 上定义常属性 (constant property) (指那些你永远无需改变的属性),但一般来说,在构造器内定义属性更好。</p>
+
+<div class="note">
+<p><strong>译者注</strong>:关于 <code>this</code> 关键字指代(引用)什么范围/哪个对象,这个问题超出了本文讨论范围。事实上,这个问题有点复杂,如果现在你没能理解,也不用担心。</p>
+</div>
+
+<p>事实上,一种极其常见的对象定义模式是,在构造器(函数体)中定义属性、在 <code>prototype</code> 属性上定义方法。如此,构造器只包含属性定义,而方法则分装在不同的代码块,代码更具可读性。例如:</p>
+
+<pre class="brush: js">// 构造器及其属性定义
+
+function Test(a,b,c,d) {
+ // 属性定义
+};
+
+// 定义第一个方法
+
+Test.prototype.x = function () { ... }
+
+// 定义第二个方法
+
+Test.prototype.y = function () { ... }
+
+// 等等……</pre>
+
+<p>在 Piotr Zalewa 的 <a href="https://github.com/zalun/school-plan-app/blob/master/stage9/js/index.js">school plan app</a> 样例中可以看到这种模式。</p>
+
+<h2 id="总结">总结</h2>
+
+<p>本文介绍了 JavaScript 对象原型,包括原型链如何允许对象之间继承特性、<code>prototype</code> 属性、如何通过它来向构造器添加方法,以及其他有关主题。</p>
+
+<p>下一篇文章中,我们将了解如何在两个自定义的对象间实现功能的继承。</p>
+
+<p>{{PreviousMenuNext("Learn/JavaScript/Objects/Object-oriented_JS", "Learn/JavaScript/Objects/Inheritance", "Learn/JavaScript/Objects")}}</p>
diff --git a/files/zh-cn/learn/javascript/objects/向“弹跳球”演示程序添加新功能/index.html b/files/zh-cn/learn/javascript/objects/向“弹跳球”演示程序添加新功能/index.html
new file mode 100644
index 0000000000..2730489d15
--- /dev/null
+++ b/files/zh-cn/learn/javascript/objects/向“弹跳球”演示程序添加新功能/index.html
@@ -0,0 +1,468 @@
+---
+title: 为“弹球”示例添加新功能
+slug: Learn/JavaScript/Objects/向“弹跳球”演示程序添加新功能
+tags:
+ - JavaScript
+ - 初学者
+ - 对象
+ - 测验
+ - 面向对象
+translation_of: Learn/JavaScript/Objects/Adding_bouncing_balls_features
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/JavaScript/Objects/Object_building_practice", "", "Learn/JavaScript/Objects")}}</div>
+
+<p class="summary">在此次测验中, 你需要将上一节中的“弹球”演示程序作为模板,添加一些新的有趣的功能。</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">预备知识:</th>
+ <td>请确保完整学习本章所有内容后再开始测验。</td>
+ </tr>
+ <tr>
+ <th scope="row">目标:</th>
+ <td>测试你对 JavaScript 对象和面向对象结构的理解。</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="开始">开始</h2>
+
+<p>请先下载 <a class="external external-icon" href="https://github.com/roy-tian/learning-area/blob/master/javascript/oojs/bouncing-balls/index.html">index.html</a>、<a class="external external-icon" href="https://github.com/roy-tian/learning-area/blob/master/javascript/oojs/bouncing-balls/style.css">style.css</a> 和 <a class="external external-icon" href="https://github.com/roy-tian/learning-area/blob/master/javascript/oojs/bouncing-balls/main.js">main.js</a> 三个文件。</p>
+
+<div class="note">
+<p><strong>注:</strong>也可以使用 <a class="external external-icon" href="http://jsbin.com/">JSBin</a> 或 <a class="external external-icon" href="https://thimble.mozilla.org/">Thimble</a> 这样的网站来进行测验。 你可以选择其中一个将HTML,CSS 和JavaScript 粘贴过去。 如果你的版本没有单独的 JavaScript / CSS 板块,可以把它们嵌入 HTML 页面内的 <code>&lt;script&gt;</code>/<code>&lt;style&gt;</code> 元素。</p>
+</div>
+
+<h2 id="项目简介">项目简介</h2>
+
+<p>我们的弹球 demo 很有趣, 但是现在我们想让它更具有互动性,我们为它添加一个由玩家控制的“恶魔圈”,如果恶魔圈抓到弹球会把它会吃掉。我们还想测验你面向对象的水平,首先创建一个通用 <code>Shape()</code> 对象,然后由它派生出弹球和恶魔圈。最后,我们为 demo 添加一个计分器来记录剩下的球数。</p>
+
+<p>程序最终会像这样:</p>
+
+<div class="hidden">
+<h6 id="Evil_circle">Evil circle</h6>
+
+<pre class="brush: html notranslate">&lt;!DOCTYPE html&gt;
+&lt;html lang="zh-CN"&gt;
+ &lt;head&gt;
+ &lt;meta charset="utf-8"&gt;
+ &lt;title&gt;弹球&lt;/title&gt;
+ &lt;style&gt;
+body {
+    margin: 0;
+    overflow: hidden;
+    font-family: 'PingFangSC-Regular', '微软雅黑', sans-serif;
+    height: 100%;
+}
+
+h1 {
+    font-size: 2rem;
+    letter-spacing: -1px;
+    position: absolute;
+    margin: 0;
+    top: -4px;
+    right: 5px;
+
+    color: transparent;
+    text-shadow: 0 0 4px white;
+  }
+
+p {
+    position: absolute;
+    margin: 0;
+    top: 35px;
+    right: 5px;
+    color: #aaa;
+}  &lt;/style&gt;
+ &lt;/head&gt;
+
+ &lt;body&gt;
+ &lt;h1&gt;弹球&lt;/h1&gt;
+ &lt;p&gt;&lt;/p&gt;
+ &lt;canvas&gt;&lt;/canvas&gt;
+
+ &lt;script&gt;
+const BALLS_COUNT = 25;
+const BALL_SIZE_MIN = 10;
+const BALL_SIZE_MAX = 20;
+const BALL_SPEED_MAX = 7;
+
+class Shape {
+    constructor(x, y, velX, velY, exists) {
+        this.x = x;
+        this.y = y;
+        this.velX = velX;
+        this.velY = velY;
+        this.exists = exists;
+    }
+}
+
+class Ball extends Shape {
+    constructor(x, y, velX, velY, color, size, exists) {
+        super(x, y, velX, velY, exists);
+
+        this.color = color;
+        this.size = size;
+    }
+
+    draw() {
+        ctx.beginPath();
+        ctx.fillStyle = this.color;
+        ctx.arc(this.x, this.y, this.size, 0, 2 * Math.PI);
+        ctx.fill();
+    }
+
+    update() {
+        if ((this.x + this.size) &gt;= width) {
+            this.velX = -(this.velX);
+        }
+
+        if ((this.x - this.size) &lt;= 0) {
+            this.velX = -(this.velX);
+        }
+
+        if ((this.y + this.size) &gt;= height) {
+            this.velY = -(this.velY);
+        }
+
+        if ((this.y - this.size) &lt;= 0) {
+            this.velY = -(this.velY);
+        }
+
+        this.x += this.velX;
+        this.y += this.velY;
+    }
+
+    collisionDetect() {
+        for (let j = 0; j &lt; balls.length; j++) {
+            if ( ! (this === balls[j]) ) {
+                const dx = this.x - balls[j].x;
+                const dy = this.y - balls[j].y;
+                const distance = Math.sqrt(dx * dx + dy * dy);
+
+                if (distance &lt; this.size + balls[j].size &amp;&amp; balls[j].exists) {
+                    balls[j].color = this.color = randomColor();
+                }
+            }
+        }
+    }
+}
+
+class EvilCircle extends Shape {
+    constructor(x, y, exists) {
+        super(x, y, exists);
+
+        this.velX = BALL_SPEED_MAX;
+        this.velY = BALL_SPEED_MAX;
+        this.color = "white";
+        this.size = 10;
+        this.setControls();
+    }
+
+    draw() {
+        ctx.beginPath();
+        ctx.strokeStyle = this.color;
+        ctx.lineWidth = 3;
+        ctx.arc(this.x, this.y, this.size, 0, 2 * Math.PI);
+        ctx.stroke();
+    }
+
+    checkBounds() {
+        if ((this.x + this.size) &gt;= width) {
+            this.x -= this.size;
+        }
+
+        if ((this.x - this.size) &lt;= 0) {
+            this.x += this.size;
+        }
+
+        if ((this.y + this.size) &gt;= height) {
+            this.y -= this.size;
+        }
+
+        if ((this.y - this.size) &lt;= 0) {
+            this.y += this.size;
+        }
+    }
+
+    setControls() {
+        window.onkeydown = (e) =&gt; {
+            switch(e.key) {
+                case 'a':
+                case 'A':
+                case 'ArrowLeft':
+                    this.x -= this.velX;
+                    break;
+                case 'd':
+                case 'D':
+                case 'ArrowRight':
+                    this.x += this.velX;
+                    break;
+                case 'w':
+                case 'W':
+                case 'ArrowUp':
+                    this.y -= this.velY;
+                    break;
+                case 's':
+                case 'S':
+                case 'ArrowDown':
+                    this.y += this.velY;
+                    break;
+            }
+        };
+    }
+
+    collisionDetect() {
+        for (let j = 0; j &lt; balls.length; j++) {
+            if (balls[j].exists) {
+                const dx = this.x - balls[j].x;
+                const dy = this.y - balls[j].y;
+                const distance = Math.sqrt(dx * dx + dy * dy);
+
+                if (distance &lt; this.size + balls[j].size) {
+                    balls[j].exists = false;
+                    count--;
+                    para.textContent = '还剩 ' + count + ' 个球';
+                }
+            }
+        }
+    }
+}
+
+const para = document.querySelector('p');
+const canvas = document.querySelector('canvas');
+const ctx = canvas.getContext('2d');
+
+const width = canvas.width = window.innerWidth;
+const height = canvas.height = window.innerHeight;
+
+const balls = [];
+let count = 0;
+
+const evilBall = new EvilCircle(
+    random(0, width),
+    random(0, height),
+    true
+);
+
+loop();
+
+function random(min,max) {
+    return Math.floor(Math.random()*(max-min)) + min;
+}
+
+function randomColor() {
+    return 'rgb(' +
+           random(0, 255) + ', ' +
+           random(0, 255) + ', ' +
+           random(0, 255) + ')';
+}
+
+function loop() {
+    ctx.fillStyle = 'rgba(0, 0, 0, 0.25)';
+    ctx.fillRect(0, 0, width, height);
+
+    while (balls.length &lt; BALLS_COUNT) {
+        const size = random(BALL_SIZE_MIN, BALL_SIZE_MAX);
+        const ball = new Ball(
+            random(0 + size, width - size),
+            random(0 + size, height - size),
+            random(-BALL_SPEED_MAX, BALL_SPEED_MAX),
+            random(-BALL_SPEED_MAX, BALL_SPEED_MAX),
+            randomColor(),
+            size,
+            true
+        );
+        balls.push(ball);
+        count++;
+        para.textContent = '还剩 ' + count + ' 个球';
+    }
+
+    for (let i = 0; i &lt; balls.length; i++) {
+        if (balls[i].exists) {
+            balls[i].draw();
+            balls[i].update();
+            balls[i].collisionDetect();
+        }
+    }
+
+    evilBall.draw();
+    evilBall.checkBounds();
+    evilBall.collisionDetect();
+
+    requestAnimationFrame(loop);
+}
+  &lt;/script&gt;
+ &lt;/body&gt;
+&lt;/html&gt;
+</pre>
+</div>
+
+<ul>
+</ul>
+
+<p>{{ EmbedLiveSample('Evil_circle', '100%', 480, "", "", "hide-codepen-jsfiddle") }}</p>
+
+<p>可以 <a class="external external-icon" href="https://roy-tian.github.io/learning-area/javascript/oojs/assessment/">查看完成版本</a> 来获得更全面的体验。(别偷看源代码哦。)</p>
+
+<h2 id="步骤">步骤</h2>
+
+<p>以下各节介绍你需要完成的步骤。</p>
+
+<h3 id="创建我们的新对象">创建我们的新对象</h3>
+
+<p>首先, 改变你现有的构造器 <code>Ball()</code> 使其成为构造器 <code>Shape()</code> 并添加一个新的构造器 <code>Ball()</code> :</p>
+
+<ol>
+ <li>构造器 <code>Shape()</code> 应该像构造器 <code>Ball()</code> 那样的方式定义 <code>x</code>, <code>y</code>, <code>velX</code>, 和 <code>velY</code> 属性,但不包括 <code>color</code> 和 <code>size</code> 。</li>
+ <li>还应该定义一个叫 <code>exists</code> 的新属性,用来标记球是否存在于程序中 (没有被恶魔圈吃掉)。这应该是一个布尔型((<code>true</code>/<code>false</code>)。</li>
+ <li>构造器 <code>Ball()</code> 应该从构造器 <code>Shape()</code> 继承 <code>x</code>, <code>y</code>, <code>velX</code>, <code>velY</code>,和 <code>exists</code> 属性。</li>
+ <li>构造器 <code>Ball()</code> 还应该像最初的构造器 <code>Ball()</code> 那样定义一个 <code>color</code> 和一个<code>size</code> 属性。</li>
+ <li>请记得给构造器 <code>Ball()</code> 的<code>prototype</code> 和 <code>constructor</code> 属性设置适当的值。</li>
+</ol>
+
+<p><code>draw()</code>, <code>update()</code>, 和<code>collisionDetect()</code> 方法定义应保持不变。</p>
+
+<p>你还需要为 <code>new Ball() { ... }</code> 构造器添加第五个参数—— <code>exists</code>, 且值为 <code>true</code>。</p>
+
+<p>到这里, 尝试重新加载代码(运行程序),程序以及重新设计的对象都应该像之前那样工作。</p>
+
+<h3 id="定义恶魔圈_EvilCircle">定义恶魔圈 EvilCircle()</h3>
+
+<p>现在是时候来看看那个坏蛋了——恶魔圈 <code>EvilCircle()</code>! 我们的游戏中只会有一个恶魔圈,但我们仍然要使用继承自 <code>Shape()</code> 的构造器来定义它,这是为让你得到锻炼。 之后你可能会想再添加一个由另一个玩家控制的恶魔圈到程序中,或者有几个电脑控制的恶魔圈。你可没法通过一个恶魔圈来掌管程序中的这个世界,但这个评估中就先只这么做吧。</p>
+
+<p><code>EvilCircle()</code> 构造器应该从<code>Shape()</code> 继承 <code>x</code>, <code>y</code>, 和 <code>exists</code> ,<code>velX</code> 和 <code>velY</code> 要恒为 20。</p>
+
+<p>可以这样做:<code>Shape.call(this, x, y, 20, 20, exists);</code></p>
+
+<p>它还应该定义自己的一些属性,如:</p>
+
+<ul>
+ <li><code>color</code> —— <code>'white'</code></li>
+ <li><code>size</code> —— <code>10</code></li>
+</ul>
+
+<p>再次记得给你的 <code>EvilCircle()</code> 构造器的传递的参数中定义你继承的属性,并且给<code>prototype</code> 和 <code>constructor</code> 属性设置适当的值。</p>
+
+<h3 id="定义_EvilCircle_的方法">定义 EvilCircle() 的方法</h3>
+
+<p><code>EvilCircle()</code> 应该有以下四个方法:</p>
+
+<h4 id="draw"><code>draw()</code></h4>
+
+<p>这个方法和 <code>Ball()</code>'s <code>draw()</code> 方法有着相同的目的:它们把都是对象的实例画在画布上(canvas) 。它们实现的方式差不多,所以你可以先复制 <code>Ball.prototype.draw</code> 的定义。然后你需要做下面的修改:</p>
+
+<ul>
+ <li>我们不想让恶魔圈是实心的,而是一个圈或者说是环。你可以通过将 <code><a href="/en-US/docs/Web/API/CanvasRenderingContext2D/fillStyle">fillStyle</a></code> 和 <code><a href="/en-US/docs/Web/API/CanvasRenderingContext2D/fill">fill()</a></code> 修改为 <code><a href="/en-US/docs/Web/API/CanvasRenderingContext2D/strokeStyle">strokeStyle</a></code> 和 <code><a href="/en-US/docs/Web/API/CanvasRenderingContext2D/stroke">stroke()</a></code>而实现这个效果。</li>
+ <li>我们还想让这个圈更厚一点, 从而使你能更好地辨认它。 可以在调用 <code><a href="/en-US/docs/Web/API/CanvasRenderingContext2D/beginPath">beginPath()</a></code> 的后面给 <code><a href="/en-US/docs/Web/API/CanvasRenderingContext2D/lineWidth">lineWidth</a></code> 赋值实现这个效果。(赋值为 3 就可以了)</li>
+</ul>
+
+<h4 id="checkBounds"><code>checkBounds()</code></h4>
+
+<p>这个方法和 <code>Ball()</code> 的 <code>update()</code> 函数做相同的事情—— 查看恶魔圈是否将要超出屏幕的边界, 并且禁止它超出。 同样,你可以直接复制 <code>Ball.prototype.update</code> 的定义, 但是你需要做一些修改:</p>
+
+<ul>
+ <li>删除最后两行 — 我们不想要在每一帧中自动的更新恶魔圈的位置,因为我们会以下面所述的方式移动它。</li>
+ <li>在 <code>if()</code> 语句中,如果检测为真(即小恶魔圈超出边界),我们不需要更新 <code>velX</code>/<code>velY</code>;取而代之的是,我们想要修改 <code>x</code>/<code>y</code> 的值,使恶魔圈稍微地弹回屏幕。增加或减去 (根据实际判断)恶魔圈 <code>size</code> 的值即可实现。</li>
+</ul>
+
+<h4 id="setControls"><code>setControls()</code></h4>
+
+<p>这个方法将会一个 <code>onkeydown</code> 的事件监听器给 <code>window</code> 对象,这样当特定的键盘按键按下的时候,我们就可以移动恶魔圈。下面的代码块应该放在方法的定义里:</p>
+
+<pre class="brush: js notranslate">window.onkeydown = e =&gt; {
+  switch(e.key) {
+    case 'a':
+      this.x -= this.velX;
+      break;
+    case 'd':
+      this.x += this.velX;
+      break;
+    case 'w':
+      this.y -= this.velY;
+      break;
+    case 's':
+      this.y += this.velY;
+      break;
+ }
+};</pre>
+
+<p>所以当一个按键按下时, 事件对象的 <a href="/zh-CN/docs/Web/API/KeyboardEvent/key">key</a> 属性 就可以请求到按下的按键值。如果是代码中那四个指定的键值之一, 那么恶魔圈将会左右上下的移动。</p>
+
+<div class="blockIndicator warning">
+<p><strong>译注:</strong>英文页面中使用了事件对象的 <a href="/zh-CN/docs/Web/API/KeyboardEvent/keyCode">keyCode</a> 属性,不推荐在新代码中使用该属性,应使用标准 <a href="/zh-CN/docs/Web/API/KeyboardEvent/key">key</a> 属性代替。(详见介绍页面)</p>
+</div>
+
+<div class="blockIndicator note"><strong>译注:</strong>这里的 <code>window.onkeydown</code> 用一个 <a href="/zh-CN/docs/Web/JavaScript/Reference/Functions/Arrow_functions">箭头函数</a> 代替了英文页面中的匿名函数,从而无需 <code>var _this = this</code>。</div>
+
+<h4 id="collisionDetect"><code>collisionDetect()</code></h4>
+
+<p>这个方法和 <code>Ball()</code>'s <code>collisionDetect()</code> 方法很相似,所以你可以从它那里复制过来作为新方法的基础。但有一些不同之处:</p>
+
+<ul>
+ <li>在外层的 <code>if</code> 语句中,你不需要再检验循环到的小球是否是当前 <code>collisionDetect()</code> 所在的对象 — 因为它不再是一个小球了,它是恶魔圈! 而是检查小球是否存在 (你可以通过哪个属性实现这个呢?)。如果小球不存在,说明它已经被恶魔圈吃掉了,那么就不需要再检测它是否与恶魔圈碰撞了。</li>
+ <li>在里层的 <code>if</code> 语句中,你不再需要在碰撞被检测到时去改变对象的颜色 — 而是需要将与恶魔圈发生碰撞的小球设置为不存在(再次提问,你觉得你该怎么实现呢?)。</li>
+</ul>
+
+<h3 id="把恶魔圈带到程序中">把恶魔圈带到程序中</h3>
+
+<p>现在我们已经定义了恶魔圈,我们需要让它显示到我们的屏幕中。为了做这件事,你需要修改一下 <code>loop()</code> 函数:</p>
+
+<ul>
+ <li>首先,创建一个新的恶魔圈的对象实例 (指定必需的参数),然后调用它的 <code>setControls()</code> 方法。 这两件事你只需要做一次,不需要放在loop的循环中。</li>
+ <li>在你每一次遍历小球并调用 <code>draw()</code>, <code>update()</code>, 和 <code>collisionDetect()</code> 函数的地方进行修改, 使这些函数只会在小球存在时被调用。</li>
+ <li>在每个loop的循环中调用恶魔圈实例的方法 <code>draw()</code>, <code>checkBounds()</code>, 和<code>collisionDetect()</code> 。</li>
+</ul>
+
+<h3 id="计算得分">计算得分</h3>
+
+<p>为了计算得分,需按照以下步骤:</p>
+
+<ol>
+ <li>在你的HTML文件中添加一个{{HTMLElement("p")}} 元素到 {{HTMLElement("h1")}} 元素的下面,其中包含文本 "还剩多少个球"。</li>
+ <li>在你的CSS文件中,添加下面的代码到底部:
+ <pre class="brush: css notranslate">p {
+ position: absolute;
+ margin: 0;
+ top: 35px;
+ right: 5px;
+ color: #aaa;
+}</pre>
+ </li>
+ <li>在你的 JavaScript 文件中,做下列的修改:
+ <ul>
+ <li>创建一个变量存储段落的引用。</li>
+ <li>以同样的方式在屏幕上显示小球的数量。</li>
+ <li>增加球数并在每次将球添加到屏幕里时显示更新的球数量。</li>
+ <li>减少球数并在每次恶魔吃球时显示更新的球数(因为被吃掉的球不存在了)</li>
+ </ul>
+ </li>
+</ol>
+
+<h2 id="提示">提示</h2>
+
+<ul>
+ <li>这个评估非常具有挑战性。请仔细按照步骤慢慢来。</li>
+ <li>每完成一个阶段时,你可以保留程序的副本,这是一种有用的方式。这样当你发现你程序出了问题,你可以参考之前的代码。</li>
+</ul>
+
+<h2 id="评定">评定</h2>
+
+<p>如果你将此评估作为有组织的课程的一部分,你可以将你的成果交给您的老师/导师进行评分。 如果你是自学的,通过在 <a href="https://discourse.mozilla-community.org/t/learning-web-development-marking-guides-and-questions/16294">Learning Area Discourse thread</a>, 或者在 <a href="https://wiki.mozilla.org/IRC">Mozilla IRC</a> 的 <a href="irc://irc.mozilla.org/mdn">#mdn</a> IRC 频道上申请,你可以十分容易地得到评分指南。首先先尝试这个练习,作弊不会有任何收获。</p>
+
+<p>{{PreviousMenuNext("Learn/JavaScript/Objects/Object_building_practice", "", "Learn/JavaScript/Objects")}}</p>
+
+<h2 id="本章目录">本章目录</h2>
+
+<ul>
+ <li><a href="/zh-CN/docs/Learn/JavaScript/Objects/Basics">对象基础</a></li>
+ <li><a href="/zh-CN/docs/Learn/JavaScript/Objects/Object-oriented_JS">适合初学者的 JavaScript 面向对象</a></li>
+ <li><a href="/zh-CN/docs/Learn/JavaScript/Objects/Object_prototypes">对象原型</a></li>
+ <li><a href="/zh-CN/docs/Learn/JavaScript/Objects/Inheritance">JavaScript 中的继承</a></li>
+ <li><a href="/zh-CN/docs/Learn/JavaScript/Objects/JSON">使用 JSON 数据</a></li>
+ <li><a href="/zh-CN/docs/Learn/JavaScript/Objects/Object_building_practice">构建对象实战</a></li>
+ <li><a href="/zh-CN/docs/Learn/JavaScript/Objects/Adding_bouncing_balls_features">向“弹跳球”演示程序添加新功能</a></li>
+</ul>
diff --git a/files/zh-cn/learn/javascript/objects/测试你的技能_colon_面向对象的javascript/index.html b/files/zh-cn/learn/javascript/objects/测试你的技能_colon_面向对象的javascript/index.html
new file mode 100644
index 0000000000..8fd0cc3256
--- /dev/null
+++ b/files/zh-cn/learn/javascript/objects/测试你的技能_colon_面向对象的javascript/index.html
@@ -0,0 +1,95 @@
+---
+title: 测试你的技能:面向对象的Javascript
+slug: 'Learn/JavaScript/Objects/测试你的技能:面向对象的Javascript'
+tags:
+ - JavaScript
+ - OOJS
+ - 初学者
+ - 学习
+ - 对象
+ - 测试你的技能
+translation_of: 'Learn/JavaScript/Objects/Test_your_skills:_Object-oriented_JavaScript'
+---
+<div>{{learnsidebar}}</div>
+
+<div>这个测试的目的是为了评估你是否已经理解了我们的<a href="/zh-CN/docs/Learn/JavaScript/Objects/Object-oriented_JS">适合初学者的JavaScript面向对象</a>,<a href="/zh-CN/docs/Learn/JavaScript/Objects/Object_prototypes">对象原型</a>,和 <a href="/zh-CN/docs/Learn/JavaScript/Objects/Inheritance">JavaScript 中的继承</a>文章。</div>
+
+<div></div>
+
+<div class="blockIndicator note">
+<p><strong>注意</strong>: 你可以尝试在下方的交互编辑器,但是若你下载源码或是使用在线工具例如 <a href="https://codepen.io/">CodePen</a>, <a href="https://jsfiddle.net/">jsFiddle</a>, 或 <a href="https://glitch.com/">Glitch</a> 来进行这些项目的话,会更有帮助。</p>
+
+<p>如果你在过程中想不出解决方案,你可以向我们寻求帮助——查看在本页的底部章节 {{anch("Assessment or further help")}}。</p>
+</div>
+
+<div class="blockIndicator note">
+<p><strong>注意</strong>: 在下方的例子中,如果在你的代码中有错误内容的话,错误内容将在页面的结果面板进行显示,以此来帮助你想出解决方案(若是下载的版本,请进入浏览器的 JavaScript 控制台)。</p>
+</div>
+
+<h2 id="OOJS_1">OOJS 1</h2>
+
+<p>In this task we provide you with a constructor. We want you to:</p>
+
+<ul>
+ <li>Add a new method to the <code>Shape</code> class's prototype, <code>calcPerimeter()</code>, which calculates its perimeter (the length of the shape's outer edge) and logs the result to the console.</li>
+ <li>Create a new instance of the <code>Shape</code> class called <code>square</code>. Give it a <code>name</code> of <code>square</code> and a <code>sideLength</code> of <code>5</code>.</li>
+ <li>Call your <code>calcPerimeter()</code> method on the instance, to see whether it logs the calculation result to the browser DevTools' console as expected.</li>
+ <li>Create a new instance of <code>Shape</code> called <code>triangle</code>, with a <code>name</code> of <code>triangle</code> and a <code>sideLength</code> of <code>3</code>.</li>
+ <li>Call <code>triangle.calcPerimeter()</code> to check that it works OK.</li>
+</ul>
+
+<p>Try updating the live code below to recreate the finished example:</p>
+
+<p>{{EmbedGHLiveSample("learning-area/javascript/oojs/tasks/oojs/oojs1.html", '100%', 400)}}</p>
+
+<div class="blockIndicator note">
+<p><a href="https://github.com/mdn/learning-area/tree/master/javascript/oojs/tasks/oojs/oojs1-download.html">Download the starting point for this task</a> to work in your own editor or in an online editor.</p>
+</div>
+
+<h2 id="OOJS_2">OOJS 2</h2>
+
+<p>Next up we want you to take the <code>Shape</code> class you saw in Task #1 (including the <code>calcPerimeter()</code> method) and recreate it using ES class syntax instead.</p>
+
+<p>Test that it works by creating the <code>square</code> and <code>triangle</code> object instances as before (using <code>new Shape()</code> for both), and then calling their <code>calcPerimeter()</code> methods.</p>
+
+<p>Try updating the live code below to recreate the finished example:</p>
+
+<p>{{EmbedGHLiveSample("learning-area/javascript/oojs/tasks/oojs/oojs2.html", '100%', 400)}}</p>
+
+<div class="blockIndicator note">
+<p><a href="https://github.com/mdn/learning-area/tree/master/javascript/oojs/tasks/oojs/oojs2-download.html">Download the starting point for this task</a> to work in your own editor or in an online editor.</p>
+</div>
+
+<h2 id="OOJS_3">OOJS 3</h2>
+
+<p>Finally, we'd like you to start with the ES <code>Shape</code> class you created in the last task.</p>
+
+<p>We'd like you to create a <code>Square</code> class that inherits from <code>Shape</code>, and adds a <code>calcArea()</code> method that calculates the square's area. Also set up the constructor so that the <code>name</code> property of <code>Square</code> object instances is automatically set to <code>square</code>, and the <code>sides</code> property is automatically set to <code>4</code>. When invoking the constructor, you should therefore just need to provide the <code>sideLength</code> property.</p>
+
+<p>Create an instance of the <code>Square</code> class called <code>square</code> with appropriate property values, and call its <code>calcPerimeter()</code> and <code>calcArea()</code> methods to show that it works ok.</p>
+
+<p>Try updating the live code below to recreate the finished example:</p>
+
+<p>{{EmbedGHLiveSample("learning-area/javascript/oojs/tasks/oojs/oojs3.html", '100%', 400)}}</p>
+
+<div class="blockIndicator note">
+<p><a href="https://github.com/mdn/learning-area/tree/master/javascript/oojs/tasks/oojs/oojs3-download.html">Download the starting point for this task</a> to work in your own editor or in an online editor.</p>
+</div>
+
+<h2 id="Assessment_or_further_help">Assessment or further help</h2>
+
+<p>You can practice these examples in the Interactive Editors above.</p>
+
+<p>If you would like your work assessed, or are stuck and want to ask for help:</p>
+
+<ol>
+ <li>Put your work into an online shareable editor such as <a href="https://codepen.io/">CodePen</a>, <a href="https://jsfiddle.net/">jsFiddle</a>, or <a href="https://glitch.com/">Glitch</a>. You can write the code yourself, or use the starting point files linked to in the above sections.</li>
+ <li>Write a post asking for assessment and/or help at the <a href="https://discourse.mozilla.org/c/mdn/learn">MDN Discourse forum Learning category</a>. Your post should include:
+ <ul>
+ <li>A descriptive title such as "Assessment wanted for OOJS 1 skill test".</li>
+ <li>Details of what you have already tried, and what you would like us to do, e.g. if you are stuck and need help, or want an assessment.</li>
+ <li>A link to the example you want assessed or need help with, in an online shareable editor (as mentioned in step 1 above). This is a good practice to get into — it's very hard to help someone with a coding problem if you can't see their code.</li>
+ <li>A link to the actual task or assessment page, so we can find the question you want help with.</li>
+ </ul>
+ </li>
+</ol>
diff --git a/files/zh-cn/learn/javascript/异步/async_await/index.html b/files/zh-cn/learn/javascript/异步/async_await/index.html
new file mode 100644
index 0000000000..ae79af9899
--- /dev/null
+++ b/files/zh-cn/learn/javascript/异步/async_await/index.html
@@ -0,0 +1,379 @@
+---
+title: 'async和await:让异步编程更简单'
+slug: learn/JavaScript/异步/Async_await
+translation_of: Learn/JavaScript/Asynchronous/Async_await
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/JavaScript/Asynchronous/Promises", "Learn/JavaScript/Asynchronous/Choosing_the_right_approach", "Learn/JavaScript/Asynchronous")}}</div>
+
+<p class="summary"><a href="/en-US/docs/Web/JavaScript/Reference/Statements/async_function">async functions</a> 和 <code><a href="/en-US/docs/Web/JavaScript/Reference/Operators/await">await</a></code> 关键字是最近添加到JavaScript语言里面的。它们是ECMAScript 2017 JavaScript版的一部分(参见<a href="/en-US/docs/Web/JavaScript/New_in_JavaScript/ECMAScript_Next_support_in_Mozilla">ECMAScript Next support in Mozilla</a>)。简单来说,它们是基基于promises的语法糖,使异步代码更易于编写和阅读。通过使用它们,异步代码看起来更像是老式同步代码,因此它们非常值得学习。本文为您提供了您需要了解的内容。</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">先决条件:</th>
+ <td>基本的计算机知识,较好理解 JavaScript 基础,以及理解一般异步代码和 promises 。</td>
+ </tr>
+ <tr>
+ <th scope="row">目标:</th>
+ <td>理解并使用 promise</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="asyncawait_基础">async/await 基础</h2>
+
+<p>在代码中使用 async / await 有两个部分。</p>
+
+<h3 id="async_关键字">async 关键字</h3>
+
+<p>首先,我们使用 <code>async</code> 关键字,把它放在函数声明之前,使其成为 <a href="/en-US/docs/Web/JavaScript/Reference/Statements/async_function">async function</a>。异步函数是一个知道怎样使用 <code>await</code> 关键字调用异步代码的函数。</p>
+
+<p>尝试在浏览器的JS控制台中键入以下行:</p>
+
+<pre class="brush: js notranslate">function hello() { return "Hello" };
+hello();</pre>
+
+<p>该函数返回“Hello” —— 没什么特别的,对吧?</p>
+
+<p>如果我们将其变成异步函数呢?请尝试以下方法:</p>
+
+<pre class="brush: js notranslate">async function hello() { return "Hello" };
+hello();</pre>
+
+<p>哈。现在调用该函数会返回一个 promise。这是异步函数的特征之一 —— 它保证函数的返回值为 promise。</p>
+
+<p>你也可以创建一个异步函数表达式(参见 <a href="/en-US/docs/Web/JavaScript/Reference/Operators/async_function">async function expression</a> ),如下所示:</p>
+
+<pre class="brush: js notranslate">let hello = async function() { return "Hello" };
+hello();</pre>
+
+<p>你可以使用箭头函数:</p>
+
+<pre class="brush: js notranslate">let hello = async () =&gt; { return "Hello" };</pre>
+
+<p>这些都基本上是一样的。</p>
+
+<p>要实际使用promise完成时返回的值,我们可以使用<code>.then()</code>块,因为它返回的是 promise:</p>
+
+<pre class="brush: js notranslate">hello().then((value) =&gt; console.log(value))</pre>
+
+<p>甚至只是简写如</p>
+
+<pre class="brush: js notranslate">hello().then(console.log)</pre>
+
+<p>这就像我们在上一篇文章中看到的那样。</p>
+
+<p>将 <code>async</code> 关键字加到函数申明中,可以告诉它们返回的是 promise,而不是直接返回值。此外,它避免了同步函数为支持使用 <font face="consolas, Liberation Mono, courier, monospace"><span style="background-color: rgba(220, 220, 220, 0.5);">await</span></font> 带来的任何潜在开销。在函数声明为 <code>async</code> 时,JavaScript引擎会添加必要的处理,以优化你的程序。爽!</p>
+
+<h3 id="await关键字">await关键字</h3>
+
+<p>当 <a href="/en-US/docs/Web/JavaScript/Reference/Operators/await">await</a> 关键字与异步函数一起使用时,它的真正优势就变得明显了 —— 事实上, <strong><font face="consolas, Liberation Mono, courier, monospace"><span style="background-color: rgba(220, 220, 220, 0.5);">await</span></font> 只在异步函数里面才起作用</strong>。它可以放在任何异步的,基于 promise 的函数之前。它会暂停代码在该行上,直到 promise 完成,然后返回结果值。在暂停的同时,其他正在等待执行的代码就有机会执行了。</p>
+
+<p>您可以在调用任何返回Promise的函数时使用 <strong><font face="consolas, Liberation Mono, courier, monospace"><span style="background-color: rgba(220, 220, 220, 0.5);">await</span></font></strong>,包括Web API函数。</p>
+
+<p>这是一个简单的示例:</p>
+
+<pre class="brush: js notranslate">async function hello() {
+ return greeting = await Promise.resolve("Hello");
+};
+
+hello().then(alert);</pre>
+
+<p>当然,上面的示例不是很有用,但它确实展示了语法。让我们继续,看一个真实示例。</p>
+
+<h2 id="使用_asyncawait_重写_promise_代码"> 使用 async/await 重写 promise 代码</h2>
+
+<p>让我们回顾一下我们在上一篇文章中简单的 fetch 示例:</p>
+
+<pre class="brush: js notranslate">fetch('coffee.jpg')
+.then(response =&gt; response.blob())
+.then(myBlob =&gt; {
+ let objectURL = URL.createObjectURL(myBlob);
+ let image = document.createElement('img');
+ image.src = objectURL;
+ document.body.appendChild(image);
+})
+.catch(e =&gt; {
+ console.log('There has been a problem with your fetch operation: ' + e.message);
+});</pre>
+
+<p>到现在为止,你应该对 promises 及其工作方式有一个较好的理解。让我们将其转换为使用async / await看看它使事情变得简单了多少:</p>
+
+<pre class="brush: js notranslate">async function myFetch() {
+ let response = await fetch('coffee.jpg');
+ let myBlob = await response.blob();
+
+ let objectURL = URL.createObjectURL(myBlob);
+ let image = document.createElement('img');
+ image.src = objectURL;
+ document.body.appendChild(image);
+}
+
+myFetch()
+.catch(e =&gt; {
+ console.log('There has been a problem with your fetch operation: ' + e.message);
+});</pre>
+
+<p>它使代码简单多了,更容易理解 —— 去除了到处都是 <code>.then()</code> 代码块!</p>
+
+<p>由于 <code>async</code> 关键字将函数转换为 promise,您可以重构以上代码 —— 使用 promise 和 await 的混合方式,将函数的后半部分抽取到新代码块中。这样做可以更灵活:</p>
+
+<pre class="brush: js notranslate">async function myFetch() {
+ let response = await fetch('coffee.jpg');
+ return await response.blob();
+}
+
+myFetch().then((blob) =&gt; {
+ let objectURL = URL.createObjectURL(blob);
+ let image = document.createElement('img');
+ image.src = objectURL;
+ document.body.appendChild(image);
+});</pre>
+
+<p>您可以尝试自己输入示例,或运行我们的 <a href="https://mdn.github.io/learning-area/javascript/asynchronous/async-await/simple-fetch-async-await.html">live example</a> (另请参阅<a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/async-await/simple-fetch-async-await.html">source code</a>)。</p>
+
+<h3 id="它到底是如何工作的?">它到底是如何工作的?</h3>
+
+<p>您会注意到我们已经将代码封装在函数中,并且我们在 <code>function</code> 关键字之前包含了 <code>async</code> 关键字。这是必要的 –– 您必须创建一个异步函数来定义一个代码块,在其中运行异步代码; <font face="consolas, Liberation Mono, courier, monospace"><span style="background-color: rgba(220, 220, 220, 0.5);">await</span></font> 只能在异步函数内部工作。</p>
+
+<p>在<code>myFetch()</code>函数定义中,您可以看到代码与先前的 promise 版本非常相似,但存在一些差异。不需要附加 <code>.then()</code> 代码块到每个promise-based方法的结尾,你只需要在方法调用前添加 <font face="consolas, Liberation Mono, courier, monospace"><span style="background-color: rgba(220, 220, 220, 0.5);">await</span></font> 关键字,然后把结果赋给变量。<font face="consolas, Liberation Mono, courier, monospace"><span style="background-color: rgba(220, 220, 220, 0.5);">await</span></font> 关键字使JavaScript运行时暂停于此行,允许其他代码在此期间执行,直到异步函数调用返回其结果。一旦完成,您的代码将继续从下一行开始执行。例如:</p>
+
+<pre class="brush: js notranslate">let response = await fetch('coffee.jpg');</pre>
+
+<p>解析器会在此行上暂停,直到当服务器返回的响应变得可用时。此时 <code>fetch()</code> 返回的 promise 将会完成(fullfilled),返回的 response 会被赋值给 <code>response</code> 变量。一旦服务器返回的响应可用,解析器就会移动到下一行,从而创建一个<code><a href="/en-US/docs/Web/API/Blob">Blob</a></code>。Blob这行也调用基于异步promise的方法,因此我们也在此处使用<code>await</code>。当操作结果返回时,我们将它从<code>myFetch()</code>函数中返回。</p>
+
+<p>这意味着当我们调用<code>myFetch()</code>函数时,它会返回一个promise,因此我们可以将<code>.then()</code>链接到它的末尾,在其中我们处理显示在屏幕上的<code>blob</code>。</p>
+
+<p>你可能已经觉得“这真的很酷!”,你是对的 —— 用更少的.<code>then()</code>块来封装代码,同时它看起来很像同步代码,所以它非常直观。</p>
+
+<h3 id="添加错误处理">添加错误处理</h3>
+
+<p>如果你想添加错误处理,你有几个选择。</p>
+
+<p>您可以将同步的 <code><a href="/en-US/docs/Web/JavaScript/Reference/Statements/try...catch">try...catch</a></code> 结构和 <code>async/await</code> 一起使用 。此示例扩展了我们上面展示的第一个版本代码:</p>
+
+<pre class="brush: js notranslate">async function myFetch() {
+ try {
+ let response = await fetch('coffee.jpg');
+ let myBlob = await response.blob();
+
+ let objectURL = URL.createObjectURL(myBlob);
+ let image = document.createElement('img');
+ image.src = objectURL;
+ document.body.appendChild(image);
+ } catch(e) {
+ console.log(e);
+ }
+}
+
+myFetch();</pre>
+
+<p><code>catch() {}</code> 代码块会接收一个错误对象 <code>e</code> ; 我们现在可以将其记录到控制台,它将向我们提供详细的错误消息,显示错误被抛出的代码中的位置。</p>
+
+<p>如果你想使用我们上面展示的第二个(重构)代码版本,你最好继续混合方式并将 <code>.catch()</code> 块链接到 <code>.then()</code> 调用的末尾,就像这样:</p>
+
+<pre class="brush: js notranslate">async function myFetch() {
+ let response = await fetch('coffee.jpg');
+ return await response.blob();
+}
+
+myFetch().then((blob) =&gt; {
+ let objectURL = URL.createObjectURL(blob);
+ let image = document.createElement('img');
+ image.src = objectURL;
+ document.body.appendChild(image);
+})
+.catch((e) =&gt;
+ console.log(e)
+);</pre>
+
+<p>这是因为 <code>.catch()</code> 块将捕获来自异步函数调用和promise链中的错误。如果您在此处使用了<code>try/catch</code> 代码块,则在调用 <code>myFetch()</code> 函数时,您仍可能会收到未处理的错误。</p>
+
+<p>您可以在GitHub上找到这两个示例:</p>
+
+<ul>
+ <li><a href="https://mdn.github.io/learning-area/javascript/asynchronous/async-await/simple-fetch-async-await-try-catch.html">simple-fetch-async-await-try-catch.html</a> (参见 <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/async-await/simple-fetch-async-await-try-catch.html">源码</a>)</li>
+ <li><a href="https://mdn.github.io/learning-area/javascript/asynchronous/async-await/simple-fetch-async-await-promise-catch.html">simple-fetch-async-await-promise-catch.html</a> (参见 <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/async-await/simple-fetch-async-await-promise-catch.html">源码</a>)</li>
+</ul>
+
+<h2 id="等待Promise.all">等待Promise.all()</h2>
+
+<p><code>async / await</code> 建立在 <a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise">promises</a> 之上,因此它与promises提供的所有功能兼容。这包括<code><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all">Promise.all()</a></code> –– 你完全可以通过调用 <code>await</code> <code>Promise.all()</code> 将所有结果返回到变量中,就像同步代码一样。让我们再次回到<a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/promises/promise-all.html">上一篇中看到的例子</a>。在单独的选项卡中打开它,以便您可以与下面显示的新版本进行比较和对比。</p>
+
+<p>将其转换为 async / await(请参阅 <a href="https://mdn.github.io/learning-area/javascript/asynchronous/async-await/promise-all-async-await.html">样例</a> 和 <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/async-await/promise-all-async-await.html">源码</a>),现在看起来像这样:</p>
+
+<pre class="brush: js notranslate">async function fetchAndDecode(url, type) {
+ let response = await fetch(url);
+
+ let content;
+
+ if(type === 'blob') {
+ content = await response.blob();
+ } else if(type === 'text') {
+ content = await response.text();
+ }
+
+ return content;
+}
+
+async function displayContent() {
+ let coffee = fetchAndDecode('coffee.jpg', 'blob');
+ let tea = fetchAndDecode('tea.jpg', 'blob');
+ let description = fetchAndDecode('description.txt', 'text');
+
+ let values = await Promise.all([coffee, tea, description]);
+
+ let objectURL1 = URL.createObjectURL(values[0]);
+ let objectURL2 = URL.createObjectURL(values[1]);
+ let descText = values[2];
+
+ let image1 = document.createElement('img');
+ let image2 = document.createElement('img');
+ image1.src = objectURL1;
+ image2.src = objectURL2;
+ document.body.appendChild(image1);
+ document.body.appendChild(image2);
+
+ let para = document.createElement('p');
+ para.textContent = descText;
+ document.body.appendChild(para);
+}
+
+displayContent()
+.catch((e) =&gt;
+  console.log(e)
+);</pre>
+
+<p>可以看到 <code>fetchAndDecode()</code> 函数只进行了一丁点的修改就转换成了异步函数。请看<code>Promise.all()</code> 行:</p>
+
+<pre class="brush: js notranslate">let values = await Promise.all([coffee, tea, description]);</pre>
+
+<p>在这里,通过使用<code>await</code>,我们能够在三个promise的结果都可用的时候,放入<code>values</code>数组中。这看起来非常像同步代码。我们需要将所有代码封装在一个新的异步函数<code>displayContent()</code> 中,尽管没有减少很多代码,但能够将大部分代码从 <code>.then()</code> 代码块移出,使代码得到了简化,更易读。</p>
+
+<p>为了错误处理,我们在 <code>displayContent()</code> 调用中包含了一个 <code>.catch()</code> 代码块;这将处理两个函数中出现的错误。</p>
+
+<div class="blockIndicator note">
+<p><strong>注意</strong>: 也可以在异步函数中使用同步 <code><a href="/en-US/docs/Web/JavaScript/Reference/Statements/try...catch#The_finally_clause">finally</a></code> 代码块代替 <code><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/finally">.finally()</a></code> 异步代码块,以显示操作如何进行的最终报告——您可以在我们的 <a href="https://mdn.github.io/learning-area/javascript/asynchronous/async-await/promise-finally-async-await.html">live example</a> (查看<a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/async-await/promise-finally-async-await.html">源代码</a>)中看到这一点。</p>
+</div>
+
+<h2 id="asyncawait的缺陷">async/await的缺陷</h2>
+
+<p>了解<code>Async/await</code>是非常有用的,但还有一些缺点需要考虑。</p>
+
+<p><code>Async/await</code> 让你的代码看起来是同步的,在某种程度上,也使得它的行为更加地同步。 <code>await</code> 关键字会阻塞其后的代码,直到promise完成,就像执行同步操作一样。它确实可以允许其他任务在此期间继续运行,但您自己的代码被阻塞。</p>
+
+<p>这意味着您的代码可能会因为大量<code>await</code>的promises相继发生而变慢。每个<code>await</code>都会等待前一个完成,而你实际想要的是所有的这些promises同时开始处理(就像我们没有使用<code>async/await</code>时那样)。</p>
+
+<p>有一种模式可以缓解这个问题——通过将 <code>Promise</code> 对象存储在变量中来同时开始它们,然后等待它们全部执行完毕。让我们看一些证明这个概念的例子。</p>
+
+<p>我们有两个可用的例子 —— <a href="https://mdn.github.io/learning-area/javascript/asynchronous/async-await/slow-async-await.html">slow-async-await.html</a>(参见<a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/async-await/slow-async-await.html">source code</a>)和<a href="https://mdn.github.io/learning-area/javascript/asynchronous/async-await/fast-async-await.html">fast-async-await.html</a>(参见<a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/async-await/fast-async-await.html">source code</a>)。它们都以自定义promise函数开始,该函数使用<code>setTimeout()</code> 调用伪造异步进程:</p>
+
+<pre class="brush: js notranslate">function timeoutPromise(interval) {
+ return new Promise((resolve, reject) =&gt; {
+ setTimeout(function(){
+ resolve("done");
+ }, interval);
+ });
+};</pre>
+
+<p>然后每个包含一个 <code>timeTest()</code> 异步函数,等待三个 <code>timeoutPromise()</code> 调用:</p>
+
+<pre class="brush: js notranslate">async function timeTest() {
+ ...
+}</pre>
+
+<p>每一个都以记录开始时间结束,查看 <code>timeTest()</code> promise 需要多长时间才能完成,然后记录结束时间并报告操作总共需要多长时间:</p>
+
+<pre class="brush: js notranslate">let startTime = Date.now();
+timeTest().then(() =&gt; {
+ let finishTime = Date.now();
+ let timeTaken = finishTime - startTime;
+ alert("Time taken in milliseconds: " + timeTaken);
+})</pre>
+
+<p><code>timeTest()</code> 函数在每种情况下都不同。</p>
+
+<p>在<a href="https://mdn.github.io/learning-area/javascript/asynchronous/async-await/slow-async-await.html">slow-async-await.html</a>示例中,<code>timeTest()</code> 如下所示:</p>
+
+<pre class="brush: js notranslate">async function timeTest() {
+ await timeoutPromise(3000);
+ await timeoutPromise(3000);
+ await timeoutPromise(3000);
+}</pre>
+
+<p>在这里,我们直接等待所有三个timeoutPromise()调用,使每个调用3秒钟。后续的每一个都被迫等到最后一个完成 - 如果你运行第一个例子,你会看到弹出框报告的总运行时间大约为9秒。</p>
+
+<p>在<a href="https://mdn.github.io/learning-area/javascript/asynchronous/async-await/fast-async-await.html">fast-async-await.html</a>示例中,<code>timeTest()</code> 如下所示:</p>
+
+<pre class="brush: js notranslate">async function timeTest() {
+ const timeoutPromise1 = timeoutPromise(3000);
+ const timeoutPromise2 = timeoutPromise(3000);
+ const timeoutPromise3 = timeoutPromise(3000);
+
+ await timeoutPromise1;
+ await timeoutPromise2;
+ await timeoutPromise3;
+}</pre>
+
+<p>在这里,我们将三个Promise对象存储在变量中,这样可以同时启动它们关联的进程。</p>
+
+<p>接下来,我们等待他们的结果 - 因为promise都在基本上同时开始处理,promise将同时完成;当您运行第二个示例时,您将看到弹出框报告总运行时间仅超过3秒!</p>
+
+<p>您必须仔细测试您的代码,并在性能开始受损时牢记这一点。</p>
+
+<p>另一个小小的不便是你必须将期待已久的promise封装在异步函数中。</p>
+
+<h2 id="Asyncawait_的类方法">Async/await 的类方法</h2>
+
+<p>最后值得一提的是,我们可以在类/对象方法前面添加<code>async</code>,以使它们返回promises,并<code>await</code>它们内部的promises。查看 <a href="/en-US/docs/Learn/JavaScript/Objects/Inheritance#ECMAScript_2015_Classes">ES class code we saw in our object-oriented JavaScript article</a>,然后使用异步方法查看我们的修改版本:</p>
+
+<pre class="brush: js notranslate">class Person {
+ constructor(first, last, age, gender, interests) {
+ this.name = {
+ first,
+ last
+ };
+ this.age = age;
+ this.gender = gender;
+ this.interests = interests;
+ }
+
+ async greeting() {
+ return await Promise.resolve(`Hi! I'm ${this.name.first}`);
+ };
+
+ farewell() {
+ console.log(`${this.name.first} has left the building. Bye for now!`);
+ };
+}
+
+let han = new Person('Han', 'Solo', 25, 'male', ['Smuggling']);</pre>
+
+<p>第一个实例方法可以使用如下:</p>
+
+<pre class="brush: js notranslate">han.greeting().then(console.log);</pre>
+
+<h2 id="浏览器的支持">浏览器的支持</h2>
+
+<p>决定是否使用 async/await 时的一个考虑因素是支持旧浏览器。它们适用于大多数浏览器的现代版本,与promise相同; 主要的支持问题存在于Internet Explorer和Opera Mini。</p>
+
+<p>如果你想使用async/await但是担心旧的浏览器支持,你可以考虑使用<a href="https://babeljs.io/">BabelJS</a>库 —— 这允许你使用最新的JavaScript编写应用程序,让Babel找出用户浏览器需要的更改。在遇到不支持async/await 的浏览器时,Babel的 polyfill 可以自动提供适用于旧版浏览器的实现。</p>
+
+<h2 id="总结">总结</h2>
+
+<p>async/await提供了一种很好的,简化的方法来编写更易于阅读和维护的异步代码。即使浏览器支持在撰写本文时比其他异步代码机制更受限制,但无论是现在还是将来,都值得学习和考虑使用。</p>
+
+<p>{{PreviousMenuNext("Learn/JavaScript/Asynchronous/Promises", "Learn/JavaScript/Asynchronous/Choosing_the_right_approach", "Learn/JavaScript/Asynchronous")}}</p>
+
+<h2 id="本章内容">本章内容</h2>
+
+<ul>
+ <li><a href="/zh-CN/docs/Learn/JavaScript/Asynchronous/Concepts">异步编程的基本概念</a></li>
+ <li><a href="/zh-CN/docs/Learn/JavaScript/Asynchronous/Introducing">JavaScript异步简介</a></li>
+ <li><a href="/zh-CN/docs/Learn/JavaScript/Asynchronous/Timeouts_and_intervals">合作异步的JavaScript:超时和间隔</a></li>
+ <li><a href="/zh-CN/docs/Learn/JavaScript/Asynchronous/Promises">使用Promises:优雅的异步编程</a></li>
+ <li><a href="/zh-CN/docs/Learn/JavaScript/Asynchronous/Async_await">使用async和await:让异步更简单</a></li>
+ <li><a href="/zh-CN/docs/Learn/JavaScript/Asynchronous/Choosing_the_right_approach">选择正确的方法</a></li>
+</ul>
diff --git a/files/zh-cn/learn/javascript/异步/choosing_the_right_approach/index.html b/files/zh-cn/learn/javascript/异步/choosing_the_right_approach/index.html
new file mode 100644
index 0000000000..f11113420e
--- /dev/null
+++ b/files/zh-cn/learn/javascript/异步/choosing_the_right_approach/index.html
@@ -0,0 +1,523 @@
+---
+title: 选择正确的方法
+slug: learn/JavaScript/异步/Choosing_the_right_approach
+translation_of: Learn/JavaScript/Asynchronous/Choosing_the_right_approach
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenu("Learn/JavaScript/Asynchronous/Async_await", "Learn/JavaScript/Asynchronous")}}</div>
+
+<p>为了完成这个模块,我们将简要讨论我们在整个过程中讨论的不同编码技术和功能,看看你应该使用哪一个,并提供适当的常见陷阱的建议和提醒。随着时间的推移,我们可能会添加到此资源中。</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">预备条件:</th>
+ <td>基本的计算机素养,对JavaScript基础知识的合理理解。</td>
+ </tr>
+ <tr>
+ <th scope="row">目标:</th>
+ <td>能够在使用不同的异步编程技术时做出合理的选择。</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="异步回调">异步回调</h2>
+
+<p>通常在旧式API中找到,涉及将函数作为参数传递给另一个函数,然后在异步操作完成时调用该函数,以便回调可以依次对结果执行某些操作。这是promise的先导;它不那么高效或灵活。仅在必要时使用。</p>
+
+<table class="standard-table">
+ <caption>Useful for...</caption>
+ <thead>
+ <tr>
+ <th scope="col">Single delayed operation</th>
+ <th scope="col">Repeating operation</th>
+ <th scope="col">Multiple sequential operations</th>
+ <th scope="col">Multiple simultaneous operations</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>No</td>
+ <td>Yes (recursive callbacks)</td>
+ <td>Yes (nested callbacks)</td>
+ <td>No</td>
+ </tr>
+ </tbody>
+</table>
+
+<h3 id="代码示例">代码示例</h3>
+
+<p>通过<a href="/en-US/docs/Web/API/XMLHttpRequest"><code>XMLHttpRequest</code> API</a>加载资源的示例(<a href="https://mdn.github.io/learning-area/javascript/asynchronous/introducing/xhr-async-callback.html">run it live</a>,并查看<a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/introducing/xhr-async-callback.html">see the source</a>):</p>
+
+<pre class="brush: js notranslate">function loadAsset(url, type, callback) {
+ let xhr = new XMLHttpRequest();
+ xhr.open('GET', url);
+ xhr.responseType = type;
+
+ xhr.onload = function() {
+ callback(xhr.response);
+ };
+
+ xhr.send();
+}
+
+function displayImage(blob) {
+ let objectURL = URL.createObjectURL(blob);
+
+ let image = document.createElement('img');
+ image.src = objectURL;
+ document.body.appendChild(image);
+}
+
+loadAsset('coffee.jpg', 'blob', displayImage);</pre>
+
+<h3 id="缺陷">缺陷</h3>
+
+<ul>
+ <li>嵌套回调可能很麻烦且难以阅读(即“回调地狱”)</li>
+ <li>每层嵌套都需要调用一次失败回调,而使用promises,您只需使用一个<code>.catch()</code>代码块来处理整个链的错误。</li>
+ <li>异步回调不是很优雅。</li>
+ <li>Promise回调总是按照它们放在事件队列中的严格顺序调用;异步回调不是。</li>
+ <li>当传入到一个第三方库时,异步回调对函数如何执行失去完全控制。</li>
+</ul>
+
+<h3 id="浏览器兼容性">浏览器兼容性</h3>
+
+<p>非常好的一般支持,尽管API中回调的确切支持取决于特定的API。有关更具体的支持信息,请参阅您正在使用的API的参考文档。</p>
+
+<h3 id="更多信息">更多信息</h3>
+
+<ul>
+ <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Introducing">Introducing asynchronous JavaScript</a>, 尤其是 <a href="/en-US/docs/Learn/JavaScript/Asynchronous/Introducing#Async_callbacks">Async callbacks</a></li>
+</ul>
+
+<h2 id="setTimeout">setTimeout()</h2>
+
+<p><code><a href="/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout">setTimeout()</a></code> 是一种允许您在经过任意时间后运行函数的方法</p>
+
+<table class="standard-table">
+ <caption>Useful for...</caption>
+ <thead>
+ <tr>
+ <th scope="col">Single delayed operation</th>
+ <th scope="col">Repeating operation</th>
+ <th scope="col">Multiple sequential operations</th>
+ <th scope="col">Multiple simultaneous operations</th>
+ </tr>
+ <tr>
+ <td>Yes</td>
+ <td>Yes (recursive timeouts)</td>
+ <td>Yes (nested timeouts)</td>
+ <td>No</td>
+ </tr>
+ </thead>
+</table>
+
+<h3 id="代码示例_2">代码示例</h3>
+
+<p>这里浏览器将在执行匿名函数之前等待两秒钟,然后将显示警报消息(<a href="https://mdn.github.io/learning-area/javascript/asynchronous/loops-and-intervals/simple-settimeout.html">see it running live</a>,<a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/loops-and-intervals/simple-settimeout.html">see the source code</a>):</p>
+
+<pre class="brush: js notranslate">let myGreeting = setTimeout(function() {
+ alert('Hello, Mr. Universe!');
+}, 2000)</pre>
+
+<h3 id="缺陷_2">缺陷</h3>
+
+<p>您可以使用递归的<code>setTimeout()</code>调用以类似于<code>setInterval()</code>的方式重复运行函数,使用如下代码:</p>
+
+<pre class="brush: js notranslate">let i = 1;
+setTimeout(function run() {
+ console.log(i);
+ i++;
+
+ setTimeout(run, 100);
+}, 100);</pre>
+
+<p>递归<code>setTimeout()</code>和<code>setInterval()</code>之间存在差异:</p>
+
+<ul>
+ <li>递归<code>setTimeout()</code>保证执行之间至少经过指定的时间(在本例中为100ms);代码将运行,然后等待100毫秒再次运行。无论代码运行多长时间,间隔都是相同的。</li>
+ <li>使用<code>setInterval()</code>,我们选择的间隔包括执行我们想要运行的代码所花费的时间。假设代码需要40毫秒才能运行 ––然后间隔最终只有60毫秒。</li>
+</ul>
+
+<p>当你的代码有可能比你分配的时间间隔更长时间运行时,最好使用递归的<code>setTimeout()</code> ––这将使执行之间的时间间隔保持不变,无论代码执行多长时间,你不会得到错误。</p>
+
+<h3 id="浏览器兼容性_2">浏览器兼容性</h3>
+
+<p>{{Compat("api.WindowOrWorkerGlobalScope.setTimeout")}}</p>
+
+<h3 id="更多信息_2">更多信息</h3>
+
+<ul>
+ <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Timeouts_and_intervals">Cooperative asynchronous JavaScript: Timeouts and intervals</a>, in particular <a href="/en-US/docs/Learn/JavaScript/Asynchronous/Timeouts_and_intervals#setTimeout()">setTimeout()</a></li>
+ <li><a href="/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout">setTimeout() reference</a></li>
+</ul>
+
+<h2 id="setInterval">setInterval()</h2>
+
+<p><code><a href="/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setInterval">setInterval()</a></code>是一种允许您在每次执行之间以设定的时间间隔重复运行函数的方法。不如<code><a href="/en-US/docs/Web/API/window/requestAnimationFrame">requestAnimationFrame()</a></code>有效,但允许您选择运行速率/帧速率。</p>
+
+<table class="standard-table">
+ <caption>Useful for...</caption>
+ <thead>
+ <tr>
+ <th scope="col">Single delayed operation</th>
+ <th scope="col">Repeating operation</th>
+ <th scope="col">Multiple sequential operations</th>
+ <th scope="col">Multiple simultaneous operations</th>
+ </tr>
+ <tr>
+ <td>No</td>
+ <td>Yes</td>
+ <td>No (unless it is the same one)</td>
+ <td>No</td>
+ </tr>
+ </thead>
+</table>
+
+<h3 id="代码示例_3">代码示例</h3>
+
+<p>以下函数创建一个新的<code><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date">Date()</a></code>对象,使用<code><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleTimeString">toLocaleTimeString()</a></code>从中提取时间字符串,然后在UI中显示它。然后我们使用<code>setInterval()</code>每秒运行一次,创建每秒更新一次的数字时钟的效果(<a href="https://mdn.github.io/learning-area/javascript/asynchronous/loops-and-intervals/setinterval-clock.html">see this live</a>,<a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/loops-and-intervals/setinterval-clock.html">see the source</a>):</p>
+
+<pre class="brush: js notranslate">function displayTime() {
+ let date = new Date();
+ let time = date.toLocaleTimeString();
+ document.getElementById('demo').textContent = time;
+}
+
+const createClock = setInterval(displayTime, 1000);</pre>
+
+<h3 id="缺陷_3">缺陷</h3>
+
+<ul>
+ <li>帧速率未针对运行动画的系统进行优化,并且可能效率低下。除非您需要选择特定(较慢)的帧速率,否则通常最好使用<code>requestAnimationFrame(</code>).</li>
+</ul>
+
+<h3 id="浏览器兼容性_3">浏览器兼容性</h3>
+
+<p>{{Compat("api.WindowOrWorkerGlobalScope.setInterval")}}</p>
+
+<h3 id="更多信息_3">更多信息</h3>
+
+<ul>
+ <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Timeouts_and_intervals">Cooperative asynchronous JavaScript: Timeouts and intervals</a>, in particular <a href="/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setInterval">setInterval()</a></li>
+ <li><a href="/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setInterval">setInterval() reference</a></li>
+</ul>
+
+<h2 id="requestAnimationFrame">requestAnimationFrame()</h2>
+
+<p><code><a href="/en-US/docs/Web/API/window/requestAnimationFrame">requestAnimationFrame()</a></code>是一种允许您以给定当前浏览器/系统的最佳帧速率重复且高效地运行函数的方法。除非您需要特定的速率帧,否则您应该尽可能使用它而不要去使用<code>setInterval()/recursive setTimeout()</code>。</p>
+
+<table class="standard-table">
+ <caption>Useful for...</caption>
+ <thead>
+ <tr>
+ <th scope="col">Single delayed operation</th>
+ <th scope="col">Repeating operation</th>
+ <th scope="col">Multiple sequential operations</th>
+ <th scope="col">Multiple simultaneous operations</th>
+ </tr>
+ <tr>
+ <td>No</td>
+ <td>Yes</td>
+ <td>No (unless it is the same one)</td>
+ <td>No</td>
+ </tr>
+ </thead>
+</table>
+
+<h3 id="代码示例_4">代码示例</h3>
+
+<p>一个简单的动画旋转器;你可以查看<a href="https://mdn.github.io/learning-area/javascript/asynchronous/loops-and-intervals/simple-raf-spinner.html">example live on GitHub</a>(参见 <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/loops-and-intervals/simple-raf-spinner.html">source code</a> ):</p>
+
+<pre class="brush: js notranslate">const spinner = document.querySelector('div');
+let rotateCount = 0;
+let startTime = null;
+let rAF;
+
+function draw(timestamp) {
+ if(!startTime) {
+ startTime = timestamp;
+ }
+
+ let rotateCount = (timestamp - startTime) / 3;
+
+ spinner.style.transform = 'rotate(' + rotateCount + 'deg)';
+
+ if(rotateCount &gt; 359) {
+ rotateCount = 0;
+ }
+
+ rAF = requestAnimationFrame(draw);
+}
+
+draw();</pre>
+
+<h3 id="缺陷_4">缺陷</h3>
+
+<ul>
+ <li>您无法使用<code>requestAnimationFrame()</code>选择特定的帧速率。如果需要以较慢的帧速率运行动画,则需要使用<code>setInterval()</code>或递归的<code>setTimeout()</code>。</li>
+</ul>
+
+<h3 id="浏览器兼容性_4">浏览器兼容性</h3>
+
+<p>{{Compat("api.Window.requestAnimationFrame")}}</p>
+
+<h3 id="更多信息_4">更多信息</h3>
+
+<ul>
+ <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Timeouts_and_intervals">Cooperative asynchronous JavaScript: Timeouts and intervals</a>, in particular <a href="/en-US/docs/Learn/JavaScript/Asynchronous/Timeouts_and_intervals#requestAnimationFrame()">requestAnimationFrame()</a></li>
+ <li><a href="/en-US/docs/Web/API/window/requestAnimationFrame">requestAnimationFrame() reference</a></li>
+</ul>
+
+<h2 id="Promises">Promises</h2>
+
+<p><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise">Promises</a> 是一种JavaScript功能,允许您运行异步操作并等到它完全完成后再根据其结果运行另一个操作。 Promise是现代异步JavaScript的支柱。</p>
+
+<table class="standard-table">
+ <caption>Useful for...</caption>
+ <thead>
+ <tr>
+ <th scope="col">Single delayed operation</th>
+ <th scope="col">Repeating operation</th>
+ <th scope="col">Multiple sequential operations</th>
+ <th scope="col">Multiple simultaneous operations</th>
+ </tr>
+ <tr>
+ <td>No</td>
+ <td>No</td>
+ <td>Yes</td>
+ <td>See <code>Promise.all()</code>, below</td>
+ </tr>
+ </thead>
+</table>
+
+<h3 id="代码示例_5">代码示例</h3>
+
+<p>以下代码从服务器获取图像并将其显示在 {{htmlelement("img")}} 元素中;(<a href="https://mdn.github.io/learning-area/javascript/asynchronous/promises/simple-fetch-chained.html">see it live also</a>,<a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/promises/simple-fetch-chained.html">the source code</a>):</p>
+
+<pre class="brush: js notranslate">fetch('coffee.jpg')
+.then(response =&gt; response.blob())
+.then(myBlob =&gt; {
+ let objectURL = URL.createObjectURL(myBlob);
+ let image = document.createElement('img');
+ image.src = objectURL;
+ document.body.appendChild(image);
+})
+.catch(e =&gt; {
+ console.log('There has been a problem with your fetch operation: ' + e.message);
+});</pre>
+
+<h3 id="缺陷_5">缺陷</h3>
+
+<p>Promise链可能很复杂,难以解析。如果你嵌套了许多promises,你最终可能会遇到类似的麻烦来回调地狱。例如:</p>
+
+<pre class="brush: js notranslate">remotedb.allDocs({
+ include_docs: true,
+ attachments: true
+}).then(function (result) {
+ var docs = result.rows;
+ docs.forEach(function(element) {
+ localdb.put(element.doc).then(function(response) {
+ alert("Pulled doc with id " + element.doc._id + " and added to local db.");
+ }).catch(function (err) {
+ if (err.name == 'conflict') {
+ localdb.get(element.doc._id).then(function (resp) {
+ localdb.remove(resp._id, resp._rev).then(function (resp) {
+// et cetera...</pre>
+
+<p>最好使用promises的链功能,这样使用更平顺,更易于解析的结构:</p>
+
+<pre class="brush: js notranslate">remotedb.allDocs(...).then(function (resultOfAllDocs) {
+ return localdb.put(...);
+}).then(function (resultOfPut) {
+ return localdb.get(...);
+}).then(function (resultOfGet) {
+ return localdb.put(...);
+}).catch(function (err) {
+ console.log(err);
+});</pre>
+
+<p>乃至:</p>
+
+<pre class="brush: js notranslate">remotedb.allDocs(...)
+.then(resultOfAllDocs =&gt; {
+ return localdb.put(...);
+})
+.then(resultOfPut =&gt; {
+ return localdb.get(...);
+})
+.then(resultOfGet =&gt; {
+ return localdb.put(...);
+})
+.catch(err =&gt; console.log(err));</pre>
+
+<p>这涵盖了很多基础知识。对于更完整的论述,请参阅诺兰劳森的<a href="https://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html">We have a problem with promises</a>。</p>
+
+<h3 id="浏览器兼容性_5">浏览器兼容性</h3>
+
+<p>{{Compat("javascript.builtins.Promise")}}</p>
+
+<h3 id="更多信息_5">更多信息</h3>
+
+<ul>
+ <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Promises">Graceful asynchronous programming with Promises</a></li>
+ <li><a href="/en-US/docs/Web/JavaScript/Guide/Using_promises">Using promises</a></li>
+ <li><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise">Promise reference</a></li>
+</ul>
+
+<h2 id="Promise.all">Promise.all()</h2>
+
+<p>一种JavaScript功能,允许您等待多个promises完成,然后根据所有其他promises的结果运行进一步的操作。</p>
+
+<table class="standard-table">
+ <caption>Useful for...</caption>
+ <thead>
+ <tr>
+ <th scope="col">Single delayed operation</th>
+ <th scope="col">Repeating operation</th>
+ <th scope="col">Multiple sequential operations</th>
+ <th scope="col">Multiple simultaneous operations</th>
+ </tr>
+ <tr>
+ <td>No</td>
+ <td>No</td>
+ <td>No</td>
+ <td>Yes</td>
+ </tr>
+ </thead>
+</table>
+
+<h3 id="代码示例_6">代码示例</h3>
+
+<p>以下示例从服务器获取多个资源,并使用<code>Promise.all()</code>等待所有资源可用,然后显示所有这些资源––  <a href="https://mdn.github.io/learning-area/javascript/asynchronous/promises/promise-all.html">see it live</a>,并查看<a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/promises/promise-all.html">source code</a>:</p>
+
+<pre class="brush: js notranslate">function fetchAndDecode(url, type) {
+ // Returning the top level promise, so the result of the entire chain is returned out of the function
+ return fetch(url).then(response =&gt; {
+ // Depending on what type of file is being fetched, use the relevant function to decode its contents
+ if(type === 'blob') {
+ return response.blob();
+ } else if(type === 'text') {
+ return response.text();
+ }
+ })
+ .catch(e =&gt; {
+ console.log(`There has been a problem with your fetch operation for resource "${url}": ` + e.message);
+ });
+}
+
+// Call the fetchAndDecode() method to fetch the images and the text, and store their promises in variables
+let coffee = fetchAndDecode('coffee.jpg', 'blob');
+let tea = fetchAndDecode('tea.jpg', 'blob');
+let description = fetchAndDecode('description.txt', 'text');
+
+// Use Promise.all() to run code only when all three function calls have resolved
+Promise.all([coffee, tea, description]).then(values =&gt; {
+ console.log(values);
+ // Store each value returned from the promises in separate variables; create object URLs from the blobs
+ let objectURL1 = URL.createObjectURL(values[0]);
+ let objectURL2 = URL.createObjectURL(values[1]);
+ let descText = values[2];
+
+ // Display the images in &lt;img&gt; elements
+ let image1 = document.createElement('img');
+ let image2 = document.createElement('img');
+ image1.src = objectURL1;
+ image2.src = objectURL2;
+ document.body.appendChild(image1);
+ document.body.appendChild(image2);
+
+ // Display the text in a paragraph
+ let para = document.createElement('p');
+ para.textContent = descText;
+ document.body.appendChild(para);
+});</pre>
+
+<h3 id="缺陷_6">缺陷</h3>
+
+<ul>
+ <li>如果<code>Promise.all()</code>拒绝,那么你在其数组参数中输入的一个或多个promise(s)就会被拒绝,或者可能根本不返回promises。你需要检查每一个,看看他们返回了什么。</li>
+</ul>
+
+<h3 id="浏览器兼容性_6">浏览器兼容性</h3>
+
+<p>{{Compat("javascript.builtins.Promise.all")}}</p>
+
+<h3 id="更多信息_6">更多信息</h3>
+
+<ul>
+ <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Promises#Running_code_in_response_to_multiple_promises_fulfilling">Running code in response to multiple promises fulfilling</a></li>
+ <li><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all">Promise.all() reference</a></li>
+</ul>
+
+<h2 id="Asyncawait">Async/await</h2>
+
+<p>构造在promises之上的语法糖,允许您使用更像编写同步回调代码的语法来运行异步操作。</p>
+
+<table class="standard-table">
+ <caption>Useful for...</caption>
+ <thead>
+ <tr>
+ <th scope="col">Single delayed operation</th>
+ <th scope="col">Repeating operation</th>
+ <th scope="col">Multiple sequential operations</th>
+ <th scope="col">Multiple simultaneous operations</th>
+ </tr>
+ <tr>
+ <td>No</td>
+ <td>No</td>
+ <td>Yes</td>
+ <td>Yes (in combination with <code>Promise.all()</code>)</td>
+ </tr>
+ </thead>
+</table>
+
+<h3 id="代码示例_7">代码示例</h3>
+
+<p>以下示例是我们之前看到的简单承诺示例的重构,该示例获取并显示图像,使用async / await编写(<a href="https://mdn.github.io/learning-area/javascript/asynchronous/async-await/simple-refactored-fetch.html">see it live</a>,并查看<a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/async-await/simple-refactored-fetch.html">source code</a>):</p>
+
+<pre class="brush: js notranslate">async function myFetch() {
+ let response = await fetch('coffee.jpg');
+ let myBlob = await response.blob();
+
+ let objectURL = URL.createObjectURL(myBlob);
+ let image = document.createElement('img');
+ image.src = objectURL;
+ document.body.appendChild(image);
+}
+
+myFetch();</pre>
+
+<h3 id="缺陷_7">缺陷</h3>
+
+<ul>
+ <li>您不能在非<code>async</code>函数内或代码的顶级上下文环境中使用<code>await</code>运算符。这有时会导致需要创建额外的函数封包,这在某些情况下会略微令人沮丧。但大部分时间都值得。</li>
+ <li>浏览器对async / await的支持不如promises那样好。如果你想使用async / await但是担心旧的浏览器支持,你可以考虑使用<a href="https://babeljs.io/">BabelJS</a> 库 - 这允许你使用最新的JavaScript编写应用程序,让Babel找出用户浏览器需要的更改。</li>
+</ul>
+
+<h3 id="浏览器兼容性_7">浏览器兼容性</h3>
+
+<p>{{Compat("javascript.statements.async_function")}}</p>
+
+<h3 id="更多信息_7">更多信息</h3>
+
+<ul>
+ <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Async_await">Making asynchronous programming easier with async and await</a></li>
+ <li><a href="/en-US/docs/Web/JavaScript/Reference/Statements/async_function">Async function reference</a></li>
+ <li><a href="/en-US/docs/Web/JavaScript/Reference/Operators/await">Await operator reference</a></li>
+</ul>
+
+<p>{{PreviousMenu("Learn/JavaScript/Asynchronous/Async_await", "Learn/JavaScript/Asynchronous")}}</p>
+
+<h2 id="本章内容">本章内容</h2>
+
+<ul>
+ <li><a href="/zh-CN/docs/Learn/JavaScript/Asynchronous/Concepts">异步编程的基本概念</a></li>
+ <li><a href="/zh-CN/docs/Learn/JavaScript/Asynchronous/Introducing">JavaScript异步简介</a></li>
+ <li><a href="/zh-CN/docs/Learn/JavaScript/Asynchronous/Timeouts_and_intervals">JavaScript合作异步:超时和间隔</a></li>
+ <li><a href="/zh-CN/docs/Learn/JavaScript/Asynchronous/Promises">使用Promises:优雅的异步编程</a></li>
+ <li><a href="/zh-CN/docs/Learn/JavaScript/Asynchronous/Async_await">使用async和await:让异步编程更简单</a></li>
+ <li><a href="/zh-CN/docs/Learn/JavaScript/Asynchronous/Choosing_the_right_approach">选择正确的方法</a></li>
+</ul>
diff --git a/files/zh-cn/learn/javascript/异步/index.html b/files/zh-cn/learn/javascript/异步/index.html
new file mode 100644
index 0000000000..3d89f5a060
--- /dev/null
+++ b/files/zh-cn/learn/javascript/异步/index.html
@@ -0,0 +1,59 @@
+---
+title: 异步JavaScript
+slug: learn/JavaScript/异步
+tags:
+ - JavaScript
+ - Promises
+ - requestAnimationFrame
+ - 初学者
+ - 回调函数
+ - 异步
+ - 指南
+ - 等待
+ - 脚本编程
+ - 设置定时器
+ - 设置间隔
+translation_of: Learn/JavaScript/Asynchronous
+---
+<div></div>
+
+<div>{{LearnSidebar}}</div>
+
+<div></div>
+
+<p class="summary"><span class="seoSummary">在这个模块,我们将查看 {{Glossary("asynchronous")}} {{Glossary("JavaScript")}},异步为什么很重要,以及怎样使用异步来有效处理潜在的阻塞操作,比如从服务器上获取资源。</span></p>
+
+<h2 id="预备知识">预备知识</h2>
+
+<p>异步JavaScript 是一个相当高级的话题,建议你先完成( <a href="/en-US/docs/Learn/JavaScript/First_steps">JavaScript first steps</a> 和 <a href="/en-US/docs/Learn/JavaScript/Building_blocks">JavaScript building blocks</a>) 两个模块的学习后再来学习。</p>
+
+<p>如果你还不熟悉异步编程的概念,请从 <a href="/en-US/docs/Learn/JavaScript/Asynchronous/Concepts">通用异步编程概念</a>开始. 如果熟悉的话,可以直接从<a href="/en-US/docs/Learn/JavaScript/Asynchronous/Introducing">介绍异步JavaScript</a> 开始.</p>
+
+<div class="note">
+<p><strong>Note</strong>: 如果在当前阅读本文档而使用的电子设备(电脑/平板/其他)上,你没有权限生成自己的文件,你可以使用 <a href="http://jsbin.com/">JSBin</a> or <a href="https://thimble.mozilla.org/">Thimble</a> 在线编程工具来尝试文章里面的代码示例</p>
+</div>
+
+<h2 id="指南">指南</h2>
+
+<dl>
+ <dt><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Concepts">一般异步编程概念</a></dt>
+ <dd>
+ <p>浏览 异步相关的重要概念,在浏览器和JS里面的应用,学习本模块其他文章之前,你应该理解这些基本的概念。</p>
+ </dd>
+ <dt><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Introducing">介绍异步JS</a></dt>
+ <dd>这篇文章简单概括同步JS遇到的问题,首次提到一些不同的异步JS技术,他们是如何解决同步JS的问题</dd>
+ <dt><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Loops_and_intervals">合作异步JS:Timeouts and intervals</a></dt>
+ <dd>在这里介绍JS传统的异步方法:在一段时间后运行 或者 在设定时间周期反复运行,看看这些技术如何使用,有什么内在的问题.</dd>
+ <dt><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Promises">优雅的处理异步操作:Promises</a></dt>
+ <dd>Promises 是JS一个相对比较新的特性,你可以使用它来延迟一些操作直到前面的代码已经返回结果。对于时间上顺序完成的一系列操作,这个真的有用。本文展示promises 如何工作,使用WebAPIs何处会见到它, 最重要的:怎样写你自己的promises.</dd>
+ <dt><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Async_await">让异步编程简单: async and await</a></dt>
+ <dd>Promises 有点复杂, 所以现代的浏览器都实现了 <code>async</code> 函数和 <code>await</code> 操作符 —--前者允许标准函数隐式地和 promises 工作, 后者可以在<code>async</code> 函数里面使用,等待promises运行结束,函数再继续运行。</dd>
+ <dt><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Choosing_the_right_approach">选择正确的方法</a></dt>
+ <dd>结束本模块之前,回顾一下已经讨论的编程技术和特性:什么时候用哪个。有推荐,也有常见的陷阱提醒。</dd>
+</dl>
+
+<h2 id="参考">参考</h2>
+
+<ul>
+ <li><a href="https://eloquentjavascript.net/11_async.html">Asynchronous Programming</a> from the fantastic <a href="https://eloquentjavascript.net/">Eloquent JavaScript</a> online book by Marijn Haverbeke.</li>
+</ul>
diff --git a/files/zh-cn/learn/javascript/异步/promises语法/index.html b/files/zh-cn/learn/javascript/异步/promises语法/index.html
new file mode 100644
index 0000000000..a817a71d79
--- /dev/null
+++ b/files/zh-cn/learn/javascript/异步/promises语法/index.html
@@ -0,0 +1,589 @@
+---
+title: 优雅的异步处理
+slug: learn/JavaScript/异步/Promises语法
+translation_of: Learn/JavaScript/Asynchronous/Promises
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/JavaScript/Asynchronous/Timeouts_and_intervals", "Learn/JavaScript/Asynchronous/Async_await", "Learn/JavaScript/Asynchronous")}}</div>
+
+<p class="summary"><strong>Promise</strong> 是 JavaScript 语言的一个相对较新的功能,允许你推迟进一步的操作,直到上一个操作完成或响应其失败。这对于设置一系列异步操作以正常工作非常有用。本文向你展示了promises如何工作,如何在Web API中使用它们以及如何编写自己的API</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">前提条件:</th>
+ <td>基本的计算机素养,具备基础的JavaScript知识</td>
+ </tr>
+ <tr>
+ <th scope="row">目标:</th>
+ <td>理解并使用学习如何使用Promises</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="什么是promises">什么是promises?</h2>
+
+<p>我们在教程的第一篇文章中简要地了解了 <a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise">Promises</a>,接下来我们将在更深层次理解Promise。</p>
+
+<p>本质上,Promise 是一个对象,代表操作的中间状态 —— 正如它的单词含义 '承诺' ,它保证在未来可能返回某种结果。虽然 Promise 并不保证操作在何时完成并返回结果,但是它保证当结果可用时,你的代码能正确处理结果,当结果不可用时,你的代码同样会被执行,来优雅的处理错误。</p>
+
+<p>通常你不会对一个异步操作从开始执行到返回结果所用的时间感兴趣(除非它耗时过长),你会更想在任何时候都能响应操作结果,当然它不会阻塞其余代码的执行就更好了。</p>
+
+<p>你与 Promise 常见的交互之一就是 Web API 返回的 promise 对象。让我们设想一个视频聊天应用程序,该程序有一个展示用户的朋友列表的窗口,可以点击朋友旁边的按钮对朋友视频呼叫。</p>
+
+<p>该按钮的处理程序调用 {{domxref("MediaDevices.getUserMedia", "getUserMedia()")}} 来访问用户的摄像头和麦克风。由于 <code>getUserMedia()</code> 必须确保用户具有使用这些设备的权限,并询问用户要使用哪个麦克风和摄像头(或者是否仅进行语音通话,以及其他可能的选项),因此它会产生阻塞,直到用户做出所有的决定,并且摄像头和麦克风都已启用。此外,用户可能不会立即响应权限请求。所以 <code>getUserMedia()</code> 可能需要很长时间。</p>
+
+<p>由于 <code>getUserMedia()</code> 是在浏览器的主线程进行调用,整个浏览器将会处于阻塞状态直到 <code>getUserMedia()</code> 返回,这是不应该发生的;不使用Promise,浏览器将处于不可用状态直到用户为摄像头和麦克风做出决定。因此 <code>getUserMedia()</code> 返回一个Promise对象,即 {{jsxref("promise")}},一旦  {{domxref("MediaStream")}} 流可用才去解析,而不是等待用户操作、启动选中的设备并直接返回从所选资源创建的 {{domxref("MediaStream")}} 流。</p>
+
+<p>上述视频聊天应用程序的代码可能像下面这样:</p>
+
+<pre class="brush: js notranslate">function handleCallButton(evt) {
+ setStatusMessage("Calling...");
+ navigator.mediaDevices.getUserMedia({video: true, audio: true})
+ .then(chatStream =&gt; {
+ selfViewElem.srcObject = chatStream;
+ chatStream.getTracks().forEach(track =&gt; myPeerConnection.addTrack(track, chatStream));
+ setStatusMessage("Connected");
+ }).catch(err =&gt; {
+ setStatusMessage("Failed to connect");
+ });
+}
+</pre>
+
+<p>这个函数在开头调用 <code>setStatusMessage()</code> 来更新状态显示信息"Calling...", 表示正在尝试通话。接下来调用 <code>getUserMedia()</code>,请求具有视频及音频轨的流,一旦获得这个流,就将其显示在"selfViewElem"的video元素中。接下来将这个流的每个轨道添加到表示与另一个用户的连接的 <a href="/en-US/docs/Web/API/WebRTC_API">WebRTC</a>,参见{{domxref("RTCPeerConnection")}}。在这之后,状态显示为"Connected"。</p>
+
+<p>如果<code>getUserMedia()</code>失败,则catch块运行。这使用<code>setStatusMessage()</code>更新状态框以指示发生错误。</p>
+
+<p>这里重要的是<code>getUserMedia()</code>调用几乎立即返回,即使尚未获得相机流。即使<code>handleCallButton()</code>函数向调用它的代码返回结果,当<code>getUserMedia()</code>完成工作时,它也会调用你提供的处理程序。只要应用程序不假设流式传输已经开始,它就可以继续运行。</p>
+
+<div class="blockIndicator note">
+<p><strong>注意:</strong>  如果你有兴趣,可以在文章<a href="/docs/Web/API/WebRTC_API/Signaling_and_video_calling">Signaling and video calling</a>中了解有关此高级主题的更多信息。在该示例中使用与此类似的代码,但更完整。</p>
+</div>
+
+<h2 id="回调函数的麻烦"> 回调函数的麻烦</h2>
+
+<p>要完全理解为什么 promise 是一件好事,应该回想之前的回调函数,理解它们造成的困难。</p>
+
+<p>我们来谈谈订购披萨作为类比。为了使你的订单成功,你必须按顺序执行,不按顺序执行或上一步没完成就执行下一步是不会成功的:</p>
+
+<ol>
+ <li>选择配料。如果你是优柔寡断,这可能需要一段时间,如果你无法下定决心或者决定换咖喱,可能会失败。</li>
+ <li>下订单。返回比萨饼可能需要一段时间,如果餐厅没有烹饪所需的配料,可能会失败。</li>
+ <li>然后你收集你的披萨吃。如果你忘记了自己的钱包,那么这可能会失败,所以无法支付比萨饼的费用!</li>
+</ol>
+
+<p>对于旧式<a href="/en-US/docs/Learn/JavaScript/Asynchronous/Introducing#Callbacks">callbacks</a>,上述功能的伪代码表示可能如下所示:</p>
+
+<pre class="brush: js notranslate">chooseToppings(function(toppings) {
+ placeOrder(toppings, function(order) {
+ collectOrder(order, function(pizza) {
+ eatPizza(pizza);
+ }, failureCallback);
+ }, failureCallback);
+}, failureCallback);</pre>
+
+<p>这很麻烦且难以阅读(通常称为“回调地狱”),需要多次调用<code>failureCallback()</code>(每个嵌套函数一次),还有其他问题。</p>
+
+<h3 id="使用promise改良">使用promise改良</h3>
+
+<p>Promises使得上面的情况更容易编写,解析和运行。如果我们使用异步promises代表上面的伪代码,我们最终会得到这样的结果:</p>
+
+<pre class="brush: js notranslate">chooseToppings()
+.then(function(toppings) {
+ return placeOrder(toppings);
+})
+.then(function(order) {
+ return collectOrder(order);
+})
+.then(function(pizza) {
+ eatPizza(pizza);
+})
+.catch(failureCallback);</pre>
+
+<p>这要好得多 - 更容易看到发生了什么,我们只需要一个<code>.catch()</code>块来处理所有错误,它不会阻塞主线程(所以我们可以在等待时继续玩视频游戏为了准备好收集披萨),并保证每个操作在运行之前等待先前的操作完成。我们能够以这种方式一个接一个地链接多个异步操作,因为每个<code>.then()</code>块返回一个新的promise,当<code>.then()</code>块运行完毕时它会解析。聪明,对吗?</p>
+
+<p>使用箭头函数,你可以进一步简化代码:</p>
+
+<pre class="brush: js notranslate">chooseToppings()
+.then(toppings =&gt;
+ placeOrder(toppings)
+)
+.then(order =&gt;
+ collectOrder(order)
+)
+.then(pizza =&gt;
+ eatPizza(pizza)
+)
+.catch(failureCallback);</pre>
+
+<p>甚至这样:</p>
+
+<pre class="brush: js notranslate">chooseToppings()
+.then(toppings =&gt; placeOrder(toppings))
+.then(order =&gt; collectOrder(order))
+.then(pizza =&gt; eatPizza(pizza))
+.catch(failureCallback);</pre>
+
+<p>这是有效的,因为使用箭头函数 <code>() =&gt; x</code> 是 <code>()=&gt; {return x;}</code>  的有效简写; 。</p>
+
+<p>你甚至可以这样做,因为函数只是直接传递它们的参数,所以不需要额外的函数层:</p>
+
+<pre class="brush: js notranslate">chooseToppings().then(placeOrder).then(collectOrder).then(eatPizza).catch(failureCallback);</pre>
+
+<p>但是,这并不容易阅读,如果你的块比我们在此处显示的更复杂,则此语法可能无法使用。</p>
+
+<div class="blockIndicator note">
+<p><strong>注意</strong>: 你可以使用 <code>async/await</code> 语法进行进一步的改进,我们将在下一篇文章中深入讨论。</p>
+</div>
+
+<p>最基本的,promise与事件监听器类似,但有一些差异:</p>
+
+<ul>
+ <li>一个promise只能成功或失败一次。它不能成功或失败两次,并且一旦操作完成,它就无法从成功切换到失败,反之亦然。</li>
+ <li>如果promise成功或失败并且你稍后添加成功/失败回调,则将调用正确的回调,即使事件发生在较早的时间。</li>
+</ul>
+
+<h2 id="解释promise的基本语法:一个真实的例子"> 解释promise的基本语法:一个真实的例子</h2>
+
+<p>Promises 很重要,因为大多数现代Web API都将它们用于执行潜在冗长任务的函数。要使用现代Web技术,你需要使用promises。在本章的后面我们将看看如何编写自己的promises,但是现在我们将看一些你将在Web API中遇到的简单示例。</p>
+
+<p>在第一个示例中,我们将使用<code>fetch()</code>方法从Web获取图像,<code>blob()</code> 方法来转换获取响应的原始内容到 <a href="https://developer.mozilla.org/en-US/docs/Web/API/Blob">Blob</a> 对象,然后在 <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img">&lt;img&gt; </a>元素内显示该<code>blob</code>。这与我们在 <a href="/en-US/docs/Learn/JavaScript/Asynchronous/Introducing#Asynchronous_JavaScript">first article of the series</a>中看到的示例非常相似,但是会在构建你自己的基于 promise 的代码时有所不同。 </p>
+
+<div class="blockIndicator note">
+<p><strong>注意: </strong>下列代码无法直接运行(i.e. via a <code>file://</code> URL)。你需要<a href="https://developer.mozilla.org/en-US/docs/Learn/Common_questions/set_up_a_local_testing_server">本地测试服务器</a>,或是 <a href="https://glitch.com/">Glitch</a> 和 <a href="https://developer.mozilla.org/en-US/docs/Learn/Common_questions/Using_Github_pages">GitHub pages</a> 这样的在线解决方案。</p>
+</div>
+
+<ol>
+ <li>
+ <p>首先,下载我们的 <a href="https://github.com/mdn/learning-area/blob/master/html/introduction-to-html/getting-started/index.html">simple HTML template</a>和<a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/promises/coffee.jpg">sample image file</a>。</p>
+ </li>
+ <li>
+ <p>将 {{htmlelement("script")}} 元素添加在HTML {{htmlelement("body")}} 的底部。</p>
+ </li>
+ <li>
+ <p>在 {{HTMLElement("script")}} 元素内,添加以下行:</p>
+
+ <pre class="brush: js notranslate">let promise = fetch('coffee.jpg');</pre>
+
+ <p>这会调用 <code>fetch()</code> 方法,将图像的URL作为参数从网络中提取。这也可以将options对象作为可选的第二个参数,但我们现在只使用最简单的版本。我们将 <code>fetch()</code> 返回的promise对象存储在一个名为promise的变量中。正如我们之前所说的,这个对象代表了一个最初既不成功也不失败的中间状态 - 这个状态下的promise的官方术语叫作<strong>pending</strong>。</p>
+ </li>
+ <li>为了响应成功完成操作(在这种情况下,当返回{{domxref("Response")}}时),我们调用promise对象的.<code>then()</code>方法。 <code>.then()</code>块中的回调(称为执行程序)仅在promise调用成功完成时运行并返回{{domxref("Response")}}对象 - 在promise-speak中,当它已被满足时。它将返回的{{domxref("Response")}}对象作为参数传递。</li>
+</ol>
+
+<div class="blockIndicator note">
+<p><strong>注意</strong>: <code>.then()</code>块的工作方式类似于使用<code>AddEventListener()</code>向对象添加事件侦听器时的方式。它不会在事件发生之前运行(当promise履行时)。最显着的区别是<code>.then()</code>每次使用时只运行一次,而事件监听器可以多次调用。</p>
+</div>
+
+<p>我们立即对此响应运行<code>blob()</code>方法以确保响应主体完全下载,并且当它可用时将其转换为我们可以执行某些操作的<code>Blob</code>对象。返回的结果如下:</p>
+
+<pre class="brush: js notranslate">response =&gt; response.blob()</pre>
+
+<p>这是下面的简写</p>
+
+<pre class="brush: js notranslate">function(response) {
+ return response.blob();
+}
+</pre>
+
+<p>好的,我们还需要做点额外的工作。Fetch promises 不会产生 404 或 500错误,只有在产生像网路故障的情况时才会不工作。总的来说,Fetch promises 总是成功运行,即使<a href="https://developer.mozilla.org/en-US/docs/Web/API/Response/ok">response.ok</a> 属性是<code> false</code>。为了产生404错误,我们需要判断 <code>response.ok</code> ,如果是 <code>false</code>,抛出错误,否则返回 blob。就像下面的代码这样做。</p>
+
+<pre class="notranslate"><code>let promise2 = promise.then(response =&gt; {
+ if (!response.ok) {
+ throw new Error(`HTTP error! status: ${response.status}`);
+ } else {
+ return response.blob();
+ }
+});</code></pre>
+
+<p>5. 每次调用<code>.then()</code>都会创建一个新的promise。这非常有用;因为<code>blob()</code>方法也返回一个promise,我们可以通过调用第二个promise的<code>.then()</code>方法来处理它在履行时返回的<code>Blob</code>对象。因为我们想要对<code>blob</code>执行一些更复杂的操作,而不仅仅运行单个方法并返回结果,这次我们需要将函数体包装成花括号(否则会抛出错误)。</p>
+
+<p>将以下内容添加到代码的末尾:</p>
+
+<pre class="brush: js notranslate">let promise3 = promise2.then(myBlob =&gt; {})</pre>
+
+<p>6. 现在让我们填写执行程序函数的主体。在花括号内添加以下行:</p>
+
+<pre class="notranslate"><code>let objectURL = URL.createObjectURL(myBlob);
+let image = document.createElement('img');
+image.src = objectURL;
+document.body.appendChild(image);</code></pre>
+
+<p>这里我们运行{{domxref("URL.createObjectURL()")}}方法,将其作为<code>Blob</code>在第二个promise实现时返回的参数传递。这将返回指向该对象的URL。然后我们创建一个{{htmlelement("img")}}元素,将其<code>src</code>属性设置为等于对象URL并将其附加到DOM,这样图像就会显示在页面上!</p>
+
+<p>如果你保存刚刚创建的HTML文件并将其加载到浏览器中,你将看到图像按预期显示在页面中。干得好!</p>
+
+<div class="blockIndicator note">
+<p><strong>注意</strong>: 你可能会注意到这些例子有点做作。你可以取消整个<code>fetch()</code>和<code>blob()</code>链,只需创建一个&lt;img&gt;元素并将其<code>src</code>属性值设置为图像文件的URL,即<code>coffee.jpg</code>。然而,我们选择了这个例子,因为它以简单的方式展示了promise,而不是真实世界的适当性。</p>
+</div>
+
+<h3 id="响应失败">响应失败</h3>
+
+<p>缺少一些东西 - 如果其中一个promise失败(<strong>rejects</strong>,in promise-speak),目前没有什么可以明确地处理错误。我们可以通过运行前一个promise的 <code>.catch()</code> 方法来添加错误处理。立即添加:</p>
+
+<pre class="brush: js line-numbers language-js notranslate"><code class="language-js"><span class="keyword token">let</span> errorCase <span class="operator token">=</span> promise3<span class="punctuation token">.</span><span class="function token">catch</span><span class="punctuation token">(</span><span class="parameter token">e</span> <span class="operator token">=&gt;</span> <span class="punctuation token">{</span>
+ console<span class="punctuation token">.</span><span class="function token">log</span><span class="punctuation token">(</span><span class="string token">'There has been a problem with your fetch operation: '</span> <span class="operator token">+</span> e<span class="punctuation token">.</span>message<span class="punctuation token">)</span><span class="punctuation token">;</span>
+<span class="punctuation token">}</span><span class="punctuation token">)</span><span class="punctuation token">;</span></code></pre>
+
+<p>要查看此操作,请尝试拼错图像的URL并重新加载页面。该错误将在浏览器的开发人员工具的控制台中报告。</p>
+
+<p>如果你根本不操心包括的 <code>.catch()</code> 块,这并没有做太多的事情,但考虑一下(指.catch()块) ––这会使我们可以完全控制错误处理方式。在真实的应用程序中,你的<code>.catch()</code>块可以重试获取图像,或显示默认图像,或提示用户提供不同的图像URL等等。</p>
+
+<div class="blockIndicator note">
+<p><strong>注意</strong>: 你可以参考 <a href="https://mdn.github.io/learning-area/javascript/asynchronous/promises/simple-fetch.html">our version of the example live</a> (参阅 <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/promises/simple-fetch.html">source code</a> ).</p>
+</div>
+
+<h3 id="将代码块链在一起">将代码块链在一起</h3>
+
+<p>这是写出来的一种非常简便的方式;我们故意这样做是为了帮助你清楚地了解发生了什么。如本文前面所示,你可以将<code>.then()</code>块(以及<code>.catch()</code>块)链接在一起。上面的代码也可以这样写(参阅GitHub上的<a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/promises/simple-fetch-chained.html">simple-fetch-chained.html</a> ):</p>
+
+<pre class="brush: js notranslate">fetch('coffee.jpg')
+.then(response =&gt; response.blob())
+.then(myBlob =&gt; {
+ let objectURL = URL.createObjectURL(myBlob);
+ let image = document.createElement('img');
+ image.src = objectURL;
+ document.body.appendChild(image);
+})
+.catch(e =&gt; {
+ console.log('There has been a problem with your fetch operation: ' + e.message);
+});</pre>
+
+<p>请记住,履行的promise所返回的值将成为传递给下一个 <code>.then()</code> 块的executor函数的参数。</p>
+
+<div class="blockIndicator note">
+<p><strong>注意</strong>: promise中的<code>.then()/catch()</code>块基本上是同步代码中<code><a href="/en-US/docs/Web/JavaScript/Reference/Statements/try...catch">try...catch</a></code>块的异步等价物。请记住,同步<code>try ... catch</code>在异步代码中不起作用。</p>
+</div>
+
+<h2 id="Promise术语回顾">Promise术语回顾</h2>
+
+<p>在上面的部分中有很多要介绍的内容,所以让我们快速回过头来给你<a href="/zh-CN/docs/Learn/JavaScript/Asynchronous/Promises#Promise_terminology_recap">一个简短的指南</a>,你可以将它添加到书签中,以便将来更新你的记忆。你还应该再次阅读上述部分,以确保这些概念坚持下去。</p>
+
+<ol>
+ <li>创建promise时,它既不是成功也不是失败状态。这个状态叫作<strong>pending</strong>(待定)。</li>
+ <li>当promise返回时,称为 <strong>resolved</strong>(已解决).
+ <ol>
+ <li>一个成功<strong>resolved</strong>的promise称为<strong>fullfilled</strong>(<strong>实现</strong>)。它返回一个值,可以通过将<code>.then()</code>块链接到promise链的末尾来访问该值。<code> .then()</code>块中的执行程序函数将包含promise的返回值。</li>
+ <li>一个不成功<strong>resolved</strong>的promise被称为<strong>rejected</strong>(<strong>拒绝</strong>)了。它返回一个原因(<strong>reason</strong>),一条错误消息,说明为什么拒绝promise。可以通过将<code>.catch()</code>块链接到promise链的末尾来访问此原因。</li>
+ </ol>
+ </li>
+</ol>
+
+<h2 id="运行代码以响应多个Promises的实现"> 运行代码以响应多个Promises的实现</h2>
+
+<p>上面的例子向我们展示了使用promises的一些真正的基础知识。现在让我们看一些更高级的功能。首先,链接进程一个接一个地发生都很好,但是如果你想在一大堆Promises全部完成之后运行一些代码呢?</p>
+
+<p> 你可以使用巧妙命名的<a href="/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/all">Promise.all()</a>静态方法完成此操作。这将一个promises数组作为输入参数,并返回一个新的Promise对象,只有当数组中的所有promise都满足时才会满足。它看起来像这样:</p>
+
+<pre class="brush: js notranslate">Promise.all([a, b, c]).then(values =&gt; {
+ ...
+});</pre>
+
+<p>如果它们都<strong>实现</strong>,那么一个包含所有这些结果的数组将作为<code>.all()</code>的参数传给其链接的<code>.then()</code>块的执行器函数。如果传递给<code>Promise.all()</code>的任何promise都<strong>拒绝</strong>,整个块将<strong>拒绝</strong>。</p>
+
+<p>这非常有用。想象一下,我们正在获取信息以在内容上动态填充页面上的UI功能。在许多情况下,接收所有数据然后才显示完整内容,而不是显示部分信息是有意义的。</p>
+
+<p>让我们构建另一个示例来展示这一点。</p>
+
+<ol>
+ <li>
+ <p>下载我们页面模板(<a href="https://github.com/mdn/learning-area/blob/master/html/introduction-to-html/getting-started/index.html">page template</a>)的新副本,并再次在结束&lt;/ body&gt;标记之前放置一个&lt;script&gt;元素。</p>
+ </li>
+ <li>
+ <p>下载我们的源文件(<a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/promises/coffee.jpg">coffee.jpg</a>, <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/promises/tea.jpg">tea.jpg</a>和 <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/promises/description.txt">description.txt</a>),或者随意替换成你自己的文件。</p>
+ </li>
+ <li>
+ <p>在我们的脚本中,我们将首先定义一个函数,该函数返回我们要发送给<code>Promise.all()</code>的promise。如果我们只想运行<code>Promise.all()</code>块以响应三个<code>fetch()</code>操作完成,这将很容易。我们可以这样做:</p>
+
+ <pre class="brush: js notranslate">let a = fetch(url1);
+let b = fetch(url2);
+let c = fetch(url3);
+
+Promise.all([a, b, c]).then(values =&gt; {
+ ...
+});</pre>
+
+ <p>当promise是<strong>fullfilled</strong>时,传递到履行处理程序的<code>values</code>将包含三个Response对象,每个对象用于已完成的每个<code>fetch()</code>操作。</p>
+
+ <p>但是,我们不想这样做。我们的代码不关心<code>fetch()</code>操作何时完成。相反,我们想要的是加载的数据。这意味着当我们返回代表图像的可用<code>blob</code>和可用的文本字符串时,我们想要运行<code>Promise.all()</code>块。我们可以编写一个执行此操作的函数;在<code>&lt;script&gt;</code>元素中添加以下内容:</p>
+
+ <pre class="brush: js notranslate">function fetchAndDecode(url, type) {
+ return fetch(url).then(response =&gt; {
+ if (type === 'blob') {
+ return response.blob();
+ } else if (type === 'text') {
+ return response.text();
+ }
+ })
+ .catch(e =&gt; {
+ console.log('There has been a problem with your fetch operation: ' + e.message);
+ });
+}</pre>
+
+ <p>这看起来有点复杂,所以让我们一步一步地完成它:</p>
+
+ <ol>
+ <li>首先,我们定义函数,向它传递一个URL和一个表示它正在获取的资源类型的字符串。</li>
+ <li>在函数体内部,我们有一个类似于我们在第一个例子中看到的结构 - 我们调用<code>fetch()</code>函数来获取指定URL处的资源,然后将其链接到另一个返回解码(或“read”)的promise。 )响应body。这始终是前一个示例中的<code>blob()</code>方法。</li>
+ <li>但是,这里有两处不同:
+ <ul>
+ <li>首先,我们返回的第二个promise会因类型值的不同而不同。在执行函数内部,我们包含一个简单的<code>if ... else if</code>语句,根据我们需要解码的文件类型返回不同的promise(在这种情况下,我们可以选择<code>blob</code>或<code>text</code>,但这很容易扩展这个以处理其他类型)。</li>
+ <li>其次,我们在<code>fetch()</code>调用之前添加了<code>return</code>关键字。它的作用是运行整个链,然后运行最终结果(即<code>blob()</code>或<code>text()</code>返回的promise作为我们刚刚定义的函数的返回值)。实际上,<code>return</code>语句将结果从链返回到顶部。</li>
+ </ul>
+ </li>
+ <li>
+ <p>在块结束时,我们链接一个<code>.catch()</code>调用,以处理任何可能出现在数组中传递给<code>.all()</code>的任何promise的错误情况。如果任何promise被拒绝,<code>catch</code>块将告诉你哪个promise有问题。 <code>.all()</code>块(见下文)仍然可以实现,但不会显示有问题的资源。如果你想要<code>.all</code>拒绝,你必须将<code>.catch()</code>块链接到那里的末尾。</p>
+ </li>
+ </ol>
+
+ <p>函数体内部的代码是async(异步)和基于promise的,因此实际上整个函数就像一个promise ––方便啊!</p>
+ </li>
+ <li>
+ <p>接下来,我们调用我们的函数三次以开始获取和解码图像和文本的过程,并将每个返回的promises存储在变量中。在以前的代码下面添加以下内容:</p>
+
+ <pre class="brush: js notranslate">let coffee = fetchAndDecode('coffee.jpg', 'blob');
+let tea = fetchAndDecode('tea.jpg', 'blob');
+let description = fetchAndDecode('description.txt', 'text');</pre>
+ </li>
+ <li>
+ <p>接下来,我们将定义一个<code>Promise.all()</code>块,仅当上面存储的所有三个promise都已成功完成时才运行一些代码。首先,在<code>.then()</code>调用中添加一个带有空执行程序的块,如下所示:</p>
+
+ <pre class="brush: js notranslate">Promise.all([coffee, tea, description]).then(values =&gt; {
+
+});</pre>
+
+ <p>你可以看到它需要一个包含promises作为参数的数组。执行者只有在所有三个promises的状态成为<strong>resolved</strong>时才会运行;当发生这种情况时,它将传递一个数组,其中包含来自各个promise(即解码的响应主体)的结果,类似于 [coffee-results, tea-results, description-results].</p>
+ </li>
+ <li>
+ <p>最后,在执行程序中添加以下内容。这里我们使用一些相当简单的同步代码将结果存储在单独的变量中(从blob创建对象URL),然后在页面上显示图像和文本。</p>
+
+ <pre class="brush: js notranslate">console.log(values);
+// Store each value returned from the promises in separate variables; create object URLs from the blobs
+let objectURL1 = URL.createObjectURL(values[0]);
+let objectURL2 = URL.createObjectURL(values[1]);
+let descText = values[2];
+
+// Display the images in &lt;img&gt; elements
+let image1 = document.createElement('img');
+let image2 = document.createElement('img');
+image1.src = objectURL1;
+image2.src = objectURL2;
+document.body.appendChild(image1);
+document.body.appendChild(image2);
+
+// Display the text in a paragraph
+let para = document.createElement('p');
+para.textContent = descText;
+document.body.appendChild(para);</pre>
+ </li>
+ <li>
+ <p>保存并刷新,你应该看到所有UI组件都已加载,尽管不是特别有吸引力!</p>
+ </li>
+</ol>
+
+<p>我们在这里提供的用于显示项目的代码相当简陋,但现在作为解释器。</p>
+
+<div class="blockIndicator note">
+<p><strong>注意</strong>: 如果你遇到困难,可以将你的代码版本与我们的代码进行比较,看看它的外观 -––<a href="https://mdn.github.io/learning-area/javascript/asynchronous/promises/promise-all.html">see it live</a>,也可以参阅<a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/promises/promise-all.html">source code</a>。</p>
+</div>
+
+<div class="blockIndicator note">
+<p><strong>注意</strong>: 如果你正在改进这段代码,你可能想要遍历一个项目列表来显示,获取和解码每个项目,然后循环遍历<code>Promise.all()</code>内部的结果,运行一个不同的函数来显示每个项目取决于什么代码的类型是。这将使它适用于任何数量的项目,而不仅仅是三个。</p>
+
+<p>此外,你可以确定要获取的文件类型,而无需显式类型属性。例如,你可以使用<code><a href="/en-US/docs/Web/API/Headers/get">response.headers.get("content-type")</a></code>检查响应的{{HTTPHeader("Content-Type")}} HTTP标头,然后做出相应的反应。</p>
+</div>
+
+<h2 id="在promise_fullfillreject后运行一些最终代码"> 在promise fullfill/reject后运行一些最终代码</h2>
+
+<p>在promise完成后,你可能希望运行最后一段代码,无论它是否已实现(fullfilled)或被拒绝(rejected)。此前,你必须在<code>.then()</code>和<code>.catch()</code>回调中包含相同的代码,例如:</p>
+
+<pre class="brush: js notranslate">myPromise
+.then(response =&gt; {
+ doSomething(response);
+ runFinalCode();
+})
+.catch(e =&gt; {
+ returnError(e);
+ runFinalCode();
+});</pre>
+
+<p>在最近的现代浏览器中,<code><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/finally">.finally()</a></code> 方法可用,它可以链接到常规promise链的末尾,允许你减少代码重复并更优雅地执行操作。上面的代码现在可以写成如下:</p>
+
+<pre class="brush: js notranslate">myPromise
+.then(response =&gt; {
+ doSomething(response);
+})
+.catch(e =&gt; {
+ returnError(e);
+})
+.finally(() =&gt; {
+ runFinalCode();
+});</pre>
+
+<p>有关一个真实示例,请查看我们的<a href="https://mdn.github.io/learning-area/javascript/asynchronous/promises/promise-finally.html">promise-finally.html demo</a>(另请参阅<a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/promises/promise-finally.html">source code</a>)。这与我们在上面部分中看到的<code>Promise.all()</code>演示完全相同,除了在<code>fetchAndDecode()</code>函数中我们将<code>finally()</code>调用链接到链的末尾:</p>
+
+<pre class="brush: js notranslate">function fetchAndDecode(url, type) {
+ return fetch(url).then(response =&gt; {
+ if(type === 'blob') {
+ return response.blob();
+ } else if(type === 'text') {
+ return response.text();
+ }
+ })
+ .catch(e =&gt; {
+ console.log(`There has been a problem with your fetch operation for resource "${url}": ` + e.message);
+ })
+ .finally(() =&gt; {
+ console.log(`fetch attempt for "${url}" finished.`);
+ });
+}</pre>
+
+<p>这会将一条简单的消息记录到控制台,告诉我们每次获取尝试的时间。</p>
+
+<div class="blockIndicator note">
+<p><strong>注意</strong>:finally()允许你在异步代码中编写异步等价物try/ catch / finally。</p>
+</div>
+
+<h2 id="构建自定义promise"> 构建自定义promise</h2>
+
+<p>好消息是,在某种程度上,你已经建立了自己的promise。当你使用<code>.then()</code>块链接多个promise时,或者将它们组合起来创建自定义函数时,你已经在创建自己的基于异步声明的自定义函数。例如,从前面的示例中获取我们的<code>fetchAndDecode()</code>函数。</p>
+
+<p>将不同的基于promise的API组合在一起以创建自定义函数是迄今为止你使用promises进行自定义事务的最常见方式,并展示了基于相同原则的大多数现代API的灵活性和强大功能。然而,还有另一种方式。</p>
+
+<h3 id="使用Promise构造函数">使用Promise()构造函数</h3>
+
+<p>可以使用<code><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise">Promise()</a></code> 构造函数构建自己的promise。当你需要使用现有的旧项目代码、库或框架以及基于现代promise的代码时,这会派上用场。比如,当你遇到没有使用promise的旧式异步API的代码时,你可以用promise来重构这段异步代码。</p>
+
+<p>让我们看一个简单的示例来帮助你入门 —— 这里我们使用promise包装一个<code><a href="/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout">setTimeout()</a></code>调用 - 它会在两秒后运行一个函数,该函数将用字符串“Success!”,解析当前promise(调用链接的<code><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/resolve">resolve()</a></code>),。</p>
+
+<pre class="brush: js notranslate">let timeoutPromise = new Promise((resolve, reject) =&gt; {
+ setTimeout(function(){
+ resolve('Success!');
+ }, 2000);
+});</pre>
+
+<p><code>resolve()</code>和<code>reject()</code>是用来<strong>实现</strong>和<strong>拒绝</strong>新创建的promise的函数。此处,promise<strong>实现</strong>了字符串“Success!”。</p>
+
+<p>因此,当你调用此promise时,可以将<code>.then()</code>块链接到它的末尾,它将传递给<code>.then()</code>块一串“Success!”。在下面的代码中,我们显示出该消息:</p>
+
+<pre class="brush: js notranslate">timeoutPromise
+.then((message) =&gt; {
+ alert(message);
+})</pre>
+
+<p>更简化的版本:</p>
+
+<pre class="brush: js notranslate">timeoutPromise.then(alert);
+</pre>
+
+<p>尝试 <a href="https://mdn.github.io/learning-area/javascript/asynchronous/promises/custom-promise.html">running this live</a> 以查看结果 (可参考 <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/promises/custom-promise.html">source code</a>).</p>
+
+<p>上面的例子不是很灵活 - promise只能<strong>实现</strong>一个字符串,并且它没有指定任何类型的<code><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/reject">reject()</a></code>条件(诚然,<code>setTimeout()</code>实际上没有失败条件,所以对这个简单的例子并不重要)。</p>
+
+<div class="blockIndicator note">
+<p><strong>注意</strong>: 为什么要<code>resolve()</code>,而不是<code>fullfill()</code>?我们现在给你的答案有些复杂。</p>
+</div>
+
+<h3 id="拒绝一个自定义promise">拒绝一个自定义promise</h3>
+
+<p>我们可以创建一个<code><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/reject">reject()</a></code> 方法拒绝promise  - 就像<code>resolve()</code>一样,这需要一个值,但在这种情况下,它是拒绝的原因,即将传递给<code>.catch()</code>的错误块。</p>
+
+<p>让我们扩展前面的例子,使其具有一些<code>reject()</code>条件,并允许在成功时传递不同的消息。</p>
+
+<p>获取<a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/promises/custom-promise.html">previous example</a>副本,并将现有的<code>timeoutPromise()</code>定义替换为:</p>
+
+<pre class="brush: js notranslate">function timeoutPromise(message, interval) {
+ return new Promise((resolve, reject) =&gt; {
+ if (message === '' || typeof message !== 'string') {
+ reject('Message is empty or not a string');
+ } else if (interval &lt; 0 || typeof interval !== 'number') {
+ reject('Interval is negative or not a number');
+ } else {
+ setTimeout(function(){
+ resolve(message);
+ }, interval);
+ }
+ });
+};</pre>
+
+<p>在这里,我们将两个方法传递给一个自定义函数 - 一个用来做某事的消息,以及在做这件事之前要经过的时间间隔。在函数内部,我们返回一个新的<code>Promise</code>对象 - 调用该函数将返回我们想要使用的promise。</p>
+
+<p>在<code>Promise</code>构造函数中,我们在if ... else结构中进行了一些检查:</p>
+
+<ol>
+ <li>首先,我们检查消息是否适合被警告。如果它是一个空字符串或根本不是字符串,我们会使用合适的错误消息拒绝该promise。</li>
+ <li>接下来,我们检查间隔是否是适当的间隔值。如果是负数或不是数字,我们会使用合适的错误消息拒绝promise。</li>
+ <li>最后,如果参数看起来都正常,我们使用<code>setTimeout()</code>在指定的时间间隔过后,使用指定的消息解析promise。</li>
+</ol>
+
+<p>由于<code>timeoutPromise()</code>函数返回一个<code>Promise</code>,我们可以将<code>.then()</code>,<code>.catch()</code>等链接到它上面以利用它的功能。现在让我们使用它 - 将以前的timeoutPromise用法替换为以下值:</p>
+
+<pre class="brush: js notranslate">timeoutPromise('Hello there!', 1000)
+.then(message =&gt; {
+ alert(message);
+})
+.catch(e =&gt; {
+ console.log('Error: ' + e);
+});</pre>
+
+<p>当你按原样保存并运行代码时,一秒钟后你将收到消息提醒。现在尝试将消息设置为空字符串或将间隔设置为负数,例如,你将能够通过相应的错误消息查看被拒绝的promise!你还可以尝试使用已解决的消息执行其他操作,而不仅仅是提醒它。</p>
+
+<div class="blockIndicator note">
+<p><strong>注意</strong>: 你可以在GitHub上找到我们的这个示例版本<a href="https://mdn.github.io/learning-area/javascript/asynchronous/promises/custom-promise2.html">custom-promise2.html</a>(另请参阅<a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/promises/custom-promise2.html">source code</a>)。</p>
+</div>
+
+<h3 id="一个更真实的例子">一个更真实的例子</h3>
+
+<p>上面的例子是故意做得简单,以使概念易于理解,但它并不是实际上完全同步。异步性质基本上是使用<code>setTimeout()</code>伪造的,尽管它仍然表明promises对于创建具有合理的操作流程,良好的错误处理等的自定义函数很有用</p>
+
+<p>我们想邀请你学习的一个例子是<a href="https://github.com/jakearchibald/idb/">Jake Archibald's idb library</a>,它真正地显示了<code>Promise()</code>构造函数的有用异步应用程序。这采用了 <a href="/en-US/docs/Web/API/IndexedDB_API">IndexedDB API</a>,它是一种旧式的基于回调的API,用于在客户端存储和检索数据,并允许你将其与promises一起使用。如果你查看<a href="https://github.com/jakearchibald/idb/blob/master/lib/idb.js">main library file</a>,你将看到我们在上面讨论过的相同类型的技术。以下块将许多IndexedDB方法使用的基本请求模型转换为使用promise:</p>
+
+<pre class="brush: js notranslate">function promisifyRequest(request) {
+ return new Promise(function(resolve, reject) {
+ request.onsuccess = function() {
+ resolve(request.result);
+ };
+
+ request.onerror = function() {
+ reject(request.error);
+ };
+ });
+}</pre>
+
+<p>这可以通过添加一些事件处理程序来实现,这些事件处理程序在适当的时候实现(fullfill)和拒绝(reject)promise。</p>
+
+<ul>
+ <li>当<code><a href="/en-US/docs/Web/API/IDBRequest">request</a></code>的<a href="/en-US/docs/Web/API/IDBRequest/success_event"><code>success</code> event</a>触发时,<code><a href="/en-US/docs/Web/API/IDBRequest/onsuccess">onsuccess</a></code>处理程序将使用请求的<code><a href="/en-US/docs/Web/API/IDBRequest/result">result</a></code>实现(fullfill)promise。</li>
+ <li>当<code><a href="/en-US/docs/Web/API/IDBRequest">request</a></code>的<a href="/en-US/docs/Web/API/IDBRequest/error_event"><code>error</code> event</a>触发时,<code><a href="/en-US/docs/Web/API/IDBRequest/onerror">onerror</a></code>处理程序拒绝带有请求<code><a href="/en-US/docs/Web/API/IDBRequest/error">error</a></code>的promise</li>
+</ul>
+
+<h2 id="总结">总结</h2>
+
+<p>当我们不知道函数的返回值或返回需要多长时间时,Promises是构建异步应用程序的好方法。它们使得在没有深度嵌套回调的情况下更容易表达和推理异步操作序列,并且它们支持类似于同步<code>try ... catch</code>语句的错误处理方式。</p>
+
+<p>Promise适用于所有现代浏览器的最新版本;promise有兼容问题的唯一情况是Opera Mini和IE11及更早版本。</p>
+
+<p>本文中,我们没有涉及的所有promise的功能,只是最有趣和最有用的功能。当你开始了解有关promise的更多信息时,你会遇到更多功能和技巧。</p>
+
+<p>大多数现代Web API都是基于promise的,因此你需要了解promise才能充分利用它们。这些API包括<a href="/en-US/docs/Web/API/WebRTC_API">WebRTC</a>,<a href="/en-US/docs/Web/API/Web_Audio_API">Web Audio API</a>,<a href="/en-US/docs/Web/API/Media_Streams_API">Media Capture and Streams</a>等等。随着时间的推移,Promises将变得越来越重要,因此学习使用和理解它们是学习现代JavaScript的重要一步。</p>
+
+<p><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></p>
+
+<ul>
+ <li><code><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise">Promise()</a></code></li>
+ <li><a href="/en-US/docs/Web/JavaScript/Guide/Using_promises">Using promises</a></li>
+ <li><a href="https://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html">We have a problem with promises</a> by Nolan Lawson</li>
+</ul>
+
+<p>{{PreviousMenuNext("Learn/JavaScript/Asynchronous/Timeouts_and_intervals", "Learn/JavaScript/Asynchronous/Async_await", "Learn/JavaScript/Asynchronous")}}</p>
+
+<h2 id="本章内容"> 本章内容</h2>
+
+<ul>
+ <li><a href="/zh-CN/docs/Learn/JavaScript/Asynchronous/Concepts">异步编程的基本概念</a></li>
+ <li><a href="/zh-CN/docs/Learn/JavaScript/Asynchronous/Introducing">JavaScript异步编程简介</a></li>
+ <li><a href="/zh-CNdocs/Learn/JavaScript/Asynchronous/Timeouts_and_intervals">合作的异步JavaScript:超时和间隔</a></li>
+ <li><a href="/zh-CN/docs/Learn/JavaScript/Asynchronous/Promises">使用Promises实现优雅的异步编程</a></li>
+ <li><a href="/zh-CN/docs/Learn/JavaScript/Asynchronous/Async_await">使用async和await让异步编程更简单</a></li>
+ <li><a href="/zh-CN/docs/Learn/JavaScript/Asynchronous/Choosing_the_right_approach">选择正确的方法</a></li>
+</ul>
diff --git a/files/zh-cn/learn/javascript/异步/概念/index.html b/files/zh-cn/learn/javascript/异步/概念/index.html
new file mode 100644
index 0000000000..6e59cda54b
--- /dev/null
+++ b/files/zh-cn/learn/javascript/异步/概念/index.html
@@ -0,0 +1,162 @@
+---
+title: 通用异步编程概念
+slug: learn/JavaScript/异步/概念
+tags:
+ - JavaScript
+ - Promises
+ - Threads
+ - 学习
+ - 异步
+ - 阻塞
+translation_of: Learn/JavaScript/Asynchronous/Concepts
+---
+<div>{{LearnSidebar}}{{NextMenu("Learn/JavaScript/Asynchronous/Introducing", "Learn/JavaScript/Asynchronous")}}</div>
+
+<p>在本文中,我们将介绍与异步编程相关的一些重要概念,以及它们在浏览器和JavaScript里的体现。在学习本系列的其他文章之前,你应该先理解这些概念。</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">预备条件:</th>
+ <td>拥有基本的计算机知识,对JavaScript原理有一定了解。</td>
+ </tr>
+ <tr>
+ <th scope="row">目标:</th>
+ <td>理解异步编程的基本概念,以及异步编程在浏览器和JavaScript里面的表现。</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="异步">异步?</h2>
+
+<p>通常来说,程序都是顺序执行,同一时刻只会发生一件事。如果一个函数依赖于另一个函数的结果,它只能等待那个函数结束才能继续执行,从用户的角度来说,整个程序才算运行完毕.</p>
+
+<p>Mac 用户有时会经历过这种旋转的彩虹光标(常称为沙滩球),操作系统通过这个光标告诉用户:“现在运行的程序正在等待其他的某一件事情完成,才能继续运行,都这么长的时间了,你一定在担心到底发生了什么事情”。</p>
+
+<p><img alt="multi-colored macos beachball busy spinner" src="https://mdn.mozillademos.org/files/16577/beachball.jpg" style="display: block; margin: 0 auto;"></p>
+
+<p>这是令人沮丧的体验,没有充分利用计算机的计算能力 — 尤其是在计算机普遍都有多核CPU的时代,坐在那里等待毫无意义,你完全可以在另一个处理器内核上干其他的工作,同时计算机完成耗时任务的时候通知你。这样你可以同时完成其他工作,这就是<strong>异步编程</strong>的出发点。你正在使用的编程环境(就web开发而言,编程环境就是web浏览器)负责为你提供异步运行此类任务的API。</p>
+
+<h2 id="产生阻塞的代码">产生阻塞的代码</h2>
+
+<p>异步技术非常有用,特别是在web编程。当浏览器里面的一个web应用进行密集运算还没有把控制权返回给浏览器的时候,整个浏览器就像冻僵了一样,这叫做<strong>阻塞;</strong>这时候浏览器无法继续处理用户的输入并执行其他任务,直到web应用交回处理器的控制。</p>
+
+<p>我们来看一些<strong>阻塞</strong>的例子。</p>
+
+<p>例子: <a href="https://github.com/mdn/learning-area/tree/master/javascript/asynchronous/introducing">simple-sync.html</a>  (<a href="https://mdn.github.io/learning-area/javascript/asynchronous/introducing/simple-sync.html">see it running live</a>), 在按钮上添加了一个事件监听器,当按钮被点击,它就开始运行一个非常耗时的任务(计算1千万个日期,并在console里显示最后一个日期),然后在DOM里面添加一个段落:</p>
+
+<pre class="brush: js">const btn = document.querySelector('button');
+btn.addEventListener('click', () =&gt; {
+ let myDate;
+ for(let i = 0; i &lt; 10000000; i++) {
+ let date = new Date();
+ myDate = date
+ }
+
+ console.log(myDate);
+
+ let pElem = document.createElement('p');
+ pElem.textContent = 'This is a newly-added paragraph.';
+ document.body.appendChild(pElem);
+});</pre>
+
+<p>运行这个例子的时候,打开JavaScript console,然后点击按钮 — 你会注意到,直到日期的运算结束,最后一个日期在console上显示出来,段落才会出现在网页上。代码按照源代码的顺序执行,只有前面的代码结束运行,后面的代码才会执行。</p>
+
+<div class="blockIndicator note">
+<p><strong>Note</strong>: 这个例子不现实:在实际情况中一般不会发生,没有谁会计算1千万次日期,它仅仅提供一个非常直观的体验.</p>
+</div>
+
+<p>第二个例子, <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/introducing/simple-sync-ui-blocking.html">simple-sync-ui-blocking.html</a> (<a href="https://mdn.github.io/learning-area/javascript/asynchronous/introducing/simple-sync-ui-blocking.html">see it live</a>), 我们模拟一个在现实的网页可能遇到的情况:因为渲染UI而阻塞用户的互动,这个例子有2个按钮:</p>
+
+<ul>
+ <li>"Fill canvas" : 点击的时候用1百万个蓝色的圆填满整个{{htmlelement("canvas")}} .</li>
+ <li>"Click me for alert" :点击显示alert 消息.</li>
+</ul>
+
+<pre class="brush: js">function expensiveOperation() {
+ for(let i = 0; i &lt; 1000000; i++) {
+ ctx.fillStyle = 'rgba(0,0,255, 0.2)';
+ ctx.beginPath();
+ ctx.arc(random(0, canvas.width), random(0, canvas.height), 10, degToRad(0), degToRad(360), false);
+ ctx.fill()
+ }
+}
+
+fillBtn.addEventListener('click', expensiveOperation);
+
+alertBtn.addEventListener('click', () =&gt;
+ alert('You clicked me!')
+);</pre>
+
+<p>如果你点击第一个按钮,然后快速点击第二个,会注意到alert消息并没有出现,只有等到圆圈都画完以后,才会出现:因为第一个操作没有完成之前阻塞了第二个操作的运行.</p>
+
+<div class="blockIndicator note">
+<p><strong>Note</strong>: 当然,这个例子也很丑陋,因为我们只是在模拟阻塞效果。但在现实中,这是一个很常见的问题。开发人员们一直在努力缓解它。</p>
+</div>
+
+<p>为什么是这样? 答案是:JavaScript一般来说是单线程的(<strong>single threaded</strong>)<strong>。</strong>接着我们来介绍<strong>线程</strong>的概念。</p>
+
+<h2 id="线程">线程</h2>
+
+<p>一个<strong>线程</strong>是一个基本的处理过程,程序用它来完成任务。每个线程一次只能执行一个任务:</p>
+
+<pre>Task A --&gt; Task B --&gt; Task C</pre>
+
+<p>每个任务顺序执行,只有前面的结束了,后面的才能开始。</p>
+
+<p>正如我们之前所说,现在的计算机大都有多个内核(core),因此可以同时执行多个任务。支持多线程的编程语言可以使用计算机的多个内核,同时完成多个任务:</p>
+
+<pre>Thread 1: Task A --&gt; Task B
+Thread 2: Task C --&gt; Task D</pre>
+
+<h3 id="JavaScript_是单线程的">JavaScript 是单线程的</h3>
+
+<p>JavaScript 传统上是单线程的。即使有多个内核,也只能在单一线程上运行多个任务,此线程称为主线程(<strong>main thread</strong>)。我们上面的例子运行如下:</p>
+
+<pre>Main thread: Render circles to canvas --&gt; Display alert()</pre>
+
+<p>经过一段时间,JavaScript获得了一些工具来帮助解决这种问题。通过 <a href="/en-US/docs/Web/API/Web_Workers_API">Web workers</a> 可以把一些任务交给一个名为worker的单独的线程,这样就可以同时运行多个JavaScript代码块。一般来说,用一个worker来运行一个耗时的任务,主线程就可以处理用户的交互(避免了阻塞)</p>
+
+<pre>Main thread: Task A --&gt; Task C
+Worker thread: Expensive task B</pre>
+
+<p>记住这些,请查看<a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/introducing/simple-sync-worker.html">simple-sync-worker.html</a> (<a href="https://mdn.github.io/learning-area/javascript/asynchronous/introducing/simple-sync-worker.html">see it running live</a>) , 再次打开浏览器的JavaScript 控制台。这个例子重写了前例:在一个单独的worker线程中计算一千万次日期,你再点击按钮,现在浏览器可以在日期计算完成之前显示段落,阻塞消失了。</p>
+
+<h2 id="异步代码">异步代码</h2>
+
+<p>web workers相当有用,但是他们确实也有局限。主要的一个问题是他们不能访问 {{Glossary("DOM")}} — 不能让一个worker直接更新UI。我们不能在worker里面渲染1百万个蓝色圆圈,它基本上只能做算数的苦活。</p>
+
+<p>其次,虽然在worker里面运行的代码不会产生阻塞,但是基本上还是同步的。当一个函数依赖于几个在它之前运行的过程的结果,这就会成为问题。考虑下面的情况:</p>
+
+<pre>Main thread: Task A --&gt; Task B</pre>
+
+<p>在这种情况下,比如说Task A 正在从服务器上获取一个图片之类的资源,Task B 准备在图片上加一个滤镜。如果开始运行Task A 后立即尝试运行Task B,你将会得到一个错误,因为图像还没有获取到。</p>
+
+<pre>Main thread: Task A --&gt; Task B --&gt; |Task D|
+Worker thread: Task C -----------&gt; | |</pre>
+
+<p>在这种情况下,假设Task D 要同时使用 Task B 和Task C的结果,如果我们能保证这两个结果同时提供,程序可能正常运行,但是这不太可能。如果Task D 尝试在其中一个结果尚未可用的情况下就运行,程序就会抛出一个错误。</p>
+
+<p>为了解决这些问题,浏览器允许我们异步运行某些操作。像<a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise">Promises</a> 这样的功能就允许让一些操作运行 (比如:从服务器上获取图片),然后等待直到结果返回,再运行其他的操作:</p>
+
+<pre>Main thread: Task A Task B
+ Promise: |__async operation__|</pre>
+
+<p>由于操作发生在其他地方,因此在处理异步操作的时候,主线程不会被阻塞。</p>
+
+<p><span class="tlid-translation translation" lang="zh-CN"><span title="">我们将在下一篇文章中开始研究如何编写异步代码。</span> <span title="">非常令人兴奋,对吧?</span> <span title="">继续阅读!</span></span></p>
+
+<h2 id="总结">总结</h2>
+
+<p>围绕异步编程领域,现代软件设计正在加速旋转,就为了让程序在一个时间内做更多的事情。当你使用更新更强大的API时,你会发现在更多的情况下,使用异步编程是唯一的途径。以前写异步代码很困难,现在也需要你来适应,但是已经变容易了很多。在余下的部分,我们将进一步探讨异步代码的重要性,以及如何设计代码来防止前面已经提到过的问题。</p>
+
+<h2 id="模块大纲">模块大纲</h2>
+
+<ul>
+ <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Concepts">通用异步编程概念</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Introducing">介绍异步JavaScript</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Timeouts_and_intervals">合作异步JavaScript: Timeouts and intervals</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Promises">使用Promises优雅的异步编程</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Async_await">async and await:让异步编程更容易</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Choosing_the_right_approach">选择正确的方法</a></li>
+</ul>
diff --git a/files/zh-cn/learn/javascript/异步/简介/index.html b/files/zh-cn/learn/javascript/异步/简介/index.html
new file mode 100644
index 0000000000..c218d064ca
--- /dev/null
+++ b/files/zh-cn/learn/javascript/异步/简介/index.html
@@ -0,0 +1,272 @@
+---
+title: 异步JavaScript简介
+slug: learn/JavaScript/异步/简介
+translation_of: Learn/JavaScript/Asynchronous/Introducing
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/JavaScript/异步/概念", "Learn/JavaScript/异步/Timeouts_and_intervals", "Learn/JavaScript/异步")}}</div>
+
+<p class="summary"><span class="tlid-translation translation" lang="zh-CN"><span title="">在本文中,我们简要回顾一下与同步JavaScript相关的问题,首次介绍你将遇到的一些不同的异步技术,并展示如何使用这些技术解决问题。</span></span></p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">预备条件:</th>
+ <td>基本的计算机素养,以及对JavaScript 基础知识的较好的理解。</td>
+ </tr>
+ <tr>
+ <th scope="row">目标:</th>
+ <td>熟悉什么是异步JavaScript,与同步JavaScript 的区别,以及使用场合。</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="同步JavaScript">同步JavaScript</h2>
+
+<p>要理解什么是<strong>{{Glossary("异步")}}</strong> JavaScript ,我们应该从确切理解<strong>{{Glossary("同步")}}</strong> JavaScript 开始。本节回顾我们在上一篇文章中看到的一些信息。</p>
+
+<p>前面学的很多知识基本上都是同步的 — 运行代码,然后浏览器尽快返回结果。先看一个简单的例子 (<a href="https://mdn.github.io/learning-area/javascript/asynchronous/introducing/basic-function.html">运行它</a>, <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/introducing/basic-function.html">这是源码</a>):</p>
+
+<pre class="brush: js notranslate">const btn = document.querySelector('button');
+btn.addEventListener('click', () =&gt; {
+ alert('You clicked me!');
+
+ let pElem = document.createElement('p');
+ pElem.textContent = 'This is a newly-added paragraph.';
+ document.body.appendChild(pElem);
+});
+</pre>
+
+<p>这段代码, 一行一行的顺序执行:</p>
+
+<ol>
+ <li>先取得一个在DOM里面的 {{htmlelement("button")}} 引用。</li>
+ <li>点击按钮的时候,添加一个 <code><a href="/en-US/docs/Web/API/Element/click_event">click</a></code> 事件监听器:
+ <ol>
+ <li><code><a href="/en-US/docs/Web/API/Window/alert">alert()</a></code> 消息出现。</li>
+ <li>一旦alert 结束,创建一个{{htmlelement("p")}} 元素。</li>
+ <li>给它的文本内容赋值。</li>
+ <li>最后,把这个段落放进网页。</li>
+ </ol>
+ </li>
+</ol>
+
+<p>每一个操作在执行的时候,其他任何事情都没有发生 — 网页的渲染暂停. 因为前篇文章提到过 <a href="/en-US/docs/Learn/JavaScript/Asynchronous/Concepts#JavaScript_is_single_threaded">JavaScript is single threaded</a>. 任何时候只能做一件事情, 只有一个主线程,其他的事情都阻塞了,直到前面的操作完成。</p>
+
+<p>所以上面的例子,点击了按钮以后,段落不会创建,直到在alert消息框中点击ok,段落才会出现,你可以自己试试:</p>
+
+<div class="hidden">
+<pre class="brush: html notranslate">&lt;<span class="pl-ent">button</span>&gt;Click me&lt;/<span class="pl-ent">button</span>&gt;</pre>
+</div>
+
+<p>{{EmbedLiveSample('Synchronous_JavaScript', '100%', '70px')}}</p>
+
+<div class="blockIndicator note">
+<p><strong>Note</strong>: 这很重要请记住,<code><a href="/en-US/docs/Web/API/Window/alert">alert()</a></code>在演示阻塞效果的时候非常有用,但是在正式代码里面,它就是一个噩梦。</p>
+</div>
+
+<h2 id="异步JavaScript">异步JavaScript</h2>
+
+<p>就前面提到的种种原因(比如,和阻塞相关)很多网页API特性使用异步代码,特别是从外部的设备上获取资源,譬如,从网络获取文件,访问数据库,从网络摄像头获得视频流,或者向VR头罩广播图像。</p>
+
+<p>为什么使用异步代码这么难?看一个例子,当你从服务器获取一个图像,通常你不可能立马就得到,这需要时间,虽然现在的网络很快。这意味着下面的伪代码可能不能正常工作:</p>
+
+<pre class="brush: js notranslate">var response = fetch('myImage.png');
+var blob = response.blob();
+// display your image blob in the UI somehow</pre>
+
+<p>因为你不知道下载图片会多久,所以第二行代码执行的时候可能报错(可能间歇的,也可能每次)因为图像还没有就绪。取代的方法就是,代码必须等到 <code>response</code> 返回才能继续往下执行。</p>
+
+<p>在JavaScript代码中,你经常会遇到两种异步编程风格:老派callbacks,新派promise。下面就来分别介绍。</p>
+
+<h2 id="异步callbacks">异步callbacks</h2>
+
+<p>异步callbacks 其实就是函数,只不过是作为参数传递给那些在后台执行的其他函数. 当那些后台运行的代码结束,就调用callbacks函数,通知你工作已经完成,或者其他有趣的事情发生了。使用callbacks 有一点老套,在一些老派但经常使用的API里面,你会经常看到这种风格。</p>
+
+<p>举个例子,异步callback 就是{{domxref("EventTarget.addEventListener", "addEventListener()")}}第二个参数(前面的例子):</p>
+
+<pre class="brush: js notranslate">btn.addEventListener('click', () =&gt; {
+ alert('You clicked me!');
+
+ let pElem = document.createElement('p');
+ pElem.textContent = 'This is a newly-added paragraph.';
+ document.body.appendChild(pElem);
+});</pre>
+
+<p>第一个参数是侦听的事件类型,第二个就是事件发生时调用的回调函数。.</p>
+
+<p>当我们把回调函数作为一个参数传递给另一个函数时,仅仅是把回调函数定义作为参数传递过去 — 回调函数并没有立刻执行,回调函数会在包含它的函数的某个地方异步执行,包含函数负责在合适的时候执行回调函数。</p>
+
+<p>你可以自己写一个容易的,包含回调函数的函数。来看另外一个例子,用 <a href="/en-US/docs/Web/API/XMLHttpRequest"><code>XMLHttpRequest</code> API</a> (<a href="https://mdn.github.io/learning-area/javascript/asynchronous/introducing/xhr-async-callback.html">运行它</a>, <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/introducing/xhr-async-callback.html">源代码</a>) 加载资源:</p>
+
+<pre class="brush: js notranslate">function loadAsset(url, type, callback) {
+ let xhr = new XMLHttpRequest();
+ xhr.open('GET', url);
+ xhr.responseType = type;
+
+ xhr.onload = function() {
+ callback(xhr.response);
+ };
+
+ xhr.send();
+}
+
+function displayImage(blob) {
+ let objectURL = URL.createObjectURL(blob);
+
+ let image = document.createElement('img');
+ image.src = objectURL;
+ document.body.appendChild(image);
+}
+
+loadAsset('coffee.jpg', 'blob', displayImage);</pre>
+
+<p>创建 <code>displayImage()</code> 函数,简单的把blob传递给它,生成objectURL,然后再生成一个image元素,把objectURL作为image的源地址,最后显示这张图片。  然后,我们创建 <code>loadAsset()</code> 函数,把URL,type,和回调函数同时都作为参数。函数用 <code>XMLHttpRequest</code> (通常缩写 "XHR") 获取给定URL的资源,在获得资源响应后再把响应作为参数传递给回调函数去处理。 (使用 <code><a href="/en-US/docs/Web/API/XMLHttpRequestEventTarget/onload">onload</a></code> 事件处理) ,有点烧脑,是不是?!</p>
+
+<p>回调函数用途广泛 — 他们不仅仅可以用来控制函数的执行顺序和函数之间的数据传递,还可以根据环境的不同,将数据传递给不同的函数,所以对下载好的资源,你可以采用不同的操作来处理,譬如 <code>processJSON()</code>, <code>displayText()</code>, 等等。</p>
+
+<p>请注意,不是所有的回调函数都是异步的 — 有一些是同步的。一个例子就是使用 {{jsxref("Array.prototype.forEach()")}} 来遍历数组 (<a href="https://mdn.github.io/learning-area/javascript/asynchronous/introducing/foreach.html">运行</a>, <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/introducing/foreach.html">源代码</a>):</p>
+
+<pre class="brush: js notranslate">const gods = ['Apollo', 'Artemis', 'Ares', 'Zeus'];
+
+gods.forEach(function (eachName, index){
+ console.log(index + '. ' + eachName);
+});</pre>
+
+<p>在这个例子中,我们遍历一个希腊神的数组,并在控制台中打印索引和值。<code>forEach()</code> 需要的参数是一个回调函数,回调函数本身带有两个参数,数组元素和索引值。它无需等待任何事情,立即运行。</p>
+
+<h2 id="Promises">Promises</h2>
+
+<p>Promises 是新派的异步代码,现代的web APIs经常用到。 <code><a href="/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch">fetch()</a></code> API就是一个很好的例子, 它基本上就是一个现代版的,更高效的 {{domxref("XMLHttpRequest")}}。看个例子,来自于文章 <a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Fetching_data">Fetching data from the server</a> :</p>
+
+<pre class="brush: js notranslate">fetch('products.json').then(function(response) {
+  return response.json();
+}).then(function(json) {
+  products = json;
+  initialize();
+}).catch(function(err) {
+  console.log('Fetch problem: ' + err.message);
+});</pre>
+
+<div class="blockIndicator note">
+<p><strong>Note</strong>: 在GitHub上有完整的代码 (<a href="https://github.com/mdn/learning-area/blob/master/javascript/apis/fetching-data/can-store-xhr/can-script.js">see the source here</a>, and also <a href="https://mdn.github.io/learning-area/javascript/apis/fetching-data/can-store-xhr/">see it running live</a>)。</p>
+</div>
+
+<p>这里<code>fetch</code><code>()</code> 只需要一个参数— 资源的网络 URL — 返回一个 <a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise">promise</a>. promise 是表示异步操作完成或失败的对象。可以说,它代表了一种中间状态。 本质上,这是浏览器说“我保证尽快给您答复”的方式,因此得名“promise”。</p>
+
+<p>这个概念需要练习来适应;它感觉有点像运行中的{{interwiki("wikipedia", "薛定谔猫")}}。这两种可能的结果都还没有发生,因此fetch操作目前正在等待浏览器试图在将来某个时候完成该操作的结果。然后我们有三个代码块链接到fetch()的末尾:</p>
+
+<ul>
+ <li>两个 <code><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then">then()</a></code> 块。两者都包含一个回调函数,如果前一个操作成功,该函数将运行,并且每个回调都接收前一个成功操作的结果作为输入,因此您可以继续对它执行其他操作。每个 <code>.then()</code>块返回另一个promise,这意味着可以将多个<code>.then()</code>块链接到另一个块上,这样就可以依次执行多个异步操作。</li>
+ <li>如果其中任何一个<code>then()</code>块失败,则在末尾运行<code><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch">catch()</a></code>块——与同步<code><a href="/en-US/docs/Web/JavaScript/Reference/Statements/try...catch">try...catch</a></code>类似,<code><a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch">catch()</a></code>提供了一个错误对象,可用来报告发生的错误类型。但是请注意,同步<code><a href="/en-US/docs/Web/JavaScript/Reference/Statements/try...catch">try...catch</a></code>不能与promise一起工作,尽管它可以与<a href="/en-US/docs/Learn/JavaScript/Asynchronous/Async_await">async/await</a>一起工作,稍后您将了解到这一点。</li>
+</ul>
+
+<div class="blockIndicator note">
+<p><strong>Note</strong>: 在本模块稍后的部分中,你将学习更多关于promise的内容,所以如果你还没有完全理解这些promise,请不要担心。</p>
+</div>
+
+<h3 id="事件队列">事件队列</h3>
+
+<p>像promise这样的异步操作被放入事件队列中,事件队列在主线程完成处理后运行,这样它们就不会阻止后续JavaScript代码的运行。排队操作将尽快完成,然后将结果返回到JavaScript环境。</p>
+
+<h3 id="Promises_对比_callbacks">Promises 对比 callbacks</h3>
+
+<p>promises与旧式callbacks有一些相似之处。它们本质上是一个返回的对象,您可以将回调函数附加到该对象上,而不必将回调作为参数传递给另一个函数。</p>
+
+<p>然而,<code>Promise</code>是专门为异步操作而设计的,与旧式回调相比具有许多优点:</p>
+
+<ul>
+ <li>您可以使用多个then()操作将多个异步操作链接在一起,并将其中一个操作的结果作为输入传递给下一个操作。这种链接方式对回调来说要难得多,会使回调以混乱的“末日金字塔”告终。 (也称为<a href="http://callbackhell.com/">回调地狱</a>)。</li>
+ <li><code>Promise</code>总是严格按照它们放置在事件队列中的顺序调用。</li>
+ <li>错误处理要好得多——所有的错误都由块末尾的一个.catch()块处理,而不是在“金字塔”的每一层单独处理。</li>
+</ul>
+
+<h2 id="异步代码的本质">异步代码的本质</h2>
+
+<p>让我们研究一个示例,它进一步说明了异步代码的本质,展示了当我们不完全了解代码执行顺序以及将异步代码视为同步代码时可能发生的问题。下面的示例与我们之前看到的非常相似( <a href="https://mdn.github.io/learning-area/javascript/asynchronous/introducing/async-sync.html">运行它</a> 和 <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/introducing/async-sync.html">源代码</a>)。一个不同之处在于,我们包含了许多{{domxref("console.log()")}}语句,以展示代码将在其中执行的顺序。</p>
+
+<pre class="brush: js notranslate">console.log ('Starting');
+let image;
+
+fetch('coffee.jpg').then((response) =&gt; {
+ console.log('It worked :)')
+ return response.blob();
+}).then((myBlob) =&gt; {
+ let objectURL = URL.createObjectURL(myBlob);
+ image = document.createElement('img');
+ image.src = objectURL;
+ document.body.appendChild(image);
+}).catch((error) =&gt; {
+ console.log('There has been a problem with your fetch operation: ' + error.message);
+});
+
+console.log ('All done!');</pre>
+
+<p>浏览器将会执行代码,看见第一个<code>console.log()</code> 输出(<code>Starting</code>) ,然后创建<code>image</code> 变量。</p>
+
+<p>然后,它将移动到下一行并开始执行<code>fetch()</code>块,但是,因为<code>fetch()</code>是异步执行的,没有阻塞,所以在<code>promise</code>相关代码之后程序继续执行,从而到达最后的<code>console.log()</code>语句(<code>All done</code>!)并将其输出到控制台。</p>
+
+<p>只有当<code>fetch()</code> 块完成运行返回结果给<code>.then()</code> ,我们才最后看到第二个<code>console.log()</code> 消息 (<code>It worked ;)</code>) . 所以 这些消息 可能以 和你预期不同的顺序出现:</p>
+
+<ul>
+ <li>Starting</li>
+ <li>All done!</li>
+ <li>It worked :)</li>
+</ul>
+
+<p>如果你感到疑惑,考虑下面这个小例子:</p>
+
+<pre class="brush: js notranslate">console.log("registering click handler");
+
+button.addEventListener('click', () =&gt; {
+ console.log("get click");
+});
+
+console.log("all done");</pre>
+
+<p>这在行为上非常相似——第一个和第三个<code>console.log()</code>消息将立即显示,但是第二个消息将被阻塞,直到有人单击鼠标按钮。前面的示例以相同的方式工作,只是在这种情况下,第二个消息在<code>promise</code>链上被阻塞,直到获取资源后再显示在屏幕上,而不是单击。</p>
+
+<p>要查看实际情况,请尝试获取<a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/introducing/async-sync.html">示例</a>的本地副本,并将第三个<code>console.log()</code>调用更改为以下命令:</p>
+
+<pre class="brush: js notranslate">console.log ('All done! ' + image + 'displayed.');</pre>
+
+<p>此时控制台将会报错,而不会显示第三个 <code>console.log</code> 的信息:</p>
+
+<pre class="notranslate"><span class="message-body-wrapper"><span class="message-flex-body"><span class="devtools-monospace message-body">TypeError: image is undefined; can't access its "src" property</span></span></span></pre>
+
+<p>这是因为:浏览器运行第三个<code>console.log()</code>的时候,<code>fetch()</code> 语句块还没有完成,因此<code>image</code>还没有赋值。</p>
+
+<h2 id="主动学习:把代码全部异步化">主动学习:把代码全部异步化</h2>
+
+<p>要修复有问题的<code>fetch()</code>示例并使三个<code>console.log()</code>语句按期望的顺序出现,还可以让第三个<code>console.log()</code>语句异步运行。这可以通过将它移动到另一个<code>then()</code>块中来实现,然后将它链接到第二个<code>then()</code>块的末尾,或者简单地将它移动到第二个<code>then()</code>块中。现在就试试。</p>
+
+<div class="blockIndicator note">
+<p><strong>Note</strong>: 如果你困住了,你可以<a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/introducing/async-sync-fixed.html">在这里找到答案</a> (<a href="https://mdn.github.io/learning-area/javascript/asynchronous/introducing/async-sync-fixed.html">这里运行</a>)。在后面的文章:<a href="/en-US/docs/Learn/JavaScript/Asynchronous/Promises">用Promises优雅的异步编程</a>, 你将会发现更多信息。</p>
+</div>
+
+<h2 id="小结">小结</h2>
+
+<p>在最基本的形式中,JavaScript是一种同步的、阻塞的、单线程的语言,在这种语言中,一次只能执行一个操作。但web浏览器定义了函数和API,允许我们当某些事件发生时不是按照同步方式,而是异步地调用函数(比如,时间的推移,用户通过鼠标的交互,或者获取网络数据)。这意味着您的代码可以同时做几件事情,而不需要停止或阻塞主线程。</p>
+
+<p>异步还是同步执行代码,取决于我们要做什么。</p>
+
+<p>有些时候,我们希望事情能够立即加载并发生。例如,当将一些用户定义的样式应用到一个页面时,您希望这些样式能够尽快被应用。</p>
+
+<p>但是,如果我们正在运行一个需要时间的操作,比如查询数据库并使用结果填充模板,那么最好将该操作从主线程中移开使用异步完成任务。随着时间的推移,您将了解何时选择异步技术比选择同步技术更有意义。</p>
+
+<ul>
+</ul>
+
+<p>{{PreviousMenuNext("Learn/JavaScript/Asynchronous/Concepts", "Learn/JavaScript/Asynchronous/Timeouts_and_intervals", "Learn/JavaScript/Asynchronous")}}</p>
+
+<h2 id="模块大纲">模块大纲</h2>
+
+<ul>
+ <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Concepts">通用异步编程概念</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Introducing">异步JavaScript简介</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Timeouts_and_intervals">合作异步JavaScript: Timeouts and intervals</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Promises">使用Promises优雅地异步编程</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Async_await">使用 async 和 await 使异步编程更容易</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Choosing_the_right_approach">选择正确的方法</a></li>
+</ul>
diff --git a/files/zh-cn/learn/javascript/异步/超时和间隔/index.html b/files/zh-cn/learn/javascript/异步/超时和间隔/index.html
new file mode 100644
index 0000000000..2cf83da82c
--- /dev/null
+++ b/files/zh-cn/learn/javascript/异步/超时和间隔/index.html
@@ -0,0 +1,617 @@
+---
+title: '合作异步JavaScript: 超时和间隔'
+slug: learn/JavaScript/异步/超时和间隔
+translation_of: Learn/JavaScript/Asynchronous/Timeouts_and_intervals
+---
+<div>{{LearnSidebar}}</div>
+
+<div></div>
+
+<div>{{PreviousMenuNext("Learn/JavaScript/Asynchronous/Introducing", "Learn/JavaScript/Asynchronous/Promises", "Learn/JavaScript/Asynchronous")}}</div>
+
+<div></div>
+
+<p class="summary">在这里,我们将讨论传统的JavaScript方法,这些方法可以在一段时间或一段规则间隔(例如,每秒固定的次数)之后,以异步方式运行代码,并讨论它们的用处,以及它们的固有问题。</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">预备条件:</th>
+ <td>基本的计算机知识,对JavaScript基本原理有较好的理解。</td>
+ </tr>
+ <tr>
+ <th scope="row">目标:</th>
+ <td>了解异步循环和间隔及其用途。</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="介绍">介绍</h2>
+
+<p>很长一段时间以来,web平台为JavaScript程序员提供了许多函数,这些函数允许您在一段时间间隔过后异步执行代码,或者重复异步执行代码块,直到您告诉它停止为止。这些都是:</p>
+
+<dl>
+ <dt><code><a href="/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout">setTimeout()</a></code></dt>
+ <dd>在指定的时间后执行一段代码.</dd>
+ <dt><code><a href="/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setInterval">setInterval()</a></code></dt>
+ <dd>以固定的时间间隔,重复运行一段代码.</dd>
+ <dt><code><a href="/en-US/docs/Web/API/window/requestAnimationFrame">requestAnimationFrame()</a></code></dt>
+ <dd>setInterval()的现代版本;在浏览器下一次重新绘制显示之前执行指定的代码块,从而允许动画在适当的帧率下运行,而不管它在什么环境中运行.</dd>
+</dl>
+
+<p>这些函数设置的异步代码实际上在主线程上运行(在其指定的计时器过去之后)。</p>
+
+<p>在 <code>setTimeout()</code> 调用执行之前或 <code>setInterval()</code> 迭代之间可以(并且经常会)运行其他代码。根据这些操作的处理器密集程度,它们可以进一步延迟异步代码,因为任何异步代码仅在主线程可用后才执行(换句话说,当调用栈为空时)。在阅读本文时,您将学到更多关于此问题的信息。</p>
+
+<p>无论如何,这些函数用于在web站点或应用程序上运行不间断的动画和其他后台处理。在下面的部分中,我们将向您展示如何使用它们。</p>
+
+<h2 id="setTimeout">setTimeout()</h2>
+
+<p>正如前述, <code><a href="/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout">setTimeout()</a></code> 在指定的时间后执行一段特定代码. 它需要如下参数:</p>
+
+<ul>
+ <li>要运行的函数,或者函数引用。</li>
+ <li>表示在执行代码之前等待的时间间隔(以毫秒为单位,所以1000等于1秒)的数字。如果指定值为0(或完全省略该值),函数将尽快运行(参阅下面的注释,了解为什么它“尽快”而不是“立即”运行)。稍后详述这样做的原因。</li>
+ <li>更多的参数:在指定函数运行时,希望传递给函数的值。</li>
+</ul>
+
+<div class="blockIndicator note">
+<p><strong>Note:</strong> 指定的时间(或延迟)不能保证在指定的确切时间之后执行,而是最短的延迟执行时间。在主线程上的堆栈为空之前,传递给这些函数的回调将无法运行。</p>
+
+<p>结果,像 <code>setTimeout(fn, 0)</code> 这样的代码将在堆栈为空时立即执行,而不是立即执行。如果执行类似 <code>setTimeout(fn, 0)</code> 之类的代码,之后立即运行从 1 到 100亿 的循环之后,回调将在几秒后执行。 </p>
+</div>
+
+<p>在下面的示例中,浏览器将在执行匿名函数之前等待两秒钟,然后显示alert消息 (<a href="https://mdn.github.io/learning-area/javascript/asynchronous/loops-and-intervals/simple-settimeout.html">see it running live</a>, and <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/loops-and-intervals/simple-settimeout.html">see the source code</a>):</p>
+
+<pre class="brush: js notranslate">let myGreeting = setTimeout(function() {
+ alert('Hello, Mr. Universe!');
+}, 2000)</pre>
+
+<p>我们指定的函数不必是匿名的。我们可以给函数一个名称,甚至可以在其他地方定义它,并将函数引用传递给 <code>setTimeout()</code> 。以下两个版本的代码片段相当于第一个版本:</p>
+
+<pre class="brush: js notranslate">// With a named function
+let myGreeting = setTimeout(function sayHi() {
+ alert('Hello, Mr. Universe!');
+}, 2000)
+
+// With a function defined separately
+function sayHi() {
+ alert('Hello Mr. Universe!');
+}
+
+let myGreeting = setTimeout(sayHi, 2000);</pre>
+
+<p>例如,如果我们有一个函数既需要从超时调用,也需要响应某个事件,那么这将非常有用。此外它也可以帮助保持代码整洁,特别是当超时回调超过几行代码时。</p>
+
+<p><code>setTimeout()</code> 返回一个标志符变量用来引用这个间隔,可以稍后用来取消这个超时任务,下面就会学到 {{anch("Clearing timeouts")}} 。</p>
+
+<h3 id="传递参数给setTimeout">传递参数给setTimeout() </h3>
+
+<p>我们希望传递给<code>setTimeout()</code>中运行的函数的任何参数,都必须作为列表末尾的附加参数传递给它。</p>
+
+<p>例如,我们可以重构之前的函数,这样无论传递给它的人的名字是什么,它都会向它打招呼:</p>
+
+<pre class="brush: js notranslate">function sayHi(who) {
+ alert('Hello ' + who + '!');
+}</pre>
+
+<p>人名可以通过第三个参数传进 <code>setTimeout()</code> :</p>
+
+<pre class="brush: js notranslate">let myGreeting = setTimeout(sayHi, 2000, 'Mr. Universe');</pre>
+
+<h3 id="清除超时">清除超时</h3>
+
+<p>最后,如果创建了 timeout,您可以通过调用<code>clearTimeout()</code>,将<code>setTimeout()</code>调用的标识符作为参数传递给它,从而在超时运行之前取消。要取消上面的超时,你需要这样做:</p>
+
+<pre class="brush: js notranslate">clearTimeout(myGreeting);</pre>
+
+<div class="blockIndicator note">
+<p><strong>注意</strong>: 请参阅 <a href="https://mdn.github.io/learning-area/javascript/asynchronous/loops-and-intervals/greeter-app.html">greeter-app.html</a> 以获得稍微复杂一点的演示,该演示允许您在表单中设置要打招呼的人的姓名,并使用单独的按钮取消问候语(<a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/loops-and-intervals/greeter-app.html">see the source code also</a>)。</p>
+</div>
+
+<h2 id="setInterval">setInterval()</h2>
+
+<p>当我们需要在一段时间之后运行一次代码时,<code>setTimeout()</code>可以很好地工作。但是当我们需要反复运行代码时会发生什么,例如在动画的情况下?</p>
+
+<p>这就是<code>setInterval()</code>的作用所在。这与<code>setTimeout()</code>的工作方式非常相似,只是作为第一个参数传递给它的函数,<strong>重复</strong>执行的时间不少于第二个参数给出的毫秒数,<strong>而不是一次执行</strong>。您还可以将正在执行的函数所需的任何参数作为 <code>setInterval()</code> 调用的后续参数传递。</p>
+
+<p>让我们看一个例子。下面的函数创建一个新的<code>Date()</code>对象,使用<code>toLocaleTimeString()</code>从中提取一个时间字符串,然后在UI中显示它。然后,我们使用<code>setInterval()</code>每秒运行该函数一次,创建一个每秒更新一次的数字时钟的效果。</p>
+
+<p>(<a href="https://mdn.github.io/learning-area/javascript/asynchronous/loops-and-intervals/setinterval-clock.html">see this live</a>, and also <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/loops-and-intervals/setinterval-clock.html">see the source</a>):</p>
+
+<pre class="brush: js notranslate">function displayTime() {
+ let date = new Date();
+ let time = date.toLocaleTimeString();
+ document.getElementById('demo').textContent = time;
+}
+
+const createClock = setInterval(displayTime, 1000);</pre>
+
+<p><code><font face="Arial, x-locale-body, sans-serif"><span style="background-color: #ffffff;">像</span></font>setTimeout()一样</code>, <code>setInterval()</code> 返回一个确定的值,稍后你可以用它来取消间隔任务。</p>
+
+<h3 id="清除intervals">清除intervals</h3>
+
+<p><code>setInterval</code>()永远保持运行任务,除非我们做点什么——我们可能会想阻止这样的任务,否则当浏览器无法完成任何进一步的任务时我们可能得到错误, 或者动画处理已经完成了。我们可以用与停止超时相同的方法来实现这一点——通过将<code>setInterval</code>()调用返回的标识符传递给<code>clearInterval</code>()函数:</p>
+
+<pre class="brush: js notranslate">const myInterval = setInterval(myFunction, 2000);
+
+clearInterval(myInterval);</pre>
+
+<h4 id="主动学习:创建秒表!">主动学习:创建秒表!</h4>
+
+<p>话虽如此,我们还是要给你一个挑战。以我们的<a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/loops-and-intervals/setinterval-clock.html">setInterval-clock.html</a>为例,修改它以创建您自己的简单秒表。</p>
+
+<p>你要像前面一样显示时间,但是在这里,你需要:</p>
+
+<ul>
+ <li>"Start" 按钮开始计时.</li>
+ <li>"Stop" 按钮暂停/停止计时</li>
+ <li>"Reset" 按钮清零.</li>
+ <li>时间显示经过的秒数.</li>
+</ul>
+
+<p>提示:</p>
+
+<ul>
+ <li>您可以随心所欲地构造按钮标记;只需确保使用HTML语法,确保JavaScript获取按钮引用。</li>
+ <li>创建一个变量,从0开始,每秒钟增加1.</li>
+ <li>与我们版本中所做的一样,在不使用<code>Date</code>()对象的情况下创建这个示例更容易一些,但是不那么精确——您不能保证回调会在恰好1000ms之后触发。更准确的方法是运行<code>startTime = Date.now()</code>来获取用户单击start按钮的确切时间戳,然后执行<code>Date.now() - startTime</code>来获取单击<code>start</code>按钮后的毫秒数。</li>
+ <li>您还希望将小时、分钟和秒作为单独的值计算,然后在每次循环迭代之后将它们一起显示在一个字符串中。从第二个计数器,你可以计算出他们.</li>
+ <li>如何计时时分秒? 想一下:
+ <ul>
+ <li>一小时3600秒.</li>
+ <li>分钟应该是:时间减去小时后剩下的除以60.</li>
+ <li>分钟都减掉后,剩下的就是秒钟了.</li>
+ </ul>
+ </li>
+ <li>如果这个数字小于10,最好在显示的值前面加一个0,这样看起来更加像那么回事。</li>
+ <li>暂停的话,要清掉间隔函数。重置的话,显示要清零,要更新显示</li>
+</ul>
+
+<div class="blockIndicator note">
+<p><strong>Note</strong>: 如果您在操作过程有困难,请参考 <a href="https://mdn.github.io/learning-area/javascript/asynchronous/loops-and-intervals/setinterval-stopwatch.html">see it runing live</a> (see the <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/loops-and-intervals/setinterval-stopwatch.html">source code</a> also).</p>
+</div>
+
+<h2 id="关于_setTimeout_和_setInterval_需要注意的几点">关于 setTimeout() 和 setInterval() 需要注意的几点</h2>
+
+<p>当使用 <code>setTimeout()</code> 和 <code>setInterval()</code>的时候,有几点需要额外注意。 现在让我们回顾一下:</p>
+
+<h3 id="递归的timeouts">递归的timeouts</h3>
+
+<p>还有另一种方法可以使用<code>setTimeout()</code>:我们可以递归调用它来重复运行相同的代码,而不是使用<code>setInterval()</code>。</p>
+
+<p>下面的示例使用递归<code>setTimeout()</code>每100毫秒运行传递来的函数:</p>
+
+<pre class="brush: js notranslate">let i = 1;
+
+setTimeout(function run() {
+ console.log(i);
+ i++;
+ setTimeout(run, 100);
+}, 100);</pre>
+
+<p>将上面的示例与下面的示例进行比较 ––这使用 <code>setInterval()</code> 来实现相同的效果:</p>
+
+<pre class="brush: js notranslate">let i = 1;
+
+setInterval(function run() {
+ console.log(i);
+ i++
+}, 100);</pre>
+
+<h4 id="递归setTimeout和setInterval有何不同?">递归setTimeout()和setInterval()有何不同?</h4>
+
+<p>上述代码的两个版本之间的差异是微妙的。</p>
+
+<ul>
+ <li>递归 <code>setTimeout()</code> 保证执行之间的延迟相同,例如在上述情况下为100ms。 代码将运行,然后在它再次运行之前等待100ms,因此无论代码运行多长时间,间隔都是相同的。</li>
+ <li>使用 <code>setInterval()</code> 的示例有些不同。 我们选择的间隔包括执行我们想要运行的代码所花费的时间。假设代码需要40毫秒才能运行 - 然后间隔最终只有60毫秒。</li>
+ <li>当递归使用 <code>setTimeout()</code> 时,每次迭代都可以在运行下一次迭代之前计算不同的延迟。 换句话说,第二个参数的值可以指定在再次运行代码之前等待的不同时间(以毫秒为单位)。</li>
+</ul>
+
+<p>当你的代码有可能比你分配的时间间隔,花费更长时间运行时,最好使用递归的 <code>setTimeout()</code> - 这将使执行之间的时间间隔保持不变,无论代码执行多长时间,你不会得到错误。</p>
+
+<h3 id="立即超时">立即超时</h3>
+
+<p>使用0用作setTimeout()的回调函数会立刻执行,但是在主线程代码运行之后执行。</p>
+
+<p>举个例子,下面的代码(<a href="https://mdn.github.io/learning-area/javascript/asynchronous/loops-and-intervals/zero-settimeout.html">see it live</a>) 输出一个包含警报的"Hello",然后在您点击第一个警报的OK之后立即弹出“world”。</p>
+
+<pre class="brush: js notranslate">setTimeout(function() {
+ alert('World');
+}, 0);
+
+alert('Hello');</pre>
+
+<p><font><font>如果您希望设置一个代码块以便在所有主线程完成运行后立即运行,这将很有用。将其放在异步事件循环中,这样它将随后直接运行。</font></font></p>
+
+<h3 id="使用_clearTimeout_or_clearInterval清除">使用 clearTimeout() or clearInterval()清除</h3>
+
+<p><code>clearTimeout()</code> 和<code>clearInterval()</code> 都使用相同的条目列表进行清除。有趣的是,这意味着你可以使用任一一种方法来清除 <code>setTimeout()</code> 和 <code>setInterval()。</code></p>
+
+<p>但为了保持一致性,你应该使用 <code>clearTimeout()</code> 来清除 <code>setTimeout()</code> 条目,使用 <code>clearInterval()</code> 来清除 <code>setInterval()</code> 条目。 这样有助于避免混乱。</p>
+
+<h2 id="requestAnimationFrame">requestAnimationFrame()</h2>
+
+<p><code><a href="/en-US/docs/Web/API/window/requestAnimationFrame">requestAnimationFrame()</a></code> 是一个专门的循环函数,旨在浏览器中高效运行动画。它基本上是现代版本的<code>setInterval()</code> —— 它在浏览器重新加载显示内容之前执行指定的代码块,从而允许动画以适当的帧速率运行,不管其运行的环境如何。</p>
+
+<p>它是针对<code>setInterval()</code> 遇到的问题创建的,比如 <code>setInterval()</code>并不是针对设备优化的帧率运行,有时会丢帧。还有即使该选项卡不是活动的选项卡或动画滚出页面等问题 。</p>
+
+<p>(在<a href="http://creativejs.com/resources/requestanimationframe/index.html">CreativeJS</a>上了解有关此内容的更多信息).</p>
+
+<div class="blockIndicator note">
+<p><strong>注意:</strong> 你可以在课程中其他地方找到<code>requestAnimationFrame()</code> 的使用范例—参见 <a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Drawing_graphics">Drawing graphics</a>, 和 <a href="/en-US/docs/Learn/JavaScript/Objects/Object_building_practice">Object building practice</a>。</p>
+</div>
+
+<p><font>该方法将重新加载页面之前要调用的回调函数作为参数。</font><font>这是您将看到的常见表达:</font></p>
+
+<pre class="brush: js notranslate">function draw() {
+ // Drawing code goes here
+ requestAnimationFrame(draw);
+}
+
+draw();</pre>
+
+<p>这个想法是要定义一个函数,在其中更新动画 (例如,移动精灵,更新乐谱,刷新数据等),然后调用它来开始这个过程。在函数的末尾,以 <code>requestAnimationFrame()</code> 传递的函数作为参数进行调用,这指示浏览器在下一次显示重新绘制时再次调用该函数。然后这个操作连续运行, 因为<code>requestAnimationFrame()</code> 是递归调用的。</p>
+
+<div class="blockIndicator note">
+<p><strong>注意</strong>: 如果要执行某种简单的常规DOM动画, <a href="/en-US/docs/Web/CSS/CSS_Animations">CSS 动画</a> 可能更快,因为它们是由浏览器的内部代码计算而不是JavaScript直接计算的。但是,如果您正在做一些更复杂的事情,并且涉及到在DOM中不能直接访问的对象(such as <a href="/en-US/docs/Web/API/Canvas_API">2D Canvas API</a> or <a href="/en-US/docs/Web/API/WebGL_API">WebGL</a> objects), <code>requestAnimationFrame()</code> 在大多数情况下是更好的选择。</p>
+</div>
+
+<h3 id="你的动画跑得有多快?">你的动画跑得有多快?</h3>
+
+<p>动画的平滑度直接取决于动画的帧速率,并以每秒帧数(fps)为单位进行测量。这个数字越高,动画看起来就越平滑。</p>
+
+<p>由于大多数屏幕的刷新率为60Hz,因此在使用web浏览器时,可以达到的最快帧速率是每秒60帧(FPS)。然而,更多的帧意味着更多的处理,这通常会导致卡顿和跳跃-也称为丢帧或跳帧。</p>
+
+<p>如果您有一个刷新率为60Hz的显示器,并且希望达到60fps,则大约有16.7毫秒(1000/60)来执行动画代码来渲染每个帧。这提醒我们,我们需要注意每次通过动画循环时要运行的代码量。</p>
+
+<p><code>requestAnimationFrame()</code> 总是试图尽可能接近60帧/秒的值,当然有时这是不可能的如果你有一个非常复杂的动画,你是在一个缓慢的计算机上运行它,你的帧速率将更少。<code>requestAnimationFrame()</code> 会尽其所能利用现有资源提升帧速率。</p>
+
+<h3 id="requestAnimationFrame_与_setInterval_和_setTimeout有什么不同"> requestAnimationFrame() 与 setInterval() 和 setTimeout()有什么不同?</h3>
+
+<p>让我们进一步讨论一下 <code>requestAnimationFrame()</code> 方法与前面介绍的其他方法的区别. 下面让我们看一下代码:</p>
+
+<pre class="brush: js notranslate">function draw() {
+ // Drawing code goes here
+ requestAnimationFrame(draw);
+}
+
+draw();</pre>
+
+<p>现在让我们看看如何使用<code>setInterval()</code>:</p>
+
+<pre class="brush: js notranslate">function draw() {
+ // Drawing code goes here
+}
+
+setInterval(draw, 17);</pre>
+
+<p>如前所述,我们没有为<code>requestAnimationFrame()</code>;指定时间间隔;它只是在当前条件下尽可能快速平稳地运行它。如果动画由于某些原因而处于屏幕外浏览器也不会浪费时间运行它。</p>
+
+<p> 另一方面<code>setInterval()</code>需要指定间隔。我们通过公式1000毫秒/60Hz得出17的最终值,然后将其四舍五入。四舍五入是一个好主意,浏览器可能会尝试运行动画的速度超过60fps,它不会对动画的平滑度有任何影响。如前所述,60Hz是标准刷新率。</p>
+
+<h3 id="包括时间戳">包括时间戳</h3>
+
+<p>传递给 <code>requestAnimationFrame()</code> 函数的实际回调也可以被赋予一个参数(一个时间戳值),表示自 <code>requestAnimationFrame()</code> 开始运行以来的时间。这是很有用的,因为它允许您在特定的时间以恒定的速度运行,而不管您的设备有多快或多慢。您将使用的一般模式如下所示:</p>
+
+<pre class="brush: js notranslate">let startTime = null;
+
+function draw(timestamp) {
+ if(!startTime) {
+ startTime = timestamp;
+ }
+
+ currentTime = timestamp - startTime;
+
+ // Do something based on current time
+
+ requestAnimationFrame(draw);
+}
+
+draw();</pre>
+
+<h3 id="浏览器支持">浏览器支持</h3>
+
+<p> 与<code>setInterval()<font face="Arial, x-locale-body, sans-serif"><span style="background-color: #ffffff;">或</span></font></code><code>setTimeout()</code> 相比最近的浏览器支持<code>requestAnimationFrame()</code></p>
+
+<p>— <code>requestAnimationFrame()</code>.在Internet Explorer 10及更高版本中可用。因此,除非您的代码需要支持旧版本的IE,否则没有什么理由不使用<code>requestAnimationFrame()</code> 。</p>
+
+<h3 id="一个简单的例子">一个简单的例子</h3>
+
+<p>学习上述理论已经足够了,下面让我们仔细研究并构建自己的<code>requestAnimationFrame()</code> 示例。我们将创建一个简单的“旋转器动画”(spinner animation),即当应用程序忙于连接到服务器时可能会显示的那种动画。</p>
+
+<div class="blockIndicator note">
+<p><strong>注意</strong>: 一般来说,像以下例子中如此简单的动画应用CSS动画来实现,这里使用<code>requestAnimationFrame()</code>只是为了帮助解释其用法。<code>requestAnimationFrame()</code>正常应用于如逐帧更新游戏画面这样的复杂动画。</p>
+</div>
+
+<ol>
+ <li>
+ <p>首先, 下载我们的<a href="https://github.com/mdn/learning-area/blob/master/html/introduction-to-html/getting-started/index.html">网页模板</a>。</p>
+ </li>
+ <li>
+ <p>放置一个空的 {{htmlelement("div")}} 元素进入 {{htmlelement("body")}}, 然后在其中加入一个 ↻ 字符.这是一个将循环的字符将在我们的例子中作为我们的旋转器(spinner)。</p>
+ </li>
+ <li>
+ <p>用任何你喜欢的方法应用下述的CSS到HTML模板中。这些在页面上设置了一个红色背景,将<code>&lt;body&gt;</code>的高度设置为100%<code>&lt;html&gt;</code>的高度,并将<code>&lt;div&gt;</code>水平和竖直居中。</p>
+
+ <pre class="brush: html notranslate">html {
+ background-color: white;
+ height: 100%;
+}
+
+body {
+ height: inherit;
+ background-color: red;
+ margin: 0;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+div {
+ display: inline-block;
+ font-size: 10rem;
+}</pre>
+ </li>
+ <li>
+ <p>插入一个 {{htmlelement("script")}}元素在 <code>&lt;/body&gt; </code>标签之上。</p>
+ </li>
+ <li>
+ <p>插入下述的JavaScript在你的 <code>&lt;script&gt; </code>元素中。这里我们存储了一个&lt;div&gt;的引用在一个常量中,设置<code>rotateCount</code>变量为 0, 设置一个未初始化的变量之后将会用作容纳一个<code>requestAnimationFrame()</code> 的调用, 然后设置一个 <code>startTime</code> 变量为 <code>null</code>,它之后将会用作存储 <code>requestAnimationFrame()</code> 的起始时间.。</p>
+
+ <pre class="brush: js notranslate">const spinner = document.querySelector('div');
+let rotateCount = 0;
+let rAF;
+let startTime = null;</pre>
+ </li>
+ <li>
+ <p>在之前的代码下面, 插入一个 <code>draw()</code> 函数将被用作容纳我们的动画代码,并且包含了 <code>时间戳</code> 参数。</p>
+
+ <pre class="brush: js notranslate">function draw(timestamp) {
+
+}</pre>
+ </li>
+ <li>
+ <p>在<code>draw()</code>中, 加入下述的几行。 如果起始时间还没有被赋值的话,将<code>timestamp</code> 传给它(这只将发生在循环中的第一步)。 并赋值给<code>rotateCount</code> ,以旋转 旋转器(spinning)(此处取<code>(当前时间戳 – 起始时间戳) / 3</code>,以免它转得太快):</p>
+
+ <pre class="brush: js notranslate"> if (!startTime) {
+ startTime = timestamp;
+ }
+
+ rotateCount = (timestamp - startTime) / 3;
+</pre>
+ </li>
+ <li>
+ <p>在<code>draw()</code>内我们刚刚添加的代码之后,添加以下代码 — 此处是在检查<code>rotateCount</code> 的值是否超过了<code>359</code> (e.g. <code>360</code>, 一个正圆的度数)。 如果是,与360取模(值除以 <code>360</code> 后剩下的余数),使得圆圈的动画能以合理的低值连续播放。需要注意的是,这样的操作并不是必要的,只是比起类似于“128000度”的值,运行在 0-359 度之间会使你的操作更容易些。</p>
+
+ <pre class="brush: js notranslate">if (rotateCount &gt; 359) {
+ rotateCount %= 360;
+}</pre>
+ </li>
+ <li>
+ <p>接下来,在上一个块下面添加以下行以实际旋转 旋转(spinner)器:</p>
+
+ <pre class="notranslate">spinner.style.transform = `rotate(${rotateCount}deg)`;</pre>
+ </li>
+ <li>
+ <p>在函数<code>draw()</code>内的最下方,插入下面这行代码。这是整个操作中最关键的部分 — 我们将前面定义的变量设置为以<code>draw()</code>函数为参数的活动<code>requestAnimation()</code>调用。 这样动画就开始了,以尽可能接近60 FPS的速率不断地运行<code>draw()</code>函数。</p>
+
+ <pre class="notranslate">rAF = requestAnimationFrame(draw);</pre>
+ </li>
+ <li>
+ <p>在 <code>draw()</code> 函数定义下方,添加对 <code>draw()</code> 函数的调用以启动动画。</p>
+ </li>
+ <li>
+ <pre class="notranslate">draw();</pre>
+ </li>
+</ol>
+
+<div class="blockIndicator note">
+<p><strong>Note</strong>: You can find this <a href="https://mdn.github.io/learning-area/javascript/asynchronous/loops-and-intervals/simple-raf-spinner.html">example live on GitHub</a> (see the <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/loops-and-intervals/simple-raf-spinner.html">source code</a> also).</p>
+</div>
+
+<h3 id="撤销requestAnimationFrame">撤销requestAnimationFrame()</h3>
+
+<p><code>requestAnimationFrame()</code>可用与之对应的<code>cancelAnimationFrame()</code>方法“撤销”(不同于“set…”类方法的“清除”,此处更接近“撤销”之意)。</p>
+
+<p>该方法以<code>requestAnimationFrame()</code>的返回值为参数,此处我们将该返回值存在变量 <code>rAF</code> 中:</p>
+
+<pre class="brush: js notranslate">cancelAnimationFrame(rAF);</pre>
+
+<h3 id="主动学习:_启动和停止旋转器(spinner)">主动学习: 启动和停止旋转器(spinner)</h3>
+
+<p>在这个练习中,我们希望你能对<code>cancelAnimationFrame()</code>方法做一些测试,在之前的示例中添加新内容。你需要在示例中添加一个事件监听器,用于当鼠标在页面任意位置点击时,启动或停止旋转器。 </p>
+
+<p>一些提示:</p>
+
+<ul>
+ <li>包含<code>&lt;body&gt;</code>在内大多数元素都可以添加<code>click</code>事件处理器<font face="Arial, x-locale-body, sans-serif"><span style="background-color: #ffffff;">。为了使可点击区域最大化,直接监听</span></font><code>&lt;body&gt;</code>元素的事件是更有效的,因为事件可以冒泡到其子元素。</li>
+ <li>你可能需要添加一个追踪用变量来检测旋转器是否在旋转。该变量若为真,则清除动画帧;若为假,则重新调用函数。</li>
+</ul>
+
+<div class="blockIndicator note">
+<p><strong>Note</strong>: 先自己尝试一下,如果你确实遇到困难,可以参看我们的<a href="https://mdn.github.io/learning-area/javascript/asynchronous/loops-and-intervals/start-and-stop-spinner.html">在线示例</a>和<a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/loops-and-intervals/start-and-stop-spinner.html">源代码</a>。</p>
+</div>
+
+<h3 id="限制_节流_requestAnimationFrame动画">限制 (节流) requestAnimationFrame()动画</h3>
+
+<p>requestAnimationFrame() 的限制之一是无法选择帧率。在大多数情况下,这不是问题,因为通常希望动画尽可能流畅地运行。但是,当要创建老式的8位类型的动画时,怎么办?</p>
+
+<p>例如,在我们的 <a href="/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Drawing_graphics">Drawing Graphics</a> 文章中的猴子岛行走动画中的一个问题:</p>
+
+<p>{{EmbedGHLiveSample("learning-area/javascript/apis/drawing-graphics/loops_animation/7_canvas_walking_animation.html", '100%', 260)}}</p>
+
+<p>在此示例中,必须为角色在屏幕上的位置及显示的精灵设置动画。精灵动画中只有6帧。如果通过 <code>requestAnimationFrame()</code> 为屏幕上显示的每个帧显示不同的精灵帧,则 Guybrush 的四肢移动太快,动画看起来很荒谬。因此,此示例使用以下代码限制精灵循环的帧率:</p>
+
+<pre class="brush: js notranslate">if (posX % 13 === 0) {
+ if (sprite === 5) {
+ sprite = 0;
+ } else {
+ sprite++;
+ }
+}</pre>
+
+<p>因此,代码每13个动画帧循环一次精灵。</p>
+
+<p>...实际上,大约是每 6.5 帧,因为我们将每帧更新 <code>posX</code> 2个单位值(角色在屏幕上的位置)</p>
+
+<pre class="brush: js notranslate">if(posX &gt; width/2) {
+ newStartPos = -( (width / 2) + 102 );
+ posX = Math.ceil(newStartPos / 13) * 13;
+ console.log(posX);
+} else {
+ posX += 2;
+}</pre>
+
+<p>这是用于计算如何更新每个动画帧中的位置的代码。</p>
+
+<p>用于限制动画的方法将取决于特定代码。例如,在前面的旋转器实例中,可以通过仅在每帧中增加 rotateCount 一个单位(而不是两个单位)来使其运动速度变慢。</p>
+
+<h2 id="主动学习:反应游戏">主动学习:反应游戏</h2>
+
+<p>对于本文的最后部分,将创建一个 2 人反应游戏。游戏将有两个玩家,其中一个使用 <kbd>A</kbd> 键控制游戏,另一个使用 <kbd>L</kbd> 键控制游戏。</p>
+
+<p>按下 <em>开始</em> 按钮后,像前面看到的旋转器那样的将显示 5 到 10 秒之间的随机时间。之后将出现一条消息,提示 “PLAYERS GO!!”。一旦这发生,第一个按下其控制按钮的玩家将赢得比赛。</p>
+
+<p>{{EmbedGHLiveSample("learning-area/javascript/asynchronous/loops-and-intervals/reaction-game.html", '100%', 500)}}</p>
+
+<p>让我们来完成以下工作:</p>
+
+<ol>
+ <li>
+ <p>首先,下载 <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/loops-and-intervals/reaction-game-starter.html">starter file for the app</a> 。其中包含完成的 HTML 结构和 CSS 样式,为我们提供了一个游戏板,其中显示了两个玩家的信息(如上所示),但 spinner 和 结果段落重叠显示,只需要编写 JavaScript 代码。</p>
+ </li>
+ <li>
+ <p>在页面上空的 {{htmlelement("script")}} 元素里,首先添加以下几行代码,这些代码定义了其余代码中需要的一些常量和变量:</p>
+
+ <pre class="brush: js notranslate">const spinner = document.querySelector('.spinner p');
+const spinnerContainer = document.querySelector('.spinner');
+let rotateCount = 0;
+let startTime = null;
+let rAF;
+const btn = document.querySelector('button');
+const result = document.querySelector('.result');</pre>
+
+ <p>这些是:</p>
+
+ <ol>
+ <li>对旋转器的引用,因此可以对其进行动画处理。</li>
+ <li>包含旋转器 {{htmlelement("div")}} 元素的引用。用于显示和隐藏它。</li>
+ <li>旋转计数。这确定了显示在动画每一帧上的旋转器旋转了多少。</li>
+ <li>开始时间为null。当旋转器开始旋转时,它将赋值为 开始时间。</li>
+ <li>一个未初始化的变量,用于之后存储使 旋转器 动画化的  {{domxref("Window.requestAnimationFrame", "requestAnimationFrame()")}}  调用。</li>
+ <li>开始按钮的引用。</li>
+ <li>结果字段的引用。</li>
+ </ol>
+ </li>
+ <li>
+ <p>接下来,在前几行代码下方,添加以下函数。它只接收两个数字并返回一个在两个数字之间的随机数。稍后将需要它来生成随机超时间隔。</p>
+
+ <pre class="brush: js notranslate">function random(min,max) {
+ var num = Math.floor(Math.random()*(max-min)) + min;
+ return num;
+}</pre>
+ </li>
+ <li>
+ <p>接下来添加 <code>draw()</code> 函数以使 旋转器 动画化。这与之前的简单旋转器示例中的版本非常相似:</p>
+
+ <pre class="brush: js notranslate"> function draw(timestamp) {
+ if(!startTime) {
+ startTime = timestamp;
+ }
+
+ let rotateCount = (timestamp - startTime) / 3;
+
+ if(rotateCount &gt; 359) {
+ rotateCount %= 360;
+ }
+
+ spinner.style.transform = 'rotate(' + rotateCount + 'deg)';
+ rAF = requestAnimationFrame(draw);
+ }</pre>
+ </li>
+ <li>
+ <p>现在是时候在页面首次加载时设置应用程序的初始状态了。添加以下两行,它们使用 <code>display: none;</code> 隐藏结果段落和旋转器容器。</p>
+
+ <pre class="brush: js notranslate">result.style.display = 'none';
+spinnerContainer.style.display = 'none';</pre>
+ </li>
+ <li>
+ <p>接下来,定义一个 <code>reset()</code> 函数。该函数在游戏结束后将游戏设置回初始状态以便再次开启游戏。在代码底部添加以下内容:</p>
+
+ <pre class="brush: js notranslate">function reset() {
+ btn.style.display = 'block';
+ result.textContent = '';
+ result.style.display = 'none';
+}</pre>
+ </li>
+ <li>好的,准备充分!现在该使游戏变得可玩了!将以下代码块添加到代码中。 <code>start()</code> 函数调用 <code>draw()</code> 以启动 旋转器,并在UI中显示它,隐藏“开始”按钮,这样您就无法通过同时启动多次来弄乱游戏,并运行一个经过5到10秒的随机间隔后,会运行 <code>setEndgame()</code> 函数的 <code>setTimeout()</code> 。下面的代码块还将一个事件侦听器添加到按钮上,以在单击它时运行 <code>start()</code> 函数。
+ <pre class="notranslate">btn.addEventListener('click', start);
+
+function start() {
+ draw();
+ spinnerContainer.style.display = 'block';
+ btn.style.display = 'none';
+ setTimeout(setEndgame, random(5000,10000));
+}</pre>
+ </li>
+ <li>添加以下方法到代码:</li>
+</ol>
+
+<pre class="brush: js notranslate">function setEndgame() {
+ cancelAnimationFrame(rAF);
+ spinnerContainer.style.display = 'none';
+ result.style.display = 'block';
+ result.textContent = 'PLAYERS GO!!';
+
+ document.addEventListener('keydown', keyHandler);
+
+ function keyHandler(e) {
+ console.log(e.key);
+ if(e.key === 'a') {
+ result.textContent = 'Player 1 won!!';
+ } else if(e.key === 'l') {
+ result.textContent = 'Player 2 won!!';
+ }
+
+ document.removeEventListener('keydown', keyHandler);
+ setTimeout(reset, 5000);
+ };
+}</pre>
+
+<p>逐步执行以下操作:</p>
+
+<ol>
+ <li>首先通过 {{domxref("window.cancelAnimationFrame", "cancelAnimationFrame()")}}  取消 旋转器 动画(清理不必要的流程总是一件好事),隐藏 旋转器 容器。</li>
+ <li>接下来,显示结果段落并将其文本内容设置为“ PLAYERS GO !!”。向玩家发出信号,表示他们现在可以按下按钮来取胜。</li>
+ <li>将 <code><a href="/en-US/docs/Web/API/Document/keydown_event">keydown</a></code> 事件侦听器附加到 document。当按下任何按钮时,<code>keyHandler()</code> 函数将运行。</li>
+ <li>在 <code>keyHandler()</code> 里,在 <code>keyHandler()</code> 内部,代码包括作为参数的事件对象作为参数(用 <code>e</code> 表示)- 其 {{domxref("KeyboardEvent.key", "key")}} 属性包含刚刚按下的键,可以通过这个对象来对特定的操作和特定的按键做出响应。</li>
+ <li>将变量 <code>isOver</code> 设置为 <code>false</code> ,这样我们就可以跟踪是否按下了正确的按键以使玩家1或2获胜。我们不希望游戏在按下错误的键后结束。</li>
+ <li>将 <code>e.key</code> 输出到控制台,这是找出所按的不同键的键值的有用方法。</li>
+ <li>当 <code>e.key</code> 为“ a”时,显示一条消息说玩家1获胜;当 <code>e.key</code> 为“ l”时,显示消息说玩家2获胜。 (注意:这仅适用于小写的a和l - 如果提交了大写的 A 或 L(键加上 <kbd>Shift</kbd>),则将其视为另一个键!)如果按下了其中一个键,请将 <code>isOver</code> 设置为 <code>true</code> 。</li>
+ <li>仅当 <code>isOver</code> 为 <code>true</code> 时,才使用 {{domxref("EventTarget.removeEventListener", "removeEventListener()")}} 删除 <code>keydown</code> 事件侦听器,以便一旦产生获胜的按键,就不再有键盘输入可以弄乱最终游戏结果。您还可以使用 <code>setTimeout()</code> 在5秒钟后调用 <code>reset()</code>-如前所述,此函数将游戏重置为原始状态,以便可以开始新游戏。</li>
+</ol>
+
+<p>就这样-一切都完成了!</p>
+
+<div class="blockIndicator note">
+<p><strong>Note</strong>: 如果卡住了, check out <a href="https://mdn.github.io/learning-area/javascript/asynchronous/loops-and-intervals/reaction-game.html">our version of the reaction game</a> (see the <a href="https://github.com/mdn/learning-area/blob/master/javascript/asynchronous/loops-and-intervals/reaction-game.html">source code</a> also).</p>
+</div>
+
+<h2 id="结论">结论</h2>
+
+<p>就是这样-异步循环和间隔的所有要点在一篇文章中介绍了。您会发现这些方法在许多情况下都很有用,但请注意不要过度使用它们!因为它们仍然在主线程上运行,所以繁重的回调(尤其是那些操纵DOM的回调)会在不注意的情况下降低页面的速度。</p>
+
+<p>{{PreviousMenuNext("Learn/JavaScript/Asynchronous/Introducing", "Learn/JavaScript/Asynchronous/Promises", "Learn/JavaScript/Asynchronous")}}</p>
+
+<h2 id="In_this_module">In this module</h2>
+
+<ul>
+ <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Concepts">General asynchronous programming concepts</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Introducing">Introducing asynchronous JavaScript</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Timeouts_and_intervals">Cooperative asynchronous JavaScript: Timeouts and intervals</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Promises">Graceful asynchronous programming with Promises</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Async_await">Making asynchronous programming easier with async and await</a></li>
+ <li><a href="/en-US/docs/Learn/JavaScript/Asynchronous/Choosing_the_right_approach">Choosing the right approach</a></li>
+</ul>