JavaScriptとは?その魅力と役割

ウェブサイトに動きや対話性を加えるために不可欠なプログラミング言語、それがJavaScriptです。
1995年にBrendan Eich氏によって考案されて以来、ウェブ開発の中心的な役割を担い、進化を続けてきました。
その汎用性と学習のしやすさから、初心者から上級者まで幅広い層に支持されています。

ウェブを動かす魔法の言語

JavaScriptは、ウェブブラウザ上で実行されるプログラミング言語であり、ウェブページに動的なコンテンツやインタラクティブな機能を追加するために使用されます。
例えば、ユーザーがボタンをクリックした際の反応、フォームに入力されたデータのリアルタイムな検証、スムーズなアニメーションの表示など、ウェブサイトの「動き」のほとんどはJavaScriptによって実現されています。

また、バックグラウンドでのサーバーとの非同期通信(Ajaxなど)もJavaScriptの得意分野であり、これによりページ全体をリリロードすることなく、必要な情報だけを更新するといった快適なユーザー体験を提供できます。
まさに、静的だったウェブページに命を吹き込み、ユーザーとの対話を可能にする「魔法の言語」と言えるでしょう。
ウェブの進化とともにJavaScriptも進化し、現代のウェブアプリケーション開発には欠かせない存在となっています。

進化し続けるJavaScriptの姿

JavaScriptは、ECMAScriptという標準仕様のアップデートにより、毎年新しい機能が追加され、開発者の生産性を高めています。
近年のECMAScriptでは、配列操作の新メソッドが導入されるなど、より効率的でモダンな書き方が可能になりました。
例えば、配列の最後の要素を検索する`Array.prototype.findLast()`や、元の配列を変更せずにソートされた新しい配列を返す`toSorted()`といったメソッドは、開発者体験の向上に大きく貢献しています(参考情報より)。

さらに、静的型付けへのニーズが高まる中、Microsoftは「Types as Comments」という提案をTC39(JavaScriptの標準策定委員会)に提出しており、将来的にはJavaScript本体に型チェック機能が取り込まれる可能性も示唆されています。
これは、大規模な開発におけるコードの信頼性向上とエラーの早期発見に繋がり、より堅牢なアプリケーション開発が可能になることを意味します。
常に最新の技術動向をキャッチアップすることが、JavaScript開発者には求められます。

広がるJavaScriptの可能性

JavaScriptはもはやウェブブラウザの中だけに留まりません。
Node.jsのような実行環境が登場したことで、ウェブブラウザ上でのフロントエンド開発だけでなく、サーバーサイド開発にも活用されるようになりました。
これにより、ウェブサイトのフロントエンドからバックエンドまで、一貫してJavaScriptで開発することが可能になり、開発効率の向上と学習コストの削減に寄与しています。

さらに、Electronフレームワークを使えばデスクトップアプリケーションを、React NativeやIonicを使えばスマートフォンアプリを開発するなど、その応用範囲は多岐にわたります。
「State of JavaScript 2024」調査によると、フロントエンド開発ではReactが依然として高い利用率を誇り、Vue.jsも人気を集めています。
また、モジュールバンドラーにおいても、かつての主流であったwebpackに代わり、Viteやesbuildといった高速で軽量な代替手段が注目されているとのことです(参考情報より)。
このように、JavaScriptはウェブ開発を超え、あらゆるプラットフォームで活躍する汎用性の高い言語へと進化を続けています。

JavaScriptの基本構文と書き方のコツ

JavaScriptを効果的に学ぶ上で、その基本構文を理解し、読みやすく効率的なコードを書くコツを掴むことは非常に重要です。
ここでは、変数の使い方から制御構造、そしてコードの可読性を高めるためのポイントまでを解説します。
これらの基礎をしっかりと身につけることで、より複雑なプログラムもスムーズに開発できるようになります。

変数の宣言とデータ型

