JavaScript#
Shortcut keys for the JavaScript interactive interpreters the
operator reaches for: Node.js REPL (node), the Chrome
/ Edge DevTools Console, and Firefox Web Console. The Node
REPL uses Node’s own readline implementation; DevTools consoles
add Monaco-style editing on top of the page’s runtime. Bun and
Deno have their own REPLs that overlap with Node’s bindings.
For setup, package managers (npm / pnpm / yarn / bun), and runtime configuration, see JavaScript.
Cursor Movement (Node)#
Key |
Action |
|---|---|
|
move to start of line |
|
move to end of line |
|
move back one character |
|
move forward one character |
|
move back one word |
|
move forward one word |
Editing (Node)#
Key |
Action |
|---|---|
|
delete character under cursor (or exit if line is empty) |
|
delete character before cursor |
|
delete word forward |
|
delete word backward |
|
kill to end of line |
|
kill to start of line |
|
yank (paste) last killed text |
|
transpose characters |
|
transpose words |
|
undo (where the terminal supports it) |
History (Node)#
Node persists REPL history at ~/.node_repl_history by
default. Override with NODE_REPL_HISTORY=<path> or empty to
disable.
Key |
Action |
|---|---|
|
previous history entry |
|
next history entry |
|
reverse incremental search |
|
insert last argument of previous command |
|
print persistent history (REPL |
REPL Commands (Node)#
Node’s .-prefixed dot commands operate on the REPL itself
rather than the JS runtime.
Command |
Action |
|---|---|
|
list dot commands |
|
enter multi-line editor mode |
|
abort current input (without exiting) |
|
reset the REPL context |
|
exit the REPL |
|
save the REPL session to a file |
|
load JS from a file into the session |
|
reference the last evaluated expression |
|
reference the last thrown error |
Process Control#
Key |
Action |
|---|---|
|
cancel current line (twice in a row to exit Node) |
|
send EOF (exits the REPL on empty line) |
|
clear screen |
|
suspend Node (SIGTSTP) |
DevTools Console (browser runtimes)#
The browser console accepts JS at a single-line input by default;
Shift-Enter opens multi-line mode. Chromium and Firefox share
most of the bindings.
Key |
Action |
|---|---|
|
open DevTools focused on the Console |
|
open DevTools (last-used panel) |
|
open the Web Console |
|
submit single-line expression |
|
newline without submitting (multi-line mode) |
|
submit multi-line block |
|
browse expression history |
|
clear console output (Chromium) |
|
accept the inline ghost-text suggestion |
|
open completion menu |
|
toggle line comment in multi-line mode |
|
reference last evaluated expression |
|
reference last five inspected DOM nodes |
|
shorthand for |
|
shorthand for |
|
evaluate XPath against the document |
|
copy a value to the system clipboard |
Node Inspector (Chrome DevTools attach)#
Run with node --inspect (start with inspector listening) or
node --inspect-brk (break before user code). Open
chrome://inspect in Chromium, attach to the target, and
DevTools binds the standard debugger keys.
Key |
Action |
|---|---|
|
resume / pause execution |
|
step over |
|
step into |
|
step out |
|
toggle breakpoint on current line |
|
search across loaded scripts |
|
open script by name |
|
source-level breakpoint statement |
Bun / Deno REPLs#
Same readline keymap as Node for the bindings above; the notable extras / differences:
Key / Command |
Action |
|---|---|
|
start the Bun REPL |
|
start the Deno REPL |
|
pre-evaluate before opening the REPL |
|
enable unstable APIs in either REPL |
|
abort current input (single press in Bun / Deno, two presses in Node) |
|
exit the REPL |
Config and Discovery#
Command / file |
Action |
|---|---|
|
path to Node REPL history file ( |
|
default history file |
|
max entries in history |
|
|
|
run REPL in strict mode |
|
enable Chrome DevTools inspector |
|
customize how the REPL prints objects |
|
low-level readline API (when building your own REPL) |
fetch With Timeout and Retry#
async function fetchJson(url, { timeout = 5000, retries = 3 } = {}) {
for (let attempt = 0; attempt <= retries; attempt++) {
const ac = new AbortController();
const id = setTimeout(() => ac.abort(), timeout);
try {
const r = await fetch(url, { signal: ac.signal });
if (!r.ok) throw new Error(`HTTP ${r.status}`);
return await r.json();
} catch (err) {
if (attempt === retries) throw err;
await new Promise((r) => setTimeout(r, 2 ** attempt * 200));
} finally {
clearTimeout(id);
}
}
}
Parallel With Concurrency Cap#
async function pool(items, n, fn) {
const out = [];
let i = 0;
async function worker() {
while (i < items.length) {
const idx = i++;
out[idx] = await fn(items[idx]);
}
}
await Promise.all(Array.from({ length: n }, worker));
return out;
}
Debounce / Throttle#
function debounce(fn, wait) {
let t;
return (...args) => {
clearTimeout(t);
t = setTimeout(() => fn(...args), wait);
};
}
function throttle(fn, wait) {
let last = 0;
return (...args) => {
const now = Date.now();
if (now - last >= wait) {
last = now;
fn(...args);
}
};
}
Group By#
// ES2024 native
Object.groupBy(users, (u) => u.team);
// Manual
const byTeam = users.reduce((acc, u) => {
(acc[u.team] ??= []).push(u);
return acc;
}, {});
Deep Clone (Structured)#
const clone = structuredClone(obj); // built in everywhere modern
Pick / Omit#
const pick = (obj, keys) =>
Object.fromEntries(keys.filter((k) => k in obj).map((k) => [k, obj[k]]));
const omit = (obj, keys) =>
Object.fromEntries(Object.entries(obj).filter(([k]) => !keys.includes(k)));
Sorting#
xs.sort((a, b) => a - b); // numbers
ys.sort((a, b) => a.localeCompare(b)); // strings, locale
people.sort((a, b) => a.lastName.localeCompare(b.lastName) ||
a.firstName.localeCompare(b.firstName));
Async Iteration#
for await (const chunk of response.body) {
process(chunk);
}
// Async generator
async function* paginate(url) {
let next = url;
while (next) {
const r = await fetch(next).then((r) => r.json());
yield* r.items;
next = r.next;
}
}
for await (const item of paginate("/api/items")) {
process(item);
}
Once#
function once(fn) {
let called = false, result;
return (...args) => {
if (!called) {
result = fn(...args);
called = true;
}
return result;
};
}
Memoize#
function memoize(fn) {
const cache = new Map();
return (...args) => {
const key = JSON.stringify(args);
if (!cache.has(key)) cache.set(key, fn(...args));
return cache.get(key);
};
}
Sleep#
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
await sleep(500);
Format Bytes / Duration#
const formatBytes = (n) => {
if (n < 1024) return `${n} B`;
const units = ["KB", "MB", "GB", "TB"];
let i = -1;
do { n /= 1024; i++; } while (n >= 1024 && i < units.length - 1);
return `${n.toFixed(1)} ${units[i]}`;
};
// Intl is the right tool for most formatting
new Intl.NumberFormat("en-US").format(1234567);
new Intl.DateTimeFormat("en-US", { dateStyle: "medium" }).format(new Date());
URL Building#
const url = new URL("https://api.example.com/search");
url.searchParams.set("q", "operator");
url.searchParams.set("limit", "10");
await fetch(url);
EventEmitter / Pub-Sub#
class Emitter {
#listeners = new Map();
on(event, fn) { (this.#listeners.get(event) ?? this.#listeners.set(event, new Set()).get(event)).add(fn); }
off(event, fn) { this.#listeners.get(event)?.delete(fn); }
emit(event, ...args) {
for (const fn of this.#listeners.get(event) ?? []) fn(...args);
}
}
Type-Safe JSON Parse (TypeScript)#
import { z } from "zod";
const User = z.object({ id: z.number(), name: z.string() });
type User = z.infer<typeof User>;
async function fetchUser(id: number): Promise<User> {
const r = await fetch(`/api/users/${id}`);
if (!r.ok) throw new Error(`HTTP ${r.status}`);
return User.parse(await r.json());
}