✔️ 읽기 전에
이 글은 제가 기록용으로 영어 원문을 요약해놓은 글 입니다. 설명이 부족하다고 느끼신다면, 링크를 통해 원문을 참고해주시면 좋을 것 같습니다.
1. Improper Variable Declarations and Scope => Declare variables locally inside functions whenever possible
2. Inefficient DOM Manipulation
const ul = document.getElementById('list');
for (let i = 0; i < 10; i++) {
const li = document.createElement('li');
li.textContent = i;
ul.appendChild(li);
}
it is better to build a string first then set .innerHTML
const ul = document.getElementById('list');
let html = '';
for (let i = 0; i < 10; i++) {
html += `<li>${i}</li>`;
}
ul.innerHTML = html;
Building a string minimizes reflows => only update DOM once instead of 10 times
For multiple updates,
build up changes, then apply at the end.
or,
use DocumentFragment to batch appends.
3. Excessive DOM Manipulation => use Throttle
frequent DOM updates can crush performance
it is better to throttle updates
let chatLogHTML = '';
const throttleTime = 100; // ms
// New message received
chatLogHTML += `<div>${messageText}</div>`;
// Throttle DOM updates
setTimeout(() => {
chatLog.innerHTML = chatLogHTML;
chatLogHTML = '';
}, throttleTime);
for highly dynamic UIs, consider virtual DOM libraries like React. These minimize DOM manipulation using a virtual representation
4. Lack of Event Delegation
Event delegation utilizes event bubbling. One listener can handle events from multiple descendants.
const table = document.querySelector('table');
table.addEventListener('click', e => {
if (e.target.classList.contains('delete')) {
handleDelete(e);
}
});
5. Inefficient String Concatenation => better to use array ✔️
creating new strings requires memory allocatin. better to use array
const parts = [];
for (let i = 0; i < 10; i++) {
parts.push('<div>', i, '</div>');
}
const html = parts.join('');
building an array minimizes intermediate strings. join()
concatenates once at the end.
also consider template literals for embedded values.
6. Unoptimized Loops ✔️ => caching length
Loops often cause performance problems in JS.
// a common mistake is repeatedly accessing array length
const items = [/*...*/];
for (let i = 0; i < items.length; i++) {
// ...
}
caching length improves speed.
//better
const items = [/*...*/];
const len = items.length;
for (let i = 0; i < len; i++) {
// ...
}
other optimizations include hoisting invariants out of loops, simplifying termination conditions and avoiding
expensive operations inside iterations.
7. Unnecessary Synchronous Operations
8. Blocking the Event Loop
9.. Inefficient Error Handling => Handle all errors explicitly
unhandled errors often lead to memory leaks or data corruption
//bad
try {
// ...
} catch (err) {
console.error(err); // just logging
}
Logging isn't enough.
Clean up artifacts, notify users, and consider recovery options.
Use tools like Sentry to monitor errors in production.
//better
try {
// ...
} catch (err) {
console.error(err);
// Emit error event
emitError(err);
// Nullify variables
obj = null;
// Inform user
showErrorNotice();
}
10. Memory Leaks
Memory leaks happen when memory is allocated but never released.
Leaks accumulate and degrage performance
- uncleaned-up event listeners
- Outdated references to deleted DOM nodes
- Cached data that's no longer needed
- Accumulating state in closures
const data = [];
// Use closure to accumulate data
return function() {
data.push(getData());
}
}
const processor = processData();
// Long running...keeps holding reference to growing data array!
to fix:
- Use weak references
- Clean up even listeners
- Delete no-longer-needed references
- Limit closured state size
11. Overuse of Dependencies
Only import what you need.
Review dependencies regularly to prune unused ones.
Keep bundles lean and minimize dependencies.
//bad
import _ from 'lodash';
import moment from 'moment';
import validator from 'validator';
// etc...
//good
import cloneDeep from 'lodash/cloneDeep';
import { format } from 'date-fns';
import { isEmail } from 'validator';
12. Inadequate Caching
//bad
function generateReport() {
// Perform expensive processing
// to generate report data...
}
generateReport(); // Computes
generateReport(); // Computes again!
//good
let cachedReport;
function generateReport() {
if (cachedReport) {
return cachedReport;
}
cachedReport = // expensive processing...
return cachedReport;
}
other strategies:
- Memory caches like Redis
- HTTP caching headers
- LocalStorage for client caching
- CDNs for asset caching
13. Unoptimized Database Queries
14. Improper Error Handling in Promises
Promises simplify asynchronous code, but unhandled rejections are silent failures.
//bad - if fetch rejects, exception goes unnoticed
function getUser() {
return fetch('/user')
.then(r => r.json());
}
getUser();
//good
function getUser() {
return fetch('/user')
.then(r => r.json())
.catch(err => console.error(err));
}
getUser();
other tips:
- Avoid promise nesting hell
- Handle rejections at the top level
- Configure unhandled rejection tracking
15. Synchronous Network Operations => use callbacks or promises
Network requests should be asynchronous, but sometimes sync variants get used.
//bad - installs the event loop during the request
const data = http.getSync('http://example.com/data'); // blocks!
instead, use callbacks
//good
http.get('http://example.com/data', res => {
// ...
});
or promises
fetch('http://example.com/data')
.then(res => res.json())
.then(data => {
// ...
});
16. Inefficient File I/) Operations
17. Ignoring Performance Profiling and Optimization
18. Unnecessary Code Duplication
duplicated code harms maintainability and optimazability.
//bad
function userStats(user) {
const name = user.name;
const email = user.email;
// ...logic...
}
function orderStats(order) {
const name = order.customerName;
const email = order.customerEmail;
// ...logic...
}
// refactor
function getCustomerInfo(data) {
return {
name: data.name,
email: data.email
};
}
function userStats(user) {
const { name, email } = getCustomerInfo(user);
// ...logic...
}
function orderStats(order) {
const { name, email } = getCustomerInfo(order);
// ...logic...
}
other fixes:
- Extract utility functions
- Build helper classes
- Leverage modules for reusability
Conclusions
Minimizing DOM changes, leveraging asynchronous techniques, eliminating blocking operations, reducing dependencies, utilizing caching and removing unneeded duplication
원문