JavaScriptで値(データ)を扱うには「変数」を使います。
変数を宣言するには、主に`var`、`let`、`const`の3つのキーワードがあります。

  • `var`: JavaScriptの初期から存在するキーワードですが、変数のスコープ(有効範囲)が関数単位になるため、意図しない挙動を引き起こすことがあります。
  • `let`: ES2015(ES6)で導入され、ブロックスコープ(`{}`で囲まれた範囲)を持ちます。再代入は可能ですが、再宣言はできません。現代のJavaScriptでは推奨される変数の宣言方法の一つです。
  • `const`: `let`と同様にブロックスコープを持ちますが、一度値を代入すると再代入ができません。定数として使用する場合や、意図しない値の変更を防ぎたい場合に非常に有効です。

JavaScriptには、文字列(`”Hello”`)、数値(`123`)、真偽値(`true`/`false`)、配列(`[1, 2, 3]`)、オブジェクト(`{ name: “Alice” }`)、`null`、`undefined`などのデータ型があります。
JavaScriptは動的型付け言語であるため、変数の型を明示的に宣言する必要がなく、実行時に型が決定されます。
しかし、型の自動変換には注意が必要で、例えば`”10″ + 5`は`”105″`となり、`”10″ – 5`は`5`となります。
このような挙動を理解し、適切に型を扱うことがバグを防ぐ上で重要です。

制御構造を使いこなす

プログラムの流れを制御するために「制御構造」は不可欠です。
JavaScriptには、条件によって処理を分岐させる「条件分岐」と、同じ処理を繰り返す「繰り返し処理」があります。

条件分岐:

  • `if-else`文: 最も基本的な条件分岐で、指定した条件が真(true)の場合に特定の処理を実行し、偽(false)の場合に別の処理を実行します。
    if (score >= 80) {
        console.log("合格");
    } else {
        console.log("不合格");
    }
  • `switch`文: 複数の条件分岐がある場合に、`if-else if`を続けるよりも簡潔に記述できます。
    switch (day) {
        case "月":
            console.log("月曜日");
            break;
        case "火":
            console.log("火曜日");
            break;
        default:
            console.log("その他の曜日");
    }

繰り返し処理:

  • `for`ループ: 指定回数繰り返したい場合や、配列のインデックスを使って処理したい場合に便利です。
    for (let i = 0; i < 5; i++) {
        console.log(i); // 0, 1, 2, 3, 4
    }
  • `while`ループ: 条件が真である間、処理を繰り返します。無限ループにならないよう、条件がいつか偽になるように注意が必要です。
  • `for…of`ループ: 配列や文字列など、イテラブルな(反復可能な)オブジェクトの各要素に対して処理を実行します。
  • `for…in`ループ: オブジェクトのプロパティ名を列挙する際に使用します。

これらの制御構造を適切に使いこなすことで、複雑なロジックも整理して記述し、プログラムに柔軟な挙動を与えることができます。

コメントとコードの可読性

コードを書く上で、自分だけでなく他の開発者、そして未来の自分が理解しやすい「可読性の高いコード」を書くことは非常に重要です。
そのために役立つのが「コメント」と、適切な「コーディングスタイル」です。

コメントの活用:

コメントは、コードの動作や意図を説明するためにコード内に記述するテキストで、プログラムの実行には影響しません。

  • 単一行コメント: `//` の後に記述します。
    let total = 0; // 合計値を初期化
  • 複数行コメント: `/*` と `*/` で囲んで記述します。
    /*
    これは複数行にわたるコメントです。
    関数の目的や複雑なロジックの説明に役立ちます。
    */

コメントは、コードの「なぜ(Why)」や「何のために(What for)」を補足するのに役立ちますが、冗長なコメントは避けるべきです。
コード自体が読みやすければ、コメントは最小限で済みます。

