Open-source libraries used

1 libraries are bundled into this tool's code.

JavaScript Cheatsheet — Quick Reference

A cheatsheet for modern JavaScript (ES2022) syntax, the standard library, and the most common DOM / Node helpers — covering about 80% of everyday scenarios.

JS

JavaScript ES2022

ECMAScript · Multi-paradigm · Dynamic, weakly typed

Recommended Learning Path

Start with "Hello World and Runtime" (Node.js or browser console) → learn variables, types, and control flow → master array and object higher-order methods → understand references, copying, functions, and closures → focus on async (Promise / async / await) → finally consult DOM, fetch, Node modules, build, and debugging as needed. The FAQ section is great for revisiting common pitfalls.

1.Hello World and Runtime

Run JavaScript with Node.js or in the browser, and understand scripts, modules, and npm project structure.

console output

console.log writes to the console. console.error prints errors, console.table renders tabular data. Works in browsers and Node.

1
2
3
4
5
console.log('Hello, world!');
console.error('something failed'); // red error output
console.log('values:', 42, true); // multiple arguments
console.table([{ id: 1 }, { id: 2 }]); // table
// Node: $ node script.js

Run with Node

node runs a .js file directly. node -e evaluates an inline expression; node -p prints the result. Preferred when no browser is required.

1
2
3
4
5
// $ node script.js # run the script
// $ node -e "console.log(1)" # execute inline
// $ node -p 1 + 2 # print result: 3
// interactive REPL: $ node
// syntax check: $ node --check script.js

Browser <script>

HTML loads JS via <script>. defer delays execution (after DOM is parsed), async loads asynchronously. type="module" enables ESM.

1
2
3
4
5
6
<!-- normal script (blocks rendering) -->
<script src="app.js"></script>
<!-- delayed execution: runs after DOM is parsed -->
<script src="app.js" defer></script>
<!-- ES module -->
<script type="module" src="app.js"></script>

Command-line arguments

In Node, process.argv holds the full command line: argv[0] is node, argv[1] is the script path, argv[2]+ are arguments.

1
2
3
4
5
6
// $ node app.js --name Nick
const args = process.argv.slice(2);
console.log(args); // ['--name', 'Nick']
// simplified parsing:
const name = process.argv[process.argv.indexOf('--name') + 1];
console.log('Hello, ' + name);

npm init

npm init creates a package.json. npm install adds dependencies and generates node_modules plus a lockfile.

1
2
3
4
5
// $ npm init -y # quick init
// $ npm install express # install and add to dependencies
// $ npm install -D vitest # dev dependency
// $ npm run build # run scripts.build
// $ npm ls # list installed packages

package.json

package.json describes the project: name/version, main entry, scripts, and dependencies.

1
2
3
4
5
6
7
8
9
10
11
{
"name": "my-app",
"version": "1.0.0",
"type": "module", // ESM module
"main": "src/index.js",
"scripts": {
"start": "node src/index.js",
"test": "vitest run"
},
"dependencies": { }
}

ES Modules

import/export modules: export exposes values, import pulls them in. Set type: "module" in package.json or use the .mjs extension.

1
2
3
4
5
6
7
// math.js
export const PI = 3.14;
export function add(a, b) { return a + b; }
export default function main() { }
// main.js
import main, { add, PI as pi } from './math.js';
console.log(add(1, 2), pi);

Strict mode

'use strict' enables strict mode: implicit globals are forbidden and silent errors become exceptions. ESM is strict by default.

1
2
3
4
5
6
'use strict';
// forbid assignment to undeclared variables:
x = 10; // ReferenceError
// strict mode fixes legacy quirks
// ESM / class / modern tooling enable it by default
// recommendation: declare at the top of every source file

2.Variables and Constants

let/const declarations, scope, dynamic typing, destructuring, and template literals.

let and const

let declares a mutable binding; const declares a constant binding (the object itself is still mutable). Both are block-scoped. Prefer const; switch to let when you need to reassign.

1
2
3
4
5
6
7
8
let count = 42;
count = 43; // OK: let can be reassigned
const name = 'Nick';
// name = 'X'; // throws: const cannot be reassigned
const obj = { a: 1 };
obj.a = 2; // OK: object properties are mutable
const arr = [];
arr.push(1); // OK: array contents are mutable

var vs let

var is function-scoped with hoisting; let/const are block-scoped. Modern code should always use let/const to avoid var's traps.

1
2
3
4
5
6
7
8
9
if (true) {
var x = 1; // var is hoisted to function scope
let y = 2; // let is only block-scoped
}
console.log(x); // 1
// console.log(y); // ReferenceError
// var allows redeclaration and hoisting:
console.log(z); // undefined (hoisted)
var z = 3;

Scope

Block scope {}, function scope, global scope. Inner scopes can read outer variables; outer scopes cannot read inner ones.

1
2
3
4
5
6
7
8
9
10
let global = 'g';
function outer() {
let outerVar = 'o';
function inner() {
let innerVar = 'i';
console.log(outerVar); // can access outer scope
console.log(global);
}
// innerVar is not visible here
}

Dynamic typing

JS variables have no type constraint: the same variable can hold any value. typeof checks the runtime type.

1
2
3
4
5
let value = 42; // number
value = 'hello'; // string (allowed)
value = { a: 1 }; // object
console.log(typeof value); // 'object'
// weakly typed: '5' + 3 = '53', '5' - 3 = 2 (implicit coercion)

Destructuring

Pull values from arrays or objects into variables. Object destructuring matches by name, array destructuring by position. Supports defaults and nested patterns.

1
2
3
4
5
6
7
const { name, age = 0 } = person; // object destructuring
const [first, , third] = arr; // array by position
const { config: { host = 'localhost' } } = app;
// rename:
const { name: userName } = person;
// swap:
[a, b] = [b, a];

Naming conventions

Variables/functions use lowerCamelCase; constants use UPPER_SNAKE_CASE; classes/components use UpperCamelCase. Booleans get is/has prefixes.

1
2
3
4
5
6
7
let userId = 1;
const MAX_RETRY = 3;
class UserProfile { }
function fetchData() { }
let isValid = false; // boolean prefix
let hasPermission = true;
// file names commonly use kebab-case: user-profile.js

Hoisting

var declarations and function declarations are hoisted to the top of their scope. let/const have a temporal dead zone (TDZ): accessing them before declaration throws.

1
2
3
4
5
6
7
console.log(foo()); // function declarations are hoisted, usable
function foo() { return 1; }
// console.log(a); // TDZ throws
let a = 1;
// var hoisting initializes to undefined:
console.log(b); // undefined
var b = 2;

Template literals

Backtick strings support ${} interpolation and multi-line content. Preferred over + concatenation for string building and multi-line text.

1
2
3
4
5
6
7
8
const name = 'Nick';
const age = 30;
const msg = `${name} is ${age} years old`;
const multi = `第一行
第二行
缩进保留`;
const expr = `${1 + 2}`; // '3'
// tagged templates: tag`...` advanced usage

3.Data Types

Primitives, objects, typeof checks, and type conversion.

Primitive types

There are 7 primitive types: string, number, boolean, null, undefined, symbol, and bigint. Everything else is object.

1
2
3
4
5
6
7
8
const s = 'text'; // string
const n = 42; // number
const b = true; // boolean
const nl = null; // null
const u = undefined; // undefined
const sym = Symbol('id'); // symbol
const big = 10n; // bigint
console.log(typeof s, typeof n, typeof nl);

Number and NaN

number covers integers and floats. NaN means "not a number"; Infinity is positive infinity. NaN is not equal to anything, including itself.

1
2
3
4
5
6
7
const n = 3.14;
const int = 42;
Number.isNaN(NaN); // true
Number.isFinite(1 / 0); // false (Infinity)
parseInt('42px', 10); // 42
parseFloat('3.14'); // 3.14
// floats are inexact: 0.1 + 0.2 !== 0.3

Strings

Strings are immutable. Use length, [i] indexing, and methods like toUpperCase/slice/split. Strings are sequences of UTF-16 code units.

1
2
3
4
5
6
7
8
const s = 'hello world';
s.length; // 11
s[0]; // 'h'
s.toUpperCase(); // 'HELLO WORLD'
s.slice(0, 5); // 'hello'
s.split(' '); // ['hello', 'world']
s.includes('lo'); // true
s.startsWith('he'); // true

Truthy and falsy

Falsy values: false, 0, '', null, undefined, NaN. Everything else is truthy. if checks coerce to boolean.

1
2
3
4
5
6
7
8
9
// only 6 falsy values:
!!false === false;
!!0 === false;
!!'' === false;
!!null === false;
!!undefined === false;
!!NaN === false;
// everything else is truthy: [] truthy, {} truthy, '0' truthy
// empty arrays are truthy! if (arr.length) is the accurate check

null and undefined

undefined means "not defined" or "not assigned"; null is an explicit empty value. Accessing a missing property returns undefined.

1
2
3
4
5
6
7
let a; // undefined
let b = null; // explicit empty
const obj = {};
console.log(obj.x); // undefined
console.log(obj?.x?.y); // undefined (optional chaining)
// null check: value == null covers both null and undefined
if (a == null) { /* empty */ }

