Getting started
Async Iteration
async function* asyncGenerator() {
let i = 0;
while (i < 3) yield i++;
}
(async () => {
for await (const num of asyncGenerator()) {
console.log(num); // 0, 1, 2
}
})();
Iterate over async data sources.
Object Rest/Spread
// Rest properties
const { a, ...rest } = { a: 1, b: 2, c: 3 };
// a = 1, rest = { b: 2, c: 3 }
// Spread properties
const cloned = { ...obj };
const merged = { ...obj1, ...obj2 };
Extract and merge object properties.
Promise.finally()
fetch(url)
.then((res) => res.json())
.catch((err) => console.error(err))
.finally(() => {
isLoading = false;
});
Cleanup logic for promises.
Async Iteration
for await...of
async function* asyncGenerator() {
yield Promise.resolve(1);
yield Promise.resolve(2);
yield Promise.resolve(3);
}
for await (const num of asyncGenerator()) {
console.log(num); // 1, 2, 3
}
Iterate over async iterables.
Streaming Data
async function* streamAsyncIterable(stream) {
const reader = stream.getReader();
try {
while (true) {
const { done, value } = await reader.read();
if (done) return;
yield value;
}
} finally {
reader.releaseLock();
}
}
for await (const chunk of streamAsyncIterable(stream)) {
console.log(chunk);
}
Process streams asynchronously.
Async Iterable Protocol
const asyncIterable = {
[Symbol.asyncIterator]() {
let i = 0;
return {
async next() {
if (i < 3) {
return { value: i++, done: false };
}
return { done: true };
},
};
},
};
for await (const num of asyncIterable) {
console.log(num); // 0, 1, 2
}
Implement custom async iterables.
Object Rest/Spread
Object Rest
const { a, b, ...rest } = { a: 1, b: 2, c: 3, d: 4 };
// a = 1, b = 2, rest = { c: 3, d: 4 }
// Extract known properties
const { x, y, ...coords } = point;
Collect remaining properties.
Object Spread
// Shallow copy
const cloned = { ...obj };
// Merge objects
const merged = { ...obj1, ...obj2 };
// Override defaults
const config = { ...defaults, ...options };
Replaces Object.assign({}, obj).
Conditional Properties
const config = {
mode: "production",
...(isSummer && { theme: "light" }),
...(debug && { logging: true }),
};
// Add property conditionally
const user = {
name: "John",
...(age && { age }),
};
Conditionally include properties.
Nested Objects
// Shallow copy (nested objects shared)
const copy = { ...original };
copy.nested.value = 42; // Mutates original!
// Deep copy alternative
const deepCopy = JSON.parse(JSON.stringify(obj));
⚠️ Creates shallow copies only.
Promise.finally()
Basic Usage
fetch(url)
.then((res) => res.json())
.then((data) => console.log(data))
.catch((err) => console.error(err))
.finally(() => {
isLoading = false;
});
Run cleanup after promise settles.
Pass-through Behavior
Promise.resolve(2)
.finally(() => 77)
.then((v) => console.log(v)); // 2
Promise.reject(3)
.finally(() => 88)
.catch((e) => console.log(e)); // 3
Returns original promise value.
Exception Override
Promise.resolve(2)
.finally(() => {
throw 99;
})
.catch((e) => console.log(e)); // 99
Promise.reject(3)
.finally(() => Promise.reject(100))
.catch((e) => console.log(e)); // 100
⚠️ Throwing overrides original value.
Async Cleanup
fetch(url)
.finally(async () => {
await closeConnection();
await logActivity();
})
.then((data) => console.log(data));
Supports async cleanup logic.
RegExp Named Capture Groups
Basic Syntax
const re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const match = "2023-12-25".match(re);
match.groups.year; // "2023"
match.groups.month; // "12"
match.groups.day; // "25"
Name capturing groups.
Named Backreference
// Match opening and closing quotes
/(?<quote>['"]).*?\k<quote>/.test(`"hello"`); // true
/(?<quote>['"]).*?\k<quote>/.test(`'world'`); // true
/(?<quote>['"]).*?\k<quote>/.test(`"mixed'`); // false
// Duplicate word finder
/\b(?<word>\w+)\s+\k<word>\b/;
Reference named groups with \k<name>.
With matchAll()
const text = "John Doe, Jane Smith";
const re = /(?<first>\w+) (?<last>\w+)/g;
for (const match of text.matchAll(re)) {
console.log(match.groups.first); // "John", "Jane"
console.log(match.groups.last); // "Doe", "Smith"
}
Iterate over all matches.
With replace()
const date = "2023-12-25";
// Use named groups in replacement
date.replace(
/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/,
"$<month>/$<day>/$<year>",
); // "12/25/2023"
// Callback with groups
date.replace(/(?<y>\d{4})-(?<m>\d{2})-(?<d>\d{2})/, (match, ...args) => {
const groups = args[args.length - 1];
return `${groups.m}/${groups.d}/${groups.y}`;
});
Use in replacement strings.
RegExp Lookbehind Assertions
Positive Lookbehind
// Match digits after $
/(?<=\$)\d+/.exec("$10.53")?.[0]; // "10"
// Match price
/(?<=Price: \$)\d+\.\d{2}/.exec("Price: $19.99");
// ["19.99"]
Match if preceded by pattern.
Negative Lookbehind
// Match "happy" not preceded by "un"
/(?<!un)happy/.test("happy"); // true
/(?<!un)happy/.test("unhappy"); // false
// Match numbers not preceded by $
/(?<!\$)\d+/.exec("$10 and 20")?.[0]; // "0" (from $10)
Match if NOT preceded by pattern.
Before ES2018
// Workaround with capturing group
const match = /\$(\d+)/.exec("$10");
const price = match?.[1]; // "10"
// Or with replace
const num = "$10".replace(/^\$/, "");
No native lookbehind support.
RegExp Unicode Property Escapes
Script Matching
// Match Greek letters (requires /u flag)
/\p{Script=Greek}/u.test("π"); // true
/\p{Script=Greek}/u.test("a"); // false
// Match emoji
/\p{Emoji_Presentation}/gu.test("😀"); // true
⚠️ Requires /u flag.
General Categories
// Match any letter
/\p{Letter}/gu.test("A"); // true
// Match any number
/\p{Number}/gu.test("5"); // true
// Match currency symbols
/\p{Sc}/gu.test("$"); // true
/\p{Sc}/gu.test("€"); // true
// Match punctuation
/\p{Punctuation}/gu.test("!"); // true
Match character categories.
Negated Properties
// Match non-Latin characters
/\P{Script_Extensions=Latin}/gu.test("π"); // true
/\P{Script_Extensions=Latin}/gu.test("a"); // false
// Match non-digits
/\P{Number}/gu.test("a"); // true
Use \P for negation.
Common Properties
| Property | Matches |
|---|---|
\p{Letter} |
Any letter |
\p{Number} |
Any number |
\p{Sc} |
Currency symbols |
\p{Emoji} |
Emoji characters |
\p{Script=Greek} |
Greek script |
\p{White_Space} |
Whitespace |
RegExp s (dotAll) Flag
Basic Usage
// . matches newlines with /s
/foo.bar/s.test("foo\nbar"); // true
/foo.bar/.test("foo\nbar"); // false
// Multiline matching
/^.+$/s.test("line1\nline2"); // true
Make . match newlines.
Check Flag
const re = /pattern/s;
re.dotAll; // true
const re2 = /pattern/;
re2.dotAll; // false
Detect dotAll flag.
Before ES2018
// Workaround with [\s\S] or [^]
/foo[\s\S]bar/.test("foo\nbar"); // true
/foo[^]bar/.test("foo\nbar"); // true
Character class workarounds.
Template Literal Revision
Tagged Templates
function latex(strings) {
return strings.raw[0];
}
// Illegal escape sequences now allowed
latex`\unicode{00A0}`; // "\unicode{00A0}"
Allow illegal escape sequences.
Windows Paths
function windowsPath(strings) {
return strings.raw[0];
}
windowsPath`C:\Windows\System32`; // Works!
windowsPath`C:\new\folder`; // Works!
No escaping needed.
strings.raw
function tag(strings, ...values) {
console.log(strings.raw[0]); // Raw string
console.log(strings[0]); // Cooked string
}
tag`Line 1\nLine 2`;
// Raw: "Line 1\\nLine 2"
// Cooked: "Line 1\nLine 2"
Access raw template strings.
SharedArrayBuffer & Atomics
SharedArrayBuffer
// Shared memory between workers
const sab = new SharedArrayBuffer(1024);
const view = new Int32Array(sab);
// Share with worker
worker.postMessage({ buffer: sab });
Share memory between threads.
Atomic Operations
const sab = new SharedArrayBuffer(4);
const view = new Int32Array(sab);
// Atomic add
Atomics.add(view, 0, 5);
// Atomic load/store
Atomics.load(view, 0);
Atomics.store(view, 0, 10);
// Wait/notify
Atomics.wait(view, 0, 10);
Atomics.notify(view, 0, 1);
Thread-safe operations.
Browser Requirements
// Requires these headers:
// Cross-Origin-Opener-Policy: same-origin
// Cross-Origin-Embedder-Policy: require-corp
⚠️ Needs cross-origin isolation.
Gotchas
Async Iteration
// ⚠️ Awaits EVERY iteration (even sync values)
for await (const x of [1, 2, 3]) {
// Slower!
console.log(x);
}
// ✓ Use regular for...of for sync data
for (const x of [1, 2, 3]) {
console.log(x);
}
Object Spread
// ⚠️ Shallow copy (nested objects shared)
const copy = { ...original };
copy.nested.value = 42; // Mutates original.nested!
// ✓ Deep copy for nested objects
const deepCopy = JSON.parse(JSON.stringify(obj));
Promise.finally()
// ⚠️ Throwing in finally() overrides original value
Promise.resolve(2)
.finally(() => {
throw 99;
})
.catch((e) => console.log(e)); // 99 (not 2)
Unicode Property Escapes
// ⚠️ Requires /u flag
/\p{Letter}/gu.test("A"); // ✓ Works
/\p{Letter}/g.test("A"); // ✗ SyntaxError
Browser Support
All ES2018 features are Baseline Widely Available since mid-2020. Supported in:
- Chrome 63+
- Firefox 58+
- Safari 11.1+
- Edge 79+
- Node.js 10+
Also see
- MDN: for await...of - Async iteration reference
- MDN: Promise.finally() - Promise cleanup method
- MDN: RegExp dotAll - Dot matches newlines
- MDN: Named Capture Groups - RegExp named groups
- 2ality: ES2018 - Dr. Axel Rauschmayer's overview