JavaScript Coding Standards
The full reference for modern, safe JavaScript: variables, equality, functions and this, modules, immutability, objects and arrays, async, iteration, and security. JavaScript has well-known traps (coercion, hoisting, mutation, this, floating promises). These standards keep you in the safe modern subset. We write TypeScript by default (see TypeScript Coding Standards), and all of this applies underneath it.
Modern JavaScript (ES2015 and later) fixed most of the language's old hazards, as long as you use it and avoid the legacy behaviours. This page guides you to that safe subset, whether you are in a plain .js file (build scripts, config) or writing the JavaScript that sits under TypeScript and React.
ESLint and Prettier enforce most of this automatically. The rules here are the reasoning and the things tools cannot catch. Fix lint warnings rather than disabling rules.
Variables & scope
- DoDeclare with
constby default. Useletonly when you must reassign. Both are block-scoped and predictable. - NeverUse
var. Its function-scoping and hoisting cause bugs thatconstandletdo not have. - DoDeclare variables at first use, in the narrowest scope. Always declare them so you do not create accidental globals.
- DoPrefer immutable bindings and values. Reassign as little as possible.
Equality & coercion
- AlwaysUse strict equality
===and!==. The loose==and!=do surprising type coercion and are a classic source of bugs. - DoKnow which values are falsy (
0,'',NaN,null,undefined,false) before relying on a truthy check. Be explicit when 0 or '' are valid values. - DoUse
Number.isNaNandNumber.isIntegerinstead of the global versions that coerce. Be careful withparseIntand always pass the radix. - DoUse
??for null/undefined defaults, not||, which also triggers on 0 and ''.
Functions & this
- DoPrefer arrow functions for callbacks; they do not rebind
this. Be deliberate aboutthiswhen you need a method context. - DoKeep functions small and ideally pure. Avoid hidden side effects in getters or in calls that look harmless.
- DoUse default parameters and rest/spread instead of
arguments. Avoid long positional parameter lists; pass an options object. - AvoidMutating function arguments. Return new values instead.
Objects, arrays & immutability
- DoTreat data as immutable. Build new objects and arrays with spread,
map,filter, andreduceinstead of changing them in place. This is essential for React state. - DoUse destructuring and spread for clarity, optional chaining (
?.) for deep access, and template literals instead of string concatenation. - DoRemember the difference between reference and value. Assigning or copying an object copies the reference; clone it when you need a separate copy.
- DoUse
MapandSetwhen you need keyed or unique collections, instead of misusing plain objects. - AvoidMutating shared objects or arrays you do not own, and relying on object key order or other engine-specific behaviour.
Modules & structure
- DoUse ES modules (
import/export). Prefer named exports and keep modules small and focused (see Coding Standards & Style). - DoAvoid circular dependencies and large catch-all util files. Organise by feature.
- DoKeep side effects out of the top level of a module where possible. Importing a module should not do work.
Async & errors
- DoUse
async/awaitinstead of raw promise chains and, above all, callbacks. It reads top to bottom and works withtry/catch(see Error Handling). - AlwaysNever leave a floating promise. Always
awaitit or add a.catch. An unhandled rejection is a silent failure. - DoUse
Promise.allfor independent concurrent work, andallSettledwhen you need every result. Handle partial failure on purpose. - DoThrow
Errorobjects, not strings. Keep the cause, and do not ignore errors silently (see Error Handling).
Iteration & language use
- DoPrefer array methods (
map,filter,reduce,for...of) over index loops where they read better. Usefor...offor arrays, notfor...in. - DoUse modern syntax: object shorthand, computed keys, nullish coalescing, and optional chaining. Keep it readable.
- AvoidClever one-liners that hide the intent. In shared code, clear beats short.
Safety & security
- DoLet ESLint and Prettier enforce style and catch common mistakes. Fix warnings rather than disabling rules (see CI/CD & Deployment).
- Avoid
eval,new Function, and building DOM or HTML from strings. These are code-injection and XSS risks (see Web & Frontend Security). - NeverPut secrets or other users' data in client-side JavaScript. Everything sent to the browser can be read by the user (see Web & Frontend Security).
- DoKeep dependencies few and vetted. npm is a real supply-chain risk (see Dependency & Supply-Chain Security).
Safe modern JavaScript, end to end
var items = getItems();
if (items.count == '0') return; // var plus loose equality
items.list.push(newItem); // mutating shared state
save(items); // promise ignored (floating)
var and == invite coercion bugs, the push mutates shared state (which breaks change detection in React), and the unawaited save can fail silently. All avoidable mistakes.
const items = getItems();
if (items.count === 0) return;
const updated = { ...items, list: [...items.list, newItem] }; // new value
await save(updated); // handled
Block-scoped consts, strict equality, an immutable update, and an awaited async call. Predictable and safe.
Self-review checklist
- AskAm I using
const/letand strict equality, with novaror==? - AskIs data treated as immutable, with no mutation of shared objects?
- AskAre all promises awaited or handled, with no floating rejections?
- AskIs there any
evalor HTML built from strings, or any secret or PII sent to the client?