Symbol and BigInt

Symbol creates unique identifiers (great for private object keys). BigInt is arbitrary-precision integers, written with a trailing n. typeof distinguishes them.

1
2
3
4
5
6
7
const key = Symbol('id');
obj[key] = 1; // unique property key
Symbol('a') === Symbol('a'); // false (always unique)
const big = 12345678901234567890n;
big + 10n; // BigInt arithmetic
// BigInt cannot mix with number:
// 10n + 1 // throws

typeof checks

typeof identifies primitive types; arrays, Date, and null all return 'object', so other checks are needed to distinguish them.

1
2
3
4
5
6
7
8
typeof 'a'; // 'string'
typeof 42; // 'number'
typeof true; // 'boolean'
typeof undefined; // 'undefined'
typeof null; // 'object' (historical bug)
Array.isArray([]); // true
// precise object check:
Object.prototype.toString.call([]); // '[object Array]'

Type conversion

Explicit conversion: String()/Number()/Boolean(). Implicit conversion causes many bugs: + concatenates, - coerces to number.

1
2
3
4
5
6
7
8
9
String(42); // '42'
Number('3.14'); // 3.14
Number('abc'); // NaN
Boolean(''); // false
// implicit:
'5' + 3; // '53' (+ concatenates)
'5' - 3; // 2 (- coerces to number)
!!'text'; // true
// safe number coercion: Number(null) is 0

Object types

Everything except primitives is an object: plain objects, arrays, functions, Date, Map, etc. Objects are reference types.

1
2
3
4
5
6
7
const obj = { a: 1, b: 'x' }; // plain object
const arr = [1, 2]; // array
const fn = () => {}; // function
const date = new Date(); // date
const map = new Map(); // Map
Object.keys(obj); // ['a', 'b']
// typeof returns 'object' or 'function' for all of these

4.References and Copying

Value vs reference, shallow vs deep copy, array/object references, and GC.

Value vs reference

Primitives are passed by value (copied); objects/arrays/functions are passed by reference (shared). This is the most important mental model in JS.

1
2
3
4
5
6
7
let a = 5, b = a; // b is a copy of a
b = 10;
console.log(a); // 5 (value copy)
const objA = { x: 1 };
const objB = objA; // shared reference
objB.x = 99;
console.log(objA.x); // 99 (same object reference)

Object references

Function arguments: an object argument shares the same object, so mutations inside the function affect the outside. Primitives are unaffected.

1
2
3
4
5
6
7
function mutate(o) { o.x = 99; }
const obj = { x: 1 };
mutate(obj);
console.log(obj.x); // 99 (modified by the function)
function bad(o) { o = {}; } // reassigning does not affect the caller
// copy explicitly when you need to:
// const copy = { ...obj };

Shallow copy

Spread {...obj} / [...arr] or Object.assign produces a one-level copy. Nested objects still share references.

1
2
3
4
5
6
const obj = { a: 1, nested: { b: 2 } };
const copy = { ...obj };
copy.a = 99; // original is unaffected
copy.nested.b = 99; // original is also changed (shallow copy)
const arrCopy = [...arr];
// Object.assign({}, obj) is an equivalent shallow copy

Deep copy

Deep copy recursively clones every level. Use structuredClone (modern) or hand-write a recursive clone. Functions and circular references need special handling.

1
2
3
4
5
6
const deep = structuredClone(obj); // built-in deep copy
// mutating the nested value does not affect the source:
deep.nested.b = 1;
console.log(obj.nested.b); // 2
// older environments:
// JSON.parse(JSON.stringify(obj)) drops functions and undefined

Array copy

[...arr] shallow-copies, slice() copies, Array.from converts. Nested arrays (multi-dimensional) are still shallow-copied.

1
2
3
4
5
6
const arr = [1, 2, [3]];
const copy = [...arr]; // shallow copy
const copy2 = arr.slice(); // same as above
copy[0] = 99; // original array unchanged
copy[2][0] = 99; // original array also changes!
// deep copy multi-dimensional arrays: structuredClone(arr)

JSON round-trip copy

JSON.stringify/parse is a simple deep-copy technique, but it drops undefined, functions, and Symbol, and does not support circular references.

1
2
3
4
5
6
const obj = { a: 1, b: { c: 2 }, d: undefined };
const copy = JSON.parse(JSON.stringify(obj));
// copy.d is dropped (undefined does not serialize)
// circular references throw:
// const o = {}; o.self = o;
// JSON.stringify(o) // throws

Garbage collection

V8 automatically collects unreachable objects. Locals are released when their function exits. Globals and closed-over variables extend lifetimes.

1
2
3
4
5
6
7
function create() {
const big = new Array(1000); // heap allocation
return () => big.length; // closure holds big
}
const fn = create();
// set to null when no longer needed so GC can collect:
// fn = null; releases the closure chain

Object equality

=== compares references for objects, not contents. Two objects with identical contents are not ===. Compare by field with a manual deep-equality routine.

1
2
3
4
5
6
7
8
const a = { x: 1 };
const b = { x: 1 };
a === b; // false (different references)
const c = a;
c === a; // true (same reference)
// compare contents manually:
JSON.stringify(a) === JSON.stringify(b); // true
// or compare field by field (use recursion/a library for nested values)

5.Control Flow

if/else, loops, switch, ternary, and logical short-circuit.

if / else

if/else branches on a condition that is truthiness-coerced. Chain with else-if for multiple branches.

1
2
3
4
5
6
7
8
9
10
const score = 85;
let grade;
if (score >= 90) {
grade = 'A';
} else if (score >= 60) {
grade = 'B';
} else {
grade = 'F';
}
console.log(grade);

Ternary operator

cond ? a : b is a one-line conditional. Nested ternaries hurt readability; use if/switch for complex logic.

1
2
3
4
5
6
const age = 20;
const type = age >= 18 ? 'adult' : 'minor';
// can also be used in return statements:
return ok ? data : null;
// chainable:
const v = n > 0 ? 'pos' : n < 0 ? 'neg' : 'zero';

for loop

The classic for loop uses an index. Most collection scenarios are cleaner with for...of or array methods.

1
2
3
4
5
6
for (let i = 0; i < 10; i++) {
console.log(i); // 0..9
}
for (let i = arr.length - 1; i >= 0; i--) {
// reverse iteration (when an index is needed)
}

for...of

for...of iterates iterables (arrays, strings, Map/Set, NodeList). No index needed.

1
2
3
4
5
6
7
8
9
for (const item of items) {
console.log(item);
}
for (const ch of 'abc') { console.log(ch); }
for (const [key, val] of map) {
console.log(key, val);
}
// when you need the index:
// for (const [i, v] of arr.entries())

for...in (objects)

for...in iterates an object's enumerable keys (including inherited ones). Use for...of for arrays, or Object.keys to iterate an object's keys.

1
2
3
4
5
6
7
const obj = { a: 1, b: 2 };
for (const key in obj) {
console.log(key, obj[key]);
}
// safer alternative:
Object.keys(obj).forEach(k => console.log(k));
// don't use for...in on arrays (it iterates string indices)

while / do-while

while checks first then runs; do-while runs at least once. Use when the number of iterations is unknown.

1
2
3
4
5
6
7
let i = 0;
while (i < 5) { i++; }
let x = 0;
do {
x++;
} while (x < 3); // runs at least once
// common pattern for streams/polling: while (true) + break

switch

switch uses strict equality (===). After a case matches, execution falls through to the next case until break. Watch for fall-through.

1
2
3
4
5
6
7
8
9
10
11
12
const day = 3;
switch (day) {
case 1:
console.log('Monday');
break;
case 2:
case 3: // grouped cases
console.log('Tue/Wed');
break;
default:
console.log('other');
}

Short-circuit and optional chaining

&&, ||, and ?? short-circuit. ?. chains safely; ?? falls back only on null/undefined.

1
2
3
4
5
6
7
const name = user?.name ?? 'guest'; // safe access with fallback
const ok = value && doSomething(); // run only when truthy
const fallback = a || 'default'; // fallback on any falsy value
// watch the difference:
// a ?? 'd' falls back only on null/undefined
// a || 'd' falls back on every falsy value
// ?. chaining: obj?.a?.b?.c

break and continue

continue skips to the next iteration; break exits the loop. Labeled break/continue controls nested loops.

1
2
3
4
5
6
7
8
9
10
for (let i = 0; i < 10; i++) {
if (i % 2 === 0) continue; // skip even numbers
if (i > 7) break; // exit early
}
outer:
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (j === 2) continue outer; // jump to the outer loop's next iteration
}
}

6.Functions and Closures

Function declarations, arrow functions, parameters, closures, this, and callbacks.

Function declarations and expressions

Function declarations are hoisted; function expressions are not. Arrow functions are expressions and more concise. The three forms have different syntax.

1
2
3
4
function add(a, b) { return a + b; } // declaration (hoisted)
const sub = function (a, b) { return a - b; }; // expression
const mul = (a, b) => a * b; // arrow function
// call syntax is uniform: add(1, 2) / sub(5, 3) / mul(2, 4)

