From 33058f2b292b3a581333bdfb21b8f671898c5060 Mon Sep 17 00:00:00 2001 From: Peter Bengtsson Date: Tue, 8 Dec 2020 14:40:17 -0500 Subject: initial commit --- .../build_your_own_function/index.html | 256 +++++++ .../building_blocks/conditionals/index.html | 609 ++++++++++++++++ .../javascript/building_blocks/events/index.html | 564 +++++++++++++++ .../building_blocks/functions/index.html | 428 +++++++++++ .../learn/javascript/building_blocks/index.html | 55 ++ .../building_blocks/looping_code/index.html | 781 +++++++++++++++++++++ .../building_blocks/return_values/index.html | 172 +++++ .../index.html" | 244 +++++++ 8 files changed, 3109 insertions(+) create mode 100644 files/zh-cn/learn/javascript/building_blocks/build_your_own_function/index.html create mode 100644 files/zh-cn/learn/javascript/building_blocks/conditionals/index.html create mode 100644 files/zh-cn/learn/javascript/building_blocks/events/index.html create mode 100644 files/zh-cn/learn/javascript/building_blocks/functions/index.html create mode 100644 files/zh-cn/learn/javascript/building_blocks/index.html create mode 100644 files/zh-cn/learn/javascript/building_blocks/looping_code/index.html create mode 100644 files/zh-cn/learn/javascript/building_blocks/return_values/index.html create mode 100644 "files/zh-cn/learn/javascript/building_blocks/\347\233\270\347\211\207\350\265\260\345\273\212/index.html" (limited to 'files/zh-cn/learn/javascript/building_blocks') 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 +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/JavaScript/Building_blocks/Functions","Learn/JavaScript/Building_blocks/Return_values", "Learn/JavaScript/Building_blocks")}}
+ +

我们在之前的文章里大多学的是理论,这篇文章将提供一个练习的机会——您将练习构建一些您自己风格的函数。在练习过程中,我们也会解释一些针对函数的更深层的实用细节。

+ + + + + + + + + + + + +
先修知识:基本的电脑常识,对于HTML和CSS的基本了解, JavaScript第一步函数-可复用代码块
目标:提供一些练习来构建一个传统的函数,并解释一些有用的相关细节。
+ +

先活跃下气氛:构建一个函数

+ +

我们将构建的传统函数将被命名为 displayMessage(),它向用户展示一个传统的消息盒子于web页面的顶部。它充当浏览器内建的 alert() 函数更有用的替代品。你已经看过了这个,但是我们回复一下我们的记忆——在你的浏览器的 JavaScript控制台中,在任意一个页面里尝试以下代码

+ +
alert('This is a message');
+ +

这个函数只带有一个参数——在 alert box 中展示的字符串。您可以尝试改变字符串来改变消息。

+ +

这个alert()函数不是很好的:您可以alert()出这条信息,但是您不能很容易的表达其他内容,例如颜色,图标或者是其他东西。接下来我们将会构建一个更有趣的函数。

+ +
+

笔记: 这个例子能够在现代浏览器上很好的工作,但是这个风格在老的浏览器上并没那么有趣。我们建议你实现这个例子时在现代浏览器上,例如Firefox,Opera或者Chrome浏览器。

+
+ +

基本函数

+ +

首先,让我们来组织一个基本的函数。

+ +
+

注:对于函数命名约定,应遵循与变量命名约定相同的规则。 这很好,尽你所能理解它们之间的区别 - 函数名称后带有括号,而变量则没有。

+
+ +
    +
  1. 我们希望您首先访问function-start.html文件并创建一个本地拷贝。您将会看到这个HTML很简单 — 我们的body块仅包含一个按钮。我们还提供了一些基本的CSS来装饰自定义消息框,以及一个用于放置JavaScript代码的{{htmlelement("script")}}元素。
  2. +
  3. 接下来,将下面的代码添加至 <script> 元素中: +
    function displayMessage() {
    +
    +}
    + 我们从表示定义一个函数的关键字 function开始,这之后是我们想给我们的函数取的名字;一组括号;和一组大括号。我们要传给我们的函数的任何参数都在括号内,当我们调用该函数时运行的代码均在大括号内。
  4. +
  5. 最后,添加以下代码到大括号中: +
    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);
    +}
    +
  6. +
+ +

天哪,这么多代码!好吧,一行一行的解释给你听。

+ +

第一行代码使用了一个DOM(文档对象模型)的内置方法 {{domxref("document.querySelector()")}} 来选择{{htmlelement("html")}} 元素并且把它存放在一个叫 html的常量中, 这样方便我们接下来使用这个元素:

+ +
const html = document.querySelector('html');
+ +

下段代码使用了另一个名字叫做 {{domxref("Document.createElement()")}} 的DOM方法,用来创建 {{htmlelement("div")}} 元素并且把该新建元素的引用(实际上是新建对象的地址)放在一个叫做 panel的常量中。 这个元素将成为我们的消息框的外部容器。

+ +

然后我们又使用了一个叫做 {{domxref("Element.setAttribute()")}} 的DOM方法给panel元素添加了一个值为msgBox 的class 类属性。 这样做方便我们来给这个元素添加样式 — 查看CSS代码你就知道我们使用.msgBox 类选择器来给消息框和消息内容设置样式。

+ +

