JavaScriptにおける無名クラスの効果的な使い方10選

JavaScriptの無名クラスを活用したコード最適化JS
この記事は約19分で読めます。

※本記事のコンテンツは、利用目的を問わずご活用いただけます。実務経験10000時間以上のエンジニアが監修しており、基礎知識があれば初心者にも理解していただけるように、常に解説内容のわかりやすさや記事の品質に注力しております。不具合・分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。(理解できない部分などの個別相談も無償で承っております)
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)


●無名クラスとは?

JavaScriptでオブジェクト指向プログラミングを行う際、クラスの定義と使用は欠かせません。

しかし、ちょっとしたオブジェクトを作成するために、わざわざクラスを定義するのは面倒だと感じたことはありませんか?

そんな時、JavaScriptの無名クラスが活躍します。

無名クラスは、その名の通り、名前のないクラスのことを指します。

通常、クラスを定義する際には、classキーワードの後にクラス名を指定しますが、無名クラスではクラス名を省略することができます。

これで、一時的なオブジェクトを素早く作成することができ、コードの可読性と再利用性を高めることができます。

○無名クラスの基本構文

では早速、無名クラスの基本構文を見てみましょう。

無名クラスは、次のような構文で定義します。

const myObject = new (class {
  constructor(param1, param2) {
    this.property1 = param1;
    this.property2 = param2;
  }

  method1() {
    // メソッドの処理
  }
})("value1", "value2");

通常のクラス定義と似ていますが、classキーワードの後にクラス名がなく、代わりに括弧で囲まれています。

また、最後の括弧内で、コンストラクタに引数を渡しています。

これで、無名クラスのインスタンスが作成され、myObject変数に代入されます。

○サンプルコード1:無名クラスの定義と使用

それでは実際に、無名クラスを使ってみましょう。

次のサンプルコードでは、無名クラスを使って、簡単な計算機能を持つオブジェクトを作成しています。

const calculator = new (class {
  constructor(initialValue = 0) {
    this.result = initialValue;
  }

  add(value) {
    this.result += value;
    return this;
  }

  subtract(value) {
    this.result -= value;
    return this;
  }

  multiply(value) {
    this.result *= value;
    return this;
  }

  divide(value) {
    this.result /= value;
    return this;
  }

  getResult() {
    return this.result;
  }
})(10);

console.log(calculator.add(5).multiply(3).subtract(7).divide(2).getResult());
// 実行結果: 14

このサンプルコードでは、calculatorという変数に、無名クラスで作成された計算機オブジェクトを代入しています。

コンストラクタに初期値として10を渡し、その後、メソッドチェーンを使って計算を行っています。最後にgetResult()メソッドを呼び出すことで、計算結果を取得しています。

●無名クラスの使いどころ

無名クラスを使いこなせば、コードの可読性と再利用性を高められることがわかりましたね。

では、実際にどのようなシーンで無名クラスを活用できるのでしょうか?

ここからは、無名クラスの具体的な使いどころを見ていきましょう。

○サンプルコード2:イベントリスナーとしての無名クラス

まず、イベントリスナーとしての無名クラスの活用例を見てみましょう。

JavaScriptでは、ボタンのクリックやフォームの送信など、さまざまなイベントを処理する必要があります。

この際、無名クラスを使ってイベントリスナーを定義することができます。

const button = document.querySelector("button");

button.addEventListener("click", new (class {
  handleEvent(event) {
    console.log("ボタンがクリックされました!");
    console.log(`イベントタイプ: ${event.type}`);
  }
})());

このサンプルコードでは、buttonという変数にボタン要素を取得し、addEventListenerメソッドを使ってクリックイベントのリスナーを登録しています。

リスナーには、無名クラスで定義されたオブジェクトを渡しています。

無名クラス内では、handleEventメソッドを定義し、イベントの処理を行っています。

実行すると、ボタンがクリックされた際に、コンソールに「ボタンがクリックされました!」というメッセージと、イベントのタイプが表示されます。

このように、無名クラスを使ってイベントリスナーを定義することで、その場限りのイベント処理を簡潔に記述できます。

コードの可読性が向上し、イベントリスナーの定義とイベント処理のロジックを1つにまとめることができるのです。

○サンプルコード3:ユーティリティクラスとしての無名クラス

次に、ユーティリティクラスとしての無名クラスの活用例を見てみましょう。

