2020-02 の TC39 meeting
まとめ
決まったこと
Web compatibility issues / Needs Consensus PRs
Atomics.store のために ToInteger を変更する提案。承認された。
Proxy のコンストラクタの引数に revoked Proxy を入れたときに TypeError を投げる仕様になっているが、ただ複雑になっているだけで特に意味がないので取りやめる提案。承認された。
Stage 4 (ES2020)
Intl.NumberFormat と Number#toLocaleString にオプションに単位を追加する提案。Stage 4 になった 🎉
locale の文字列を組み立てるための API。Stage 4 になった 🎉
Stage 3
Moddable XS と SpiderMonkey, V8 にフラグ付きで実装されている状態。liveness の追加など細かな修正がなされた。修正にあたって FinalizationGroup をより明確な FinalizationRegistry にしてはどうかという話がなされている。
Stage 2
Realm の更新情報。どうやらコンストラクタのオプションで thisValue を設定できるようになったっぽい。
code: (ts)
interface RealmInit {
thisValue?: object;
}
interface Realm {
readonly global: typeof globalThis;
readonly thisValue: typeof globalThis | object;
evaluate(sourceText: string): any;
intrinsics(): Record<string, any>;
}
declare var Realm: {
prototype: Realm;
new(options?: RealmInit): Realm;
};
Stage 3 にするべくレビューが始まった。
文字列を区切るときに
code: (js)
// バイト単位(code unit)
// code point
のような区切り方はサポートされているが、grapheme (cluster), word そして sentence 単位ではサポートしていない。Unicode の UAX29 を元にそれらをサポートする提案。 code: (js)
// grapheme cluster
const graphemeSegmenter = new Intl.Segmenter(locale, { granularity: "grapheme" });
今まで Intl.Segmenter#segment が直接 %SegmentIterator% を返していたところを、新たに %Segment% を間に挟む形で返すように変更がなされた。それに伴って SegmentIterator#{following, preceding} などのメソッドが廃止され、それらの責務は %Segment% が担うことになった。
code: (ts)
declare class Segment {
segment: string;
index: number;
// granularity が "word" でなければ undefined
isWordLike: boolean|undefined;
// index が code point に含まれる位置の Segment を返す
containing(index: number): Segment|undefined;
}
Temporal で任意のカレンダーを設定できるようにする提案。以下のような Calendar のインターフェースを定義し、デフォルトの ISO 以外の独自のカレンダーを作ることができる。
code: (ts)
interface Calendar {
///////////////////
// To/From ISO //
///////////////////
/** Returns the projection of self in the ISO calendar */
toISO(self: Temporal.Date): Temporal.Date;
/** Returns the projection of isoDate in the custom calendar */
fromISO(isoDate: Temporal.Date): Temporal.Date;
/** Constructs a Temporal.Date from a free-form option bag */
dateFromFields(fields: object): Temporal.Date;
/** A string identifier for this calendar */
id: string;
//////////////////
// Arithmetic //
//////////////////
/** Returns self plus duration according to the calendar rules. */
plus(
self: Temporal.Date,
duration: Temporal.Duration,
options: /* options bag */
): Temporal.Date;
/** Returns self minus duration according to the calendar rules. */
minus(
self: Temporal.Date,
duration: Temporal.Duration,
options: /* options bag */
): Temporal.Date;
/** Returns self minus other, which are dates in the same calendar. */
difference(
self: Temporal.Date,
other: Temporal.Date,
options: /* options bag */
): Temporal.Duration;
////////////////////////////////////
// Accessors: //
// Semantics defined in date.md //
////////////////////////////////////
year(self: Temporal.Date): number;
month(self: Temporal.Date): number;
day(self: Temporal.Date): number;
dayOfWeek(self: Temporal.Date): number;
weekOfYear(self: Temporal.Date): number;
daysInMonth(self: Temporal.Date): number;
daysInYear(self: Temporal.Date): number;
isLeapYear(self: Temporal.Date): boolean;
}
このときに Temporal.Date には以下のようにしてカレンダーを設定することができる。
code: (js)
// Force the Gregorian calendar:
Temporal.Date.from("2019-12-06").withCalendar("gregory").weekOfYear;
// Use a calendar from another source:
Temporal.Date.from("2019-12-06").withCalendar(Intl.defaultCalendar).weekOfYear;
Temporal.Date.from("2019-12-06").withCalendar(request.calendar).weekOfYear;
ところで今のところ ECMAScript にはインターフェースを言語でサポートしていない(Stage 1 First Class Protocol が提案されてはいる)ので、どのような形にするかが議論された。たとえば Temporal.Calendar.toISO を Well-known Symbols のように扱って文字列のキーの代わりにこっちを使うみたいなこともできる。 とりあえず文字列の方向になったらしい。
明示的にリソースを開放するシンタックスの追加提案。今まではブロックスコープを生成する構文だったが、ブロック内で使う構文に変更されるらしい。
code: (js)
// current syntax
try using (const foo = expr) {}
try using await (const bar = expr) {}
try using (expr) {}
try using await (expr) {}
// future syntax
{ using const foo = expr; }
{ using await const foo = expr; }
{ using value expr; }
{ using await value expr; }
このあたりは以前あった let (foo = expr) {} あたりの話題と似てそう。確かにわざわざスコープを作らなくていい。ただ using をつけ忘れそうな気もするが……。
また今までの提案だとクラスに @@dispose, @@asyncDispose メソッドが定義されていない場合には using 構文を使うことができないため、それらのメソッドを持つインスタンスを簡単に作れるクラスを入れる話になっている。
code: (js)
{
const file = openFile();
using value new Disposable(() => file.close());
...
}
これを使うことで特にインスタンスを操作せずにスコープを抜けるときの処理を書きやすくなる。Go 言語における defer とか D 言語における scope(exit) {} っぽい。インターフェースとしては以下の通りかな。
code: (ts)
interface IDisposable {
}
declare class Disposable {
static from(...disposables: IDisposable[]);
constructor(onDispose: () => any);
}
interface IAsyncDisposable {
}
declare class AsyncDisposable {
static from(...disposables: (IDisposable|IAsyncDisposable)[]);
constructor(onAsyncDispose: () => Promise<any>);
}
演算子として ||=, &&=, ??= を追加する提案。Stage 2 になった 🎉
Stage 1
Factory パターンでインスタンスの Promise を返す設計の場合、そのクラスを継承したサブクラスを作るのが難しい問題がある。クラスのコンストラクタを非同期に作ることができるとこの問題を解決する。
code: (js)
class X {
async constructor() {
await null;
}
}
class Y extends X {
async constructor() {
// bikeshed to have this be the instance
await.super();
}
}
うーん、気持ちはわからないでもないがあまり乗り気になれない……。賛否両論っぽいがとりあえず Stage 1 になった。
仮想化可能性を保持するための仕様を記述する提案。Stage 1 になった。
BigInt みたいに 0.1m + 0.2m で Decimal を扱える。BigDecimal にするか IEEE754-2008 の Decimal128 に寄せるかが議論された。Stage 1 になった。
Annex B で言及されている Sloppy mode における Function#{caller, arguments} がブラウザごとで動作が異なっているため、ちゃんと記述するようにする提案。Stage 1 になった。
Object.iterate{Keys, Values, Entries} を追加する提案。調査が必要ということになり Stage 2 にはならなかった。
Array#filter と対照的な Array#reject の提案。メソッド名として Array#filterOut はどうかみたいな話がされた。
BigInt などに対応するために JSON.parse へ渡す関数の引数を増やす提案。以前は source だけだったが色々と増えているっぽい。
code: (ts)
type JSONValue = string | number | boolean | null | { key: string: JSONValue } | JSONValue[]; interface ParseContext {
source: string;
index: number;
input: string;
keys: string[];
}
interface JSON {
parse(text: string, reviver: (key: string, value: JSONValue, context: ParseContext) => any);
}
Web 互換性のためにこのような形になっているがもっといい方法がないか模索することになった。
UUID の提案のために暗号論的疑似乱数生成器を ECMAScript にいれる提案。内部関数として FillRandomValues(view: TypedArray) を用意し、それを ArrayBuffer.fillRandom として公開するのはどうかという話がされた。Stage 1 になった。
TypedArray のコンストラクタの第四引数として stride を渡せるようにする提案。これを入れることによって例えば ImageData から RGBA それぞれの Uint8ClampedArray を作ることができるようになる。
code: (js)
const alphas = new Uint8ClampedArray(
imageData.buffer,
3 * Uint8Array.BYTES_PER_ELEMENT,
imageData.width * imageData.height,
/* stride */ 4
);
Stage 1 になった。
かつて Frozen Realms という仕様だったが Realms と独立された提案。
https://gyazo.com/92612438fa050015f316ac1dd1e55dffhttps://gyazo.com/137ecdc287fdb7507dcb0ac5439b44c7
SES は Strict Mode よりも厳しい実行環境と言える。
https://gyazo.com/7013096e4e4ed275114721af7e84b722
Realm.lockdown() を実行すると Function コンストラクタや Math.random, Date.now のような副作用を持つ組み込み函数が使えなくなり、プロトタイプ汚染されない Freezing intrinsics のみが共有される状態になる。その際に Compartment コンストラクタを呼び出すことができるようになり、安全に eval できるようになるらしい。
https://gyazo.com/f6d794b313cbc3d3c61f27837f38a10c
Originals では全ての組み込み函数を取得しに行かないといけなかったのに対して、こちらだとミスによる脆弱性は起きにくそう。 "1 hour, 46 minutes and 40 seconds" のような期間を表すことができる Intl.DurationFormat の提案。文字列を生成するときに Temporal.Duration を使うのが面白い。
code: (js)
const formatter = new Intl.DurationFormat('en-US', {
upperField: 'hour',
lowerField: 'second',
style: 'short',
round: true,
hideZeroValued: false,
hideUpperZeroValued: true,
});
const duration = Temporal.Duration.from({
hours: 2, minutes: 46, seconds: 40,
});
// output: 2 hr 46 min 40 sec
console.log(formatter.format(duration));
Stage 1 になった。
Module に属性を付与できる提案。将来的に Web Worker にも new Worker("foo.wasm", { as: "webassembly" }) のような形で入れられるのではという話がされた。
Stage 0
函数内で this に明示的に別の名前を付けられるようにする提案。
code: (js)
function fullName(this) { // explicit this parameter
return ${this.firstName} ${this.lastName}
}
function fullName(this user) { // explicit naming this to user
return ${user.firstName} ${user.lastName}
}
function fullName(this {firstName, lastName}) { // destructuring
return ${firstName} ${lastName}
}
コンセンサスは得られなかった。
函数が this の引数を要求しているかどうかを得られる Function#thisArgumentExpected の提案。
class constructor => null
bound function => false
arrow function => false
explicit this (gilbert/es-explicit-this proposal) => true
implicit this (FunctionBody contains this or super.foo) => true
otherwise => false
こちらもコンセンサスは得られなかった。もっと明確なモチベーションが提示されたら Stage 1 になるかもしれない。
その他
総括
今回は Async Initializasion や Decimals といった新しい提案がチラホラあったものの、どちらかと言えば既存の提案の更新状況が多く議題にあがっていた。WeakRefs や Realms, そして Explicit Resourse Management あたりは使い勝手が良さそうなこともあり、進捗が見られてよかったように思う。