コハム

Webクリエイターのコハムが提供する、Web制作に役立つ情報をまとめたブログです。

年末だからおさらいして!ES2024完全ガイド - JavaScriptの革新的な新機能を総まとめ

JavaScript — What’s new with ECMAScript® 2024 (ES15) — In Depth Guide

記事は上記記事を意訳したものです。

※当ブログでの翻訳記事は元サイト様に許可を得て掲載しています。


プログラミング言語における新機能の発見は、まるで祝日や誕生日のようなものです - 期待に胸を膨らませ、新しい贈り物を探求する喜びに満ちた時間です。ES2024®の提案機能により、開発者たちは、JavaScriptでのコーディングをより効率的で、読みやすく、堅牢にする様々な機能強化を手に入れようとしています。トップレベルawaitの直感的な構文から、パイプライン演算子の表現力、そして不変なレコードとタプルの信頼性まで、それぞれの新機能は、JavaScript環境を豊かにし、開発者により多くのツールを提供するために慎重に選ばれた贈り物のようなものです。

ECMAScript 2024 国際化API仕様(ECMA-402第11版)によると、いくつかの機能がES2024に含まれる予定です。

これらの一部はまだ「提案」段階であり、変更される可能性があることに注意してください。ただし、この記事は承認された変更に応じて更新されます!

それでは、さっそく見ていきましょう...

整形されたUnicode文字列

この機能は、JavaScriptがUnicode文字列を扱う方法を改善することを目的としています。Unicode文字列は、様々な言語や記号から幅広い文字を表現するために不可欠です。このアップデートにより、異なるJavaScript環境間でこれらの文字列の一貫した正確な処理が保証されます。

const sampleStrings = [
  // 単独のサロゲートの例
  "igor\uD800", // 先行サロゲート
  "igor\uD800komolov", // テキストが続く先行サロゲート
  "\uDC00yourfuse",    // 後続サロゲート
  "your\uDC00fuse",    // テキストが続く後続サロゲート
  
  // 整形された例
  "yourFuse",       // サロゲートのない通常の文字列
  "emoji\uD83D\uDE00", // 完全なサロゲートペアを持つ文字列(絵文字)
];

sampleStrings.forEach(str => {
  console.log(`処理された文字列: ${str.toWellFormed()}`);
});

// 期待される出力:
// "処理された文字列: igor�"
// "処理された文字列: igor�komolov"
// "処理された文字列: �yourfuse"
// "処理された文字列: your�fuse"
// "処理された文字列: yourFuse"
// "処理された文字列: emoji😀"

上記の例では、toWellFormed()メソッドが、単独のサロゲートを含む文字列と整形された文字列の配列に適用されています。このメソッドは、無効なシーケンスを置換文字で置き換えることで、単独のサロゲートを含む文字列を整形されたUnicode文字列に変換し、すでに整形された文字列は変更しません。

const problematicURL = "https://yourfuse.com/query=\uDC00data";

try {
  encodeURI(problematicURL);
} catch (e) {
  console.log('エラー:', e.message); // 期待される出力: URIError: URI malformed
}

// toWellFormed()を使用してエラーを防ぐ
console.log('整形されたURI:', encodeURI(problematicURL.toWellFormed())); 
// 期待される出力: "https://yourfuse.com/query=%EF%BF%BDdata"
  • problematicURLは単独の後続サロゲート(\uDC00)を含むURLです。
  • この URLをencodeURI()でエンコードしようとすると、不正なUnicode文字列によりURIErrorがスローされます。
  • toWellFormed()を適用すると、単独のサロゲートはUnicodeの置換文字(U+FFFD、%EF%BF%BDとしてエンコード)に置き換えられ、encodeURI()がエラーなく処理できるようになります。

Atomic waitSync

この追加は、特に共有メモリのコンテキストにおける並行操作を対象としています。これは、マルチスレッド操作におけるデータの整合性を確保し、競合状態を防ぐために重要な同期メカニズムを提供します。例えば、waitSyncは複数のワーカー間で共有バッファーへのアクセスを同期するために使用できます。