最后,我们还使用了一个叫做 {{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’。这个按钮的功能就是关闭消息提示框。

+ +
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);
+ +

最后我们使用一个叫做 {{domxref("GlobalEventHandlers.onclick")}} 的事件句柄给按钮添加了一个点击事件, 点击事件后定义了一个匿名函数,功能是将消息提示框从父容器中删除 — 达到了关闭的效果。

+ +

简单来说,这个 onclick 句柄是一个按钮的属性 (事实上,页面上的任何元素) 当按钮被点击的时候能够执行一些代码。 你可以在之后的介绍事件的章节了解详情。我们给 onclick 句柄绑定了一个匿名函数, 函数中代码在元素被点击的时候运行。函数里面的这行代码使用了 {{domxref("Node.removeChild()")}} DOM 方法指定了我们想要移除的HTML的子元素 — 在这里指panel<div>.

+ +

PS:我来解释下是什么意思,panel是消息框,panel.parentNode就是指panel的上一级,就是整个DOM,然后再来用这个父亲来干掉这个儿子,儿子不能自己干掉自己,所以要这么做。

+ +
closeBtn.onclick = function() {
+  panel.parentNode.removeChild(panel);
+}
+ +

大体上, 这一整块的代码我就不解释了就是一个div,一个段落,一个按钮, 把这个加在页面上:

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

啊,看完了这么多代码,是不是很累? — 不用担心,你现在没有必要完全知道这些代码的细节! 这里我们只关心函数的结构和使用方式, 下面的例子将展示一些有意思的东西。

+ +

调用函数

+ +

相信你已经迫不及待的在你的<script> 标签中写好了一个函数, 但仅仅是定义而已,这玩意不会做任何事情。

+ +
    +
  1. 把下面这行代码加在写好的函数下面来调用函数(当然,不一定要放在函数下面来调用,在C语言中确实是还要先定义后使用,但是我们现在用的是JavaScript,这玩意很强大,不管你是先定义后调用还是先调用后定义都行,但是别忘了定义): +
    displayMessage();
    + 这行代码调用了你写的函数, 当浏览器解析到这行代码时会立即执行函数内的代码。当你保存好你的代码以后在浏览器中刷新, 你会马上看到一个小小的提示框弹出来, 但是只弹出了一次。毕竟我们只调用了一次函数是不?
  2. +
  3. +

    现在打开浏览器开发工具, 找到JavaScript控制台把上面这一句再输入一遍然后回车, 你会看到又弹出了一次!有点意思... — 现在我们有了一个能够重复调用的函数,只要你高兴可以随时调用它。

    + +

    但是,这玩意有什么用呢?在真实的应用当中这样的消息提示框一般用来提示一些什么新的东西, 或者是出现了一个什么错误, 或者当用户删除配置文件的时候("你确定要这样做?"), 或者用户添加一个新的联系人之后提示操作成功..等等。 在这个例子里面, 当用户点击这个按钮的时候这个提示框会出现。

    +
  4. +
  5. 删掉你之前加的那一行代码。
  6. +
  7. 下一步我们用选择器找到这个按钮并赋值给一个常量。 在你的函数定义之前把这行代码加上去: +
    const btn = document.querySelector('button');
    +
  8. +
  9. 最后,把这行代码加在上面这行的下面: +
    btn.onclick = displayMessage;
    + 跟关闭按钮类似closeBtn.onclick... , 当按钮被点击的时候我们运行了点代码。 但不同的是, 之前等号的右边是一个匿名函数,看起来是这样的:btn.onclick = function(){...}, 我们现在是直接使用函数名称来调用。
  10. +
  11. 保存好以后刷新页面 — 现在你应该能看到当你点击按钮的时候提示框弹出来。
  12. +
+ +

你会想“怎么函数名后面没有括号呢?”. 这是因为我们不想直接调用这个函数 — 而是只有当按钮被点击的时候才调用这个函数。 试试把代码改成这样:

+ +
btn.onclick = displayMessage();
+ +

保存刷新, 你会发现按钮都还没点击提示框就出来了! 在函数名后面的这个括号叫做“函数调用运算符”(function invocation operator)。你只有在想直接调用函数的地方才这么写。 同样要重视的是, 匿名函数里面的代码也不是直接运行的, 只要代码在函数作用域内。

+ +

如果你做了这个函数括号的实验, 在继续之前把代码恢复到之前的状态。

+ +

使用参数列表改进函数

+ +

就现在看来,我们的函数还不是特别有用 — 我们想要的不仅仅是每点击一次展示一个默认的消息。我们来改造下我们的函数,给它添加几个参数, 允许我们以不同的方式调用这个函数。

+ +
    +
  1. 第一步,修改函数的第一行代码: +
    function displayMessage() {
    + +

    改成这样的:

    + +
    function displayMessage(msgText, msgType) {
    + 当我们调用函数的时候,我们可以在括号里添加两个变量,来指定显示在消息框里面的消息,和消息的类型。
  2. +
  3. 为了使用第一个参数, 把接下来的一行: +
    msg.textContent = 'This is a message box';
    + +

    改成这样:

    + +
    msg.textContent = msgText;
    +
  4. +
  5. 最后但同样重要的一点, 我们来调用这个函数,并且使用了带参数的形式,修改下面这行: +
    btn.onclick = displayMessage;
    + +

    改成这样:

    + +
    btn.onclick = function() {
    +  displayMessage('Woo, this is a different message!');
    +};
    + 如果我们要在点击事件里面绑定这个新函数,我们不能直接使用(btn.onclick = displayMessage('Woo, this is a different message!');)前面已经讲过— 我们要把它放在一个匿名函数里面,不然函数会直接调用,而不是按钮点击之后才会调用,这不是我们想要的结果。
  6. +
  7. 保存刷新, 就像你所期待的那样现在你可以随意的指定消息框里面显示的消息!
  8. +
+ +

一个更加复杂的参数

+ +

刚才我们只使用了我们定义的第一个参数msgText,对于第二个参数msgType,这个就涉及了稍微多一点的东西— 我们要设置一些依赖于这个 msgType 参数的东西, 我们的函数将会显示不同的图标和不同的背景颜色。

+ +
    +
  1. 第一步, 从Github上下载我们需要的图标 (警告图标 和 聊天图标) 。 把图标保存在一个叫做icons 的文件夹下,和你的HTML文件在同一个目录下。 + +
    笔记: 警告和聊天图标是在这个网站iconfinder.com上找到的, 设计者是 Nazarrudin Ansyari. 感谢他!
    +
  2. +
  3. 下一步, 找到页面的CSS文件. 我们要修改下以便我们使用图标. 首先, 修改 .msgBox 的宽度: +
    width: 200px;
    + 改成: + +
    width: 242px;
    +
  4. +
  5. 下一步, 在 .msgBox p { ... } 里面添加几条新规则: +
    padding-left: 82px;
    +background-position: 25px center;
    +background-repeat: no-repeat;
    +
  6. +
  7. CSS改完了以后我们就要来修改函数 displayMessage() 让它能够显示图标. 在你的函数结束符之前}添加下面这几行代码: +
    if (msgType === 'warning') {
    +  msg.style.backgroundImage = 'url(icons/warning.png)';
    +  panel.style.backgroundColor = 'red';
    +} else if (msgType === 'chat') {
    +  msg.style.backgroundImage = 'url(icons/chat.png)';
    +  panel.style.backgroundColor = 'aqua';
    +} else {
    +  msg.style.paddingLeft = '20px';
    +}
    + 来解释下, 如果第二个参数 msgType 的值为 'warning', 我们的消息框将显示一个警告图标和一个红色的背景. 如果这个参数的值是 'chat', 将显示聊天图标和水蓝色的背景. 如果 msgType 没有指定任何值 (或者不是'warning''chat'), 然后这个 else { ... } 代码块将会被执行, 代码的意思是给消息段落设置了一个简单的左内边距并且没有图标, 也没有背景颜色。这么做是为了当没有提供 msgType 参数的时候给函数一个默认行为, 意思是这是一个可选参数(你没发现?其实我们已经用过了!就在这里btn.onclick = function() { displayMessage('Woo, this is a different message!'); };只是当时我们没有写这个else段,也就是啥操作也没做)!
  8. +
  9. 现在来测试下我们的新函数, 可以直接调用 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');
    + 你能看到我们现在的函数稍微有了点用 (不是非常有用) ,一个小的新功能被我们写出来了(当然,函数可以做很多你想的到的和想不到的事)!
  10. +
+ +
+

注意: 如果你写这个例子遇到了困难, 在这里查看免费的代码 完整版本的代码 (或者在线运行的完整代码), 也可以向我们寻求帮助。

+
+ +

测试你的技能!

+ +

你已经来到了本文章的结尾,但是你还能记得最重要的知识吗?你可以在离开这里找到一些更深度的测试来证实你已经记住了这些知识——查看测试你的技能:函数(英文)。后两章文本包含了这个测试需要的技能,所以你可能先需要阅读再尝试该测试。

+ +

结论

+ +

恭喜你,终于到了这里(等你好久了)! 这篇文章介绍了如何写一个自定义函数, 要把这个新技能在真实项目中使用上你可能还要花点功夫。 下一篇文章中我们将会介绍函数的另一个相关概念 — 返回值。

+ + + +

{{PreviousMenuNext("Learn/JavaScript/Building_blocks/Functions","Learn/JavaScript/Building_blocks/Return_values", "Learn/JavaScript/Building_blocks")}}

+ +

在这个模块中

+ + 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 +--- +
{{LearnSidebar}}
+ +
{{NextMenu("Learn/JavaScript/Building_blocks/Looping_code", "Learn/JavaScript/Building_blocks")}}
+ +

在任何的编程语言中,代码需要依靠不同的输入作出决定并且采取行动。例如,在游戏中,如果玩家的生命值变成了0,那么游戏就结束了。在天气应用中,如果在早晨运行,就显示一张日出的图片;如果在晚上,就显示星星和月亮的图片。在这篇文章中,我们将探索在JavaScript中所谓的条件语句是怎样工作的。

+ + + + + + + + + + + + +
预备知识:基本的计算机知识,对HTML和CSS有基本的了解,JavaScript的第一步
目标:了解怎样在JavaScript中使用条件语句的结构。
+ +

只需一个条件你就可以拥有……!

+ +

人类(以及其他的动物)无时无刻不在做决定,这些决定都影响着他们的生活,从小事(“我应该吃一片还是两片饼干”)到重要的大事(“我应该留在我的祖国,在我父亲的农场工作;还是应该去美国学习天体物理学”)。

+ +

条件语句结构允许我们来描述在JavaScript中这样的选择,从不得不作出的选择(例如:“一片还是两片”)到产生的结果或这些选择(也许是“吃一片饼干”可能会“仍然感觉饿”,或者是“吃两片饼干”可能会“感觉饱了,但妈妈会因为我吃掉了所有的饼干而骂我”。)

+ +

+ +

if ... else 语句

+ +

让我们看看到目前为止你将会在JavaScript中用到的最常见的条件语句类型 — if ... else语句

+ +

基本的的 if…else 语法

+ +

基本的if…else语法看起来像下面的 {{glossary("伪代码")}}:

+ +
if (condition) {
+  code to run if condition is true
+} else {
+  run some other code instead
+}
+ +

在这里我们有:

+ +
    +
  1. 关键字 if,并且后面跟随括号。
  2. +
  3. 要测试的条件,放到括号里(通常是“这个值大于另一个值吗”或者“这个值存在吗”)。这个条件会利用比较运算符(我们会在最后的模块中讨论)进行比较,并且返回true或者false。
  4. +
  5. 一组花括号,在里面我们有一些代码——可以是任何我们喜欢的代码,并且只会在条件语句返回true的时候运行。
  6. +
  7. 关键字else。
  8. +
  9. 另一组花括号,在里面我们有一些代码——可以是任何我们喜欢的代码,并且当条件语句返回值不是true的话,它才会运行。
  10. +
+ +

这段代码真的非常易懂——它说“如果(if)条件(condition)返回true,运行代码A,否则(else)运行代码B”

+ +

注意:你不一定需要else和第二个花括号——下面的代码也是符合语法规则的:

+ +
if (condition) {
+  code to run if condition is true
+}
+
+run some other code
+ +

不过,这里你需要注意——在这种情况下,第二段代码不被条件语句控制,所以它总会运行,不管条件返回的是true还是false。这不一定是一件坏事,但这可能不是你想要的——你经常只想要运行一段代码或者另一段,而不是两个都运行。

+ +

最后,有时候你可能会看到 if…else 语句没有写花括号,像下面的速记风格:

+ +
if (condition) code to run if condition is true
+else run some other code instead
+ +

这是完全有效的代码,但不建议这样使用——因为如果有花括号进行代码切割的话,整体代码被切割为多行代码,更易读和易用。

+ +

一个真实的例子

+ +

为了更好的理解这种语法,让我们考虑一个真实的例子。想像一个孩子被他的父母要求帮助他们做家务。父母可能会说“嗨,宝贝儿,如果你帮我去购物,我会给你额外的零花钱,这样你就能买得起你想要的玩具了。”在JavaScript中,我们可以这样表示:

+ +
var shoppingDone = false;
+
+if (shoppingDone === true) {
+  var childsAllowance = 10;
+} else {
+  var childsAllowance = 5;
+}
+ +

这段代码显示的结果是变量 shoppingDone 总是返回 false, 意味着对我们的穷孩子来说很失望。如果孩子去购物的话,就需要依靠我们提供机制来使父母把变量 shoppingDone 变成 true

+ +
+

Note: 你可以看到在Github上这个例子的完整版本(也可以在线运行

+
+ +

else if

+ +

最后一个例子提供给我们两个选择或结果,但是如果我们想要两个以上呢?

+ +

有一种方法来让你的 if…else 连接你的额外的选择和结果——使用else if 。每一个额外的选择要求放到 if() { ... } 和 else { ... } 里——看看下面更多涉及到的例子,它们属于一个普通的天气预报的应用的一部分。

+ +
<label for="weather">Select the weather type today: </label>
+<select id="weather">
+  <option value="">--Make a choice--</option>
+  <option value="sunny">Sunny</option>
+  <option value="rainy">Rainy</option>
+  <option value="snowing">Snowing</option>
+  <option value="overcast">Overcast</option>
+</select>
+
+<p></p>
+ +
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 = '';
+  }
+}
+
+
+ +

{{ EmbedLiveSample('else_if', '100%', 100) }}

+ +
    +
  1. 这里我们有 HTML {{htmlelement("select")}} 元素让我们选择不同的天气,以及一个简单的段落。
  2. +
  3. 在 JavaScript 中, 我们同时存储了对 {{htmlelement("select")}} 和 {{htmlelement("p")}} 的引用, 并对 <select> 添加了一个事件监听器,因此,当它的值改变时,setWeather()函数被执行。
  4. +
  5. 当函数运行时,我们首先新建了一个 choice 变量去存储当前被选的 <select> 中的值。接着我们用条件判断语句根据 choice 的值选择性的展示段落中的文本。注意 else if() {...}段中的条件是怎么被判断的,除了第一个,它是在 if() {...}中被判断的。
  6. +
  7. 最后一个 else {...} 中的选择通常被叫做 “最后招数”  — 在所有的条件都不为 true 时其中的代码会被执行。在这个例子中,如果用户没有选择任何一个选项,它会将段落中的文本清空,例如当用户决定重新选择最开始出现的"--Make a choice--"选项时,就会有这样的效果。
  8. +
+ +
+

Note: 你可以 在 GitHub 上找到这个例子 (也可以在线运行。)

+
+ +

关于比较运算符

+ +

比较运算符是用来判断条件语句中的条件的。我们先回过头来看看Basic math in JavaScript — numbers and operators 文章中的比较运算符。我们有如下选择:

+ + + +
+

Note: 如果你想复习这些内容,可以回顾之前链接上的材料。

+
+ +

我们想特别提到测试布尔值(true / false),和一个通用模式,你会频繁遇到它,任何不是 false, undefined, null, 0, NaN 的值,或一个空字符串('')在作为条件语句进行测试时实际返回true,因此您可以简单地使用变量名称来测试它是否为真,甚至是否存在(即它不是未定义的)。例如: 

+ +
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.');
+}
+ +

而且,回到我们以前关于孩子为自己的父母做家务的例子,你可以这样写:

+ +
var shoppingDone = false;
+
+if (shoppingDone) { // don't need to explicitly specify '=== true'
+  var childsAllowance = 10;
+} else {
+  var childsAllowance = 5;
+}
+ +

嵌套if ... else

+ +

将另一个if ... else 语句放在另一个中 - 嵌套它是完全可行的。例如,我们可以更新我们的天气预报应用程序,以显示更多的选择,具体取决于温度:

+ +
if (choice === 'sunny') {
+  if (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.';
+  } else if (temperature >= 86) {
+    para.textContent = 'It is ' + temperature + ' degrees outside — REALLY HOT! If you want to go outside, make sure to put some suncream on.';
+  }
+}
+ +

即使代码全部一起工作,每个if ... else语句完全独立于另一个。

+ +

逻辑运算符:&&  , || 和 !

+ +

如果要测试多个条件,而不需要编写嵌套if ... else语句,逻辑运算符可以帮助您。当在条件下使用时,前两个执行以下操作:

+ + + +

举一个逻辑 && 的例子, 刚才的那段代码片段可以写成下面这样:

+ +
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.';
+} else if (choice === 'sunny' && temperature >= 86) {
+  para.textContent = 'It is ' + temperature + ' degrees outside — REALLY HOT! If you want to go outside, make sure to put some suncream on.';
+}
+ +

所以,只有当choice === 'sunny'并且temperature < 86都返回true时,第一个代码块才能运行。

+ +

让我们快速看一个 || 的例子:

+ +
if (iceCreamVanOutside || houseStatus === 'on fire') {
+  console.log('You should leave the house quickly.');
+} else {
+  console.log('Probably should just stay in then.');
+}
+ +

最后一种类型的逻辑运算符,  逻辑非!  运算符表示, 可以用于对一个表达式取否. 让我们把  非运算符 结合上一个例子里的  或表达式 看看:

+ +
if (!(iceCreamVanOutside || houseStatus === 'on fire')) {
+  console.log('Probably should just stay in then.');
+} else {
+  console.log('You should leave the house quickly.');
+}
+ +

在这一段代码中,如果逻辑或所在的语句返回 true,则非运算符会将其取否,于是整个表达式的返回值将会是false

+ +

您可以在任何结构中随意合并很多个逻辑表达式。接下来的例子将会只在或运算符两边的语句同时返回true时才会执行代码,这也就意味着整个与运算符语句将会返回true:

+ +
if ((x === 5 || y > 3 || z <= 10) && (loggedIn || userName === 'Steve')) {
+  // run the code
+}
+ +

在条件语句中运用或逻辑运算符最常见的错误是尝试声明变量后,仅检查该变量一次的情况下赋予很多个都会返回true的值,不同的值之间用 || (或)运算符分隔。比如:

+ +
if (x === 5 || 7 || 10 || 20) {
+  // run my code
+}
+ +

在这个例子里 if(...) 里的条件总为真,因为 7 (或者其它非零的数) 的值总是为真. 这个条件实际意思是 "如果x等于5, 或者7为真 — 它总是成立的". 这不是我们想要的逻辑,为了 让它正常工作你必须指定每个或表达式两边都是完整的检查:

+ +
if (x === 5 || x === 7 || x === 10 ||x === 20) {
+  // run my code
+}
+ +

switch语句

+ +

if...else 语句能够很好地实现条件代码,但是它们不是没有缺点。 它们主要适用于您只有几个选择的情况,每个都需要相当数量的代码来运行,和/或 的条件很复杂的情况(例如多个逻辑运算符)。 对于只想将变量设置一系列为特定值的选项或根据条件打印特定语句的情况,语法可能会很麻烦,特别是如果您有大量选择。

+ +

