# Modern JavaScript (ES2020–ES2025) β€” Deep Dive

> Comprehensive research on JavaScript features from ES2020 through ES2025.

---

## 1. ES2020 (ES11) Features

### Nullish Coalescing (`??`)

Returns right operand only when left is `null` or `undefined` β€” NOT falsy values like `0` or `""`.

```javascript
// ❌ Logical OR β€” treats 0 and "" as falsy
const port = config.port || 3000;       // 0 becomes 3000! ❌

// βœ… Nullish coalescing β€” only null/undefined
const port = config.port ?? 3000;       // 0 stays 0 βœ…
const name = config.name ?? "Anonymous"; // "" stays "" βœ…
```

### Optional Chaining (`?.`)

Safe property access without intermediate checks.

```javascript
// ❌ Before β€” verbose
const city = user && user.address && user.address.city;

// βœ… After β€” clean
const city = user?.address?.city;

// Safe function calls
const result = safeFunction?.();

// Safe array access
const first = arr?.[0];

// Short-circuit assignment
user?.preferences?.theme ??= "dark";
```

### `globalThis`

Universal global object reference β€” works in browser, Node.js, and Web Workers.

```javascript
// Before β€” platform-specific
const g = typeof window !== 'undefined' ? window 
    : typeof global !== 'undefined' ? global 
    : typeof self !== 'undefined' ? self : {};

// After β€” universal
console.log(globalThis);  // window, global, or self depending on context
```

### `Promise.allSettled()`

Wait for all promises, regardless of success/failure.

```javascript
const results = await Promise.allSettled([
  fetch('/api/users'),
  fetch('/api/posts'),
  fetch('/api/comments'),
]);

results.forEach((result, i) => {
  if (result.status === 'fulfilled') {
    console.log(`Promise ${i} succeeded:`, result.value);
  } else {
    console.log(`Promise ${i} failed:`, result.reason);
  }
});

// Use case: "Fire and forget all, collect results"
// vs Promise.all() β€” fails fast on first rejection
```

### `BigInt`

Arbitrary-precision integers β€” beyond `Number.MAX_SAFE_INTEGER` (2⁡³-1).

```javascript
const bigNum = 9007199254740991n;  // n suffix
const bigNum2 = BigInt("9007199254740991");

// BigInt math
console.log(bigNum + bigNum2);  // 18014398509481982n

// ⚠️ Cannot mix with Number
// bigNum + 1  // TypeError!
bigNum + 1n    // βœ…
Number(bigNum) // Convert to number (precision loss possible)
```

---

## 2. ES2021 (ES12) Features

### `String.prototype.replaceAll()`

Replace all occurrences without regex.

```javascript
// ❌ Before β€” need /g flag
"text".replace("t", "T");     // "Text" (first only)
"text".replace(/t/g, "T");    // "Text" (all)

// βœ… After β€” no regex needed
"text".replaceAll("t", "T");  // "Text"
```

### `Promise.any()`

Returns first *fulfilled* promise. Fails only if ALL reject.

```javascript
// Race β€” returns first settled (fulfilled OR rejected)
const winner = await Promise.race(promises);

// Any β€” returns first FULFILLED (waits out rejections)
const winner = await Promise.any(promises);

// AggregateError if all fail
try {
  await Promise.any([Promise.reject(1), Promise.reject(2)]);
} catch (e) {
  console.log(e.errors);  // [1, 2] β€” AggregateError
}

// Use case: Try multiple CDN endpoints, use first that succeeds
const cdnUrl = await Promise.any([
  fetch('https://cdn1.com/lib.js'),
  fetch('https://cdn2.com/lib.js'),
  fetch('https://cdn3.com/lib.js'),
]);
```

### `logicalAssignment` (??=, ||=, &&=)

```javascript
let config = { port: 0 };

config.port ||= 3000;   // No-op, port is 0 (truthy-ish... wait, 0 is falsy)
// Actually: config.port ||= 3000 β†’ port becomes 3000 (0 is falsy)

config.port ??= 3000;   // No-op, port is 0 (not null/undefined) βœ…
config.timeout ??= 5000; // Sets timeout to 5000

let arr = [];
arr &&= [...arr, "new"];  // Only push if arr exists
```

### `WeakRef` and `FinalizationRegistry`

```javascript
// WeakRef β€” doesn't prevent garbage collection
const ref = new WeakRef(largeObject);
const deref = ref.deref();  // Returns object or undefined if GC'd

// FinalizationRegistry β€” callback when object is collected
const registry = new FinalizationRegistry((heldValue) => {
  console.log(`Object with key ${heldValue} was garbage collected`);
});
registry.register(largeObject, "myKey");
```

---

## 3. ES2022 (ES13) Features

