31
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【JavaScript】ES2026の新機能全部解説する

Posted at

JavaScriptの仕様は、TC39というところで決められています。
ブラウザベンダや関係者が定期的に会合を行い、様々な新機能について話し合って今後のJavaScriptの方向性を決めていきます。

ここでは2025年にFinishedになったproposalについて紹介してみます。
『Finishedになった』の定義は、現在ではChrome・Firefox・Safariのうち2つ以上に実装された、という意味だと思えばだいたい合っています。
すなわち、主要ブラウザでは既にほとんどの機能を使用可能何気にChromeだけ対応してないのが多いです。

Finished Proposals

JSON.parse source text access

JSON.parseの引数reviverに、パラメータcontextを追加します。

JSON.parseは数値型文字列を数値にしたりと一部の型を勝手に変換するので、元の正確な値が失われてしまうことがあります。

const tooBigInt = "100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001";
JSON.parse(tooBigInt); // 1e128

第二引数reviverを使うと変換中の操作に割り込むことができます。

const reviver = (key, val) => {
  console.table(val);     // 1e128
  return "hoge";
}

JSON.parse(tooBigInt, reviver); // "hoge"

ところがreviverに渡ってくる第二引数valは既に変換された後の値なので、元の値を知りたいという役には立ちません。

ということで、変換される前の値を第三引数contextで受け取ることができるようになりました。

ES2026
const reviver = (key, val, context) => {
  console.table(context); // {source:"100000…1"}
  return context.source;
}

JSON.parse(tooBigInt, reviver); // "100000…1"

これによって余計な変換をされることなく、正確な値を回収することが可能になります。
めでたし。

Upsert

Map.getOrInsertです。

キーが存在していれば取得し、存在していなかったら値を設定したいという、SQLでいうUPSERTと同じ機能ですね。

let hoge = new Map([["foo", 1], ["bar", 2]]);

// これまで
if(!hoge.has("baz")){
    hoge.set("baz", 3);
}

// これから
hoge.getOrInsert("baz", 3);

hoge.getOrInsert("foo", 4); // fooは1のまま

既にキーが存在する場合は上書きされずそのままになります。

なおオブジェクトには使えません。

let hoge = {foo:1, bar:2};

hoge.has("baz");            // hoge.has is not a function
hoge.getOrInsert("baz", 3); // hoge.getOrInsert is not a function

hoge.buz??=3; // 3

null合体代入演算子と使い分ける必要があります。
どうして。

ちなみに同じ機能がJavaではcomputeIfAbsent、ScalaではgetOrElseUpdate、C++ではemplace、C#ではGetOrAdd、Rustではor_insert_with、Pythonではsetdefaultです。
どうしてそんなにバラバラなんですか?

Math.sumPrecise

できるだけ正確な足し算です。

let values = [1e20, 1, -1e20];

values.reduce((a, b) => a + b, 0); // 0
Math.sumPrecise(values);           // 1

大きな差のある数を計算すると、小さいほうの値が情報落ちで消滅してしまうことがよくあります。
そのような場合にも、なるべく正確な計算をしてくれる便利関数です。

なお丸め誤差については効かないみたいです。

0.1 + 0.2;                   // 0.30000000000000004
Math.sumPrecise([0.1, 0.2]); // 0.30000000000000004

Error.isError

なんだよその名前。

引数のエラーが真のエラーだった場合にのみtrueを返します。

真のエラーってなんだよ。

どうもこのproposal説明不足すぎてよくわからないのですが、こんなかんじのようです。

obj instanceof Errorクロスレルムで判定に失敗することがあり、また偽造することも可能。
Object.prototype.toString.callを使った判定もtoStringTag等で偽造可能。

つまり、throwされたErrorが本当にErrorであるかを確実に判定する方法が、実はこれまで存在しませんでした。
ということで確実に判定する方法が追加されたというわけです。

let realError = new Error();
let fakeError = new Map();
fakeError.__proto__ = Error.prototype;

realError instanceof Error; // true
fakeError instanceof Error; // true ←

Error.isError(realError); // true
Error.isError(fakeError); // false

同様のメソッドとしてArray.isArray()があります。
でもたとえばMap.isMap()Iterator.isIterator()等は存在しておらず、そのMapが真のMapか、そのIteratorが真のIteratorかを判定する方法はありません。
個別にメソッドを追加していくのではなく、なんかそういう汎用な、強いinstanceofみたいなのがほしいですよね。

Iterator Sequencing

複数のイテレータを合体させます。

let lows = Iterator.from([0, 1, 2, 3]);
let highs = Iterator.from([6, 7, 8, 9]);

let digits = Iterator.concat(lows, [4, 5], highs); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

単に前から順にくっつけるだけのようです。

これと同じ
let digits = function* () {
  yield* lows;
  yield 4;
  yield 5;
  yield* highs;
}();

[0, 6, 1, 7, 2, 8, 3, 9]と交互にくっつける、みたいな機能はないみたいでした。

Uint8Array to/from Base64

バイナリをBASE64に変換します。

// なんかのバイナリ
let arr = new Uint8Array([72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]);

// BASE64にする
arr.toBase64(); // SGVsbG8gV29ybGQ=

// HEXにする
arr.toHex(); // 48656c6c6f20576f726c64

// BASE64から元に戻す
Uint8Array.fromBase64("SGVsbG8gV29ybGQ=");

// HEXから元に戻す
Uint8Array.fromHex("48656c6c6f20576f726c64");

これまでは間にatobを挟まないといけなかったのが直接できるようになったのかな。
画像などのバイナリデータをJSから楽に送受信できるってかんじでいいんだろうか。

Array.fromAsync

同期イテレータからはArray.fromでArrayを作成できますが、非同期イテレータからは作成できません。

async function * asyncGen (n) {
  for (let i = 0; i < n; i++)
    yield i * 2;
}

const arr = await Array.from(asyncGen(4)); // []

どうして。

ということで非同期イテレータからArrayを作成できるようになりました。

const arr = await Array.fromAsync(asyncGen(4)); // [0, 2, 4, 6]

実質↓と同じだそうです。

const arr = [];
for await (const v of asyncGen(4)) {
  arr.push(v);
}

arr; // [0, 2, 4, 6]

普通にArray.fromが非同期も受け取れるようにするのでは駄目だったのだろうか。

感想

今年もいろいろな機能が搭載されましたね。
特にJSONのパースには困っていた人がわりかし多いのではないでしょうか。

逆にError.isErrorなんかは、意義はわかるんだけど、実際これがなくて困ってたんだよねーって現場は存在するんだろうか。

31
12
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
31
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?