Programming languages are very useful for rapidly completing repetitive tasks, 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.
| Prerequisites: | Basic computer literacy, a basic understanding of HTML and CSS, JavaScript 第一步 . |
|---|---|
| Objective: | To understand how to use loops in JavaScript. |
Loops are all about doing the same thing over and over again. Often, the code will be slightly different each time round the loop, or the same code will run but with different variables.
Suppose we wanted to draw 100 random circles on a
<canvas>
element (press the
更新
button to run the example again and again to see different random sets):
Here's the JavaScript code that implements this example:
const btn = document.querySelector('button');
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
const WIDTH = document.documentElement.clientWidth;
const 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 (let 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);
You don't have to understand all the code for now, but let's look at the part of the code that actually draws the 100 circles:
for (let 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)
, defined earlier in the code, returns a whole number between
0
and
x-1
.
WIDTH
and
HEIGHT
are the width and height of the inner browser window.
You should get the basic idea — we are using a loop to run 100 iterations of this code, each one of which draws a circle in a random position on the page. The amount of code needed would be the same whether we were drawing 100 circles, 1000, or 10,000. Only one number has to change.
If we weren't using a loop here, we'd have to repeat the following code for every circle we wanted to draw:
ctx.beginPath();
ctx.fillStyle = 'rgba(255,0,0,0.5)';
ctx.arc(random(WIDTH), random(HEIGHT), random(50), 0, 2 * Math.PI);
ctx.fill();
This would get very boring and difficult to maintain.
Most of the time when you use a loop, you will have a collection of items and want to do something with every item.
One type of collection is the
数组
, which we met in the
数组
chapter of this course.
But there are other collections in JavaScript as well, including
Set
and
地图
.
The basic tool for looping through a collection is the
for...of
循环:
const cats = ['Leopard', 'Serval', 'Jaguar', 'Tiger', 'Caracal', 'Lion'];
for (const cat of cats) {
console.log(cat);
}
在此范例中,
for (const cat of cats)
says:
cats
, get the first item in the collection.
cat
and then run the code between the curly brackets
{}
.
JavaScript also has more specialized loops for collections, and we'll mention two of them here.
可以使用
map()
to do something to each item in a collection and create a new collection containing the changed items:
function toUpper(string) {
return string.toUpperCase();
}
const cats = ['Leopard', 'Serval', 'Jaguar', 'Tiger', 'Caracal', 'Lion'];
const upperCats = cats.map(toUpper);
console.log(upperCats);
// [ "LEOPARD", "SERVAL", "JAGUAR", "TIGER", "CARACAL", "LION" ]
Here we pass a function into
cats.map()
,和
map()
calls the function once for each item in the array, passing in the item. It then adds the return value from each function call to a new array, and finally returns the new array. In this case the function we provide converts the item to uppercase, so the resulting array contains all our cats in uppercase:
[ "LEOPARD", "SERVAL", "JAGUAR", "TIGER", "CARACAL", "LION" ]
可以使用
filter()
to test each item in a collection, and create a new collection containing only items that match:
function lCat(cat) {
return cat.startsWith('L');
}
const cats = ['Leopard', 'Serval', 'Jaguar', 'Tiger', 'Caracal', 'Lion'];
const filtered = cats.filter(lCat);
console.log(filtered);
// [ "Leopard", "Lion" ]
This looks a lot like
map()
, except the function we pass in returns a
boolean
: if it returns
true
, then the item is included in the new array.
Our function tests that the item starts with the letter "L", so the result is an array containing only cats whose names start with "L":
[ "Leopard", "Lion" ]
注意,
map()
and
filter()
are both often used with
function expressions
, which we will learn about in the
函数
模块。
Using function expressions we could rewrite the example above to be much more compact:
const cats = ['Leopard', 'Serval', 'Jaguar', 'Tiger', 'Caracal', 'Lion'];
const filtered = cats.filter((cat) => cat.startsWith('L'));
console.log(filtered);
// [ "Leopard", "Lion" ]
In the "drawing circles" example above, you don't have a collection of items to loop through: you really just want to run the same code 100 times.
In a case like that you should use the
for
循环。
This has the following syntax:
for (initializer; condition; final-expression) {
// code to run
}
Here we have:
for
, followed by some parentheses.
true
.
Let's look at a real example so we can visualize what these do more clearly.
const results = document.querySelector('#results');
function calculate() {
for (let i = 1; i < 10; i++) {
const newResult = `${i} x ${i} = ${i * i}`;
results.textContent += `${newResult}\n`;
}
results.textContent += '\n...finished!';
}
const calculateBtn = document.querySelector('#calculate');
const clearBtn = document.querySelector('#clear');
calculateBtn.addEventListener('click', calculate);
clearBtn.addEventListener('click', () => results.textContent = '');
This gives us the following output:
This code calculates squares for the numbers from 1 to 9, and writes out the result. The core of the code is the
for
loop that performs the calculation.
Let's break down the
for (let i = 1; i < 10; i++)
line into its three pieces:
let i = 1
: the counter variable,
i
, starts at
1
. Note that we have to use
let
for the counter, because we're reassigning it each time we go round the loop.
i < 10
: keep going round the loop for as long as
i
<
10
.
i++
: add one to
i
each time round the loop.
Inside the loop, we calculate the square of the current value of
i
, that is:
i * i
. We create a string expressing the calculation we made and the result, and add this string to the output text. We also add
\n
, so the next string we add will begin on a new line. So:
i = 1
, so we will add
1 x 1 = 1
.
i = 2
, so we will add
2 x 2 = 4
.
i
becomes equal to
10
we will stop running the loop and move straight to the next bit of code below the loop, printing out the
...finished!
消息。
You can use a
for
loop to iterate through a collection, instead of a
for...of
循环。
Let's look again at our
for...of
example above:
const cats = ['Leopard', 'Serval', 'Jaguar', 'Tiger', 'Caracal', 'Lion'];
for (const cat of cats) {
console.log(cat);
}
We could rewrite that code like this:
const cats = ['Leopard', 'Serval', 'Jaguar', 'Tiger', 'Caracal', 'Lion'];
for (let i = 0; i < cats.length; i++) {
console.log(cats[i]);
}
In this loop we're starting
i
at
0
, and stopping when
i
reaches the length of the array.
Then inside the loop we're using
i
to access each item in the array in turn.
This works just fine, and in early versions of JavaScript,
for...of
didn't exist, so this was the standard way to iterate through an array.
However, it offers more chances to introduce bugs into your code. For example:
i
at
1
, forgetting that the first array index is zero, not 1.
i <= cats.length
, forgetting that the last array index is at
length - 1
.
For reasons like this, it's usually best to use
for...of
if you can.
Sometimes you still need to use a
for
loop to iterate through an array.
For example, in the code below we want to log a message listing our cats:
const cats = ['Pete', 'Biggles', 'Jasmine'];
let myFavoriteCats = 'My cats are called ';
for (const cat of cats) {
myFavoriteCats = `${myFavoriteCats}${cat}, `
}
console.log(myFavoriteCats); // "My cats are called Pete, Biggles, Jasmine, "
The final output sentence isn't very well-formed:
My cats are called Pete, Biggles, Jasmine,
We'd prefer it to handle the last cat differently, like this:
My cats are called Pete, Biggles, and Jasmine.
But to do this we need to know when we are on the final loop iteration, and to do that we can use a
for
loop and examine the value of
i
:
const cats = ['Pete', 'Biggles', 'Jasmine'];
let myFavoriteCats = 'My cats are called ';
for (let i = 0; i < cats.length; i++) {
if (i === cats.length - 1) { // We are at the end of the array
myFavoriteCats = `${myFavoriteCats}and ${cats[i]}.`
} else {
myFavoriteCats = `${myFavoriteCats}${cats[i]}, `
}
}
console.log(myFavoriteCats); // "My cats are called Pete, Biggles, and Jasmine."
If you want to exit a loop before all the iterations have been completed, you can use the
break
语句。
We already met this in the previous article when we looked at
switch statements
— when a case is met in a switch statement that matches the input expression, the
break
statement immediately exits the switch statement and moves on to the code after it.
It's the same with loops — a
break
statement will immediately exit the loop and make the browser move on to any code that follows it.
Say we wanted to search through an array of contacts and telephone numbers and return just the number we wanted to find?
First, some simple HTML — a text
<input>
allowing us to enter a name to search for, a
<button>
element to submit a search, and a
<p>
element to display the results in:
<label for="search">Search by contact name: </label>
<input id="search" type="text">
<button>Search</button>
<p></p>
Now on to the 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', () => {
const searchName = input.value.toLowerCase();
input.value = '';
input.focus();
para.textContent = '';
for (const contact of contacts) {
const splitContact = contact.split(':');
if (splitContact[0].toLowerCase() === searchName) {
para.textContent = splitContact[0] + '\'s number is ' + splitContact[1] + '.';
break;
}
}
if (para.textContent === '') {
para.textContent = 'Contact not found.';
}
});
btn
) so that when it is pressed some code is run to perform the search and return the results.
searchName
, before then emptying the text input and focusing it again, ready for the next search.
Note that we also run the
toLowerCase()
method on the string, so that searches will be case-insensitive.
for...of
循环:
splitContact
.
splitContact[0]
(the contact's name, again lower-cased with
toLowerCase()
) is equal to the inputted
searchName
.
If it is, we enter a string into the paragraph to report what the contact's number is, and use
break
to end the loop.
注意: You can view the full source code on GitHub too (also see it running live ).
continue
statement works in a similar manner to
break
, but instead of breaking out of the loop entirely, it skips to the next iteration of the loop.
Let's look at another example that takes a number as an input, and returns only the numbers that are squares of integers (whole numbers).
The HTML is basically the same as the last example — a simple text input, and a paragraph for output.
<label for="number">Enter number: </label>
<input id="number" type="text">
<button>Generate integer squares</button>
<p>Output: </p>
The JavaScript is mostly the same too, although the loop itself is a bit different:
const para = document.querySelector('p');
const input = document.querySelector('input');
const btn = document.querySelector('button');
btn.addEventListener('click', () => {
para.textContent = 'Output: ';
const num = input.value;
input.value = '';
input.focus();
for (let i = 1; i <= num; i++) {
let sqRoot = Math.sqrt(i);
if (Math.floor(sqRoot) !== sqRoot) {
continue;
}
para.textContent += `${i} `;
}
});
Here's the output:
num
)。
for
loop is given a counter starting at 1 (as we are not interested in 0 in this case), an exit condition that says the loop will stop when the counter becomes bigger than the input
num
, and an iterator that adds 1 to the counter each time.
!==
), it means that the square root is not an integer, so we are not interested in it. In such a case, we use the
continue
statement to skip on to the next loop iteration without recording the number anywhere.
if
block entirely, so the
continue
statement is not executed; instead, we concatenate the current
i
value plus a space on to the end of the paragraph content.
注意: You can view the full source code on GitHub too (also see it running live ).
for
is not the only type of loop available in JavaScript. There are actually many others and, while you don't need to understand all of these now, it is worth having a look at the structure of a couple of others so that you can recognize the same features at work in a slightly different way.
First, let's have a look at the while loop. This loop's syntax looks like so:
初始化器
while (condition) {
// code to run
final-expression
}
This works in a very similar way to the
for
loop, except that the initializer variable is set before the loop, and the final-expression is included inside the loop after the code to run, rather than these two items being included inside the parentheses.
The condition is included inside the parentheses, which are preceded by the
while
keyword rather than
for
.
The same three items are still present, and they are still defined in the same order as they are in the for loop. This is because you must have an initializer defined before you can check whether or not the condition is true. The final-expression is then run after the code inside the loop has run (an iteration has been completed), which will only happen if the condition is still true.
Let's have a look again at our cats list example, but rewritten to use a while loop:
const cats = ['Pete', 'Biggles', 'Jasmine'];
let myFavoriteCats = 'My cats are called ';
let i = 0;
while (i < cats.length) {
if (i === cats.length - 1) {
myFavoriteCats += `and ${cats[i]}.`;
} else {
myFavoriteCats += `${cats[i]}, `;
}
i++;
}
console.log(myFavoriteCats); // "My cats are called Pete, Biggles, and Jasmine."
注意: 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 loop is very similar, but provides a variation on the while structure:
初始化器
do {
// code to run
final-expression
} while (condition)
In this case, the initializer again comes first, before the loop starts. The keyword directly precedes the curly braces containing the code to run and the final expression.
The main difference between a
do...while
loop and a
while
loop is that
the code inside a
do...while
loop is always executed at least once
. That's because the condition comes after the code inside the loop. So we always run that code, then check to see if we need to run it again. In
while
and
for
loops, the check comes first, so the code might never be executed.
Let's rewrite our cat listing example again to use a
do...while
循环:
const cats = ['Pete', 'Biggles', 'Jasmine'];
let myFavoriteCats = 'My cats are called ';
let i = 0;
do {
if (i === cats.length - 1) {
myFavoriteCats += `and ${cats[i]}.`;
} else {
myFavoriteCats += `${cats[i]}, `;
}
i++;
} while (i < cats.length);
console.log(myFavoriteCats); // "My cats are called Pete, Biggles, and Jasmine."
注意: Again, this works just the same as expected — have a look at it running live on GitHub (also view the full source code ).
警告: With while and do...while — as with all loops — you must make sure that the initializer is incremented or, depending on the case, decremented, so the condition eventually becomes false. 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 .
In this exercise, we want you to print out a simple launch countdown to the output box, from 10 down to Blastoff. Specifically, we want you to:
let i = 10;
.
<div>
, which we've selected using
const output = document.querySelector('.output');
.
In comments, we've provided you with three code lines that need to be used somewhere inside the loop:
const para = document.createElement('p');
— creates a new paragraph.
output.appendChild(para);
— appends the paragraph to the output
<div>
.
para.textContent =
— makes the text inside the paragraph equal to whatever you put on the right-hand side, after the equals sign.
para.textContent =
lines):
i++
— how do you iterate downwards?
注意: If you start typing the loop (for example (while(i>=0)), the browser might stuck because you have not yet entered the end condition. So be careful with this. You can start writing your code in a comment to deal with this issue and remove the comment after you finish.
If you make a mistake, you can always reset the example with the "Reset" button. If you get really stuck, press "Show solution" to see a solution.
html {
font-family: sans-serif;
}
h2 {
font-size: 16px;
}
.a11y-label {
margin: 0;
text-align: right;
font-size: 0.7rem;
width: 98%;
}
body {
margin: 10px;
background: #f5f9fa;
}
In this exercise, we want you to take a list of names stored in an array and put them into a guest list. But it's not quite that easy — we don't want to let Phil and Lola in because they are greedy and rude, and always eat all the food! We have two lists, one for guests to admit, and one for guests to refuse.
Specifically, we want you to:
people
数组。
refused
paragraph's
textContent
, followed by a comma and a space.
admitted
paragraph's
textContent
, followed by a comma and a space.
We've already provided you with:
refused.textContent +=
— the beginnings of a line that will concatenate something on to the end of
refused.textContent
.
admitted.textContent +=
— the beginnings of a line that will concatenate something on to the end of
admitted.textContent
.
Extra bonus question — after completing the above tasks successfully, you will be left with two lists of names, separated by commas, but they will be untidy — there will be a comma at the end of each one. Can you work out how to write lines that slice the last comma off in each case, and add a full stop to the end? 查看 Useful string methods article for help.
If you make a mistake, you can always reset the example with the "Reset" button. If you get really stuck, press "Show solution" to see a solution.
If you're iterating through an array or some other object that supports it, and don't need access to the index position of each item, then
for...of
is the best choice. It's easier to read and there's less to go wrong.
For other uses,
for
,
while
,和
do...while
loops are largely interchangeable.
They can all be used to solve the same problems, and which one you use will largely depend on your personal preference — which one you find easiest to remember or most intuitive.
We would recommend
for
, at least to begin with, as it is probably the easiest for remembering everything — the initializer, condition, and final-expression all have to go neatly into the parentheses, so it is easy to see where they are and check that you aren't missing them.
Let's have a look at them all again.
First
for...of
:
for (const item of array) {
// code to run
}
for
:
for (initializer; condition; final-expression) {
// code to run
}
while
:
初始化器
while (condition) {
// code to run
final-expression
}
and finally
do...while
:
初始化器
do {
// code to run
final-expression
} while (condition)
注意: 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 .
You've reached the end of this article, but can you remember the most important information? You can find some further tests to verify that you've retained this information before you move on — see Test your skills: Loops .
This article has revealed to you the basic concepts behind, and different options available when looping code in JavaScript. You should now be clear on why loops are a good mechanism for dealing with repetitive code and are raring to use them in your own examples!
If there is anything you didn't understand, feel free to read through the article again, or contact us to ask for help.
最后修改: , 由 MDN 贡献者