JavaScript では、単一のオブジェクトからのみ継承できます。 1 つのオブジェクトに対して[[Prototype]]
は 1 つだけ存在できます。また、クラスは他のクラスを 1 つだけ拡張できます。
しかし、それが限界だと感じることもあります。たとえば、クラスStreetSweeper
とクラスBicycle
あり、これらを組み合わせたStreetSweepingBicycle
を作成したいとします。
または、イベント生成を実装するクラスUser
とクラスEventEmitter
があり、ユーザーがイベントを発行できるようにEventEmitter
の機能をUser
に追加したいと考えています。
ここで役に立つのが「ミックスイン」と呼ばれる概念です。
Wikipedia で定義されているように、ミックスインは、継承する必要なく他のクラスで使用できるメソッドを含むクラスです。
言い換えれば、ミックスインは特定の動作を実装するメソッドを提供しますが、それを単独で使用するのではなく、他のクラスに動作を追加するために使用します。
JavaScript でミックスインを実装する最も簡単な方法は、便利なメソッドを含むオブジェクトを作成し、それらを任意のクラスのプロトタイプに簡単にマージできるようにすることです。
たとえば、ここでは、ミックスインsayHiMixin
を使用してUser
に「音声」を追加しています。
// ミックスイン HiMixin = { と言いましょう SayHi() { alert(`こんにちは ${this.name}`); }、 SayBye() { alert(`さようなら ${this.name}`); } }; // 使用法: クラス ユーザー { コンストラクター(名前) { this.name = 名前; } } // メソッドをコピーする Object.assign(User.prototype、sayHiMixin); // これでユーザーは挨拶できるようになりました new User("Dude").sayHi(); // こんにちは!
継承はありませんが、単純なメソッドのコピーです。したがって、 User
別のクラスから継承し、次のように追加メソッドを「ミックスイン」するためのミックスインも含めることができます。
クラス ユーザーは Person を拡張します { // ... } Object.assign(User.prototype、sayHiMixin);
ミックスインは、それ自体の内部で継承を利用できます。
たとえば、ここではsayHiMixin
sayMixin
を継承しています。
言ってみますMixin = { 言う(フレーズ) { アラート(フレーズ); } }; HiMixin = { と言いましょう __proto__:sayMixin, // (または、Object.setPrototypeOf を使用してここでプロトタイプを設定することもできます) SayHi() { // 親メソッドを呼び出す super.say(`こんにちは ${this.name}`); // (*) }、 SayBye() { super.say(`さようなら ${this.name}`); // (*) } }; クラス ユーザー { コンストラクター(名前) { this.name = 名前; } } // メソッドをコピーする Object.assign(User.prototype、sayHiMixin); // これでユーザーは挨拶できるようになりました new User("Dude").sayHi(); // こんにちは!
sayHiMixin
からの親メソッドsuper.say()
の呼び出し ( (*)
でラベル付けされた行) は、クラスではなく、そのミックスインのプロトタイプ内のメソッドを検索することに注意してください。
これが図です (右側の部分を参照)。
これは、メソッドsayHi
とsayBye
が最初にsayHiMixin
で作成されたためです。したがって、コピーされたとしても、上の図に示すように、 [[HomeObject]]
内部プロパティはsayHiMixin
参照します。
super
[[HomeObject]].[[Prototype]]
で親メソッドを検索します。つまり、 sayHiMixin.[[Prototype]]
を検索します。
では、実際にミックスインを作ってみましょう。
(たとえば) 多くのブラウザ オブジェクトの重要な機能は、イベントを生成できることです。イベントは、情報を必要とする人に「情報をブロードキャスト」する優れた方法です。それでは、イベント関連の関数を任意のクラス/オブジェクトに簡単に追加できるミックスインを作成してみましょう。
ミックスインは、何か重要なことが起こったときに「イベントを生成」するメソッド.trigger(name, [...data])
を提供します。 name
引数はイベントの名前であり、オプションでその後にイベント データを含む追加の引数が続きます。
また、指定された名前のイベントにリスナーとしてhandler
関数を追加するメソッド.on(name, handler)
も使用します。指定されたname
のイベントがトリガーされると呼び出され、 .trigger
呼び出しから引数を取得します。
…そして、 handler
リスナーを削除するメソッド.off(name, handler)
。
ミックスインを追加すると、オブジェクトuser
、訪問者がログインしたときにイベント"login"
生成できるようになります。また、別のオブジェクト (たとえば、 calendar
、ログインしている人のカレンダーをロードするために、そのようなイベントをリッスンする必要がある場合があります。
または、メニュー項目が選択されたときにmenu
イベント"select"
生成し、他のオブジェクトがそのイベントに反応するハンドラーを割り当てることもできます。等々。
コードは次のとおりです。
leteventMixin = { /** * イベントの購読、使用方法: * menu.on('select', function(item) { ... } */ on(イベント名, ハンドラー) { if (!this._eventHandlers) this._eventHandlers = {}; if (!this._eventHandlers[イベント名]) { this._eventHandlers[イベント名] = []; } this._eventHandlers[イベント名].push(ハンドラー); }、 /** * サブスクリプションのキャンセル、使用: * menu.off('select', ハンドラー) */ off(イベント名, ハンドラー) { let handlers = this._eventHandlers?.[イベント名]; if (!handlers) が返る場合; for (let i = 0; i < handlers.length; i++) { if (handlers[i] === ハンドラー) { handlers.splice(i--, 1); } } }、 /** * 指定された名前とデータを使用してイベントを生成します * this.trigger('select', data1, data2); */ トリガー(イベント名, ...args) { if (!this._eventHandlers?.[イベント名]) { 戻る; // そのイベント名のハンドラーはありません } // ハンドラーを呼び出す this._eventHandlers[イベント名].forEach(handler => handler.apply(this, args)); } };
.on(eventName, handler)
– その名前のイベントが発生したときに実行する関数handler
を割り当てます。技術的には、イベント名ごとにハンドラーの配列を格納する_eventHandlers
プロパティがあり、それをリストに追加するだけです。
.off(eventName, handler)
– ハンドラー リストから関数を削除します。
.trigger(eventName, ...args)
– イベントを生成します。 _eventHandlers[eventName]
のすべてのハンドラーが、引数...args
のリストとともに呼び出されます。
使用法:
// クラスを作成する クラスメニュー { 選択(値) { this.trigger("選択", 値); } } // イベント関連のメソッドを使用してミックスインを追加します Object.assign(Menu.prototype,eventMixin); let menu = new Menu(); // 選択時に呼び出されるハンドラーを追加します。 menu.on("選択", 値 => アラート(`選択された値: ${値}`)); // イベントをトリガー => 上記のハンドラーが実行され、以下が表示されます。 // 選択された値: 123 menu.choose("123");
ここで、コードがメニュー選択に反応するようにしたい場合は、 menu.on(...)
を使用してそれをリッスンできます。
そして、 eventMixin
ミックスインを使用すると、継承チェーンを妨げることなく、そのような動作を必要な数のクラスに簡単に追加できます。
Mixin – は一般的なオブジェクト指向プログラミング用語で、他のクラスのメソッドを含むクラスを指します。
他の一部の言語では多重継承が可能です。 JavaScript は多重継承をサポートしていませんが、メソッドをプロトタイプにコピーすることでミックスインを実装できます。
ミックスインを、上で見たイベント処理などの複数の動作を追加することでクラスを拡張する方法として使用できます。
ミックスインが既存のクラス メソッドを誤って上書きすると、ミックスインが競合点になる可能性があります。したがって、一般に、そのようなことが起こる可能性を最小限に抑えるために、ミックスインの命名方法についてよく考える必要があります。