본문 바로가기
What I Read

Is Your Code Slow? Avoid These 19 Common JavaScript and Node.js Mistakes

by ウリ김영은 2023. 11. 16.

✔️ 읽기 전에

이 글은 제가 기록용으로 영어 원문을 요약해놓은 글 입니다. 설명이 부족하다고 느끼신다면, 링크를 통해 원문을 참고해주시면 좋을 것 같습니다.


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

 


원문

https://hackernoon.com/is-your-code-slow-avoid-these-19-common-javascript-and-nodejs-mistakes?source=rss&ref=dailydev