Skip to content

Latest commit

 

History

History
193 lines (191 loc) · 7.1 KB

File metadata and controls

193 lines (191 loc) · 7.1 KB
technology JavaScript
domain frontend
level Senior/Architect
version ES2024+
tags
javascript
advanced
best-practices
clean-code
scalable-code
ai_role Senior JavaScript Expert
last_updated 2026-03-22

🧠 JavaScript Professional & Niche Best Practices (Senior Level)

⬆️ Back to Top

📖 Context & Scope

  • Primary Goal: Detail advanced and niche topics in JavaScript for high-performance applications.
  • Target Tooling: Cursor, Windsurf, Antigravity.
  • Tech Stack Version: ES2024+

⚡ IV. Professional & Niche (Senior Level)

⚡ 31. Memory Leaks: Unremoved Event Listeners

Note

Context: Long-lived applications (SPAs).

❌ Bad Practice

window.addEventListener('resize', () => this.handleResize());
// Component unmounts, but listener remains

⚠️ Problem

The event listener keeps a reference to the component/context, preventing garbage collection even after the component is destroyed.

✅ Best Practice

const handler = () => this.handleResize();
window.addEventListener('resize', handler);
// Cleanup:
window.removeEventListener('resize', handler);

🚀 Solution

Always remove event listeners in cleanup phases (e.g., componentWillUnmount or useEffect return). Use AbortController for an even more modern approach to listener cleanup.

⚡ 32. Memory Leaks: Forgotten Intervals/Timeouts

Note

Context: Managing temporal background tasks.

❌ Bad Practice

setInterval(() => {
    fetchStatus();
}, 1000);

⚠️ Problem

Intervals continue to run indefinitely until the page is closed, even if the data they process is no longer needed, consuming CPU and memory.

✅ Best Practice

const intervalId = setInterval(fetchStatus, 1000);
// Later:
clearInterval(intervalId);

🚀 Solution

Store the ID returned by setTimeout or setInterval and clear it when the task is no longer relevant.

⚡ 33. Closures inside loops (Memory/Scope issues)

Note

Context: Understanding the Event Loop and closure capture.

❌ Bad Practice

for (var i = 0; i < 5; i++) {
    setTimeout(() => console.log(i), 100); // Prints '5' five times
}

⚠️ Problem

var is function-scoped. By the time the setTimeout executes, the loop has finished and i is 5. Each closure shares the same reference to i.

✅ Best Practice

for (let i = 0; i < 5; i++) {
    setTimeout(() => console.log(i), 100); // Prints 0, 1, 2, 3, 4
}

🚀 Solution

Use let in loop headers. It creates a new binding for each iteration, ensuring the closure captures the value of i at that specific moment.

⚡ 34. Throwing Strings instead of new Error()

Note

Context: Ensuring useful stack traces.

❌ Bad Practice

throw 'Something went wrong';

⚠️ Problem

Throwing a string provides no stack trace. It makes it nearly impossible to determine where the error originated in a complex call stack.

✅ Best Practice

throw new Error('Something went wrong');

🚀 Solution

Always throw an instance of Error (or a subclass). This captures the stack property, which is vital for debugging.

⚡ 35. Modifying Built-in Prototypes

Note

Context: Ecosystem compatibility and stability.

❌ Bad Practice

Array.prototype.last = function() {
    return this[this.length - 1];
};

⚠️ Problem

"Monkey patching" built-ins can lead to collisions if a future ECMAScript version implements a method with the same name but different behavior. It also breaks for-in loops if not handled carefully.

✅ Best Practice

const last = (arr) => arr[arr.length - 1];

🚀 Solution

Use utility functions or wrapper classes instead of modifying global prototypes.

⚡ 36. Premature Optimization (e.g., bitwise for rounding)

Note

Context: Readability vs Micro-benchmarks.

❌ Bad Practice

const floor = ~~x; // Double bitwise NOT to floor

⚠️ Problem

While ~~ is slightly faster in some engines, it makes the code cryptic. It also only works for numbers within the 32-bit integer range.

✅ Best Practice

const floor = Math.floor(x);

🚀 Solution

Prioritize readability. Modern JIT compilers are smart enough to optimize Math.floor. Only use bitwise tricks if profiling proves it's a critical bottleneck in a hot path.

⚡ 37. V8 Hidden Classes: Changing object shape after initialization

Note

Context: V8 JIT optimization.

❌ Bad Practice

function User(name) {
    this.name = name;
}
const u1 = new User('Alice');
u1.age = 25; // Dynamically adding property

⚠️ Problem

V8 creates "Hidden Classes" to optimize object property access. Adding properties after initialization changes the "shape" of the object, causing V8 to drop to a slower "Dictionary Mode" for that object.

✅ Best Practice

function User(name, age) {
    this.name = name;
    this.age = age;
}
const u1 = new User('Alice', 25);

🚀 Solution

Initialize all object properties in the constructor or a factory function. Maintain a consistent object "shape" to keep V8 in the optimized path.

⚡ 38. Array Hole (Sparse Arrays) performance

Note

Context: Memory allocation and JIT optimization.

❌ Bad Practice

const arr = new Array(100);
arr[50] = 'val';

⚠️ Problem

Creating "holes" in arrays makes them "sparse". Sparse arrays are stored differently (as hash maps) which is much slower for iteration and access than "packed" arrays.

✅ Best Practice

const arr = Array.from({ length: 100 }, () => null);

🚀 Solution

Initialize arrays with default values (like null or 0) instead of leaving empty slots. This keeps the array in "packed" mode.

⚡ 39. Using eval() or new Function()

Note

Context: Security and performance.

❌ Bad Practice

const result = eval('2 + 2');

⚠️ Problem

eval() executes strings as code, opening a massive XSS security vulnerability if the string contains user input. It also prevents the JIT compiler from optimizing the surrounding scope.

✅ Best Practice

const result = new Function('a', 'b', 'return a + b')(2, 2); // Slightly better, but still risky
// Better:
const operations = { '+': (a, b) => a + b };

🚀 Solution

Avoid eval(). Use lookup tables, JSON parsing, or safe math libraries to handle dynamic logic.

⚡ 40. Micro-optimizations that hurt readability

Note

Context: Maintaining a healthy codebase.

❌ Bad Practice

for (let i = 0, len = arr.length; i < len; i++) { /* ... */ }

⚠️ Problem

Caching arr.length was necessary 15 years ago. Today, modern engines optimize this automatically. Adding extra variables for micro-gains makes the code harder to read.

✅ Best Practice

for (const item of arr) { /* ... */ }

🚀 Solution

Focus on "Big O" complexity and deterministic, strictly typed code. Only micro-optimize after profiling identifies a specific performance hotspot.