ドキュメントはまだ整備中のため具体的な例を示すことはできませんが、既存のAtomicsメソッドに基づいて推測することはできます。以下が予想される使用例です...

// sharedArrayはSharedArrayBufferとする
const sharedArray = new Int32Array(new SharedArrayBuffer(1024));

function performSynchronizedOperation(index, value) {
    // waitSyncメソッドは特定の条件が満たされるまで実行をブロックする
    // 例えば、指定されたインデックスの値が0でなくなるまで待機する
    Atomics.waitSync(sharedArray, index, 0);

    // 共有メモリに対する操作を実行
    sharedArray[index] = value;

    // インデックスの値が更新されたことを他のスレッドやワーカーに通知
    Atomics.notify(sharedArray, index, 1);
}

// Webワーカーまたは他のスレッドで
performSynchronizedOperation(0, 123);

セット表記とストリングのプロパティを持つRegExp vフラグ

JavaScriptの正規表現に対するこの改善により、より複雑なパターンマッチングと文字列操作が可能になります。'v'フラグとセット表記により、より正確で表現力豊かな正規表現パターンが可能になります。例えば、この機能を使用して特定のUnicodeプロパティを持つ文字のセットにマッチさせることができます。

// 差分/減算
[A--B]

// 交差
[A&&B]

// ネストされた文字クラス
[A--[0-9]]

AとBは文字クラス(例:[a-z])またはプロパティエスケープのプレースホルダーと考えることができます。

トップレベルawait

この「Just Do It」機能により、awaitキーワードをasync関数の外部で使用できるようになり、非同期コードの記述と読み取りが容易になります。例えば、モジュールのトップレベルで直接プロミスをawaitすることができ、モジュールのインポートやデータの非同期フェッチのコードが簡潔になります。

// トップレベルawaitを使用
const data = await fetchData();
console.log(data);

重いasync/await構造に新鮮な空気を吹き込んでくれますね!

パイプライン演算子

パイプライン演算子(|>)は、複数の関数呼び出しを含むコードの可読性を向上させます。式の結果を次の関数の引数として渡す関数型スタイルの構文を可能にします。例えば、ネストされた関数呼び出しを明確な操作の連鎖として書き直すことができます:

// パイプライン演算子なし
const calculatedValue = Math.ceil(Math.pow(Math.max(0, -10), 1/3));

// パイプライン演算子あり
const calculatedValue = -10
  |> (n => Math.max(0, n)) // Math.maxの置き換え
  |> (n => Math.pow(n, 1/3)) // Math.powの置き換え
  |> Math.ceil; // Math.ceilを使用

この例では: - Math.max関数は数値が負にならないようにします - Math.pow関数は立方根(1/3乗)を計算します - Math.ceil関数は数値を切り上げて最も近い整数にします - パイプライン演算子(|>)はこれらの操作の連鎖を簡略化し、コードをより読みやすくします

以下は、データ変換におけるパイプライン演算子の有用性を示す例です:

// パイプライン演算子は、一連の関数を明確で簡潔な方法で適用することで、
// 複雑なデータ操作を簡略化します

const numbers = [10, 20, 30, 40, 50];

const processedNumbers = numbers
  |> (_ => _.map(n => n / 2)) // 各数値を半分にする
  |> (_ => _.filter(n => n > 10)); // 10以下の数値をフィルタリング

console.log(processedNumbers); // [15, 20, 25]

この例では: - map関数は配列の各数値を半分にします - filter関数は10以下の数値を除去します - パイプライン演算子(|>)はこれらの変換を優雅に連鎖させ、コードの可読性を向上させます

パイプライン演算子は現在、TC39のステージ2の「ドラフト」段階にあることを覚えておいてください。

レコードとタプル

これらの不変データ構造は、それぞれオブジェクトと配列に似ていますが、作成後に変更することはできません。例えば、レコードやタプルを更新すると新しいインスタンスが生成されます:

// 不変なレコードの作成
const userProfile = #{
  username: "IgorKomolov",
  age: 39,
};

// 不変なタプルの作成
const numberSequence = #[10, 20, 30];