switch 语句在这里是您的朋友 - 他们以单个表达式/值作为输入,然后查看多个选项,直到找到与该值相匹配的选项,执行与之相关的代码。 这里有一些伪代码,可以给你一点灵感:

+ +
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
+}
+ +

这里我们得到:

+ +
    +
  1. 关键字 switch, 后跟一组括号.
  2. +
  3. 括号内的表达式或值.
  4. +
  5. 关键字 case, 后跟一个选项的表达式/值,后面跟一个冒号.
  6. +
  7. 如果选择与表达式匹配,则运行一些代码.
  8. +
  9. 一个 break 语句, 分号结尾. 如果先前的选择与表达式/值匹配,则浏览器在此停止执行代码块,并执行switch语句之后的代码.
  10. +
  11. 你可以添加任意的 case 选项(选项3-5).
  12. +
  13. 关键字 default, 后面跟随和 case 完全相同的代码模式 (选项 3–5), except that default 之后不需要再有选项, 并且您不需要 break 语句, 因为之后没有任何运行代码. 如果之前没有选项匹配,则运行default选项.
  14. +
+ +
+

Note: default 部分不是必须的 - 如果表达式不可能存在未知值,则可以安全地省略它。 如果有机会,您需要包括它来处理未知的情况。

+
+ +

switch语句示例

+ +

我们来看一个真实的例子 - 我们将重写天气预报应用程序,以改用switch语句:

+ +
<label for="weather">Select the weather type today: </label>
+<select id="weather">
+  <option value="">--Make a choice--</option>
+  <option value="sunny">Sunny</option>
+  <option value="rainy">Rainy</option>
+  <option value="snowing">Snowing</option>
+  <option value="overcast">Overcast</option>
+</select>
+
+<p></p>
+ +
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 = '';
+  }
+}
+ +

{{ EmbedLiveSample('A_switch_example', '100%', 100) }}

+ +
+

Note: 你可以 在 GitHub 上找到这个例子 (也可以在线运行。)

+
+ +

三元运算符

+ +

在我们举一些例子之前,我们要介绍一下最后一句语法。三元或条件运算符是一个语法的小点,用于测试一个条件,并返回一个值/表达,如果它是true,另一个是false-这种情况下是有用的,并且可以占用比if...else块较少的代码块。如果你只有两个通过truefalse条件选择伪代码看起来像这样:

+ +
( condition ) ? run this code : run this code instead
+ +

所以我们来看一个简单的例子:

+ +
var greeting = ( isBirthday ) ? 'Happy birthday Mrs. Smith — we hope you have a great day!' : 'Good morning Mrs. Smith.';
+ +

在这里我们有一个变量叫做isBirthday- 如果它是true,我们给客人一个生日快乐的消息; 如果不是,我们给她标准的每日问候。

+ +

三元运算符示例

+ +

你不需要用三元运算符设置变量值; 你也可以运行任何你喜欢的函数或代码行。以下实例显示了一个简单的主题选择器,其中该站点的样式应用了三元运算符。

+ +
<label for="theme">Select theme: </label>
+<select id="theme">
+  <option value="white">White</option>
+  <option value="black">Black</option>
+</select>
+
+<h1>This is my website</h1>
+ +
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');
+}
+
+ +

{{ EmbedLiveSample('Ternary_operator_example', '100%', 300) }}

+ +

在这里,我们有一个<select>选择主题(黑色或白色)元素,加上一个简单<h1>的显示网站标题。我们也有一个函数叫做update(),它将两种颜色作为参数(输入)。网站的背景颜色设置为第一个提供的颜色,其文本颜色设置为第二个提供的颜色。

+ +

最后,我们还有一个onchange事件监听器,用于运行一个包含三元运算符的函数。它以测试条件开始select.value === 'black'如果这返回true,我们运行update()带有黑色和白色参数函数,这意味着我们最终得到黑色的背景颜色和白色的文字颜色。如果返回false,我们运行update()带有白色和黑色参数函数,这意味着站点颜色被反转。

+ +
+

Note: 你可以 在 GitHub 上找到这个例子 (也可以在线运行。)

+
+ +

主动学习:一个简单的日历

+ +

在这个例子中,您将帮助我们完成一个简单的日历应用程序。在你的代码中:

+ + + +

我们需要你在onchange处理函数中写一个条件语句,就在// ADD CONDITIONAL HERE任务的下面  这应该:

+ +
    +
  1. 查看所选月份(存储在choice变量中,这将是<select>值更改后的元素值,例如“1月”)。
  2. +
  3. 设置一个被调用days为等于所选月份天数的变量为此,您必须查看一年中每个月的天数。为了这个例子的目的,你可以忽略闰年。
  4. +
+ +

提示:

+ + + +

如果您犯了错误,您可以随时使用“Reset”按钮重置该示例。如果真的卡住了,请按“Show solution”查看解决方案。

+ + + +

{{ EmbedLiveSample('Playable_code', '100%', 1110) }}

+ +

主动学习:更多颜色选择!

+ +

在这个例子中,您将要采取我们前面看到的三元运算符示例,并将三元运算符转换为一个switch语句,这将允许我们对简单的网站应用更多的选择。看看<select>- 这次你会看到它不是两个主题选项,而是五个。您需要在// ADD SWITCH STATEMENT注释下面添加一个switch语句

+ + + +

如果您犯了错误,您可以随时使用“Reset”按钮重置该示例。如果真的卡住了,请按“Show solution”查看解决方案。

+ + + +

{{ EmbedLiveSample('Playable_code_2', '100%', 850) }}

+ +

结论

+ +

这就是现在您真正需要了解的JavaScript中的条件结构!我相信你会理解这些概念,并轻松地通过这些例子; 如果有什么不明白的,请随时阅读文章,或者联系我们寻求帮助。

+ +

参见

+ + + +

{{NextMenu("Learn/JavaScript/Building_blocks/Looping_code", "Learn/JavaScript/Building_blocks")}}

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 +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/JavaScript/Building_blocks/Return_values","Learn/JavaScript/Building_blocks/Image_gallery", "Learn/JavaScript/Building_blocks")}}
+ +

事件是您在编程时系统内发生的动作或者发生的事情,系统响应事件后,如果需要,您可以某种方式对事件做出回应。例如:如果用户在网页上单击一个按钮,您可能想通过显示一个信息框来响应这个动作。在这篇文章中,我们将讨论一些关于事件的重要概念,并且观察它们在浏览器上如何运行。这篇文章不会面面俱到,仅聚焦于您现阶段需要掌握的知识。

+ + + + + + + + + + + + +
前提:基本电脑知识, 对HTML和CSS的基本了解,及 JavaScript first steps.
目标:了解事件的基本理论,它们怎么在浏览器上运行的,以及在不同的编程环境下事件有何不同。
+ +

一系列事件

+ +

就像上面提到的, 事件是您在编程时系统内发生的动作或者发生的事情——系统会在事件出现时产生或触发某种信号,并且会提供一个自动加载某种动作(列如:运行一些代码)的机制,比如在一个机场,当跑道清理完成,飞机可以起飞时,飞行员会收到一个信号,因此他们开始起飞。

+ +

+ +

在 Web 中, 事件在浏览器窗口中被触发并且通常被绑定到窗口内部的特定部分 — 可能是一个元素、一系列元素、被加载到这个窗口的 HTML 代码或者是整个浏览器窗口。举几个可能发生的不同事件:

+ + + +

如果您想看看更多其他的事件 ,请移步至MDN的Event reference

+ +

每个可用的事件都会有一个事件处理器,也就是事件触发时会运行的代码块。当我们定义了一个用来回应事件被激发的代码块的时候,我们说我们注册了一个事件处理器。注意事件处理器有时候被叫做事件监听器——从我们的用意来看这两个名字是相同的,尽管严格地来说这块代码既监听也处理事件。监听器留意事件是否发生,然后处理器就是对事件发生做出的回应。

+ +
+

注: 网络事件不是 JavaScript 语言的核心——它们被定义成内置于浏览器的 JavaScript APIs。

+
+ +

一个简单的例子

+ +

让我们看一个简单的例子。前面您已经见到过很多事件和事件监听器,现在我们概括一下以巩固我们的知识。在接下来的例子中,我们的页面中只有一个 button,按下时,背景会变成随机的一种颜色。

+ +
<button>Change color</button>
+ + + +

JavaScript代码如下所示:

+ +
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;
+}
+ +

我们使用 btn 变量存储 button,并使用了Document.querySelector() 函数。我们也定义了一个返回随机数字的函数。代码第三部分就是事件处理器。btn变量指向 button 元素,在 button 这种对象上可触发一系列的事件,因此也就可以使用事件处理器。我们通过将一个匿名函数(这个赋值函数包括生成随机色并赋值给背景色的代码)赋值给“点击”事件处理器参数,监听“点击”这个事件。

+ +

只要点击事件在<button>元素上触发,该段代码就会被执行。即每当用户点击它时,都会运行此段代码。

+ +

示例输出如下:

+ +

{{ EmbedLiveSample('A_simple_example', '100%', 200, "", "", "hide-codepen-jsfiddle") }}

+ +

这不仅应用在网页上

+ +

值得注意的是并不是只有 JavaScript 使用事件——大多的编程语言都有这种机制,并且它们的工作方式不同于 JavaScript。实际上,JavaScript 网页上的事件机制不同于在其他环境中的事件机制。

+ +

比如, Node.js 是一种非常流行的允许开发者使用 JavaScript 来建造网络和服务器端应用的运行环境。Node.js event model 依赖定期监听事件的监听器和定期处理事件的处理器——虽然听起来好像差不多,但是实现两者的代码是非常不同的,Node.js 使用像 on ( ) 这样的函数来注册一个事件监听器,使用 once ( ) 这样函数来注册一个在运行一次之后注销的监听器。 HTTP connect event docs 提供了很多例子。

+ +

另外一个例子:您可以使用 JavaScript 来开发跨浏览器的插件(使用 WebExtensions 开发技术。事件模型和网站的事件模型是相似的,仅有一点点不同——事件监听属性是大驼峰的(如onMessage而不是onmessage),还需要与 addListener 函数结合, 参见 runtime.onMessage page 上的一个例子。

+ +

您现在不需要掌握这些,我们只想表明不同的编程环境下事件机制是不同的,

+ +

使用网页事件的方式

+ +

您可以通过多种不同的方法将事件侦听器代码添加到网页,以便在关联的事件被触发时运行它。在本节中,我们将回顾不同的机制,并讨论应该使用哪些机制。

+ +

事件处理器属性

+ +

这些是我们的课程中最常见到的代码 - 存在于事件处理程序过程的属性中。回到上面的例子:

+ +
const btn = document.querySelector('button');
+
+btn.onclick = function() {
+  const rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
+  document.body.style.backgroundColor = rndCol;
+}
+ +

这个 onclick 是被用在这个情景下的事件处理器的属性,它就像 button 其他的属性(如 btn.textContent, or btn.style), 但是有一个特别的地方——当您将一些代码赋值给它的时候,只要事件触发代码就会运行。

+ +

您也可以将一个有名字的函数赋值给事件处理参数(正如我们在 Build your own function 中看到的),下面的代码也是这样工作的:

+ +
const btn = document.querySelector('button');
+
+function bgChange() {
+  const rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
+  document.body.style.backgroundColor = rndCol;
+}
+
+btn.onclick = bgChange;
+ +

有很多事件处理参数可供选择,我们来做一个实验。

+ +

首先将 random-color-eventhandlerproperty.html 复制到本地,然后用浏览器打开。别慌,这只是我们之前已经进行过的一个简单随机颜色的示例的代码复制。将 btn.onclick 依次换成其他值,在浏览器中观察效果。

+ + + +

一些事件非常通用,几乎在任何地方都可以用(比如 onclick 几乎可以用在几乎每一个元素上),然而另一些元素就只能在特定场景下使用,比如我们只能在 video 元素上使用 onplay 。

+ +

行内事件处理器 - 请勿使用

+ +

你也许在你的代码中看到过这么一种写法:

+ +
<button onclick="bgChange()">Press me</button>
+
+ +
function bgChange() {
+  const rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
+  document.body.style.backgroundColor = rndCol;
+}
+ +
+

Note: 您可以在GitHub上找到这个示例的完整源代码(也可以在线运行).

+
+ +

在Web上注册事件处理程序的最早方法是类似于上面所示的事件处理程序HTML属性(也称为内联事件处理程序)—属性值实际上是当事件发生时要运行的JavaScript代码。上面的例子中调用一个在{{htmlelement("script")}}元素在同一个页面上,但也可以直接在属性内插入JavaScript,例如:

+ +
<button onclick="alert('Hello, this is my old-fashioned event handler!');">Press me</button>
+ +

你会发现HTML属性等价于对许多事件处理程序的属性;但是,你不应该使用这些 —— 他们被认为是不好的做法。使用一个事件处理属性似乎看起来很简单,如果你只是在做一些非常快的事情,但很快就变得难以管理和效率低下。

+ +

一开始,您不应该混用 HTML 和 JavaScript,因为这样文档很难解析——最好的办法是只在一块地方写 JavaScript 代码。

+ +

即使在单一文件中,内置事件处理器也不是一个好主意。一个按钮看起来还好,但是如果有一百个按钮呢?您得在文件中加上100个属性。这很快就会成为维护人员的噩梦。使用 Java Script,您可以给网页中的 button 都加上事件处理器。就像下面这样:

+ +
const buttons = document.querySelectorAll('button');
+
+for (let i = 0; i < buttons.length; i++) {
+  buttons[i].onclick = bgChange;
+}
+ +
+

注释: 将您的编程逻辑与内容分离也会让您的站点对搜索引擎更加友好。

+
+ +

addEventListener() 和removeEventListener()

+ +

新的事件触发机制被定义在 Document Object Model (DOM) Level 2 Events Specification, 这个细则给浏览器提供了一个函数 — addEventListener()。这个函数和事件处理属性是类似的,但是语法略有不同。我们可以重写上面的随机颜色背景代码:

+ +
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);
+ +
+