Arrow functions

Arrow functions are concise, have no own this/arguments, cannot be used as constructors, and implicitly return single expressions.

1
2
3
4
5
6
7
8
const square = x => x * x; // single param can drop parentheses
const add = (a, b) => a + b;
const nothing = () => console.log('hi');
// multi-line body needs braces + return:
const calc = (a, b) => {
const sum = a + b;
return sum * 2;
};

Parameters and defaults

Function parameters can have defaults; rest collects the remainder. arguments is array-like and does not exist in arrow functions.

1
2
3
4
5
6
7
8
9
10
function greet(name = 'guest') {
return 'Hello, ' + name;
}
greet(); // 'Hello, guest'
function sum(...nums) { // rest parameter
return nums.reduce((a, b) => a + b, 0);
}
sum(1, 2, 3); // 6
// destructured parameter:
function f({ a, b }) { return a + b; }

Closures

A closure lets a function remember the scope where it was created: inner functions access outer variables even after the outer function has returned.

1
2
3
4
5
6
7
8
function counter() {
let count = 0; // captured by the closure
return () => ++count; // every call shares count
}
const c = counter();
c(); // 1
c(); // 2
// closures keep references to outer variables; watch memory usage

this binding

this is determined at call time: object methods get the object, regular functions in non-strict mode get the global, arrow functions inherit the outer this.

1
2
3
4
5
6
7
8
9
const obj = {
name: 'Nick',
greet() { return 'Hi ' + this.name; }, // method
arrow: () => this, // arrow: inherits outer this
};
obj.greet(); // 'Hi Nick'
// losing this:
const g = obj.greet; // when calling g(), this is the global
// fix: g.bind(obj) or wrap with an arrow () => obj.greet()

call / apply / bind

call/apply invoke immediately with a specified this; bind returns a new function bound to this. apply takes args as an array.

1
2
3
4
5
6
7
8
function info(a, b) { return this.name + a + b; }
const ctx = { name: 'Nick' };
info.call(ctx, 1, 2); // pass arguments one by one
info.apply(ctx, [1, 2]); // pass arguments as an array
const bound = info.bind(ctx, 1); // bind this + first argument
bound(2);
// reuse array methods on array-like values:
Array.prototype.slice.call(arguments)

Callback functions

A callback is a function passed to an async operation. Modern code prefers Promise/async. Nested callbacks become callback hell.

1
2
3
4
5
6
7
8
9
function load(cb) {
setTimeout(() => cb('data'), 1000);
}
load(result => console.log(result));
// callback hell:
load(a => load(b => load(c => {
// nested too deep, hard to maintain
})));
// rewriting with Promise is clearer

IIFE

An IIFE (function(){})() runs immediately and isolates its scope. Modern code uses block scope {} or modules instead.

1
2
3
4
5
6
7
8
9
(function () {
const secret = 'private'; // not accessible outside
})();
// modern alternative:
{
const scoped = 'block scope';
}
// arrow IIFE:
(() => console.log('run once'))();

Recursion

A function calling itself needs a base case or the stack overflows. Tail-call optimization is not fully implemented in most JS engines.

1
2
3
4
5
6
function factorial(n) {
if (n <= 1) return 1; // base case
return n * factorial(n - 1);
}
factorial(5); // 120
// deep recursion can hit RangeError: Maximum call stack

7.String Handling

String methods, template literals, regex matching, splitting, and character handling.

Common methods

Common methods: length, slice/substring for substrings, indexOf to find, includes to test, replace to substitute, split to break apart, trim to strip whitespace.

1
2
3
4
5
6
7
8
const s = ' Hello, World ';
s.trim(); // 'Hello, World'
s.indexOf('World'); // 8
s.includes('lo'); // true
s.slice(0, 5); // ' Hell'
s.replace('World', 'JS'); // ' Hello, JS '
s.split(','); // [' Hello', ' World ']
'5'.padStart(3, '0'); // '005'

Case and encoding

toUpperCase/toLowerCase change case. charCodeAt reads a code unit; fromCharCode converts back. localeCompare compares in collation order.

1
2
3
4
5
6
'hello'.toUpperCase(); // 'HELLO'
'HELLO'.toLowerCase(); // 'hello'
'A'.charCodeAt(0); // 65
String.fromCharCode(65); // 'A'
'a'.localeCompare('b'); // -1 (lexicographic order)
// for CJK characters use code points: '中'.codePointAt(0)

Unicode handling

Strings are sequences of UTF-16 code units; supplementary-plane characters like emoji take 2 units. Iterate with for...of or Array.from.

1
2
3
4
5
6
const emoji = '😀';
emoji.length; // 2 (code units)
[...emoji].length; // 1 (full characters)
for (const ch of 'ab😀') { console.log(ch); }
// reverse a string (handles emoji correctly):
const rev = [...'abc😀'].reverse().join('');

Template literal composition

Template literal ${} interpolation and multi-line strings are the most recommended approach. Expressions and method calls can be nested inside.

1
2
3
4
5
6
7
const user = { name: 'Nick', age: 30 };
const msg = `${user.name} 今年 ${user.age} 岁`;
const html = `<div>
<p>${user.name}</p>
</div>`; // multi-line
// conditional interpolation:
const tag = `${user.age >= 18 ? '成年' : '未成年'}`;

Regex basics

Use regex literals /pattern/ or new RegExp. test checks a match, exec returns one. g is global, i is case-insensitive.

1
2
3
4
5
6
7
const re = /\d{3}-\d{4}/;
re.test('123-4567'); // true
re.exec('call 123-4567'); // match result
const m = 'abc123'.match(/(\d+)/);
m[0]; // '123'
m[1]; // '123' (group)
// iterate all matches: 'a1b2'.matchAll(/\d/g)

Regex replacement

replace with a regex supports $1 group references. A replacer callback handles complex logic. replaceAll replaces every literal occurrence.

1
2
3
4
5
6
'2024-01-01'.replace(/(\d{4})-(\d{2})-(\d{2})/, '$1/$2/$3');
// mask sensitive digits:
'13812345678'.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2');
// replacer callback:
'abc'.replace(/\w/g, ch => ch.toUpperCase()); // 'ABC'
// replace all occurrences: 'a-b-c'.replaceAll('-', '+')

Split and join

split breaks a string by a delimiter into an array; join merges an array back into a string. split('') splits into characters. Be careful with empty delimiters.

1
2
3
4
5
6
7
'a,b,c'.split(','); // ['a','b','c']
['a','b','c'].join('-'); // 'a-b-c'
'hello'.split(''); // ['h','e','l','l','o']
// limit the count:
'a,b,c'.split(',', 2); // ['a','b']
// split on whitespace:
' a b '.trim().split(/\s+/); // ['a','b']

Number to string

toString/templates convert to string, toFixed fixes decimals, toLocaleString adds grouping separators, padStart pads with leading characters.

1
2
3
4
5
6
(255).toString(16); // 'ff'
(3.14159).toFixed(2); // '3.14'
(1234567).toLocaleString('en-US'); // '1,234,567'
(42).toString().padStart(5, '0'); // '00042'
// safe float handling:
(0.1 + 0.2).toFixed(1); // '0.3'

8.Arrays and Objects

Array higher-order methods, Map/Set, object operations, and destructuring.

Array higher-order methods

map projects, filter selects, reduce folds, find locates, some/every test. These methods do not mutate the original array.

1
2
3
4
5
6
7
const nums = [1, 2, 3, 4];
nums.map(n => n * 2); // [2, 4, 6, 8]
nums.filter(n => n % 2 === 0); // [2, 4]
nums.reduce((a, b) => a + b, 0); // 10
nums.find(n => n > 2); // 3
nums.some(n => n > 3); // true
nums.every(n => n > 0); // true

Map

Map keys any value: get/set/has/delete/size. Keys can be objects. Iteration follows insertion order.

1
2
3
4
5
6
7
8
9
const m = new Map();
m.set('name', 'Nick');
m.set(obj, 'value'); // object as key
m.get('name'); // 'Nick'
m.has('name'); // true
m.delete('name');
m.size; // 1
for (const [k, v] of m) { console.log(k, v); }
// initialize: new Map([['a',1],['b',2]])

Set

Set is a deduplicating collection: add/has/delete/size. Preferred for array deduplication and membership tests.

1
2
3
4
5
6
7
8
const set = new Set([1, 2, 2, 3]);
set.size; // 3 (auto-deduplicated)
set.has(2); // true
set.add(4);
set.delete(1);
// deduplicate an array:
const uniq = [...new Set(arr)];
// iterate: for (const v of set) / set.forEach

Object operations

Object.keys/values/entries iterate. Object.assign merges. Object.freeze locks. The in operator tests for a property.

1
2
3
4
5
6
7
const obj = { a: 1, b: 2 };
Object.keys(obj); // ['a', 'b']
Object.values(obj); // [1, 2]
Object.entries(obj); // [['a',1],['b',2]]
Object.assign({}, obj, { c: 3 }); // merge
'a' in obj; // true
Object.freeze(obj); // make immutable