コードの可読性を高めるコツ:

  • 適切なインデント: コードの階層構造を明確にし、ブロックの始まりと終わりを視覚的に分かりやすくします。
  • 意味のある変数名・関数名: `x`や`tmp`のような抽象的な名前ではなく、`userName`や`calculateTotalPrice`のように、その役割や内容がすぐにわかる名前をつけましょう。
  • 空白行やスペースの活用: 論理的なまとまりごとに空白行を入れ、演算子の前後にもスペースを入れることで、コードがすっきりとして見やすくなります。
  • 一貫したコーディングスタイル: プロジェクト内で統一されたルール(セミコロンの有無、クォーテーションの種類など)に従うことで、コード全体の品質が向上します。

これらの習慣を身につけることで、デバッグが容易になり、チーム開発におけるコミュニケーションコストも削減できます。

JavaScriptで必須!関数とその使い方

JavaScriptプログラミングにおいて、関数はコードを整理し、再利用性を高める上で非常に重要な役割を果たします。
特定の処理をひとまとまりにし、必要に応じて何度も呼び出せるようにすることで、プログラムはより効率的で管理しやすくなります。
ここでは、関数の基本的な定義方法から、引数と戻り値の使い方、そしてJavaScript特有のスコープとクロージャの概念について深く掘り下げていきます。

関数の基本と定義方法

関数は、特定のタスクを実行するためのコードのブロックであり、一度定義すれば何度でも呼び出すことができます。
これにより、同じコードを繰り返し書く手間を省き、プログラムの保守性を高めることができます。

JavaScriptで関数を定義する方法はいくつかあります。

  1. 関数宣言 (Function Declaration)

    最も基本的な方法で、`function`キーワードを使用します。ホイスティング(巻き上げ)の対象となるため、定義される前に呼び出すことも可能です。

    function greet(name) {
        return "こんにちは、" + name + "さん!";
    }
    console.log(greet("太郎")); // こんにちは、太郎さん!
  2. 関数式 (Function Expression)

    関数を変数に代入する方法です。関数は値として扱われ、ホイスティングはされません。

    const sayHello = function(name) {
        return "Hello, " + name + "!";
    };
    console.log(sayHello("次郎")); // Hello, 次郎さん!

    関数式は、即時実行関数 (IIFE) やコールバック関数としてよく利用されます。

  3. アロー関数 (Arrow Function)

    ES2015 (ES6) で導入された、より簡潔な関数記述方法です。特に無名関数として使用する際に威力を発揮します。`this`の扱いが関数式と異なる点も特徴です。

    const multiply = (a, b) => a * b;
    console.log(multiply(3, 4)); // 12

    引数が一つの場合は丸括弧を省略でき、処理が一行の場合は`return`と波括弧も省略できます。これにより、非常に簡潔なコードが書けるようになります。

これらの定義方法を状況に応じて使い分けることで、より柔軟でモダンなJavaScriptコードを書くことが可能になります。

引数と戻り値の活用

関数は、外部から値を受け取ったり、処理結果を返したりすることで、より柔軟に機能します。
この役割を担うのが「引数」と「戻り値」です。

引数 (Arguments):

関数に渡される値を引数と呼びます。引数を使うことで、様々なデータに対して同じ処理を実行できるようになります。

  • 基本的な引数:
    function add(a, b) { // a, bが引数
        return a + b;
    }
    console.log(add(5, 3)); // 8
  • デフォルト引数: ES2015 (ES6) から導入され、引数が渡されなかった場合にデフォルト値を設定できます。
    function greet(name = "ゲスト") {
        return "こんにちは、" + name + "さん!";
    }
    console.log(greet()); // こんにちは、ゲストさん!
    console.log(greet("花子")); // こんにちは、花子さん!
  • Restパラメータ (`…`): 渡された複数の引数を配列として受け取ることができます。
    function sumAll(...numbers) { // numbersが[1, 2, 3]となる
        return numbers.reduce((total, num) => total + num, 0);
    }
    console.log(sumAll(1, 2, 3)); // 6

戻り値 (Return Value):

