Best Practices & Security

JavaScript · Lesson 60 of 61 8 min read Updated

Code that merely works is only the first step. Real projects are read and changed by other people, run on slow phones, and face visitors who may try to break them. A few habits make the difference between fragile code and code you can trust.

This lesson collects the most important habits from across the course into four groups: writing clear modern code, letting tools check and format it, keeping your users safe, and keeping pages fast.

Write modern, strict code

Use modules or strict mode. Modules run in strict mode automatically (see Modules). For an old-style classic script, put "use strict"; as its first line. Strict mode turns silent mistakes into errors you can see:

JavaScriptRun it yourself
let total = 0;

function addToCart(price) {
  totla = total + price; // typo: should be total
}

try {
  addToCart(250);
} catch (error) {
  console.log(`${error.name}: ${error.message}`);
}

console.log(0 == "", 0 === "");
console.log(null == undefined, null === undefined);
Output
ReferenceError: totla is not defined
true false
true false

Without strict mode, the typo would silently create a new global variable called totla, and the cart total would never change. You would spend a long time wondering why.

The last two lines show why you should always use ===. The loose == converts types before comparing, so 0 == "" is true. The strict === compares without converting, which is almost always what you mean.

A few more habits pay off every day:

  • const by default, let only when the value must change, never var. It makes clear which values change. See Variables: let and const.
  • Meaningful names. discountedPrice beats dp; functions are verbs, such as calculateTotal() and showError().
  • Small functions that do one job. If you need the word "and" to describe a function, consider splitting it.
  • Return early. Check for bad input at the top and return, instead of nesting the main logic deep inside if blocks.
  • Avoid global variables. Keep data inside modules and functions, and pass it in as arguments.
JavaScript
// Hard to follow: deep nesting, unclear names
function f(u) {
  if (u) {
    if (u.age >= 18) {
      if (u.verified) {
        return "ok";
      }
    }
  }
  return "no";
}

// Clear: a meaningful name and early returns
function canBookTicket(user) {
  if (!user) return false;
  if (user.age < 18) return false;
  return user.verified;
}

Let tools check and format your code

A linter reads your code without running it and warns about likely bugs: unused variables, variables that were never declared, == instead of ===, unreachable code. The standard JavaScript linter is ESLint. A formatter rewrites your code's layout (spacing, quotes, line breaks) into one consistent style, so nobody argues about it. The most popular is Prettier. Both run with Node.js, and both have editor extensions that check or format your file every time you save.

Terminal
npm init @eslint/config@latest
npx eslint .
npm install --save-dev --save-exact prettier
npx prettier . --write

The first command asks a few questions and creates a config file. Current ESLint versions use a file named eslint.config.js, which can be as small as this:

JavaScript
// eslint.config.js
import js from "@eslint/js";
import globals from "globals";

export default [
  js.configs.recommended,
  {
    languageOptions: {
      globals: globals.browser, // allow window, document and other browser globals
    },
    rules: {
      eqeqeq: "error", // always require === and !==
    },
  },
];

Use them together: Prettier decides how code looks, ESLint finds problems in what it does. On a team, the shared config files mean everyone's code follows the same rules.

Keep your users safe

Front-end code runs on the visitor's device, and anyone can read it, change it in DevTools or bypass it completely. These rules protect your users and your site:

  • Never put untrusted text into innerHTML. Anything from users, URLs, storage or APIs goes into the page with textContent or createElement. Otherwise you open the door to cross-site scripting (XSS), as shown in Changing the DOM. If you truly must display user-written HTML, clean it with a well-maintained sanitiser library first.
  • Check URLs before using them as links. A link like javascript:... runs code when clicked. Allow only http: and https:.
  • Validate input twice. Checks in the browser give quick, friendly feedback; the server must check everything again, because browser checks can be skipped. See Working with Forms.
  • No secrets in front-end code. API keys, passwords and tokens in JavaScript or localStorage can be read by anyone. Keep them on the server.
  • Be careful with packages. Every library you install is code you trust. Install only what you need, keep it updated, and run npm audit to check for known security problems.

This review form applies several of these rules at once:

