mixin-pattern について
継承チェーンを作らずにオブジェクトやクラスへ再利用可能な振る舞いを加える、JavaScript の mixin を解説するスキルです。プロトタイプを使う方法や適用場面、名前の衝突やプロトタイプ汚染のリスクを扱います。
主な機能
- 01継承なしで振る舞いを追加
- 02Object.assign() でプロパティを適用
- 03複数の mixin の振る舞いを合成
- 04プロトタイプ汚染のリスクを確認
- 05React では Hooks を優先
活用シーン
- 01複数のクラスでメソッドを再利用
- 02異なるソースの振る舞いを合成
- 03React mixin の代替手段を検討
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ patternsdev/ skills --skill mixin-patternインストール後、Claude Code で/mixin-patternを入力して読み込まれたか確認します。
詳細
ミックスインパターン
ミックスインとは、継承を使わずに、再利用可能な機能を別のオブジェクトやクラスに追加するために使えるオブジェクトです。ミックスイン単独では使えません。ミックスインの唯一の目的は、継承を使わずにオブジェクトやクラスに_機能を追加する_ことです。
たとえば、アプリケーションで複数の犬を作成する必要があるとします。しかし、作成する基本的な犬には、name プロパティしかありません。
使用する場面
- 継承チェーンを作成せずに、複数のクラスへ再利用可能な機能を追加する必要がある場合に使用します
- 複数のソースから振る舞いを組み合わせたい場合に役立ちます
使用しない場面
- フック(React)やコンポーザブル(Vue)によるコンポジションで、同じ結果をより追跡しやすく実現できる場合
- プロトタイプ汚染のリスクがある場合。ミックスインは共有プロトタイプを変更し、名前の衝突を引き起こす可能性があります
- 追加する機能が、ユーティリティ関数やモジュールのインポートで十分に実現できるほど単純な場合
手順
Object.assign()を使って、ミックスインのプロパティをクラスのプロトタイプに追加します- プロトタイプ汚染に注意してください。プロトタイプの変更は、予期しない動作につながる可能性があります
- React では、ミックスインよりフックを優先してください(React チームはミックスインを推奨していません)
- 再利用可能な振る舞いを設計する際は、継承よりコンポジションを検討してください
詳細
class Dog {
constructor(name) {
this.name = name;
}
}
犬は名前があるだけでなく、もっと多くのことができるべきです。吠えたり、しっぽを振ったり、遊んだりできる必要があります。これらを直接 Dog に追加する代わりに、bark、wagTail、play プロパティを提供するミックスインを作成できます。
const dogFunctionality = {
bark: () => console.log("Woof!"),
wagTail: () => console.log("Wagging my tail!"),
play: () => console.log("Playing!"),
};
Object.assign メソッドを使って、dogFunctionality ミックスインを Dog のプロトタイプに追加できます。このメソッドを使うと、対象オブジェクト(この場合は Dog.prototype)にプロパティを追加できます。dogFunctionality のプロパティは Dog のプロトタイプに追加されるため、Dog の新しいインスタンスはすべてこれらのプロパティにアクセスできます。
class Dog {
constructor(name) {
this.name = name;
}
}
const dogFunctionality = {
bark: () => console.log("Woof!"),
wagTail: () => console.log("Wagging my tail!"),
play: () => console.log("Playing!"),
};
Object.assign(Dog.prototype, dogFunctionality);
最初のペット pet1 を作成し、Daisy と名付けましょう。dogFunctionality ミックスインを Dog のプロトタイプに追加したので、Daisy は歩いたり、しっぽを振ったり、遊んだりできるはずです。
const pet1 = new Dog("Daisy");
pet1.name; // Daisy
pet1.bark(); // Woof!
pet1.play(); // Playing!
完璧です。ミックスインを使えば、継承を使わずにクラスやオブジェクトへカスタム機能を簡単に追加できます。
継承を使わずにミックスインで機能を追加できますが、ミックスイン自体は継承を使うこともできます。
ほとんどの哺乳類は歩いたり眠ったりもできます。犬は哺乳類なので、歩いたり眠ったりできるはずです。
walk と sleep プロパティを追加する animalFunctionality ミックスインを作成しましょう。
const animalFunctionality = {
walk: () => console.log("Walking!"),
sleep: () => console.log("Sleeping!"),
};
Object.assign を使って、これらのプロパティを dogFunctionality のプロトタイプに追加できます。この場合、対象オブジェクトは dogFunctionality です。
const animalFunctionality = {
walk: () => console.log("Walking!"),
sleep: () => console.log("Sleeping!"),
};
const dogFunctionality = {
bark: () => console.log("Woof!"),
wagTail: () => console.log("Wagging my tail!"),
play: () => console.log("Playing!"),
walk() {
super.walk();
},
sleep() {
super.sleep();
},
};
Object.assign(dogFunctionality, animalFunctionality);
Object.assign(Dog.prototype, dogFunctionality);
完璧です。Dog の新しいインスタンスはすべて、walk メソッドと sleep メソッドにもアクセスできるようになりました。
実世界のミックスインの例は、ブラウザー環境の Window インターフェースに見られます。Window オブジェクトは、WindowOrWorkerGlobalScope と WindowEventHandlers のミックスインから多くのプロパティを実装しており、setTimeout や setInterval、indexedDB、isSecureContext などのプロパティにアクセスできます。
ミックスインはオブジェクトに_機能を追加する_ためだけに使われるため、WindowOrWorkerGlobalScope 型のオブジェクトを作成することはできません。
React(ES6 以前)
ES6 クラスが導入される前は、React コンポーネントに機能を追加するためにミックスインがよく使われていました。React チームは、ミックスインを使うとコンポーネントに不要な複雑さが生じやすく、保守や再利用が難しくなるため、ミックスインの使用を推奨していません。その代わりに、React チームは高階コンポーネントの使用を推奨していました。現在では、多くの場合、これらをフックで置き換えられます。
ミックスインを使うと、オブジェクトのプロトタイプに機能を注入することで、継承を使わずにオブジェクトへ簡単に機能を追加できます。オブジェクトのプロトタイプを変更するのは悪い慣習とされています。プロトタイプ汚染につながる可能性があり、関数の由来が不明確になるためです。
ソース
参考資料
- Functional Mixins - Eric Elliott
- Mixins - JavaScript Info