this is a very valid point. We are aware that .forEach may outperform for..of, but the same could be said about a classic for loop, which is usually the most performant. In scenarios where performance is a key aspect, I agree this rule can be disabled.
But for most cases, the advantages of a for...of outweigh the minor (in majority of situations) performance differences, especially when you need control flow (break/continue), async/await support, or want to iterate over non-array iterables like Maps and Sets.
So maybe the explanation of the issue: “The forEach method creates a function call for each array element, which introduces performance overhead compared to native for...of loops. This overhead becomes more significant with larger arrays,” should be mitigated.
The answer is indeed that it’s complicated. Simple benchmarks may go either way. I experimented some more with Node 20.
A for…of loop is defined in ECMAScript in a complicated way involving returning a new object with value and done fields for every item. This probably explains why it runs slower when not optimized. When running the loop a few times (3 in my experiments), it speeds up greatly, but it is still somewhat slower than an indexed for loop.
The problems of forEach with closures and closed-over variables are mentioned in the linked post. Indeed, in a more realistic scenario where the inner function closes over a variable (I’m summing the numbers in the array), forEach stays slow, especially if it mutates the variable. The first run is a bit faster than for…of.
If for…of needs to be rewritten to an older ECMAScript version, the effect depends on whether this supports any iterable (e.g. TypeScript "downlevelIteration":true) or just array-like objects via index (e.g. TypeScript "downlevelIteration":false. If it supports any iterable, it’s probably always slow and it cannot be optimized well. If it works via index, it’s probably always fast.