HTMLTry it yourself
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Safe reviews</title>
</head>
<body>
  <h1>Reviews for Kabir's Dhaba, Jaipur</h1>
  <form id="review-form">
    <p><label>Your review <input name="review" required maxlength="200"></label></p>
    <p><label>Your website (optional) <input name="website"></label></p>
    <button>Post</button>
  </form>
  <p id="error" role="alert"></p>
  <ul id="reviews"></ul>

  <script type="module">
    const form = document.querySelector("#review-form");
    const errorBox = document.querySelector("#error");

    // Allow only real web links, never "javascript:" or other schemes
    function safeLink(text) {
      try {
        const url = new URL(text);
        return url.protocol === "https:" || url.protocol === "http:" ? url.href : null;
      } catch {
        return null; // not a valid absolute URL
      }
    }

    form.addEventListener("submit", (event) => {
      event.preventDefault();
      const review = form.elements.review.value.trim();
      const website = form.elements.website.value.trim();

      if (review.length < 5) {
        errorBox.textContent = "Please write at least 5 characters.";
        return;
      }
      const link = website ? safeLink(website) : null;
      if (website && !link) {
        errorBox.textContent = "The website must start with http:// or https://";
        return;
      }

      errorBox.textContent = "";
      const li = document.createElement("li");
      li.textContent = review; // text, never HTML
      if (link) {
        const a = document.createElement("a");
        a.href = link;
        a.textContent = " (website)";
        a.rel = "nofollow noopener";
        li.append(a);
      }
      document.querySelector("#reviews").append(li);
      form.reset();
    });
  </script>
</body>
</html>

Try posting <b>Great</b> dal! as a review and javascript:alert(1) as the website. The tags appear as plain text, and the dangerous link is refused. Websites can add one more layer of protection with a Content Security Policy, a server header that tells the browser which scripts it may run.

Keep pages fast

Most slow pages are slow for a handful of reasons:

  • Load scripts without blocking. Use type="module" or defer so the HTML can be shown first (see Adding JavaScript to a Page).
  • Touch the DOM less. Build elements in memory and insert them in one go, and use event delegation instead of thousands of listeners (see Events).
  • Calm down busy events. Debounce input and resize handlers (see Timers & Animation Frames).
  • Load heavy code only when needed with dynamic import().
  • Do not block the main thread with long loops; split the work or move it to a Web Worker.
  • Measure before you optimise. The Performance panel in DevTools shows where time really goes.

One less obvious trap: reading a layout value such as offsetWidth right after changing styles forces the browser to recalculate the layout immediately. Doing that inside a loop, read-write-read-write, can be very slow. Read everything first, then write:

JavaScript
const cards = document.querySelectorAll(".card");

// Slow: each read forces a fresh layout after the previous write
for (const card of cards) {
  card.style.height = `${card.offsetWidth * 0.75}px`;
}

// Faster: all reads first, then all writes
const widths = [...cards].map((card) => card.offsetWidth);
cards.forEach((card, i) => {
  card.style.height = `${widths[i] * 0.75}px`;
});

Key points

  • Use modules (or "use strict"), const and let, ===, clear names and small functions with early returns.
  • Let ESLint find likely bugs and Prettier keep formatting consistent, ideally on every save.
  • Show untrusted data with textContent, allow only http: and https: links, and never use eval() or string timers.
  • Validate in the browser for friendliness and on the server for security; keep secrets off the front end.
  • For speed: non-blocking scripts, fewer DOM changes, debounced events, code loaded on demand, and measuring first.

Exercise

Rewrite this function using the habits from this lesson: strict equality, const, a meaningful name, early returns and no innerHTML for the user's name.

JavaScript
function x(n, a) {
  var r;
  if (n != "") {
    if (a == 18 || a > 18) {
      r = "Welcome " + n;
    } else {
      r = "Too young";
    }
  } else {
    r = "No name";
  }
  document.querySelector("#msg").innerHTML = r;
}
Show solution
JavaScript
function showWelcome(name, age) {
  const message = document.querySelector("#msg");

  if (name === "") {
    message.textContent = "No name";
    return;
  }
  if (age < 18) {
    message.textContent = "Too young";
    return;
  }
  message.textContent = `Welcome ${name}`;
}

Early returns remove the nesting, === avoids type conversion surprises, and textContent means a name like <img onerror=...> is shown as text instead of running.

Quick Quiz

1. Why does strict mode help catch bugs?

2. What is the difference between ESLint and Prettier?

3. A user enters javascript:alert(1) as their website. What should your code do?

4. Why must the server validate form data even if JavaScript already checked it?

Learn JavaScript with a mentor

Free lessons get you started. Our Full Stack Web Development training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call