JavaScript
WebOverview
High-level, dynamic scripting language. The language of the web — runs in every browser and on the server via Node.js/Deno/Bun.
Resources
Popular learning and reference links:
Installation & Getting Started
JavaScript runs in every browser. For server-side / local development, install a runtime like Node.js, Deno, or Bun.
# Node.js — install via nvm (recommended)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.0/install.sh | bash
nvm install --lts
node --version
# Or via Homebrew (macOS)
brew install node
# Deno — built-in TypeScript, no config needed
curl -fsSL https://deno.land/install.sh | sh
deno --version
# Bun — fast all-in-one runtime
curl -fsSL https://bun.sh/install | bash
bun --version
# REPL — start an interactive session
node # Node.js REPL
deno # Deno REPL
bun repl # Bun REPL
# Run a script
node script.js
deno run script.js
bun run script.js
# Quick one-liner
node -e "console.log('Hello!')" Project Scaffolding
Use npm init for a basic project or a framework CLI for a full setup.
# Basic project
mkdir my-project && cd my-project
npm init -y
# Using Vite
npm create vite@latest my-app
# Using Next.js
npx create-next-app@latest my-app
# Using Astro
npm create astro@latest
# Run the project
npm run dev Package Management
npm is the default package manager. Yarn, pnpm, and Bun are popular alternatives.
# Install a package
npm install lodash
# Install as dev dependency
npm install --save-dev vitest
# Install globally
npm install -g typescript
# Using pnpm
pnpm add lodash
# Using yarn
yarn add lodash
# Using bun
bun add lodash Tooling & Formatter/Linter
Rich ecosystem of formatters and linters. Most projects use a combination.
# Prettier — opinionated code formatter
npm install --save-dev prettier
npx prettier --write .
# ESLint — linting and code quality
npm install --save-dev eslint
npx eslint --init
npx eslint .
# Biome — fast all-in-one (format + lint)
npm install --save-dev @biomejs/biome
npx biome check --write .
# oxlint — extremely fast linter (Rust-based)
npx oxlint .
// .prettierrc
{
"semi": false,
"singleQuote": true,
"trailingComma": "all",
"printWidth": 100
}
// eslint.config.js (flat config)
import js from '@eslint/js'
export default [
js.configs.recommended,
{ rules: { 'no-unused-vars': 'warn' } },
] Build & Compile Model
Interpreted / JIT-compiled. JavaScript is parsed and JIT-compiled at runtime by the engine (V8, SpiderMonkey, JavaScriptCore).
# No compile step — run directly
node app.js
deno run app.js
bun run app.js
# Bundling for browsers (not compilation, but required)
npx vite build # Vite (esbuild + Rollup)
npx esbuild app.js --bundle --outfile=out.js
npx webpack # webpack
# Output: .js bundles, source maps
# No binary output — always JavaScript
Execution model:
- Source → Parse → AST → Bytecode → JIT machine code
- V8 uses TurboFan (optimizing compiler) and Sparkplug (baseline)
- No ahead-of-time compilation (except experimental with Hermes, QuickJS)
- Tree-shaking and dead-code elimination happen at bundle time, not compile time
Libraries & Frameworks
JavaScript has an enormous ecosystem. Key libraries and frameworks by category:
Web Frameworks - React, Vue, Svelte, Angular, Solid, Astro, Next.js, Nuxt, SvelteKit, Remix
Server / API - Express, Fastify, Hono, Koa, Nest.js, tRPC
Build Tools - Vite, esbuild, Rollup, webpack, Turbopack, Parcel
State Management - Zustand, Jotai, Redux Toolkit, Pinia (Vue), Signals
Styling - Tailwind CSS, Styled Components, CSS Modules, UnoCSS
Databases / ORM - Prisma, Drizzle, Mongoose, Knex, TypeORM
Utilities - Lodash, date-fns, Zod, Axios, Ramda
Testing - Vitest, Jest, Playwright, Cypress, Testing Library
Mobile - React Native, Expo, Capacitor, NativeScript
Testing
Popular testing frameworks include Vitest, Jest, and the built-in Node.js test runner. Playwright and Cypress are used for end-to-end testing.
// Vitest (recommended — fast, Vite-native)
import { describe, it, expect } from 'vitest'
describe('math', () => {
it('adds numbers', () => {
expect(1 + 2).toBe(3)
})
it('handles arrays', () => {
expect([1, 2, 3]).toContain(2)
expect([1, 2, 3]).toHaveLength(3)
})
})
// Node.js built-in test runner (v18+)
import { describe, it } from 'node:test'
import assert from 'node:assert'
describe('math', () => {
it('adds numbers', () => {
assert.strictEqual(1 + 2, 3)
})
})
# Run tests
npx vitest # Vitest (watch mode)
npx vitest run # Vitest (single run)
npx jest # Jest
node --test # Node.js built-in
# E2E testing
npx playwright test # Playwright
npx cypress run # Cypress Debugging
Chrome DevTools for browser, Node.js inspector for server. console methods and debugger statement.
// console methods
console.log('basic log')
console.error('error message')
console.warn('warning')
console.table([{ name: 'Alice', age: 30 }])
console.dir(obj, { depth: null })
console.time('op'); doWork(); console.timeEnd('op')
console.trace('call stack')
console.group('section'); /* ... */ console.groupEnd()
// debugger statement — triggers breakpoint
function processData(data) {
debugger // Pauses here if DevTools is open
return data.map(transform)
}
# Node.js debugging
node --inspect app.js # Start with inspector
node --inspect-brk app.js # Break on first line
# Open chrome://inspect in Chrome
# VS Code — built-in debugger
# launch.json:
{
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/src/index.js",
"console": "integratedTerminal"
}
# Set breakpoints by clicking gutter
# F5 to start, F10 step over, F11 step into
# Chrome DevTools
# Sources tab: breakpoints, watch, call stack
# Network tab: request/response inspection
# Console tab: live REPL in page context
# Elements tab: DOM inspection
# ndb — improved Node.js debugging
npx ndb node app.js Variables
Use const for immutable bindings and let for mutable ones. var is legacy and function-scoped.
const name = 'Alice';
let age = 30;
var legacy = true; // function-scoped, avoid
// Destructuring
const { x, y } = { x: 1, y: 2 };
const [first, ...rest] = [1, 2, 3]; Types
JavaScript is dynamically typed. Primitives: string, number, boolean, null, undefined, symbol, bigint.
typeof 'hello'; // "string"
typeof 42; // "number"
typeof true; // "boolean"
typeof undefined; // "undefined"
typeof null; // "object" (legacy bug)
typeof Symbol(); // "symbol"
typeof 42n; // "bigint"
// Arrays and objects
const arr = [1, 2, 3];
const obj = { key: 'value' }; Data Structures
Built-in: Arrays, Objects, Map, Set, WeakMap, WeakSet. No built-in linked list, queue, or stack types.
// Arrays — ordered, mixed types
const arr = [1, 'two', true]
arr.push(4)
arr.pop()
arr.includes(1) // true
arr.find(x => x === 'two')
arr.slice(0, 2) // [1, 'two']
// Objects — key-value (string keys)
const obj = { name: 'Alice', age: 30 }
obj.email = 'a@b.com'
delete obj.age
Object.keys(obj) // ['name', 'email']
Object.entries(obj) // [['name','Alice'], ...]
// Map — key-value (any key type, ordered)
const map = new Map()
map.set('key', 'value')
map.set(42, 'number key')
map.get('key') // 'value'
map.has(42) // true
map.size // 2
// Set — unique values
const set = new Set([1, 2, 3, 3])
set.add(4)
set.has(2) // true
set.size // 4
// WeakMap / WeakSet — garbage-collectable keys
const wm = new WeakMap()
wm.set(someObj, 'metadata')
// Typed arrays (for binary data)
const buf = new ArrayBuffer(16)
const view = new Int32Array(buf) Functions
Functions can be declared, expressed, or written as arrow functions.
// Function declaration
function greet(name) {
return `Hello, ${name}!`;
}
// Arrow function
const add = (a, b) => a + b;
// Default parameters
function power(base, exp = 2) {
return base ** exp;
}
// Rest parameters
function sum(...nums) {
return nums.reduce((a, b) => a + b, 0);
} Conditionals
Standard if/else, ternary operator, and switch.
// If / else
if (x > 0) {
console.log('positive');
} else if (x === 0) {
console.log('zero');
} else {
console.log('negative');
}
// Ternary
const label = x > 0 ? 'positive' : 'non-positive';
// Switch
switch (color) {
case 'red':
console.log('#f00');
break;
case 'blue':
console.log('#00f');
break;
default:
console.log('unknown');
}
// Nullish coalescing
const val = input ?? 'default';
// Optional chaining
const city = user?.address?.city; Loops
Standard for, while, and iterator-based loops.
// For loop
for (let i = 0; i < 5; i++) {
console.log(i);
}
// For...of (iterables)
for (const item of [1, 2, 3]) {
console.log(item);
}
// For...in (object keys)
for (const key in { a: 1, b: 2 }) {
console.log(key);
}
// While
let n = 0;
while (n < 3) {
n++;
}
// Array methods
[1, 2, 3].forEach((x) => console.log(x));
[1, 2, 3].map((x) => x * 2); Generics & Type System
JavaScript has no type system at runtime. Types are dynamic — any variable can hold any value. Use TypeScript for static types.
// No generics — functions work with any type naturally
function identity(x) { return x }
identity(42) // number
identity('hello') // string
// Duck typing — if it has the right shape, it works
function getLength(thing) {
return thing.length // Works for strings, arrays, etc.
}
// typeof — runtime type checking
typeof 42 // 'number'
typeof 'hello' // 'string'
typeof true // 'boolean'
typeof undefined // 'undefined'
typeof null // 'object' (historical bug)
typeof {} // 'object'
typeof [] // 'object'
// instanceof — check prototype chain
[] instanceof Array // true
new Date() instanceof Date // true
// Array.isArray — reliable array check
Array.isArray([1, 2]) // true
JavaScript is dynamically typed — generics are a TypeScript concept. See the TypeScript column for full generics support.
Inheritance & Composition
Prototypal inheritance with class syntax sugar. Composition via mixins and object spread is often preferred.
// Class inheritance
class Animal {
constructor(name) { this.name = name }
speak() { return `${this.name} makes a sound` }
}
class Dog extends Animal {
speak() { return `${this.name} barks` }
}
// Super calls
class Puppy extends Dog {
constructor(name) {
super(name)
this.young = true
}
}
// Mixins (composition pattern)
const Serializable = (Base) => class extends Base {
toJSON() { return JSON.stringify(this) }
}
class User extends Serializable(Animal) {}
// Object composition (preferred)
const canWalk = (state) => ({
walk: () => `${state.name} walks`,
})
const canSwim = (state) => ({
swim: () => `${state.name} swims`,
})
const createDuck = (name) => {
const state = { name }
return { ...canWalk(state), ...canSwim(state) }
} Functional Patterns
First-class functions, closures, and a rich set of array methods for functional programming.
// Higher-order functions
const apply = (fn, x) => fn(x)
const double = (x) => x * 2
apply(double, 5) // 10
// Map, filter, reduce
const nums = [1, 2, 3, 4, 5]
const squared = nums.map((x) => x * x)
const evens = nums.filter((x) => x % 2 === 0)
const sum = nums.reduce((acc, x) => acc + x, 0)
// Function composition
const compose = (f, g) => (x) => f(g(x))
const addOne = (x) => x + 1
const doubleAndAdd = compose(addOne, double)
// Closures
function counter(start = 0) {
let count = start
return { inc: () => ++count, get: () => count }
}
// Currying
const add = (a) => (b) => a + b
const add5 = add(5)
add5(3) // 8
// Chaining
const result = [1, 2, 3, 4, 5]
.filter((x) => x > 2)
.map((x) => x * 10)
.reduce((a, b) => a + b, 0) Concurrency
Single-threaded event loop with async/await. Web Workers and worker_threads provide true parallelism.
// async/await — non-blocking I/O
async function fetchData() {
const res = await fetch('/api/data')
return res.json()
}
// Promise.all — concurrent requests
const [users, posts] = await Promise.all([
fetch('/api/users').then(r => r.json()),
fetch('/api/posts').then(r => r.json()),
])
// Promise.allSettled — don't fail on one rejection
const results = await Promise.allSettled([
fetch('/api/a'),
fetch('/api/b'),
])
// setTimeout / setInterval
setTimeout(() => console.log('delayed'), 1000)
// Worker threads (Node.js) — true parallelism
import { Worker, isMainThread, parentPort } from 'worker_threads'
if (isMainThread) {
const worker = new Worker(new URL(import.meta.url))
worker.on('message', (msg) => console.log(msg))
worker.postMessage('start')
} else {
parentPort.on('message', () => {
parentPort.postMessage('done')
})
}
// Web Workers (browser) — similar API
// const worker = new Worker('worker.js') Modules & Imports
ES Modules (ESM) are the standard. CommonJS (CJS) is legacy but still used in Node.js. Bundlers like Vite, esbuild, and webpack handle bundling for the browser.
// Named exports
export const PI = 3.14
export function add(a, b) { return a + b }
// Default export
export default class App {}
// Named imports
import { add, PI } from './math.js'
// Default import
import App from './App.js'
// Rename imports
import { add as sum } from './math.js'
// Import everything
import * as math from './math.js'
// Dynamic import (code splitting)
const module = await import('./heavy.js')
// Re-export
export { add } from './math.js'
export { default } from './App.js'
// CommonJS (Node.js legacy)
const fs = require('fs')
module.exports = { add } Error Handling
Use try/catch/finally for synchronous and async error handling.
// Try / catch
try {
JSON.parse('invalid');
} catch (error) {
console.error('Parse failed:', error.message);
} finally {
console.log('always runs');
}
// Throwing errors
function divide(a, b) {
if (b === 0) throw new Error('Division by zero');
return a / b;
}
// Async error handling
try {
const res = await fetch('/api');
const data = await res.json();
} catch (err) {
console.error('Fetch failed:', err);
}
// Promise catch
fetch('/api')
.then((res) => res.json())
.catch((err) => console.error(err)); Memory Management
Garbage collected. V8 uses a generational GC (Scavenger for young gen, Mark-Sweep/Mark-Compact for old gen). No manual memory management.
// Memory is automatically allocated and freed
let obj = { name: 'Alice' } // Allocated on heap
obj = null // Eligible for GC
// WeakRef — reference that doesn't prevent GC
let target = { data: 'important' }
const weak = new WeakRef(target)
weak.deref() // { data: 'important' } or undefined
// FinalizationRegistry — callback when object is GC'd
const registry = new FinalizationRegistry((value) => {
console.log(`${value} was garbage collected`)
})
registry.register(target, 'my-target')
// WeakMap / WeakSet — keys are weakly held
const cache = new WeakMap()
cache.set(someObj, expensiveResult)
// When someObj is GC'd, the entry is removed
Common memory issues:
- Closures retaining references longer than needed
- Event listeners not removed (use
AbortController) - Detached DOM nodes still referenced in JS
- Large arrays/buffers — use streaming or
ArrayBuffer - Timers (
setInterval) not cleared
Use Chrome DevTools → Memory tab to profile heap snapshots and detect leaks.
Performance Profiling
Chrome DevTools and Node.js built-in profiler are the primary tools. performance.now() for manual timing.
// Manual timing
const start = performance.now()
doExpensiveWork()
console.log(`Took ${performance.now() - start}ms`)
// console.time / console.timeEnd
console.time('fetch')
await fetch('/api/data')
console.timeEnd('fetch') // fetch: 142.3ms
// Performance API (browser)
performance.mark('start')
doWork()
performance.mark('end')
performance.measure('work', 'start', 'end')
console.log(performance.getEntriesByName('work'))
# Node.js built-in profiler
node --prof app.js
node --prof-process isolate-*.log > profile.txt
# Node.js inspector (CPU + heap profiling)
node --inspect app.js
# Open chrome://inspect in Chrome
# Clinic.js — Node.js performance toolkit
npx clinic doctor -- node app.js
npx clinic flame -- node app.js
npx clinic bubbleprof -- node app.js
# Chrome DevTools
# Performance tab: CPU flame chart, call tree
# Memory tab: heap snapshots, allocation timeline
# Lighthouse: web performance auditing Interop
JavaScript interops with native code via N-API/node-addon-api (Node.js), WebAssembly, and Web APIs (browser).
// WebAssembly — run compiled code in JS
const wasmModule = await WebAssembly.instantiateStreaming(
fetch('module.wasm')
)
const result = wasmModule.instance.exports.add(1, 2)
// Node.js native addons (N-API)
// Written in C/C++, compiled with node-gyp
const addon = require('./build/Release/myaddon.node')
addon.hello() // Calls native C++ function
// Node.js FFI with ffi-napi
const ffi = require('ffi-napi')
const libm = ffi.Library('libm', {
ceil: ['double', ['double']],
})
libm.ceil(1.5) // 2
// Child process — call any system command
import { execSync, spawn } from 'child_process'
const output = execSync('ls -la').toString()
// Browser Web APIs
navigator.geolocation.getCurrentPosition(pos => { })
const stream = await navigator.mediaDevices.getUserMedia({ video: true })
// postMessage — cross-origin / worker communication
worker.postMessage({ type: 'compute', data })
window.parent.postMessage('done', 'https://parent.com')
// JSON — universal data interchange
const data = JSON.parse(jsonString)
const json = JSON.stringify(data) Packaging & Distribution
Publish libraries to npm. Distribute apps as Docker containers, serverless functions, or static sites.
# Publish a library to npm
npm login
npm publish # Public package
npm publish --access public # Scoped package (@org/pkg)
# package.json essentials
{
"name": "my-lib",
"version": "1.0.0",
"main": "dist/index.cjs",
"module": "dist/index.mjs",
"types": "dist/index.d.ts",
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
}
},
"files": ["dist"]
}
# Build before publishing
npm run build
npm pack --dry-run # Preview what gets published
# CLI distribution
# Add "bin" field to package.json
# "bin": { "mycli": "./dist/cli.js" }
npm publish
# Users: npx mycli or npm install -g mycli
# Docker
FROM node:22-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --production
COPY dist/ ./dist/
CMD ["node", "dist/index.js"]
# Static site deployment
# Vercel, Netlify, Cloudflare Pages — auto-deploy from Git
# npx vercel deploy