8月21日、JSDev.spaceが「The Future of JavaScript: What Awaits Us」と題した記事を公開した。この記事では、ECMAScriptの将来動向と、第108回TC39会議で前進した各提案の内容と影響について詳しく紹介されている。以下に、その内容を紹介する。

本記事は、以下のエキスパートに監修していただきました:
監修者からのコメント
ここの記事に紹介されているものの中で注目されているものは using でしょうか。記事に記載されている通り、 C#ではすでに実装済みの機能で、スコープを抜けた時にクリーンナップするための関数を呼ぶ仕組みです。これにより、リソースの回収漏れを防げる効果があります。
C#は実は最初の実装は using をブロックといっしょに定義してその中でリソースを開放させるという仕組みでした。近年になり、変数宣言といっしょに使う方法が増えました。
using (var file = new StreamWriter("test.txt"))
{
file.WriteLine("Hello");
} // ブロック終端で Dispose() が呼ばれる
void DoSomething()
{
using var file = new StreamWriter("a.txt");
file.WriteLine("Hello");
} // 関数スコープの終端で Dispose() が呼ばれる
つまり、JavaScriptのusingもこのあとから追加された方を参考にした機能です。宣言とスコープが強く結びつきやすいという側面はありますが、余計なブロックが減って見やすい記法になったかなと思います。
こういったいいところどりを常に後からしていく姿勢がJavaScriptの面白いところですね。
そんなJavaScriptの仕様を決めているTC39ですが、JSConf.jpにも来ることになっているので、興味がある方はぜひ参加してください。
概要
JavaScriptは、TC39というワーキンググループが策定する仕様に追随する形で実装されており、着実な進化を続けている。直近の第108回TC39会議(2025年5月28~30日に開催、議事録は未公開)では、初期段階(Stage 0)から最終段階(Stage 4)まで、9件の提案が各ステージで前進した。この記事は、近く利用可能になる機能、実験段階のドラフト、そして開発体験に与える影響を整理したものである。
Stage 4(仕様議論完了・複数のJavaScriptエンジンに組み込み済み)
Explicit Resource Management(using
)
using
(および非同期版のawait using
)により、スコープを抜けたときに確実にリソースを解放する決定論的クリーンアップが実現する。C#やPythonの発想を取り込み、オブジェクトが[Symbol.dispose]()
または[Symbol.asyncDispose]()
を実装していれば、自動的に後始末が行われる仕様である。
class FileHandle {
constructor(name) {
this.name = name; // open file...
}
[Symbol.dispose]() {
console.log(`${this.name} closed`);
}
}
function readFile() {
{
using file = new FileHandle("data.txt");
// File operations...
}
// file.[Symbol.dispose]() is automatically called here
}
Chrome 134、Firefox 134、Deno v2.3で既にサポート済みだ。
Array.fromAsync
Array.fromAsync
は、非同期で値を順番に返す「async iterable(非同期イテレーター)」を一気に処理して、その結果をまとめた配列を返してくれる。結果はPromiseとして返されるので、awaitで待てば最終的に普通の配列として扱える。
async function* generate() {
yield await Promise.resolve(1);
yield await Promise.resolve(2);
}
const nums = await Array.fromAsync(generate());
console.log(nums); // [1, 2]
主要ブラウザ、Deno v1.38、Node.js v22でサポート済みである。
Error.isError
Error.isError
は、「その値が本当にエラーオブジェクトなのかどうか」を確実に判定できる新しい方法だ。これまでの instanceof Error
では、フレームや実行環境(iframe、Web Worker など)が違うと正しく判定できないことがあったが、Error.isErrorならそうしたケースでも正しく判定できる。
Error.isError(new TypeError("oops")); // true
Error.isError({ name: "TypeError", message: "oops" }); // false
主要ブラウザとDeno v2.2で利用可能だ。
Stage 3(候補機能)
Immutable ArrayBuffer
transferToImmutable()
やsliceToImmutable()
を使うと、ArrayBuffer
を「もう書き換えできない状態(イミュータブル)」に変換できる。これにより、一度作ったデータが別のスレッドやAPIに渡されたあとに勝手に変更される心配がなくなる。さらに、コピーを作らずにそのまま渡せるので、「もとの配列を破壊されないようコピーして渡す」という従来の手法に比べて、効率が大きく向上する。
let buf = new ArrayBuffer(100);
let imm = buf.transferToImmutable();
console.log(buf.byteLength, imm.byteLength); // 0, 100
imm[0] = 1; // ❌ TypeError
Stage 2(ドラフト)
Random.Seeded
Random.Seeded
を使うと、乱数を生成するときに「シード値(初期値)」を指定できる。これにより、毎回まったく同じ乱数の並びを再現できる。たとえばシミュレーションやゲームで同じ結果を再現したいとき、またテストで毎回同じ挙動を確認したいときに便利だ。
const prng = new Random.Seeded(42);
console.log(prng.random()); // same sequence every run
Number.prototype.clamp
Number.prototype.clamp
は、ある数値を「最小値と最大値の範囲内」に収めてくれるメソッドだ。たとえば (-5).clamp(0, 10)
は 0
、(15).clamp(0, 10)
は 10
になり、範囲外の値を自動的に端の値に丸めてくれる。従来は Math.min(Math.max(x, min), max)
と書く必要があった処理を、一行でわかりやすく書けるようになる。
(5).clamp(0, 10); // 5
(-5).clamp(0, 10); // 0
(15).clamp(0, 10); // 10
Stage 1(初期提案)
Intl.NumberFormatの末尾ゼロ制御
trailingZeroDisplay
オプションを使うと、小数点以下の「0」をどう表示するかを選べる。たとえば 1.50
のように小数点以下の桁数を揃えて表示したり、逆に整数のときは 2.00
ではなく 2 とシンプルに表示したりできる。用途に応じて「見やすい数値フォーマット」を選べるのがポイントだ。
new Intl.NumberFormat("en", {
minimumFractionDigits: 2,
trailingZeroDisplay: "auto"
}).format(1.5); // "1.50"
new Intl.NumberFormat("en", {
trailingZeroDisplay: "stripIfInteger"
}).format(2); // "2"
Comparisons
「Comparisons」提案は、JavaScript標準に差分を取るAPIを導入しようという試みである。例えばテストで期待値と実際の値が違ったとき、「どこがどう変わったか」を標準の方法で出力できるようになることを目指している。
// 仮のAPIイメージ
Comparisons.diff({ a: 1, b: 2 }, { a: 1, b: 3 });
// => { b: { from: 2, to: 3 } }
Comparisons.diff([1, 2, 3], [1, 4, 3]);
// => { 1: { from: 2, to: 4 } }
これが実現すれば、Jestなどのテストフレームワークが持つ独自のdiff機能を共通化でき、ログ出力やデバッグでも「値がどこでどう変わったのか」を一貫した形で表示できるようになる。まだStage 1で具体的な仕様は固まっていないが、テストやデバッグ体験を大きく改善する可能性がある。
Random Namespace
数値や配列操作の便利な乱数ヘルパーをまとめた名前空間(Random)が提案されている。ランダムな整数生成や配列シャッフル、サンプリングなどを簡潔に記述できる。
// -5から5までの範囲でランダムな整数を返す
Random.int(-5, 5); // -1
// 配列からランダムに1要素を取り出す
Random.sample(["A", "B"]); // "B"
// 配列の要素をシャッフルする
Random.shuffle([1,2,3]); // [3,1,2]
何が変わるのか
TC39は、非同期処理の書き味改善、安全なデータ取り扱い、再現性、開発者の作業効率に焦点を当てて言語を進化させている。次回会合は9月下旬に予定され、各提案の継続審議が見込まれる。
詳細はThe Future of JavaScript: What Awaits Usを参照していただきたい。