惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

量子位
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
F
Fortinet All Blogs
博客园 - 聂微东
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Hugging Face - Blog
Hugging Face - Blog
V
Visual Studio Blog
小众软件
小众软件
有赞技术团队
有赞技术团队
雷峰网
雷峰网
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
AWS News Blog
AWS News Blog
C
Cisco Blogs
美团技术团队
T
Threat Research - Cisco Blogs
C
CERT Recently Published Vulnerability Notes
人人都是产品经理
人人都是产品经理
宝玉的分享
宝玉的分享
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
酷 壳 – CoolShell
酷 壳 – CoolShell
Stack Overflow Blog
Stack Overflow Blog
W
WeLiveSecurity
D
DataBreaches.Net
博客园 - 司徒正美
Blog — PlanetScale
Blog — PlanetScale
IT之家
IT之家
云风的 BLOG
云风的 BLOG
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Simon Willison's Weblog
Simon Willison's Weblog
Google DeepMind News
Google DeepMind News
T
The Blog of Author Tim Ferriss
Know Your Adversary
Know Your Adversary
NISL@THU
NISL@THU
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
The Cloudflare Blog
Vercel News
Vercel News
月光博客
月光博客
T
Tailwind CSS Blog
H
Help Net Security
aimingoo的专栏
aimingoo的专栏
P
Proofpoint News Feed
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Spread Privacy
Spread Privacy
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Cisco Talos Blog
Cisco Talos Blog
Microsoft Security Blog
Microsoft Security Blog
V
V2EX
WordPress大学
WordPress大学
Cyberwarzone
Cyberwarzone
Recent Announcements
Recent Announcements

Secret Weblog

Becoming More Xee: A Modern XPath and XSLT Engine in Rust Looking for new challenges! Repeat Yourself, A Bit The Curious Case of Quentell The Humble For Loop in Rust Don Question Best Practices I Was a 1980s Teenage Programmer Part 5: Achieving Assembly I Was a 1980s Teenage Programmer Part 4: The Call of Assembly The Tooling Shift I Was a 1980s Teenage Programmer Part 3: MSX-2 JavaScript: when you need two ways to do it! Empowering Programming Languages Bloat and Retrofuturism Refreshing my Blog Again Random Rust Impressions Apilar: An Alife System I Was a 1980s Teenage Programmer Part 2: Olivetti M24 I Was a 1980s Teenage Programmer: the Alphatronic SolidJS fits my brain Is premature optimization the root of all evil? Framework Patterns: JavaScript edition Roll Your Own Frameworks Framework Patterns Secret Weblog Highlights Refactoring to Multiple Exit Points mstform: a form library for mobx-state-tree Seven Years: A Very Personal History of the Web Morepath 0.16 released! Is Morepath Fast Yet? Introducing Bob Strongpinion Punctuated Equilibrium in Software Morepath 0.15 released! Impressions of React Europe 2016 Morepath 0.14 released! Morepath 0.13 now with Dectate Dectate: advanced configuration for Python code JavaScript Dependencies Revisited: An Example Project The Incredible Drifting Cyber A Brief History of Reselect The Emerging GraphQL Python stack Thoughts about React Europe Build a better batching UI with Morepath and Jinja2 GraphQL and REST Server Templating in Morepath 0.10 10 reasons to check out the Morepath web framework in 2015 A Review of the Web and how Morepath fits in Morepath 0.9 released! Better REST with Morepath 0.8 Morepath 0.7: new inter-app linking They say something I don Life at the Boundaries: Conversion and Validation BowerStatic 0.4 released! Morepath 0.6 released! Morepath 0.5(.1) and friends released! New HTTP 1.1 RFCs versus WSGI Against On Naming In Open Source My visit to EuroPython 2014 Morepath 0.4.1 released (with Python 3 fixes) Morepath 0.4 and breaking changes Announcing BowerStatic Morepath 0.3 released! Morepath 0.2 Morepath Python 3 support The Call of Python 2.8 Morepath 0.1 released! WebOb and Werkzeug compared Morepath: from Werkzeug to WebOb Racing the Morepath: SQLAlchemy Integration The Centre Cannot Hold Breaking Morepath Changes Morepath Update How to do REST with Morepath Morepath Security the Gravity of Python 2 #python2.8 discussion channel on freenode Alex Gaynor on Python 3 Morepath Documentation Starting to Take Shape Back to the Center Morepath App Reuse Implementing Grok Grok: the Idea Why Linux Works for Me On the Morepath Reg, Now With More Generic! The New Zope as a Web Framework Jim Fulton, Zope Architect Renewing Zope Object Publishing The Weirdness of Zope The Rise of Zope My Exit from Zope Reg: Component Architecture Reimagined JSConf EU 2013 impressions Obviel 1.0! JS Dependency Tools Redux Obviel 1.0rc1 released! Succinct data structures
The Humble For Loop in JavaScript
Martijn Faassen · 2024-12-11 · via Secret Weblog