注释: 您可以在Github上找到这个示例的完整源代码(也可以 在线运行)。

+
+ +

在addEventListener() 函数中, 我们具体化了两个参数——我们想要将处理器应用上去的事件名称,和包含我们用来回应事件的函数的代码。注意将这些代码全部放到一个匿名函数中是可行的:

+ +
btn.addEventListener('click', function() {
+  var rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
+  document.body.style.backgroundColor = rndCol;
+});
+
+ +

这个机制带来了一些相较于旧方式的优点。有一个相对应的方法,removeEventListener()这个方法移除事件监听器。例如,下面的代码将会移除上个代码块中的事件监听器:

+ +
btn.removeEventListener('click', bgChange);
+ +

在这个简单的、小型的项目中可能不是很有用,但是在大型的、复杂的项目中就非常有用了,可以非常高效地清除不用的事件处理器,另外在其他的一些场景中也非常有效——比如您需要在不同环境下运行不同的事件处理器,您只需要恰当地删除或者添加事件处理器即可。

+ +

您也可以给同一个监听器注册多个处理器,下面这种方式不能实现这一点:

+ +
myElement.onclick = functionA;
+myElement.onclick = functionB;
+ +

第二行会覆盖第一行,但是下面这种方式就会正常工作了:

+ +
myElement.addEventListener('click', functionA);
+myElement.addEventListener('click', functionB);
+ +

当元素被点击时两个函数都会工作:

+ +

此外,该事件机制还提供了许多其他强大的特性和选项。这对于本文来说有点超出范围,但是如果您想要阅读它们,请查看addEventListener()removeEventListener()参考页面。

+ +

我该使用哪种机制?

+ +

在三种机制中,您绝对不应该使用HTML事件处理程序属性 - 这些属性已经过时了,而且也是不好的做法,如上所述.

+ +

另外两种是相对可互换的,至少对于简单的用途:

+ + + +

第三种机制(DOM Level 2 Events (addEventListener(), etc.))的主要优点是,如果需要的话,可以使用removeEventListener()删除事件处理程序代码,而且如果有需要,您可以向同一类型的元素添加多个监听器。例如,您可以在一个元素上多次调用addEventListener('click', function() { ... }),并可在第二个参数中指定不同的函数。对于事件处理程序属性来说,这是不可能的,因为后面任何设置的属性都会尝试覆盖较早的属性,例如:

+ +
element.onclick = function1;
+element.onclick = function2;
+etc.
+ +
+

注解:如果您在工作中被要求支持比Internet Explorer 8更老的浏览器,那么您可能会遇到困难,因为这些古老的浏览器会使用与现代浏览器不同的事件处理模型。但是不要害怕,大多数 JavaScript 库(例如 jQuery )都内置了能够跨浏览器差异的函数。在你学习 JavaScript 旅程里的这个阶段,不要太担心这个问题。

+
+ +

其他事件概念

+ +

本节我们将简要介绍一些与事件相关的高级概念。在这一点并不需要完全理解透彻,但它可能有助于你解释一些经常会遇到的代码模式。

+ +

事件对象

+ +

有时候在事件处理函数内部,您可能会看到一个固定指定名称的参数,例如eventevt或简单的e。 这被称为事件对象,它被自动传递给事件处理函数,以提供额外的功能和信息。 例如,让我们稍稍重写一遍我们的随机颜色示例:

+ +
function bgChange(e) {
+  const rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
+  e.target.style.backgroundColor = rndCol;
+  console.log(e);
+}
+
+btn.addEventListener('click', bgChange);
+ +
+

Note: 您可以在Github上查看这个示例的 完整代码 ,或者在这里查看 实时演示

+
+ +

在这里,您可以看到我们在函数中包括一个事件对象e,并在函数中设置背景颜色样式在e.target上 - 它指的是按钮本身。 事件对象 etarget属性始终是事件刚刚发生的元素的引用。 所以在这个例子中,我们在按钮上设置一个随机的背景颜色,而不是页面。

+ +
+

Note: 您可以使用任何您喜欢的名称作为事件对象 - 您只需要选择一个名称,然后可以在事件处理函数中引用它。 开发人员最常使用 e / evt / event,因为它们很简单易记。 坚持标准总是很好。

+
+ +

当您要在多个元素上设置相同的事件处理程序时,e.target非常有用,并且在发生事件时对所有元素执行某些操作.  例如,你可能有一组16块方格,当它们被点击时就会消失。用e.target总是能准确选择当前操作的东西(方格)并执行操作让它消失,而不是必须以更困难的方式选择它。在下面的示例中(请参见useful-eventtarget.html完整代码;也可以在线运行running live)我们使用JavaScript创建了16个<div>元素。接着我们使用 document.querySelectorAll()选择全部的元素,然后遍历每一个,为每一个元素都添加一个onclick单击事件,每当它们点击时就会为背景添加一个随机颜色。

+ +
const divs = document.querySelectorAll('div');
+
+for (let i = 0; i < divs.length; i++) {
+  divs[i].onclick = function(e) {
+    e.target.style.backgroundColor = bgChange();
+  }
+}
+ +

输出如下(试着点击它-玩的开心):

+ + + +

{{ EmbedLiveSample('Hidden_example', '100%', 400) }}

+ +

你遇到的大多数事件处理器的事件对象都有可用的标准属性和函数(方法)(请参阅完整列表 Event 对象引用 )。然而,一些更高级的处理程序会添加一些专业属性,这些属性包含它们需要运行的额外数据。例如,媒体记录器API有一个dataavailable事件,它会在录制一些音频或视频时触发,并且可以用来做一些事情(例如保存它,或者回放)。对应的ondataavailable处理程序的事件对象有一个可用的数据属性。

+ +

阻止默认行为

+ +

有时,你会遇到一些情况,你希望事件不执行它的默认行为。 最常见的例子是Web表单,例如自定义注册表单。 当你填写详细信息并按提交按钮时,自然行为是将数据提交到服务器上的指定页面进行处理,并将浏览器重定向到某种“成功消息”页面(或 相同的页面,如果另一个没有指定。)

+ +

当用户没有正确提交数据时,麻烦就来了 - 作为开发人员,你希望停止提交信息给服务器,并给他们一个错误提示,告诉他们什么做错了,以及需要做些什么来修正错误。 一些浏览器支持自动的表单数据验证功能,但由于许多浏览器不支持,因此建议你不要依赖这些功能,并实现自己的验证检查。 我们来看一个简单的例子。

+ +

首先,一个简单的HTML表单,需要你填入名(first name)和姓(last name)

+ +
<form>
+  <div>
+    <label for="fname">First name: </label>
+    <input id="fname" type="text">
+  </div>
+  <div>
+    <label for="lname">Last name: </label>
+    <input id="lname" type="text">
+  </div>
+  <div>
+     <input id="submit" type="submit">
+  </div>
+</form>
+<p></p>
+ + + +

这里我们用一个onsubmit事件处理程序(在提交的时候,在一个表单上发起submit事件)来实现一个非常简单的检查,用于测试文本字段是否为空。 如果是,我们在事件对象上调用preventDefault()函数,这样就停止了表单提交,然后在我们表单下面的段落中显示一条错误消息,告诉用户什么是错误的:

+ +
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!';
+  }
+}
+ +

显然,这是一种非常弱的表单验证——例如,用户输入空格或数字提交表单,表单验证并不会阻止用户提交——这不是我们例子想要达到的目的。输出如下:

+ +

{{ EmbedLiveSample('Preventing_default_behaviour', '100%', 140) }}

+ +
+

Note: 查看完整的源代码 preventdefault-validation.html (也可以 running live )

+
+ +

事件冒泡及捕获

+ +

最后即将介绍的这个主题你常常不会深究,但如果你不理解这个主题,就会十分痛苦。事件冒泡和捕捉是两种机制,主要描述当在一个元素上有两个相同类型的事件处理器被激活会发生什么。为了容易理解,我们来看一个例子——在新标签页打开这个show-video-box.html 例子(在这里可以查看源码 source code)。也可以在下面查看:

+ + + +

{{ EmbedLiveSample('Hidden_video_example', '100%', 500) }}

+ +

这是一个非常简单的例子,它显示和隐藏一个包含<video>元素的<div>元素:

+ +
<button>Display video</button>
+
+<div class="hidden">
+  <video>
+    <source src="rabbit320.mp4" type="video/mp4">
+    <source src="rabbit320.webm" type="video/webm">
+    <p>Your browser doesn't support HTML5 video. Here is a <a href="rabbit320.mp4">link to the video</a> instead.</p>
+  </video>
+</div>
+ +

当‘’button‘’元素按钮被单击时,将显示视频,它是通过将改变<div>的class属性值从hidden变为showing(这个例子的CSS包含两个class,它们分别控制这个<div>盒子在屏幕上显示还是隐藏。):

+ +
btn.onclick = function() {
+  videoBox.setAttribute('class', 'showing');
+}
+ +

然后我们再添加几个onclick事件处理器,第一个添加在<div>元素上,第二个添加在<video>元素上。这个想法是当视频(<video>)外 <div>元素内这块区域被单击时,这个视频盒子应该再次隐藏;当单击视频(<video>)本身,这个视频将开始播放。

+ +
videoBox.onclick = function() {
+  videoBox.setAttribute('class', 'hidden');
+};
+
+video.onclick = function() {
+  video.play();
+};
+ +

但是有一个问题 - 当您点击video开始播放的视频时,它会在同一时间导致<div>也被隐藏。 这是因为video<div>之内 - video<div>的一个子元素 - 所以点击video实际上是同时也运行<div>上的事件处理程序。

+ +

对事件冒泡和捕捉的解释

+ +

当一个事件发生在具有父元素的元素上(例如,在我们的例子中是<video>元素)时,现代浏览器运行两个不同的阶段 - 捕获阶段和冒泡阶段。 在捕获阶段:

+ + + +

在冒泡阶段,恰恰相反:

+ + + +

+ +

(单击图片可以放大这个图表)

+ +

在现代浏览器中,默认情况下,所有事件处理程序都在冒泡阶段进行注册。因此,在我们当前的示例中,当您单击视频时,这个单击事件从 <video>元素向外冒泡直到<html>元素。沿着这个事件冒泡线路:

+ + + +

用 stopPropagation() 修复问题

+ +

这是令人讨厌的行为,但有一种方法来解决它!标准事件对象具有可用的名为 stopPropagation()的函数, 当在事件对象上调用该函数时,它只会让当前事件处理程序运行,但事件不会在冒泡链上进一步扩大,因此将不会有更多事件处理器被运行(不会向上冒泡)。所以,我们可以通过改变前面代码块中的第二个处理函数来解决当前的问题:

+ +
video.onclick = function(e) {
+  e.stopPropagation();
+  video.play();
+};
+ +

你可以尝试把 show-video-box.html source code 拷贝到本地,然后自己动手修复它,或者在 show-video-box-fixed.html 页面查看修复结果(也可以在这里 source code 查看源码)。

+ +
+

