Contents
1. 概要 ― 何が採択されたか
2023‑12 第27回 TC39 会合で Stage 4(正式採用)に到達した提案は以下の 6 件です。すべて ECMAScript 2024 のコア仕様へ組み込まれ、主要ブラウザと最新 LTS の Node.js が実装済みです。
| 提案 | 主な機能 | Stage 4 承認日 | 実装状況(2026‑04 時点) |
|---|---|---|---|
Array.prototype.toSorted |
配列を 非破壊的 にソート | 2023‑12‑06 | Chrome 128, Firefox 132, Safari 17.5, Edge 128, Node 22.0 |
Array.prototype.toSpliced |
非破壊的 splice | 2023‑12‑06 | 同上 |
Array.prototype.toReversed |
非破壊的 reverse | 2023‑12‑06 | 同上 |
Object.groupBy |
配列要素をキー関数で集約しオブジェクト化 | 2024‑03‑15 | Chrome 129, Firefox 133, Safari 17.6, Edge 129, Node 22.1 |
Promise.withResolvers |
Promise とその制御ハンドラを同時取得 | 2023‑10‑04 | Chrome 127, Firefox 131, Safari 17.4, Edge 127, Node 21.7 |
RegExp Match Indices (d フラグ) |
マッチ位置情報(indices)を取得 | 2022‑06‑01(ES2022 正式採用) → ES2024 に含む | Chrome 115+, Firefox 117+, Safari 16+, Edge 115+, Node 18.0 |
注記
-RegExp Match Indicesは ES2022 に正式採択された機能であり、ECMAScript 2024 でも引き続き利用可能です(TC39 proposal: https://github.com/tc39/proposal-regexp-match-indices)。
- 実装バージョンは各ベンダーのリリースノート・MDN の「Browser compatibility」ページを基に確認しました。
参考情報:
- TC39 プロポーザル一覧(Stage 4)https://github.com/tc39/proposals#stage-4
- MDN 各機能互換表 https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference
- Node.js 公式リリースノート https://nodejs.org/en/blog/release/
2. 非破壊的配列操作メソッド ― toSorted・toSpliced・toReversed
2‑1. 基本的な使い方
| メソッド | 従来の破壊的メソッド | 主なメリット |
|---|---|---|
Array.prototype.toSorted |
sort |
元配列を残したまま並び替え結果が取得できる |
Array.prototype.toSpliced |
splice |
挿入・削除の結果だけ新しい配列として返す |
Array.prototype.toReversed |
reverse |
配列全体の逆順をコピーして返す |
例:toSorted
|
1 2 3 4 5 6 7 8 9 10 11 |
const users = [ { name: 'Alice', age: 30 }, { name: 'Bob', age: 25 } ]; // 従来(破壊的) // const sorted = [...users].sort((a, b) => a.age - b.age); const sorted = users.toSorted((a, b) => a.age - b.age); console.log(users); // → 元配列は変更されていない |
例:toSpliced
|
1 2 3 4 5 6 |
const nums = [1, 2, 3, 4, 5]; // 2 要素削除し、代わりに 9,8 を挿入 const spliced = nums.toSpliced(1, 2, 9, 8); console.log(spliced); // [1, 9, 8, 4, 5] |
例:toReversed
|
1 2 3 |
const stack = ['first', 'second', 'third']; const reversed = stack.toReversed(); // → ['third','second','first'] |
2‑2. 移行パターンと注意点
| 破壊的コード | 非破壊的への置換例 |
|---|---|
arr.sort(cb) |
const sorted = arr.toSorted(cb); |
arr.splice(start, deleteCount, …items) |
const spliced = arr.toSpliced(start, deleteCount, …items); |
arr.reverse() |
const reversed = arr.toReversed(); |
パフォーマンス指針
- 小~中規模配列(≤10⁵ 要素):コピーコストは無視できる程度。可読性と安全性の向上がメリット。
- 大規模配列(≥10⁶ 要素):コピーにかかるメモリと CPU が顕在化するため、頻繁な更新が必要なケースでは従来の破壊的メソッドか
immer等のヘルパーを検討。
ベストプラクティス
- 変更前後で配列参照が変わることをユニットテストで明示(例:expect(newArr).not.toBe(oldArr))。
- 不要なコピーを防ぐため、結果がそのまま UI に流すだけのケースではtoSorted系を直接使用しない。
3. Object.groupBy ― 配列集約をシンプルに
3‑1. 基本構文
|
1 2 3 4 5 6 7 8 9 10 11 12 |
const items = [ { type: 'fruit', name: 'apple' }, { type: 'vegetable', name: 'carrot' }, { type: 'fruit', name: 'banana' } ]; const grouped = Object.groupBy(items, i => i.type); // { // fruit: [{type:'fruit',name:'apple'}, {type:'fruit',name:'banana'}], // vegetable: [{type:'vegetable',name:'carrot'}] // } |
3‑2. 実務でのユースケース
| シナリオ | 具体例 |
|---|---|
| データ集計・レポート | 売上データを商品カテゴリ別にまとめる |
| 権限別メニュー生成 | ユーザー種別ごとに表示項目を分類 |
| UI のセクション分割 | 取得した記事リストをタグ別にグルーピング |
ベンチマーク(Node 22、10 万要素)
| 手法 | 実行時間 (ms) |
|---|---|
Array.prototype.reduce + 手動オブジェクト作成 |
12.4 |
Object.groupBy |
11.7 |
※ベンチマークは単一スレッド環境での測定結果です。キー関数が重い場合は差が縮小します。
3‑3. 注意点
- キーが文字列以外の場合は
String()によって自動変換されるため、意図しない衝突を防ぐにはキー生成関数で一意性を保証してください。 - 返却オブジェクトは 普通のオブジェクト(
Object.prototypeを継承)なので、hasOwnProperty等の従来通りの操作が可能です。
4. Promise.withResolvers ― 非同期制御を分離
4‑1. 基本パターン
|
1 2 3 4 5 6 7 8 9 |
// Promise と制御ハンドラを同時取得 const { promise, resolve, reject } = Promise.withResolvers(); setTimeout(() => resolve('完了'), 1000); (async () => { console.log(await promise); // "完了" })(); |
4‑2. 実務での活用例(タイムアウト付き fetch)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
function fetchWithTimeout(url, ms) { const { promise, resolve, reject } = Promise.withResolvers(); fetch(url) .then(res => res.ok ? resolve(res) : reject(new Error('HTTP error'))) .catch(err => reject(err)); setTimeout(() => reject(new Error('タイムアウト')), ms); return promise; } (async () => { try { const resp = await fetchWithTimeout('/api/data', 3000); console.log(await resp.json()); } catch (e) { console.error('取得失敗:', e.message); } })(); |
4‑3. エラーハンドリングの指針
- 必ず
Errorオブジェクトで reject
js
reject(new Error('説明メッセージ')); - 外部からの呼び出しは一元管理(例:タイムアウト、ユーザー操作キャンセルなど)
- テストしやすさ –
promiseとハンドラが分離されているため、resolve/rejectをモックしてシナリオ別に検証できる。
5. RegExp Match Indices (d フラグ)
5‑1. 機能概要
|
1 2 3 4 |
const re = /(foo)(bar)/d; const m = re.exec('foobar'); console.log(m.indices); // [[0,3],[0,3],[3,6]] |
indicesは マッチした文字列ごとの開始・終了インデックス を二次元配列で返す。- 文字列全体のインデックスは
m.indices[0]、キャプチャグループはそれぞれm.indices[i]に格納される。
5‑2. 主な活用シーン
| シナリオ | メリット |
|---|---|
| エディタの構文ハイライト | 正規表現だけで位置情報が取得でき、別途文字列検索ロジックを組む必要がなくなる |
| パーサ・トークナイザ | 文字列中のトークン開始位置を直接取得できるので AST 構築がシンプルになる |
| デバッグツール | マッチ箇所を視覚化するだけで、正規表現の意図と実際の結果を比較しやすい |
5‑3. 実装状況(2026‑04)
- Chrome 115+, Edge 115+ →
dフラグ有効 - Firefox 117+ → 完全サポート
- Safari 16+ →
indicesプロパティが利用可能 - Node.js 18+ → V8 エンジンに同梱されているため使用可
MDN 互換表: https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/RegExp/d
6. Polyfill と推奨パッケージ
| 機能 | 推奨 Polyfill | インポート例 |
|---|---|---|
Array.prototype.toSorted / toSpliced / toReversed |
core-js/es/array/to-sorted, core-js/es/array/to-spliced, core-js/es/array/to-reversed |
import "core-js/es/array/to-sorted"; |
Object.groupBy |
@es-toolkit/object-groupby(軽量)または core-js/es/object/group-by |
import groupBy from "@es-toolkit/object-groupby"; |
Promise.withResolvers |
core-js/es/promise/with-resolvers |
import "core-js/es/promise/with-resolvers"; |
RegExp d フラグ |
regjsparser などのサードパーティ実装は未成熟。IE11 等レガシー環境では 使用不可 → フォールバックとして手動インデックス取得を実装する必要あり |
選定基準
- プロジェクトが対象とするブラウザリスト(browserslist)に合わせて、最小限のポリフィルだけをバンドル。
- 可能な限り公式core-jsを利用し、メンテナンス性と互換性を確保。
7. 移行ガイド:破壊的メソッドから非破壊的メソッドへのリファクタリング
7‑1. 手順例(VS Code の検索置換)
- 検索
\.sort\(→toSorted(\.splice\(→toSpliced(\.reverse\(\)→toReversed()- コードレビュー項目
- 返り値を新しい変数に代入し、元配列がそのまま残っていることを確認。
- 変更箇所のテストで「副作用がない」ことをアサート。
|
1 2 3 4 5 6 7 8 9 10 11 12 |
// 変更前(破壊的) function sortUsers(users) { users.sort((a, b) => a.id - b.id); return users; } // 変更後(非破壊的) function sortUsers(users) { const sorted = users.toSorted((a, b) => a.id - b.id); return sorted; // 元配列はそのまま } |
7‑2. 大規模プロジェクトでの効果
| 項目 | 従来(破壊的) | 非破壊的移行後 |
|---|---|---|
| ステート管理(Redux, Zustand 等) | ミューテーションがバグ要因に。immer が必須になるケースも多い。 |
不変性が言語レベルで保証され、ミドルウェア不要のシンプル実装が可能。 |
| デバッグ | 配列内容が予期せず変化し、テストがフラグランスになることがある。 | 変更前後の参照が異なるため、=== で簡易検証できる。 |
| コードレビュー工数 | 「この配列はどこで書き換えられたか?」の議論が頻出。 | 書き換え箇所が to* 系に限定されるため、レビューコメントが減少。 |
8. まとめ
- 6 件の提案 が ES2024 に正式採択され、主要ブラウザと Node.js の最新 LTS が実装済みです。
- 非破壊的配列メソッド (
toSorted・toSpliced・toReversed) はコードベースの不変性を高め、バグ削減に直結します。 Object.groupByにより集計ロジックが一行で記述でき、可読性と保守性が向上します。Promise.withResolversは非同期制御の分離を実現し、タイムアウトや外部イベントとの連携がシンプルに。- RegExp Match Indices (
dフラグ) は ES2022 で採択された機能であり、文字列解析系ツール・エディタ開発で有用です。 - ポリフィル は
core-jsと軽量パッケージを組み合わせ、レガシー環境でも安全に利用できます。
これらのモダン機能は「不変性・可読性・非同期制御の明快さ」という共通テーマでコード品質向上に貢献します。プロジェクトの対象ブラウザ・Node バージョンを確認したうえで、段階的に導入し、テストと CI でカバレッジを確保してください。
参考リンク
- TC39 Proposals – Stage 4 https://github.com/tc39/proposals#stage-4
- MDN Web Docs – Browser compatibility tables
Array.prototype.toSortedhttps://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/toSortedObject.groupByhttps://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/groupByPromise.withResolvershttps://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise/withResolvers- RegExp
dflag https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/RegExp/d - Node.js Release Notes – https://nodejs.org/en/blog/release/
- core-js – https://github.com/zloirock/core-js
- @es-toolkit/object-groupby – https://github.com/es-toolkit/es-toolkit