diff --git a/files/zh-cn/learn/javascript/asynchronous/index.md b/files/zh-cn/learn/javascript/asynchronous/index.md index 181c26514669fb..504bee2a07e74b 100644 --- a/files/zh-cn/learn/javascript/asynchronous/index.md +++ b/files/zh-cn/learn/javascript/asynchronous/index.md @@ -1,5 +1,5 @@ --- -title: 异步JavaScript +title: 异步 JavaScript slug: Learn/JavaScript/Asynchronous tags: - JavaScript @@ -30,7 +30,7 @@ original_slug: learn/JavaScript/异步 ## 预备知识 -异步 JavaScript 是一个相当高级的话题,建议你先完成 [JavaScript 第一步](/zh-CN/docs/Learn/JavaScript/First_steps)和[创建JavaScript代码块](/zh-CN/docs/Learn/JavaScript/Building_blocks) 两个模块的学习后再来学习。 +异步 JavaScript 是一个相当高级的话题,建议你先完成 [JavaScript 第一步](/zh-CN/docs/Learn/JavaScript/First_steps)和[创建 JavaScript 代码块](/zh-CN/docs/Learn/JavaScript/Building_blocks) 两个模块的学习后再来学习。 > **备注:** 如果你工作在一个无权创建自己文件的电脑/平板/其他设备上,你需要在一个在线编程工具上试验(大多数)代码示例,如 [JSBin](https://jsbin.com/) 或者 [Glitch](https://glitch.com). diff --git a/files/zh-cn/learn/javascript/asynchronous/introducing/index.md b/files/zh-cn/learn/javascript/asynchronous/introducing/index.md index 6f21025d8d54ed..d3b6c4d6548472 100644 --- a/files/zh-cn/learn/javascript/asynchronous/introducing/index.md +++ b/files/zh-cn/learn/javascript/asynchronous/introducing/index.md @@ -40,7 +40,7 @@ original_slug: learn/JavaScript/异步/简介 ## 同步编程 -观察下面的代码: +观察下面的代码: ```js const name = 'Miriam'; @@ -49,7 +49,7 @@ console.log(greeting); // "Hello, my name is Miriam!" ``` -这段代码: +这段代码: 1. 声明了一个叫做 `name` 的字符串常量 2. 声明了另一个叫做 `greeting` 的字符串常量(并使用了 `name` 常量的值) @@ -259,7 +259,7 @@ function doOperation() { doOperation(); ``` -现在我们有一个被分成三步的操作,每一步都依赖于上一步。在这个例子中,第一步给输入的数据加1,第二步加2,第三步加3。从输入0开始,最终结果是6(0+1+2+3)。作为同步代码,这很容易理解。但是如果我们用回调来实现这些步骤呢? +现在我们有一个被分成三步的操作,每一步都依赖于上一步。在这个例子中,第一步给输入的数据加 1,第二步加 2,第三步加 3。从输入 0 开始,最终结果是 6(0+1+2+3)。作为同步代码,这很容易理解。但是如果我们用回调来实现这些步骤呢? ```js function doStep1(init, callback) { diff --git a/files/zh-cn/learn/javascript/asynchronous/introducing_workers/index.md b/files/zh-cn/learn/javascript/asynchronous/introducing_workers/index.md index 65cf1b1c0bbc16..d257154ad257f7 100644 --- a/files/zh-cn/learn/javascript/asynchronous/introducing_workers/index.md +++ b/files/zh-cn/learn/javascript/asynchronous/introducing_workers/index.md @@ -143,7 +143,7 @@ textarea { // 在 "generate.js" 中创建一个新的 worker const worker = new Worker('./generate.js'); -// 当用户点击 "Generate primes" 时, 给 worker 发送一条消息。 +// 当用户点击 "Generate primes" 时,给 worker 发送一条消息。 // 消息中的 command 属性是 "generate", 还包含另外一个属性 "quota",即要生成的质数。 document.querySelector('#generate').addEventListener('click', () => { const quota = document.querySelector('#quota').value; @@ -216,7 +216,7 @@ worker 要做的第一件事情就是开始监听来自主脚本的消息。这 `generatePrimes()` 函数与同步版本类似,只不过我们在完成后向主脚本发送一条消息,而不是返回一个值。我们对此使用 {{DOMxRef("dedicatedWorkerGlobalScope.postMessage()", "postMessage()")}} 函数,就像在 worker 中 `addEventListener`是全局函数一样。如我们所见,主脚本正在监听这条消息并且将会在收到消息后更新 DOM。 -> **备注:** 要运行此站点,你必须运行一个本地 web 服务器,因为 file:// URLs 不允许加载 workers。参考我们的[设置一个本地测试服务器](/zh-CN/docs/Learn/Common_questions/set_up_a_local_testing_server)的指导。完成后,你应该可以点击 "Generate primes" 并且使你的主页面保持响应。 +> **备注:** 要运行此站点,你必须运行一个本地 web 服务器,因为 file:// URLs 不允许加载 workers。参考我们的 [设置一个本地测试服务器](/zh-CN/docs/Learn/Common_questions/set_up_a_local_testing_server) 的指导。完成后,你应该可以点击 "Generate primes" 并且使你的主页面保持响应。 > 如果你在创建和运行这个样例的过程中有疑问,你可以在 [https://github.com/mdn/learning-area/blob/main/javascript/asynchronous/workers/finished](https://github.com/mdn/learning-area/blob/main/javascript/asynchronous/workers/finished) 查看完成后的版本,并且在 [https://mdn.github.io/learning-area/javascript/asynchronous/workers/finished](https://mdn.github.io/learning-area/javascript/asynchronous/workers/finished) 进行在线尝试。 ## 其他类型的 worker @@ -232,7 +232,7 @@ worker 要做的第一件事情就是开始监听来自主脚本的消息。这 在本文中,我们介绍了 web workers,它使得 web 应用能够离线加载任务到单独的线程中。主线程和 worker 不直接共享任何变量,但是可以通过发送消息来进行通信,这些消息作为 `message` 事件被对方接受。 -Workers 尽管不能访问主应用程序能访问的所有 API,尤其是不能访问 DOM, 但是可以作为使主应用程序保持响应的一个有效的方式。 +Workers 尽管不能访问主应用程序能访问的所有 API,尤其是不能访问 DOM,但是可以作为使主应用程序保持响应的一个有效的方式。 ## 参见 diff --git a/files/zh-cn/learn/javascript/asynchronous/promises/index.html b/files/zh-cn/learn/javascript/asynchronous/promises/index.html index 345f0c20814987..14ce8c5706e1c2 100644 --- a/files/zh-cn/learn/javascript/asynchronous/promises/index.html +++ b/files/zh-cn/learn/javascript/asynchronous/promises/index.html @@ -8,24 +8,24 @@
Promise 是 JavaScript 语言的一个相对较新的功能,允许你推迟进一步的操作,直到上一个操作完成或响应其失败。这对于设置一系列异步操作以正常工作非常有用。本文向你展示了promises如何工作,如何在Web API中使用它们以及如何编写自己的API
+Promise 是 JavaScript 语言的一个相对较新的功能,允许你推迟进一步的操作,直到上一个操作完成或响应其失败。这对于设置一系列异步操作以正常工作非常有用。本文向你展示了 promises 如何工作,如何在 Web API 中使用它们以及如何编写自己的 API
前提条件: | -基本的计算机素养,具备基础的JavaScript知识 | +前提条件: | +基本的计算机素养,具备基础的 JavaScript 知识 |
---|---|---|---|
目标: | -理解并使用学习如何使用Promises | +目标: | +理解并使用学习如何使用 Promises |
我们在教程的第一篇文章中简要地了解了 Promises,接下来我们将在更深层次理解Promise。
+我们在教程的第一篇文章中简要地了解了 Promises,接下来我们将在更深层次理解 Promise。
本质上,Promise 是一个对象,代表操作的中间状态 —— 正如它的单词含义 '承诺' ,它保证在未来可能返回某种结果。虽然 Promise 并不保证操作在何时完成并返回结果,但是它保证当结果可用时,你的代码能正确处理结果,当结果不可用时,你的代码同样会被执行,来优雅的处理错误。
@@ -35,7 +35,7 @@该按钮的处理程序调用 {{domxref("MediaDevices.getUserMedia", "getUserMedia()")}} 来访问用户的摄像头和麦克风。由于 getUserMedia()
必须确保用户具有使用这些设备的权限,并询问用户要使用哪个麦克风和摄像头(或者是否仅进行语音通话,以及其他可能的选项),因此它会产生阻塞,直到用户做出所有的决定,并且摄像头和麦克风都已启用。此外,用户可能不会立即响应权限请求。所以 getUserMedia()
可能需要很长时间。
由于 getUserMedia()
是在浏览器的主线程进行调用,整个浏览器将会处于阻塞状态直到 getUserMedia()
返回,这是不应该发生的;不使用Promise,浏览器将处于不可用状态直到用户为摄像头和麦克风做出决定。因此 getUserMedia()
返回一个Promise对象,即 {{jsxref("promise")}},一旦 {{domxref("MediaStream")}} 流可用才去解析,而不是等待用户操作、启动选中的设备并直接返回从所选资源创建的 {{domxref("MediaStream")}} 流。
由于 getUserMedia()
是在浏览器的主线程进行调用,整个浏览器将会处于阻塞状态直到 getUserMedia()
返回,这是不应该发生的;不使用 Promise,浏览器将处于不可用状态直到用户为摄像头和麦克风做出决定。因此 getUserMedia()
返回一个 Promise 对象,即 {{jsxref("promise")}},一旦 {{domxref("MediaStream")}} 流可用才去解析,而不是等待用户操作、启动选中的设备并直接返回从所选资源创建的 {{domxref("MediaStream")}} 流。
上述视频聊天应用程序的代码可能像下面这样:
@@ -52,14 +52,14 @@这个函数在开头调用 setStatusMessage()
来更新状态显示信息"Calling...", 表示正在尝试通话。接下来调用 getUserMedia()
,请求具有视频及音频轨的流,一旦获得这个流,就将其显示在"selfViewElem"的video元素中。接下来将这个流的每个轨道添加到表示与另一个用户的连接的 WebRTC,参见{{domxref("RTCPeerConnection")}}。在这之后,状态显示为"Connected"。
这个函数在开头调用 setStatusMessage()
来更新状态显示信息"Calling...", 表示正在尝试通话。接下来调用 getUserMedia()
,请求具有视频及音频轨的流,一旦获得这个流,就将其显示在"selfViewElem"的 video 元素中。接下来将这个流的每个轨道添加到表示与另一个用户的连接的 WebRTC,参见{{domxref("RTCPeerConnection")}}。在这之后,状态显示为"Connected"。
如果getUserMedia()
失败,则catch块运行。这使用setStatusMessage()
更新状态框以指示发生错误。
如果getUserMedia()
失败,则 catch 块运行。这使用setStatusMessage()
更新状态框以指示发生错误。
这里重要的是getUserMedia()
调用几乎立即返回,即使尚未获得相机流。即使handleCallButton()
函数向调用它的代码返回结果,当getUserMedia()
完成工作时,它也会调用你提供的处理程序。只要应用程序不假设流式传输已经开始,它就可以继续运行。
注意: 如果你有兴趣,可以在文章Signaling and video calling中了解有关此高级主题的更多信息。在该示例中使用与此类似的代码,但更完整。
+注意: 如果你有兴趣,可以在文章Signaling and video calling中了解有关此高级主题的更多信息。在该示例中使用与此类似的代码,但更完整。
这很麻烦且难以阅读(通常称为“回调地狱”),需要多次调用failureCallback()
(每个嵌套函数一次),还有其他问题。
Promises使得上面的情况更容易编写,解析和运行。如果我们使用异步promises代表上面的伪代码,我们最终会得到这样的结果:
+Promises 使得上面的情况更容易编写,解析和运行。如果我们使用异步 promises 代表上面的伪代码,我们最终会得到这样的结果:
chooseToppings() .then(function(toppings) { @@ -102,7 +102,7 @@-使用promise改良
}) .catch(failureCallback);
这要好得多 - 更容易看到发生了什么,我们只需要一个.catch()
块来处理所有错误,它不会阻塞主线程(所以我们可以在等待时继续玩视频游戏为了准备好收集披萨),并保证每个操作在运行之前等待先前的操作完成。我们能够以这种方式一个接一个地链接多个异步操作,因为每个.then()
块返回一个新的promise,当.then()
块运行完毕时它会解析。聪明,对吗?
这要好得多 - 更容易看到发生了什么,我们只需要一个.catch()
块来处理所有错误,它不会阻塞主线程(所以我们可以在等待时继续玩视频游戏为了准备好收集披萨),并保证每个操作在运行之前等待先前的操作完成。我们能够以这种方式一个接一个地链接多个异步操作,因为每个.then()
块返回一个新的 promise,当.then()
块运行完毕时它会解析。聪明,对吗?
使用箭头函数,你可以进一步简化代码:
@@ -138,21 +138,21 @@注意: 你可以使用 async/await
语法进行进一步的改进,我们将在下一篇文章中深入讨论。
最基本的,promise与事件监听器类似,但有一些差异:
+最基本的,promise 与事件监听器类似,但有一些差异:
Promises 很重要,因为大多数现代Web API都将它们用于执行潜在冗长任务的函数。要使用现代Web技术,你需要使用promises。在本章的后面我们将看看如何编写自己的promises,但是现在我们将看一些你将在Web API中遇到的简单示例。
+Promises 很重要,因为大多数现代 Web API 都将它们用于执行潜在冗长任务的函数。要使用现代 Web 技术,你需要使用 promises。在本章的后面我们将看看如何编写自己的 promises,但是现在我们将看一些你将在 Web API 中遇到的简单示例。
-在第一个示例中,我们将使用fetch()
方法从Web获取图像,blob()
方法来转换获取响应的原始内容到 Blob 对象,然后在 <img> 元素内显示该blob
。这与我们在 first article of the series中看到的示例非常相似,但是会在构建你自己的基于 promise 的代码时有所不同。
在第一个示例中,我们将使用fetch()
方法从 Web 获取图像,blob()
方法来转换获取响应的原始内容到 Blob 对象,然后在 <img> 元素内显示该blob
。这与我们在 first article of the series中看到的示例非常相似,但是会在构建你自己的基于 promise 的代码时有所不同。
注意: 下列代码无法直接运行(i.e. via a file://
URL)。你需要本地测试服务器,或是 Glitch 和 GitHub pages 这样的在线解决方案。
注意:下列代码无法直接运行 (i.e. via a file://
URL)。你需要本地测试服务器,或是 Glitch 和 GitHub pages 这样的在线解决方案。
首先,下载我们的 simple HTML template和sample image file。
将 {{htmlelement("script")}} 元素添加在HTML {{htmlelement("body")}} 的底部。
+将 {{htmlelement("script")}} 元素添加在 HTML {{htmlelement("body")}} 的底部。
在 {{HTMLElement("script")}} 元素内,添加以下行:
let promise = fetch('coffee.jpg');-
这会调用 fetch()
方法,将图像的URL作为参数从网络中提取。这也可以将options对象作为可选的第二个参数,但我们现在只使用最简单的版本。我们将 fetch()
返回的promise对象存储在一个名为promise的变量中。正如我们之前所说的,这个对象代表了一个最初既不成功也不失败的中间状态 - 这个状态下的promise的官方术语叫作pending。
这会调用 fetch()
方法,将图像的 URL 作为参数从网络中提取。这也可以将 options 对象作为可选的第二个参数,但我们现在只使用最简单的版本。我们将 fetch()
返回的 promise 对象存储在一个名为 promise 的变量中。正如我们之前所说的,这个对象代表了一个最初既不成功也不失败的中间状态 - 这个状态下的 promise 的官方术语叫作pending。
then()
方法。 .then()
块中的回调(称为执行程序)仅在promise调用成功完成时运行并返回{{domxref("Response")}}对象 - 在promise-speak中,当它已被满足时。它将返回的{{domxref("Response")}}对象作为参数传递。then()
方法。 .then()
块中的回调(称为执行程序)仅在 promise 调用成功完成时运行并返回{{domxref("Response")}}对象 - 在 promise-speak 中,当它已被满足时。它将返回的{{domxref("Response")}}对象作为参数传递。注意: .then()
块的工作方式类似于使用AddEventListener()
向对象添加事件侦听器时的方式。它不会在事件发生之前运行(当promise履行时)。最显着的区别是.then()
每次使用时只运行一次,而事件监听器可以多次调用。
注意: .then()
块的工作方式类似于使用AddEventListener()
向对象添加事件侦听器时的方式。它不会在事件发生之前运行(当 promise 履行时)。最显着的区别是.then()
每次使用时只运行一次,而事件监听器可以多次调用。
我们立即对此响应运行blob()
方法以确保响应主体完全下载,并且当它可用时将其转换为我们可以执行某些操作的Blob
对象。返回的结果如下:
好的,我们还需要做点额外的工作。Fetch promises 不会产生 404 或 500错误,只有在产生像网路故障的情况时才会不工作。总的来说,Fetch promises 总是成功运行,即使response.ok 属性是 false
。为了产生404错误,我们需要判断 response.ok
,如果是 false
,抛出错误,否则返回 blob。就像下面的代码这样做。
好的,我们还需要做点额外的工作。Fetch promises 不会产生 404 或 500 错误,只有在产生像网路故障的情况时才会不工作。总的来说,Fetch promises 总是成功运行,即使response.ok 属性是 false
。为了产生 404 错误,我们需要判断 response.ok
,如果是 false
,抛出错误,否则返回 blob。就像下面的代码这样做。
let promise2 = promise.then(response => {
if (!response.ok) {
@@ -197,7 +197,7 @@ 解释promise的
}
});
-5. 每次调用.then()
都会创建一个新的promise。这非常有用;因为blob()
方法也返回一个promise,我们可以通过调用第二个promise的.then()
方法来处理它在履行时返回的Blob
对象。因为我们想要对blob
执行一些更复杂的操作,而不仅仅运行单个方法并返回结果,这次我们需要将函数体包装成花括号(否则会抛出错误)。
5. 每次调用.then()
都会创建一个新的 promise。这非常有用;因为blob()
方法也返回一个 promise,我们可以通过调用第二个 promise 的.then()
方法来处理它在履行时返回的Blob
对象。因为我们想要对blob
执行一些更复杂的操作,而不仅仅运行单个方法并返回结果,这次我们需要将函数体包装成花括号(否则会抛出错误)。
将以下内容添加到代码的末尾:
@@ -210,25 +210,25 @@这里我们运行{{domxref("URL.createObjectURL()")}}方法,将其作为Blob
在第二个promise实现时返回的参数传递。这将返回指向该对象的URL。然后我们创建一个{{htmlelement("img")}}元素,将其src
属性设置为等于对象URL并将其附加到DOM,这样图像就会显示在页面上!
这里我们运行{{domxref("URL.createObjectURL()")}}方法,将其作为Blob
在第二个 promise 实现时返回的参数传递。这将返回指向该对象的 URL。然后我们创建一个{{htmlelement("img")}}元素,将其src
属性设置为等于对象 URL 并将其附加到 DOM,这样图像就会显示在页面上!
如果你保存刚刚创建的HTML文件并将其加载到浏览器中,你将看到图像按预期显示在页面中。干得好!
+如果你保存刚刚创建的 HTML 文件并将其加载到浏览器中,你将看到图像按预期显示在页面中。干得好!
注意: 你可能会注意到这些例子有点做作。你可以取消整个fetch()
和blob()
链,只需创建一个<img>元素并将其src
属性值设置为图像文件的URL,即coffee.jpg
。然而,我们选择了这个例子,因为它以简单的方式展示了promise,而不是真实世界的适当性。
注意: 你可能会注意到这些例子有点做作。你可以取消整个fetch()
和blob()
链,只需创建一个<img>元素并将其src
属性值设置为图像文件的 URL,即coffee.jpg
。然而,我们选择了这个例子,因为它以简单的方式展示了 promise,而不是真实世界的适当性。
缺少一些东西 - 如果其中一个promise失败(rejects,in promise-speak),目前没有什么可以明确地处理错误。我们可以通过运行前一个promise的 .catch()
方法来添加错误处理。立即添加:
缺少一些东西 - 如果其中一个 promise 失败(rejects,in promise-speak),目前没有什么可以明确地处理错误。我们可以通过运行前一个 promise 的 .catch()
方法来添加错误处理。立即添加:
let errorCase = promise3.catch(e => {
console.log('There has been a problem with your fetch operation: ' + e.message);
});
-要查看此操作,请尝试拼错图像的URL并重新加载页面。该错误将在浏览器的开发人员工具的控制台中报告。
+要查看此操作,请尝试拼错图像的 URL 并重新加载页面。该错误将在浏览器的开发人员工具的控制台中报告。
-如果你根本不操心包括的 .catch()
块,这并没有做太多的事情,但考虑一下(指.catch()块) ––这会使我们可以完全控制错误处理方式。在真实的应用程序中,你的.catch()
块可以重试获取图像,或显示默认图像,或提示用户提供不同的图像URL等等。
如果你根本不操心包括的 .catch()
块,这并没有做太多的事情,但考虑一下(指.catch() 块) ––这会使我们可以完全控制错误处理方式。在真实的应用程序中,你的.catch()
块可以重试获取图像,或显示默认图像,或提示用户提供不同的图像 URL 等等。
注意: 你可以参考 our version of the example live (参阅 source code ).
@@ -236,7 +236,7 @@这是写出来的一种非常简便的方式;我们故意这样做是为了帮助你清楚地了解发生了什么。如本文前面所示,你可以将.then()
块(以及.catch()
块)链接在一起。上面的代码也可以这样写(参阅GitHub上的simple-fetch-chained.html ):
这是写出来的一种非常简便的方式;我们故意这样做是为了帮助你清楚地了解发生了什么。如本文前面所示,你可以将.then()
块(以及.catch()
块)链接在一起。上面的代码也可以这样写 (参阅 GitHub 上的simple-fetch-chained.html ):
fetch('coffee.jpg')
.then(response => {
@@ -256,31 +256,31 @@ 将代码块链在一起
console.log('There has been a problem with your fetch operation: ' + e.message);
});
-请记住,履行的promise所返回的值将成为传递给下一个 .then()
块的executor函数的参数。
请记住,履行的 promise 所返回的值将成为传递给下一个 .then()
块的 executor 函数的参数。
注意: promise中的.then()/catch()
块基本上是同步代码中try...catch
块的异步等价物。请记住,同步try ... catch
在异步代码中不起作用。
注意: promise 中的.then()/catch()
块基本上是同步代码中try...catch
块的异步等价物。请记住,同步try ... catch
在异步代码中不起作用。
在上面的部分中有很多要介绍的内容,所以让我们快速回过头来给你一个简短的指南,你可以将它添加到书签中,以便将来更新你的记忆。你还应该再次阅读上述部分,以确保这些概念坚持下去。
.then()
块链接到promise链的末尾来访问该值。 .then()
块中的执行程序函数将包含promise的返回值。.catch()
块链接到promise链的末尾来访问此原因。.then()
块链接到 promise 链的末尾来访问该值。 .then()
块中的执行程序函数将包含 promise 的返回值。.catch()
块链接到 promise 链的末尾来访问此原因。上面的例子向我们展示了使用promises的一些真正的基础知识。现在让我们看一些更高级的功能。首先,链接进程一个接一个地发生都很好,但是如果你想在一大堆Promises全部完成之后运行一些代码呢?
+上面的例子向我们展示了使用 promises 的一些真正的基础知识。现在让我们看一些更高级的功能。首先,链接进程一个接一个地发生都很好,但是如果你想在一大堆 Promises 全部完成之后运行一些代码呢?
-你可以使用巧妙命名的Promise.all()静态方法完成此操作。这将一个promises数组作为输入参数,并返回一个新的Promise对象,只有当数组中的所有promise都满足时才会满足。它看起来像这样:
+你可以使用巧妙命名的Promise.all()静态方法完成此操作。这将一个 promises 数组作为输入参数,并返回一个新的 Promise 对象,只有当数组中的所有 promise 都满足时才会满足。它看起来像这样:
Promise.all([a, b, c]).then(values => { ... @@ -288,7 +288,7 @@运行代码以响应多
如果它们都实现,那么数组中的结果将作为参数传递给
-.then()
块中的执行器函数。如果传递给Promise.all()
的任何一个 promise 拒绝,整个块将拒绝。这非常有用。想象一下,我们正在获取信息以在内容上动态填充页面上的UI功能。在许多情况下,接收所有数据然后才显示完整内容,而不是显示部分信息。
+这非常有用。想象一下,我们正在获取信息以在内容上动态填充页面上的 UI 功能。在许多情况下,接收所有数据然后才显示完整内容,而不是显示部分信息。
让我们构建另一个示例来展示这一点。
@@ -297,10 +297,10 @@运行代码以响应多
下载我们页面模板(page template)的新副本,并再次在结束</ body>标记之前放置一个<script>元素。
下载我们的源文件(coffee.jpg, tea.jpg和 description.txt),或者随意替换成你自己的文件。
+下载我们的源文件 (coffee.jpg, tea.jpg和 description.txt),或者随意替换成你自己的文件。
在我们的脚本中,我们将首先定义一个函数,该函数返回我们要发送给Promise.all()
的promise。如果我们只想运行Promise.all()
块以响应三个fetch()
操作完成,这将很容易。我们可以这样做:
在我们的脚本中,我们将首先定义一个函数,该函数返回我们要发送给Promise.all()
的 promise。如果我们只想运行Promise.all()
块以响应三个fetch()
操作完成,这将很容易。我们可以这样做:
let a = fetch(url1); let b = fetch(url2); @@ -310,7 +310,7 @@-运行代码以响应多 ... });
当promise是fullfilled时,传递到履行处理程序的values
将包含三个Response对象,每个对象用于已完成的每个fetch()
操作。
当 promise 是fullfilled时,传递到履行处理程序的values
将包含三个 Response 对象,每个对象用于已完成的每个fetch()
操作。
但是,我们不想这样做。我们的代码不关心fetch()
操作何时完成。相反,我们想要的是加载的数据。这意味着当我们返回代表图像的可用blob
和可用的文本字符串时,我们想要运行Promise.all()
块。我们可以编写一个执行此操作的函数;在<script>
元素中添加以下内容:
这看起来有点复杂,所以让我们一步一步地完成它:
fetch()
函数来获取指定URL处的资源,然后将其链接到另一个 promise ,它解码(或“read”)响应body。这是前一个示例中的blob()
方法。fetch()
函数来获取指定 URL 处的资源,然后将其链接到另一个 promise,它解码(或“read”)响应 body。这是前一个示例中的blob()
方法。if ... else if
语句,根据我们需要解码的文件类型返回不同的promise(在这种情况下,我们可以选择blob
或text
,而且很容易扩展这个以处理其他类型)。fetch()
调用之前添加了return
关键字。它的作用是运行整个链,然后运行最终结果(即blob()
或text()
返回的promise作为我们刚刚定义的函数的返回值)。实际上,return
语句将结果从链返回到顶部。if ... else if
语句,根据我们需要解码的文件类型返回不同的 promise(在这种情况下,我们可以选择blob
或text
,而且很容易扩展这个以处理其他类型)。fetch()
调用之前添加了return
关键字。它的作用是运行整个链,然后运行最终结果(即blob()
或text()
返回的 promise 作为我们刚刚定义的函数的返回值)。实际上,return
语句将结果从链返回到顶部。在块结束时,我们链接一个.catch()
调用,以处理任何可能出现在数组中传递给.all()
的任何promise的错误情况。如果任何promise被拒绝,catch
块将告诉你哪个promise有问题。 .all()
块(见下文)仍然可以实现,但不会显示有问题的资源。如果你想要.all
拒绝,你必须将.catch()
块链接到那里的末尾。
在块结束时,我们链接一个.catch()
调用,以处理任何可能出现在数组中传递给.all()
的任何 promise 的错误情况。如果任何 promise 被拒绝,catch
块将告诉你哪个 promise 有问题。 .all()
块(见下文)仍然可以实现,但不会显示有问题的资源。如果你想要.all
拒绝,你必须将.catch()
块链接到那里的末尾。
函数体内部的代码是async(异步)和基于promise的,因此实际上整个函数就像一个promise ––方便啊!
+函数体内部的代码是 async(异步)和基于 promise 的,因此实际上整个函数就像一个 promise ––方便啊!
接下来,我们调用我们的函数三次以开始获取和解码图像和文本的过程,并将每个返回的promises存储在变量中。在以前的代码下面添加以下内容:
+接下来,我们调用我们的函数三次以开始获取和解码图像和文本的过程,并将每个返回的 promises 存储在变量中。在以前的代码下面添加以下内容:
let coffee = fetchAndDecode('coffee.jpg', 'blob'); let tea = fetchAndDecode('tea.jpg', 'blob'); let description = fetchAndDecode('description.txt', 'text');
接下来,我们将定义一个Promise.all()
块,仅当上面存储的所有三个promise都已成功完成时才运行一些代码。首先,在.then()
调用中添加一个带有空执行程序的块,如下所示:
接下来,我们将定义一个Promise.all()
块,仅当上面存储的所有三个 promise 都已成功完成时才运行一些代码。首先,在.then()
调用中添加一个带有空执行程序的块,如下所示:
Promise.all([coffee, tea, description]).then(values => { });-
你可以看到它需要一个包含promises作为参数的数组。执行者只有在所有三个promises的状态成为resolved时才会运行;当发生这种情况时,它将被传入一个数组,其中包含来自各个promise(即解码的响应主体)的结果,类似于 [coffee-results, tea-results, description-results].
+你可以看到它需要一个包含 promises 作为参数的数组。执行者只有在所有三个 promises 的状态成为resolved时才会运行;当发生这种情况时,它将被传入一个数组,其中包含来自各个 promise(即解码的响应主体)的结果,类似于 [coffee-results, tea-results, description-results].
最后,在执行程序中添加以下内容。这里我们使用一些相当简单的同步代码将结果存储在单独的变量中(从blob创建对象URL),然后在页面上显示图像和文本。
+最后,在执行程序中添加以下内容。这里我们使用一些相当简单的同步代码将结果存储在单独的变量中(从 blob 创建对象 URL),然后在页面上显示图像和文本。
console.log(values); // Store each value returned from the promises in separate variables; create object URLs from the blobs @@ -384,7 +384,7 @@运行代码以响应多 document.body.appendChild(para);
保存并刷新,你应该看到所有UI组件都已加载,尽管不是特别有吸引力!
+保存并刷新,你应该看到所有 UI 组件都已加载,尽管不是特别有吸引力!
注意: 如果你正在改进这段代码,你可能想要遍历一个项目列表来显示,获取和解码每个项目,然后循环遍历Promise.all()
内部的结果,运行一个不同的函数来显示每个项目取决于什么代码的类型是。这将使它适用于任何数量的项目,而不仅仅是三个。
此外,你可以确定要获取的文件类型,而无需显式类型属性。例如,你可以使用response.headers.get("content-type")
检查响应的{{HTTPHeader("Content-Type")}} HTTP标头,然后做出相应的反应。
此外,你可以确定要获取的文件类型,而无需显式类型属性。例如,你可以使用response.headers.get("content-type")
检查响应的{{HTTPHeader("Content-Type")}} HTTP 标头,然后做出相应的反应。
在promise完成后,你可能希望运行最后一段代码,无论它是否已实现(fullfilled)或被拒绝(rejected)。此前,你必须在.then()
和.catch()
回调中包含相同的代码,例如:
在 promise 完成后,你可能希望运行最后一段代码,无论它是否已实现(fullfilled)或被拒绝(rejected)。此前,你必须在.then()
和.catch()
回调中包含相同的代码,例如:
myPromise .then(response => { @@ -414,7 +414,7 @@-在promise fullf runFinalCode(); });
在现代浏览器中,.finally()
方法可用,它可以链接到常规promise链的末尾,允许你减少代码重复并更优雅地执行操作。上面的代码现在可以写成如下:
在现代浏览器中,.finally()
方法可用,它可以链接到常规 promise 链的末尾,允许你减少代码重复并更优雅地执行操作。上面的代码现在可以写成如下:
myPromise .then(response => { @@ -452,20 +452,20 @@在promise fullf
这会将一条简单的消息记录到控制台,告诉我们每次获取尝试的时间。
--注意:finally()允许你在异步代码中编写异步等价物try/ catch / finally。
+注意:finally() 允许你在异步代码中编写异步等价物 try/ catch / finally。
构建自定义promise
+构建自定义 promise
-好消息是,在某种程度上,你已经建立了自己的promise。当你使用
+.then()
块链接多个promise时,或者将它们组合起来创建自定义函数时,你已经在创建自己的基于异步声明的自定义函数。例如,从前面的示例中获取我们的fetchAndDecode()
函数。好消息是,在某种程度上,你已经建立了自己的 promise。当你使用
-.then()
块链接多个 promise 时,或者将它们组合起来创建自定义函数时,你已经在创建自己的基于异步声明的自定义函数。例如,从前面的示例中获取我们的fetchAndDecode()
函数。将不同的基于promise的API组合在一起以创建自定义函数是迄今为止你使用promises进行自定义事务的最常见方式,并展示了基于相同原则的大多数现代API的灵活性和强大功能。然而,还有另一种方式。
+将不同的基于 promise 的 API 组合在一起以创建自定义函数是迄今为止你使用 promises 进行自定义事务的最常见方式,并展示了基于相同原则的大多数现代 API 的灵活性和强大功能。然而,还有另一种方式。
-使用Promise()构造函数
+使用 Promise() 构造函数
-可以使用
+Promise()
构造函数构建自己的promise。当你需要使用现有的旧项目代码、库或框架以及基于现代promise的代码时,这会派上用场。比如,当你遇到没有使用promise的旧式异步API的代码时,你可以用promise来重构这段异步代码。可以使用
-Promise()
构造函数构建自己的 promise。当你需要使用现有的旧项目代码、库或框架以及基于现代 promise 的代码时,这会派上用场。比如,当你遇到没有使用 promise 的旧式异步 API 的代码时,你可以用 promise 来重构这段异步代码。让我们看一个简单的示例来帮助你入门 —— 这里我们用 promise 包装一了个
+setTimeout(),
它会在两秒后运行一个函数,该函数将用字符串“Success!”,解析当前promise(调用链接的resolve()
)。让我们看一个简单的示例来帮助你入门 —— 这里我们用 promise 包装一了个
setTimeout(),
它会在两秒后运行一个函数,该函数将用字符串“Success!”,解析当前 promise(调用链接的resolve()
)。let timeoutPromise = new Promise((resolve, reject) => { setTimeout(function(){ @@ -473,9 +473,9 @@-使用Promise()构造函数
}, 2000); });+
resolve()
和reject()
是用来实现和拒绝新创建的promise的函数。此处,promise 成功运行通过显示字符串“Success!”。-
resolve()
和reject()
是用来实现和拒绝新创建的 promise 的函数。此处,promise 成功运行通过显示字符串“Success!”。因此,当你调用此promise时,可以将
+.then()
块链接到它的末尾,它将传递给.then()
块一串“Success!”。在下面的代码中,我们显示出该消息:因此,当你调用此 promise 时,可以将
.then()
块链接到它的末尾,它将传递给.then()
块一串“Success!”。在下面的代码中,我们显示出该消息:timeoutPromise .then((message) => { @@ -489,15 +489,15 @@-使用Promise()构造函数
尝试 running this live 以查看结果 (可参考 source code).
-上面的例子不是很灵活 - promise只能实现一个字符串,并且它没有指定任何类型的
+reject()
条件(诚然,setTimeout()
实际上没有失败条件,所以对这个简单的例子并不重要)。上面的例子不是很灵活 - promise 只能实现一个字符串,并且它没有指定任何类型的
reject()
条件(诚然,setTimeout()
实际上没有失败条件,所以对这个简单的例子并不重要)。-注意: 为什么要
resolve()
,而不是fullfill()
?我们现在给你的答案有些复杂。拒绝一个自定义promise
+拒绝一个自定义 promise
-我们可以创建一个
+reject()
方法拒绝promise - 就像resolve()
一样,这需要一个值,但在这种情况下,它是拒绝的原因,即将传递给.catch()
的错误块。我们可以创建一个
reject()
方法拒绝 promise - 就像resolve()
一样,这需要一个值,但在这种情况下,它是拒绝的原因,即将传递给.catch()
的错误块。让我们扩展前面的例子,使其具有一些
@@ -517,17 +517,17 @@reject()
条件,并允许在成功时传递不同的消息。拒绝一个自定义promise
}); };在这里,我们将两个方法传递给一个自定义函数 - 一个用来做某事的消息,以及在做这件事之前要经过的时间间隔。在函数内部,我们返回一个新的
+Promise
对象 - 调用该函数将返回我们想要使用的promise。在这里,我们将两个方法传递给一个自定义函数 - 一个用来做某事的消息,以及在做这件事之前要经过的时间间隔。在函数内部,我们返回一个新的
-Promise
对象 - 调用该函数将返回我们想要使用的 promise。在
+Promise
构造函数中,我们在if ... else结构中进行了一些检查:在
Promise
构造函数中,我们在 if ... else 结构中进行了一些检查:-
-- 首先,我们检查消息是否适合被警告。如果它是一个空字符串或根本不是字符串,我们会使用合适的错误消息拒绝该promise。
-- 接下来,我们检查间隔是否是适当的间隔值。如果是负数或不是数字,我们会使用合适的错误消息拒绝promise。
-- 最后,如果参数看起来都正常,我们使用
+setTimeout()
在指定的时间间隔过后,使用指定的消息解析promise。- 首先,我们检查消息是否适合被警告。如果它是一个空字符串或根本不是字符串,我们会使用合适的错误消息拒绝该 promise。
+- 接下来,我们检查间隔是否是适当的间隔值。如果是负数或不是数字,我们会使用合适的错误消息拒绝 promise。
+- 最后,如果参数看起来都正常,我们使用
setTimeout()
在指定的时间间隔过后,使用指定的消息解析 promise。由于
+timeoutPromise()
函数返回一个Promise
,我们可以将.then()
,.catch()
等链接到它上面以利用它的功能。现在让我们使用它 - 将以前的timeoutPromise用法替换为以下值:由于
timeoutPromise()
函数返回一个Promise
,我们可以将.then()
,.catch()
等链接到它上面以利用它的功能。现在让我们使用它 - 将以前的 timeoutPromise 用法替换为以下值:timeoutPromise('Hello there!', 1000) .then(message => { @@ -537,17 +537,17 @@-拒绝一个自定义promise
console.log('Error: ' + e); });当你按原样保存并运行代码时,一秒钟后你将收到消息提醒。现在尝试将消息设置为空字符串或将间隔设置为负数,例如,你将能够通过相应的错误消息查看被拒绝的promise!你还可以尝试使用已解决的消息执行其他操作,而不仅仅是提醒它。
+当你按原样保存并运行代码时,一秒钟后你将收到消息提醒。现在尝试将消息设置为空字符串或将间隔设置为负数,例如,你将能够通过相应的错误消息查看被拒绝的 promise!你还可以尝试使用已解决的消息执行其他操作,而不仅仅是提醒它。
-注意: 你可以在GitHub上找到我们的这个示例版本custom-promise2.html(另请参阅source code)。
+注意: 你可以在 GitHub 上找到我们的这个示例版本custom-promise2.html(另请参阅source code)。
一个更真实的例子
-上面的例子是故意做得简单,以使概念易于理解,但它并不是实际上完全异步。异步性质基本上是使用
+setTimeout()
伪造的,尽管它仍然表明promises对于创建具有合理的操作流程,良好的错误处理等的自定义函数很有用上面的例子是故意做得简单,以使概念易于理解,但它并不是实际上完全异步。异步性质基本上是使用
-setTimeout()
伪造的,尽管它仍然表明 promises 对于创建具有合理的操作流程,良好的错误处理等的自定义函数很有用我们想邀请你学习的一个例子是Jake Archibald's idb library,它真正地显示了
+Promise()
构造函数的有用异步应用程序。这采用了 IndexedDB API,它是一种旧式的基于回调的API,用于在客户端存储和检索数据,并允许你将其与promises一起使用。如果你查看main library file,你将看到我们在上面讨论过的相同类型的技术。以下块将许多IndexedDB方法使用的基本请求模型转换为使用promise:我们想邀请你学习的一个例子是Jake Archibald's idb library,它真正地显示了
Promise()
构造函数的有用异步应用程序。这采用了 IndexedDB API,它是一种旧式的基于回调的 API,用于在客户端存储和检索数据,并允许你将其与 promises 一起使用。如果你查看main library file,你将看到我们在上面讨论过的相同类型的技术。以下块将许多 IndexedDB 方法使用的基本请求模型转换为使用 promise:function promisifyRequest(request) { return new Promise(function(resolve, reject) { @@ -565,18 +565,18 @@一个更真实的例子
request
的success
event触发时,onsuccess
处理程序将使用请求的result
实现(fullfill)promise。request
的error
event触发时,onerror
处理程序拒绝带有请求error
的promiserequest
的error
event触发时,onerror
处理程序拒绝带有请求error
的 promise当我们不知道函数的返回值或返回需要多长时间时,Promises是构建异步应用程序的好方法。它们使得在没有深度嵌套回调的情况下更容易表达和推理异步操作序列,并且它们支持类似于同步try ... catch
语句的错误处理方式。
当我们不知道函数的返回值或返回需要多长时间时,Promises 是构建异步应用程序的好方法。它们使得在没有深度嵌套回调的情况下更容易表达和推理异步操作序列,并且它们支持类似于同步try ... catch
语句的错误处理方式。
Promise适用于所有现代浏览器的最新版本;promise有兼容问题的唯一情况是Opera Mini和IE11及更早版本。
+Promise 适用于所有现代浏览器的最新版本;promise 有兼容问题的唯一情况是 Opera Mini 和 IE11 及更早版本。
-本文中,我们没有涉及的所有promise的功能,只是最有趣和最有用的功能。当你开始了解有关promise的更多信息时,你会遇到更多功能和技巧。
+本文中,我们没有涉及的所有 promise 的功能,只是最有趣和最有用的功能。当你开始了解有关 promise 的更多信息时,你会遇到更多功能和技巧。
-大多数现代Web API都是基于promise的,因此你需要了解promise才能充分利用它们。这些API包括WebRTC,Web Audio API,Media Capture and Streams等等。随着时间的推移,Promises将变得越来越重要,因此学习使用和理解它们是学习现代JavaScript的重要一步。
+大多数现代 Web API 都是基于 promise 的,因此你需要了解 promise 才能充分利用它们。这些 API 包括WebRTC,Web Audio API,Media Capture and Streams等等。随着时间的推移,Promises 将变得越来越重要,因此学习使用和理解它们是学习现代 JavaScript 的重要一步。
参见
@@ -592,9 +592,9 @@我们将构建的传统函数将被命名为 displayMessage()
,它向用户展示一个传统的消息盒子于web页面的顶部。它充当浏览器内建的 alert() 函数更有用的替代品。你已经看过了这个,但是我们回复一下我们的记忆——在你的浏览器的 JavaScript控制台中,在任意一个页面里尝试以下代码
我们将构建的传统函数将被命名为 displayMessage()
,它向用户展示一个传统的消息盒子于 web 页面的顶部。它充当浏览器内建的 alert() 函数更有用的替代品。你已经看过了这个,但是我们回复一下我们的记忆——在你的浏览器的 JavaScript 控制台中,在任意一个页面里尝试以下代码
alert('This is a message');@@ -39,7 +39,7 @@
这个alert()
函数不是很好的:您可以alert()
出这条信息,但是您不能很容易的表达其他内容,例如颜色,图标或者是其他东西。接下来我们将会构建一个更有趣的函数。
笔记: 这个例子能够在现代浏览器上很好的工作,但是这个风格在老的浏览器上并没那么有趣。我们建议你实现这个例子时在现代浏览器上,例如Firefox,Opera或者Chrome浏览器。
+笔记: 这个例子能够在现代浏览器上很好的工作,但是这个风格在老的浏览器上并没那么有趣。我们建议你实现这个例子时在现代浏览器上,例如 Firefox,Opera 或者 Chrome 浏览器。
首先,让我们来组织一个基本的函数。
注:对于函数命名约定,应遵循与变量命名约定相同的规则。 这很好,尽你所能理解它们之间的区别 - 函数名称后带有括号,而变量则没有。
+注:对于函数命名约定,应遵循与变量命名约定相同的规则。这很好,尽你所能理解它们之间的区别 - 函数名称后带有括号,而变量则没有。
<script>
元素中:
function displayMessage() { @@ -80,21 +80,21 @@基本函数
天哪,这么多代码!好吧,一行一行的解释给你听。
-第一行代码使用了一个DOM(文档对象模型)的内置方法 {{domxref("document.querySelector()")}} 来选择{{htmlelement("html")}} 元素并且把它存放在一个叫
+html
的常量中, 这样方便我们接下来使用这个元素:第一行代码使用了一个 DOM(文档对象模型)的内置方法 {{domxref("document.querySelector()")}} 来选择{{htmlelement("html")}} 元素并且把它存放在一个叫
html
的常量中,这样方便我们接下来使用这个元素:const html = document.querySelector('html');-下段代码使用了另一个名字叫做 {{domxref("Document.createElement()")}} 的DOM方法,用来创建 {{htmlelement("div")}} 元素并且把该新建元素的引用(实际上是新建对象的地址)放在一个叫做
+panel
的常量中。 这个元素将成为我们的消息框的外部容器。下段代码使用了另一个名字叫做 {{domxref("Document.createElement()")}} 的 DOM 方法,用来创建 {{htmlelement("div")}} 元素并且把该新建元素的引用(实际上是新建对象的地址)放在一个叫做
-panel
的常量中。 这个元素将成为我们的消息框的外部容器。然后我们又使用了一个叫做 {{domxref("Element.setAttribute()")}} 的DOM方法给panel元素添加了一个值为
+msgBox
的class
类属性。 这样做方便我们来给这个元素添加样式 — 查看CSS代码你就知道我们使用.msgBox
类选择器来给消息框和消息内容设置样式。然后我们又使用了一个叫做 {{domxref("Element.setAttribute()")}} 的 DOM 方法给 panel 元素添加了一个值为
-msgBox
的class
类属性。 这样做方便我们来给这个元素添加样式 — 查看 CSS 代码你就知道我们使用.msgBox
类选择器来给消息框和消息内容设置样式。最后,我们还使用了一个叫做 {{domxref("Node.appendChild()")}} 的DOM方法,给
+html
常量(我们之前定义好的)追加了我们设置好样式的panel元素 。该方法追加了元素的同时也把panel<div>
元素指定为<html>
的子元素 。这样做是因为我们创建了一个元素之后这个元素并不会莫名其妙的出现在我们的页面上(浏览器只知道我们创建了一个元素,但是不知道把这个元素怎么呈现出来) — 因此,我们给这个元素了一个定位,就是显示在html里面!最后,我们还使用了一个叫做 {{domxref("Node.appendChild()")}} 的 DOM 方法,给
html
常量(我们之前定义好的)追加了我们设置好样式的 panel 元素 。该方法追加了元素的同时也把 panel<div>
元素指定为<html>
的子元素 。这样做是因为我们创建了一个元素之后这个元素并不会莫名其妙的出现在我们的页面上(浏览器只知道我们创建了一个元素,但是不知道把这个元素怎么呈现出来) — 因此,我们给这个元素了一个定位,就是显示在 html 里面!const panel = document.createElement('div'); panel.setAttribute('class', 'msgBox'); html.appendChild(panel);-下面这两段使用了我们之前使用过的方法
+createElement()
和appendChild()
— 创建了一个 {{htmlelement("p")}} 元素和一个{{htmlelement("button")}}元素 — 并且把他们追加到了panel<div>
之下。我们使用元素的 {{domxref("Node.textContent")}}(Node泛指一个元素并不是说是某个元素是叫Node) 属性— 表示一个元素的文本属性 — 给一个p元素赋值, 同样按钮也有这个属性,该属性就是按钮显示的‘X’。这个按钮的功能就是关闭消息提示框。下面这两段使用了我们之前使用过的方法
createElement()
和appendChild()
— 创建了一个 {{htmlelement("p")}} 元素和一个{{htmlelement("button")}}元素 — 并且把他们追加到了 panel<div>
之下。我们使用元素的 {{domxref("Node.textContent")}}(Node 泛指一个元素并不是说是某个元素是叫 Node)属性— 表示一个元素的文本属性 — 给一个 p 元素赋值,同样按钮也有这个属性,该属性就是按钮显示的‘X’。这个按钮的功能就是关闭消息提示框。const msg = document.createElement('p'); msg.textContent = 'This is a message box'; @@ -104,45 +104,45 @@-基本函数
closeBtn.textContent = 'x'; panel.appendChild(closeBtn);最后我们使用一个叫做 {{domxref("GlobalEventHandlers.onclick")}} 的事件句柄给按钮添加了一个点击事件, 点击事件后定义了一个匿名函数,功能是将消息提示框从父容器中删除 — 达到了关闭的效果。
+最后我们使用一个叫做 {{domxref("GlobalEventHandlers.onclick")}} 的事件句柄给按钮添加了一个点击事件,点击事件后定义了一个匿名函数,功能是将消息提示框从父容器中删除 — 达到了关闭的效果。
-简单来说,这个
+onclick
句柄是一个按钮的属性 (事实上,页面上的任何元素) 当按钮被点击的时候能够执行一些代码。 你可以在之后的介绍事件的章节了解详情。我们给onclick
句柄绑定了一个匿名函数, 函数中代码在元素被点击的时候运行。函数里面的这行代码使用了 {{domxref("Node.removeChild()")}} DOM 方法指定了我们想要移除的HTML的子元素 — 在这里指panel<div>
.简单来说,这个
-onclick
句柄是一个按钮的属性 (事实上,页面上的任何元素) 当按钮被点击的时候能够执行一些代码。 你可以在之后的介绍事件的章节了解详情。我们给onclick
句柄绑定了一个匿名函数,函数中代码在元素被点击的时候运行。函数里面的这行代码使用了 {{domxref("Node.removeChild()")}} DOM 方法指定了我们想要移除的 HTML 的子元素 — 在这里指 panel<div>
.PS:我来解释下是什么意思,panel是消息框,panel.parentNode就是指panel的上一级,就是整个DOM,然后再来用这个父亲来干掉这个儿子,儿子不能自己干掉自己,所以要这么做。
+PS:我来解释下是什么意思,panel 是消息框,panel.parentNode 就是指 panel 的上一级,就是整个 DOM,然后再来用这个父亲来干掉这个儿子,儿子不能自己干掉自己,所以要这么做。
closeBtn.onclick = function() { panel.parentNode.removeChild(panel); }-大体上, 这一整块的代码我就不解释了就是一个div,一个段落,一个按钮, 把这个加在页面上:
+大体上,这一整块的代码我就不解释了就是一个 div,一个段落,一个按钮,把这个加在页面上:
<div class="msgBox"> <p>This is a message box</p> <button>x</button> </div>-啊,看完了这么多代码,是不是很累? — 不用担心,你现在没有必要完全知道这些代码的细节! 这里我们只关心函数的结构和使用方式, 下面的例子将展示一些有意思的东西。
+啊,看完了这么多代码,是不是很累? — 不用担心,你现在没有必要完全知道这些代码的细节!这里我们只关心函数的结构和使用方式, 下面的例子将展示一些有意思的东西。
调用函数
-相信你已经迫不及待的在你的
+<script>
标签中写好了一个函数, 但仅仅是定义而已,这玩意不会做任何事情。相信你已经迫不及待的在你的
<script>
标签中写好了一个函数,但仅仅是定义而已,这玩意不会做任何事情。-
- 把下面这行代码加在写好的函数下面来调用函数(当然,不一定要放在函数下面来调用,在C语言中确实是还要先定义后使用,但是我们现在用的是JavaScript,这玩意很强大,不管你是先定义后调用还是先调用后定义都行,但是别忘了定义): +
- 把下面这行代码加在写好的函数下面来调用函数(当然,不一定要放在函数下面来调用,在 C 语言中确实是还要先定义后使用,但是我们现在用的是 JavaScript,这玩意很强大,不管你是先定义后调用还是先调用后定义都行,但是别忘了定义):
+ 这行代码调用了你写的函数,当浏览器解析到这行代码时会立即执行函数内的代码。当你保存好你的代码以后在浏览器中刷新,你会马上看到一个小小的提示框弹出来,但是只弹出了一次。毕竟我们只调用了一次函数是不?displayMessage();- 这行代码调用了你写的函数, 当浏览器解析到这行代码时会立即执行函数内的代码。当你保存好你的代码以后在浏览器中刷新, 你会马上看到一个小小的提示框弹出来, 但是只弹出了一次。毕竟我们只调用了一次函数是不?
现在打开浏览器开发工具, 找到JavaScript控制台把上面这一句再输入一遍然后回车, 你会看到又弹出了一次!有点意思... — 现在我们有了一个能够重复调用的函数,只要你高兴可以随时调用它。
+现在打开浏览器开发工具,找到 JavaScript 控制台把上面这一句再输入一遍然后回车,你会看到又弹出了一次!有点意思... — 现在我们有了一个能够重复调用的函数,只要你高兴可以随时调用它。
-但是,这玩意有什么用呢?在真实的应用当中这样的消息提示框一般用来提示一些什么新的东西, 或者是出现了一个什么错误, 或者当用户删除配置文件的时候("你确定要这样做?"), 或者用户添加一个新的联系人之后提示操作成功..等等。 在这个例子里面, 当用户点击这个按钮的时候这个提示框会出现。
+但是,这玩意有什么用呢?在真实的应用当中这样的消息提示框一般用来提示一些什么新的东西,或者是出现了一个什么错误,或者当用户删除配置文件的时候 ("你确定要这样做?"), 或者用户添加一个新的联系人之后提示操作成功..等等。在这个例子里面,当用户点击这个按钮的时候这个提示框会出现。
const btn = document.querySelector('button');
btn.onclick = displayMessage;-
跟关闭按钮类似closeBtn.onclick...
, 当按钮被点击的时候我们运行了点代码。 但不同的是, 之前等号的右边是一个匿名函数,看起来是这样的:btn.onclick = function(){...}
, 我们现在是直接使用函数名称来调用。跟关闭按钮类似closeBtn.onclick...
, 当按钮被点击的时候我们运行了点代码。 但不同的是,之前等号的右边是一个匿名函数,看起来是这样的:btn.onclick = function(){...}
, 我们现在是直接使用函数名称来调用。
btn.onclick = displayMessage();-
保存刷新, 你会发现按钮都还没点击提示框就出来了! 在函数名后面的这个括号叫做“函数调用运算符”(function invocation operator)。你只有在想直接调用函数的地方才这么写。 同样要重视的是, 匿名函数里面的代码也不是直接运行的, 只要代码在函数作用域内。
+保存刷新,你会发现按钮都还没点击提示框就出来了! 在函数名后面的这个括号叫做“函数调用运算符”(function invocation operator)。你只有在想直接调用函数的地方才这么写。 同样要重视的是,匿名函数里面的代码也不是直接运行的,只要代码在函数作用域内。
-如果你做了这个函数括号的实验, 在继续之前把代码恢复到之前的状态。
+如果你做了这个函数括号的实验,在继续之前把代码恢复到之前的状态。
就现在看来,我们的函数还不是特别有用 — 我们想要的不仅仅是每点击一次展示一个默认的消息。我们来改造下我们的函数,给它添加几个参数, 允许我们以不同的方式调用这个函数。
+就现在看来,我们的函数还不是特别有用 — 我们想要的不仅仅是每点击一次展示一个默认的消息。我们来改造下我们的函数,给它添加几个参数,允许我们以不同的方式调用这个函数。
function displayMessage(msgText, msgType) {当我们调用函数的时候,我们可以在括号里添加两个变量,来指定显示在消息框里面的消息,和消息的类型。
msg.textContent = 'This is a message box';-
改成这样:
+改成这样:
msg.textContent = msgText;
btn.onclick = displayMessage;-
改成这样:
+改成这样:
btn.onclick = function() { displayMessage('Woo, this is a different message!'); };如果我们要在点击事件里面绑定这个新函数,我们不能直接使用(
btn.onclick = displayMessage('Woo, this is a different message!');
)前面已经讲过— 我们要把它放在一个匿名函数里面,不然函数会直接调用,而不是按钮点击之后才会调用,这不是我们想要的结果。刚才我们只使用了我们定义的第一个参数msgText
,对于第二个参数msgType
,这个就涉及了稍微多一点的东西— 我们要设置一些依赖于这个 msgType
参数的东西, 我们的函数将会显示不同的图标和不同的背景颜色。
刚才我们只使用了我们定义的第一个参数msgText
,对于第二个参数msgType
,这个就涉及了稍微多一点的东西— 我们要设置一些依赖于这个 msgType
参数的东西,我们的函数将会显示不同的图标和不同的背景颜色。
icons
的文件夹下,和你的HTML文件在同一个目录下。
+ icons
的文件夹下,和你的 HTML 文件在同一个目录下。
- .msgBox
的宽度:
+ .msgBox
的宽度:
width: 200px;改成:
width: 242px;
.msgBox p { ... }
里面添加几条新规则:
+ .msgBox p { ... }
里面添加几条新规则:
padding-left: 82px; background-position: 25px center; background-repeat: no-repeat;
displayMessage()
让它能够显示图标. 在你的函数结束符之前}
添加下面这几行代码:
+ displayMessage()
让它能够显示图标。在你的函数结束符之前}
添加下面这几行代码:
if (msgType === 'warning') { msg.style.backgroundImage = 'url(icons/warning.png)'; panel.style.backgroundColor = 'red'; @@ -215,11 +215,11 @@- 来解释下, 如果第二个参数一个更加复杂的参数
} else { msg.style.paddingLeft = '20px'; }
msgType
的值为 'warning'
, 我们的消息框将显示一个警告图标和一个红色的背景. 如果这个参数的值是 'chat'
, 将显示聊天图标和水蓝色的背景. 如果 msgType
没有指定任何值 (或者不是'warning'
和'chat'
), 然后这个 else { ... }
代码块将会被执行, 代码的意思是给消息段落设置了一个简单的左内边距并且没有图标, 也没有背景颜色。这么做是为了当没有提供 msgType
参数的时候给函数一个默认行为, 意思是这是一个可选参数(你没发现?其实我们已经用过了!就在这里btn.onclick = function() { displayMessage('Woo, this is a different message!'); };
只是当时我们没有写这个else
段,也就是啥操作也没做)!displayMessage()
像这样:
+ 来解释下,如果第二个参数 msgType
的值为 'warning'
, 我们的消息框将显示一个警告图标和一个红色的背景。如果这个参数的值是 'chat'
, 将显示聊天图标和水蓝色的背景。如果 msgType
没有指定任何值 (或者不是'warning'
和'chat'
), 然后这个 else { ... }
代码块将会被执行,代码的意思是给消息段落设置了一个简单的左内边距并且没有图标,也没有背景颜色。这么做是为了当没有提供 msgType
参数的时候给函数一个默认行为,意思是这是一个可选参数(你没发现?其实我们已经用过了!就在这里btn.onclick = function() { displayMessage('Woo, this is a different message!'); };
只是当时我们没有写这个else
段,也就是啥操作也没做)!displayMessage()
像这样:
displayMessage('Woo, this is a different message!');-
或者这样:
+或者这样:
displayMessage('Your inbox is almost full — delete some mails', 'warning'); displayMessage('Brian: Hi there, how are you today?','chat');@@ -227,7 +227,7 @@
注意: 如果你写这个例子遇到了困难, 在这里查看免费的代码 完整版本的代码 (或者在线运行的完整代码), 也可以向我们寻求帮助。
+注意: 如果你写这个例子遇到了困难,在这里查看免费的代码 完整版本的代码 (或者在线运行的完整代码), 也可以向我们寻求帮助。
恭喜你,终于到了这里(等你好久了)! 这篇文章介绍了如何写一个自定义函数, 要把这个新技能在真实项目中使用上你可能还要花点功夫。 下一篇文章中我们将会介绍函数的另一个相关概念 — 返回值。
+恭喜你,终于到了这里(等你好久了)! 这篇文章介绍了如何写一个自定义函数,要把这个新技能在真实项目中使用上你可能还要花点功夫。 下一篇文章中我们将会介绍函数的另一个相关概念 — 返回值。
在任何的编程语言中,代码需要依靠不同的输入作出决定并且采取行动。例如,在游戏中,如果玩家的生命值变成了0,那么游戏就结束了。在天气应用中,如果在早晨运行,就显示一张日出的图片;如果在晚上,就显示星星和月亮的图片。在这篇文章中,我们将探索在JavaScript中所谓的条件语句是怎样工作的。
+在任何的编程语言中,代码需要依靠不同的输入作出决定并且采取行动。例如,在游戏中,如果玩家的生命值变成了 0,那么游戏就结束了。在天气应用中,如果在早晨运行,就显示一张日出的图片;如果在晚上,就显示星星和月亮的图片。在这篇文章中,我们将探索在 JavaScript 中所谓的条件语句是怎样工作的。
预备知识: | -基本的计算机知识,对HTML和CSS有基本的了解,JavaScript的第一步。 | +基本的计算机知识,对 HTML 和 CSS 有基本的了解,JavaScript 的第一步。 |
---|---|---|
目标: | -了解怎样在JavaScript中使用条件语句的结构。 | +了解怎样在 JavaScript 中使用条件语句的结构。 |
人类(以及其他的动物)无时无刻不在做决定,这些决定都影响着他们的生活,从小事(“我应该吃一片还是两片饼干”)到重要的大事(“我应该留在我的祖国,在我父亲的农场工作;还是应该去美国学习天体物理学”)。
-条件语句结构允许我们来描述在JavaScript中这样的选择,从不得不作出的选择(例如:“一片还是两片”)到产生的结果或这些选择(也许是“吃一片饼干”可能会“仍然感觉饿”,或者是“吃两片饼干”可能会“感觉饱了,但妈妈会因为我吃掉了所有的饼干而骂我”。)
+条件语句结构允许我们来描述在 JavaScript 中这样的选择,从不得不作出的选择(例如:“一片还是两片”)到产生的结果或这些选择(也许是“吃一片饼干”可能会“仍然感觉饿”,或者是“吃两片饼干”可能会“感觉饱了,但妈妈会因为我吃掉了所有的饼干而骂我”。)
@@ -36,7 +36,7 @@基本的if…else语法看起来像下面的 {{glossary("伪代码")}}:
+基本的 if…else 语法看起来像下面的 {{glossary("伪代码")}}:
if (condition) { code to run if condition is true @@ -48,15 +48,15 @@基本的的 if…else 语法
-
- 关键字 if,并且后面跟随括号。
-- 要测试的条件,放到括号里(通常是“这个值大于另一个值吗”或者“这个值存在吗”)。这个条件会利用比较运算符(我们会在最后的模块中讨论)进行比较,并且返回true或者false。
-- 一组花括号,在里面我们有一些代码——可以是任何我们喜欢的代码,并且只会在条件语句返回true的时候运行。
-- 关键字else。
-- 另一组花括号,在里面我们有一些代码——可以是任何我们喜欢的代码,并且当条件语句返回值不是true的话,它才会运行。
+- 要测试的条件,放到括号里(通常是“这个值大于另一个值吗”或者“这个值存在吗”)。这个条件会利用比较运算符(我们会在最后的模块中讨论)进行比较,并且返回 true 或者 false。
+- 一组花括号,在里面我们有一些代码——可以是任何我们喜欢的代码,并且只会在条件语句返回 true 的时候运行。
+- 关键字 else。
+- 另一组花括号,在里面我们有一些代码——可以是任何我们喜欢的代码,并且当条件语句返回值不是 true 的话,它才会运行。
这段代码真的非常易懂——它说“如果(if)条件(condition)返回true,运行代码A,否则(else)运行代码B”
+这段代码真的非常易懂——它说“如果(if)条件(condition)返回 true,运行代码 A,否则(else)运行代码 B”
-注意:你不一定需要else和第二个花括号——下面的代码也是符合语法规则的:
+注意:你不一定需要 else 和第二个花括号——下面的代码也是符合语法规则的:
if (condition) { code to run if condition is true @@ -64,7 +64,7 @@-基本的的 if…else 语法
run some other code不过,这里你需要注意——在这种情况下,第二段代码不被条件语句控制,所以它总会运行,不管条件返回的是true还是false。这不一定是一件坏事,但这可能不是你想要的——你经常只想要运行一段代码或者另一段,而不是两个都运行。
+不过,这里你需要注意——在这种情况下,第二段代码不被条件语句控制,所以它总会运行,不管条件返回的是 true 还是 false。这不一定是一件坏事,但这可能不是你想要的——你经常只想要运行一段代码或者另一段,而不是两个都运行。
最后,有时候你可能会看到 if…else 语句没有写花括号,像下面的速记风格:
@@ -75,7 +75,7 @@基本的的 if…else 语法
一个真实的例子
-为了更好的理解这种语法,让我们考虑一个真实的例子。想像一个孩子被他的父母要求帮助他们做家务。父母可能会说“嗨,宝贝儿,如果你帮我去购物,我会给你额外的零花钱,这样你就能买得起你想要的玩具了。”在JavaScript中,我们可以这样表示:
+为了更好的理解这种语法,让我们考虑一个真实的例子。想像一个孩子被他的父母要求帮助他们做家务。父母可能会说“嗨,宝贝儿,如果你帮我去购物,我会给你额外的零花钱,这样你就能买得起你想要的玩具了。”在 JavaScript 中,我们可以这样表示:
var shoppingDone = false; @@ -88,7 +88,7 @@一个真实的例子
这段代码显示的结果是变量
shoppingDone
总是返回false
, 意味着对我们的穷孩子来说很失望。如果孩子去购物的话,就需要依靠我们提供机制来使父母把变量shoppingDone
变成true
。-Note: 你可以看到在Github上这个例子的完整版本(也可以在线运行)
+Note: 你可以看到在Github 上这个例子的完整版本(也可以在线运行)
else if
@@ -135,9 +135,9 @@else if
- 这里我们有 HTML {{htmlelement("select")}} 元素让我们选择不同的天气,以及一个简单的段落。
-- 在 JavaScript 中, 我们同时存储了对 {{htmlelement("select")}} 和 {{htmlelement("p")}} 的引用, 并对
+<select>
添加了一个事件监听器,因此,当它的值改变时,setWeather()
函数被执行。- 在 JavaScript 中,我们同时存储了对 {{htmlelement("select")}} 和 {{htmlelement("p")}} 的引用,并对
<select>
添加了一个事件监听器,因此,当它的值改变时,setWeather()
函数被执行。- 当函数运行时,我们首先新建了一个
-choice
变量去存储当前被选的<select>
中的值。接着我们用条件判断语句根据choice
的值选择性的展示段落中的文本。注意else if() {...}
段中的条件是怎么被判断的,除了第一个,它是在if() {...}中被判断的。
- 最后一个
+else {...}
中的选择通常被叫做 “最后招数” — 在所有的条件都不为 true 时其中的代码会被执行。在这个例子中,如果用户没有选择任何一个选项,它会将段落中的文本清空,例如当用户决定重新选择最开始出现的"--Make a choice--"选项时,就会有这样的效果。- 最后一个
else {...}
中的选择通常被叫做“最后招数” — 在所有的条件都不为 true 时其中的代码会被执行。在这个例子中,如果用户没有选择任何一个选项,它会将段落中的文本清空,例如当用户决定重新选择最开始出现的"--Make a choice--"选项时,就会有这样的效果。@@ -158,7 +158,7 @@-关于比较运算符
Note: 如果你想复习这些内容,可以回顾之前链接上的材料。
我们想特别提到测试布尔值(true / false),和一个通用模式,你会频繁遇到它,任何不是
+false
,undefined
,null
,0
,NaN
的值,或一个空字符串('')在作为条件语句进行测试时实际返回true,因此您可以简单地使用变量名称来测试它是否为真,甚至是否存在(即它不是未定义的)。例如:我们想特别提到测试布尔值(true / false),和一个通用模式,你会频繁遇到它,任何不是
false
,undefined
,null
,0
,NaN
的值,或一个空字符串('')在作为条件语句进行测试时实际返回 true,因此您可以简单地使用变量名称来测试它是否为真,甚至是否存在(即它不是未定义的)。例如:var cheese = 'Cheddar'; @@ -178,9 +178,9 @@-关于比较运算符
var childsAllowance = 5; }嵌套if ... else
+嵌套 if ... else
-将另一个if ... else 语句放在另一个中 - 嵌套它是完全可行的。例如,我们可以更新我们的天气预报应用程序,以显示更多的选择,具体取决于温度:
+将另一个 if ... else 语句放在另一个中 - 嵌套它是完全可行的。例如,我们可以更新我们的天气预报应用程序,以显示更多的选择,具体取决于温度:
if (choice === 'sunny') { if (temperature < 86) { @@ -190,19 +190,19 @@-嵌套if ... else
} }即使代码全部一起工作,每个if ... else语句完全独立于另一个。
+即使代码全部一起工作,每个 if ... else 语句完全独立于另一个。
逻辑运算符:&& , || 和 !
-如果要测试多个条件,而不需要编写嵌套if ... else语句,逻辑运算符可以帮助您。当在条件下使用时,前两个执行以下操作:
+如果要测试多个条件,而不需要编写嵌套 if ... else 语句,逻辑运算符可以帮助您。当在条件下使用时,前两个执行以下操作:
&&
— 逻辑与; 使得并列两个或者更多的表达式成为可能,只有当这些表达式每一个都返回true
时,整个表达式才会返回true.
||
— 逻辑或; 当两个或者更多表达式当中的任何一个返回 true
则整个表达式将会返回 true
.举一个逻辑 && 的例子, 刚才的那段代码片段可以写成下面这样:
+举一个逻辑 && 的例子,刚才的那段代码片段可以写成下面这样:
if (choice === 'sunny' && temperature < 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.'; @@ -220,7 +220,7 @@逻辑运算符:&& , || 和 !
-最后一种类型的逻辑运算符, 逻辑非
+!
运算符表示, 可以用于对一个表达式取否. 让我们把 非运算符 结合上一个例子里的 或表达式 看看:最后一种类型的逻辑运算符, 逻辑非
!
运算符表示,可以用于对一个表达式取否。让我们把 非运算符 结合上一个例子里的 或表达式 看看:if (!(iceCreamVanOutside || houseStatus === 'on fire')) { console.log('Probably should just stay in then.'); @@ -230,29 +230,29 @@逻辑运算符:&& , || 和 !
在这一段代码中,如果逻辑或所在的语句返回true
,则非运算符会将其取否,于是整个表达式的返回值将会是false
。 -您可以在任何结构中随意合并很多个逻辑表达式。接下来的例子将会只在或运算符两边的语句同时返回true时才会执行代码,这也就意味着整个与运算符语句将会返回true:
+您可以在任何结构中随意合并很多个逻辑表达式。接下来的例子将会只在或运算符两边的语句同时返回 true 时才会执行代码,这也就意味着整个与运算符语句将会返回 true:
if ((x === 5 || y > 3 || z <= 10) && (loggedIn || userName === 'Steve')) { // run the code }-在条件语句中运用或逻辑运算符最常见的错误是尝试声明变量后,仅检查该变量一次的情况下赋予很多个都会返回true的值,不同的值之间用
+||
(或)运算符分隔。比如:在条件语句中运用或逻辑运算符最常见的错误是尝试声明变量后,仅检查该变量一次的情况下赋予很多个都会返回 true 的值,不同的值之间用
||
(或) 运算符分隔。比如:if (x === 5 || 7 || 10 || 20) { // run my code }-在这个例子里
+if(...)
里的条件总为真,因为 7 (或者其它非零的数) 的值总是为真. 这个条件实际意思是 "如果x等于5, 或者7为真 — 它总是成立的". 这不是我们想要的逻辑,为了 让它正常工作你必须指定每个或表达式两边都是完整的检查:在这个例子里
if(...)
里的条件总为真,因为 7 (或者其它非零的数) 的值总是为真。这个条件实际意思是 "如果 x 等于 5, 或者 7 为真 — 它总是成立的". 这不是我们想要的逻辑,为了 让它正常工作你必须指定每个或表达式两边都是完整的检查:if (x === 5 || x === 7 || x === 10 ||x === 20) { // run my code }-switch语句
+switch 语句
-+
if...else
语句能够很好地实现条件代码,但是它们不是没有缺点。 它们主要适用于您只有几个选择的情况,每个都需要相当数量的代码来运行,和/或 的条件很复杂的情况(例如多个逻辑运算符)。 对于只想将变量设置一系列为特定值的选项或根据条件打印特定语句的情况,语法可能会很麻烦,特别是如果您有大量选择。-
if...else
语句能够很好地实现条件代码,但是它们不是没有缺点。它们主要适用于您只有几个选择的情况,每个都需要相当数量的代码来运行,和/或 的条件很复杂的情况(例如多个逻辑运算符)。对于只想将变量设置一系列为特定值的选项或根据条件打印特定语句的情况,语法可能会很麻烦,特别是如果您有大量选择。+
switch
语句在这里是您的朋友 - 他们以单个表达式/值作为输入,然后查看多个选项,直到找到与该值相匹配的选项,执行与之相关的代码。 这里有一些伪代码,可以给你一点灵感:
switch
语句在这里是您的朋友 - 他们以单个表达式/值作为输入,然后查看多个选项,直到找到与该值相匹配的选项,执行与之相关的代码。这里有一些伪代码,可以给你一点灵感:switch (expression) { case choice1: @@ -269,25 +269,25 @@-switch语句
actually, just run this code }这里我们得到:
+这里我们得到:
-
- 关键字
-switch
, 后跟一组括号.- 括号内的表达式或值.
+- 关键字
+switch
, 后跟一组括号。- 括号内的表达式或值。
- 关键字
-case
, 后跟一个选项的表达式/值,后面跟一个冒号.- 如果选择与表达式匹配,则运行一些代码.
-- 一个
-break
语句, 分号结尾. 如果先前的选择与表达式/值匹配,则浏览器在此停止执行代码块,并执行switch语句之后的代码.- 你可以添加任意的 case 选项(选项3-5).
-- 关键字
+default
, 后面跟随和case
完全相同的代码模式 (选项 3–5), except thatdefault
之后不需要再有选项, 并且您不需要break
语句, 因为之后没有任何运行代码. 如果之前没有选项匹配,则运行default
选项.- 如果选择与表达式匹配,则运行一些代码。
+- 一个
+break
语句,分号结尾。如果先前的选择与表达式/值匹配,则浏览器在此停止执行代码块,并执行switch语句之后的代码.- 你可以添加任意的 case 选项(选项 3-5).
+- 关键字
default
, 后面跟随和case
完全相同的代码模式 (选项 3–5), except thatdefault
之后不需要再有选项, 并且您不需要break
语句, 因为之后没有任何运行代码. 如果之前没有选项匹配,则运行default
选项。--Note:
+default
部分不是必须的 - 如果表达式不可能存在未知值,则可以安全地省略它。 如果有机会,您需要包括它来处理未知的情况。Note:
default
部分不是必须的 - 如果表达式不可能存在未知值,则可以安全地省略它。如果有机会,您需要包括它来处理未知的情况。switch语句示例
+switch 语句示例
-我们来看一个真实的例子 - 我们将重写天气预报应用程序,以改用switch语句:
+我们来看一个真实的例子 - 我们将重写天气预报应用程序,以改用 switch 语句:
<label for="weather">Select the weather type today: </label> <select id="weather"> @@ -394,7 +394,7 @@主动学习:一个简单的日
我们需要你在
onchange
处理函数中写一个条件语句,就在// ADD CONDITIONAL HERE
任务的下面 。这应该:-
@@ -516,7 +516,7 @@- 查看所选月份(存储在
+choice
变量中,这将是<select>
值更改后的元素值,例如“1月”)。- 查看所选月份(存储在
choice
变量中,这将是<select>
值更改后的元素值,例如“1 月”)。- 设置一个被调用
days
为等于所选月份天数的变量。为此,您必须查看一年中每个月的天数。为了这个例子的目的,你可以忽略闰年。Playable code
主动学习:更多颜色选择!
-在这个例子中,您将要采取我们前面看到的三元运算符示例,并将三元运算符转换为一个switch语句,这将允许我们对简单的网站应用更多的选择。看看
+<select>
- 这次你会看到它不是两个主题选项,而是五个。您需要在// ADD SWITCH STATEMENT
注释下面添加一个switch语句:在这个例子中,您将要采取我们前面看到的三元运算符示例,并将三元运算符转换为一个 switch 语句,这将允许我们对简单的网站应用更多的选择。看看
<select>
- 这次你会看到它不是两个主题选项,而是五个。您需要在// ADD SWITCH STATEMENT
注释下面添加一个 switch 语句:
choice
变量作为其输入表达式。这就是现在您真正需要了解的JavaScript中的条件结构!我相信你会理解这些概念,并轻松地通过这些例子; 如果有什么不明白的,请随时阅读文章,或者联系我们寻求帮助。
+这就是现在您真正需要了解的 JavaScript 中的条件结构!我相信你会理解这些概念,并轻松地通过这些例子; 如果有什么不明白的,请随时阅读文章,或者联系我们寻求帮助。
前提: | -基本电脑知识, 对HTML和CSS的基本了解,及 JavaScript first steps. | +前提: | +基本电脑知识,对 HTML 和 CSS 的基本了解,及 JavaScript first steps. |
---|---|---|---|
目标: | +目标: | 了解事件的基本理论,它们怎么在浏览器上运行的,以及在不同的编程环境下事件有何不同。 |
前提条件: | -基本的电脑知识,对HTML与CSS有基本的了解,及已阅读: JavaScript first steps(JS的入门)。 | +前提条件: | +基本的电脑知识,对 HTML 与 CSS 有基本的了解,及已阅读: JavaScript first steps(JS 的入门)。 |
---|---|---|---|
目标: | +目标: | 了解 Javascript 函数背后的基本概念。 |
在 JavaScript中, 你将发现函数无处不在 。事实上, 到目前为止,我们一直在使用函数,只是我们还没很好的讨论它们。然而现在是时候了,让我们开始聊聊函数,并探索它们的语法。
+在 JavaScript 中,你将发现函数无处不在。事实上,到目前为止,我们一直在使用函数,只是我们还没很好的讨论它们。然而现在是时候了,让我们开始聊聊函数,并探索它们的语法。
-几乎任何时候,只要你使用一个带有一对圆括号()的JavaScript结构,并且你不是在使用一个常见的比如for for循环,while或do…while循环,或者if语句这样的内置语言结构时,那么您就正在使用函数。
+几乎任何时候,只要你使用一个带有一对圆括号()的 JavaScript 结构,并且你不是在使用一个常见的比如 for for 循环,while 或 do…while 循环,或者 if 语句这样的内置语言结构时,那么您就正在使用函数。
@@ -74,13 +74,13 @@提示:如果需要,你可以随意将这些代码输入浏览器控制台以便于你熟悉其功能。
-JavaScript有许多内置的函数,可以让您做很多有用的事情,而无需自己编写所有的代码。事实上, 许多你调用(运行或者执行的专业词语)浏览器内置函数时调用的代码并不是使用JavaScript来编写——大多数调用浏览器后台的函数的代码,是使用像C++这样更低级的系统语言编写的,而不是像JavaScript这样的web编程语言。
+JavaScript 有许多内置的函数,可以让您做很多有用的事情,而无需自己编写所有的代码。事实上,许多你调用(运行或者执行的专业词语)浏览器内置函数时调用的代码并不是使用 JavaScript 来编写——大多数调用浏览器后台的函数的代码,是使用像 C++这样更低级的系统语言编写的,而不是像 JavaScript 这样的 web 编程语言。
-请记住,这些内置浏览器函数不是核心JavaScript语言的一部分——被定义为浏览器API的一部分,它建立在默认语言之上,以提供更多的功能(请参阅本课程的早期部分以获得更多的描述)。我们将在以后的模块中更详细地使用浏览器API。
+请记住,这些内置浏览器函数不是核心 JavaScript 语言的一部分——被定义为浏览器 API 的一部分,它建立在默认语言之上,以提供更多的功能(请参阅本课程的早期部分以获得更多的描述)。我们将在以后的模块中更详细地使用浏览器 API。
程序员把函数称为对象方法(method)的一部分。你还不必了解JavaScript中已建构的对象在更深层次上是如何运作的——你可以等到下一小节,我们会教给你有关对象运作方式的一切。在我们继续前进之前,我们需要澄清一些有关方法和函数概念之间可能存在的误会——当你在网络上浏览相关信息的时候,你很可能会碰上这两个术语。
+程序员把函数称为对象方法(method)的一部分。你还不必了解 JavaScript 中已建构的对象在更深层次上是如何运作的——你可以等到下一小节,我们会教给你有关对象运作方式的一切。在我们继续前进之前,我们需要澄清一些有关方法和函数概念之间可能存在的误会——当你在网络上浏览相关信息的时候,你很可能会碰上这两个术语。
到目前为止我们所使用的内置代码同属于这两种形式:函数和方法。你可以在这里查看内置函数,内置对象以及其相关方法的完整列表。
@@ -92,7 +92,7 @@您在过去的课程中还看到很多定制功能 - 在代码中定义的功能,而不是在浏览器中。每当您看到一个自定义名称后面都带有括号,那么您使用的是自定义函数. 在我们的循环文章中的random-canvas-circles.html示例(另见完整的源代码)中,我们包括一个如下所示的自定义函数:draw()
您在过去的课程中还看到很多定制功能 - 在代码中定义的功能,而不是在浏览器中。每当您看到一个自定义名称后面都带有括号,那么您使用的是自定义函数。在我们的循环文章中的random-canvas-circles.html示例(另见完整的源代码)中,我们包括一个如下所示的自定义函数:draw()
function draw() { ctx.clearRect(0,0,WIDTH,HEIGHT); @@ -104,7 +104,7 @@-自定义函数
} }
该函数在<canvas>
元素中绘制100个随机圆。每次我们想要这样做,我们可以使用这个函数来调用这个功能
该函数在<canvas>
元素中绘制 100 个随机圆。每次我们想要这样做,我们可以使用这个函数来调用这个功能
draw();@@ -114,13 +114,13 @@
我们需要这个函数,因为浏览器的内置Math.random()函数只生成一个0到1之间的随机十进制数。我们想要一个0到一个指定数字之间的随机整数。
+我们需要这个函数,因为浏览器的内置Math.random()函数只生成一个 0 到 1 之间的随机十进制数。我们想要一个 0 到一个指定数字之间的随机整数。
现在你可能很清楚这一点,但仅仅为了防止……,要在函数定义之后,实际使用它,你必须运行或调用它。这是通过将函数名包含在代码的某个地方,后跟圆括号来完成的。
+现在你可能很清楚这一点,但仅仅为了防止……,要在函数定义之后,实际使用它,你必须运行或调用它。这是通过将函数名包含在代码的某个地方,后跟圆括号来完成的。
function myFunction() { alert('hello'); @@ -143,7 +143,7 @@-匿名函数
alert('hello'); }
这个函数叫做匿名函数 — 它没有函数名! 它也不会自己做任何事情。 你通常将匿名函数与事件处理程序一起使用, 例如,如果单击相关按钮,以下操作将在函数内运行代码:
+这个函数叫做匿名函数 — 它没有函数名!它也不会自己做任何事情。你通常将匿名函数与事件处理程序一起使用,例如,如果单击相关按钮,以下操作将在函数内运行代码:
var myButton = document.querySelector('button'); @@ -200,7 +200,7 @@-函数参数
Note: 参数有时称为参数(arguments),属性(properties)或甚至属性(attributes)
-例如,浏览器的内置Math.random()函数不需要任何参数。当被调用时,它总是返回0到1之间的随机数:
+例如,浏览器的内置Math.random()函数不需要任何参数。当被调用时,它总是返回 0 到 1 之间的随机数:
var myNumber = Math.random();@@ -227,15 +227,15 @@函数参数
函数作用域和冲突
-我们来谈一谈 {{glossary("scope")}}即作用域 — 处理函数时一个非常重要的概念。当你创建一个函数时,函数内定义的变量和其他东西都在它们自己的单独的范围内, 意味着它们被锁在自己独立的隔间中, 不能被函数外的代码访问。
+我们来谈一谈 {{glossary("scope")}}即作用域 — 处理函数时一个非常重要的概念。当你创建一个函数时,函数内定义的变量和其他东西都在它们自己的单独的范围内,意味着它们被锁在自己独立的隔间中,不能被函数外的代码访问。
-所有函数的最外层被称为全局作用域。 在全局作用域内定义的值可以在任意地方访问。
+所有函数的最外层被称为全局作用域。在全局作用域内定义的值可以在任意地方访问。
-JavaScript由于各种原因而建立,但主要是由于安全性和组织性。有时您不希望变量可以在代码中的任何地方访问 - 您从其他地方调用的外部脚本可能会开始搞乱您的代码并导致问题,因为它们恰好与代码的其他部分使用了相同的变量名称,造成冲突。这可能是恶意的,或者是偶然的。
+JavaScript 由于各种原因而建立,但主要是由于安全性和组织性。有时您不希望变量可以在代码中的任何地方访问 - 您从其他地方调用的外部脚本可能会开始搞乱您的代码并导致问题,因为它们恰好与代码的其他部分使用了相同的变量名称,造成冲突。这可能是恶意的,或者是偶然的。
-例如,假设您有一个HTML文件,它调用两个外部JavaScript文件,并且它们都有一个使用相同名称定义的变量和函数:
+例如,假设您有一个 HTML 文件,它调用两个外部 JavaScript 文件,并且它们都有一个使用相同名称定义的变量和函数:
<!-- Excerpt from my HTML --> <script src="first.js"></script> @@ -270,14 +270,14 @@-函数作用域和冲突
动物园管理员就像全局作用域 - 他或她有钥匙访问每个园子,重新投喂食物,照顾生病的动物等。
-主动学习: 和 scope 玩耍
+主动学习:和 scope 玩耍
我们来看一个真正的例子来展示范围
- 首先,制作我们的function-scope.html示例的本地副本。它包含两个函数
a()
和b()
,和三个变量——x
,y
和z
——其中两个在函数中被定义,另一个被定义在全局作用域内。它还包含一个名为output()
的函数,它接收一个参数,并将其输出到页面的一个段落中。- 在浏览器和文本编辑器中打开示例。
-- 在浏览器开发工具中打开JavaScript控制台。在JavaScript控制台中,输入以下命令:
+- 在浏览器开发工具中打开 JavaScript 控制台。在 JavaScript 控制台中,输入以下命令:
output(x);@@ -289,7 +289,7 @@主动学习: 和 scope 玩耍
output(y); output(z);-这两个都应该返回错误沿“ ReferenceError:y未定义 ”。这是为什么?由于函数作用域 -
+y和
z
被锁定在函数a()
和b()
函数中,所以output()
从全局作用域调用时无法访问它们。这两个都应该返回错误沿“ ReferenceError:y 未定义 ”。这是为什么?由于函数作用域 -
y和
z
被锁定在函数a()
和b()
函数中,所以output()
从全局作用域调用时无法访问它们。5.但是,从另一个函数里面调用什么呢?尝试编辑
@@ -303,7 +303,7 @@a()
,b()
所以他们看起来像这样:主动学习: 和 scope 玩耍
output(z); }保存代码并重新加载到浏览器中,然后尝试从JavaScript控制台调用
+a()
和b()
函数:保存代码并重新加载到浏览器中,然后尝试从 JavaScript 控制台调用
a()
和b()
函数:a(); b();@@ -322,12 +322,12 @@主动学习: 和 scope 玩耍
output(x); }
再次保存并重新加载,并在JavaScript控制台中再次尝试:
+再次保存并重新加载,并在 JavaScript 控制台中再次尝试:
a(); b();-
函数 a()
和b()
都应该输出x---1的值。这些没有问题,因为即使output()
的调用与x
的定义不在同一个作用域内,但x
是一个全局变量,所以在所有代码中都可用。
函数 a()
和b()
都应该输出 x---1 的值。这些没有问题,因为即使output()
的调用与x
的定义不在同一个作用域内,但x
是一个全局变量,所以在所有代码中都可用。
7.最后,尝试更新您的代码,如下所示:
@@ -341,7 +341,7 @@再次保存并重新加载,并在JavaScript控制台中再次尝试:
+再次保存并重新加载,并在 JavaScript 控制台中再次尝试:
a(); b();@@ -384,7 +384,7 @@
要确保函数调取的数值处在有效的作用域内。上面的例子中会产生一个错误提示,ReferenceError:myValue is not define
,因为尽管myValue
变量与函数调用指令处在同一个作用域中, 但它却没有在函数内被定义 —— 实际代码在调用函数时就开始运行了。为了使代码正确运作,你必须将值作为参数传递给函数,如下所示:
要确保函数调取的数值处在有效的作用域内。上面的例子中会产生一个错误提示,ReferenceError:myValue is not define
,因为尽管myValue
变量与函数调用指令处在同一个作用域中,但它却没有在函数内被定义 —— 实际代码在调用函数时就开始运行了。为了使代码正确运作,你必须将值作为参数传递给函数,如下所示:
function myBigFunction() { var myValue = 1; diff --git a/files/zh-cn/learn/javascript/building_blocks/image_gallery/index.md b/files/zh-cn/learn/javascript/building_blocks/image_gallery/index.md index eca5d5951f48a6..9a7c456371bb08 100644 --- a/files/zh-cn/learn/javascript/building_blocks/image_gallery/index.md +++ b/files/zh-cn/learn/javascript/building_blocks/image_gallery/index.md @@ -34,7 +34,7 @@ original_slug: learn/JavaScript/Building_blocks/相片走廊 另外,你还可以使用类似 [JSBin](https://jsbin.com/) 或 [Glitch](https://glitch.com/))这些在线编辑器来完成测验。你可以将 HTML、CSS 和 JavaScript 粘贴到这几个在线编辑器中。如果你使用的在线编辑器没有单独的 JavaScript/CSS 面板,请将它们放入 HTML 页面内联的 `