注解: 为什么我们要弄清楚捕捉和冒泡呢?那是因为,在过去糟糕的日子里,浏览器的兼容性比现在要小得多,Netscape(网景)只使用事件捕获,而Internet Explorer只使用事件冒泡。当W3C决定尝试规范这些行为并达成共识时,他们最终得到了包括这两种情况(捕捉和冒泡)的系统,最终被应用在现在浏览器里。

+
+ +
+

注解: 如上所述,默认情况下,所有事件处理程序都是在冒泡阶段注册的,这在大多数情况下更有意义。如果您真的想在捕获阶段注册一个事件,那么您可以通过使用addEventListener()注册您的处理程序,并将可选的第三个属性设置为true。

+
+ +

事件委托

+ +

冒泡还允许我们利用事件委托——这个概念依赖于这样一个事实,如果你想要在大量子元素中单击任何一个都可以运行一段代码,您可以将事件监听器设置在其父节点上,并让子节点上发生的事件冒泡到父节点上,而不是每个子节点单独设置事件监听器。

+ +

一个很好的例子是一系列列表项,如果你想让每个列表项被点击时弹出一条信息,您可以将click单击事件监听器设置在父元素<ul>上,这样事件就会从列表项冒泡到其父元素<ul>上。

+ +

这个的概念在David Walsh的博客上有更多的解释,并有多个例子——看看How JavaScript Event Delegation Works.

+ +

结论

+ +

现在您应该知道在这个早期阶段您需要了解的所有web事件。如上所述,事件并不是JavaScript的核心部分——它们是在浏览器Web APIs中定义的。

+ +

另外,理解JavaScript在不同环境下使用不同的事件模型很重要——从Web api到其他领域,如浏览器WebExtensions和Node.js(服务器端JavaScript)。我们并不期望您现在了解所有这些领域,但是当您在学习web开发的过程中,理解这些事件的基础是很有帮助的。

+ +

如果你有什么不明白的地方,请重新阅读这篇文章,或者联系contact us我们寻求帮助。

+ +

参见

+ + + +

{{PreviousMenuNext("Learn/JavaScript/Building_blocks/Return_values","Learn/JavaScript/Building_blocks/Image_gallery", "Learn/JavaScript/Building_blocks")}}

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 +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/JavaScript/Building_blocks/Looping_code","Learn/JavaScript/Building_blocks/Build_your_own_function", "Learn/JavaScript/Building_blocks")}}
+ +

在JavaScript中另一个基本概念是函数, 它允许你在一个代码块中存储一段用于处理单任务的代码,然后在任何你需要的时候用一个简短的命令来调用,而不是把相同的代码写很多次。在本文中,我们将探索函数的基本概念,如基本语法、如何定义和调用、范围和参数。

+ + + + + + + + + + + + +
前提条件:基本的电脑知识,对HTML与CSS有基本的了解,及已阅读: JavaScript first steps(JS的入门)。
目标:了解 Javascript 函数背后的基本概念。
+ +

我能在哪找到函数?

+ +

在 JavaScript中, 你将发现函数无处不在 。事实上, 到目前为止,我们一直在使用函数,只是我们还没很好的讨论它们。然而现在是时候了,让我们开始聊聊函数,并探索它们的语法。

+ +

几乎任何时候,只要你使用一个带有一对圆括号()的JavaScript结构,并且你不是在使用一个常见的比如for for循环,while或do…while循环,或者if语句这样的内置语言结构时,那么您就正在使用函数。

+ + + +

浏览器内置函数

+ +

在这套课程中我们已经使用了很多浏览器内置函数,当我们操作一个字符串的时候,例如:

+ +
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
+ +

或者当我们操作一个数组的时候:

+ +
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
+ +

或者当我们生成一个随机数时:

+ +
var myNumber = Math.random()
+// the random() function generates a random
+// number between 0 and 1, and returns that
+// number
+ +

...我们已经使用过函数了!

+ +
+

提示:如果需要,你可以随意将这些代码输入浏览器控制台以便于你熟悉其功能。

+
+ +

JavaScript有许多内置的函数,可以让您做很多有用的事情,而无需自己编写所有的代码。事实上, 许多你调用(运行或者执行的专业词语)浏览器内置函数时调用的代码并不是使用JavaScript来编写——大多数调用浏览器后台的函数的代码,是使用像C++这样更低级的系统语言编写的,而不是像JavaScript这样的web编程语言。

+ +

请记住,这些内置浏览器函数不是核心JavaScript语言的一部分——被定义为浏览器API的一部分,它建立在默认语言之上,以提供更多的功能(请参阅本课程的早期部分以获得更多的描述)。我们将在以后的模块中更详细地使用浏览器API。

+ +

函数与方法

+ +

程序员把函数称为对象方法(method)的一部分。你还不必了解JavaScript中已建构的对象在更深层次上是如何运作的——你可以等到下一小节,我们会教给你有关对象运作方式的一切。在我们继续前进之前,我们需要澄清一些有关方法和函数概念之间可能存在的误会——当你在网络上浏览相关信息的时候,你很可能会碰上这两个术语。

+ +

到目前为止我们所使用的内置代码同属于这两种形式:函数和方法。你可以在这里查看内置函数,内置对象以及其相关方法的完整列表。

+ +

严格说来,内置浏览器函数并不是函数——它们是方法这听起来有点可怕和令人困惑,但不要担心 ——函数和方法在很大程度上是可互换的,至少在我们的学习阶段是这样的

+ +

二者区别在于方法是在对象内定义的函数。浏览器内置函数(方法)和变量(称为属性)存储在结构化对象内,以使代码更加高效,易于处理。

+ + + +

自定义函数

+ +

您在过去的课程中还看到很多定制功能 - 在代码中定义的功能,而不是在浏览器中。每当您看到一个自定义名称后面都带有括号,那么您使用的是自定义函数. 在我们循环文章中random-canvas-circles.html示例(另见完整的源代码)中,我们包括一个如下所示的自定义函数:draw()

+ +
function draw() {
+  ctx.clearRect(0,0,WIDTH,HEIGHT);
+  for (var i = 0; i < 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();
+  }
+}
+ +

该函数在<canvas>元素中绘制100个随机圆每次我们想要这样做,我们可以使用这个函数来调用这个功能

+ +
draw();
+ +

而不是每次我们想重复一遍,都要写出所有的代码。函数可以包含任何您喜欢的代码 - 甚至可以从内部函数调用其他函数。以上函数例如调用random()函数三次,由以下代码定义:

+ +
function random(number) {
+  return Math.floor(Math.random()*number);
+}
+ +

我们需要这个函数,因为浏览器的内置Math.random()函数只生成一个0到1之间的随机十进制数。我们想要一个0到一个指定数字之间的随机整数。

+ + + +

调用函数

+ +

现在你可能很清楚这一点,但仅仅为了防止……,要在函数定义之后,实际使用它,你必须运行或调用它。这是通过将函数名包含在代码的某个地方,后跟圆括号来完成的。

+ +
function myFunction() {
+  alert('hello');
+}
+
+myFunction()
+// calls the function once
+ +

匿名函数

+ +

您可能会以稍微不同的方式看到定义和调用的函数。到目前为止,我们刚刚创建了如下函数:

+ +
function myFunction() {
+  alert('hello');
+}
+ +

但是您也可以创建一个没有名称的函数:

+ +
function() {
+  alert('hello');
+}
+ +

这个函数叫做匿名函数 — 它没有函数名! 它也不会自己做任何事情。 你通常将匿名函数与事件处理程序一起使用, 例如,如果单击相关按钮,以下操作将在函数内运行代码:

+ +
var myButton = document.querySelector('button');
+
+myButton.onclick = function() {
+  alert('hello');
+}
+ +

上述示例将要求{{htmlelement("button")}} 在页面上提供可用于选择并单击元素。您在整个课程中已经看到过这种结构了几次,您将在下一篇文章中了解更多信息并在其中使用。

+ +

你还可以将匿名函数分配为变量的值,例如:

+ +
var myGreeting = function() {
+  alert('hello');
+}
+ +

现在可以使用以下方式调用此函数:

+ +
myGreeting();
+ +

有效地给变量一个名字;还可以将该函数分配为多个变量的值,例如:

+ +
var anotherGreeting = function() {
+  alert('hello');
+}
+ +

现在可以使用以下任一方法调用此函数

+ +
myGreeting();
+anotherGreeting();
+ +

但这只会令人费解,所以不要这样做!创建方法时,最好坚持下列形式:

+ +
function myGreeting() {
+  alert('hello');
+}
+ +

您将主要使用匿名函数来运行负载的代码以响应事件触发(如点击按钮) - 使用事件处理程序。再次,这看起来像这样:

+ +
myButton.onclick = function() {
+  alert('hello');
+  // I can put as much code
+  // inside here as I want
+}
+ +
+

匿名函数也称为函数表达式。函数表达式与函数声明有一些区别。函数声明会进行声明提升(declaration hoisting),而函数表达式不会。

+
+ +

函数参数

+ +

一些函数需要在调用它们时指定参数 ——这些参数值需要放在函数括号内,才能正确地完成其工作。

+ +
+

Note: 参数有时称为参数(arguments),属性(properties)或甚至属性(attributes)

+
+ +

例如,浏览器的内置Math.random()函数不需要任何参数。当被调用时,它总是返回0到1之间的随机数:

+ +
var myNumber = Math.random();
+ +

浏览器的内置字符串replace()函数需要两个参数:在主字符串中查找的子字符串,以及用以下替换该字符串的子字符串:

+ +
var myText = 'I am a string';
+var newString = myText.replace('string', 'sausage');
+ +
+

Note:当您需要指定多个参数时,它们以逗号分隔。

+
+ +

还应该注意,有时参数不是必须的 —— 您不必指定它们。如果没有,该功能一般会采用某种默认行为。作为示例,数组 join()函数的参数是可选的:

+ +
var myArray = ['I', 'love', 'chocolate', 'frogs'];
+var madeAString = myArray.join(' ');
+// returns 'I love chocolate frogs'
+var madeAString = myArray.join();
+// returns 'I,love,chocolate,frogs'
+ +

如果没有包含参数来指定加入/分隔符,默认情况下会使用逗号

+ + + +

函数作用域和冲突

+ +

我们来谈一谈 {{glossary("scope")}}即作用域 — 处理函数时一个非常重要的概念。当你创建一个函数时,函数内定义的变量和其他东西都在它们自己的单独的范围内, 意味着它们被锁在自己独立的隔间中, 不能被函数外的代码访问。

+ +

所有函数的最外层被称为全局作用域。 在全局作用域内定义的值可以在任意地方访问。

+ +

JavaScript由于各种原因而建立,但主要是由于安全性和组织性。有时您不希望变量可以在代码中的任何地方访问 - 您从其他地方调用的外部脚本可能会开始搞乱您的代码并导致问题,因为它们恰好与代码的其他部分使用了相同的变量名称,造成冲突。这可能是恶意的,或者是偶然的。

+ + + +

例如,假设您有一个HTML文件,它调用两个外部JavaScript文件,并且它们都有一个使用相同名称定义的变量和函数:

+ +
<!-- Excerpt from my HTML -->
+<script src="first.js"></script>
+<script src="second.js"></script>
+<script>
+  greeting();
+</script>
+ +
// first.js
+var name = 'Chris';
+function greeting() {
+  alert('Hello ' + name + ': welcome to our company.');
+}
+ +
// second.js
+var name = 'Zaptec';
+function greeting() {
+  alert('Our company is called ' + name + '.');
+}
+ +

这两个函数都使用 greeting() 形式调用,但是你只能访问到 first.js 文件的greeting()函数(第二个文件被忽视了)。另外,第二次尝试使用 let 关键字定义 name 变量导致了一个错误。

+ +
+

Note: 您可以参考这个例子 running live on GitHub (查看完整 源代码).

+
+ +

将代码锁定在函数中的部分避免了这样的问题,并被认为是最佳实践。

+ +

这有点像一个动物园。狮子,斑马,老虎和企鹅都保留在自己的园子中,只能拿到到它们园子中的东西 —— 与其函数作用域相同。如果他们能进入其他园子,就会出现问题。不同的动物会在不熟悉的栖息地内感到真的不舒服 - 一只狮子或老虎会在企鹅的水多的,冰冷的的领域中感到可怕。最糟糕的是,狮子和老虎可能会尝试吃企鹅!

+ +

+ +

动物园管理员就像全局作用域 - 他或她有钥匙访问每个园子,重新投喂食物,照顾生病的动物等。

+ +

主动学习: 和 scope 玩耍

+ +

我们来看一个真正的例子来展示范围

