JavaScript

Status: 🟩 COMPLETE Last updated: 2026-06-20 Plain-English tagline: The programming language of the web — and now the language behind servers, mobile apps, build tools, and most of the modern dev ecosystem.


In plain English

JavaScript is the language that makes web pages interactive. When you click a button and a menu opens, when a form validates as you type, when a page updates without reloading — all JavaScript. It runs inside every browser; it also runs on servers via Node.js. One language, both sides.

Created in 1995 by Brendan Eich in 10 days at Netscape. The name was a marketing move (Java was hot; JavaScript had nothing to do with Java). For two decades it was dismissed as a toy language. Then the modern web happened, and JavaScript ate everything.

JavaScript is officially called ECMAScript — that’s the standard. New versions ship roughly yearly (ES2024, ES2025, etc.), each adding features. Browsers and Node support new features quickly.

Modern JavaScript looks very different from 2010 JavaScript. The language is much better now: arrow functions, async/await, destructuring, modules, classes, optional chaining. Most of what you’ll write in 2026 is modern JS.


Why it matters

  • It’s the only language browsers run. Period. Other languages (TypeScript, Sass, etc.) compile to JavaScript before reaching the browser.
  • Node.js made it the dominant backend language too. Same code on both sides.
  • The tooling ecosystem is JavaScript. npm packages, build tools (Webpack, Vite, esbuild), test runners — all JS.
  • Every modern web framework is JavaScript — React, Vue, Svelte, Next.js, Nuxt, SvelteKit, Remix.

If you’re going to learn one programming language for the modern web, this is the one. Knowing it well opens up everything else.


A 5-minute tour of the language

Variables

let count = 0;           // can be reassigned
const name = "George";   // can't be reassigned (but contents can mutate for arrays/objects)
var x = 1;               // old-style; basically never use this in modern code

Use const by default. Reach for let when you need to reassign. Avoid var.

Primitive types

const a = 42;              // number
const b = 3.14;            // also number — no separate float type
const c = "hello";         // string
const d = `template ${a}`; // template string with interpolation
const e = true;            // boolean
const f = null;            // null
const g = undefined;       // undefined
const h = Symbol("id");    // unique symbol (rare)
const i = 9007199254740993n; // bigint (for very large numbers)

Arrays and objects

const arr = [1, 2, 3];
arr.push(4);              // [1, 2, 3, 4]
arr.length;               // 4
arr[0];                   // 1
 
const obj = { name: "George", age: 99 };
obj.name;                 // "George"
obj["name"];              // also "George"
obj.email = "g@x.com";    // add new property

Functions

// Function declaration
function add(a, b) {
  return a + b;
}
 
// Arrow function (modern, shorter)
const add = (a, b) => a + b;
 
// With default values
const greet = (name = "world") => `Hello, ${name}`;
 
// Higher-order: function that takes/returns functions
const double = (fn) => (x) => fn(x) * 2;

Control flow

if (count > 0) {
  doSomething();
} else if (count === 0) {
  doSomethingElse();
} else {
  doDefault();
}
 
// Ternary (inline if/else)
const message = count > 0 ? "positive" : "non-positive";
 
// Switch (less common in modern JS)
switch (status) {
  case "loading": return <Spinner />;
  case "ready": return <Result />;
  default: return null;
}

Loops

// for-of (most common)
for (const item of items) {
  console.log(item);
}
 
// for-in (object keys)
for (const key in obj) {
  console.log(key, obj[key]);
}
 
// .forEach (array method)
items.forEach((item) => console.log(item));
 
// Traditional for (rarely needed)
for (let i = 0; i < items.length; i++) { ... }

Array methods (the workhorse)

const nums = [1, 2, 3, 4, 5];
 
nums.map((n) => n * 2);         // [2, 4, 6, 8, 10] — transform each
nums.filter((n) => n % 2 === 0); // [2, 4] — keep matching
nums.reduce((sum, n) => sum + n, 0); // 15 — combine
nums.find((n) => n > 3);         // 4 — first match
nums.some((n) => n > 4);         // true — any match
nums.every((n) => n > 0);        // true — all match
nums.includes(3);                // true
nums.slice(1, 3);                // [2, 3] — sub-array (non-mutating)

These are used constantly. map/filter/reduce especially.

Destructuring

// Object
const { name, age } = obj;
// equivalent to: const name = obj.name; const age = obj.age;
 