// これらの構造を更新すると新しいインスタンスが作成される
const updatedProfile = userProfile.with({ age: 40});
console.log(updatedProfile); // #{ username: "IgorKomolov", age: 40 }
console.log(userProfile); // #{ username: "IgorKomolov", age: 39 } (変更なし)

const newNumberSequence = numberSequence.with(1, 25);
console.log(newNumberSequence); // #[10, 25, 30]
console.log(numberSequence); // #[10, 20, 30] (変更なし)

レコードはオブジェクトに似た機能を持ち、タプルは配列に似た機能を持ちます。ただし、その特徴的な機能は不変性です。

レコードとタプルは特定の状況でパフォーマンスを向上させ、コードベースでの不変性を強制することができます。これらは提案のステージ2にあり、まだJavaScriptエンジンには実装されていませんが、開発者はBabelのようなトランスパイラを使用して実験することができます。

デコレータ

これはTypeScriptのおかげで長く待ち望まれていた機能で、今やそのまま使えます!デコレータを使用すると、クラス、メソッド、プロパティ、またはパラメータの動作を変更または拡張することができます。特にメタデータの追加、ログ記録、または動作の宣言的な変更に便利です:

// メソッドの実行を追跡するデコレータを適用
class SampleClass {
  @trackExecution
  performAction(parameter1, parameter2) {
    // メソッドの実装
  }
}

この例では: - SampleClassは定義されているクラスです - @trackExecutionperformActionメソッドの呼び出しをログまたは追跡するために使用されるデコレータです - performActionは2つのパラメータ(parameter1とparameter2)を取るSampleClass内のメソッドです。デコレータはこのメソッドへのすべての呼び出しをログまたは追跡します

パターンマッチング

この機能は、複雑なデータ構造の分解とマッチングのための簡潔な構文を導入し、コードの可読性を向上させ、定型コードを削減します。

(調査中)続報をお待ちください!

Temporal

しばらく前からドラフトされていましたが、更新されたTemporalは、JavaScriptのために提案された現代的で包括的な日時APIで、現在ステージ3にあります。既存のDateオブジェクトの多くの制限と複雑さに対処するように設計されています。以下はES2024でのTemporalの使用例です:

このオブジェクトは、現在の時刻のTemporal値を作成するためのいくつかのファクトリメソッドを提供します。

UTCでの現在のインスタントを取得:

Temporal.Now.instant().toString()

特定のタイムゾーンでの現在のゾーン付き日時を取得:

Temporal.Now.zonedDateTimeISO('Asia/Shanghai').toString()

ISO形式での現在のプレーン日時を取得:

Temporal.Now.plainDateTimeISO().toString()

ISO形式での現在のプレーン時刻を取得:

Temporal.Now.plainTimeISO().toString()

ZonedDateTime.prototypeのプロパティ

TemporalのZonedDateTimeクラスには、日時情報の詳細な操作と取得を可能にする複数のプロパティとメソッドがあります。

これらには、カレンダー、タイムゾーン、年、月、日、時、分、秒、そしてナノ秒までのゲッターが含まれます。 また、.with().add().subtract().until().since().round()などのメソッドも含まれており、ゾーン付き日時値を扱うための広範な機能を提供します。

Temporalのプレーン時間クラス

Temporalは、タイムゾーンのない時間の抽象的な表現である「プレーン」クラスを導入します。

  • これらのクラスには、PlainDateTime、PlainDate、PlainTimeが含まれます。
  • 特定のタイムゾーンでの壁時計時刻の表示や、1984年6月の最初の火曜日を見つけるようなタイムゾーンが無関係な時間計算に便利です。

これらの例は、ES2024のTemporalがJavaScriptの日時処理をどのように簡素化し強化できるか、開発者により堅牢で多用途なツールを提供する方法を示しています。

今すぐ使ってみたいですか?問題ありません!

提案をインポートするか、Babelポリフィルを使用してください。以下が提案のインポート方法です...

//そうです、提案をインポートできます :) 
import { Temporal } from '@std/proposal-temporal';

//基本的な操作
const now = Temporal.Now.zonedDateTimeISO('America/New_York');
console.log(now.toString());