+ +
    +
  1. 首先,制作我们的function-scope.html示例的本地副本。它包含两个函数a()b(),和三个变量—— xyz——其中两个在函数中被定义,另一个被定义在全局作用域内。它还包含一个名为output()的函数,它接收一个参数,并将其输出到页面的一个段落中。
  2. +
  3. 在浏览器和文本编辑器中打开示例。
  4. +
  5. 在浏览器开发工具中打开JavaScript控制台。在JavaScript控制台中,输入以下命令:
  6. +
+ +
output(x);
+ +

您应该看到变量x输出到屏幕的值

+ +

       4.现在尝试在您的控制台中输入以下内容

+ +
output(y);
+output(z);
+ +

这两个都应该返回错误沿“ ReferenceError:y未定义 ”。这是为什么?由于函数作用域 - yz锁定在函数a()b()函数中,所以output()从全局作用域调用时无法访问它们。

+ +

        5.但是,从另一个函数里面调用什么呢?尝试编辑a()b()所以他们看起来像这样:

+ +
function a() {
+  var y = 2;
+  output(y);
+}
+
+function b() {
+  var z = 3;
+  output(z);
+}
+ +

保存代码并重新加载到浏览器中,然后尝试从JavaScript控制台调用a()b()函数:

+ +
a();
+b();
+ +

您应该看到页面中输出的yz值。这样就没问题,因为output()函数在其他函数的内部被调用 - 在这种情况下,输出变量的定义和函数的调用都在同一个作用域中(译者注:即函数作用域)。output()它可以从任何地方被调用,因为它在全局作用域中被定义。

+ +

       6.现在尝试更新您的代码,如下所示:

+ +
function a() {
+  var y = 2;
+  output(x);
+}
+
+function b() {
+  var z = 3;
+  output(x);
+}
+ +

再次保存并重新加载,并在JavaScript控制台中再次尝试:

+ +
a();
+b();
+ +

函数 a()b()都应该输出x---1的值。这些没有问题,因为即使output()调用与x定义不在同一个作用域内,但x是一个全局变量,所以在所有代码中都可用。

+ +

7.最后,尝试更新您的代码,如下所示:

+ +
function a() {
+  var y = 2;
+  output(z);
+}
+
+function b() {
+  var z = 3;
+  output(y);
+}
+ +

再次保存并重新加载,并在JavaScript控制台中再次尝试:

+ +
a();
+b();
+ +

这次a()b()调用都会返回那个令人讨厌的 "ReferenceError: z is not defined" error — 这是因为output()函数的调用和输出变量的定义不在同一个函数作用域内 - 变量对这些函数调用是不可见的。

+ +
+

注意:相同的范围规则不适用于循环(for(){...})和条件块(if(){...}) - 它们看起来非常相似,但它们不一样!小心不要让这些困惑。

+
+ +
+

注意:ReferenceError:“x”未定义错误是您遇到的最常见的错误。如果您收到此错误,并且确定您已经定义了该问题的变量,请检查它的范围。

+
+ + + +

函数内部的函数

+ +

请记住,您可以从任何地方调用函数,甚至可以在另一个函数中调用函数。这通常被用作保持代码整洁的方式 - 如果您有一个复杂的函数,如果将其分解成几个子函数,它更容易理解:

+ +
function myBigFunction() {
+  var myValue;
+
+  subFunction1();
+  subFunction2();
+  subFunction3();
+}
+
+function subFunction1() {
+  console.log(myValue);
+}
+
+function subFunction2() {
+  console.log(myValue);
+}
+
+function subFunction3() {
+  console.log(myValue);
+}
+
+ +

要确保函数调取的数值处在有效的作用域内。上面的例子中会产生一个错误提示,ReferenceError:myValue is not define,因为尽管myValue变量与函数调用指令处在同一个作用域中, 但它却没有在函数内被定义 —— 实际代码在调用函数时就开始运行了。为了使代码正确运作,你必须将值作为参数传递给函数,如下所示:

+ +
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);
+}
+ +

测试你的技能!

+ +

你已经来到了本文章的结尾,但是你还能记得最重要的知识吗?你可以在离开这里找到一些更深度的测试来证实你已经记住了这些知识——查看测试你的技能:函数(英文)。后两章文本包含了这个测试需要的技能,所以你可能先需要阅读再尝试该测试。

+ +

总结

+ +

本文探讨了函数背后的基本概念,为之后的学习奠定了基础。下一步,我们将进行实践,并带你一步步建立起你自己的函数。

+ +

参见

+ + + + + +

{{PreviousMenuNext("Learn/JavaScript/Building_blocks/Looping_code","Learn/JavaScript/Building_blocks/Build_your_own_function", "Learn/JavaScript/Building_blocks")}}

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 +--- +
{{LearnSidebar}}
+ +

在这个模块中, 我们将继续介绍JavaScript的关键基本特性,在这一章中我们将关注条件控制语句、循环语句、函数模块、事件等通用代码块。你可能在之前的的课程中见过这些模块,但仅仅是见过—在这篇模块中我们将明确讨论这些模块.

+ +

预备知识

+ +

在开始这部分模块之前, 你应该熟悉基本的HTML和CSS, 并且已经看完我们之前的模块:JavaScript 第一步

+ +
+

: 如果你在使用无法创建自己文件的电脑/平板/其他设备,你可以试试在线编辑器,例如 JSBinThimble.

+
+ +

指南

+ +
+
在代码中做决定 — 条件
+
在任何程序语言中,程序需要根据不同的输入数据作出相应的选择并执行相关的操作。例如,在游戏中,如果玩家的生命值是0,那么游戏就结束了。在天气应用中,如果在早上打开应用,则显示一个太阳升起的图片,如果在晚上打开,则显示星星和月亮。在这篇文章里我们将探索如何在JS中使用条件结构。
+
循环语句
+
有时候你需要在一个行中重复执行某一个任务。例如,查看一整列的名字。在程序中,循环能非常好的处理好这个问题。在本章中我们将介绍JavaScript的循环语句。
+
函数 — 可重用的代码块
+
在编码中的另一个基本概念是函数(functions)。 函数 允许你在定义的区块内存储一段代码用来执行一个单独的任务,然后调用该段代码时,你需要使用一个简短的命令,而不用重复编写多次该段代码。在这篇文章中我们将探讨函数的基本概念,如语法、如何调用定义的函数、作用域和参数。
+
打造自己的函数
+
本文结合前几篇文章中所涉及的基本理论,提供了一个实践经验。在这里你会得到一些实践,并且编写自己的自定义函数。随后,我们也将进一步解释一些与函数相关的有用的细节。
+
函数返回值
+
在这个课程中,我们要讨论的最后一个基本概念是返回值(通过返回值结束我们的函数)。有些函数在完成后不返回任何值,而有些函数返回。重要的是了解返回的值是什么,和如何在你的代码中使用他们,以及如何使自定义的函数返回需要的值。
+
事件介绍
+
事件是你正在编写的系统中发生的动作或事件,系统告诉你的是这些动作或事件,如果需要的话,你可以以某种方式对它们做出反应。例如,如果用户单击网页上的按钮,您可能希望通过显示信息框来响应该操作。在这最后一篇文章中,我们将重点讨论一些围绕事件有关的概念,看看他们如何在浏览器中工作。
+
+ +

评估

+ +

下面的评估将测试您对JavaScript基础知识的理解。

+ +
+
图片画廊
+
现在我们已经学习了构建JavaScript的基本代码块,我们会通过构建一个在很多网站上相当常见的项目——一个由JavaScript驱动的相册,来测试你循环、函数、条件语句和事件方面的知识。
+
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 +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/JavaScript/Building_blocks/conditionals","Learn/JavaScript/Building_blocks/Functions", "Learn/JavaScript/Building_blocks")}}
+ +

编程语言可以很迅速方便地帮我们完成一些重复性的任务,从多个基本计算到几乎完成了很多类似工作的其他情况。现在我们来看看处理这种需求的JavaScript中可用的循环结构。

+ + + + + + + + + + + + +
前提条件:基本的电脑知识,对HTML与CSS有基本的了解,及已阅读: JavaScript first steps(JS的入门).
目标:学习如何在JS里面使用循环语句.
+ +

来一起循环

+ +

循环,循环,循环. 就与这些:popular breakfast cereals, roller coasters and musical production一样,类似存在于编程中.编程中的循环也是一直重复着去做一件事 - 此处循环便是编程中的术语.

+ +

让我们来想一下下图,这位农夫考虑为他的家庭做一周的食物计划,他或许就需要执行一段循环:

+ +


+  

+ +

一段循环通常需要一个或多个条件:

+ + + +

在 {{glossary("伪代码")}} 中,这看起来就像下面这样:

+ +
loop(food = 0; foodNeeded = 10) {
+  if (food = foodNeeded) {
+    exit loop;
+    // 我们有足够的食物了,回家吧。
+  } else {
+    food += 2; // 花一个小时多收集两个食物。
+    // 循环将会继续执行。
+  }
+}
+ +

所以需要的食物量定为10,农民目前的数量为0。在循环的每次迭代中,我们检查农民的食物量是否等于他需要的量。 如果是这样,我们可以退出循环。 如果没有,农民花一个小时收集两部分食物,循环再次运行。

+ +

为何?

+ +

在这一点上,您可能会了解循环中的高级概念,但您可能会认为“好的,但是,这有助于我编写更好的JavaScript代码?” 正如我们前面所说,循环与所做的事情都是一样的,这对于快速完成重复任务是非常有用的。

+ +

通常,循环的每个连续迭代的代码将略有不同,这意味着您可以完成相同但略有不同的任务的全部负载 - 如果您有很多不同的计算要做, 做不同的一个,不一样的一个又一个!

+ +

让我们来看一个例子来完美地说明为什么循环是一件好事。 假设我们想在{{htmlelement("canvas")}}元素上绘制100个随机圆(按更新按钮一次又一次地运行示例以查看不同的随机集):

+ + + +

{{ EmbedLiveSample('Hidden_code', '100%', 400, "", "", "hide-codepen-jsfiddle") }}

+ +

您现在不需要理解所有代码,但我们来看看实际绘制100个圆的那部分代码:

+ +
for (var i = 0; i < 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();
+}
+
+ +

 

+ + + +

 

+ +

您应该有一个基本的想法 - 我们使用一个循环来运行这个代码的100次迭代,其中每一个在页面上的随机位置绘制一个圆。 无论我们绘制100个圆,1000还是10,000,所需的代码量将是相同的。 只有一个数字必须改变。

+ +

如果我们在这里没有使用循环,我们必须为我们想要绘制的每个圆重复以下代码:

+ +
ctx.beginPath();
+ctx.fillStyle = 'rgba(255,0,0,0.5)';
+ctx.arc(random(WIDTH), random(HEIGHT), random(50), 0, 2 * Math.PI);
+ctx.fill();
+ +

这将非常无聊而且很难维持高速。 循环真的相当好用。

+ +

循环的标准

+ +

我们开始探索一些特定的循环结构。 第一个,你会经常使用到它,for循环 - 以下为for循环的语法:

+ +
for (initializer; exit-condition; final-expression) {
+  // code to run
+}
+ +

我们有:

+ +
    +
  1. 关键字for,后跟一些括号。
  2. +
  3. 在括号内,我们有三个项目,以分号分隔: +
      +
    1. 一个初始化器 - 这通常是一个设置为一个数字的变量,它被递增来计算循环运行的次数。它也有时被称为计数变量
    2. +
    3. 一个退出条件 -如前面提到的,这个定义循环何时停止循环。这通常是一个表现为比较运算符的表达式,用于查看退出条件是否已满足的测试。
    4. +
    5. 一个最终条件 -这总是被判断(或运行),每个循环已经通过一个完整的迭代消失时间。它通常用于增加(或在某些情况下递减)计数器变量,使其更接近退出条件值。
    6. +
    +
  4. +
  5. 一些包含代码块的花括号 - 每次循环迭代时都会运行这个代码。
  6. +
+ +

我们来看一个真实的例子,所以我们可以看出这些做得更清楚。

+ +
var cats = ['Bill', 'Jeff', 'Pete', 'Biggles', 'Jasmin'];
+var info = 'My cats are called ';
+var para = document.querySelector('p');
+
+for (var i = 0; i < cats.length; i++) {
+  info += cats[i] + ', ';
+}
+
+para.textContent = info;
+ +

这给我们以下输出:

+ + + +

{{ EmbedLiveSample('Hidden_code_2', '100%', 60, "", "", "hide-codepen-jsfiddle") }}

+ +
+

: 您可以在GitHub上找到这段示例代码。 (也可以在线运行)。

+
+ +

这显示了一个循环用于迭代数组中的项目,并与每个项目进行一些操作 - JavaScript中非常常见的模式。 这里:

