Since the introduction of iterator helpers in ECMAScript 2025, you can do:
Array(10).keys().forEach((_, i) => {
console.log(i * 10);
});
This does not populate an array of the given length (as Array.from would do), and so the memory for it is constant, no matter what the provided length is for it. The forEachmethod here is not the Array method, but the Iterator method.
Note that if you just need a string repeated you can use String.prototype.repeat.
console.log("0".repeat(10))
// 0000000000
Answer from Tieme on Stack OverflowIs there a mechanism to loop x times in ES6 (ECMAScript 6) without mutable variables?
Which way do you prefer to loop x times in ES6?
ES6 - Loop through objects of objects and mutate the object with additional properties
Javascript for .. of loop (ECMAScript 6) - Stack Overflow
Videos
Since the introduction of iterator helpers in ECMAScript 2025, you can do:
Array(10).keys().forEach((_, i) => {
console.log(i * 10);
});
This does not populate an array of the given length (as Array.from would do), and so the memory for it is constant, no matter what the provided length is for it. The forEachmethod here is not the Array method, but the Iterator method.
Note that if you just need a string repeated you can use String.prototype.repeat.
console.log("0".repeat(10))
// 0000000000
OK!
The code below is written using ES6 syntaxes but could just as easily be written in ES5 or even less. ES6 is not a requirement to create a "mechanism to loop x times"
If you don't need the iterator in the callback, this is the most simple implementation
const times = x => f => {
if (x > 0) {
f()
times (x - 1) (f)
}
}
// use it
times (3) (() => console.log('hi'))
// or define intermediate functions for reuse
let twice = times (2)
// twice the power !
twice (() => console.log('double vision'))
If you do need the iterator, you can use a named inner function with a counter parameter to iterate for you
const times = n => f => {
let iter = i => {
if (i === n) return
f (i)
iter (i + 1)
}
return iter (0)
}
times (3) (i => console.log(i, 'hi'))
Stop reading here if you don't like learning more things ...
But something should feel off about those...
- single branch
ifstatements are ugly — what happens on the other branch ? - multiple statements/expressions in the function bodies — are procedure concerns being mixed ?
- implicitly returned
undefined— indication of impure, side-effecting function
"Isn't there a better way ?"
There is. Let's first revisit our initial implementation
// times :: Int -> (void -> void) -> void
const times = x => f => {
if (x > 0) {
f() // has to be side-effecting function
times (x - 1) (f)
}
}
Sure, it's simple, but notice how we just call f() and don't do anything with it. This really limits the type of function we can repeat multiple times. Even if we have the iterator available, f(i) isn't much more versatile.
What if we start with a better kind of function repetition procedure ? Maybe something that makes better use of input and output.
Generic function repetition
// repeat :: forall a. Int -> (a -> a) -> a -> a
const repeat = n => f => x => {
if (n > 0)
return repeat (n - 1) (f) (f (x))
else
return x
}
// power :: Int -> Int -> Int
const power = base => exp => {
// repeat <exp> times, <base> * <x>, starting with 1
return repeat (exp) (x => base * x) (1)
}
console.log(power (2) (8))
// => 256
Above, we defined a generic repeat function which takes an additional input which is used to start the repeated application of a single function.
// repeat 3 times, the function f, starting with x ...
var result = repeat (3) (f) (x)
// is the same as ...
var result = f(f(f(x)))
Implementing times with repeat
Well this is easy now; almost all of the work is already done.
// repeat :: forall a. Int -> (a -> a) -> a -> a
const repeat = n => f => x => {
if (n > 0)
return repeat (n - 1) (f) (f (x))
else
return x
}
// times :: Int -> (Int -> Int) -> Int
const times = n=> f=>
repeat (n) (i => (f(i), i + 1)) (0)
// use it
times (3) (i => console.log(i, 'hi'))
Since our function takes i as an input and returns i + 1, this effectively works as our iterator which we pass to f each time.
We've fixed our bullet list of issues too
- No more ugly single branch
ifstatements - Single-expression bodies indicate nicely separated concerns
- No more useless, implicitly returned
undefined
JavaScript comma operator, the
In case you're having trouble seeing how the last example is working, it depends on your awareness of one of JavaScript's oldest battle axes; the comma operator – in short, it evaluates expressions from left to right and returns the value of the last evaluated expression
(expr1 :: a, expr2 :: b, expr3 :: c) :: c
In our above example, I'm using
(i => (f(i), i + 1))
which is just a succinct way of writing
(i => { f(i); return i + 1 })
Tail Call Optimisation
As sexy as the recursive implementations are, at this point it would be irresponsible for me to recommend them given that no JavaScript VM I can think of supports proper tail call elimination – babel used to transpile it, but it's been in "broken; will reimplement" status for well over a year.
repeat (1e6) (someFunc) (x)
// => RangeError: Maximum call stack size exceeded
As such, we should revisit our implementation of repeat to make it stack-safe.
The code below does use mutable variables n and x but note that all mutations are localized to the repeat function – no state changes (mutations) are visible from outside of the function
// repeat :: Int -> (a -> a) -> (a -> a)
const repeat = n => f => x =>
{
let m = 0, acc = x
while (m < n)
(m = m + 1, acc = f (acc))
return acc
}
// inc :: Int -> Int
const inc = x =>
x + 1
console.log (repeat (1e8) (inc) (0))
// 100000000
This is going to have a lot of you saying "but that's not functional !" – I know, just relax. We can implement a Clojure-style loop/recur interface for constant-space looping using pure expressions; none of that while stuff.
Here we abstract while away with our loop function – it looks for a special recur type to keep the loop running. When a non-recur type is encountered, the loop is finished and the result of the computation is returned
const recur = (...args) =>
({ type: recur, args })
const loop = f =>
{
let acc = f ()
while (acc.type === recur)
acc = f (...acc.args)
return acc
}
const repeat = $n => f => x =>
loop ((n = $n, acc = x) =>
n === 0
? acc
: recur (n - 1, f (acc)))
const inc = x =>
x + 1
const fibonacci = $n =>
loop ((n = $n, a = 0, b = 1) =>
n === 0
? a
: recur (n - 1, b, a + b))
console.log (repeat (1e7) (inc) (0)) // 10000000
console.log (fibonacci (100)) // 354224848179262000000
This question came to my mind when I had to render some random content a certain number of times and came up with four solutions. So I was wondering if I should choose one over the others, maybe for performance reasons.
Below are my approaches. Note that no index is needed, I just determine how many times to loop.
Using for:
for (let i = 0; i < 6; i++) {
console.log("Repeat")
}Using the spread operator:
[...Array(6)].forEach(() => {
console.log("Repeat");
});Using Array.from():
Array.from({ length: 6 }, () => {
console.log("Repeat");
});Using Recursion:
function repeat(num) {
if (num === 0) return;
console.log("Repeat")
repeat(num - 1)
}
repeat(6);So, are there any significant differences? If so, what are they? Or maybe some of you would take a completely different approach?
Thanks in advance.
Quick hint
for..of takes the element.
var a = ['a','b','c'];
for (let elem of a){
console.log(elem);
}
// output:
// 'a'
// 'b'
// 'c'
for..in takes the index.
var a = ['a','b','c'];
for (let i in a){
console.log(i);
}
// output:
// 0
// 1
// 2
.forEach takes element and index (optional).
var a = ['a','b','c'];
a.forEach(function(elem,i){
console.log(i + ': ' + elem);
});
// output:
// 0: 'a'
// 1: 'b'
// 2: 'c'
From mdn doc:
While for...in iterates over property names, for...of iterates over property values.
What else need to be clear?