関数内で実行された処理の結果を、関数の呼び出し元に返す値です。`return`キーワードを使って指定します。
`return`文が実行されると、関数の処理はそこで終了し、指定した値が返されます。
`return`文がない関数や、`return;`とだけ書かれた関数は`undefined`を返します。
戻り値を利用することで、関数の処理結果を別の変数に代入したり、さらに別の関数の引数として利用したりすることが可能になり、プログラムの連携がスムーズになります。

スコープとクロージャの理解

JavaScriptを深く理解する上で避けて通れないのが「スコープ」と「クロージャ」の概念です。
これらは変数の生存期間やアクセス範囲を決定し、プログラムの構造に大きな影響を与えます。

スコープ (Scope):

スコープとは、変数がアクセス可能な範囲のことです。

  • グローバルスコープ: プログラムのどこからでもアクセスできる変数です。乱用すると意図しないバグの原因となることがあります。
  • 関数スコープ: `var`キーワードで宣言された変数は、その変数が宣言された関数内でのみアクセス可能です。
  • ブロックスコープ: `let`および`const`キーワードで宣言された変数は、`{}`(ブロック)内で定義された場合、そのブロック内でのみアクセス可能です。これにより、変数の有効範囲をより細かく制御できるようになり、現代のJavaScript開発では推奨されています。

変数が参照される際、JavaScriptエンジンはまず現在のスコープを探索し、見つからなければ上位のスコープへと順に探索していきます。これを「スコープチェーン」と呼びます。

クロージャ (Closure):

クロージャとは、「関数が、それが定義された環境(レキシカル環境)を記憶し、その環境の変数にアクセスできる機能」のことです。
内側の関数が外側の関数のスコープにある変数に、外側の関数が実行を終えてもアクセスできる状態を指します。

function createCounter() {
    let count = 0; // 外側の関数のローカル変数

    return function() { // 内側の関数(クロージャ)
        count++;
        return count;
    };
}

const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2

この例では、`createCounter`関数は実行を終えても、返された無名関数(クロージャ)は`count`変数にアクセスし続けることができます。
クロージャは、プライベート変数の実装、イベントハンドラ、高階関数など、JavaScriptの強力な機能を実現するために広く利用されています。
スコープとクロージャを理解することで、より洗練された、モジュール性の高いコードを書くことができるようになります。

JavaScriptで役立つ演算子の種類と活用法

JavaScriptプログラミングにおいて、演算子はデータの操作や処理の流れを制御するための基本的なツールです。
様々な種類の演算子を適切に使いこなすことで、コードを簡潔に記述し、意図した通りの動作を実現できます。
ここでは、算術演算子や比較演算子といった基本的なものから、近年導入された便利な演算子まで、その種類と活用法を詳しく見ていきましょう。

算術演算子と代入演算子

算術演算子は数値の計算に、代入演算子は変数に値を割り当てるために使われます。
これらはプログラミングのあらゆる場面で頻繁に登場する基本的な演算子です。

算術演算子:

演算子 説明
`+` 加算 `5 + 3` → `8`
`-` 減算 `5 – 3` → `2`
`*` 乗算 `5 * 3` → `15`
`/` 除算 `5 / 2` → `2.5`
`%` 剰余(割り算の余り) `5 % 2` → `1`
`**` べき乗(ES2016より) `2 ** 3` → `8` (`2 * 2 * 2`)

文字列同士を`+`で連結することもできます(例: `”Hello” + “World”` → `”HelloWorld”`)。
数値と文字列を`+`で連結しようとすると、数値が文字列に変換されて連結される点に注意が必要です。

インクリメント/デクリメント演算子:

  • `++` (インクリメント): 変数の値を1増やす
    let x = 5;
    x++; // xは6になる
  • `–` (デクリメント): 変数の値を1減らす
    let y = 5;
    y--; // yは4になる

これらは前置(`++x`)と後置(`x++`)で挙動が異なるため、使用する際は注意が必要です。

代入演算子:

演算子 説明
`=` 代入 `let a = 10;`
`+=` 加算して代入 `a += 5;` は `a = a + 5;` と同じ
`-=` 減算して代入 `a -= 5;` は `a = a – 5;` と同じ
`*=` 乗算して代入 `a *= 5;` は `a = a * 5;` と同じ
`/=` 除算して代入 `a /= 5;` は `a = a / 5;` と同じ
`%=` 剰余を代入 `a %= 5;` は `a = a % 5;` と同じ
`**=` べき乗を代入 `a **= 2;` は `a = a ** 2;` と同じ

これらを活用することで、コードをより簡潔かつ効率的に記述することができます。

比較演算子と論理演算子

比較演算子と論理演算子は、条件分岐や繰り返し処理といった制御構造において、条件を評価するために不可欠なツールです。
プログラムに「判断」させるために使われます。

比較演算子:

2つの値の関係性を評価し、結果を真偽値(`true`または`false`)で返します。

演算子 説明 結果
`==` 等価(値が等しいか) `5 == “5”` `true`
`===` 厳密等価(値と型が等しいか) `5 === “5”` `false`
`!=` 不等価(値が異なるか) `5 != “5”` `false`
`!==` 厳密不等価(値か型が異なるか) `5 !== “5”` `true`
`>` より大きい `5 > 3` `true`
`<` より小さい `5 < 3` `false`
`>=` 以上 `5 >= 5` `true`
`<=` 以下 `5 <= 3` `false`

特に、`==`と`===`の違いは重要です。
`==`は値が同じであれば型が異なっても`true`を返しますが、`===`は値と型の両方が同じでなければ`true`を返しません。
予期せぬ型変換によるバグを防ぐためにも、厳密等価演算子(`===`)の使用が強く推奨されます。

論理演算子:

複数の真偽値を組み合わせて評価します。

  • `&&` (AND): 両方の条件が`true`の場合に`true`を返します。
    (age >= 18 && isStudent) // 18歳以上かつ学生の場合にtrue
  • `||` (OR): いずれかの条件が`true`の場合に`true`を返します。
    (isWeekend || isHoliday) // 週末または祝日の場合にtrue
  • `!` (NOT): 条件を反転させます。
    !isValid // isValidがtrueならfalse、falseならtrue

論理演算子には「短絡評価(Short-circuit evaluation)」という特性があり、`&&`では左辺が`false`の場合、`||`では左辺が`true`の場合に右辺の評価がスキップされます。
この特性は、条件付きで処理を実行する際などに活用できます。

その他の便利な演算子

JavaScriptには、コードをより簡潔に、あるいは安全に記述するための便利な演算子が他にもたくさんあります。
これらを使いこなすことで、モダンで読みやすいコードを書くことができます。

三項演算子 (`? :`):

条件に基づいて2つの値のうちいずれかを選択する、`if-else`文の短縮形です。

const age = 20;
const status = (age >= 18) ? "成人" : "未成年";
console.log(status); // 成人

簡単な条件分岐を一行で記述でき、コードの可読性を高めます。

分割代入 (Destructuring Assignment):

配列やオブジェクトから値を取り出し、個別の変数に代入する構文です。

// 配列の分割代入
const colors = ["red", "green", "blue"];
const [firstColor, secondColor] = colors;
console.log(firstColor); // red

// オブジェクトの分割代入
const user = { name: "Alice", age: 30 };
const { name, age } = user;
console.log(name); // Alice

コードの記述量を減らし、データ構造から必要な値だけを取り出す際に非常に便利です。

スプレッド構文 (`…`):

配列やオブジェクトを展開して、別の配列やオブジェクトにコピーしたり、関数の引数として渡したりする際に使用します。

// 配列の結合
const arr1 = [1, 2];
const arr2 = [3, 4];
const combined = [...arr1, ...arr2]; // [1, 2, 3, 4]

// オブジェクトの結合
const obj1 = { a: 1 };
const obj2 = { b: 2 };
const merged = { ...obj1, ...obj2 }; // { a: 1, b: 2 }

