Recently, Kent C. Dodds posted about an instance where using a for loop dramatically resulted in better performance.
When you delve into how filter
, map
and reduce
methods work (based on callbacks), really it is of no surprise to anyone who sees the results for themselves that these methods are a lot slower.
Replying to Kent's Tweet, I mentioned how I always use for loops and how their performance is unrivalled:
Really, it did not take long for the defensive Tweets from other devs on Twitter.
For loops are not just about optimisation
Yes, for loops are faster and yes, if you're not careful they can have unintended side effects as a result of not using better-suited methods like filter
or reduce
, but I don't use for loops with the sole purpose of optimising my code.
In most cases, you won't notice any difference in performance for small data sets. When you're dealing with tens of thousands of items like Kent was, then you definitely will.
If you are working with promises and want to use async/await
then you need to use a for loop to do that as those beloved one-liner methods do not support them very well.
If you want to be able to control the flow of the loop (break out of it), then you will want to use a for loop.
While for loops will add to the size of your code and not be as "clean", we need to stop focusing on clean code and focusing on clear code. To me, nothing is more immediately clear than a for loop.
I am also not advocating against using those specific methods. I still use filter
, map
and reduce
where applicable. In most instances, I find for loop is easier to write, even if it usually results in more code.
As front-end and Javascript developers, we need to stop pretending there is one right and true way of doing something in Javascript. Because there is not. Everyone has opinions and preferences, we need to accept that we are all different.
Top comments (19)
I usually opt for a loop due to simplicity and readability, not necessarily performance. I found the premature optimization comment that you received pretty shallow because of that. With something like
.reduce(...)
, there is mental overhead to parse it and figure out what is going on. If it isn't calculating some type of sum or total, then it is being used only for the loop aspect. In that case, just use a loop!It seems like many JavaScript devs look at a collection that they want to loop over and say "hmmm how can I use a SICK reduce on this". It is clever and shorter, but is it more readable? Most of the time, I would disagree.
JavaScript is a language that has some functional features incorporated into it, which is great. But I think taking that to the extreme and trying to make everything functional is not always the best approach. I'm not sure why the functional stance has become so prevalent in the JavaScript community.
You hit the nail on the head here, Ryan. I have no idea where the functional mindset originated from. I know quite a few JS "thought leaders" are strong advocates for the functional approach and I honestly think React shares a lot of the blame for this in recent years.
Because of design choices and mistakes in React itself lending itself to things like hooks being created, their anti-classes/oop approach has resulted in an ecosystem of developers who don't question the approach and blindly assume that Facebook is the be-all and end-all of JS and follows them into the mouth of the volcano.
Just based on my experiences (and sounds like yours as well) I have seen what happens when "clever coders" think they are writing "clever code" and it always almost results in the code being rewritten eventually because it's trying to be too smart and nobody can work out what it is doing.
I have seen a lot of people advocating for using
map
as a means of looping over items in arrays where a for loop would be more appropriate. And in the case ofreduce
especially, I have seen some pretty crazy stuff.And truth to be told, I have fallen into some traps with
reduce
before forgetting the first argument in the initial value or the previous value, because I am used to how methods likeforEach
return the value in the iteration first.My ex colleague used to use fancy
reduce
all the time which made it so much harder for me as a Junior to understand what was going on. Fancy one-liners are cool on codewars or similar sites but not so much in actual code that everybody in the team needs to understand.Agree
So, I've noticed this pattern in my code a lot:
Knowing that
for ... of
loops are not as fast, I started using the following construct:This post got me wondering how my loop compares, so I tested my way and other loop constructs here. It's interesting how much slower these other loop constructs are. Needless to say, I'm going back to the traditional for loop now!
Here are my simple test results:
I ran your tests on a longer array, and found increasing the array length magnifies the performance advantage of the vanilla for-loop quite a bit:
I used an array of length 1000, and since I'm impatient I cut down LOOPS to 1e5.
Also to be noted is that the 'shift' loop is not equivalent to the others, because it destroys the input array in the process. I dealt with that (and with creating a long array) by setting up
const someNumbers = Array.from({ length: 1000 }, (v, i) => i);
on the top level, and copying it into testArrayShift() on each iteration of the outer loop.
It runs a bit against my intuition that the reverse loop becomes slower on longer arrays -- I expected it to have a (small) performance advantage.
Actually I'm used to writing the reverse loop as
(and that's the version I actually tested)
but again a surprise: your version is actually a bit faster.
Altogether, I guess I'll just stick to vanilla for-loops from now on.
I added
.reduce()
to the test, and on a long array it's only about twice as slow as the vanilla for-loop.For loops can be clean. In ES6 we got for..of loops, they are declarative and work with more data types than just arrays.
My only problem with for loops is that it takes discipline to extend their behavior in a "clean way." It is just so easy to go and add little
if
statement here and there (specially if you are under pressure) and before you know it you have block of code that is dificult to deal with.I love
for..of
loops, they are the only kind of for loop I really write these days. Especially being able to do something likefor (const value of object.entries())
which makes working with different types of data (not just arrays) so much nicer.There are definitely some traps you can fall into (side effects from modifying the reference if you forget to create a new array/collection) but if you're aware of them, it's not much of a problem.
Thank you for this article. I've used a lot of for loop, and recently i feel like using for loop makes me something like an "outdated" js coder. But yeah thanks, now when I use for loop I have the mindset that it gives better performance, and the future of js frameworks is all about performance.
Definitely nothing wrong with using for loops. I do recommend knowing and learning the functional methods
filter
,map
andreduce
but you don't have to use them in most cases.It's sad that JS has got to a point where a staple of every language (a for loop) has become something many in the community argue against and seemingly for no good reason.
well around 2-3 months ago i always use
for loop
almost all the time, but then since I started using reactjs, I use nowfilter
andmap
only if don't need a break statementI rarely use reduce because I never remember how to use it lol. The reason why I would use filter or map over a normal loop is for readability. If I see a
filter
I can already guess what I was trying to do in that part of the code rather than seeing a for loop and guessing. That being said I'm not working on large data so performance is never really a big concern.That last paragraph really hits the nail on the head. Javascript was designed so that there are many ways to write things, so obviously the best way to write something is how you prefer it.
These people who care more about everyone doing what they are doing than actually writing good code are toxic.
I'd definitely agree with you for Reduce, I find the way that works and is structured to just be inherently confusing.
Map and Filter though I will usually default to, rather than a for loop. For me it's all about reducing intellectual overhead and ensuring my hands can keep up with my brain so that I don't break my flow.
At one point I was somewhat well known in the Haskell community, so functional programming feels very natural to me. And I only use for loops in JavaScript, since I have found the error messages and debugging support for map/filter/fold to be too unpleasant to use.
Really good stuff. This is similar to what I heard Kyle Simpson say he is so intentional about what variable declaration he uses and where. It was cool he said that when he is using a variable that will be scoped and used though out a function he would use var to declare the variable but when creating a for loop and declaring a variable inside of it or to declare his iterator he would use let. He explained the more in the code he sticks to this thought process hopefully the easier it is to read. It really inspired me to not just ever say one thing fits all like always use === or filter or reduce they all have their place let's just be intentional.
Nothing wrong with loops indeed. However I find filter, reduce and map useful as they are immutable and can be chained. How do you deal with these use cases?