MDN’s new design is in Beta! A sneak peek: https://blog.mozilla.org/opendesign/mdns-new-design-beta/

循环吧代码

此页面上有脚本错误。虽然它是写给网站编辑,您可以在下面查看部分内容。

这篇翻译不完整。请帮忙从英语翻译这篇文章

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

编程语言是可以很迅速方便的帮我们完成一些重复性的任务, from multiple basic calculations to just about any other situation where you've got a lot of similar items of work to complete. Here we'll look at the loop structures available in JavaScript that handle such needs.

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

来一起循环

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

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


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

  • 一个开始条件,它被初始化为一个特定的值 - 这是循环的起点(“开始:我没有食物”,上面)。
  • 一个结束条件,这是循环停止的标准 - 通常计数器达到一定值。 以上所说的“我有足够的食物”吗? 假设他需要10份食物来养活他的家人。
  • 一个容器,这通常在每个连续循环上递增少量的计数器,直到达到退出条件。 我们以前没有明确说明,但是我们可以考虑一下农民能够每小时收集2份食物。 每小时后,他收集的食物量增加了两倍,他检查他是否有足够的食物。 如果他已经达到10分(退出条件),他可以停止收集回家。

In {{glossary("pseudocode")}}, this would look something like the following:

loop(food = 0; foodNeeded = 10) {
  if (food = foodNeeded) {
    exit loop;
    // We have enough food; let's go home
  } else {
    food += 2; // Spend an hour collecting 2 more food
    // loop will then run again
  }
}

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

Why bother?

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

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

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

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

您现在不需要了解所有代码(您可以在GitHub上看到完整的源代码,并且可以在单独的窗口中查看示例),但是我们来看看实际绘制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 (initializer; exit-condition; final-expression) {
  // code to run
}

Here we have:

  1. The keyword for, followed by some parentheses.
  2. Inside the parentheses we have three items, separated by semi-colons:
    1. An initializer — this is usually a variable set to a number, which is incremented to count the number of times the loop has run. It is also sometimes referred to as a counter variable.
    2. An exit-condition — as mentioned before, this defines when the loop should stop looping. This is generally an expression featuring a comparison operator, a test to see if the exit condition has been met.
    3. A final-expression — this is always evaluated (or run) each time the loop has gone through a full iteration. It usually serves to increment (or in some cases decrement) the counter variable, to bring it closer to the exit condition value.
  3. Some curly braces that contain a block of code — this code will be run each time the loop iterates.

Let's look at a real example so we can visualize what these do more clearly.

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;

This gives us the following output:

{{ EmbedLiveSample('Hidden_code_2', '100%', 60) }}

Note: You can find this example code on GitHub too (also see it running live).

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

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

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

Note: 退出条件的一个常见错误是使它们使用“等于”而不是说“小于或等于”。 如果我们想要运行我的循环到i = 5,退出条件将需要是i <= cats.length /如果我们设置为i = cats.length,循环将不会运行,因为我不等于 在第一次循环迭代时为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] + ', ';
  }
}

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

退出循环与休息

如果要在所有迭代完成之前退出循环,可以使用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>

Now on to the 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) }}

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

跳过迭代继续

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

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

Note: You can view the full source code on GitHub, and see it running live there too.

while and 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).

The do...while loop is very similar, but provides a variation on the while structure:

initializer
do {
  // code to run

  final-expression
} while (exit-condition)

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

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

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

var i = 0;

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

  i++;
} while (i < cats.length);

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

Important: With while and do...while — as with all loops — you must make sure that the initializer is iterated so that it eventually reaches the exit condition. If not, the loop will go on forever, and either the browser will force it to stop, or it will crash. This is called an infinite loop.

积极学习:启动倒计时!

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

  • 从10下降到0.我们为您提供了一个初始化器 - var i = 10;
  • 对于每次迭代,创建一个新的段落并将其附加到输出<div>,我们使用var output = document.querySelector('.output');. 在评论中,我们为您提供了需要在循环中某处使用的三条代码行:
    • var para = document.createElement('p'); — creates a new paragraph.
    • output.appendChild(para); — appends the paragraph to the output <div>.
    • para.textContent = — 段落内的文字等于您放在右侧的任何内容,等于等号。
  • 不同的迭代数字需要将不同的文本放在该迭代的段落中(您需要一个条件语句和多个para.textContent = lines):
    • 如果数字是10,打印“Countdown 10”到段落。
    • 如果数字为0,请打印“Blast off!” 到段落。
    • 对于任何其他数字,只打印段落的数字。
  • 记住要包括一个迭代器! 然而,在这个例子中,我们在每次迭代之后都倒计时,而不是上升,所以你不想要我的++ - 你如何向下迭代?

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

{{ EmbedLiveSample('Active_learning', '100%', 780) }}

积极学习:填写来宾列表

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

具体来说,我们希望你:

  • 编写一个循环,它将从0迭代到people数组的长度。 你需要从一个初始化器var i = 0开始,但是你需要什么退出条件?
  • 在每个循环迭代期间,使用条件语句检查当前数组项是否等于“Phil”或“Lola”:
    • 如果是,则将数组项连接到拒绝段落的textContent的末尾,后跟逗号和空格。
    • 如果不是,则将数组项连接到接收段落的textContent的末尾,后跟逗号和空格。

我们已经提供给您:

  • var i = 0; — 你的初始化程序
  • refused.textContent + = - 将连接某些东西的行的开头,结束于refused.textContent。
  • admitted.textContent + = - 将连接某些内容到一行的结尾的行的开始。

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

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

{{ EmbedLiveSample('Active_learning_2', '100%', 580) }}

你应该使用哪种循环类型?

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

First for:

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

while:

initializer
while (exit-condition) {
  // code to run

  final-expression
}

and finally do...while:

initializer
do {
  // code to run

  final-expression
} while (exit-condition)

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

Note: There are other loop types/features too, which are useful in advanced/specialized situations and beyond the scope of this article. If you want to go further with your loop learning, read our advanced Loops and iteration guide.

Conclusion

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

If there is anything you didn't understand, feel free to read through the article again, or contact us to ask for help.

See also

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

文档标签和贡献者

 此页面的贡献者: wanqing19954, Marslin92
 最后编辑者: wanqing19954,