お正月だョ!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