← BackJan 6, 2026

Benchmarking Array Iteration in JavaScript: for-of vs Classic for Loops

A recent performance study on Chrome’s V8 engine reveals that classic indexed loops with a cached length continue to be the fastest choice for large arrays, while for-of can match their speed after sufficient warm‑up. The benchmark exercised five data types across three array sizes, demonstrating how engine optimizations and loop structures influence runtime. For most production code, the ergonomics of for-of remain attractive, but critical paths may still benefit from hand‑tuned indexed loops.

# Benchmarking Array Iteration in JavaScript ## 1. Introduction When iterating over collections in JavaScript, developers often default to the familiar `for (let i = 0; i < arr.length; i++)` pattern. The newer `for…of` syntax, part of ECMAScript 2015, offers a more readable approach but historically carries a performance cost because each iteration requires an iterator object with a `next()` method that returns `{ value, done }`. Modern V8 releases, however, have introduced aggressive loop optimisations that can narrow this gap. This article revisits the debate with a comprehensive benchmark conducted on Chrome 143 (V8 11.11), running on Windows 11 with an AMD Ryzen 5000U. The tests were executed sequentially on **jsbenchmark.com**, with additional warm‑up runs on **jsben.ch** to overcome timeout limits for very large arrays. ## 2. Benchmark Design ### 2.1 Data Sets Five distinct array types were generated: 1. **Integers** – Random `0‑999` values. 2. **Floats** – Random floating‐point numbers up to `1000`. 3. **Strings** – Base‑10 substrings of a random `toString()`. 4. **Objects** – Each element is an object containing an integer, float, and string. 5. **Mixed** – Randomly selected from the four categories above. Each set was created at three sizes: `5 000`, `50 000`, and `500 000` elements. The generation code is reproduced below (for clarity): ```js const N = 5000; // adjust to 50_000 or 500_000 const MAX = 1000; const integers = Array.from({ length: N }, () => Math.floor(Math.random() * MAX)); const floats = Array.from({ length: N }, () => Math.random() * MAX); const strings = Array.from({ length: N }, () => Math.random().toString().replace('.', '')); const objects = Array.from({ length: N }, () => ({ int: Math.floor(Math.random() * MAX), float: Math.random() * MAX, string: Math.random().toString().replace('.', '') })); const mixed = Array.from({ length: N }, () => { switch (Math.floor(Math.random() * 4)) { case 0: return Math.floor(Math.random() * MAX); case 1: return Math.random() * MAX; case 2: return Math.random().toString().replace('.', ''); case 3: return { int: Math.floor(Math.random() * MAX), float: Math.random() * MAX, string: Math.random().toString().replace('.', '') }; } }); ``` A trivial callback `doSomethingWithValue` was used to prevent V8 from eliminating the loop body. ### 2.2 Loop Variants Six loop patterns were compared: | Variant | Implementation | |---------|-----------------| | Classic `for` with `i++` | `for (let i = 0; i < arr.length; i++) { doSomething(arr[i]); }` | | Classic `for` with cached length | `const len = arr.length; for (let i = 0; i < len; i++) { doSomething(arr[i]); }` | | Classic `for` reverse with `i--` | `for (let i = arr.length - 1; i >= 0; i--) { doSomething(arr[i]); }` | | `for…of` | `for (const x of arr) { doSomething(x); }` | | `for…in` | `for (const k in arr) { doSomething(arr[k]); }` | | `Array.forEach` | `arr.forEach(x => doSomething(x));` | Each loop was executed the required number of times to produce statistically stable results. ## 3. Results ### 3.1 Small Arrays (5 000 elements) | Loop | Rank | |------|------| | Classic `for` cached length | 1 | | `for…of` | 1 | | Classic `for` (i++) | 3 | | `forEach` | 3 | | Classic reverse `for` | 5 | | `for…in` | 6 | The classic indexed loop with a cached length and `for…of` delivered comparable performance. The reverse traversal suffered likely due to cache misses, while `for…in` lagged by virtue of additional property look‑ups. ### 3.2 Medium Arrays (50 000 elements) Performance followed the same overall pattern; however, `forEach` began to degrade more noticeably, approaching the speed of the reverse `for`. `for…in` showed a relative drop, underscoring its unsuitability for dense numeric arrays. ### 3.3 Large Arrays (500 000 elements) At this scale, `for…of` initially performed worse than the cached `for`. Likely, the long running duration prevented V8 from fully inlining the iterator logic. To address this, the benchmark was repeated with **warm‑up** iterations: 1. **200 passes** – `for…of` narrowed the gap but remained slightly slower. 2. **1 500 passes** on `jsben.ch` – the `for…of` loop finally matched the indexed loop’s throughput; `forEach` still lagged, hinting at lower optimisation potential for callback closure. These warm‑up experiments confirm that V8’s JIT optimisations require a threshold of repeated executions before achieving maximum speed. ## 4. Discussion * **Classic `for` with cached length** remains the most reliable performance choice across all sizes, thanks to V8’s aggressive loop unrolling and minimal overhead. * **`for…of`** is surprisingly competitive once warmed up, providing an ergonomic syntax without sacrificing speed in most real‑world scenarios. * **`forEach`** is consistently the slowest, as the function call per element and lack of JIT optimisations contribute to overhead. * **`for…in`** should be avoided for array traversal; it is designed for object property iteration. Consequently, if a code path is performance‑critical and will handle very large arrays, the classic indexed loop is still recommended. For the majority of applications where readability and safety against mutation are paramount, `for…of` offers a compelling balance between ergonomics and speed. ## 5. Conclusion V8’s continual optimisation of loop constructs has closed the performance gap between legacy `for` loops and the modern `for…of` syntax. Benchmarking shows that, after sufficient warm‑up, `for…of` can be on par with, or even match, the fastest indexed loop. Nonetheless, extremely large data sets remain a challenge, with `for…of` requiring repeated execution to trigger full optimization. Developers should measure performance in their specific context, but the overall trend encourages the more expressive `for…of` for most iteration scenarios. Happy New Year, and may your loops run smooth and fast in 2026!}