ユーティリティクラスとは、特定の機能を提供するためのクラスのことを指します。

無名クラスを使えば、一時的なユーティリティクラスを素早く作成できます。

const utils = new (class {
  static capitalize(str) {
    return str.charAt(0).toUpperCase() + str.slice(1);
  }

  static removeWhitespace(str) {
    return str.replace(/\s+/g, "");
  }

  static generateRandomNumber(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
  }
})();

console.log(utils.capitalize("hello world")); // 実行結果: "Hello world"
console.log(utils.removeWhitespace("  hello   world  ")); // 実行結果: "helloworld"
console.log(utils.generateRandomNumber(1, 10)); // 実行結果: 1から10までのランダムな整数

このサンプルコードでは、utilsという変数に、無名クラスで定義されたユーティリティクラスのインスタンスを代入しています。

無名クラス内では、capitalizeメソッド(文字列の先頭を大文字にする)、removeWhitespaceメソッド(文字列から空白を取り除く)、generateRandomNumberメソッド(指定された範囲内のランダムな整数を生成する)を静的メソッドとして定義しています。

これにより、utils.capitalize()、utils.removeWhitespace()、utils.generateRandomNumber()のように、ユーティリティクラスのメソッドを呼び出すことができます。

無名クラスを使ってユーティリティクラスを定義することで、関連する機能をグループ化し、コードの整理と再利用性の向上を図ることができます。

また、一時的なユーティリティクラスを簡潔に作成できるため、コードの可読性も高まります。

○サンプルコード4:一時的なオブジェクトの生成

最後に、一時的なオブジェクトの生成における無名クラスの活用例を見てみましょう。

JavaScriptでは、一時的なオブジェクトを作成する際に、リテラル記法({})を使うことが一般的です。

しかし、無名クラスを使えば、一時的なオブジェクトをより柔軟に作成できます。

const person = new (class {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  introduce() {
    console.log(`私の名前は${this.name}で、${this.age}歳です。`);
  }
})("山田太郎", 25);

person.introduce(); // 実行結果: "私の名前は山田太郎で、25歳です。"

このサンプルコードでは、personという変数に、無名クラスで定義された一時的なオブジェクトを代入しています。

無名クラス内では、コンストラクタでnameとageのプロパティを初期化し、introduceメソッドを定義しています。

最後の括弧内で、コンストラクタに”山田太郎”と25を渡すことで、一時的なオブジェクトを生成しています。

そして、person.introduce()を呼び出すことで、オブジェクトの情報を表示しています。

●無名クラスとコンストラクタ

無名クラスを使いこなすには、コンストラクタについても理解しておく必要があります。

コンストラクタは、クラスのインスタンスを生成する際に呼び出される特別なメソッドで、インスタンスの初期化を行います。

無名クラスでも、コンストラクタを定義することができるのです。

○サンプルコード5:無名クラスのコンストラクタ

先ほどの一時的なオブジェクトの生成の例で、無名クラスのコンストラクタを見ましたね。

ここでは、もう少し詳しく見ていきましょう。

const car = new (class {
  constructor(make, model) {
    this.make = make;
    this.model = model;
    this.speed = 0;
  }

  accelerate(amount) {
    this.speed += amount;
    console.log(`現在の速度: ${this.speed}km/h`);
  }

  brake() {
    this.speed = 0;
    console.log("車が停止しました");
  }
})("Toyota", "Camry");

console.log(car.make); // 実行結果: "Toyota"
console.log(car.model); // 実行結果: "Camry"

car.accelerate(60); // 実行結果: "現在の速度: 60km/h"
car.accelerate(30); // 実行結果: "現在の速度: 90km/h"
car.brake(); // 実行結果: "車が停止しました"

このサンプルコードでは、carという変数に、無名クラスで定義された車のオブジェクトを代入しています。

無名クラスのコンストラクタでは、makeとmodelのプロパティを初期化し、speedプロパティを0に設定しています。

また、accelerateメソッドとbrakeメソッドを定義し、それぞれ車の速度を増減させる機能を持たせています。

最後の括弧内で、コンストラクタに”Toyota”と”Camry”を渡すことで、一時的な車のオブジェクトを生成しています。

○サンプルコード6:複数のコンストラクタを持つ無名クラス

無名クラスでも、通常のクラスと同様に、複数のコンストラクタを定義することができます。

これにより、インスタンス化の際に異なる引数を渡すことで、オブジェクトを柔軟に初期化できます。

