JavaScript Best Practices

Following best practices helps you write clean, maintainable, and bug-free JavaScript. These guidelines are widely agreed upon by the JavaScript community and are based on real-world experience.

Use Strict Mode

Strict mode ('use strict') catches common coding mistakes, prevents the use of unsafe features, and throws errors that would otherwise be silent. ES modules use strict mode by default.

Strict Mode Benefits
'use strict';

// Prevents accidental global variables
try {
  // Without strict mode, this creates a global variable
  // With strict mode, it throws ReferenceError
  undeclaredVar = 10;
} catch (e) {
  console.log(e.name); // 'ReferenceError'
}

// Prevents duplicate parameter names
// function sum(a, a) {} // SyntaxError in strict mode

// Prevents deleting variables
// let x = 10;
// delete x; // SyntaxError in strict mode

console.log('Strict mode is active');

// ES modules are automatically in strict mode
// No need for 'use strict' in .mjs files or type='module'

Avoid Global Variables

Global variables can be overwritten by any code, leading to hard-to-find bugs. Use modules, closures, or IIFEs to keep variables in local scope.

Avoiding Global Variables
// Bad: polluting global scope
// var count = 0;
// var name = 'App';

// Good: use modules (import/export)
// In module.js: export const count = 0;
// In main.js: import { count } from './module.js';

// Good: use an IIFE to create a scope
const app = (() => {
  let count = 0;
  return {
    increment() { count++; },
    getCount() { return count; }
  };
})();

app.increment();
app.increment();
console.log(app.getCount()); // 2
// console.log(count); // ReferenceError — count is private

// Good: block scoping with const/let
{
  const secret = 'hidden';
  console.log(secret); // 'hidden'
}
// console.log(secret); // ReferenceError

Use const and let, Not var

const and let have block scope, which prevents many common bugs caused by var's function scope and hoisting behavior. Use const by default; use let only when reassignment is needed.

const/let vs var
// Problem with var in loops
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log('var i:', i), 10);
}
// var i: 3, var i: 3, var i: 3 (all 3!)

// Solution with let
for (let j = 0; j < 3; j++) {
  setTimeout(() => console.log('let j:', j), 20);
}
// let j: 0, let j: 1, let j: 2 (correct!)

// const for values that don't change
const MAX_SIZE = 100;
const config = { debug: true };
config.debug = false; // OK — modifying property is fine
console.log(config.debug); // false

// const prevents reassignment
// MAX_SIZE = 200; // TypeError: Assignment to constant
console.log(MAX_SIZE); // 100

Use === Instead of ==

The strict equality operator (===) compares without type coercion, avoiding many surprising and hard-to-debug behaviors of loose equality (==).

Strict Equality
// Surprising == results
console.log(0 == '');       // true (avoid!)
console.log(0 == false);    // true (avoid!)
console.log('' == false);   // true (avoid!)
console.log(null == undefined); // true (avoid!)

// === is predictable
console.log(0 === '');       // false
console.log(0 === false);    // false
console.log('' === false);   // false
console.log(null === undefined); // false

// Always use === and !==
const value = '5';
if (value === 5) {
  console.log('This will NOT run');
}
if (value === '5') {
  console.log('This WILL run');
}

Handle Errors and Avoid eval()

Always handle errors gracefully. Never use eval() — it executes arbitrary code, creates security vulnerabilities, and makes debugging difficult.

Error Handling and Avoiding eval()
// Always handle potential errors
function parseJSON(str) {
  try {
    return { data: JSON.parse(str), error: null };
  } catch (e) {
    return { data: null, error: e.message };
  }
}

const good = parseJSON('{"name": "Alice"}');
console.log(good.data); // { name: 'Alice' }

const bad = parseJSON('not json');
console.log(bad.error); // 'Unexpected token...'

// Never use eval()
// eval('console.log("dangerous!")'); // Don't do this!

// Instead of eval, use safer alternatives:
// For JSON: JSON.parse()
// For dynamic property access: obj[key]
const obj = { name: 'Alice', age: 30 };
const key = 'name';
console.log(obj[key]); // 'Alice' (safe!)
📝 Note: eval() is one of the most dangerous features in JavaScript. It can execute any code string, making your application vulnerable to code injection attacks. Modern JavaScript has safer alternatives for every eval() use case.

Use Template Literals, DRY, and Early Returns

Template literals improve string readability. The DRY (Don't Repeat Yourself) principle reduces duplication. Early returns simplify complex logic by avoiding deep nesting.

Template Literals, DRY, and Early Returns
// Use template literals instead of concatenation
const name = 'Alice';
const age = 30;

// Bad: string concatenation
// const msg = 'Hello, ' + name + '! You are ' + age + '.';

// Good: template literals
const msg = `Hello, ${name}! You are ${age}.`;
console.log(msg); // Hello, Alice! You are 30.

// DRY: Extract repeated logic into functions
function formatCurrency(amount) {
  return `$${amount.toFixed(2)}`;
}
console.log(formatCurrency(10));    // $10.00
console.log(formatCurrency(3.5));   // $3.50
console.log(formatCurrency(100.1)); // $100.10

// Early returns: avoid deep nesting
function getDiscount(user) {
  if (!user) return 0;
  if (!user.membership) return 0;
  if (user.membership === 'gold') return 0.2;
  if (user.membership === 'silver') return 0.1;
  return 0.05;
}

console.log(getDiscount({ membership: 'gold' }));   // 0.2
console.log(getDiscount({ membership: 'silver' })); // 0.1
console.log(getDiscount({ membership: 'bronze' })); // 0.05
console.log(getDiscount(null)); // 0
PracticeWhy
'use strict'Catches silent errors, prevents unsafe features
Avoid globalsPrevents naming collisions and unexpected overwrites
const/let over varBlock scoping prevents hoisting bugs
=== over ==No type coercion surprises
Handle errorsPrevents crashes, improves user experience
Avoid eval()Security risk, debugging nightmare
Template literalsCleaner, more readable string building
DRY principleReduces bugs, easier maintenance
Early returnsLess nesting, clearer logic flow
Exercise:
Why should you avoid using var in modern JavaScript?
Try it YourselfCtrl+Enter to run
Click Run to see the output here.