Front-End practice: Top 25+ JavaScript code best practices for development

Front-End practice: Top 25+ JavaScript code best practices for development

Frontend development in JavaScript involves creating user interfaces and handling the presentation layer of web applications. Here are some best practices to follow, along with examples, to ensure a clean and maintainable codebase:

  1. Modularization: Break down your code into smaller, reusable modules. This enhances code readability and makes it easier to manage dependencies.

Example:

// users.js (module)
export function getUsers() {
  // Fetch users from the API or data source
}

// main.js (entry point)
import { getUsers } from './users.js';

getUsers();
  1. Use const and let: Prefer const for variables that won't be reassigned and let for variables that will change.

Example:

const PI = 3.14159;
let count = 0;

count = 10; // Valid
PI = 3; // Error
  1. Avoid global variables: Minimize the use of global variables to prevent polluting the global scope and potential conflicts.

Example:

// Avoid this
let globalVar = 'I am global';

function someFunction() {
  // ...
}

// Use this instead
(function() {
  let localVar = 'I am local';

  function someFunction() {
    // ...
  }
})();
  1. Use arrow functions: Arrow functions provide concise syntax and maintain the lexical this value, reducing the need for bind().

Example:

// Regular function
function add(a, b) {
  return a + b;
}

// Arrow function
const add = (a, b) => a + b;
  1. Avoid polluting the global namespace: Encapsulate your code within a module or an IIFE (Immediately Invoked Function Expression) to avoid global namespace pollution.

Example:

// Instead of
function myFunction() {
  // ...
}

// Use this
(function() {
  function myFunction() {
    // ...
  }

  // Call the function or attach it to the desired scope
  myFunction();
})();
  1. Use modern ES6+ features: Embrace ES6+ features like destructuring, spread syntax, and template literals to write more concise and expressive code.

Example:

// Destructuring
const { firstName, lastName } = user;

// Spread syntax
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combinedArray = [...arr1, ...arr2];

// Template literals
const name = `My name is ${firstName} ${lastName}.`;
  1. Avoid inline styles and use CSS classes: Keep your HTML and JavaScript code separated. Use CSS classes for styling, and manipulate classes with JavaScript instead of inline styles.

Example:

<!-- Bad: Inline style -->
<button style="background-color: #007bff; color: #fff;">Click Me</button>

<!-- Good: CSS class -->
<button class="primary-btn">Click Me</button>
  1. Optimize DOM manipulation: Minimize direct DOM manipulation and use efficient approaches like template literals or libraries/frameworks that efficiently update the DOM.

Example (using template literals):

const data = ['Item 1', 'Item 2', 'Item 3'];

function renderList(data) {
  const list = document.getElementById('list');
  list.innerHTML = '';

  data.forEach(item => {
    const listItem = document.createElement('li');
    listItem.textContent = item;
    list.appendChild(listItem);
  });
}

renderList(data);
  1. Use event delegation: Attach event listeners to parent elements and utilize event delegation for handling events on dynamically added elements.

Example:

<ul id="list">
  <!-- List items will be added dynamically -->
</ul>
document.getElementById('list').addEventListener('click', event => {
  if (event.target.nodeName === 'LI') {
    // Handle click on list item
    console.log(event.target.textContent);
  }
});
  1. Optimize asset loading: Minimize the number of HTTP requests and use techniques like bundling and minification to optimize asset loading.

  2. Error Handling: Always handle errors gracefully to avoid unexpected application crashes and improve the user experience.

Example:

function divide(a, b) {
  if (b === 0) {
    throw new Error('Division by zero is not allowed.');
  }
  return a / b;
}

try {
  const result = divide(10, 0);
  console.log(result);
} catch (error) {
  console.error('An error occurred:', error.message);
}
  1. Use Promises or Async/Await for Asynchronous Operations: Avoid using nested callbacks for asynchronous operations and use Promises or Async/Await to improve code readability and maintainability.

Example using Promises:

function fetchData() {
  return fetch('https://api.example.com/data')
    .then(response => response.json());
}

fetchData()
  .then(data => console.log(data))
  .catch(error => console.error('Error fetching data:', error));

Example using Async/Await:

async function fetchData() {
  try {
    const response = await fetch('https://api.example.com/data');
    const data = await response.json();
    return data;
  } catch (error) {
    throw new Error('Error fetching data:', error);
  }
}

(async () => {
  try {
    const data = await fetchData();
    console.log(data);
  } catch (error) {
    console.error(error.message);
  }
})();
  1. Avoid Manipulating the DOM Directly in Loops: When performing DOM manipulation inside loops, batch the changes or use DocumentFragment to minimize layout thrashing and improve performance.

Example:

// Bad: Directly manipulating DOM in a loop
const list = document.getElementById('list');
for (let i = 0; i < 1000; i++) {
  const listItem = document.createElement('li');
  listItem.textContent = `Item ${i}`;
  list.appendChild(listItem);
}

// Good: Batch changes using DocumentFragment
const list = document.getElementById('list');
const fragment = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) {
  const listItem = document.createElement('li');
  listItem.textContent = `Item ${i}`;
  fragment.appendChild(listItem);
}
list.appendChild(fragment);
  1. Use Debouncing or Throttling for Event Handlers: When handling events that could trigger frequently (e.g., resize or scroll), use debouncing or throttling techniques to reduce the number of function calls and improve performance.

For example, using Lodash's debounce function:

import { debounce } from 'lodash';

function handleResize() {
  // Code to handle window resize
}

window.addEventListener('resize', debounce(handleResize, 200));
  1. Use Semantic HTML: Write meaningful and semantic HTML to improve accessibility, SEO, and maintainability.

Example:

<!-- Bad: Using generic divs -->
<div class="header">
  <div class="title">My Website</div>
</div>

<!-- Good: Using semantic HTML -->
<header>
  <h1>My Website</h1>
</header>
  1. Use ES6 Modules instead of Global Scripts: Organize your JavaScript code into separate modules, and use ES6 import and export statements instead of loading multiple scripts in the global scope.

Example (Module 1):

// module1.js
export function foo() {
  // ...
}

Example (Module 2):

// module2.js
export function bar() {
  // ...
}

Example (Main Script):

// main.js
import { foo } from './module1.js';
import { bar } from './module2.js';

foo();
bar();
  1. Avoid Nested Ternary Operators: While ternary operators can be useful for concise expressions, nesting them can lead to code that is hard to read and understand. Instead, use regular if-else statements for complex conditions.

Example:

// Bad: Nested ternary
const result = condition1
  ? value1
  : condition2
  ? value2
  : condition3
  ? value3
  : defaultValue;

// Good: Using if-else
let result;
if (condition1) {
  result = value1;
} else if (condition2) {
  result = value2;
} else if (condition3) {
  result = value3;
} else {
  result = defaultValue;
}
  1. Avoid Excessive Comments: Comments are essential for code documentation, but avoid over-commenting self-explanatory code. Let the code speak for itself whenever possible.

Example:

// Bad: Excessive comments
function add(a, b) {
  // This function adds two numbers and returns the result
  return a + b; // Return the sum
}

// Good: Minimal, self-explanatory comments
function add(a, b) {
  return a + b;
}
  1. Use Object Shorthand: When creating object literals with properties that have the same name as the variables, use object shorthand for cleaner code.

Example:

// Bad: Repetitive code
const firstName = 'John';
const lastName = 'Doe';

const user = {
  firstName: firstName,
  lastName: lastName,
};

// Good: Object shorthand
const firstName = 'John';
const lastName = 'Doe';

const user = {
  firstName,
  lastName,
};
  1. Avoid Using eval(): The eval() a function can execute arbitrary code and is generally considered unsafe and bad practice. Find alternative solutions to achieve your goals without using eval().

Example (Bad - Avoid eval()):