reduce

reduce folds an array to a single value: sum, group, flatten. The initial value is optional but recommended.

1
2
3
4
5
6
7
8
9
const nums = [1, 2, 3, 4];
const sum = nums.reduce((acc, n) => acc + n, 0);
// group by:
const byType = items.reduce((acc, it) => {
(acc[it.type] ||= []).push(it);
return acc;
}, {});
// flatten:
[[1], [2, 3]].reduce((a, b) => a.concat(b), []);

Sorting

sort uses string ordering by default (a trap). Numeric sort needs a comparator. sort mutates the array in place.

1
2
3
4
5
6
[3, 10, 1].sort(); // [1, 10, 3] (string order!)
[3, 10, 1].sort((a, b) => a - b); // [1, 3, 10] ascending
[3, 10, 1].sort((a, b) => b - a); // descending
// sort objects by field:
people.sort((a, b) => a.age - b.age);
// copy-then-sort: [...arr].sort(...) leaves the original untouched

Spread and merge

The spread operator ... merges arrays/objects, copies, and spreads arguments. Rest collects and spread expands — they are mirrors.

1
2
3
4
5
6
const a = [1, 2], b = [3, 4];
const merged = [...a, ...b]; // [1,2,3,4]
const copy = [...a]; // shallow copy
const obj = { ...o1, ...o2 }; // object merge
const max = Math.max(...a); // spread arguments
function f(...rest) { } // collect arguments

Method chaining

map/filter return new arrays that can be chained. Watch for intermediate array allocations on long chains.

1
2
3
4
5
6
7
const result = people
.filter(p => p.age >= 18)
.map(p => p.name)
.sort()
.join(', ');
// avoid: repeated heavy array operations in a chain
// performance: use a plain for loop for very large datasets

Holes and sparse arrays

Array holes differ from undefined. map skips holes. Build dense arrays with Array.from or fill.

1
2
3
4
5
6
const holes = [1, , 3]; // hole in the middle
holes.map(n => n * 2); // [2, <empty>, 6]
// create a dense array:
Array.from({ length: 5 }, (_, i) => i); // [0..4]
new Array(5).fill(0); // dense array of zeros
// check: 0 in holes is false

9.Memory and Performance

Closures and memory leaks, WeakMap/WeakSet, large-data processing, and optimization.

Closure memory

Closures hold references to outer variables even when they are no longer needed. Long-lived references prevent GC.

1
2
3
4
5
6
7
function heavy() {
const big = new Array(1000000);
return function () { return big.length; };
}
const keep = heavy(); // big is held by the closure
// set keep to null when no longer needed:
// keep = null; otherwise big cannot be collected

Event listener leaks

DOM elements are removed but listeners still reference them, or listeners hold external objects, causing leaks. Detach listeners or use AbortController before removing elements.

1
2
3
4
5
6
7
8
9
const btn = document.getElementById('btn');
const handler = () => { /* ... */ };
btn.addEventListener('click', handler);
// detach before removing:
// btn.removeEventListener('click', handler);
// or btn.onclick = null;
// use AbortController for bulk detach:
// const ac = new AbortController();
// el.addEventListener('click', fn, { signal: ac.signal })

WeakMap / WeakSet

WeakMap keys are weak: when an object has no other references, its entry is collected. Great for associations, caches, and private fields.

1
2
3
4
5
6
7
8
const wm = new WeakMap();
let obj = {};
wm.set(obj, 'private data'); // associated without preventing collection
obj = null; // once the key is collected, the entry vanishes automatically
// WeakMap is not iterable and has no size
// implementing private fields:
// const priv = new WeakMap();
// class A { m() { return priv.get(this); } }

Performance optimization

Avoid unnecessary copies and recomputation. Cache length in a local, batch DOM operations, debounce/throttle high-frequency events.

1
2
3
4
5
6
7
8
9
10
11
// cache length:
for (let i = 0, len = arr.length; i < len; i++) { }
// debounce (high-frequency triggers run only the last call):
function debounce(fn, ms) {
let t;
return (...args) => {
clearTimeout(t);
t = setTimeout(() => fn(...args), ms);
};
}
const onInput = debounce(save, 300);

Stack overflow

Infinite or very deep recursion causes RangeError: Maximum call stack. Switch to loops or cap the depth.

1
2
3
4
5
6
7
8
9
10
// will stack overflow:
// function loop() { loop(); }
// loop(); // RangeError
// deep tree traversal with an explicit stack:
const stack = [root];
while (stack.length) {
const node = stack.pop();
stack.push(...node.children);
}
// or use tail recursion / generators

Large-data processing

Big arrays plus reduce/map allocate everything at once. Process in batches, use generators for laziness, and TypedArray for binary efficiency.

1
2
3
4
5
6
7
8
9
// lazy generator processing:
function* range(n) {
for (let i = 0; i < n; i++) yield i;
}
for (const x of range(1e7)) { /* fetch on demand */ }
// binary data:
const buf = new Uint8Array(1024);
// streaming large files:
// for await (const chunk of stream) { }

GC and globals

Global caches never release. Avoid unbounded caches; clean them up. Modern V8's mark-and-sweep handles the rest.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// a global cache lives forever:
globalThis.cache = {}; // never collected
// bounded cache:
function lruCache(limit) {
const map = new Map();
return {
get(k) { return map.get(k); },
set(k, v) {
map.set(k, v);
if (map.size > limit) {
map.delete(map.keys().next().value);
}
},
};
}

Memoization

Cache pure function results in a Map so identical inputs return the cached value, avoiding repeated expensive work. Watch the cache footprint.

1
2
3
4
5
6
7
8
9
10
11
12
function memoize(fn) {
const cache = new Map();
return (...args) => {
const key = JSON.stringify(args);
if (cache.has(key)) return cache.get(key);
const value = fn(...args);
cache.set(key, value);
return value;
};
}
const fib = memoize(n => (n <= 1 ? n : fib(n - 1) + fib(n - 2)));
fib(40); // instant (cached subproblems)

10.Object-Oriented

class syntax, inheritance, encapsulation, static members, and the prototype chain.

class definition

class defines an object template with a constructor, methods, and properties. new creates instances.

1
2
3
4
5
6
7
8
9
10
11
class Person {
constructor(name, age) {
this.name = name; // instance property
this.age = age;
}
greet() { // instance method
return 'Hi, ' + this.name;
}
}
const p = new Person('Nick', 30);
p.greet(); // 'Hi, Nick'

Inheritance with extends

extends inherits; super() calls the parent constructor; super.method() calls a parent method.

1
2
3
4
5
6
7
8
9
10
11
class Animal {
constructor(name) { this.name = name; }
speak() { return '...'; }
}
class Dog extends Animal {
constructor(name) { super(name); }
speak() { return 'Woof'; } // override
}
const d = new Dog('Rex');
d.speak(); // 'Woof'
d.name; // 'Rex' (inherited property)

getter / setter

get/set define accessor properties that run logic on read/write, including validation. They are accessed like properties, not methods.

1
2
3
4
5
6
7
8
9
10
11
class Account {
constructor() { this._balance = 0; }
get balance() { return this._balance; }
set balance(v) {
if (v < 0) throw new Error('负余额');
this._balance = v;
}
}
const a = new Account();
a.balance = 100; // triggers the setter
console.log(a.balance); // 100 (triggers the getter)

Static members

static members belong to the class, not instances: static methods for utilities, static fields for constants. static {} is a static block for initialization.

1
2
3
4
5
6
7
8
class MathUtils {
static PI = 3.14; // static property
static add(a, b) { return a + b; } // static method
static { console.log('init'); } // static block
}
MathUtils.add(1, 2); // called on the class
MathUtils.PI; // 3.14
// inside a static method, this refers to the class

Private fields

A # prefix defines truly private fields/methods; access outside the class throws. This is real encapsulation, not an underscore convention.

1
2
3
4
5
6
7
8
9
10
class Counter {
#count = 0; // private field
#increment() { // private method
this.#count++;
}
up() { this.#increment(); return this.#count; }
}
const c = new Counter();
c.up(); // 1
// c.#count // throws: private field

instanceof check

instanceof tests whether an object is on the class's prototype chain. Object.getPrototypeOf inspects the prototype.

1
2
3
4
5
6
7
8
class Dog extends Animal { }
const d = new Dog();
d instanceof Dog; // true
d instanceof Animal; // true (parent chain)
[] instanceof Array; // true
// prototype chain:
Object.getPrototypeOf(d) === Dog.prototype; // true
// across iframes, array instanceof Array fails; use Array.isArray

Prototype chain

Objects share methods through the prototype chain. class is syntactic sugar for prototype inheritance. Mutating prototype affects every instance.

1
2
3
4
5
6
7
8
function Animal(name) { this.name = name; }
Animal.prototype.speak = function () {
return this.name + ' speaks';
};
const a = new Animal('cat');
a.speak();
// class methods actually live on the prototype:
Person.prototype.greet === p.greet; // true (shared method)

Composition over inheritance

Deep inheritance hierarchies are hard to maintain. Prefer composition: inject dependencies via the constructor or mix in features.