//操作と比較
const date = Temporal.PlainDate.from('2024-01-01');
const newDate = date.add({ days: 10 });
console.log(newDate.toString()); // '2024-01-11'を出力

人間工学的なブランドチェック

カスタムクラスとデータ構造でのオブジェクトの型チェックを簡素化し、型検証をより直感的にしてエラーを起こしにくくします。定型コードさようなら!

従来の方法(ES2024以前)

class Book {
    #author;

    constructor(author) {
        this.#author = author;
    }

    static hasAuthorField(obj) {
        try {
            obj.#author; // プライベートフィールドへのアクセスを試みる
            return true; // アクセス成功
        } catch (err) {
            if (err instanceof TypeError) {
                return false; // アクセス失敗、フィールドが存在しない
            }
            throw err; // 他のエラーは再スロー
        }
    }
}

// 使用例:
const myBook = new Book("Igor Komolov");
console.log(Book.hasAuthorField(myBook)); // 期待される出力: true

const otherObject = {};
console.log(Book.hasAuthorField(otherObject)); // 期待される出力: false

新しいES2024の方法

class BookES2024 {
    #author;

    constructor(author) {
        this.#author = author;
    }

    static hasAuthorField(obj) {
        return #author in obj; // プライベートフィールドをチェックする新しいES2024構文
    }
}

// 使用例:
const myBook2024 = new BookES2024("Igor Komolov");
console.log(BookES2024.hasAuthorField(myBook2024)); // 期待される出力: true

const otherObject2024 = {};
console.log(BookES2024.hasAuthorField(otherObject2024)); // 期待される出力: false

この例で、Bookクラスは従来の方法を示し、BookES2024は新しいES2024の構文を使用しています。hasAuthorField静的メソッドは、各クラスで異なるアプローチを使用してオブジェクト内のプライベートフィールド#authorの存在をチェックします。

Realms API

このAPIは分離されたJavaScript環境を作成するメカニズムを提供します。セキュアなコード実行とサンドボックス化に有用で、制御された分離された環境でコードを実行することができます。さらに、名前がとてもかっこいいです!

Realmの作成と単純な式の評価

const igorsRealm = new Realm();
igorsRealm.evaluate('3 * 5'); // イゴールのrealmで15と評価される

Realm間でのシンボルの共有

const igorsRealm = new Realm();
Symbol.for('y') === igorsRealm.evaluate('Symbol.for("y")'); // true を返す、共有シンボル 'y'

自動ラップされた関数の使用

呼び出し可能なオブジェクトがある realm から別の realm に送信されると、対象の realm にWrapped Function Exotic Objectが作成されます。このラップされた関数が呼び出されると、元のrealmの接続された関数に呼び出しを連鎖します。

const igorsRealm = new Realm();
const doubleFunction = igorsRealm.evaluate('num => num * 2');
doubleFunction(10); // 20を返す

コールバック付きの関数評価

const igorsRealm = new Realm();
const processNumber = igorsRealm.evaluate('(number, callback) => callback(number + 5)');
processNumber(5, (result => console.log(result))); // 10をログ出力 (5 + 5)

制限されたグローバルコンテキストアクセス

globalThis、配列、またはObject.prototypeのようなグローバルオブジェクトにrealm.evaluateを通じて直接アクセスすると、TypeErrorがスローされます。

const igorsRealm = new Realm();
igorsRealm.evaluate('this'); // TypeErrorをスロー
igorsRealm.evaluate('new Array()'); // TypeErrorをスロー
igorsRealm.evaluate('Object.keys({})'); // TypeErrorをスロー

結論

ES2024の新機能は、JavaScriptのコーディング方法に革命をもたらそうとしています。これらの機能強化は、コードの可読性と効率性を向上させるだけでなく、不変データ構造や高度なパターンマッチングのような強力な新しいパラダイムを導入します。これらの機能が提案から実装へと移行するにつれて、開発者がよりクリーンで、保守性が高く、表現力豊かなJavaScriptコードを書くための新しい可能性が開かれています。これらの進歩により、現代のWeb開発の基礎となった言語であるJavaScriptの継続的な進化が示されており、その未来は明るく見えます。

©コハム