### Top-Level `await`

Use `await` at module scope (in ES modules only).

```javascript
// βœ… In ES modules (.mjs or "type": "module" in package.json)
const users = await fetch('/api/users').then(r => r.json());
const config = await import('./config.js');

// Before β€” needed IIFE
const init = async () => {
  const users = await fetch('/api/users').then(r => r.json());
};
init();
```

### `.at()` β€” Negative Indexing

Works on arrays, strings, and typed arrays.

```javascript
const arr = [10, 20, 30, 40, 50];

arr.at(0);    // 10  (same as arr[0])
arr.at(-1);   // 50  (last element)
arr.at(-2);   // 40  (second to last)

"hello".at(-1);    // "o"
Uint8Array.from([1,2,3]).at(-1);  // 3
```

### Class Fields and Private Methods

```javascript
class Counter {
  // Public field
  count = 0;
  
  // Private field (#)
  #step = 1;
  
  // Private method
  #validate(n) {
    return Number.isInteger(n) && n >= 0;
  }
  
  increment(n = 1) {
    if (!this.#validate(n)) throw new TypeError();
    this.count += n * this.#step;
  }
  
  // Private accessor
  #get step() { return this.#step; }
  #set step(v) { this.#step = v; }
}
```

### `Object.hasOwn()`

Safer alternative to `Object.prototype.hasOwnProperty.call()`.

```javascript
// ❌ Verbose β€” fails if object has own hasOwnProperty
obj.hasOwnProperty('key');

// βœ… Safe β€” static method
Object.hasOwn(obj, 'key');

// Works even with null prototype
const map = Object.create(null);
Object.hasOwn(map, 'key');  // βœ…
```

---

## 4. ES2023 (ES14) Features

### Array `.at()` was ES2022, new features:

### `Array.prototype.toSorted()`, `toReversed()`, `toSpliced()`, `with()`

Non-mutating array operations.

```javascript
const arr = [3, 1, 4, 1, 5];

// Mutating (original)
arr.sort();          // [1, 1, 3, 4, 5] β€” arr is changed
arr.reverse();       // arr is changed
arr.splice(1, 2);    // arr is changed
arr[0] = 99;         // arr is changed

// Non-mutating (new)
const sorted = arr.toSorted();        // New sorted array
const reversed = arr.toReversed();    // New reversed array
const spliced = arr.toSpliced(1, 2);  // New array with elements removed
const updated = arr.with(0, 99);      // New array with index 0 replaced

// Original arr unchanged βœ…
```

### `Array.prototype.findLast()` and `findLastIndex()`

Search from the end.

```javascript
const nums = [10, 20, 30, 40, 50];

nums.findLast(n => n > 25);       // 50
nums.findLastIndex(n => n > 25);  // 4
nums.findLast(n => n % 10 === 0); // 50 (all match, returns last)

// Use case: Find last active item in a list
const lastActive = users.findLast(u => u.isActive);
```

### `Hashbang Grammar`

```javascript
#!/usr/bin/env node
// Now valid JavaScript syntax in ES modules
console.log("Hello");
```

---

## 5. ES2024 (ES15) Features

### `Array.prototype.toSpan()` (Staged)

### Regular Match Indices (`/d` Flag)

```javascript
const regex = /a(b)c/d;  // /d flag captures group indices
const match = "abc".match(regex);

console.log(match.indices);
// [ [0, 3], [1, 2] ]  // [full match indices, group 1 indices]

// Named group indices
const named = /(?<letter>x)/d;
"xyz".match(named).indices.groups;
// { letter: [0, 1] }
```

### `RegExp.v` Flag β€” Unicode Sets

```javascript
// More efficient Unicode matching, supports set operations
const regex = /[\p{Script=Han}&&[^\p{Emoji}]]/v;
```

### `Promise.withResolvers()`

Create a promise and its control functions separately.

```javascript
// Before β€” awkward pattern
let resolve, reject;
const promise = new Promise((res, rej) => {
  resolve = res;
  reject = rej;
});
// use resolve/reject later...

// After β€” clean API
const { promise, resolve, reject } = Promise.withResolvers();

// Pass promise to consumer, resolve/reject to producer
apiConsumer(promise);
setTimeout(() => resolve(data), 1000);

// Use case: Event-to-promise bridge
function waitForEvent(target, type) {
  const { promise, resolve } = Promise.withResolvers();
  target.addEventListener(type, resolve, { once: true });
  return promise;
}

const click = await waitForEvent(button, 'click');
```

### `SharedArrayBuffer` Atomic Operations

### `Temporal` API (Staged β€” not finalized)

Replacement for `Date` β€” timezone-aware, immutable date/time.