1
2
3
4
5
6
7
8
9
10
11
class Logger {
constructor(logger) { this.logger = logger; }
save() { this.logger.log('saved'); }
}
// mixin:
const withLogging = (cls) => class extends cls {
log(msg) { console.log(msg); }
};
class User { }
const LoggedUser = withLogging(User);
// composition: inject dependencies in the constructor for easier testing

11.Error Handling

try/catch/finally, error types, throwing, and async error handling.

try / catch / finally

try holds risky code, catch handles exceptions, finally runs regardless. Exceptions propagate up the call stack.

1
2
3
4
5
6
7
8
try {
const data = JSON.parse(json);
} catch (e) {
console.error('parse failed:', e.message);
} finally {
console.log('cleanup'); // always runs
}
// omit the binding in catch: catch { ... }

Error types

Error is the base, with subclasses TypeError, ReferenceError, RangeError, SyntaxError. Distinguish with instanceof.

1
2
3
4
5
6
7
8
9
try {
null.prop; // TypeError
undefinedVar; // ReferenceError
} catch (e) {
if (e instanceof TypeError) { }
if (e instanceof ReferenceError) { }
}
// user-defined errors are typically subclasses of Error
// async errors do not enter a synchronous try/catch

Throwing errors

throw new Error('msg') raises an exception. Anything can be thrown, but the convention is to throw an Error (which carries a stack).

1
2
3
4
5
6
7
8
9
10
11
function divide(a, b) {
if (b === 0) {
throw new Error('除数不能为 0');
}
return a / b;
}
try {
divide(1, 0);
} catch (e) {
console.log(e.message); // '除数不能为 0'
}

Custom errors

extends Error defines a domain error. Set a custom name for recognition, and keep message and stack.

1
2
3
4
5
6
7
8
9
10
11
12
13
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = 'ValidationError';
}
}
try {
throw new ValidationError('邮箱格式错误');
} catch (e) {
if (e instanceof ValidationError) {
// domain error branch
}
}

Async errors

Promise/async errors flow through .catch or try/catch (around await). A synchronous try/catch cannot catch an async throw.

1
2
3
4
5
6
7
8
9
10
11
12
13
async function load() {
try {
const res = await fetch(url); // async
return await res.json();
} catch (e) {
console.error('load failed', e);
return null;
}
}
// or Promise style:
fetch(url)
.then(r => r.json())
.catch(e => console.error(e));

Unhandled errors

A Promise without .catch causes unhandledrejection. Listen globally for uncaughtException/unhandledrejection at the top level.

1
2
3
4
5
6
7
8
9
10
// Node globals:
process.on('uncaughtException', e => {
console.error('uncaught', e);
});
process.on('unhandledRejection', reason => {
console.error('unhandled rejection', reason);
});
// browser:
window.addEventListener('unhandledrejection', e => { });
// in normal development: try/catch every async or chain .catch

Error isolation

A single failure in batch processing shouldn't kill the whole batch. Wrap each item in try/catch and log before continuing.

1
2
3
4
5
6
7
8
9
10
11
const results = [];
for (const item of items) {
try {
results.push(process(item));
} catch (e) {
console.error('skip item:', item, e);
results.push(null); // mark failure and continue
}
}
// never swallow errors with a bare catch: at least log them
// for Promise batches, use Promise.allSettled to isolate failures

Error cause

ES2022 added the Error cause option, preserving the underlying cause and stack when wrapping errors for layered diagnosis.

1
2
3
4
5
6
7
8
9
10
11
12
13
function parse(data) {
try {
return JSON.parse(data);
} catch (e) {
throw new Error('解析失败', { cause: e });
}
}
try {
parse('not-json');
} catch (e) {
console.log(e.message); // '解析失败'
console.log(e.cause); // original parse error
}

12.Files and I/O

Node filesystem, console input, JSON read/write, and browser DOM I/O.

fs read file

Node's fs/promises reads files: readFile reads the whole file, readFileSync is the blocking version. Prefer the Promise version.

1
2
3
4
5
6
7
import { readFile, writeFile } from 'node:fs/promises';
const data = await readFile('in.txt', 'utf8');
await writeFile('out.txt', data);
// synchronous version (for simple scripts):
import fs from 'node:fs';
const s = fs.readFileSync('in.txt', 'utf8');
// line by line: data.split('\n')

fs write and append

writeFile overwrites, appendFile appends, mkdir creates directories, access tests existence. rename/cp/rm do file operations.

1
2
3
4
5
6
7
import { writeFile, appendFile, mkdir } from 'node:fs/promises';
await writeFile('a.txt', 'content');
await appendFile('a.txt', '\nmore');
await mkdir('data', { recursive: true });
// check existence:
import { access } from 'node:fs/promises';
try { await access('a.txt'); } catch { /* not found */ }

JSON file read/write

Read JSON files with parse, write with stringify. JSON is common for configuration and data files.

1
2
3
4
5
6
7
import { readFile, writeFile } from 'node:fs/promises';
const data = JSON.parse(await readFile('config.json', 'utf8'));
data.updated = true;
await writeFile('config.json', JSON.stringify(data, null, 2));
// resilient parsing:
try { JSON.parse(s) } catch { /* handle malformed JSON */ }
// pretty: JSON.stringify(obj, null, 2) pretty-prints with indentation

Standard input/output

process.stdin reads input as a stream, readline handles line-by-line interaction, process.stdout.write writes to stdout.

1
2
3
4
5
6
7
8
9
10
11
import readline from 'node:readline';
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
rl.question('你的名字?', name => {
console.log('你好', name);
rl.close();
});
// process piped input line by line:
// for await (const line of rl) { }

fetch network I/O

fetch reads remote resources. Use async/await with res.json() / res.text() to parse the body. Built into Node 18+.

1
2
3
4
5
6
7
8
9
const res = await fetch('https://api.example.com/data');
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
// send a request:
await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'Nick' }),
});

Browser DOM I/O

The DOM reads/writes page content: getElementById to locate, textContent for text, innerHTML for markup. Browser-only.

1
2
3
4
5
6
7
const el = document.getElementById('output');
el.textContent = 'Hello'; // safe text
// el.innerHTML = '<p>Hi</p>'; // HTML (watch out for XSS)
const input = document.querySelector('input');
const val = input.value; // read input
// listen for events:
btn.addEventListener('click', () => { });

Streaming I/O

Use streams for large files to avoid loading everything into memory. createReadStream/createWriteStream, pipe, or for await.

1
2
3
4
5
6
7
8
9
import { createReadStream } from 'node:fs';
const stream = createReadStream('big.log', 'utf8');
for await (const chunk of stream) {
processChunk(chunk); // process chunk by chunk
}
// piping:
// createReadStream('a').pipe(createWriteStream('b'))
// browser response stream:
// for await (const c of res.body)

path utilities

node:path handles file paths: join builds paths, resolve makes them absolute, extname gets the extension, basename gets the filename.

1
2
3
4
5
6
7
import path from 'node:path';
path.join('data', 'sub', 'a.txt'); // 'data/sub/a.txt' (cross-platform)
path.resolve('data'); // absolute path
path.extname('a/b.json'); // '.json'
path.basename('/a/b/file.txt'); // 'file.txt'
path.parse('/a/b/file.txt'); // object of segments
// relative: path.relative('/a', '/a/b') // 'b'

13.Common Pitfalls (FAQ)

The pitfalls JS developers hit most: == vs ===, this, async, copying, and truthiness.

== vs ===

== does implicit coercion (bug-prone), === is strict equality. Always use ===, except for the null == undefined idiom.

1
2
3
4
5
6
7
8
9
// BAD: implicit coercion surprises
1 == '1'; // true
null == 0; // false
[] == false; // true (weird)
// GOOD: strict equality
1 === '1'; // false
// null check: value === null || value === undefined
// or value == null (equivalent)

Lost this

this is lost when a method is assigned to a variable or passed as a callback. Use arrow functions for inheritance or bind explicitly.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// BAD: this is lost inside the callback
class A {
init() {
btn.onclick = function () {
this.doSomething(); // this is the button!
};
}
}
// GOOD: arrow function
class B {
init() {
btn.onclick = () => this.doSomething(); // this refers to the instance
}
}

Floating-point precision

0.1 + 0.2 !== 0.3 (binary representation is inexact). Compare with a tolerance; store money as integer cents or a library.

1
2
3
4
5
6
7
// BAD: direct float equality
0.1 + 0.2 === 0.3; // false
// GOOD: compare with a tolerance
Math.abs((0.1 + 0.2) - 0.3) < 1e-9; // true
// for money: store as integer cents (1990) instead of 19.9
// or display with toFixed: (0.1+0.2).toFixed(2) === '0.30'

Async execution order

setTimeout and network requests are asynchronous. Writing code in source order does not guarantee execution order. Use await/Promise.

1
2
3
4
5
6
7
8
9
// BAD: assuming synchronous execution
let data;
fetch(url).then(r => data = r);
console.log(data); // undefined!
// GOOD: await it
const res = await fetch(url);
console.log(res); // data is ready
// or use a .then chain / async function