I've seen some programmers try to avoid the humble for loop at all costs, in favor of more functional abstractions. I'm going to argue that the for loop is sometimes simply the best option. That doesn't mean you should always use it -- far from it -- but it does mean you should give it due consideration. The goal is to help develop some intuitions about this topic.

I talk about readability of code in this post, but I also talk about performance. My argument will be that there are cases where the for loop combines readability with performance.

I'm focusing on JavaScript as the example language, but it can stand in for many languages with similar characteristics like Python and Ruby. But the culture of JavaScript leans more towards functional programming patterns than these other languages, so I think it's the appropriate language to highlight.

Map

Functional programming patterns have been in vogue in the JavaScript world for a long time. And for good reason, as:

let result = [];
for (let entry of list) {
    result.push(transform(entry));
}

where we say we want to make a new array from an original one where each entry has been transformed using the transform function, is more involved and less declarative than:

let result = list.map(transform)

where we express the same. So using .map() is cool!

Reduce

I've run into JavaScript programmers who insist on using functional patterns in all cases, even though at the same time they struggled with implementing them. Maybe functional programming was considered a best practice by them? Perhaps being able to figure it out is considered to be a badge of being a good developer?

Let's look at reduce. .reduce(), if you're not familiar with it, takes in some collection of things and condenses it down to something else. Summing all the numbers in a list can be implemented as reduce:

let sum  = list.reduce((total, current) => total + current, 0);

This says "in order to get the sum, we add the total of everything else in the list to the value of the current item". And we start the sum with 0.

Reduce is cool, reduce is very functional, but reduce in JavaScript isn't the best fit for all purposes. Consider this one:

let flat = list_of_lists.reduce((accumulator, list) => [...accumulator, ...list], []);

This flattens a list of lists into a single list. So, [[1, 2, 3], [4, "five", 6], [7]] becomes [1, 2, 3, 4, "five", "6, 7]. 1

Let's compare it with the for loop:

let flat = [];
for (let entry of list) {
	flat.push(...entry);
}

For me, and I think for a lot of developers, the for loop is easier to understand. While I can see that for some reduce might be superior in readability, I think we all can agree that the for loop is very readable.

What is more important is that the for loop version is far more efficient. In my experiment the for loop version was about 200 times faster on a large list of about 10,000 entries. The implementation with reduce() constructs arrays all over the place, whereas the for loop version modifies the accumulator in-place. For small lists that doesn't matter much, but if you expect larger lists, it's going to matter in both processing time and memory usage. And even with very small lists the for loop version is still faster.

You could of course cheat in the reduce version and modify the accumulator in place too, just like the for loop does:

let flat = list_of_lists.reduce((accumulator, list) => {
    accumulator.push(...list);
    return accumulator;
}, []);

This code has indeed about the same performance as the for loop, but it destroys the "but this code is declarative" argument. It becomes more difficult to understand what's going on. Mutating the argument could in fact risk breaking the algorithm, and reasoning about that takes mental effort I don't want to be spending.

So, the for loop is a clear winner over this one from a readability perspective, and it's a little bit faster too, so the for loop wins.

The lesson

I think we can generalize this into a heuristic: whenever you are constructing a collection (like an array or hashmap) out of another collection, the for loop is worth your consideration, as it may have favorable trade-offs in both readability and performance. This heuristic applies to many programming languages. 2

I don't want people to take home the wrong lesson, however. What we examined here was a case where the for loop version is easy to understand and implement, and I'm arguing you should give it serious consideration in this case.

In other contexts using reduce makes complex code a lot more easy to implement and understand. And objects (when not used as a hash table) are really easy to combine, as long as you don't mutate them. Even if a for loop version were a lot faster, that very well may not be worth it in your context.

Conclusion

Consider the for loop, as in quite a few cases it results in code that's just as readable if not more so, and has better performance. The performance aspect is not surprising, given that for loops and mutability are a relatively low level constructs, and computers like those, but the readability benefits are sometimes ignored.

The intuition that for loops are sometimes plain better carries across to many other programming languages. What are the trade-offs for the humble for loop in Rust? Here is another article for your reading pleasure. You should be able to follow along even if you don't know Rust.

1

Yes, I know flat exists. I'm using flattening as an example of a task where you're constructing a structure (like an array, object or hashmap) out of another structure.

2

In a language like Haskell the culture is to use linked lists to express lists rather than arrays. These have different properties: linked lists can be combined cheaply but random access (list[i]) is linear rather than constant time, and iteration is going to somewhat more slowly than in arrays too.

This kind of easy to combine data structure is a Persistent data structure. You can use these in other languages too, including JavaScript. Objects can be used like this, as long as you don't mutate them. For lists and hashmaps using persistent data structures is far less common in JavaScript. A consideration is whether breaking with built-in data structures and cultural traditions in APIs is worth it for your purposes.