お正月だョ!ECMAScript Proposal全員集合!!
新年あけましておめでとうございます。カブクで主に何もしないをしているあんどうです。
いまさらで少々恥ずかしいんですが、つい先日ECMAScriptにパイプライン演算子という変態を感じる仕様が提案されていることを知り、他にどんな変態仕様が提案されているのか興味が湧いてきたので、2019年1月1日時点での全提案をざっと眺めてみました。
ECMAScriptの仕様策定プロセス
ECMAScriptの仕様策定プロセスはLiving Standardと呼ばれ、新しい仕様がオープンに提案、議論され、実装と検討が一定のステージに達したものが毎年一定のタイミングでFixされて正式な仕様として公開されます。ステージは0から4まであり、0が単なるアイデアの段階、4が正式な仕様、その間の1〜3が実際に検討が進められている提案です。
それらの提案はすべてそのステージや起案者、チャンピオンと共にGitHubリポジトリにまとめられています。今回は採用される可能性の高いステージ3から、これから正式に検討が始まる感じのステージ1までを順に紹介していきます。
ステージ3
ステージ3の提案は仕様候補で、構文やAPIなどもほぼ確定しています。また提案の仕様に準拠した実装もすでに存在します。このステージの提案は致命的な問題がなければ変更されません。
- globalThis
ブラウザでは
window、ワーカーではself、nodeではglobalなど、環境により異なるグローバルオブジェクトに統一的にアクセスできるglobalThisオブジェクトを導入しようという提案- import()
現在、モジュールは
import文によって宣言的に取り込むことしかできないが、import()という関数(風の記法)によって動的に取り込めるようにしようという提案- RegExp.$1, RegExp.prototype.compile…
RegExp.$1やRegExp.prototype.compileなど、ECMA 262には含まれないRegExpの古い仕様を復活させようという提案- BigInt
253-1(Number.MAX_SAFE_INTEGER)よりも大きな整数を扱うためのプリミティブを導入しようという提案- import.meta
呼び出し元のスクリプト要素やモジュールのファイル名など、モジュールのメタ情報を取得できる
import.metaオブジェクトを導入しようという提案- プライベートメソッド、Getter/Setter
#というプレフィクスを使用してクラスにプライベートなメンバーを追加できるようにしようという提案。class Counter extends HTMLElement { #xValue = 0; get #x() { return #xValue; } set #x(value) { this.#xValue = value; window.requestAnimationFrame(this.#render.bind(this)); } #clicked() { this.#x++; } ... }- Array.prototype.flat, Array.prototyp.flatMap
ArrayオブジェクトにflatメソッドとflatMapメソッドを導入しようという提案。前者は入れ子になった配列を元に平坦な配列を返し、後者はmapの入力と結果を平坦な配列として返す。- クラスフィールド宣言
クラス定義でインスタンス変数を定義できるようにしようという提案
class Counter extends HTMLElement { x = 0; }- 静的メンバ
static修飾子を使用してクラスに静的メンバを追加できるようにしようという提案- String.prototype.trimStart, String.prototype.trimEnd
現在、メジャーなJSエンジンすべてで
String.prototype.trimLeft/trimRightが利用できるが、非標準である。これらと同様の機能を、すでに標準として存在するpadStart/padEndと合わせてtrimStart/trimEndとして実装しようという提案- String.prototype.matchAll
正規表現マッチでグローバルオプションと部分一致を組み合わせた場合に(例:
/t(e)(st(\d?))/g)、現状では部分一致を容易に得られないという問題がある。この問題に対応するためにmatchAllを導入しようという提案- Object.fromEntries
既存の
Object.entriesメソッドと対になる、[key, value]の配列からObjectを構築するObject.fromEntriesを導入しようという提案- 整形式のJSON.stringify
現在の
JSON.stringifyは結果にUTF-8に含まれないコードポイントを含むことがあるが、これはRFC8259で規定されるJSONとしては不正である。そのような文字をエスケープシーケンスを使用して表現するようにしようという提案- Hashbang(#!)
CLIから実行する際にインタプリタを指定できるようにファイルの冒頭にShebang(例、
#!/usr/bin/env node)を指定できるようにしようという提案
ステージ2
ステージ2の提案は草案です。構文やAPIなどの記述はありますが、TODOや仮の記述が残っています。また実装は実験的なものとして存在します。提案の内容は適宜変更されます。
- 数値セパレーター
桁数の大きな数値を読みやすくするために、数字間に
_を挿入できるようにしようという提案(例、100_000_000)- function.sent
ジェネレーター内で
function.sentを使用してnextメソッドの引数にアクセスできるようにしようという提案- デコレーター
クラス定義、クラスメンバ定義にデコレーターを使用できるようにする提案。
@defineElement('num-counter') class Counter extends HTMLElement { @observed #x = 0; @bound #clicked() { this.#x++; } .. }- throw式
式が要求されるコンテキストで
throwを利用できるようにしようという提案。(例、function save(filename = throw new TypeError("Argument required")) { ... })- Atmics.asyncWait
アトミック演算を提供する
AtmicsオブジェクトにasyncWaitを導入しようという提案- WeakRef
弱参照を実現する
WeakCell/WeakRef/WeakFactoryを導入しようという提案- トップレベルawait
現状、
asyncコンテキスト内でしか使用できないawaitをそれ以外の場所でも利用できるようにしようという提案- Function.prototype.toString()の抑制
Function.prototype.toString()を使用すると関数のソースコードが取得できるがこれはセキュリティ上問題がある場合がある。この問題に対応するために"use no Function.prototype.toString"というプラグマを使用することでfn.toString()の結果を[native code]にできるようにしようという提案- Setにメソッド追加
Setクラスにintersection、unionなどの集合論に基づいた各種メソッドを導入しようという提案- Realm
プログラム内で独立した環境を実現できる
Realmオブジェクトを導入しようという提案- ArrayBuffer.prototype.transfer()
ArrayBufferにその所有権を移動するtransfer()メソッドを導入しようという提案- RegExp.prototype.exec()
RegExp.prototype.exec()にオプショナルな第二引数として正規表現にマッチするたびに部分文字列のインデックスを引数として呼び出されるコールバック関数を導入しようという提案- Unicodeプロパティエスケープで複数コードポイントからなる文字のサポート
正規表現で
\p{...}, \P{...}という形式でUnicodeプロパティエスケープが利用できるが、これは内部的にコードポイントのリストに展開される。そのため現状では複数コードポイントからなる文字は複数の候補の集まりとして表現するしかない。この問題に対応するため複数コードポイントからなる文字用のUnicodeシーケンスプロパティエスケープを導入しようという提案- InterpreterDirective
ステージ3のHashbang(#!)と同じ提案?
- Temporal
Dateオブジェクトの代替となる、よりモダンなAPIを持つCivilDate/CivilTime/CivilDateTime/Instant/ZonedInstantオブジェクトの提案
ステージ1
ステージ1はただの提案です。構文やAPIなどの詳細な記述は存在しない場合があります。また実装は単純なデモやPolyfillとして提供されます。
- export v from “mod”;
インポートしたモジュールのデフォルトオブジェクトをエクスポートする構文の提案
- Observable
Observable/Observerオブジェクトの提案- Frozen Realm API
?
- Mathオブジェクトの拡張
Mathオブジェクトにclamp/scake/radians/degreesを導入しようという提案- コレクションオブジェクトのof/fromメソッド
コレクションオブジェクトを他のコレクションオブジェクトから作成する手段として
of/fromメソッドを導入しようという提案- ジェネレータアロー関数
ジェネレーターを返すアロー関数、
=>*を導入しようという提案- Promise.try
Promiseでも通常の関数でも同期的に実行し、例外が発生した場合はPromise.prototype.catchで処理できるPromise.tryの提案- オプショナルチェーン
結果が
null/undefinedでなければ、その結果のプロパティにアクセスする?.記法の提案(例、user.address?.street)- Math.signbit
MathオブジェクトにIEEE 754の符号ビットを返すsignbitメソッドを導入しようという提案- エラースタック
Errorオブジェクトにstackプロパティを導入しようという提案- do式
ブロックを実行してその結果を返す
do式を導入しようという提案let x = do { let tmp = f(); tmp * tmp + 1 };- Float16Array
半精度浮動小数点数を扱う
Float16Arrayを導入しようという提案- Change Number.parseInt/parseFloat to not coerce null/undefined/NaN (repo link TBD)
Number.parseInt/parseFloatがnull/undefined/NaNを返さないようにしようという提案(多分)- バイナリASTフォーマット
ダウンロードサイズを小さくするためにJSのバイナリASTフォーマットを規定しようという提案
- パイプライン演算子
パイプライン演算子
|>を導入しようという提案let newScore = person.score |> double |> (_ => add(7, _)) |> (_ => boundScore(0, 100, _));- 数値リテラル拡張
3_pxなどのように_を挟んで数値に単位を付けられるようにしようという提案- First-Class Protocol
mixinのような
protocolを導入しようという提案- ??演算子
||とほぼ同じだが、左辺値がnullとundefinedの場合にだけ右辺値が評価される??演算子を導入しようという提案(||は左辺値が0や""の場合にも右辺値が評価される)- ?を使用した関数の部分適用
const addOne = add(1, ?);のように、?を使用して関数の部分適用を可能にしようという提案- Cancellation API
非同期処理のキャンセルを簡単に実現できる仕組みを導入しようという提案
- String.prototype.replaceAll
文字列の一括置換を実行する
String.prototype.replaceAllを導入しようという提案- String.prototype.codePoints
コードポイントのサイズは一定ではないので、
codePointAtを使用して文字列のすべてのコードポイントを得るにはコードポイントの値に合わせてインクリメントする幅を切り替える必要がある。この問題に対応するため、コードポイントをイテレートするためのString.prototype.codePointsを導入しようという提案- Object.freeze/Object.seal構文
Object.freeze(例、const foo={# a:1 #};)とObject.seal(例、const foo={| a:1 |};)を簡単に実現する構文を導入しようという提案- ブロックパラメーター
Rubyのブロックパラメーターを導入しようという提案
- BigInt.fromString, Number.fromString
BigInt.fromStringとNumber.fromStringを導入しようという提案- Math.seededRandoms
Math.randomは乱数シードを自動的に生成するが、そうではなく、指定した乱数シードを使用するMath.seededPRNG({seed})を導入しようという提案- mixin
mixinを簡単に実現する構文を導入しようという提案。First-Class Protocolと問題領域は同じ
- Array.prototype.lastItem
配列の最後の要素を取得する
Array.prototype.lastItemを導入しようという提案- 新しいコレクションメソッド
MapやSetにArrayと同じfilter/map/find...などのメソッドを導入しようという提案- オブジェクトの複合キー
Symbol.compositeKey(...values)を使用してオブジェクトのプロパティ名として複合キーを使用できるようにするという提案const key = Symbol.compositeKey(A, B); const o = { [key]:1 };- スライス記法
Pythonのようなスライス記法を導入しようという提案(例、
arr[1:3])- ||=, &&=
Rubyのような
||=を導入しようという提案- Module Keys
モジュール間でセキュアな通信チャネルを可能にするためのAPIの提案?
- Class Static Block
クラスにstaticコンテキストで実行される初期化ブロックを導入しようという提案
- クラスプロパティアクセス式
class.static_member()の形式でクラスの静的メンバや無名クラスのスタティックメンバにアクセスできるようにしようという提案- パターンマッチ
次のような構文でパターンマッチを可能にしようという提案
case (res) { when {status: 200, headers: {'Content-Length': s}} -> { console.log(`size is ${s}`) } when {status: 404} -> { console.log('JSON not found') } when {status} if (status >= 400) -> { throw new RequestError(res) } }- 明示的なリソース管理
リソースの利用と解放を指定する
usingブロックを導入しようという提案using (const handle = acquireFileHandle()) { // リソース初期化 ... } // 解放- 動的モジュール
ファイル単位ではなく、プログラムでモジュールを定義する手段を導入しようという提案?
- 標準ライブラリ
標準ライブラリを導入し、
import { Date } from "std:Date";のように利用できるようにしようという提案- “use module”;
ソースコードがモジュールであることを示す
"use module";プラグマを導入しようという提案- for-inの調査
for-inの仕様が曖昧な部分をいろいろと確認している?
- Date.parseフォーマットの標準化
Date.parseが受け付けられる文字列のフォーマットを標準化しようという提案- JSON.parse
例えば
"999999999999999999", "999999999999999999.0", "1000000000000000000"がすべて1000000000000000000と解釈されるように、JSON.parseを通すことで失われる情報がある。この問題に対応するため、JSON.parseで元の文字列を取得できるようにしようという提案- Promise.allSettled
Promiseの配列を受け取り、すべてが完了したときに結果の配列を返すPromise.allSettledを導入しようという提案- Interface Description Language (IDL)
ECMAScript標準の中で使用されているIDLの調査?
- asset文
モジュールを実際には読み込まず、その利用だけを明記する
asset文を導入しようという提案。実際のモジュール読込は別提案である動的importで行うことになる(例、asset Foo from "foo";)
まとめ
PythonやRubyなどの他言語を参考にしたささいな便利機能の提案もそれなりに見られ※1、なんとなく思っていたほど提案のハードルは高くなさそうという印象を受けました。いま私にはかつてのようにどうでもいい言語仕様を提案してみたいという衝動がふつふつと湧いて来ています。
期待していた変態仕様についてはやはりパイプライン演算子が白眉で、それ以外は大したことがなさそうです(E4X並の変態仕様にまた出会いたい・・・)。プライベートメソッドやHashbang、Object.freeze/Object.seal構文などで(なんとなく)禁断の#が多用されていることからわかるように、変態文法に使用できる文字がもう余っていないのかもしれません。
ざっと提案内容全体を見ると、Javaのようなオブジェクト指向言語の方向性を伸ばしたい勢力※2と関数型言語方面の機能を追加したい勢力※3がせめぎ合いつつ前者が有力というところでしょうか。無軌道に仕様が膨らんでも誰も幸せにならないのでJavaScript Standard Libraryの提案を採用して、なんでも仕様に取り込むのではなくライブラリに切り出せるものはライブラリ側で実現されるのが個人的には理想です。とはいえ、互換性の維持を考えると実際にはその方向は難しいのかもしれません。
株式会社カブクではECMAScriptに変態文法を提案したいフロントエンドエンジニアを募集しています。
その他の記事
Other Articles


関連職種
Recruit