// 関数の引数に配列を展開
function sum(a, b, c) { return a + b + c; }
const nums = [1, 2, 3];
console.log(sum(...nums)); // 6

配列やオブジェクトの操作を柔軟に行えるようになります。

Nullish coalescing演算子 (`??`):

ES2020で導入されました。左辺が`null`または`undefined`の場合にのみ右辺の値を返します。
これにより、空文字列や`0`などの「偽値」を有効な値として扱いつつ、デフォルト値を設定できます。

const name = null;
const displayName = name ?? "匿名"; // nameがnullなので「匿名」
console.log(displayName);

const count = 0;
const displayCount = count ?? 100; // countが0なので「0」
console.log(displayCount);

Optional chaining (`?.`):

ES2020で導入されました。オブジェクトのプロパティにアクセスする際、参照が`null`または`undefined`である場合にエラーを発生させずに`undefined`を返します。
これにより、深くネストされたオブジェクトのプロパティに安全にアクセスできます。

const user = {
    profile: {
        address: {
            city: "Tokyo"
        }
    }
};

console.log(user.profile.address.city);   // Tokyo
console.log(user.profile.phone?.number); // undefined (エラーにならない)

const admin = {};
console.log(admin.profile?.address?.city); // undefined (エラーにならない)

これらの便利な演算子を使いこなすことで、よりエラーに強く、簡潔で効率的なJavaScriptコードを書くことができるようになります。

JavaScriptのオブジェクトとオブジェクト指向の初歩

JavaScriptは、プリミティブ型以外のすべての値が「オブジェクト」であるという、オブジェクト中心の言語です。
オブジェクトは、データと機能(プロパティとメソッド)をひとまとめにしたもので、現実世界のモノを模倣するのに役立ちます。
ここでは、JavaScriptにおけるオブジェクトの基本から、その動作の根幹をなすプロトタイプ、そしてモダンなクラス構文を使ったオブジェクト指向プログラグラミングの初歩について解説します。

オブジェクトの基本と作成方法

JavaScriptにおいて、オブジェクトはプロパティ(キーと値のペア)とメソッド(関数であるプロパティ)の集合体です。
これにより、関連するデータをまとめて管理し、そのデータに対する操作を定義できます。

オブジェクトの作成方法:

最も一般的なオブジェクトの作成方法は「オブジェクトリテラル」を使用することです。
これは、波括弧 `{}` を使って直接オブジェクトを記述する方法です。

const person = {
    name: "山田 太郎", // プロパティ
    age: 30,         // プロパティ
    greet: function() { // メソッド
        console.log("こんにちは、私の名前は" + this.name + "です。");
    }
};

プロパティへのアクセス:

オブジェクトのプロパティにアクセスするには、主に2つの方法があります。

  • ドット記法 (Dot Notation): `オブジェクト.プロパティ名`
    console.log(person.name); // 山田 太郎
    person.greet();           // こんにちは、私の名前は山田 太郎です。
  • ブラケット記法 (Bracket Notation): `オブジェクト[“プロパティ名”]`
    console.log(person["age"]); // 30

    プロパティ名が変数に含まれる場合や、特殊文字を含む場合に使用します。

    const propName = "name";
    console.log(person[propName]); // 山田 太郎

プロパティの動的な追加・削除:

JavaScriptのオブジェクトは非常に柔軟で、実行中に新しいプロパティを追加したり、既存のプロパティを削除したりできます。

person.city = "東京"; // 新しいプロパティを追加
console.log(person.city); // 東京

delete person.age; // プロパティを削除
console.log(person.age); // undefined

このように、オブジェクトはデータを構造化し、それに関連する動作をカプセル化するための強力な手段となります。

プロトタイプと継承の概念

JavaScriptのオブジェクト指向プログラミングは、他の言語(JavaやC++など)のようなクラスベースの継承とは異なり、「プロトタイプベースの継承」という独特の仕組みを持っています。
このプロトタイプを理解することが、JavaScriptのオブジェクトの振る舞いを深く理解する鍵となります。

