A practical, comprehensive reference for JavaScript covering fundamentals through advanced language features, browser and Node.js environments, asynchronous pr…
A practical, comprehensive reference for JavaScript covering fundamentals through advanced language features, browser and Node.js environments, asynchronous programming, modules, OOP, performance, security, and modern tooling. Targets current ECMAScript standards (ES2023/ES2024+) with clear notes on environment differences. Prefer modern syntax (const/let, arrow functions, optional chaining, nullish coalescing, modules, async/await).
Table of Contents
JavaScript Overview
What JavaScript Is
JavaScript is a high-level, multi-paradigm programming language primarily used for web development. It runs in browsers to add interactivity to web pages and on servers via Node.js (and other runtimes). It supports imperative, object-oriented, and functional programming styles.
Major Characteristics
- Dynamically typed
- Prototype-based object model (with class syntax sugar)
- First-class functions and closures
- Single-threaded with an event loop for concurrency
- Automatic memory management (garbage collection)
- Highly embeddable and embeddable in many environments
ECMAScript vs JavaScript
ECMAScript (ES) is the standardized specification (ECMA-262). JavaScript is the most common implementation of that specification. Features are referenced by edition year (ES2023, ES2024, etc.) or by stage in the TC39 process.
Browser vs Node.js Runtimes
| Aspect |
Browser |
Node.js |
| Global object |
window / globalThis |
global / globalThis |
| DOM / Web APIs |
Yes |
No (unless polyfilled) |
| File system |
No (sandboxed) |
Yes (fs) |
| Modules |
ES modules (native) |
ES modules + CommonJS |
| Networking |
fetch, XHR |
http, https, fetch (modern) |
| Process control |
Limited |
Full (process) |
Engines: V8, SpiderMonkey, JavaScriptCore
- V8: Chrome, Edge, Node.js, Deno — highly optimized JIT
- SpiderMonkey: Firefox
- JavaScriptCore (JSC): Safari / WebKit
All implement the ECMAScript standard with varying degrees of feature completeness and performance characteristics.
Just-in-Time Compilation
Modern engines parse source → bytecode → interpret, then JIT-compile hot code paths to machine code. Optimizations include inline caching, type feedback, and speculative optimization with deoptimization fallbacks.
Single-Threaded Event Loop Model
JavaScript has one main thread. Asynchronous work is scheduled via the event loop:
- Call stack executes synchronous code
- Microtask queue (Promises,
queueMicrotask) drains fully after each task
- Task/macrotask queue (
setTimeout, I/O, UI events) runs next task
- Rendering (browsers) may interleave
This enables non-blocking I/O without true parallelism on the main thread (Workers exist for parallelism).
How JavaScript Code Is Executed
- Parsing (syntax → AST)
- Compilation / interpretation
- Execution in the call stack
- Asynchronous callbacks/Promises scheduled on queues
- Garbage collection of unreachable objects
Strict Mode
Strict mode ("use strict";) enables a restricted variant of JavaScript that catches common errors and prohibits certain unsafe actions.
"use strict";
// Prevents accidental globals
x = 10; // ReferenceError
// Makes assignments to non-writable properties throw
Object.defineProperty(obj, "x", { value: 1, writable: false });
obj.x = 2; // TypeError
// Disallows `with`, octal literals, etc.
Modules and classes are automatically strict. Prefer writing code that works correctly under strict mode.
Installation and Environment
Running JavaScript in the Browser
- Developer Tools Console (F12 → Console)
- Inline or external
<script> tags
- ES modules:
<script type="module" src="app.js">
<script type="module">
import { greet } from './greet.js';
console.log(greet('World'));
</script>
Installing Node.js and Package Managers
Download from nodejs.org (LTS recommended) or use a version manager (nvm, fnm, volta).
# Check installation
node -v
npm -v
# Alternative package managers
npm install -g pnpm yarn
Checking Versions and Running Scripts
node -v # e.g. v22.x
npm -v
node script.js
node --experimental-vm-modules ... # when needed
REPL
node
> 1 + 1
2
> .exit
Environment Variables and PATH
process.env in Node.js
- Shell:
export VAR=value (Unix) or set VAR=value (Windows)
- Ensure Node binaries are on
PATH
Common package managers: npm (default), pnpm (efficient disk use), yarn (classic or berry).
JavaScript Syntax Fundamentals
Statements vs Expressions
- Expression: produces a value (
2 + 2, x = 5, fn())
- Statement: performs an action (
if, for, return, declaration)
Comments
// Single-line
/*
Multi-line
*/
/**
* JSDoc-style documentation
*/
Variables
Prefer const by default; use let when reassignment is required. Avoid var.
const PI = 3.14159; // block-scoped, cannot reassign
let counter = 0; // block-scoped, can reassign
// var legacy = 1; // function-scoped, hoisted, avoid
Block Scope vs Function Scope
let/const are block-scoped. var is function-scoped (or global).
if (true) {
let x = 1;
const y = 2;
var z = 3;
}
// x and y are not accessible here
// z is accessible (function/global scope)
Temporal Dead Zone
Accessing a let/const variable before its declaration in the same scope throws ReferenceError.
console.log(a); // ReferenceError
let a = 5;
Naming Conventions and Identifiers
- camelCase for variables and functions
- PascalCase for classes and constructors
- UPPER_SNAKE for constants
- Identifiers: letters,
$, _, digits (not starting with digit)
- Reserved words cannot be used as identifiers
Operators
Arithmetic: + - * / % **
Comparison: === !== == != < > <= >=
Logical: && || ! ??
Bitwise: & | ^ ~ << >> >>>
Assignment: = += -= *= /= %= **= &&= ||= ??=
Unary: + - ! ~ typeof void delete ++ --
Optional chaining: ?.
Nullish coalescing: ??
Operator Precedence
(Higher numbers bind tighter. Simplified common order.)
| Precedence |
Operators |
| 20 |
Grouping ( ) |
| 19 |
Member access . [] optional ?. |
| 18 |
new (with args) |
| 17 |
Function call () |
| 16 |
Postfix ++ -- |
| 15 |
Prefix ++ -- ! ~ typeof void delete |
| 14 |
Exponentiation ** (right-assoc) |
| 13 |
* / % |
| 12 |
+ - |
| 11 |
Bit shifts |
| 10 |
Relational < <= > >= |
| 9 |
Equality === !== == != |
| 8–5 |
Bitwise & ^ | |
| 4 |
Logical && |
| 3 |
Logical || / ?? |
| 2 |
Conditional ?: |
| 1 |
Assignment |
| 0 |
Comma , |
Truthiness, Falsiness, and Equality
Falsy values: false, 0, -0, 0n, "", null, undefined, NaN
Everything else is truthy (including [], {}, "0").
Strict equality (=== / !==) — no type coercion. Prefer this.
Loose equality (== / !=) — performs coercion; avoid in most cases.
0 == false; // true (avoid)
0 === false; // false
null == undefined; // true
null === undefined; // false
Type Coercion Basics
JavaScript coerces types in many contexts (+, ==, if, etc.). Prefer explicit conversion:
Number("42"); // 42
String(42); // "42"
Boolean(1); // true
+"42"; // 42 (unary plus)
Built-in Data Types and Values
Primitive Types
| Type |
Example |
typeof result |
| undefined |
undefined |
"undefined" |
| null |
null |
"object" (quirk) |
| boolean |
true / false |
"boolean" |
| number |
42, 3.14, NaN |
"number" |
| bigint |
42n |
"bigint" |
| string |
"hello" |
"string" |
| symbol |
Symbol("id") |
"symbol" |
Objects (Reference Type)
Everything that is not a primitive is an object (including arrays, functions, dates, etc.).
const obj = { a: 1 };
const arr = [1, 2, 3];
const fn = () => {};
Primitives are immutable and compared by value. Objects are mutable and compared by reference.
typeof Quirks and Type Checking
typeof null; // "object" (historical bug)
typeof []; // "object"
typeof (() => {}); // "function"
Array.isArray([]); // true
Number.isNaN(NaN); // true
value === null; // reliable null check
value == null; // null or undefined
Recommended patterns:
function isObject(value) {
return value !== null && typeof value === "object";
}
Numbers and Mathematics
Numbers are IEEE 754 double-precision floating-point (64-bit).
Number.MAX_SAFE_INTEGER; // 9007199254740991
Number.MIN_SAFE_INTEGER; // -9007199254740991
Number.isSafeInteger(42); // true
Special values: NaN, Infinity, -Infinity, -0.
Number.isNaN(NaN); // true
isNaN("foo"); // true (coerces — prefer Number.isNaN)
1 / 0; // Infinity
0 / 0; // NaN
Parsing:
Number("42"); // 42
parseInt("42px", 10); // 42
parseFloat("3.14abc"); // 3.14
Math object (common methods):
Math.abs(-5); // 5
Math.ceil(4.2); // 5
Math.floor(4.8); // 4
Math.round(4.5); // 5
Math.max(1, 5, 3); // 5
Math.min(...[1, 5, 3]); // 1
Math.random(); // [0, 1)
Math.sqrt(16); // 4
Math.pow(2, 10); // 1024
Math.trunc(4.9); // 4
BigInt for arbitrary-precision integers:
const big = 9007199254740993n;
big + 1n; // 9007199254740994n
// Cannot mix Number and BigInt without conversion
Floating-point precision:
0.1 + 0.2; // 0.30000000000000004
Number.EPSILON; // smallest difference
Strings
Strings are immutable sequences of UTF-16 code units.
const s1 = "hello";
const s2 = 'world';
const s3 = `template ${s1}`;
const s4 = String(42);
Template literals and tagged templates:
const name = "Ada";
`Hello, ${name}!`;
function tag(strings, ...values) {
return strings.reduce((acc, str, i) => acc + str + (values[i] ?? ""), "");
}
tag`Hello ${name}`;
Common methods:
| Method |
Purpose |
Mutating? |
length |
Character count |
— |
charAt(i) / [] |
Character at index |
No |
includes(sub) |
Contains substring |
No |
indexOf / lastIndexOf |
Position of substring |
No |
startsWith / endsWith |
Prefix/suffix test |
No |
slice(start, end) |
Extract portion |
No |
substring |
Similar to slice (less preferred) |
No |
split(sep) |
Split into array |
No |
trim / trimStart / trimEnd |
Remove whitespace |
No |
toLowerCase / toUpperCase |
Case conversion |
No |
padStart / padEnd |
Pad to length |
No |
repeat(n) |
Repeat string |
No |
replace / replaceAll |
Replace matches |
No |
match / matchAll |
Regex matching |
No |
normalize |
Unicode normalization |
No |
"hello".toUpperCase(); // "HELLO"
" hi ".trim(); // "hi"
"abc".includes("b"); // true
"a,b,c".split(","); // ["a","b","c"]
"hello".slice(1, 4); // "ell"
Unicode and code points:
"😀".length; // 2 (UTF-16)
[..."😀"].length; // 1
"😀".codePointAt(0); // 128512
String.fromCodePoint(128512); // "😀"
Arrays
const a = [1, 2, 3];
const b = Array.of(1, 2, 3);
const c = Array.from("abc"); // ["a","b","c"]
const d = new Array(3); // sparse length-3 array (avoid)
Mutating methods (change the original array): push, pop, shift, unshift, splice, sort, reverse, fill, copyWithin.
Non-mutating methods (return new array/value): slice, concat, map, filter, reduce, flat, flatMap, toSorted, toReversed, toSpliced, with.
const arr = [1, 2, 3];
arr.push(4); // mutates → [1,2,3,4]
arr.pop(); // mutates → [1,2,3]
arr.unshift(0); // mutates
arr.shift(); // mutates
arr.slice(1, 3); // [2,3] (new)
arr.concat([4, 5]); // new array
arr.map(x => x * 2); // [2,4,6]
arr.filter(x => x > 1); // [2,3]
arr.reduce((acc, x) => acc + x, 0); // 6
arr.find(x => x > 1); // 2
arr.findIndex(x => x > 1); // 1
arr.some(x => x > 2); // true
arr.every(x => x > 0); // true
arr.includes(2); // true
arr.indexOf(2); // 1
arr.join("-"); // "1-2-3"
arr.flatMap(x => [x, x * 2]); // [1,2,2,4,3,6]
Copying (shallow):
const copy1 = [...arr];
const copy2 = arr.slice();
const copy3 = Array.from(arr);
Sparse arrays have empty slots. Prefer dense arrays. Array-like objects (e.g., arguments, NodeList) can be converted with Array.from or spread.
Objects
const obj = {
name: "Ada",
age: 36,
greet() {
return `Hi, ${this.name}`;
},
["computed" + "Key"]: true,
};
Property access:
obj.name; // dot
obj["name"]; // bracket (dynamic keys)
obj?.name; // optional chaining
Shorthand:
const name = "Ada";
const person = { name, age: 36 }; // property shorthand
Object static methods:
Object.keys(obj); // ["name", "age", ...]
Object.values(obj);
Object.entries(obj); // [["name","Ada"], ...]
Object.fromEntries([["a",1],["b",2]]);
Object.assign({}, obj, { extra: true });
Object.freeze(obj); // shallow immutable
Object.seal(obj);
Object.create(proto);
Object.hasOwn(obj, "name"); // preferred over hasOwnProperty
Prototypes:
Every object has an internal [[Prototype]]. Lookup walks the chain.
const proto = { greet() { return "hi"; } };
const obj = Object.create(proto);
obj.greet(); // "hi"
Object.getPrototypeOf(obj) === proto; // true
this binding (see Functions section for details).
Getters / setters:
const person = {
first: "Ada",
last: "Lovelace",
get fullName() {
return `${this.first} ${this.last}`;
},
set fullName(value) {
[this.first, this.last] = value.split(" ");
},
};
Symbols as keys (unique, non-enumerable by default in some iterations):
const id = Symbol("id");
const obj = { [id]: 123 };
Functions
Declarations vs Expressions vs Arrows
// Function declaration (hoisted)
function add(a, b) {
return a + b;
}
// Function expression
const multiply = function (a, b) {
return a * b;
};
// Arrow function (lexical this, no arguments object, no constructor)
const divide = (a, b) => a / b;
const square = x => x * x;
const block = (x) => {
return x * 2;
};
Parameters
function greet(name = "World", ...rest) {
console.log(`Hello, ${name}`, rest);
}
greet("Ada", "extra"); // Hello, Ada ["extra"]
Prefer rest parameters over the arguments object.
First-Class and Higher-Order Functions
Functions can be stored, passed, and returned.
function createMultiplier(factor) {
return (n) => n * factor;
}
const double = createMultiplier(2);
double(5); // 10
Closures
A function retains access to its lexical environment.
function makeCounter() {
let count = 0;
return () => ++count;
}
const counter = makeCounter();
counter(); // 1
counter(); // 2
this Binding
- Regular functions:
this depends on call site
- Arrow functions: lexical
this (from enclosing scope)
- Methods: receiver object
- Explicit:
call, apply, bind
const obj = {
name: "Ada",
regular() { return this.name; },
arrow: () => this.name, // lexical (likely global/undefined)
};
obj.regular(); // "Ada"
const unbound = obj.regular;
unbound(); // undefined (or global in non-strict)
obj.regular.call({ name: "Grace" }); // "Grace"
const bound = obj.regular.bind({ name: "Grace" });
IIFEs (modern alternatives)
// Classic IIFE (less needed with modules and block scope)
(function () {
// private scope
})();
// Modern: blocks + const/let, or modules
{
const private = 1;
}
Recursion
function factorial(n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}
Destructuring, Spread, and Rest
// Array
const [first, second, ...rest] = [1, 2, 3, 4];
const [a = 10, b] = [undefined, 2];
// Object
const { name, age: years = 0, ...others } = { name: "Ada", age: 36, city: "London" };
// Nested
const { address: { city } } = { address: { city: "London" } };
// Function parameters
function print({ name, age = 0 }) {
console.log(name, age);
}
// Spread
const arr2 = [...arr1, 4, 5];
const obj2 = { ...obj1, extra: true };
Math.max(...[1, 5, 3]);
Control Flow
if (condition) {
// ...
} else if (other) {
// ...
} else {
// ...
}
const value = condition ? "yes" : "no";
switch (expr) {
case "a":
// ...
break;
case "b":
case "c":
// fall-through
break;
default:
// ...
}
for (let i = 0; i < 10; i++) { /* ... */ }
for (const item of iterable) { /* ... */ }
for (const key in object) { /* ... */ } // own + inherited enumerable
while (condition) { /* ... */ }
do { /* ... */ } while (condition);
// break / continue
// Labeled statements (rare)
outer: for (...) {
for (...) {
break outer;
}
}
Prefer for...of, map/filter/reduce, or array methods over classic for when appropriate.
Iterables, Iterators, and Generators
Iterable protocol: object with [Symbol.iterator]() that returns an iterator.
Iterator protocol: object with next() returning { value, done }.
const iterable = {
*[Symbol.iterator]() {
yield 1;
yield 2;
},
};
for (const v of iterable) {
console.log(v);
}
Generators:
function* idGenerator() {
let id = 1;
while (true) {
yield id++;
}
}
const gen = idGenerator();
gen.next(); // { value: 1, done: false }
gen.next(); // { value: 2, done: false }
function* delegate() {
yield* [1, 2, 3];
}
Generators enable lazy sequences and custom iteration.
Asynchronous JavaScript
Event Loop Recap
- Call stack
- Microtask queue (Promises, MutationObserver, queueMicrotask)
- Task queue (setTimeout, setInterval, I/O, UI events)
Microtasks run to completion before the next task.
Callbacks
fs.readFile("file.txt", (err, data) => {
if (err) { /* handle */ return; }
// use data
});
// Nested callbacks → "callback hell"
Promises
const promise = new Promise((resolve, reject) => {
// async work
if (success) resolve(value);
else reject(error);
});
promise
.then(value => { /* ... */ return nextValue; })
.catch(err => { /* ... */ })
.finally(() => { /* cleanup */ });
Static methods:
Promise.resolve(42);
Promise.reject(new Error("fail"));
Promise.all([p1, p2]); // fails fast
Promise.allSettled([p1, p2]); // waits for all
Promise.race([p1, p2]); // first settled
Promise.any([p1, p2]); // first fulfilled
async / await
async function fetchUser(id) {
try {
const res = await fetch(`/api/users/${id}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return await res.json();
} catch (err) {
console.error(err);
throw err;
}
}
// Top-level await (ES modules)
const data = await fetchUser(1);
Concurrent vs sequential:
// Sequential
const a = await fetchA();
const b = await fetchB();
// Concurrent
const [a, b] = await Promise.all([fetchA(), fetchB()]);
Cancellation with AbortController
const controller = new AbortController();
const signal = controller.signal;
fetch(url, { signal })
.then(/* ... */)
.catch(err => {
if (err.name === "AbortError") { /* cancelled */ }
});
controller.abort();
Modules
ES Modules
// math.js
export const PI = 3.14159;
export function add(a, b) { return a + b; }
export default function subtract(a, b) { return a - b; }
// main.js
import subtract, { PI, add } from "./math.js";
import * as math from "./math.js";
import { add as sum } from "./math.js";
Dynamic import:
const module = await import("./math.js");
Package.json:
{
"type": "module"
}
CommonJS (Node.js legacy)
// math.cjs
module.exports = { add: (a, b) => a + b };
// or exports.add = ...
const { add } = require("./math.cjs");
Interop notes: ES modules are static and async-friendly. CommonJS is synchronous. Use .mjs / .cjs extensions or "type" field to disambiguate. Circular dependencies behave differently; prefer clear dependency direction.
Classes and Object-Oriented Programming
class Person {
static species = "Homo sapiens";
#privateId = crypto.randomUUID(); // private field
constructor(name, age) {
this.name = name;
this.age = age;
}
greet() {
return `Hi, I'm ${this.name}`;
}
get info() {
return `${this.name} (${this.age})`;
}
static createAnonymous() {
return new Person("Anonymous", 0);
}
}
class Employee extends Person {
constructor(name, age, role) {
super(name, age);
this.role = role;
}
greet() {
return `${super.greet()} — ${this.role}`;
}
}
Private fields/methods (#) are truly private (not just convention).
Prototype relationship: class syntax sets up the prototype chain. extends links prototypes.
Composition over inheritance:
const canFly = (obj) => ({
...obj,
fly() { return "flying"; },
});
const bird = canFly({ name: "Sparrow" });
Prefer composition/mixins for flexibility when inheritance hierarchies become deep.
Error Handling
try {
// code that may throw
throw new Error("Something went wrong");
} catch (err) {
console.error(err.message);
console.error(err.stack);
} finally {
// always runs
}
Built-in errors: Error, TypeError, ReferenceError, SyntaxError, RangeError, URIError, EvalError (legacy).
Custom errors:
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = "ValidationError";
}
}
Promises / async:
async function run() {
try {
await riskyOperation();
} catch (err) {
// handle
}
}
// Always attach .catch or use try/catch with await
promise.catch(err => { /* ... */ });
Best practices: fail fast, provide context, avoid swallowing errors, distinguish operational vs programmer errors.
Browser Environment and DOM
Globals: window, document, navigator, location, history, localStorage, sessionStorage.
Selecting elements:
document.getElementById("id");
document.querySelector(".class");
document.querySelectorAll("div.item"); // NodeList
Traversal & modification:
el.parentElement;
el.children;
el.nextElementSibling;
el.textContent = "safe text";
el.innerHTML = "<b>unsafe if untrusted</b>"; // XSS risk
el.setAttribute("data-id", "1");
el.classList.add("active");
Creating / removing:
const div = document.createElement("div");
div.textContent = "Hello";
parent.append(div);
el.remove();
Events:
el.addEventListener("click", (event) => {
event.preventDefault();
event.stopPropagation();
console.log(event.target);
});
// Delegation
parent.addEventListener("click", (e) => {
if (e.target.matches(".button")) {
// handle
}
});
Timing:
setTimeout(() => {}, 1000);
setInterval(() => {}, 1000);
requestAnimationFrame(callback);
fetch:
const res = await fetch("/api/data", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ key: "value" }),
});
const data = await res.json();
Storage:
localStorage.setItem("key", "value");
localStorage.getItem("key");
sessionStorage.clear();
Modern APIs (high-level): Intersection Observer, Mutation Observer, Resize Observer, ResizeObserver, Web Workers, Service Workers, WebSockets, BroadcastChannel, etc.
Node.js Essentials
Globals / module-scoped:
process.env.NODE_ENV;
process.argv;
process.cwd();
import.meta.url; // ES modules
// __dirname / __filename available via:
import { fileURLToPath } from "node:url";
import { dirname } from "node:path";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
Core modules (prefix with node: recommended):
| Module |
Purpose |
fs / fs/promises |
File system |
path |
Path manipulation |
http / https |
HTTP servers/clients |
url |
URL parsing |
events |
EventEmitter |
stream |
Streams |
util |
Utilities |
crypto |
Cryptography |
os |
Operating system info |
buffer |
Binary data |
child_process |
Spawn processes |
File I/O:
import { readFile, writeFile } from "node:fs/promises";
const data = await readFile("file.txt", "utf8");
await writeFile("out.txt", data);
Simple HTTP server:
import http from "node:http";
const server = http.createServer((req, res) => {
res.writeHead(200, { "Content-Type": "text/plain" });
res.end("Hello World\n");
});
server.listen(3000, () => {
console.log("Listening on :3000");
});
Streams & EventEmitter:
import { EventEmitter } from "node:events";
const ee = new EventEmitter();
ee.on("event", (data) => console.log(data));
ee.emit("event", "payload");
Working with JSON and Data Formats
const obj = { name: "Ada", age: 36 };
const json = JSON.stringify(obj, null, 2); // pretty
const parsed = JSON.parse(json);
// Reviver / replacer
JSON.stringify(obj, (key, value) => {
if (key === "age") return undefined; // omit
return value;
});
JSON.parse(json, (key, value) => {
if (key === "date") return new Date(value);
return value;
});
Pitfalls:
undefined values are omitted or become null in arrays
Date objects become ISO strings
- Circular references throw
NaN / Infinity become null
- BigInt is not supported by default
CSV and other formats require libraries or manual parsing.
Regular Expressions
const re1 = /pattern/flags;
const re2 = new RegExp("pattern", "flags");
Flags: g (global), i (ignore case), m (multiline), s (dotAll), u (unicode), y (sticky), d (indices).
Methods:
re.test(str); // boolean
re.exec(str); // match object or null
str.match(re); // array or null
str.matchAll(re); // iterator (needs /g)
str.replace(re, replacement);
str.replaceAll(re, replacement);
str.search(re); // index
str.split(re);
Groups:
const re = /(?<year>\d{4})-(?<month>\d{2})/;
const match = "2024-09".match(re);
match.groups.year; // "2024"
Lookaheads / lookbehinds:
/(?=\d)/; // positive lookahead
/(?!\d)/; // negative lookahead
/(?<=\d)/; // positive lookbehind
/(?<!\d)/; // negative lookbehind
Dates and Times
Date is mutable and has many quirks (month is 0-based, local vs UTC confusion).
const now = new Date();
const d = new Date("2024-09-18T12:00:00Z");
Date.now(); // timestamp ms
d.getTime();
d.toISOString();
d.toLocaleDateString("en-US");
Prefer libraries (date-fns, Luxon, Day.js) or the emerging Temporal API (where available) for complex date work.
// Temporal (proposal / partial implementations)
// Temporal.Now.instant()
// Temporal.PlainDate.from("2024-09-18")
Maps, Sets, WeakMaps, WeakSets
| Collection |
Keys |
Iteration |
GC behavior |
Use case |
Map |
Any value |
Yes |
Strong |
Key-value with non-string keys |
Set |
Unique values |
Yes |
Strong |
Unique collections |
WeakMap |
Objects only |
No |
Weak keys |
Private data, metadata |
WeakSet |
Objects only |
No |
Weak |
Object presence tracking |
const map = new Map([["a", 1]]);
map.set("b", 2);
map.get("a");
map.has("a");
map.delete("a");
map.size;
const set = new Set([1, 2, 2, 3]); // {1,2,3}
set.add(4);
set.has(1);
const wm = new WeakMap();
const obj = {};
wm.set(obj, "metadata");
Typed Arrays and Binary Data
const buffer = new ArrayBuffer(16);
const view = new Uint8Array(buffer);
const dataView = new DataView(buffer);
view[0] = 255;
dataView.setInt32(0, 42, true); // little-endian
Common TypedArrays: Int8Array, Uint8Array, Uint8ClampedArray, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array, BigInt64Array, BigUint64Array.
In Node.js, Buffer is a subclass of Uint8Array with extra methods:
import { Buffer } from "node:buffer";
const buf = Buffer.from("hello", "utf8");
buf.toString("hex");
Internationalization
const number = 1234567.89;
new Intl.NumberFormat("de-DE").format(number); // "1.234.567,89"
new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" }).format(number);
const date = new Date();
new Intl.DateTimeFormat("en-GB", {
dateStyle: "full",
timeStyle: "long",
}).format(date);
new Intl.Collator("de", { sensitivity: "base" }).compare("a", "ä");
Also: Intl.PluralRules, Intl.RelativeTimeFormat, Intl.ListFormat, Intl.DisplayNames, etc.
Proxy and Reflect
const target = { name: "Ada" };
const proxy = new Proxy(target, {
get(obj, prop, receiver) {
console.log(`get ${String(prop)}`);
return Reflect.get(obj, prop, receiver);
},
set(obj, prop, value, receiver) {
if (prop === "age" && typeof value !== "number") {
throw new TypeError("age must be number");
}
return Reflect.set(obj, prop, value, receiver);
},
});
Common traps: get, set, has, deleteProperty, apply, construct, getOwnPropertyDescriptor, defineProperty, getPrototypeOf, setPrototypeOf, ownKeys, isExtensible, preventExtensions.
Use cases: validation, logging, reactive systems, virtualization, default values.
Reflect provides default operations as functions that return status instead of throwing in some cases.
Metaprogramming and Advanced Features
Well-known symbols: Symbol.iterator, Symbol.toStringTag, Symbol.hasInstance, Symbol.toPrimitive, Symbol.asyncIterator, etc.
Property descriptors:
Object.defineProperty(obj, "prop", {
value: 42,
writable: false,
enumerable: true,
configurable: false,
});
Object.getOwnPropertyDescriptor(obj, "prop");
Async iterators:
async function* asyncGen() {
yield await Promise.resolve(1);
yield await Promise.resolve(2);
}
for await (const v of asyncGen()) {
console.log(v);
}
Decorators: Stage 3 / varying support. Used heavily in frameworks (Angular, TypeScript experimental). Syntax and semantics continue to evolve; check current TC39 status and runtime support before relying on them in plain JS.
Type Checking and Documentation
Runtime checks:
function assertString(value) {
if (typeof value !== "string") {
throw new TypeError("Expected string");
}
}
JSDoc:
/**
* Adds two numbers.
* @param {number} a
* @param {number} b
* @returns {number}
*/
function add(a, b) {
return a + b;
}
/**
* @typedef {Object} User
* @property {string} name
* @property {number} age
*/
TypeScript interop: JSDoc annotations can be type-checked by TypeScript (// @ts-check or checkJs). Many projects migrate gradually from JS + JSDoc to full TypeScript. Static typing catches entire classes of errors at edit/compile time.
Testing
Why: correctness, regression prevention, documentation of behavior, safer refactoring.
Node.js built-in test runner (modern Node):
import { test, describe } from "node:test";
import assert from "node:assert/strict";
describe("math", () => {
test("add", () => {
assert.equal(1 + 2, 3);
});
});
Run with node --test.
Assertions: assert.equal, assert.deepEqual, assert.throws, etc.
Mocking concepts: replace dependencies with controlled fakes/stubs/spies.
Third-party (widely used):
- Jest — batteries-included, popular in React ecosystems
- Vitest — fast, Vite-native
- Mocha + Chai — flexible, older style
Test types: unit (isolated), integration (components together), end-to-end (full system).
Debugging
console.log("value", value);
console.table(arrayOfObjects);
console.time("label");
console.timeEnd("label");
console.trace();
console.assert(condition, "message");
Browser DevTools: Sources panel, breakpoints, watch expressions, call stack, network, performance.
Node.js:
node --inspect script.js
node --inspect-brk script.js
Attach Chrome DevTools or VS Code debugger. Source maps enable debugging of transpiled/bundled code.
Performance Optimization
Measure first:
performance.now();
console.time("op");
// work
console.timeEnd("op");
Principles:
- Avoid unnecessary work and allocations in hot paths
- Prefer simple data structures
- Batch DOM reads/writes
- Debounce / throttle expensive handlers
- Use
requestAnimationFrame for visual updates
- Be aware of hidden classes / polymorphic code in engines
- Avoid blocking the event loop (long synchronous tasks)
- Tree-shake and code-split for smaller bundles
- Memoize pure expensive functions when appropriate
Debounce / throttle (conceptual):
function debounce(fn, delay) {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), delay);
};
}
Virtual DOM (React etc.) is a higher-level strategy for minimizing real DOM mutations.
Security
- XSS: Never insert untrusted data as HTML. Prefer
textContent, templating that escapes, or sanitization libraries. Avoid innerHTML, document.write, and eval with user data.
- CSRF: Use anti-CSRF tokens, SameSite cookies, and proper CORS.
- CSP: Content-Security-Policy headers restrict script sources.
- Prototype pollution: Avoid merging untrusted objects into prototypes (
__proto__, constructor.prototype). Use Object.create(null) or harden merges.
- eval / new Function: Never with untrusted input.
- Secrets: Never hard-code; use environment variables / secret managers. Do not commit
.env with secrets.
- Dependencies: Audit (
npm audit), pin versions, prefer maintained packages, enable lockfiles.
- HTTPS / Secure cookies:
Secure, HttpOnly, SameSite attributes.
- Input validation: Validate and sanitize on the server; client checks are UX only.
Packaging and Tooling
package.json essentials:
{
"name": "my-package",
"version": "1.0.0",
"type": "module",
"main": "./dist/index.js",
"exports": {
".": "./dist/index.js"
},
"scripts": {
"dev": "node --watch src/index.js",
"test": "node --test",
"build": "esbuild src/index.js --bundle --outfile=dist/index.js"
},
"dependencies": {},
"devDependencies": {}
}
Semantic versioning: MAJOR.MINOR.PATCH.
Package managers: npm, pnpm, yarn — install, lockfiles, workspaces.
Bundlers (high-level): esbuild (extremely fast), Rollup (libraries), webpack (mature, complex), Vite (dev-server + Rollup for prod).
Transpilation: Babel (syntax transforms), TypeScript (tsc or via bundlers).
Lint / format: ESLint (rules + plugins), Prettier (opinionated formatting).
Modules, Bundling, and Modern Project Structure
Typical library:
src/
index.js
utils.js
dist/
package.json
README.md
Typical app:
src/
main.js
components/
utils/
styles/
public/
tests/
package.json
Monorepos often use workspaces (npm/pnpm/yarn) + tools like Turborepo or Nx.
Export only what is needed; prefer named exports for tree-shaking. Avoid side-effectful modules when possible.
Practical Real-World Examples
Hello World
Browser:
<script type="module">
console.log("Hello, browser!");
</script>
Node:
console.log("Hello, Node!");
Array / Object Transformations
const users = [
{ id: 1, name: "Ada", active: true },
{ id: 2, name: "Grace", active: false },
];
const activeNames = users
.filter(u => u.active)
.map(u => u.name);
Async Fetch with Error Handling
async function getJson(url) {
const res = await fetch(url);
if (!res.ok) {
throw new Error(`HTTP ${res.status}: ${res.statusText}`);
}
return res.json();
}
try {
const data = await getJson("/api/data");
console.log(data);
} catch (err) {
console.error("Failed:", err.message);
}
Simple DOM Manipulation + Event Delegation
const list = document.querySelector("#list");
list.addEventListener("click", (e) => {
if (e.target.matches("button.delete")) {
e.target.closest("li")?.remove();
}
});
Custom Promise Wrapper
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
Generator for Pagination
function* paginate(items, pageSize) {
for (let i = 0; i < items.length; i += pageSize) {
yield items.slice(i, i + pageSize);
}
}
Node.js File Reader/Writer
import { readFile, writeFile } from "node:fs/promises";
async function copyFile(src, dest) {
const data = await readFile(src);
await writeFile(dest, data);
}
Basic HTTP Server
import http from "node:http";
http.createServer((req, res) => {
res.end("OK");
}).listen(3000);
Debounce Utility
export function debounce(fn, wait) {
let timeoutId;
return function (...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => fn.apply(this, args), wait);
};
}
Deep Clone Considerations
// Structured clone (modern, handles many types)
const clone = structuredClone(original);
// JSON round-trip (loses functions, dates become strings, etc.)
const jsonClone = JSON.parse(JSON.stringify(original));
Class with Private Fields
class Counter {
#count = 0;
increment() { return ++this.#count; }
get value() { return this.#count; }
}
Module Example
// utils.js
export const sum = (arr) => arr.reduce((a, b) => a + b, 0);
// main.js
import { sum } from "./utils.js";
console.log(sum([1, 2, 3]));
Unit Test Example (Node test runner)
import { test } from "node:test";
import assert from "node:assert/strict";
import { sum } from "./utils.js";
test("sum adds numbers", () => {
assert.equal(sum([1, 2, 3]), 6);
});
AbortController Usage
const controller = new AbortController();
setTimeout(() => controller.abort(), 5000);
try {
const res = await fetch(url, { signal: controller.signal });
// ...
} catch (err) {
if (err.name === "AbortError") {
console.log("Request timed out / cancelled");
} else {
throw err;
}
}
Common JavaScript Errors and Troubleshooting
| Error / Symptom |
Typical Cause |
Diagnosis / Fix |
ReferenceError: x is not defined |
Undeclared variable, TDZ, wrong scope |
Check spelling, declaration order, scope |
TypeError: Cannot read properties of undefined/null |
Accessing property on nullish value |
Optional chaining ?., null checks, default values |
TypeError: x is not a function |
Calling non-function |
Verify type, import correctness |
SyntaxError |
Invalid syntax |
Check brackets, commas, reserved words |
RangeError |
Invalid length / stack overflow |
Check array sizes, recursion depth |
| Uncaught Promise rejection |
Missing .catch or try/catch around await |
Always handle rejections |
TDZ ReferenceError |
Access before let/const declaration |
Move declaration or avoid early access |
Incorrect this |
Lost method context / arrow misuse |
Use .bind, arrow functions carefully, or wrappers |
| Module resolution errors |
Wrong path, missing extension, "type" |
Check paths, extensions, package.json "type" |
| CORS errors (browser) |
Cross-origin request blocked |
Server must send proper CORS headers |
ERR_REQUIRE_ESM |
require() of ES module |
Use import or dual package |
Common JavaScript Mistakes
- Using
== instead of ===
- Mutating shared objects/arrays that callers still reference
- Losing
this when passing methods as callbacks
- Forgetting to handle Promise rejections
- Performing heavy CPU work on the main thread (blocking UI/event loop)
- Memory leaks from lingering event listeners, closures, or detached DOM nodes
- Over-relying on dynamic typing without runtime checks or types
- Polluting the global namespace
- Ignoring strict mode / writing non-strict code
- Inserting untrusted HTML (
innerHTML)
- Deeply nested callbacks instead of async/await or Promises
- Assuming floating-point arithmetic is exact
- Modifying objects while iterating with
for...in without care
JavaScript Quick Reference
Primitive Types & typeof
| Value |
typeof |
undefined |
"undefined" |
null |
"object" |
true/false |
"boolean" |
| numbers |
"number" |
42n |
"bigint" |
| strings |
"string" |
| symbols |
"symbol" |
| functions |
"function" |
| objects/arrays |
"object" |
Common Array Methods
| Method |
Mutates |
Returns |
push/pop |
Yes |
length / element |
shift/unshift |
Yes |
element / length |
splice |
Yes |
removed elements |
sort/reverse |
Yes |
array |
slice |
No |
new array |
map/filter |
No |
new array |
reduce |
No |
accumulated value |
find/findIndex |
No |
element / index |
includes |
No |
boolean |
flat/flatMap |
No |
new array |
Object Static Methods
Object.keys, values, entries, fromEntries, assign, create, freeze, seal, hasOwn, getPrototypeOf, defineProperty, getOwnPropertyDescriptors.
Promise Methods
then, catch, finally, Promise.all, allSettled, race, any, resolve, reject.
Useful One-Liners
const unique = [...new Set(arr)];
const groupBy = (arr, key) => arr.reduce((m, x) => ((m[x[key]] ??= []).push(x), m), {});
const sleep = ms => new Promise(r => setTimeout(r, ms));
const clamp = (n, min, max) => Math.min(Math.max(n, min), max);
JavaScript by Task
| Task |
Approach |
| Create variable |
const x = value; / let x = value; |
| Loop over array |
for (const item of arr) or arr.forEach |
| Transform array |
arr.map(fn) |
| Filter array |
arr.filter(fn) |
| Reduce to single value |
arr.reduce(fn, init) |
| Fetch JSON |
const data = await (await fetch(url)).json() |
| Handle errors |
try/catch + Promise .catch |
| Create class |
class Name { constructor() {} } |
| Private field |
#field = value; |
| Work with dates |
new Date() or library / Temporal |
| Read file (Node) |
await readFile(path, "utf8") |
| Write file (Node) |
await writeFile(path, data) |
| Create HTTP server |
http.createServer(...).listen(port) |
| Debounce function |
See debounce utility above |
| Deep clone |
structuredClone(obj) |
| Unique values |
[...new Set(arr)] |
| Optional property access |
obj?.prop?.nested |
| Default nullish value |
value ?? defaultValue |
Beginner-to-Advanced Learning Path
- Fundamentals: syntax, variables, types, operators, control flow, functions, arrays, objects
- Intermediate: closures,
this, prototypes, destructuring, modules, error handling
- Asynchronous: callbacks → Promises → async/await, event loop mental model
- Environment specialization: DOM + browser APIs or Node.js core modules
- Tooling: package managers, bundlers, linters, formatters, basic TypeScript/JSDoc
- Quality: testing, debugging, code organization
- Advanced: iterators/generators, Proxy, performance, security, design patterns, concurrency (Workers)
- Mastery: deep engine knowledge, contribution to open source, architecture at scale
JavaScript Best Practices
- Prefer
const by default; use let only when reassignment is needed
- Always use strict equality (
===)
- Handle errors and Promise rejections explicitly
- Keep functions small and focused; prefer pure functions when practical
- Prefer composition over deep inheritance
- Write readable, self-documenting code; name things clearly
- Use ES modules
- Lint and format consistently (ESLint + Prettier)
- Test critical logic
- Measure before optimizing
- Treat security as a first-class concern (XSS, injection, prototypes, dependencies)
- Stay current with ECMAScript and runtime changelogs
- Avoid
var, ==, with, eval, and unnecessary mutation
- Document public APIs (JSDoc or TypeScript)
JavaScript Glossary
Closure — Function that retains access to its lexical scope even when executed outside that scope.
Event Loop — Mechanism that processes the call stack, microtask queue, and task queue.
Microtask — High-priority queue item (Promise reactions, queueMicrotask) drained after current script/task.
Promise — Object representing a future value or error; foundation of async/await.
Prototype — Object from which another object inherits properties.
Hoisting — Declarations are processed before execution (behavior differs for var/function vs let/const).
Temporal Dead Zone (TDZ) — Period between entering scope and declaration where let/const cannot be accessed.
Iterable — Object that implements [Symbol.iterator].
Generator — Function that can pause/resume via yield and produces an iterator.
Proxy — Object that intercepts operations on another object.
Symbol — Unique, immutable primitive often used as object keys.
Tree-shaking — Dead-code elimination in bundlers based on static ES module structure.
Strict mode — Restricted language variant that catches errors and disables some legacy features.
JIT — Just-in-time compilation of hot code paths to native machine code.
Event delegation — Attaching a single listener higher in the DOM to handle events from descendants.
AbortController — Standard way to cancel fetch and other async operations.
Structured clone — Algorithm used by structuredClone, postMessage, etc., for deep copying many types.