Shallow vs deep copy

Spread only copies one level; nested objects still share. Mutating nested values affects the source.

1
2
3
4
5
6
7
8
// BAD: spread assumes a deep copy
const b = { ...a };
b.nested.x = 1; // a.nested.x is also changed
// GOOD: deep copy
const c = structuredClone(a);
c.nested.x = 1; // a is unaffected
// in environments without structuredClone, use JSON or a hand-written recursion

Truthy checks

Empty arrays/objects are truthy. Check array length, and use Object.keys(obj).length for empty objects.

1
2
3
4
5
6
7
// BAD: if (arr) is always truthy
if ([]) { console.log('runs'); } // empty array is truthy
// GOOD: check the length
if (arr.length > 0) { }
if (Object.keys(obj).length === 0) { /* empty object */ }
// strings: if (str) safely tests for a non-empty string

var scope leaks

var has no block scope, so declarations in loops/if leak to function scope. Use let for loop counters.

1
2
3
4
5
6
7
8
9
// BAD: var leaks + closure captures the stale value
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i)); // all print 3
}
// GOOD: let has block scope
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i)); // 0, 1, 2
}

parseInt pitfalls

parseInt('08') used to be octal on old engines; parseInt('3px') returns 3. Use Number or + for strict parsing.

1
2
3
4
5
6
7
8
9
// BAD: parseInt is lenient
parseInt('42px'); // 42 (may be unexpected)
// legacy engines, octal:
// parseInt('010') may be 8
// GOOD: explicit radix + strict
parseInt('010', 10); // 10
Number('42px'); // NaN (strict)
+'42'; // 42

Nested ternaries

Nested ternaries hurt readability. Use if/else or a lookup map for complex branches.

1
2
3
4
5
6
7
8
9
10
// BAD: nested ternaries are hard to read
const v = a ? b ? 'x' : 'y' : 'z';
// GOOD: if/else or a lookup map
let v;
if (a) v = b ? 'x' : 'y';
else v = 'z';
// or a lookup table:
const map = { x: 'X', y: 'Y' };
const result = map[key] ?? 'default';

Mutating object props

Destructuring is a shallow copy; nested properties remain references. Deep-copy first if you need to mutate safely.

1
2
3
4
5
6
7
8
// BAD: mutating nested values after destructuring
const { nested } = obj;
nested.x = 99; // obj.nested.x also changes
// GOOD: deep clone before mutating
const copy = structuredClone(obj);
copy.nested.x = 99; // original object is untouched
// or avoid sharing: fully destructure the value before copying

14.Async and Event Loop

Event loop, Promise, async/await, timers, and concurrency.

Event loop

JS is single-threaded with an event loop. Synchronous code runs first; async callbacks (timers/I/O) queue up to run later.

1
2
3
4
5
console.log('1'); // synchronous
setTimeout(() => console.log('2')); // macrotask
Promise.resolve().then(() => console.log('3')); // microtask
// output order: 1, 3, 2
// microtasks run before macrotasks; IO callbacks are queued once IO finishes

Promise

Promise represents an async result. then handles success, catch handles failure, finally wraps up. Chain to avoid callback hell.

1
2
3
4
5
6
7
8
const p = new Promise((resolve, reject) => {
setTimeout(() => resolve('data'), 1000);
});
p.then(data => console.log(data))
.catch(err => console.error(err))
.finally(() => console.log('done'));
// resolve for success, reject for failure
// then returns a new Promise so you can keep chaining

async / await

async functions return a Promise; await suspends until the result is ready, letting you write async code like sync. try/catch around await catches errors.

1
2
3
4
5
6
7
8
9
10
11
12
async function load() {
try {
const res = await fetch(url); // wait
return await res.json();
} catch (e) {
console.error(e);
return null;
}
}
const data = await load();
// await is only allowed inside async functions or top-level modules
// async functions always return a Promise

Timers

setTimeout fires once after a delay, setInterval repeats; clearTimeout/clearInterval cancels. Delay is in milliseconds.

1
2
3
4
5
6
7
const t = setTimeout(() => console.log('once'), 1000);
clearTimeout(t); // cancel
const id = setInterval(() => tick(), 500);
clearInterval(id); // stop
// a delay of 0 is still asynchronous:
setTimeout(() => console.log('async'), 0);
// Node: setImmediate is a macrotask, process.nextTick is a microtask

Promise combinators

Promise.all resolves when all succeed; allSettled waits for all to finish and reports each status; race resolves with the first to settle.

1
2
3
4
5
6
7
8
const p1 = fetch('/a');
const p2 = fetch('/b');
// wait for all in parallel (any rejection rejects the whole call):
const [a, b] = await Promise.all([p1, p2]);
// do not bail on a single failure:
const results = await Promise.allSettled([p1, p2]);
// results[i].status === 'fulfilled' | 'rejected'
// race: Promise.race([p, timeout])

Web Worker

Workers run expensive code on a separate thread so the UI stays responsive, communicating via postMessage. Node uses worker_threads.

1
2
3
4
5
6
7
8
9
10
11
// main thread:
const w = new Worker('worker.js');
w.postMessage('start');
w.onmessage = e => console.log('result', e.data);
// worker.js:
onmessage = e => {
const result = heavyCompute(e.data);
postMessage(result);
};
// Node:
// import { Worker } from 'node:worker_threads'

Concurrency limits

Limit concurrency for batch async tasks to avoid exhausting resources. Use batches or a semaphore.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
async function mapLimit(items, limit, fn) {
const results = [];
const running = [];
for (const item of items) {
const p = fn(item).then(r => {
results.push(r);
running.splice(running.indexOf(p), 1);
});
running.push(p);
if (running.length >= limit) {
await Promise.race(running);
}
}
await Promise.all(running);
return results;
}
// batch manually: for (let i = 0; i < items.length; i += limit)

Microtasks vs macrotasks

Microtasks (Promise.then, queueMicrotask) take priority over macrotasks (timers). Microtasks are drained within each turn.

1
2
3
4
5
queueMicrotask(() => console.log('micro'));
setTimeout(() => console.log('macro'), 0);
// output: micro, macro
// order: sync code -> drain microtasks -> next macrotask
// after every macrotask, the microtask queue is drained

15.Network and HTTP

fetch requests, HTTP methods, error handling, JSON, WebSocket, and URLs.

fetch basics

fetch sends HTTP requests and returns a Promise. res.ok checks success; res.json()/text() reads the body. Built into browsers and Node 18+.

1
2
3
4
5
6
const res = await fetch('https://api.example.com/users');
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const users = await res.json();
// or as text: await res.text()
// status code: res.status (200/404...)
// header: res.headers.get('content-type')

POST and request bodies

The second argument to fetch configures method, headers, and body. JSON request bodies need JSON.stringify.

1
2
3
4
5
6
7
8
9
10
const res = await fetch('/api/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer token',
},
body: JSON.stringify({ name: 'Nick', age: 30 }),
});
if (!res.ok) throw new Error('create failed');
const created = await res.json();

Query parameters

URLSearchParams builds query strings. encodeURIComponent encodes special characters. URL parses URLs.

1
2
3
4
5
6
7
8
9
10
11
12
const params = new URLSearchParams({
q: 'cheatsheet', page: '2',
});
const url = `/search?${params}`; // q=cheatsheet&page=2
// append:
params.append('sort', 'asc');
params.get('q'); // 'cheatsheet'
// encode a single value:
const q = encodeURIComponent('a&b');
// parse a URL:
const u = new URL('https://a.com/x?y=1');
u.searchParams.get('y'); // '1'

Network error handling

fetch only rejects on network failure; HTTP 4xx/5xx don't throw — check res.ok. Use AbortController for timeouts.

1
2
3
4
5
6
7
8
9
10
11
12
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), 10000);
try {
const res = await fetch(url, { signal: controller.signal });
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
} catch (e) {
if (e.name === 'AbortError') console.error('timeout');
else console.error('network error', e);
} finally {
clearTimeout(timer);
}

WebSocket

WebSocket is a full-duplex long-lived connection with onopen/onmessage/onclose events and send() for outgoing messages. Great for realtime push.

1
2
3
4
5
6
7
const ws = new WebSocket('wss://example.com/ws');
ws.onopen = () => ws.send('hello');
ws.onmessage = e => console.log('received', e.data);
ws.onclose = () => console.log('connection closed');
ws.onerror = e => console.error('error', e);
// close: ws.close()
// keepalive: periodic ping to detect a dead connection

axios vs fetch

axios offers interceptors, automatic JSON, and richer error objects. fetch is native; axios is a third-party library.

1
2
3
4
5
6
7
8
9
10
11
import axios from 'axios';
// GET:
const { data } = await axios.get('/api/users');
// POST:
await axios.post('/api/users', { name: 'Nick' });
// interceptors:
axios.interceptors.request.use(cfg => {
cfg.headers.Authorization = 'Bearer ' + token;
return cfg;
});
// error info: err.response.status / err.response.data

HTTP status codes

2xx success, 3xx redirect, 4xx client error, 5xx server error. Common codes: 200/201/400/401/404/500.