+ +
    +
  1. 迭代器i从0开始(var i = 0)。
  2. +
  3. 循环将会一直运行直到它不再小于猫数组的长度。 这很重要 - 退出条件显示循环仍然运行的条件。 所以在这种情况下,<cats.length仍然是真的,循环仍然运行。
  4. +
  5. 在循环中,我们将当前的循环项(cats[i]cats[当前下标的任何东西])以及逗号和空格连接到info变量的末尾。 所以: +
      +
    1. 在第一次运行中,i = 0,所以cats[0] +','将被连接到info("Bill")上。
    2. +
    3. 在第二次运行中,i = 1,所以cats[1] +','将被连接到info("Jeff")上。
    4. +
    5. 等等。 每次循环运行后,1将被添加到i(i ++),然后进程将再次启动。
    6. +
    +
  6. +
  7. 当等于cats.length时,循环将停止,浏览器将移动到循环下面的下一个代码位。
  8. +
+ +
+

: 我们将退出条件设为< cats.length,而不是<= cats.length是因为计算机从0开始,而不是1 - 开始时i是0,并且逐步增加到i = 4(最后一个数组的索引)。 cats.length返回5,因为数组中有5个项目,但是我们不希望达到i = 5,因为这将返回未定义的最后一个项目(没有索引为5的数组项目)。 所以我们想要比cats.lengthi <)少1,而不是cats.lengthi <=)。

+
+ +
+

:  退出条件的一个常见错误是使它们使用“等于”(===)而不是说“小于或等于”(<=)。 如果我们想要运行我的循环到i = 5,退出条件将需要是i <= cats.length。如果我们设置为i === cats.length,循环将不会运行,因为在第一次循环迭代时 i 不等于5,所以循环会立即停止。

+
+ +

我们留下的一个小问题是最后的输出句子形式不是很好:

+ +
+

My cats are called Bill, Jeff, Pete, Biggles, Jasmin,

+
+ +

理想情况下,我们想改变最后循环迭代中的连接,以便在句子末尾没有逗号。 嗯,没问题 - 我们可以很高兴地在我们的for循环中插入一个条件来处理这种特殊情况:

+ +
for (var i = 0; i < cats.length; i++) {
+  if (i === cats.length - 1) {
+    info += 'and ' + cats[i] + '.';
+  } else {
+    info += cats[i] + ', ';
+  }
+}
+ +
+

: 您可以在GitHub上找到这个例子。(也可以在线运行

+
+ +
+

重要: 使用for- 与所有循环一样,您必须确保初始化程序被迭代,以便最终达到退出条件。 如果没有,循环将永不停止,浏览器将强制它停止,否则会崩溃。 这被称为无限循环。

+
+ +

使用break退出循环

+ +

如果要在所有迭代完成之前退出循环,可以使用break语句。 当我们查看switch语句时,我们已经在上一篇文章中遇到过这样的情况 - 当switch语句中符合输入表达式的情况满足时,break语句立即退出switch语句并移动到代码之后。

+ +

与循环相同 - break语句将立即退出循环,并使浏览器移动到跟随它的任何代码。

+ +

说我们想搜索一系列联系人和电话号码,只返回我们想要找的号码? 首先,一些简单的HTML - 一个文本{{htmlelement("input")}},允许我们输入一个名称来搜索,一个{{htmlelement("button")}}元素来提交搜索,以及一个{{htmlelement ("p")}}元素显示结果:
+  

+ +
<label for="search">Search by contact name: </label>
+<input id="search" type="text">
+<button>Search</button>
+
+<p></p>
+ +

然后是JavaScript:

+ +
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 < 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.';
+    }
+  }
+});
+ + + +

{{ EmbedLiveSample('Hidden_code_3', '100%', 100, "", "", "hide-codepen-jsfiddle") }}

+ +
    +
  1. 首先我们有一些变量定义 - 我们有一个联系信息数组,每个项目是一个字符串,包含一个以冒号分隔的名称和电话号码。
  2. +
  3. 接下来,我们将一个事件监听器附加到按钮(btn)上,这样当按下它时,运行一些代码来执行搜索并返回结果。
  4. +
  5. 我们将输入的值输入到一个名为searchName的变量中,然后清空文本输入并重新对准它,准备进行下一个搜索。
  6. +
  7. 现在有趣的部分,for循环: +
      +
    1. 我们的计数器开始时为在0,直到计数器不再小于contacts.length,并在循环的每次迭代之后将i递增1。
    2. +
    3. 在循环中,我们首先将当前联系人(contacts [i])拆分为冒号字符,并将生成的两个值存储在名为splitContact的数组中。
    4. +
    5. 然后,我们使用条件语句来测试splitContact [0](联系人姓名)是否等于输入的searchName。 如果是,我们在段落中输入一个字符串来报告联系人的号码,并使用break来结束循环。
    6. +
    +
  8. +
  9. (contacts.length-1) 迭代后,如果联系人姓名与输入的搜索不符,则段落文本设置为“未找到联系人”,循环继续迭代。
  10. +
+ +
+

注:您可以在GitHub上找到这个例子在线运行

+
+ +

使用continue跳过迭代

+ +

continue语句以类似的方式工作,而不是完全跳出循环,而是跳过当前循环而执行下一个循环。 我们来看另外一个例子,它把一个数字作为一个输入,并且只返回开平方之后为整数的数字(整数)。

+ +

HTML与最后一个例子基本相同 - 一个简单的文本输入和一个输出段落。 JavaScript也是一样的,虽然循环本身有点不同:

+ +
var num = input.value;
+
+for (var i = 1; i <= num; i++) {
+  var sqRoot = Math.sqrt(i);
+  if (Math.floor(sqRoot) !== sqRoot) {
+    continue;
+  }
+
+  para.textContent += i + ' ';
+}
+ +

Here's the output:

+ + + +

{{ EmbedLiveSample('Hidden_code_4', '100%', 100, "", "", "hide-codepen-jsfiddle") }}

+ +
    +
  1. 在这种情况下,输入应为数字(num)。 for循环给定一个从1开始的计数器(在这种情况下,我们对0不感兴趣),一个退出条件,当计数器大于输入num时循环将停止,并且迭代器的计数器将每次增加1。
  2. +
  3. 在循环中,我们使用Math.sqrt(i)找到每个数字的平方根,然后测试平方根是否是一个整数,通过判断当它被向下取整时,它是否与自身相同(这是Math.floor(...)对传递的数字的作用)。
  4. +
  5. 如果平方根和四舍五入的平方根不相等(!==),则表示平方根不是整数,因此我们对此不感兴趣。 在这种情况下,我们使用continue语句跳过当前循环而执行下一个循环迭代,而不在任何地方记录该数字。
  6. +
  7. 如果平方根是一个整数,我们完全跳过if块,所以continue语句不被执行; 相反,我们将当前i值加上一个空格连接到段落内容的末尾。
  8. +
+ +
+

注:您可以在GitHub上查看完整代码,或者在线运行

+
+ +

while语句和do ... while语句

+ +

for 不是JavaScript中唯一可用的循环类型。 实际上还有很多其他的,而现在你不需要理解所有这些,所以值得看几个人的结构,这样你就可以在稍微不同的方式识别出相同的功能。

+ +

首先,我们来看看while循环。 这个循环的语法如下所示:

+ +
initializer
+while (exit-condition) {
+  // code to run
+
+  final-expression
+}
+ +

除了在循环之前设置初始化器变量,并且在运行代码之后,循环中包含final-expression,而不是这两个项目被包含在括号中,这与以前的for循环非常类似。 退出条件包含在括号内,前面是while关键字而不是for。

+ +

同样的三个项目仍然存在,它们仍然以与for循环中相同的顺序定义 - 这是有道理的,因为您必须先定义一个初始化器,然后才能检查它是否已到达退出条件; 在循环中的代码运行(迭代已经完成)之后,运行最后的条件,这只有在尚未达到退出条件时才会发生。

+ +

我们再来看看我们的猫列表示例,但是重写了一个while循环:

+ +
var i = 0;
+
+while (i < cats.length) {
+  if (i === cats.length - 1) {
+    info += 'and ' + cats[i] + '.';
+  } else {
+    info += cats[i] + ', ';
+  }
+
+  i++;
+}
+ +
+

Note: This still works just the same as expected — have a look at it running live on GitHub (also view the full source code).

+
+ +

do...while循环非常类似但在while后提供了终止条件:

+ +
initializer
+do {
+  // code to run
+
+  final-expression
+} while (exit-condition)
+ +

在这种情况下,在循环开始之前,初始化程序先重新开始。 do关键字直接在包含要运行的代码的花括号和终止条件之前。

+ +

这里的区别在于退出条件是一切都包含在括号中,而后面是一个while关键字。 在do ... while循环中,花括号中的代码总是在检查之前运行一次,以查看是否应该再次执行(在while和for中,检查首先出现,因此代码可能永远不会执行)。

+ +

我们再次重写我们的猫列表示例,以使用do...while循环:

+ +
var i = 0;
+
+do {
+  if (i === cats.length - 1) {
+    info += 'and ' + cats[i] + '.';
+  } else {
+    info += cats[i] + ', ';
+  }
+
+  i++;
+} while (i < cats.length);
+ +
+

Note: 再一次,它正如我们期望的那样工作 — 看一看它在Github在线运行 (或者查看完整源代码).

+
+ +
+

Important: 使用 while 和 do...while — 所有循环都一样 — 你必须保证初始变量是迭代的,那么它才会逐渐地达到退出条件. 不然, 它将会永远循环下去, 要么浏览器会强制终止它,要么它自己会崩溃. 这称为无限循环.

+
+ +

主动学习:启动倒计时!

+ +

在这个练习中,我们希望你打印出一个简单的启动倒计时到输出框,从10到关闭。 具体来说,我们希望你:

+ + + +

如果您犯了错误,您可以随时使用“重置”按钮重置该示例。 如果你真的卡住了,请按“显示解决方案”来查看解决方案。

+ + + +

{{ EmbedLiveSample('Active_learning', '100%', 880, "", "", "hide-codepen-jsfiddle") }}

+ +

主动学习:填写来宾列表

+ +

在本练习中,我们希望您获取存储在数组中的名称列表,并将其放入来宾列表中。 但这不是那么容易 - 我们不想让菲尔和洛拉进来,因为他们是贪婪和粗鲁的,总是吃所有的食物! 我们有两个名单,一个是客人承认的,一个是客人拒绝的。

+ +

具体来说,我们希望你:

+ + + +

我们已经提供给您:

+ + + +

额外的奖金问题 - 成功完成上述任务后,您将留下两个名称列表,用逗号分隔,但它们将不整齐 - 每个结尾处都会有一个逗号。 你可以制定出如何在每种情况下编写最后一个逗号的行,并添加一个完整的停止? 看看有用的字符串方法文章的帮助。

+ +

如果您犯了错误,您可以随时使用“重置”按钮重置该示例。 如果你真的卡住了,请按“显示解决方案”来查看解决方案。

+ + + +

{{ EmbedLiveSample('Active_learning_2', '100%', 680, "", "", "hide-codepen-jsfiddle") }}

+ +

应该使用哪种循环类型?

+ +

对于基本用途,for,while和do ... while循环大部分可互换。 他们都可以用来解决相同的问题,你使用哪一个将在很大程度上取决于你的个人偏好 - 哪一个你最容易记住或最直观的。 我们再来看看他们。

+ +

首先是 for:

+ +
for (initializer; exit-condition; final-expression) {
+  // code to run
+}
+ +

while:

+ +
initializer
+while (exit-condition) {
+  // code to run
+
+  final-expression
+}
+ +

最后是 do...while:

+ +
initializer
+do {
+  // code to run
+
+  final-expression
+} while (exit-condition)
+ +

我们建议使用for,因为它可能是最简单地帮你记住一切 - 初始化程序,退出条件和最终表达式都必须整齐地放入括号,所以很容易看到他们在哪里并检查你没有丢失他们。

+ +
+

注:还有其他循环类型/特性,这些特性在 高级/专门 的情况下是有用的,超出了本文的范围。如果您想进一步了解循环学习,请阅读我们的高级循环和迭代指南

+
+ +

结论

+ +

本文向您展示了背后的基本概念,以及JavaScript中循环代码时可用的不同选项。 你现在应该明白为什么循环是一个处理重复代码的好机制,并且在你自己的例子中使用它们!

+ +

如果您有什么不明白的地方,可以再通读一遍,或者联系我们寻求帮助。

+ +

相关链接

+ + + +

{{PreviousMenuNext("Learn/JavaScript/Building_blocks/conditionals","Learn/JavaScript/Building_blocks/Functions", "Learn/JavaScript/Building_blocks")}}

