--- title: 循环吧代码 slug: learn/JavaScript/Building_blocks/Looping_code tags: - JavaScript - break - continue - for - while - 初学者 - 学习 - 循环 translation_of: Learn/JavaScript/Building_blocks/Looping_code ---
编程语言可以很迅速方便地帮我们完成一些重复性的任务,从多个基本计算到几乎完成了很多类似工作的其他情况。现在我们来看看处理这种需求的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个随机圆(按更新按钮一次又一次地运行示例以查看不同的随机集):
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Random canvas circles</title>
<style>
html {
width: 100%;
height: inherit;
background: #ddd;
}
canvas {
display: block;
}
body {
margin: 0;
}
button {
position: absolute;
top: 5px;
left: 5px;
}
</style>
</head>
<body>
<button>Update</button>
<canvas></canvas>
<script>
var btn = document.querySelector('button');
var canvas = document.querySelector('canvas');
var ctx = canvas.getContext('2d');
var WIDTH = document.documentElement.clientWidth;
var HEIGHT = document.documentElement.clientHeight;
canvas.width = WIDTH;
canvas.height = HEIGHT;
function random(number) {
return Math.floor(Math.random()*number);
}
function draw() {
ctx.clearRect(0,0,WIDTH,HEIGHT);
for (var i = 0; i < 100; i++) {
ctx.beginPath();
ctx.fillStyle = 'rgba(255,0,0,0.5)';
ctx.arc(random(WIDTH), random(HEIGHT), random(50), 0, 2 * Math.PI);
ctx.fill();
}
}
btn.addEventListener('click',draw);
</script>
</body>
</html>
{{ 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();
}
random(),在前面的代码中定义过了,返回一个 0 到 x-1 间的整数。WIDTH 和HEIGHT 浏览器内部窗口的宽度和高度。
您应该有一个基本的想法 - 我们使用一个循环来运行这个代码的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
}
我们有:
for,后跟一些括号。我们来看一个真实的例子,所以我们可以看出这些做得更清楚。
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;
这给我们以下输出:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Basic for loop example</title>
<style>
</style>
</head>
<body>
<p></p>
<script>
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;
</script>
</body>
</html>
{{ EmbedLiveSample('Hidden_code_2', '100%', 60, "", "", "hide-codepen-jsfiddle") }}
注: 您可以在GitHub上找到这段示例代码。 (也可以在线运行)。
这显示了一个循环用于迭代数组中的项目,并与每个项目进行一些操作 - JavaScript中非常常见的模式。 这里:
i从0开始(var i = 0)。<cats.length仍然是真的,循环仍然运行。cats[i]是cats[当前下标的任何东西])以及逗号和空格连接到info变量的末尾。 所以:
i = 0,所以cats[0] +','将被连接到info("Bill")上。i = 1,所以cats[1] +','将被连接到info("Jeff")上。cats.length时,循环将停止,浏览器将移动到循环下面的下一个代码位。注: 我们将退出条件设为< cats.length,而不是<= cats.length是因为计算机从0开始,而不是1 - 开始时i是0,并且逐步增加到i = 4(最后一个数组的索引)。 cats.length返回5,因为数组中有5个项目,但是我们不希望达到i = 5,因为这将返回未定义的最后一个项目(没有索引为5的数组项目)。 所以我们想要比cats.length(i <)少1,而不是cats.length(i <=)。
注: 退出条件的一个常见错误是使它们使用“等于”(===)而不是说“小于或等于”(<=)。 如果我们想要运行我的循环到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语句。 当我们查看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:
const contacts = ['Chris:2232322', 'Sarah:3453456', 'Bill:7654322', 'Mary:9998769', 'Dianne:9384975'];
const para = document.querySelector('p');
const input = document.querySelector('input');
const btn = document.querySelector('button');
btn.addEventListener('click', function() {
let searchName = input.value.toLowerCase();
input.value = '';
input.focus();
for (let i = 0; i < contacts.length; i++) {
let splitContact = contacts[i].split(':');
if (splitContact[0].toLowerCase() === searchName) {
para.textContent = splitContact[0] + '\'s number is ' + splitContact[1] + '.';
break;
} else if (i === contacts.length - 1) {
para.textContent = 'Contact not found.';
}
}
});
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Simple contact search example</title>
<style>
</style>
</head>
<body>
<label for="search">Search by contact name: </label>
<input id="search" type="text">
<button>Search</button>
<p></p>
<script>
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 if (i === contacts.length-1)
para.textContent = 'Contact not found.';
}
}
});
</script>
</body>
</html>
{{ EmbedLiveSample('Hidden_code_3', '100%', 100, "", "", "hide-codepen-jsfiddle") }}
btn)上,这样当按下它时,运行一些代码来执行搜索并返回结果。searchName的变量中,然后清空文本输入并重新对准它,准备进行下一个搜索。contacts.length,并在循环的每次迭代之后将i递增1。contacts [i])拆分为冒号字符,并将生成的两个值存储在名为splitContact的数组中。splitContact [0](联系人姓名)是否等于输入的searchName。 如果是,我们在段落中输入一个字符串来报告联系人的号码,并使用break来结束循环。(contacts.length-1) 迭代后,如果联系人姓名与输入的搜索不符,则段落文本设置为“未找到联系人”,循环继续迭代。注:您可以在GitHub上找到这个例子或在线运行。
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:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Integer squares generator</title>
<style>
</style>
</head>
<body>
<label for="number">Enter number: </label>
<input id="number" type="text">
<button>Generate integer squares</button>
<p>Output: </p>
<script>
var para = document.querySelector('p');
var input = document.querySelector('input');
var btn = document.querySelector('button');
btn.addEventListener('click', function() {
para.textContent = 'Output: ';
var num = input.value;
input.value = '';
input.focus();
for (var i = 1; i <= num; i++) {
var sqRoot = Math.sqrt(i);
if (Math.floor(sqRoot) !== sqRoot) {
continue;
}
para.textContent += i + ' ';
}
});
</script>
</body>
</html>
{{ EmbedLiveSample('Hidden_code_4', '100%', 100, "", "", "hide-codepen-jsfiddle") }}
num)。 for循环给定一个从1开始的计数器(在这种情况下,我们对0不感兴趣),一个退出条件,当计数器大于输入num时循环将停止,并且迭代器的计数器将每次增加1。Math.sqrt(i)找到每个数字的平方根,然后测试平方根是否是一个整数,通过判断当它被向下取整时,它是否与自身相同(这是Math.floor(...)对传递的数字的作用)。!==),则表示平方根不是整数,因此我们对此不感兴趣。 在这种情况下,我们使用continue语句跳过当前循环而执行下一个循环迭代,而不在任何地方记录该数字。注:您可以在GitHub上查看完整代码,或者在线运行。
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到关闭。 具体来说,我们希望你:
var output = document.querySelector('.output');。在评论中,我们为您提供了需要在循环中某处使用的三条代码行:
var para = document.createElement('p'); —新建一个段落。output.appendChild(para); — 将段落附加到输出 <div>中。para.textContent = — 段落内的文字等于您放在右侧的任何内容。para.textContent = lines):
i++ - 你如何向下迭代?如果您犯了错误,您可以随时使用“重置”按钮重置该示例。 如果你真的卡住了,请按“显示解决方案”来查看解决方案。
<div class="output" style="height: 410px;overflow: auto;">
</div>
<textarea id="code" class="playable-code" style="height: 300px;">
var output = document.querySelector('.output');
output.innerHTML = '';
// var i = 10;
// var para = document.createElement('p');
// para.textContent = ;
// output.appendChild(para);
</textarea>
<div class="playable-buttons">
<input id="reset" type="button" value="Reset">
<input id="solution" type="button" value="Show solution">
</div>
var textarea = document.getElementById('code');
var reset = document.getElementById('reset');
var solution = document.getElementById('solution');
var code = textarea.value;
function updateCode() {
eval(textarea.value);
}
reset.addEventListener('click', function() {
textarea.value = code;
updateCode();
});
solution.addEventListener('click', function() {
textarea.value = jsSolution;
updateCode();
});
var jsSolution = 'var output = document.querySelector(\'.output\');\noutput.innerHTML = \'\';\n\nvar i = 10;\n\nwhile(i >= 0) {\n var para = document.createElement(\'p\');\n if(i === 10) {\n para.textContent = \'Countdown \' + i;\n } else if(i === 0) {\n para.textContent = \'Blast off!\';\n } else {\n para.textContent = i;\n }\n\n output.appendChild(para);\n\n i--;\n}';
textarea.addEventListener('input', updateCode);
window.addEventListener('load', updateCode);
{{ EmbedLiveSample('Active_learning', '100%', 880, "", "", "hide-codepen-jsfiddle") }}
在本练习中,我们希望您获取存储在数组中的名称列表,并将其放入来宾列表中。 但这不是那么容易 - 我们不想让菲尔和洛拉进来,因为他们是贪婪和粗鲁的,总是吃所有的食物! 我们有两个名单,一个是客人承认的,一个是客人拒绝的。
具体来说,我们希望你:
var i = 0开始,但是你需要什么退出条件?textContent的末尾,后跟逗号和空格。textContent的末尾,后跟逗号和空格。我们已经提供给您:
var i = 0; — 你的初始化程序refused.textContent += - 将连接某些东西的行的开头,结束于refused.textContent。admitted.textContent += - 将连接某些内容到一行的结尾的行的开始。额外的奖金问题 - 成功完成上述任务后,您将留下两个名称列表,用逗号分隔,但它们将不整齐 - 每个结尾处都会有一个逗号。 你可以制定出如何在每种情况下编写最后一个逗号的行,并添加一个完整的停止? 看看有用的字符串方法文章的帮助。
如果您犯了错误,您可以随时使用“重置”按钮重置该示例。 如果你真的卡住了,请按“显示解决方案”来查看解决方案。
<div class="output" style="height: 100px;overflow: auto;">
<p class="admitted">Admit: </p>
<p class="refused">Refuse: </p>
</div>
<textarea id="code" class="playable-code" style="height: 400px;">
var people = ['Chris', 'Anne', 'Colin', 'Terri', 'Phil', 'Lola', 'Sam', 'Kay', 'Bruce'];
var admitted = document.querySelector('.admitted');
var refused = document.querySelector('.refused');
admitted.textContent = 'Admit: ';
refused.textContent = 'Refuse: '
// var i = 0;
// refused.textContent += ;
// admitted.textContent += ;
</textarea>
<div class="playable-buttons">
<input id="reset" type="button" value="Reset">
<input id="solution" type="button" value="Show solution">
</div>
var textarea = document.getElementById('code');
var reset = document.getElementById('reset');
var solution = document.getElementById('solution');
var code = textarea.value;
function updateCode() {
eval(textarea.value);
}
reset.addEventListener('click', function() {
textarea.value = code;
updateCode();
});
solution.addEventListener('click', function() {
textarea.value = jsSolution;
updateCode();
});
var jsSolution = 'var people = [\'Chris\', \'Anne\', \'Colin\', \'Terri\', \'Phil\', \'Lola\', \'Sam\', \'Kay\', \'Bruce\'];\n\nvar admitted = document.querySelector(\'.admitted\');\nvar refused = document.querySelector(\'.refused\');\n\nvar i = 0;\n\ndo {\n if(people[i] === \'Phil\' || people[i] === \'Lola\') {\n refused.textContent += people[i] + \', \';\n } else {\n admitted.textContent += people[i] + \', \';\n }\n i++;\n} while(i < people.length);\n\nrefused.textContent = refused.textContent.slice(0,refused.textContent.length-2) + \'.\';\nadmitted.textContent = admitted.textContent.slice(0,admitted.textContent.length-2) + \'.\';';
textarea.addEventListener('input', updateCode);
window.addEventListener('load', updateCode);
{{ 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中循环代码时可用的不同选项。 你现在应该明白为什么循环是一个处理重复代码的好机制,并且在你自己的例子中使用它们!
如果您有什么不明白的地方,可以再通读一遍,或者联系我们寻求帮助。
What’s the Best Way to Write a JavaScript For Loop? — some advanced loop best practices
{{PreviousMenuNext("Learn/JavaScript/Building_blocks/conditionals","Learn/JavaScript/Building_blocks/Functions", "Learn/JavaScript/Building_blocks")}}