1
2
3
4
5
6
7
8
9
10
11
const map = {
200: 'OK success',
201: 'Created resource created',
204: 'No Content no content',
400: 'Bad Request invalid parameters',
401: 'Unauthorized not authenticated',
403: 'Forbidden no permission',
404: 'Not Found not found',
500: 'Internal Server Error server error',
};
console.log(map[res.status] ?? 'other');

File upload with FormData

FormData builds a multipart form for fetch POST uploads with files and fields. XMLHttpRequest is easier for upload progress.

1
2
3
4
5
6
7
8
9
10
11
const file = document.querySelector('input[type=file]').files[0];
const form = new FormData();
form.append('file', file);
form.append('desc', 'sample file');
await fetch('/api/upload', {
method: 'POST',
body: form, // multipart is set automatically; do not set Content-Type manually
});
// progress:
// xhr.upload.onprogress = e => pct = e.loaded / e.total
// multiple files: for (const f of files) form.append('file', f)

16.Date and Time

Date, timestamps, formatting, timezones, and timers.

Date basics

new Date() is now. getFullYear, getMonth (0-based!), getDate read parts. Month is zero-indexed.

1
2
3
4
5
6
7
const now = new Date();
now.getFullYear(); // 2024
now.getMonth(); // 0-11 (January is 0!)
now.getDate(); // day of the month
now.getDay(); // weekday 0-6 (Sunday is 0)
now.getHours();
now.toISOString(); // UTC ISO string

Timestamps

Date.now() is a millisecond timestamp (UTC epoch). getTime() is the same. Store cross-zone times as a timestamp or ISO string.

1
2
3
4
5
6
7
Date.now(); // millisecond timestamp
new Date().getTime(); // same as above
new Date(1700000000000); // build from milliseconds
// seconds: Math.floor(Date.now() / 1000)
// parse a string:
new Date('2024-01-01T12:00:00Z');
// keep the parse format consistent to avoid ambiguity (e.g. '01/02')

Formatting output

toLocaleDateString/toLocaleString produce localized output. Intl.DateTimeFormat gives fine control.

1
2
3
4
5
6
7
8
9
const d = new Date();
d.toISOString(); // '2024-01-01T04:00:00.000Z'
d.toLocaleDateString('zh-CN'); // '2024/1/1'
d.toLocaleString('zh-CN', {
year: 'numeric', month: '2-digit', day: '2-digit',
});
// build yyyy-MM-dd manually:
const pad = n => String(n).padStart(2, '0');
const s = `${d.getFullYear()}-${pad(d.getMonth()+1)}-${pad(d.getDate())}`;

Timezones

getTime is a UTC instant; local methods like getHours display in the runtime timezone. Store as ISO/timestamp.

1
2
3
4
5
6
7
8
9
const d = new Date();
d.getTimezoneOffset(); // offset between local and UTC, in minutes
// UTC methods:
d.getUTCHours();
// format in a specific timezone:
new Intl.DateTimeFormat('zh-CN', {
timeZone: 'Asia/Shanghai',
}).format(d);
// store UTC on the server; render in the user's local timezone on the client

Durations

Subtracting two Dates yields a millisecond delta. Convert to seconds/minutes/hours. Use performance.now for precise measurement.

1
2
3
4
5
6
7
8
9
10
const start = Date.now();
doWork();
const ms = Date.now() - start;
// convert:
const secs = Math.floor(ms / 1000);
const mins = Math.floor(ms / 60000);
// high-precision timing (sub-millisecond):
const t0 = performance.now();
doWork();
const precise = performance.now() - t0;

Date arithmetic

Add days/months with setDate/setMonth or millisecond arithmetic. Watch for 0-based months and month boundaries.

1
2
3
4
5
6
7
8
9
const d = new Date();
d.setDate(d.getDate() + 7); // add 7 days
// add months:
d.setMonth(d.getMonth() + 1);
// add hours:
const later = new Date(Date.now() + 3 * 3600_000);
// clear the time portion:
d.setHours(0, 0, 0, 0); // midnight today
// note: for whole-day arithmetic across DST, setDate is safer

Date parsing

new Date(string) parses a date string. ISO format is most reliable; other formats vary in compatibility.

1
2
3
4
5
6
7
8
9
new Date('2024-01-01'); // local timezone midnight
new Date('2024-01-01T12:00:00Z'); // UTC, explicit
new Date('2024/01/01'); // supported by browsers, do not rely on it
// validate the parse:
const d = new Date(str);
if (isNaN(d.getTime())) { /* invalid date */ }
// parse manually for predictability:
const [y, m, day] = '2024-01-01'.split('-').map(Number);
new Date(y, m - 1, day); // month is 0-based

Intl formatting

Intl.NumberFormat for numbers/currency, Intl.DateTimeFormat for dates. Localize output by region.

1
2
3
4
5
6
7
8
9
10
const n = 1234567.89;
new Intl.NumberFormat('zh-CN').format(n); // '1,234,567.89'
new Intl.NumberFormat('zh-CN', {
style: 'currency', currency: 'CNY',
}).format(n); // '¥1,234,567.89'
new Intl.DateTimeFormat('zh-CN', {
dateStyle: 'medium', timeStyle: 'short',
}).format(new Date());
// relative time:
// new Intl.RelativeTimeFormat('zh-CN').format(-2, 'day')

17.Node Process

process env vars, arguments, exit codes, cwd, and process info.

Environment variables

process.env reads environment variables in Node. process.env.NODE_ENV is common. Use .env files for cross-platform config.

1
2
3
4
5
6
7
8
process.env.NODE_ENV; // 'production' | 'development'
process.env.PORT;
// set:
process.env.MY_VAR = 'value';
// use dotenv:
// import 'dotenv/config';
// reads variables from a .env file
// do not hard-code secrets in production config

Command-line arguments

process.argv holds raw arguments. Parse options with commander/yargs, or hand-write for simple cases.

1
2
3
4
5
6
7
8
9
10
11
// $ node app.js --name Nick
const args = process.argv.slice(2);
// use commander:
import { Command } from 'commander';
const program = new Command();
program
.option('-n, --name <name>', 'name')
.parse(process.argv);
console.log(program.opts().name);
// validate missing args:
if (!args.includes('--name')) process.exit(1);

Exit codes

process.exit(code) exits with a code (0 success, non-zero failure). Uncaught exceptions default to exit code 1.

1
2
3
4
5
6
7
8
9
process.exit(0); // success
process.exit(1); // generic error
// exit code for uncaught exceptions:
process.on('uncaughtException', e => {
console.error(e);
process.exit(1);
});
// in async code, process.exitCode is cleaner:
process.exitCode = 1; // exit once the event loop drains

Current directory and paths

process.cwd() is the current working directory; __dirname is the module directory; __filename is the module file (CJS).

1
2
3
4
5
6
7
process.cwd(); // cwd at launch
// in ESM:
import { fileURLToPath } from 'node:url';
const __dirname = fileURLToPath(new URL('.', import.meta.url));
// should config files be relative to cwd or the module directory?
// use process.cwd() when the path depends on how the app was launched
// use __dirname when you mean "next to this script"

Process signals

Handle SIGINT (Ctrl+C) and SIGTERM (default kill) for graceful shutdown (flush data, close connections).

1
2
3
4
5
6
7
8
9
10
process.on('SIGINT', () => {
console.log('received Ctrl+C, cleaning up...');
cleanup();
process.exit(0);
});
process.on('SIGTERM', () => {
console.log('received termination signal, shutting down gracefully');
server.close();
});
// prevent double-trigger: guard cleanup with a flag

Process info

process.pid is the process ID; process.uptime() is seconds running; process.memoryUsage() reports memory; process.platform is the OS.

1
2
3
4
5
6
7
8
process.pid; // process ID
process.platform; // 'win32' | 'linux' | 'darwin'
process.arch; // 'x64'
process.uptime(); // uptime in seconds
process.memoryUsage(); // { rss, heapUsed, ... }
process.version; // Node version
// common debugging:
console.log(process.memoryUsage().heapUsed / 1024 / 1024);

Standard streams

process.stdin/stdout/stderr are the three standard streams for piping, redirecting, and shell collaboration.

1
2
3
4
5
6
7
8
9
10
11
// output:
process.stdout.write('text'); // no trailing newline
process.stderr.write('error'); // error stream
// read stdin line by line (piped input):
import readline from 'node:readline';
const rl = readline.createInterface({ input: process.stdin });
for await (const line of rl) {
console.log(line.toUpperCase()); // process each line
}
// $ cat file.txt | node app.js > out.txt
// is the stream a TTY: process.stdout.isTTY

child_process

child_process launches external commands: exec grabs output, spawn streams I/O, fork runs JS scripts. Beware shell injection.

1
2
3
4
5
6
7
8
9
10
import { exec, spawn } from 'node:child_process';
// exec: run a command and collect its full output
exec('ls -la', (err, stdout) => {
if (err) console.error(err.message);
else console.log(stdout);
});
// spawn: stream large outputs
const child = spawn('node', ['worker.js'], { stdio: 'inherit' });
child.on('exit', code => console.log('exit code', code));
// prefer execFile/spawn with arg arrays to avoid shell injection

