2023-09 の TC39 meeting
まとめ
決まったこと
Web compatibility issues / Needs Consensus PRs
Intl.DateTimeFormat のオプションで hour12 が設定されているときに hourCycle を適切に設定する Normative Change。承認された。
Intl がフィンガープリンティングにならないようにする Note が追加された。
Intl の丸め関係のオプションの設定順を修正する Normative Change。承認された。
RegExp#replace において第二引数に置換する文字列として2桁のインデックスを指定したときに、第一引数のキャプチャする数によって1桁として扱うことがある。仕様を Web Reality に合わせて修正する Normative Change。承認された。
code: js
// 12番目の J がキャプチャされ $12 を置換する
"ABCDEFGHIJ".replace(/(A)(B)(C)(D)(E)(F)(G)(H)(I)(J)/, "$12");
// => "J"
// 1番目の A がキャプチャされ $1 を置換する
"ABCDEFGHIJ".replace(/(A)(B)(C)(D)(E)(F)(G)(H)(I)/, "$12");
// => "A2"
ECMA404 においてタイムゾーン文字列として "+00" や "-23:59" のように UTC からのオフセットの文字列を許容する Normative Change。承認された。
Stage 4 (ES2024)
Chrome と Safari で実装され、無事 Stage 4 になった 🎉
Stage 3
Chrome 117 と Serenity で実装されている。Firefox 119 に ship 予定。このまま順調に行けば Stage 4 になる。
徐々に実装されている。
JSON 文字列から IEEE754 で丸めずに bigint を作れたり、JSON 文字列を作る際に再シリアライズされるのを防止したりする提案。
code: js
// JSON から bigint を作る
JSON.parse("9999999999999999", (key, value, { source }) => {
// 値は reviver 函数に渡る時点で丸められてしまう
console.log(value); // => 10000000000000000
// 新たに source テキストにアクセスできる
console.log(source); // => "9999999999999999"
return BigInt(source);
});
// bigint から JSON を作る
// 今までだと再シリアライズされてしまう
JSON.stringify(1n, (key, value) => String(value)); // => '"1"'
// JSON.rawJSON を使うと再シリアライズを抑制できる
JSON.stringify(1n, (key, value) => JSON.rawJSON(value)); // => "1"
実装可能状態にあることが共有された。
Import Attributes のキーで bigint と数値リテラルが許されなくなった。
%IteratorPrototype% をフリーズした状況で古い regenerator-runtime を使っている場合に break the web を起こしてしまう。やむを得ずアクセサプロパティにしてしまう話が出ている。
一旦次の会議まで保留された。
例: "en-u-fw-fri", "ar-SA-u-fw-sun"
Stage 2
HTML との統合を考えたときにどの DOM API が ShadowRealm で使えるべきかの議論、またそれらのテストがないと実装できないと SpiderMonkey チームから共有された。
そのため
HTML との統合を辞め、Stage 3 のままとする
Stage 2 にダウングレードして HTML 統合を進める
完全に諦める
のいずれかを選択することになり、Stage 2 にダウングレードされた。また Stage 3 にするには ShadowRealm に入れる Web API のリスト、そしてテストが必要とのこと。
Symbol.isWellKnownSymbol について懸念が出ている。
code: js
const x = asyncIteratorOfUrls.map(u => fetch(u));
await Promise.all([
x.next(),
x.next(),
]);
の代わりに
code: js
asyncIteratorOfUrls
.map(u => fetch(u))
.buffered(2);
と出来る AsyncIterator#buffered を導入する話が出ている。また done: true を返したあとに next メソッドを実行すると例外を出すような”行儀の悪いイテレーター“の考慮をなくすことで仕様を簡略化出来るのではないかという話も出ている。
Promise オブジェクト自体ではなく、microtask が作られたときにキャプチャーされる。よって unhandled rejection では以下のようになる。
code: js
const asyncVar = new AsyncContext.Variable();
asyncVar.run("reg", () => {
addEventListener('unhandledrejection', () => {
console.log(asyncVar.get()); // => call
});
});
let reject;
asyncVar.run("init", () => {
new Promise((_, rej) => {
reject = rej;
});
});
asyncVar.run("call", () => {
reject('boom');
});
Async Generator Function 内では Generator が初期化されたタイミングでキャプチャーされる。
code: js
const asyncVar = new AsyncContext.Variable();
async function* gen() {
await 1;
asyncVar.get(); // => init
yield;
await 1;
asyncVar.get(); // => init
}
let g;
asyncVar.run('init', () => {
g = gen();
});
await asyncVar.run('first iter', async () => {
await g.next();
});
await asyncVar.run('second iter', async () => {
await g.next();
});
今後は HTML との統合、V8 での実験的実装、Node.js の AsyncLocalStorage との互換性周りを進めていく。
throw 式を導入する提案。文と式で後ろの優先度が変わる話が共有された。式の場合 await と同じく先に実行される。
code: js
// 文
throw 1 + 2; // => throws 3
// 式
(throw 1 + 2); // throws 1
また ASI を避けるために Early Error を出す議論もされている(#19)。 code: js
a ?? throw b // <- ASI inserts semicolon
+ c // <- prefix '+'
// or
throw b // <- no ASI
+ c
Stage 3 にはならなかった。
ストリーム周りで議論が続いている。最悪 WHATWG Streams として進める。Modabble XS が困りそう。 特定の文字だけエスケープするのは将来性があるのか懸念がある。
Stage 2 になった。
Stage 1
ユースケースを明確にして再度やり直す。
Get Intrinsics for stage 2, with two possible shapes/paths? | Jordan Harband そもそも JS から現状アクセスできない Intrinsics にもアクセスできるようになるため V8 チーム的に懐疑的に見ている。Stage 2 にはならなかった。
強い支持がある一方で、DataView#{get, set}Uint8 と同じものを入れることに対する懸念もある。次回に持ち越し。
Unicode の Message Format 2.0 (Draft) をもとに文字列を生成する提案。今回は1年ぶりの議題ということで提案が再確認された。
Fixed Layout Object としての Struct の提案。
code: js
struct class Box {
x;
constructor(x) { this.x = x; }
}
const box = new Box(42);
// 以下両方とも例外を投げる
box.y = 12;
box.__proto__ = {};
また Worker と共有する場合は shared struct を宣言する。
code: js
shared struct class SharedBox {
x;
}
V8 で Microsoft と Google がパートナーとして実験している状況が共有された。今後は TypeScript や Babylon.js そして Google Workspaces で試してみるとのこと。
IEEE754 decimal128 をデータモデルとした Decimal の提案。計算結果が NaN になる場合例外を投げる。
演算子オーバーロードがパフォーマンスの問題で辞めそうなため、新たにプリミティブにするモチベーションが無くなった。そのためリテラルも辞め、オブジェクトして追加する話になっている(演算はメソッドでやる)。
code: js
function calculateBill(items, tax) {
let total = new Decimal("0");
for (let { price, count } of items) {
total = total.add(price.times(new Decimal(count)));
}
return total.multiply(new Decimal(tax).add(1));
}
const items = [
{ price: "1.25", count: 5 },
{ price: "5.00", count: 1 },
];
const tax = "0.0735";
console.log(calculateBill(items, tax).toFixed(2));
今後は IEEE754 Decimal128 に準拠するのがゴールなのかどうかはっきりさせる。また sin, cos などが必要かどうかも議論される。
今後はシンタックスの簡略化を中心に行う。
複数の Iterator をまとめる提案。まだ仕様は固まっていないが、例えば Iterator.zip 函数を追加したとしたら以下のようになる。
code: js
}
Stage 1 になった。
複数の Iterator を結合する提案。まだ仕様は固まっていないが、例えば Iterator#concat メソッドを追加したとしたら以下のようになる。
code: js
console.log(a); // => "foo", "bar", "baz", 1, 2, 3
}
Stage 1 になった。
Intl.Locale で -u-fw 拡張が入ったが、他のロケール拡張も入れたい話。
https://gyazo.com/53e369e6ac578942c39f8c621d155197
Stage 1 になった。
!instanceof と !in 演算子を導入する提案。TypeScript の non-null 演算子と被ってしまう問題があることが共有された。Stage 1 になった。
Intl を使って文字列を生成する場合実装依存となる。例えば Date から "yyyy-mm-dd" のような文字列を作りたい場合、それは ECMAScript 的には提供されていない。
なおスタックオーバーフローでは "sv-SE" ロケールを使うというハックが紹介されていたりする。
code: js
// ICU 依存
new Intl.DateTimeFormat('sv-SE').format(new Date()) // => '2023-09-26'
そこで ISO 639.2 / BCP 47 で定義される "zxx" (null) ロケールを追加し、“標準の文字列”を生成するようにする提案。
code: js
new Intl.DateTimeFormat('zxx').format(new Date()); // => '2023-09-01'
(12345.67).toLocaleString(null); // => '12345.67'
Stage 1 になった。
Withdrawn
FinalizationRegistry#cleanupSome メソッドは同期的に回収可能なオブジェクトを GC に回収させるためのメソッド。
歴史的には Mozilla が WebAssembly のゲームを作るに当たって強く要望した機能だったが、その後 WebAssembly のコルーチンの提案(JavaScript Promise Integration)に代替されて、FinalizationRegistry 自体の提案からスプリットされて Stage 2 で停滞していた。
今回 withdraw が決まった。
ずっと Stage 0 に残っていたが辞める。
その他
TDZ, what is it good for? (slides) | Shu-yu Guo let, const の初期化前に変数にアクセスすると例外を投げる Temporal Dead Zone だが、V8 から改めてパフォーマンスに問題があることが共有された。var と同じように初期化前にアクセスすると undefined として扱いたいとのこと。今後また議論するかもしれない。
reducing wasted effort due to proposal churn (continued) (slides) | Michael Ficarra 前回とは打って変わって Stage 2 と 3 の間に新たに追加するということになった。今 Stage 3 のものでコンセンサスが得られたものはその新たな Stage に降格される。名前は未定。
Stop coercing things pt 2 (slides) | Kevin Gibbons 今後新しい提案について整数を受け取る API に非整数を渡すと例外を投げる様になる。オブジェクトとプリミティブ間や、プリミティブ間同士の変換については未定。
総括