ECMAScriptの最新動向 2021年11月版

※ 旧タイトル: TC39 meeting 86th の概要とステージの移動

TC39 の 86 回目のミーティングが 10/25 ~ 10/28 に開催されました。 このミーティングで議題に上がった提案と、そのステージの移動について紹介します。

for Stage 4

Error Cause

Stage 4 になりました。ECMAScript 2022 に入ります

Error Cause は、Error コンストラクタの第 2 引数に cause という値で原因となったエラーを渡すことができるようにする提案です。 キャッチする側では、error.cause で、そのエラーを取得できます。

例を示します。

doUploadJob 関数は fetch を実行して失敗したときに新しいエラーをスローします。そのエラーの第 2 引数に { cause: err } というオブジェクトを渡しています。

async function doUploadJob() {
  await fetch("https://example.com/upload").catch((err) => {
    throw new Error("Upload job result failed", { cause: err });
  });
}

try {
  await doJob();
} catch (e) {
  console.log(e);
  console.log("Caused by", e.cause);
}
// Error: Upload job result failed
// Caused by TypeError: Failed to fetch

doUploadJob がスローしているエラーメッセージは "Upload job result failed" ですが、その原因となったエラーを cause として渡すことで、キャッチする側でどのエラーが原因で失敗したのかを取得できます。

この例では、doUploadJob が失敗した原因が TypeError: Failed to fetch であったことがわかります。

for Stage 3

今回は、for Stage 3 の提案はありませんでした。

for Stage 2

Array Grouping

Stage 2 になりました

Array Grouping は、ArraygroupBy というインスタンスメソッドを追加する提案です。

ユースケースはLodash の groupBy と同様です。

const array = [1, 2, 3, 4, 5];
array.groupBy((i) => {
  return i % 2 === 0 ? "even" : "odd";
});
// =>  { odd: [1, 3, 5], even: [2, 4] }

Partial Application

Stage 2 になりませんでした

Partial Application は、関数の部分適用のための構文を導入します。

例を示します。

add は 2 つの引数を受け取り、その 2 つを足し合わせて返すだけの単純な関数です。 そして、Partial Application を使って addOne という新しい関数を作っています。addOne は、1 つの引数を受け取り、それに1を足して返す関数です。 つまり、既存の関数の一部の引数だけ渡して、残りの引数を受け取るような関数を作ることができます。

const add = (x, y) => x + y;
const addOne = add~(1, ?);
addOne(2); // 3

現在の JavaScript で表現すると、次のようになります。

const add = (x) => (y) => x + y;
const addOne = add(1);
addOne(2); // 3

Haskell のような関数型プログラミング言語では標準で備わっている機能です。

for Stage 1

String.cooked

Stage 1 になりました

String.cookedString に新しいスタティックメソッド cooked を追加する提案です。 String.cookedString.raw と逆のことをします。

String.raw`mmm ... \u0064elicious cooked string`;
// mmm ... \u0064elicious cooked string
String.cooked`mmm ... \u0064elicious cooked string`;
// "mmm ... delicious cooked string"

つまり、通常のテンプレートリテラルとおなじ挙動です。

`mmm ... \u0064elicious cooked string`;
// "mmm ... delicious cooked string"

この機能がタグ付きテンプレートリテラルとして存在することで、これを用いて新しいタグ付きテンプレートリテラルを作るときに役にたちます。

function myTag(strings, ...values) {
  return String.cooked(strings, ...values.map(value => String(value).toUpperCase())
}

myTag`hello ${'world'}` // "hello WORLD"

Destructure Private Fields

Stage 1 を飛ばして、Stage 2 になりました

Destructure Private Fields は、プライベートフィールドの分割代入のための構文を導入します。

# からはじまる識別子は通常であれば存在できないので、別の名前にリネームする必要があります。次の例では this.#xx という名前にリネームしています。

class Foo {
  #x = 1;
  constructor() {
    console.log(this.#x); // => 1
    const { #x: x } = this;
    console.log(x); // => 1
  }
}

Bind-this operator

Stage 1 になりました

Bind this operator は、Function.prototype.bind と同様の方法で関数をバインドするための二項演算子を導入する提案です。

以前から存在する Stage 0 の Bind Operator の後継であり、Stage 1 の Extensions の競合です。

Object.prototype.hasOwnProperty.call({ foo: "foo" }, "foo"); // true
({ foo: "foo" }::Object.prototype.hasOwnProperty("foo")); // true

Function helpers

Stage 1 になりませんでした。この提案に含まれる関数は個別の提案として再度提出されるかもしれません。

Function helpers は、Function のスタティックメソッドとして便利なヘルパー関数を追加する提案です。

Function.flow は引数に与えられた関数を合成した新しい関数を返します。

const f = Function.flow(f0, f1, f2);
f(5, 7); // f2(f1(f0(5, 7))).

Function.pipe は第 1 引数の値を、それ移行の引数として渡された関数を合成した関数に渡した結果を返します。

Function.pipe(5, f0, f1, f2); // f2(f1(f0(5))).

Function.constant は、第 1 引数として渡された値を返し続ける関数を返します。

const f = Function.constant(3);
f("fooo"); // 3
f(3009, 33, 44); // 3
f({ foo: "foo" }); // 3

Function.identifiy は、第 1 引数に与えられた値をそのまま返します。

Function.identity(3); // 3
Function.identity(4, 5); // 4

Function.tap はコールバック関数を引数として受け取り、関数を返します。 Function.tape が返した関数に引数を渡すと、それをコールバック関数に渡して実行し、その上でその引数をそのまま返します。 言葉で説明すると難しいですが、例を見れば簡単だと思います。

const f = Function.tap(console.log);
f(5); // 5 を出力して、5 を返す

Evaluator Attributes

Stage 1 になりました

Evaluator Attributes は、インポートされたモジュールの評価方法を処理系に伝えるための構文を導入します。

例にある通り、提案された目的は WebAssembly のモジュールを JavaScript の Import 文で読み込むためです。しかし、提案の仕様としては WebAssembly には限られていません。

import mod from "./foo.wasm" as "wasm-module";
mod instanceof WebAssembly.Module; // true

現在 Stage 3 の Import Assertions に似ていますが、Import Assertions はモジュールの評価方法に影響を与えることはできません。

RegExp Features

前回のミーティングで提案されたRegExp Featuresが機能ごとに別々の提案に分割されました。

RegExp Modifiers

Stage 1 になりました

RegExp Extended Mode and Comments

Stage 1 になりました

RegExp Atomic Operators

Stage 1 になりませんでした

RegExp \R Escape

Stage 1 になりました

RegExp Buffer Boundaries

Stage 1 になりました

Updates

ステージの移動はないものの、アップデートがあった提案です。

その他

提案ではなく、仕様書の変更として扱われているものです。

Extending null

合意は得られませんでした

class Foo extends null {}

記事に関する報告などはこちらから