プロトタイプとは:

すべてのJavaScriptオブジェクトは、内部的に`[[Prototype]]`と呼ばれる別のオブジェクトへのリンク(参照)を持っています。
この参照先のオブジェクトを「プロトタイプ」と呼びます。
プロトタイプオブジェクトは、共有されるプロパティやメソッドを持つことができます。

プロトタイプチェーン:

オブジェクトのプロパティやメソッドにアクセスしようとしたとき、まずそのオブジェクト自身にそのプロパティがあるかを探します。
もし見つからなければ、そのオブジェクトのプロトタイプを探しに行きます。
さらにプロトタイプにも見つからなければ、そのプロトタイプのプロトタイプを探しに行く、という連鎖的な探索が行われます。
この連鎖を「プロトタイプチェーン」と呼びます。
チェーンの終点には、`null`をプロトタイプとする`Object.prototype`が存在します。

const person = {
    name: "Alice",
    greet() {
        console.log(`Hello, ${this.name}`);
    }
};

const student = Object.create(person); // personをプロトタイプとしてstudentを作成
student.name = "Bob"; // student自身のnameプロパティ
student.study = function() {
    console.log(`${this.name} is studying.`);
};

student.greet();  // Hello, Bob (student自身のnameを使用)
student.study();  // Bob is studying.

// studentにはgreetメソッド自体はないが、プロトタイプチェーンを辿ってpersonのgreetが呼び出される

このメカニズムにより、JavaScriptはオブジェクト間でプロパティやメソッドを共有し、あたかも継承しているかのように振る舞うことができます。
`Object.getPrototypeOf()`メソッドを使って、オブジェクトのプロトタイプを取得できます。

クラス構文とモダンな書き方

ES2015 (ES6) で導入された`class`キーワードは、JavaScriptに他のオブジェクト指向言語のような「クラスベース」の構文をもたらしました。
しかし、これはシンタックスシュガー(構文糖衣)であり、内部的には従来のプロトタイプベースの継承に変換されて動作します。
このクラス構文は、より直感的で馴染みやすいオブジェクト指向プログラミングを可能にします。

クラスの定義とインスタンス化:

class Animal {
    // コンストラクタ:インスタンスが生成される際に実行される
    constructor(name) {
        this.name = name;
    }

    // メソッド
    speak() {
        console.log(`${this.name} makes a sound.`);
    }
}

// クラスからインスタンスを生成
const dog = new Animal("Dog");
dog.speak(); // Dog makes a sound.

const cat = new Animal("Cat");
cat.speak(); // Cat makes a sound.

`constructor`は、新しいオブジェクト(インスタンス)が作成されるときに実行される特別なメソッドです。

クラスの継承:

`extends`キーワードを使うことで、既存のクラス(親クラス)の機能を新しいクラス(子クラス)に継承させることができます。
これにより、コードの再利用性が高まります。

class Dog extends Animal { // Animalクラスを継承
    constructor(name, breed) {
        super(name); // 親クラス(Animal)のコンストラクタを呼び出す
        this.breed = breed;
    }

    speak() { // 親クラスのspeakメソッドをオーバーライド
        console.log(`${this.name} barks! I am a ${this.breed}.`);
    }

    fetch() {
        console.log(`${this.name} is fetching the ball.`);
    }
}

const myDog = new Dog("Buddy", "Golden Retriever");
myDog.speak();  // Buddy barks! I am a Golden Retriever.
myDog.fetch();  // Buddy is fetching the ball.

子クラスのコンストラクタでは、`super()`を呼び出して親クラスのコンストラクタを実行する必要があります。
また、親クラスのメソッドを子クラスで再定義(オーバーライド)することも可能です。
クラス構文を利用することで、大規模なアプリケーションにおいても、コードを構造化しやすく、保守性の高いオブジェクト指向プログラミングを実践できるようになります。