Loops, the most commonly used construct in programming languages, are used to help run a piece of code multiple times. In JavaScript, where everything is an object, there are many options available to loop over objects. With so many options available, it can be confusing to know which loop to use where. This article aims to simplify this confusion by providing a walkthrough of the most popular loops in JavaScript, along with some real-world examples.
Let’s dive in!
for
loopThe for
loop is the most commonly used loop to iterate over arrays, strings, or any array-like objects is the for
loop. Everyone learns for
on their first day of learning JavaScript!
A for
loop in JavaScript follows this format:
for (initialization; condition; final-expression) { // body }
Here’s an example of the loop in action:
let numbers = [1,2,3,4,5] for(let i=0;i<numbers.length;i++) { // do something... console.log(numbers[i]); }
You can use for
when you want low-level control over a loop.
Though it’s fairly basic, there are a few things a developer should know about the for
loop:
Using a break
keyword in a for
loop breaks the execution, and the code will exit the for
loop:
let numbers = [1,2,3,4,5] for(let i=0 ; i < numbers.length ;i++) { if(i === 3) break; console.log(numbers[i]); }
The above for
loop will exit when iterating over the third number in the array. The break
statement is useful when you want to stop processing elements in an array or array-like data structures when a certain condition is met.
Using the continue
keyword in a for
loop skips to the next element:
let numbers = [1,2,3,4,5] for(let i=0 ; i < numbers.length ;i++) { if(i === 3) continue; console.log(numbers[i]); }
The above for
loop will skip logging 3
to the console because the continue
keyword skips to the next element in an array. All statements below the continue
keyword are skipped. continue
is useful when you selectively process elements in an array or array-like data structure.
All expressions in the loop are optional:
Both for
loops below are valid for
loops in JavaScript:
let i = 0; for (; i < 3;) { console.log(i++); // 0 1 2 } for(;;;) { console.log("This will print forever.....") //infinite loop }
for...of
loopThis loop is a variation of a for
loop that was recently added to JavaScript. It is useful for iterating over arrays, strings, sets, maps, typed arrays, etc., — basically any object that implements the [Symbol.iterator]
function.
Following the same example, this is how you would use a for…of
loop:
const numbers = [1,2,3,4,5] for(const num of numbers) { console.log(num) // 1 2 3 4 5 }
The break
and continue
keywords work similarly for the for…of
loop.
Here are a few things to keep in mind when using this loop:
The iterated values are copies of the original ones, not references:
const numbers = [1,2,3,4] for(let num of numbers) { num = num + 2; // this doesn't change the values in the numbers array } console.log(numbers) // 1,2,3,4
for...of
can’t access the index:
Unlike the for
loop, the for…of
loop doesn’t pass the index of the current item in the collection. You would have to manually keep track of the index:
const numbers = [1,2,3,4] let index = 0 for(let num of numbers) { index++; console.log(num); }
for...of
can be used in async functions:
The for…of
loop also allows you to iterate over async data sources. Here’s an example:
async function load() { for await (const res of urls.map(fetch)) { const data = await res.json(); } }
Here, the loop iterates over an array of promises returned by urls.map(fetch)
. Once each of the promises resolves, it then iterates over the response object returned by the fetch call.
for…in
loopThis is another variation of the for loop that can be used to iterate over the keys of an object:
const user = {id: 12,first_name: "John",last_name: "Doe",age: 27, gender: "male"} for(const key in user) { console.log(key) // id first_name last_name age gender console.log(user[key]) // 12 John Doe 27 male }
One thing to understand is that for...of
iterates over values, whereas for...in
iterates over keys. Under the hood, thefor...in
loop calls Object.keys(object)
.
A few things to note:
for…in
iterates over inherited properties:
Object.prototype.foo = 123; const obj = { bar: 456 }; for (const key in obj) { console.log(key); // 'bar', 'foo' }
To avoid this, we can use hasOwnProperty
to check if the property belongs to the object or someone in the prototype chain:
Object.prototype.foo = 123; const obj = { bar: 456 }; for (const key in obj) { if(obj.hasOwnProperty(key)) { console.log(key); // 'bar' } }
Don’t use for...in
with arrays:
As mentioned earlier, for...in
iterates over the keys of an object. Internally, arrays are objects with an index as the key. Therefore, when used with arrays, for...in
will return the index of the values and not the actual values:
const nums = [101, 102]; for (const key in nums) { console.log(key); // '0', '1' }
break
and continue
work as expected with for...in
:
Using break
exits the loop and continue
skips to the next iteration. Both these keywords work as expected:
const user = { name: 'Alice', age: 30, role: 'admin', banned: false, gender: 'female' }; for (const key in user) { if (key === 'role') continue; // skip 'role' if (key === 'banned') break; // stop before 'banned' console.log(key, user[key]); // name Alice age 30 }
do...while
loopThe do…while
loop isn’t common in real-world use cases. It is a post-tested loop, which means the loop runs at least once before the condition is evaluated to either true
or false
:
const nums = [1,2,3,4,5] let i=0; do { console.log(nums[i]) // 1 2 3 i++ } while(i < 3)
Here are a few things to note about the do...while
loop:
do...while
runs at least once before the condition is evaluated:
const nums = [1,2,3,4,5] let i=0; do { console.log(nums[i]) // 1 i++ } while(i < 0)
The loop will run once, even though the while
condition is always going to be false.
The loop works with break
and continue
:
Similar to the other loops, with do...while
, you can use break
to exit the loop or continue
to skip to the next iteration:
let i = 0; do { i++; if (i === 2) continue; if (i === 4) break; console.log(i); // 1 3 } while (i < 5);
while
loopA while
loop runs as long as the condition evaluates to true or a true-like value. Unlike do..while
, while
doesn’t ensure the loop runs at least once. It’s also very easy to run into infinite loops, so you need to be very careful when writing one:
const nums = [1,2,3,4,5] let i =0; while(i < 6) { console.log(nums[i]) // 1 2 3 4 5 i++; }
Below are a few things to note about the while
loop:
You can easily create infinite loops:
The code below creates an infinite loop, so you need to be extremely cautious when using while
:
let i = 0; while (i < 3) { console.log(i) // 0 0 0 0 0 0 ..... // forgot i++; }
while
works with break
and continue
:
Like we saw for most other loops, while
works with these statements:
let i = 0; while (i < 5) { i++; if (i === 2) continue; if (i === 4) break; console.log(i); // 1 3 }
forEach
loopforEach
is a higher-order function that takes in a callback as its argument. This callback function is called for each element in the array. The callback function has the signature (element, index, array)
. Below is an example of how to use the forEach
loop:
const nums = [1,2,3,4,5] nums.forEach((num, index, nums) => { // do something })
Things to note about this loop:
forEach
is not ideal for async functions:
The forEach
loop doesn’t wait for an async function to finish before going to the next element in the array. So, if you plan on async processing array elements one after the other, you should probably stick to the simple for
loop:
const nums = [1,2,3,4,5] nums.forEach(async (num, index, nums) => { const data = await fetch(`https://someAPI.com/${num}`) // control won't stop here })
Keywords like continue
and break
don’t work as expected with forEach
:
When using forEach
, you can’t quit the loop early with break
and skip an iteration with continue
. Remember, forEach
is a higher-order function:
const nums = [1, 2, 3] nums.forEach((n) => { if (n === 2) return; // Only exits this iteration, not the loop console.log(n); });
forEach
always runs to completion:
A forEach
loop runs for all elements. You can skip processing for certain elements with a return
statement:
const nums = [1, 2, 3] nums.forEach((n) => { console.log(n); // 1 2 3 return; });
N.B., forEach
also exists for other data structures like Set
, Maps
, and TypedArrays
. They behave more or less in the same way.
map
loopmap
is generally used when we need to transform the values in an array and return the transformed values in an array. A common mistake is to use map
to iterate over an array instead of a simple for
loop or forEach
function.
map
is also a higher-order function that takes in a callback and returns an array of transformed values. Remember: to run side effects on elements in an array, use forEach
, and to perform transformations on array elements and return the transformed array, use map
:
const nums = [1, 2, 3]; const doubled = nums.map((num, index, nums) { return num * 2 } ); console.log(doubled); // [2, 4, 6]
Here are a few things one should keep in mind when using the map
function on an array:
Always return from the map
callback function:
The value returned by the callback function is returned as an element of an array from the map
function, so never forget to return a value from the map
callback function:
const nums = [1, 2, 3]; const result = nums.map(item => { return item * 2; }); console.log(result) // [2,4,6]
map
is not async-friendly:
Just like the forEach
loop, map
is a higher-order function. It doesn’t wait for any async operation to complete in the callback function:
const nums = [1, 2, 3]; const resultWithPromises = nums.map(num => { return fetch(`https://someapi.com/${num}`) }); console.log(resultWithPromises) // [Promise, Promise, Promise] const result = await Promise.all(nums.map(num => { return fetch(`https://someapi.com/${num}`) })); console.log(result) // 123 456 789
some
loopsome
is a lesser-known array function that checks if at least one element in an array passes the given test. You can think of this method as asking, “Do some item(s) in this array meet this condition?”
const nums = [1,2,3,4,5] // check if atleast one number is even const containsEven = nums.some((num, index, nums) => { return num%2 === 0 }) console.log(containsEven) // true
Here, if at least one element (2
and 4
) passes the test, the some
function will return a truthy value. You would use some
in real-world use cases like:
A few things to note about the some
function:
some
always returns a Boolean:
some
always returns a Boolean value. Looking at the function name, some might think that it would return an array of elements that pass the test. But instead, using some
returns a Boolean value of true
or false
immediately after it encounters the appropriate number.
For example, if I have an array [1,2,3,4,5]
and I pass a condition that checks if a number is even, as soon as 2
is encountered, the other numbers will be skipped and the function will “early return” with a true
value. In this way, some
can be thought of as a shortcut; it returns early if an element passes the test.
It doesn’t support await
in callback:
Because some
is a higher-order function, if there is an await
in the callback, the callback won’t wait for the promise to resolve:
await arr.some(async x => await isValid(x)); // ❌ Doesn’t work
The callback returns a Boolean:
The callback passed to some
should always return a Boolean. This Boolean signals if the element passed the test or not:
arr.some(item => item === 3);
every
loopevery
is an array method that checks if all elements in an array pass the test or not. It takes in a callback and expects it to return a Boolean value. The callback signature is similar to other array methods:
const nums = [2, 4, 6]; const allEven = nums.every(n => n % 2 === 0); console.log(allEven); // true
The code above checks if all the elements in the array are even. Though rarely used, every
can be used in the following use cases:
users.every(u => u.isActive)
items.every(i => validate(i))
A few things to note about every
:
It short-circuits on the first false
:
Because the function checks if all elements in the array pass the test, if any element fails the test (meaning it returns false
), the function will short-circuit.
It’s not async-friendly:
Because every
is a higher-order function, if there is an await in the callback, the callback won’t wait for the promise to resolve:
arr.every(async item => await isValid(item)); // ❌ Doesn't work as expected
We covered a lot of loops, and it might take time to digest this information. Here’s a quick overview of the loops we explored:
Feature | for | for…in | for…of | while | do…while | map() | forEach() | every() | some() |
---|---|---|---|---|---|---|---|---|---|
Iterates over values | ✅ (manual) | ❌ (keys only) | ✅ | ✅ (manual) | ✅ (manual) | ✅ | ✅ | ✅ | ✅ |
Target | Indexable items | Object keys | Iterables | Any condition | Any condition | Arrays | Arrays | Arrays | Arrays |
Returns a value | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ New array | ❌ undefined | ✅ Boolean | ✅ Boolean |
Can break/continue | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
Executes at least once | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ |
Supports await | ✅ (manually) | ❌ | ✅ (for await) | ✅ (manually) | ✅ (manually) | ❌ | ❌ | ❌ | ❌ |
JavaScript loops are some of the most commonly used constructs. Knowing the difference between each of them will help you write cleaner and more performant code that enhances readability.
Thanks for reading!
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowYou don’t need to guess what’s wrong with your Next.js app. I’ve mapped out the 8 biggest performance traps and the fixes that actually work.
Learn how to truncate text with three dots in CSS, and use two reliable CSS text truncation techniques while covering single-line and multi-line truncations.
Explore how to use Google’s new experimental Interest Invoker API for delays, popovers, and smarter hover UX.
Bolt.new revolutionizes how you build and deploy web apps with no-code development and seamless AI integration.