18.Regular Expressions

Regex literals, matching, capture groups, replacement, and common patterns.

Regex syntax

Use literals /pattern/ or new RegExp. \d digit, \w word char, \s whitespace, [] character class, {} quantifier.

1
2
3
4
5
6
7
const email = /^[^@\s]+@[^@\s]+\.[^@\s]+$/;
const phone = /^1[3-9]\d{9}$/;
/\d{4}-\d{2}-\d{2}/; // date
/colou?r/; // color/colour
/\bword\b/; // word boundary
// building from a string needs double-escaped backslashes:
new RegExp('\\d+'); // same as /\d+/

Regex flags

g global, i case-insensitive, m multiline (^$ match per line), s dotAll (. matches newline), u unicode.

1
2
3
4
5
6
7
const g = /\b\w+\b/g; // global match
const i = /hello/i; // case-insensitive
const m = /^start/m; // start of each line
const s = /a.b/s; // `.` also matches newlines
// inline flag:
/(?i)hello/; // case-insensitive
// combined: /ab/gi

test and match

test returns a boolean; match returns the result; matchAll iterates every match. exec returns one match and tracks lastIndex.

1
2
3
4
5
6
7
8
const re = /\d+/g;
re.test('abc123'); // true
'abc123'.match(/\d+/); // ['123', index, input]
// all matches:
'a1b2'.matchAll(/\d/g);
// [...str.matchAll(/\d/g)].map(m => m[0]);
// count matches:
('a1b2'.match(/\d/g) || []).length; // 2

Capture groups

Parentheses capture; match[1] reads a group. Named groups (?<name>...) are accessed via match.groups.name. Non-capturing groups use (?:...).

1
2
3
4
5
6
7
const m = '2024-01-01'.match(/(\d{4})-(\d{2})-(\d{2})/);
m[1]; // '2024'
m[2]; // '01'
// named groups:
const n = '2024-01-01'.match(/(?<year>\d{4})-(?<month>\d{2})/);
n.groups.year; // '2024'
// non-capturing group: /(?:ab)+/ groups without capturing

Regex replacement

replace supports $1/$<name> group references and a replacer callback. replaceAll replaces every occurrence (g semantics).

1
2
3
4
5
6
'2024-01-01'.replace(/(\d{4})-(\d{2})-(\d{2})/, '$2/$3/$1');
// masking:
'13812345678'.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2');
// callback:
'abc'.replace(/\w/g, c => c.toUpperCase()); // 'ABC'
// replace all: 'a-b'.replaceAll('-', '+')

Common patterns

Email, URL, IP, phone, alphanumeric. Production validation should use allowlists/strict libraries.

1
2
3
4
5
6
7
8
9
const patterns = {
email: /^[^@\s]+@[^@\s]+\.[^@\s]+$/,
url: /^https?:\/\/[^\s]+$/,
ipv4: /^(\d{1,3}\.){3}\d{1,3}$/,
phone: /^1[3-9]\d{9}$/,
alnum: /^[a-zA-Z0-9]+$/,
};
patterns.email.test('[email protected]'); // true
// note: simple checks are usually enough; in production use a dedicated library for stricter validation

Regex performance

Avoid catastrophic backtracking (nested quantifiers). Precompile regexes for repeated use. Fall back to string methods when simpler.

1
2
3
4
5
6
7
8
9
// slow: nested quantifiers can cause catastrophic backtracking
/(a+)+$/; // very slow on 'aaaaaaaaX'
// optimize:
/(?:a+)$/;
// precompile and reuse:
const re = /pattern/g; // reuse across many .match() calls
// prefer string methods for simple checks:
str.includes('x') beats /x/;
// for long input, watch out for timeouts and consider chunking

Lookaround

Assertions match positions without consuming: lookahead (?=), negative lookahead (?!), lookbehind (?<=). Great for password and boundary checks.

1
2
3
4
5
6
7
const re = /foo(?=\d)/; // `foo` followed by a digit
re.test('foo1'); // true
re.test('fooX'); // false
/\d+(?!px)/.test('10em'); // true (10 is not followed by px)
/(?<=@)\w+/.exec('[email protected]'); // matches the `b` after `@`
// password strength: must contain both letters and digits
/^(?=.*[a-z])(?=.*\d).{6,}$/.test('abc123'); // true

19.Build and Debug

npm scripts, bundlers, debugging, testing, and code style.

npm scripts

package.json scripts define common commands. Run with npm run. Chain with && and use pre/post hooks.

1
2
3
4
5
6
7
8
9
10
11
12
{
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"test": "vitest run",
"lint": "eslint . --fix",
"check": "npm run lint && npm run test"
}
}
// $ npm run dev / npm test
// `prebuild` runs automatically before `build`

Bundlers

Vite (modern recommendation), webpack, Rollup, and esbuild bundle ES modules for the browser.

1
2
3
4
5
6
7
8
9
// Vite (built on esbuild / rollup):
// vite.config.js
import { defineConfig } from 'vite';
export default defineConfig({
build: { outDir: 'dist' },
});
// $ npm create vite@latest my-app
// $ npm run dev # dev server with HMR
// $ npm run build # production bundle

Debugging

Use console levels, debugger for breakpoints, and Node --inspect for the debugger. Source maps locate original source.

1
2
3
4
5
6
7
8
9
console.log('info'); // normal
console.warn('warn'); // warning
console.error('error'); // error
console.debug('debug');
debugger; // breakpoint (pauses when DevTools is open)
// Node debugging:
// $ node --inspect-brk app.js
// then open chrome://inspect in Chrome
// or use VS Code F5 with a launch.json

Testing

Vitest/Jest unit tests: describe for groups, test/it for cases, expect for assertions. Run with npm test.

1
2
3
4
5
6
7
8
9
10
11
12
13
import { describe, it, expect } from 'vitest';
function add(a, b) { return a + b; }
describe('add', () => {
it('adds two numbers', () => {
expect(add(2, 3)).toBe(5);
expect(add(-1, 1)).toBe(0);
});
});
// async test:
it('async', async () => {
await expect(load()).resolves.toBeDefined();
});
// coverage: vitest run --coverage

Linting with ESLint

ESLint checks syntax and style; Prettier formats uniformly. Pre-commit hooks automate the checks.

1
2
3
4
5
6
7
8
9
10
11
12
// eslint.config.js
import js from '@eslint/js';
export default [
js.configs.recommended,
{ rules: {
'eqeqeq': ['error', 'always'], // enforce ===
'no-unused-vars': 'warn',
}},
];
// $ npx eslint src --fix
// Prettier: $ npx prettier --write .
// husky + lint-staged for pre-commit checks

TypeScript integration

TS adds types. tsc compiles; ts-node/tsx run directly. Vite supports .ts out of the box.

1
2
3
4
5
6
7
8
9
10
11
// $ npm i -D typescript
// tsconfig.json:
{ "compilerOptions": {
"target": "ES2022",
"strict": true,
"module": "ESNext",
"moduleResolution": "bundler"
} }
// $ npx tsc --noEmit # type-check only
// with Vite, just import './app.ts'
// tsx: $ npx tsx script.ts

CI and deployment

CI runs tests and builds automatically. Configure workflows in GitHub Actions and similar platforms. Deploy artifacts to static hosts or containers.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# .github/workflows/ci.yml
name: CI
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20 }
- run: npm ci
- run: npm run lint
- run: npm test
- run: npm run build
# deploy: upload dist/ to static hosting or build a container image

ESM vs CJS

Two module systems: ESM uses static import and enables tree-shaking; CJS uses runtime require. The type field controls the default.

1
2
3
4
5
6
7
8
9
// ESM (.mjs or package.json type: module)
import { readFile } from 'node:fs/promises';
export const NAME = 'app';
// CJS (.cjs or no `type` field)
const fs = require('node:fs');
module.exports = { NAME: 'app' };
// ESM can dynamically import CJS:
const mod = await import('cjs-pkg');
// prefer ESM at bundling time so tree-shaking can kick in

Official Links

Direct links to the official docs and resources.

About this Cheatsheet

This page is a self-contained cheatsheet for ECMAScript 2022 (JavaScript), covering the language core and the browser/Node APIs you actually use in about 80% of real projects. The content favors modern idioms: let/const block scope, arrow functions, template literals, destructuring, spread, Promise and async/await, optional chaining, and nullish coalescing. JavaScript was created by Brendan Eich at Netscape in 1995; today it is the only language natively supported by every browser, and the foundation of web frontends, Node.js servers, and desktop apps. The 19 sections each focus on a topic: basic syntax, variables and scope, types, references and value semantics, control flow, functions and closures, strings, arrays and objects, memory and garbage collection, prototypes and classes, error handling, I/O, common pitfalls, concurrency (event loop), network (fetch), time, processes, regex, and build tools. Each subsection pairs a concept with copy-ready code. All code and text render locally in your browser — nothing leaves your device. Authoritative references: MDN Web Docs.

Version 2.1.0