// With renaming
const { name: userName } = obj;
 
// With defaults
const { name = "anonymous" } = obj;
 
// Array
const [first, second, ...rest] = [1, 2, 3, 4, 5];
// first = 1, second = 2, rest = [3, 4, 5]

Spread

// Array spread
const merged = [...arr1, ...arr2];
 
// Object spread (shallow merge)
const merged = { ...obj1, ...obj2 };
 
// In function calls
const max = Math.max(...nums);

Equality

1 == "1"     // true — loose equality (avoid)
1 === "1"    // false — strict equality (use)
1 === 1      // true

Use === and !==. Always. The loose versions (==, !=) have surprising rules.

null vs undefined

  • undefined — “this variable was never assigned”
  • null — “this variable was intentionally set to nothing”

null === undefined is false. null == undefined is true. Use === and don’t worry about it.

Modern features (ES2020+)

// Optional chaining
const city = user?.address?.city;
// undefined if user or user.address is null/undefined; no error
 
// Nullish coalescing
const name = user.name ?? "anonymous";
// "anonymous" only if user.name is null or undefined (not 0, "" or false)
 
// Logical assignment
user.name ||= "anonymous";  // assign if falsy
user.config ??= {};         // assign if null/undefined

These three save dozens of lines per project once you internalize them.


Async — the most important concept

JavaScript is single-threaded but asynchronous. Long-running operations (network calls, file reads, timers) don’t block — they return immediately and notify you later.

Promises

A Promise represents an eventual result:

fetch("/api/data")
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error(error));

Async/await — the modern way

async function loadData() {
  try {
    const response = await fetch("/api/data");
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error(error);
  }
}

Same as the Promise version, but reads top-to-bottom. await pauses the function until the Promise resolves.

async marks a function as asynchronous (it implicitly returns a Promise). await only works inside async functions (or at the top level in modules).


Modules — splitting code across files

Modern JavaScript uses ES Modules:

// lib/math.js
export function add(a, b) { return a + b; }
export function multiply(a, b) { return a * b; }
 
// app.js
import { add, multiply } from "./lib/math.js";
console.log(add(2, 3));

export makes something available; import brings it in. Same concept in React/Next.js (import { useState } from "react").


What JavaScript runs on

  • Browsers: Chrome (V8), Firefox (SpiderMonkey), Safari (JavaScriptCore), Edge (V8). All implement the same standard.
  • Servers: Node.js (V8), Deno, Bun.
  • Desktop apps: Electron (uses Node + Chromium).
  • Mobile apps: React Native, Capacitor, Ionic.
  • Edge environments: Vercel Edge, Cloudflare Workers (V8 isolates).

One language, almost everywhere.


Common gotchas

  • == vs ===. Always use ===. The loose equality has surprising coercion rules.

  • typeof null === "object". A 25-year-old bug in the language, never fixed for compatibility. Use value === null to check.

  • Arrays are objects. Array.isArray() is the safe check, not typeof === "array".

  • this is weird. Its value depends on how a function is called. Arrow functions inherit this from where they’re defined (almost always what you want). Regular functions get this from the caller.

  • Async ≠ parallel. await runs sequentially. To parallelize, use Promise.all([promise1, promise2]).

  • Mutation surprises. const obj = {}; obj.x = 1; is legal — const prevents reassignment, not mutation. Use Object.freeze or libraries like Immer for true immutability.

  • 0, "", null, undefined, NaN, false are all “falsy.” if (value) skips on all of them. Sometimes that’s what you want; sometimes not.

  • Date is unfriendly. JavaScript’s built-in Date object has odd quirks (months are 0-indexed!). Use a library like date-fns or the new Temporal API.

  • parseInt("08") was once 0 (octal). Modern engines fixed this, but always pass the radix: parseInt("08", 10).

  • Floating-point math. 0.1 + 0.2 === 0.30000000000000004. Not a JavaScript bug — that’s IEEE 754. Use rounding or libraries for precise money math.

  • forEach doesn’t await. If you need to await inside a loop, use for-of with await, or Promise.all(items.map(...)) for parallel.

  • Hoisting. Function declarations are “hoisted” — usable before their definition. var is also hoisted (with undefined). const/let are not. Just declare things before using them.

  • async functions always return Promises. Even if they just return 42, the caller gets Promise<42>, not 42.


See also

Sources