```javascript
// (Staged β€” not yet in browsers, but available via polyfill)
const now = Temporal.Now.zonedDateTimeISO('America/New_York');
const birthday = Temporal.ZonedDateTime.from({
  year: 2000,
  month: 1,
  day: 15,
  timeZone: 'America/New_York'
});
const age = now.year - birthday.year;
```

---

## 6. ES2025 (ES16) Features

### Array Grouping

```javascript
const people = [
  { name: 'Alice', dept: 'Engineering' },
  { name: 'Bob', dept: 'Marketing' },
  { name: 'Carol', dept: 'Engineering' },
  { name: 'Dave', dept: 'Marketing' },
];

// Group into Map
const byDept = Object.groupBy(people, p => p.dept);
// Map { 'Engineering' => [{...}, {...}], 'Marketing' => [{...}, {...}] }

// Group into Array (flat key-value pairs)
const byDeptArr = Object.groupByArray(people, p => p.dept);
// [ { key: 'Engineering', values: [...] }, { key: 'Marketing', values: [...] } ]

// Array.groupBy β€” returns array of groups
const grouped = people.toGrouped(p => p.dept);
```

### `Set` Methods: `.intersection()`, `.union()`, `.difference()`, `.symmetricDifference()`

```javascript
const a = new Set([1, 2, 3, 4]);
const b = new Set([3, 4, 5, 6]);

a.intersection(b);           // Set { 3, 4 }
a.union(b);                  // Set { 1, 2, 3, 4, 5, 6 }
a.difference(b);             // Set { 1, 2 }
a.symmetricDifference(b);    // Set { 1, 2, 5, 6 }

// Also: isSubsetOf, isSupersetOf, isDisjointFrom
new Set([1, 2]).isSubsetOf(new Set([1, 2, 3]));   // true
new Set([1, 2]).isDisjointFrom(new Set([3, 4]));   // true
```

### `Map.groupBy()`

```javascript
const users = [
  { name: 'Alice', active: true },
  { name: 'Bob', active: false },
  { name: 'Carol', active: true },
];

const byStatus = Map.groupBy(users, u => u.active ? 'active' : 'inactive');
// Map { 'active' => [{...}, {...}], 'inactive' => [{...}] }
```

### `String.isWellFormed()` and `toWellFormed()`

```javascript
// Check for unpaired surrogates
"\uD800".isWellFormed();      // false
"Hello".isWellFormed();       // true

// Replace unpaired surrogates with U+FFFD
"\uD800 Hello".toWellFormed(); // "οΏ½ Hello"
```

### `ArrayBuffer.prototype.transfer()`

```javascript
// Grow/shrink ArrayBuffer without copying
const original = new ArrayBuffer(8);
const grown = original.transfer(16);  // 16 bytes, original data preserved
// original is now detached (0 bytes)
```

---

## 7. Module Features

### Dynamic `import()`

```javascript
// Load module on demand
const module = await import('./heavy-module.js');

// Conditional loading
if (user.isPremium) {
  const premiumFeatures = await import('./premium.js');
}

// Load multiple in parallel
const [react, reactDOM] = await Promise.all([
  import('react'),
  import('react-dom'),
]);
```

### Import Assertions / Attributes

```javascript
// Import JSON with assertion
import config from './config.json' assert { type: 'json' };

// New syntax (ES2023+)
import config from './config.json' with { type: 'json' };
```

### Top-Level Await (Recap)

```javascript
// In ES modules
const data = await fetch('/api/data.json').then(r => r.json());
export default data;
```

---

## Quick Reference: ES Feature Timeline

| Feature | Year | Use Case |
|---------|------|----------|
| `?.` Optional chaining | 2020 | Safe property access |
| `??` Nullish coalescing | 2020 | Null/undefined defaults |
| `globalThis` | 2020 | Universal global object |
| `Promise.allSettled()` | 2020 | Wait all, collect results |
| `BigInt` | 2020 | Large integers |
| `replaceAll()` | 2021 | Replace all without regex |
| `Promise.any()` | 2021 | First successful promise |
| `??=` / `||=` / `&&=` | 2021 | Assignment short-circuit |
| `WeakRef` | 2021 | GC-friendly references |
| Top-level `await` | 2022 | Async at module scope |
| `.at(-1)` | 2022 | Negative indexing |
| Private fields `#` | 2022 | True class privacy |
| `Object.hasOwn()` | 2022 | Safe hasOwnProperty |
| `.toSorted()` / `.with()` | 2023 | Non-mutating arrays |
| `.findLast()` | 2023 | Search from end |
| `Promise.withResolvers()` | 2024 | Separate promise controls |
| `RegExp /d` flag | 2024 | Match indices |
| `Set.union()` etc. | 2025 | Set operations |
| `Object.groupBy()` | 2025 | Group collections |