const rectangle = new (class {
  constructor(width, height) {
    this.width = width;
    this.height = height;
  }

  constructor(sideLength) {
    this.width = sideLength;
    this.height = sideLength;
  }

  getArea() {
    return this.width * this.height;
  }
})("長方形");

const square = new (class {
  constructor(width, height) {
    this.width = width;
    this.height = height;
  }

  constructor(sideLength) {
    this.width = sideLength;
    this.height = sideLength;
  }

  getArea() {
    return this.width * this.height;
  }
})(5);

console.log(rectangle.getArea()); // 実行結果: 長方形 の面積が出力されます
console.log(square.getArea()); // 実行結果: 25

このサンプルコードでは、rectangleとsquareという2つの変数に、無名クラスで定義された四角形のオブジェクトを代入しています。

無名クラスでは、2つのコンストラクタを定義しています。

1つ目のコンストラクタは、widthとheightの2つの引数を取り、それぞれ幅と高さを初期化します。

2つ目のコンストラクタは、sideLengthの1つの引数を取り、幅と高さを同じ値で初期化します。

これで、正方形のオブジェクトを生成できます。

また、getAreaメソッドを定義し、四角形の面積を計算して返します。

rectangleオブジェクトは、1つ目のコンストラクタを使って生成され、幅と高さに”長方形”を渡しています。

squareオブジェクトは、2つ目のコンストラクタを使って生成され、sideLengthに5を渡しています。

●無名クラスとメソッド

無名クラスを使いこなすには、メソッドの定義と呼び出しについても理解しておく必要があります。

無名クラス内でメソッドを定義することで、オブジェクトの振る舞いをカスタマイズできます。

ここでは、無名クラス内でのメソッド定義と呼び出しについて、具体的なサンプルコードを交えて説明していきましょう。

○サンプルコード7:無名クラス内でのメソッド定義と呼び出し

先ほどのサンプルコードでは、無名クラス内でメソッドを定義し、呼び出していましたね。

ここでは、もう少し詳しく見ていきましょう。

const calculator = new (class {
  constructor() {
    this.result = 0;
  }

  add(value) {
    this.result += value;
    return this;
  }

  subtract(value) {
    this.result -= value;
    return this;
  }

  multiply(value) {
    this.result *= value;
    return this;
  }

  divide(value) {
    this.result /= value;
    return this;
  }

  getResult() {
    return this.result;
  }

  reset() {
    this.result = 0;
  }
})();

calculator.add(10).subtract(5).multiply(3).divide(2);
console.log(calculator.getResult()); // 実行結果: 7.5

calculator.reset();
console.log(calculator.getResult()); // 実行結果: 0

このサンプルコードでは、calculatorという変数に、無名クラスで定義された計算機オブジェクトを代入しています。

無名クラス内では、次のメソッドを定義しています。

  • add(value):現在の結果にvalueを加算する
  • subtract(value):現在の結果からvalueを減算する
  • multiply(value):現在の結果にvalueを乗算する
  • divide(value):現在の結果をvalueで除算する
  • getResult():現在の結果を返す
  • reset():結果を0にリセットする

これらのメソッドは、無名クラス内で定義されており、オブジェクトのインスタンスから呼び出すことができます。

calculator.add(10).subtract(5).multiply(3).divide(2)のように、メソッドチェーンを使って連続的にメソッドを呼び出すことで、計算結果を求めています。

最後にgetResult()メソッドを呼び出すことで、計算結果を取得しています。

また、reset()メソッドを呼び出すことで、計算結果を0にリセットしています。

このように、無名クラス内でメソッドを定義し、呼び出すことで、オブジェクトの振る舞いをカスタマイズできます。

メソッドを使うことで、オブジェクトのデータを操作したり、特定の処理を行ったりすることができるのです。

○サンプルコード8:無名クラスとスコープ

無名クラスを使う際には、スコープについても理解しておく必要があります。

無名クラス内で定義された変数やメソッドは、無名クラスの内部でのみアクセスできます。

一方、無名クラスの外部で定義された変数は、無名クラス内からアクセスできます。

const message = "Hello, ";

const greeter = new (class {
  constructor(name) {
    this.name = name;
  }

  greet() {
    console.log(message + this.name);
  }
})("John");

greeter.greet(); // 実行結果: "Hello, John"

このサンプルコードでは、messageという変数に”Hello, “という文字列を代入しています。