const expression = '10 + 20';
const result = eval(expression);
console.log(result); // Output: 30
  1. Use textContent Instead of innerHTML: When working with plain text content, prefer textContent over innerHTML to prevent potential security vulnerabilities (e.g., cross-site scripting - XSS).

Example:

// Bad: Using innerHTML for plain text
const text = '<script>alert("Hello XSS!");</script>';
const element = document.getElementById('myElement');
element.innerHTML = text; // This will execute the script

// Good: Using textContent
const text = '<script>alert("Hello XSS!");</script>';
const element = document.getElementById('myElement');
element.textContent = text; // Treats it as plain text, no script execution
  1. Use addEventListener Instead of Inline Event Handlers: Instead of using inline event handlers in HTML (e.g., onclick="myFunction()"), use addEventListener in JavaScript for better separation of concerns.

Example (Bad - Inline Event Handler):

<button onclick="handleClick()">Click Me</button>
function handleClick() {
  // Event handling logic
}

Example (Good - addEventListener):

<button id="myButton">Click Me</button>
document.getElementById('myButton').addEventListener('click', handleClick);

function handleClick() {
  // Event handling logic
}
  1. Use const and let instead of var: Prefer const and let over var for variable declarations to avoid hoisting and block-scoping issues.

Example:

// Bad: Using var
var x = 10;

// Good: Using const or let
const x = 10;
let y = 20;
  1. Use map(), filter(), and reduce() for Array Operations: Utilize higher-order array methods like map(), filter(), and reduce() to perform operations on arrays in a functional and declarative manner.

Example using map():

const numbers = [1, 2, 3, 4, 5];
const doubledNumbers = numbers.map(num => num * 2);
console.log(doubledNumbers); // Output: [2, 4, 6, 8, 10]

Example using filter():

const numbers = [1, 2, 3, 4, 5];
const evenNumbers = numbers.filter(num => num % 2 === 0);
console.log(evenNumbers); // Output: [2, 4]

Example using reduce():

const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce((acc, num) => acc + num, 0);
console.log(sum); // Output: 15
  1. Avoid document.write(): The use of document.write() can cause unexpected behavior and overwrite the entire document if used after the page has finished loading. Use DOM manipulation methods instead.

Example (Bad - Avoid document.write()):

document.write('<h1>Hello World</h1>');
  1. Use classList for Managing CSS Classes: Instead of directly manipulating the className, use classList methods like add(), remove(), toggle(), and contains() to manage CSS classes.

Example:

<div id="myDiv" class="container">Content</div>
const element = document.getElementById('myDiv');

// Adding a class
element.classList.add('highlight');

// Removing a class
element.classList.remove('container');

// Checking if a class exists
if (element.classList.contains('highlight')) {
  // Do something
}

// Toggling a class
element.classList.toggle('active');
  1. Use requestAnimationFrame() for Smooth Animations: When creating animations, use requestAnimationFrame() to ensure smooth and efficient animations that run at the optimal frame rate.

Example:

function animate() {
  // Code to update the animation

  requestAnimationFrame(animate);
}

// Start the animation
animate();
  1. Avoid Synchronous AJAX Requests: Avoid using synchronous XMLHttpRequest (XHR) as it can block the main thread, causing a poor user experience. Instead, use asynchronous requests with Promises, async/await, or callbacks.

Example using Promises:

function fetchData() {
  return fetch('https://api.example.com/data')
    .then(response => response.json());
}

fetchData()
  .then(data => console.log(data))
  .catch(error => console.error('Error fetching data:', error));

Example using async/await:

async function fetchData() {
  try {
    const response = await fetch('https://api.example.com/data');
    const data = await response.json();
    return data;
  } catch (error) {
    throw new Error('Error fetching data:', error);
  }
}

(async () => {
  try {
    const data = await fetchData();
    console.log(data);
  } catch (error) {
    console.error(error.message);
  }
})();

Thanks for reading :)