You REALLY Shouldn’t Use the Array.reduce Method!

Jason Knight
5 min readJul 23, 2020

Bold statement I realize, but stick with me here.

The reduce method, alongside other Array methods like map, foreach, and so forth have a number of issues. When combined with the needlessly cryptic arrow functions they compromise code clarity. When used for simple operations or even complex ones, they oft introduce the overhead of “functions for nothing” dragging down performance. Finally? We have “for/of” now.

What do you mean? “Functions for nothing”

Using functions when you don’t need to is one of the worst things you can do in any programming language, no matter how hot and trendy it is right now to divide every blasted line of code into its own function, or WORSE its own flipping class in its own pointless separate file.

EVERY time you call a function in ANY language the current program state has to be saved, parameters pushed to the stack or a stack-like structure, memory allocated for locals, and then on exit the entire process reversed. This is only exacerbated by the waste of newer “Features” like LET/CONST where even more processing is required to keep track of them, and they end up even less prone to release themselves from memory than the already wonky VAR.

If you make a function just for a single line operation, you are likely making harder to read, harder to manage, memory wasting, cpu wasting, slow code.

Array.forEach is no better!

Just look at the original article’s examples.

Using foreach:

const total = (arr) => {
let result = 0;
arr.forEach(num => { result += num });
return result;
};

Using reduce:

const total = (arr) => arr.reduce((result, num) => result + num, 0)

BOTH of these are painfully cryptic thanks to arrow functions, have memory issues due to the use of const/let, and are performance JUNK due to the use of callback functions.

Even an old traditional approach:

function arrTotal(arr) {
for (
var i = 0, result = 0, iLen = arr.length;
i < iLen;
i++
) result += arr[i];
return result;
}

… whilst looking like a good deal of code, runs circles around the “newer” approach in everything except V8 (chrome-likes, node.js)… and even in V8 it is neck in neck with with the reduce version!

But if we do “for/of”:

function arrTotal(arr) {
var result = 0;
for (var value of arr) result += value;
return result;
}

We have very clean, easy to follow/understand code, that regardless of browser is faster!

Testing it!

I put together a demo on JSPerf to show this:

Here are screencaps showing how it does in Chrome (well, Vivaldi), Firefox, and Edge on my “piddly little” Ryzen 3600.

(Sorry no Safari, no OSX access right now)

reduce vs. old is a wash, for/of kicks names and takes a**
Edge (Well, Chakra) overhead for functions is off the cuff. Even the old “for” loop method is blazing fast by comparison.
Firefox is better than Edge on the function overhead, but still not blowing my skirt up.

From these results, I suspect that V8 at some level tries to optimize for bad code. This is probably why its memory usage is so ridiculous at times as the only way I can think of (being an Assembly language programmer) to get around these problems would be to pre-allocate memory and then refuse to let it go so long as the page/environment isn’t released.

In all cases though for/of utterly and thoroughly trashes all other approaches.

How is that even possible?!?

Take this analysis with a grain of salt, I’ve never actually delved into the code of a JavaScript interpreter. I’ve simply built my own interpreters and compilers in the past so I’ve some clue how such problems are solved at the low level.

I know for a fact that under the hood JavaScript arrays are NOT actual arrays in the sense programmers of most compiled languages would use the term. They are in fact pointered lists that the interpreter PRETENDS are an array.

Iterating through a pointered list if it is linear is much akin to walking siblings on the DOM, whilst accessing via a numerical index requires either the maintenance of a lookup table (slow) or iterating through each and every blasted element via walking to find the index you want. (even slower) GUESSING here I’d say a combination of both is likely used, and that may vary by browser.

I would compare it to how:

var testLI = document.querySelectorAll('#test > li');

for (var i = 0, iLen = testLI.length; i < iLen; i++) {
testLI[i].textContent = 'test #' + i;
}

… is PAINFULLY slow compared to:

var li = document.getElementById('test').firstElementChild;if (li) do {
li.textContent = 'something';
} while (li = li.nextElementSibling);

Even if we don’t count the querySelectorAll against it, because rather than screwing around building nodeLists and then iterating across it — which again all JavaScript iterators are slow — we skip right to the DOM itself which is a pointer based list (well, tree actually) in its own right. The pointers to the first and next elements already exist so there’s no extra “work” involved.

“For/of” likely uses the internal pointered list of Array, much akin to how “Iterable” works — I would expect the Array methods would/should do the same, but since they maintain an index I’m not so sure — which combined with the lack of excess function overhead makes it blazing fast.

Conclusions:

The JavaScript Array methods that rely on callbacks are slow and inefficient because of procedural overhead. They compromise code legibility (more so if that cryptic “arrow function” trash is used), and if you’re going to use newer features that break legacy browser support, for/of is vastly superior in most cases.

… and Array.foreach is utter and total incompetent garbage.

--

--

Jason Knight

Accessibility and Efficiency Consultant, Web Developer, Musician, and just general pain in the arse