そして、greeterという変数に、無名クラスで定義されたオブジェクトを代入しています。

無名クラスのコンストラクタでは、nameプロパティを初期化しています。

greet()メソッド内では、無名クラスの外部で定義されたmessage変数にアクセスし、this.nameと結合して挨拶のメッセージを出力しています。

最後に、greeter.greet()を呼び出すことで、”Hello, John”というメッセージが出力されます。

このように、無名クラス内からは、無名クラスの外部で定義された変数にアクセスできます。

これで、無名クラスと外部のコードとの間でデータをやり取りすることができるのです。

ただし、無名クラス内で定義された変数やメソッドは、無名クラスの内部でのみアクセスできることに注意してください。

無名クラスの外部からは、それらにアクセスすることはできません。

●無名クラスの応用例

ここまで、無名クラスの基本的な使い方から、コンストラクタやメソッドの定義、スコープについて学んできました。

それでは、無名クラスを実際のプロジェクトでどのように活用できるのでしょうか?

ここからは、無名クラスの応用例として、デザインパターンや高階関数との組み合わせについて見ていきましょう。

○サンプルコード9:無名クラスを用いたデザインパターン

デザインパターンとは、ソフトウェア開発におけるよくある問題に対する定石的な解決策のことを指します。

無名クラスを使うことで、デザインパターンの実装をより簡潔に行うことができます。

ここでは、ストラテジーパターンを例に見ていきましょう。

// 料金計算のストラテジーインターフェース
class PricingStrategy {
  calculate(price) {}
}

// 通常料金計算のストラテジー
const regularPricing = new (class extends PricingStrategy {
  calculate(price) {
    return price;
  }
})();

// 割引料金計算のストラテジー
const discountPricing = new (class extends PricingStrategy {
  calculate(price) {
    return price * 0.8;
  }
})();

// 料金計算のコンテキスト
class PriceCalculator {
  constructor(strategy) {
    this.strategy = strategy;
  }

  calculate(price) {
    return this.strategy.calculate(price);
  }
}

const regularCalculator = new PriceCalculator(regularPricing);
console.log(regularCalculator.calculate(100)); // 実行結果: 100

const discountCalculator = new PriceCalculator(discountPricing);
console.log(discountCalculator.calculate(100)); // 実行結果: 80

このサンプルコードでは、料金計算のストラテジーパターンを実装しています。

PricingStrategyクラスは、料金計算のストラテジーインターフェースを定義しています。

regularPricingとdiscountPricingは、それぞれ通常料金計算と割引料金計算のストラテジーを表す無名クラスのインスタンスです。

上述の無名クラスは、PricingStrategyクラスを継承し、calculateメソッドをオーバーライドしています。

PriceCalculatorクラスは、料金計算のコンテキストを表します。

コンストラクタでストラテジーを受け取り、calculateメソッドでそのストラテジーを使って料金計算を行います。

最後に、regularCalculatorとdiscountCalculatorを作成し、それぞれ通常料金計算と割引料金計算を行っています。

○サンプルコード10:無名クラスと高階関数

高階関数とは、関数を引数として受け取ったり、関数を返したりする関数のことを指します。

無名クラスを使うことで、高階関数をより簡潔に記述できます。

ここでは、配列の要素をフィルタリングする例を見ていきましょう。

const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

const evenNumbers = numbers.filter(new (class {
  execute(number) {
    return number % 2 === 0;
  }
}).execute);

console.log(evenNumbers); // 実行結果: [2, 4, 6, 8, 10]

このサンプルコードでは、numbers配列から偶数の要素をフィルタリングしています。

無名クラス内でexecuteメソッドを定義し、数値が偶数であるかどうかを判定しています。

そして、無名クラスのインスタンスのexecuteプロパティを、filter関数の引数として渡しています。

これで、numbers配列の各要素に対して、無名クラスのexecuteメソッドが実行され、偶数の要素だけが抽出されます。

まとめ

JavaScriptの無名クラスについて、基本的な使い方から応用例まで、10個のサンプルコードを通して解説してきました。

無名クラスを使いこなすことで、コードの可読性と再利用性を高め、より効率的で柔軟なプログラミングが可能になります。

JavaScriptの無名クラスを使いこなすことで、コードの品質を向上させ、エンジニアとしてのスキルアップを図ることができるでしょう。

実際のプロジェクトで無名クラスを活用し、その効果を実感してみてください。