+ +

 

+ +

在本单元中

+ + + +

 

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 +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/JavaScript/Building_blocks/Build_your_own_function","Learn/JavaScript/Building_blocks/Events", "Learn/JavaScript/Building_blocks")}}
+ +

函数返回值-是本章中最后一个基础概念,让我们一起来瞧瞧.。有些函数在执行完毕后不会返回一个有用的值,但有些会, 重要的是理解返回的是什么,怎样使用这些值在你的代码中,我们将在下面讨论这些。

+ + + + + + + + + + + + +
前提: +

基础的计算机知识, 懂得基础的HTML 和CSS, JavaScript 第一步学习, 函数- 可重用的代码块.

+
目标:理解什么函数的返回值   , 和如何使用它们
+ +

什么是返回值?

+ +

返回值意如其名,是指函数执行完毕后返回的值。你已经多次遇见过返回值,尽管你可能没有明确的考虑过他们。让我们一起回看一些熟悉的代码:

+ +
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
+ +

在第一篇函数文章中,我们确切地看到了这一块代码。我们对 myText 字符串调用 replace() 功能,并通过这两个参数的字符串查找,和子串替换它。当这个函数完成(完成运行)后,它返回一个值,这个值是一个新的字符串,它具有替换的功能。在上面的代码中,我们保存这个返回值,以作为newString变量的内容。

+ +

如果你看看替换功能MDN参考页面,你会看到一个返回值。知道和理解函数返回的值是非常有用的,因此我们尽可能地包含这些信息。

+ +

一些函数没有返回值就像(在我们的参考页中,返回值在这种情况下被列出为空值 void 或未定义值 undefined 。).例如, 我们在前面文章中创建的 displayMessage() function , 由于调用的函数的结果,没有返回特定的值。它只是让一个提示框出现在屏幕的某个地方——就是这样!

+ +

通常,返回值是用在函数在计算某种中间步骤。你想得到最终结果,其中包含一些值。那些值需要通过一个函数计算得到,然后返回结果可用于计算的下一个阶段。

+ +

在自定义的函数中使用返回值

+ +

要从自定义函数返回值,您需要使用…等待它… return 关键字。 我们最近在random-canvas-circles.html示例中看到了这一点。 我们的 draw() 函数绘制100随机圆在HTML的{{htmlelement("canvas")}}:

+ +
function draw() {
+  ctx.clearRect(0,0,WIDTH,HEIGHT);
+  for (var i = 0; i < 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();
+  }
+}
+ +

在每个循环迭代,random()函数调用了三次,分别生成当前圆的x坐标,一个随机值Y坐标和半径。random()函数接受一个参数-一个整数,返回0到这个整数之间的随机数。看起来像这样:

+ +
function randomNumber(number) {
+  return Math.floor(Math.random()*number);
+}
+ +

这也可以写成下面这样:

+ +
function randomNumber(number) {
+  var result = Math.floor(Math.random()*number);
+  return result;
+}
+ +

但是第一个版本写得更快,而且更紧凑。

+ +

我们每次调用函数都返回Math.floor(Math.random()*number)计算的数学结果。这个返回值出现在调用函数的位置上,并且代码继续。例如,如果我们运行下面的行:

+ +
ctx.arc(random(WIDTH), random(HEIGHT), random(50), 0, 2 * Math.PI);
+ +

这三次random()调用分别返回值500, 200和35,实际上这一行这样运行:

+ +
ctx.arc(500, 200, 35, 0, 2 * Math.PI);
+ +

在运行该行之前,首先运行该行上的函数调用,并用其返回值替换该函数调用。

+ +

主动学习:我们自己的返回值函数

+ +

让我们着手编写具有我们自己的返回值的函数。

+ +
    +
  1. 首先,从GitHub的function-library.html文件复制一份本地副本。这是一个简单的HTML页面包含一个 {{htmlelement("input")}} 文本域和一个段落。 还有一个 {{htmlelement("script")}} 元素,我们在两个变量中存储了对两个HTML元素的引用。这个小页面允许你在文本框中输入一个数字,并在下面的段落中显示不同的数字。
  2. +
  3. 让我们添加一些有用的函数。在现有的两行JavaScript下面,添加以下函数定义: +
    function squared(num) {
    +  return num * num;
    +}
    +
    +function cubed(num) {
    +  return num * num * num;
    +}
    +
    +function factorial(num) {
    +  var x = num;
    +  while (x > 1) {
    +    num *= x-1;
    +    x--;
    +  }
    +  return num;
    +}
    + squared()cubed() 功能是相当明显的-他们的平方或立方的数作为一个参数返回。factorial()函数返回给定数字的阶乘。
  4. +
  5. 接下来,我们将包括一种打印输入到文本输入中的数字的信息的方法。在现有函数下面输入以下事件处理程序: +
    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) + '.';
    +  }
    +}
    + +

    这里我们创建一个onchange事件处理程序,当文本框上面的change事件被触发的之后,事件处理程序就会运行 - 就是说,一个新的值被输入到文本框并且被提交(就比如,输入一个值,然后按Tab)。当这个匿名函数运行时,输入框中的值将被存储在num变量中。

    + +

    接下来,我们进行条件测试——如果输入的值不是数字,则在段落中打印错误消息。if语句判断isNaN(num)表达式是否返回true。我们用isNaN()函数测试num的值是否不是一个数字-如果不是数字,就返回true,否则返回false

    + +

    如果测试返回false,则数值是一个数字,所以我们在段落元素中打印出一个句子,说明数字的平方、立方体和阶乘是什么。这句话叫squared(),cubed(),和factorial()函数来获得所需的值。

    +
  6. +
  7. 保存您的代码,将其加载到浏览器中,然后尝试. 
  8. +
+ +
+

Note:如果你有麻烦让例子工作,对比GitHub的已完成版检查你的代码(或看它在线运行),或寻求我们的帮助。

+
+ +

在这一点上,我们希望您编写一个自己的几个函数,并将它们添加到库中。这个数的平方根或立方根,或一个圆的周长和半径是多少?

+ +

这个练习提出了一些重要的观点,除了研究如何使用返回语句之外。此外,我们还有:

+ + + +

结论

+ +

因此,我们让它-功能是有趣的,非常有用的,虽然有很多要谈论他们的语法和功能,相当容易理解的正确的文章学习。

+ +

如果您有什么不明白的地方,可以再通读一遍,或者联系我们寻求帮助。

+ +

参见

+ + + +

{{PreviousMenuNext("Learn/JavaScript/Building_blocks/Build_your_own_function","Learn/JavaScript/Building_blocks/Events", "Learn/JavaScript/Building_blocks")}}

+ +

在本单元中

+ + diff --git "a/files/zh-cn/learn/javascript/building_blocks/\347\233\270\347\211\207\350\265\260\345\273\212/index.html" "b/files/zh-cn/learn/javascript/building_blocks/\347\233\270\347\211\207\350\265\260\345\273\212/index.html" new file mode 100644 index 0000000000..22101b20ba --- /dev/null +++ "b/files/zh-cn/learn/javascript/building_blocks/\347\233\270\347\211\207\350\265\260\345\273\212/index.html" @@ -0,0 +1,244 @@ +--- +title: 照片库 +slug: learn/JavaScript/Building_blocks/相片走廊 +tags: + - 事件 + - 事件句柄 + - 初学者 + - 学习 + - 循环 + - 评估 +translation_of: Learn/JavaScript/Building_blocks/Image_gallery +--- +
{{LearnSidebar}}
+ +
{{PreviousMenu("Learn/JavaScript/Building_blocks/Events", "Learn/JavaScript/Building_blocks")}}
+ +

我们已经学习了 JavaScript 基础的块结构,下面我们通过编写一个常见的基于 JavaScript 的照片库来测验一下你对于循环、函数、条件和事件的掌握情况。

+ + + + + + + + + + + + +
预备知识:请读完本章其它所有小节的内容后再开始这个测验。
目标:测试你对 JavaScript 的循环、函数、条件语句和事件处理的掌握程度。
+ +

起点

+ +

下载压缩包 并在本地解压。

+ +
+

:你还可以使用类似 JSBinThimble 这些在线编辑器来完成测验。你可以把 HTML、CSS 和 JavaScript 代码复制过去。如果你选的工具没有独立的 JavaScript/CSS 板面,可以随时在 HTML 页面中添加 <script>/<style> 元素。

+
+ +

项目简介

+ +

我们提供了一些 HTML、CSS、相片和几行 JavaScript 代码。需要你来编写必要的 JavaScript 代码让这个项目运行起来。HTML 的 body 部分如下:

+ +
<h1>照片库</h1>
+
+<div class="full-img">
+  <img class="displayed-img" src="images/pic1.jpg">
+  <div class="overlay"></div>
+  <button class="dark">变暗</button>
+</div>
+
+<div class="thumb-bar">
+
+</div>
+ +

你可以尝试操作一下这个示例,也可 在线打开。(不要偷看源代码哦!)

+ + + + + +

{{ EmbedLiveSample('Image_gallery', '100%', 680, "", "", "hide-codepen-jsfiddle") }}

+ +

以下是本例中 CSS 文件最值得关注的部分:

+ + + +

你的 JavaScript 需要:

+ + + +

可以看一下 完成的示例 体会一下。(别偷看代码哦)

+ +

步骤

+ +

以下是你的工作。

+ +

遍历照片

+ +

我们提供的代码中用一个名为 thumBar 的变量用来存储 thumb-bar <div> 的引用,创建了一个新的 <img> 元素,将它的 src 属性值设置成 xxx 占位符,并且将这个新的 <img> 元素添加到 thumbBar 里。

+ +

你应该:

+ +
    +
  1. 在"遍历图片"注释下方添加一个循环来遍历 5 张图片,只需要遍历 5 个数字,每个数字代表一张图片。
  2. +
  3. 每次迭代中,用图片路径的字符串替换掉占位符 xxx。即在每次迭代中把 src 属性设置为图片的路径。记住,图片都在 images 目录下,文件名是 pic1.jpgpic2.jpg,等等。
  4. +
+ +

给每一个缩略图添加点击处理器

+ +

每次迭代中,你需要给当前的 newImage 加上一个 onclick 事件处理函数——它应该:

+ +
    +
  1. 找到当前图片的 src 属性值。这个可以通过对当前的 <img> 用 "src" 作为参数调用 getAttribute() 函数来完成,但是如何在代码里获取图片?用 newImage 是不行的,因为在事件处理函数应用之前循环已经结束,这样每次迭代 src 的值都会是最后一张图片。因此,对于每个事件处理器,<img> 都是函数的目标。是否可以从事件对象获得相关信息呢。
  2. +
  3. 调用一个函数,取上一步返回的 src 值作为参数。可以给这个函数起一个喜欢的名字。
  4. +
  5. 事件处理器函数应该把 displayed-img <img> 的 src 属性值设为作为参数传入的 src 值。我们已经提供了一个 displayedImg 变量存储相关的 <img>。注意我们需要的是一个定义好的、有名字的函数。
  6. +
+ +

为变亮/变暗按钮编写处理器

+ +

还剩最后的变亮/变暗 <button>。我们已经提供了一个名为 btn 的变量来存储 <button> 的引用。需要添加一个 onclick 事件处理函数:

+ +
    +
  1. 检查当前 <button> 按钮的类名称,仍可用 getAttribute() 函数取得。
  2. +
  3. 如果类名的值是 "dark", 将 <button> 的类名变为 "light"(使用 setAttribute()), 文本内容变为 "变亮",蒙板 <div> 的{{cssxref("background-color")}} 设为 "rgba(0,0,0,0.5)"
  4. +
  5. 如果类名的值不是 "dark", 将 <button> 的类名变为 "dark",文本内容变为 "变暗",蒙板 <div> 的{{cssxref("background-color")}} 设为 "rgba(0,0,0,0)"
  6. +
+ +

以下是实现上述 2、3 点所提功能的基本代码:

+ +
btn.setAttribute('class', xxx);
+btn.textContent = xxx;
+overlay.style.backgroundColor = xxx;
+ +

提示

+ + + +

测验

+ +

如果你是在课堂上进行这个测验,你可以把作品交给导或教授去打分了。如果你是在自学,可以很容易地在 本节测验的讨论页Mozilla 聊天室#mdn 频道回复得到批改指南。请先自己试着做,作弊学不到任何东西!

+ +

{{PreviousMenu("Learn/JavaScript/Building_blocks/Events", "Learn/JavaScript/Building_blocks")}}

+ +

本章目录

+ + -- cgit v1.2.3-54-g00ecf