-
Section 1: Getting Started
-
Section 2: Core Syntax and Types
-
Section 3: Strings and Numbers in Depth
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Objects and Arrays
-
Section 7: Maps, Sets, and Symbols
-
Section 8: Asynchronous JavaScript
-
Section 9: Object-Oriented and Prototypes
-
Section 10: The DOM
-
Section 11: Browser APIs
-
Section 12: Modern JavaScript (ES2015-ES2025)
-
Section 13: Functional Programming Patterns
-
Section 14: Error Handling and Debugging
-
Section 15: Testing
-
Section 16: Accessibility for JavaScript Developers
-
Section 17: Internationalization and Localization
-
Section 18: Performance
-
Section 19: Node.js Fundamentals
-
Section 20: Regular Expressions
-
Section 21: Design Patterns in JavaScript
-
Section 22: Security Basics
-
Section 23: Data Structures and Algorithms in JavaScript
-
Section 24: Practical Projects
-
Section 25: More Advanced Async Patterns
-
Section 26: More Object and Class Practice
-
Section 27: Working with Dates and Internationalization
-
Section 28: Web Components
-
Section 29: More DOM and Browser Practice
-
Section 30: Build Tooling for Vanilla JavaScript
-
Section 31: More Practice Projects
-
Section 32: Interview and Algorithm Practice
-
Section 33: Error Objects (MDN Reference)
-
Section 34: TypedArrays and Binary Data
-
Section 35: Reflection and Metaprogramming (MDN Reference)
-
Section 36: More Global Functions (MDN Reference)
139: Optimizing Loops and Large Data Processing
When you're dealing with a few hundred items in an array, you can write whatever code you want. You can chain five different array methods together, and the browser won't even blink. But once you hit 100,000 or a million records, the "elegant" way of writing JavaScript can actually become a liability. I've seen production apps freeze for seconds just because someone decided to be too "functional" with a massive dataset.
Let's build a small utility that processes a huge set of sensor readings. We want to filter out "noisy" data (readings below a certain threshold) and then calculate the total average of the remaining values.
The "Readable" but Expensive Chain
My first instinct is usually to go for readability. I'll start with a chain of .filter() and .reduce(). It looks clean, and it's easy for another dev to understand at a glance.
const readings = Array.from({ length: 1000000 }, () => Math.random() * 100);
console.time('Chain-Method');
const total = readings
.filter(val => val > 10)
.reduce((acc, val) => acc + val, 0);
console.timeEnd('Chain-Method');
Here is the problem: .filter() creates a brand new array. If my original array has a million elements and 900,000 pass the filter, JavaScript just allocated a massive amount of memory for a temporary array that exists for about half a second before the .reduce() finishes and the garbage collector has to come clean it up. That "stop-the-world" garbage collection is where the stuttering in your UI comes from.
Wait, why did I use for...in?
I'll be honest—sometimes I try to optimize too quickly and make a rookie mistake. I remember once trying to speed up a loop by switching to a for...in loop thinking it was a shorthand for indices. I did this in a similar project:
// DON'T DO THIS
for (let index in readings) {
if (readings[index] > 10) {
total += readings[index];
}
}
I quickly realized my mistake when the performance actually dropped. for...in is designed for iterating over object properties, not array elements. It iterates over all enumerable properties, including ones inherited from the prototype chain, and it treats the index as a string, not a number. It's significantly slower than a standard loop. I scrapped that immediately.
Flattening the Loop for Maximum Throughput
To actually optimize this, we need to do everything in a single pass. We want to avoid creating any intermediate arrays and avoid any unnecessary function calls inside the loop (since calling a callback function a million times adds up).
The "boring" for loop is almost always the fastest way to process large data in JavaScript. Let's rewrite our sensor logic:
const readings = Array.from({ length: 1000000 }, () => Math.random() * 100);
console.time('Optimized-Loop');
let sum = 0;
let count = 0;
for (let i = 0; i < readings.length; i++) {
const val = readings[i]; // Cache the value to avoid repeated array access
if (val > 10) {
sum += val;
count++;
}
}
const average = sum / count;
console.timeEnd('Optimized-Loop');
By doing this, we've moved from O(2n) (two passes over the data) to O(n) (one pass), and we've reduced our memory overhead from O(n) to O(1). We aren't creating a temporary array; we're just updating two numbers in memory.
A Note on Memory Locality
You'll notice I used const val = readings[i]. In a small loop, this doesn't matter. In a million-iteration loop, reducing the number of times the engine has to look up an index in the array can provide a tiny but measurable boost. It's not always necessary, but when you're in "optimization mode," every micro-decision counts.
📋 Practical Task
Optimizing the Transaction Log Processor
You have been given a dataset of 500,000 transaction objects. Each object looks like { id: 123, amount: 50.00, status: 'completed' }. Currently, the code uses a combination of .filter() and .map() to get an array of amounts for only the 'completed' transactions, and then calls another function to sum them up.
Your Task: Rewrite the processing logic to calculate the total sum of 'completed' transactions using a single for loop. Your solution must avoid creating any intermediate arrays to minimize memory pressure and improve execution speed.
const transactions = Array.from({ length: 500000 }, (_, i) => ({
id: i,
amount: Math.random() * 100,
status: Math.random() > 0.2 ? 'completed' : 'pending'
}));
// Rewrite the logic below to optimize for performance
let totalCompletedAmount = 0;
// Your optimized loop goes here...
console.log(`Total: ${totalCompletedAmount}`);
There are no comments for now.