[解決済み] JavaScriptにおける多重継承とプロトタイプ
2022-06-05 03:49:17
質問
JavaScriptで初歩的な多重継承を行う必要が出てきました。(私はこれが良いアイデアかどうかを議論するためにここにいるわけではないので、親切にもそれらのコメントはあなた自身の中に留めておいてください)。
私はただ、誰かがこれを試みて成功したかどうか、そしてどのようにそれを行ったかを知りたいのです。
要約すると、私が本当に必要としているのは、複数のプロトタイプからプロパティを継承することができるオブジェクトを持つことです。 チェーン (すなわち、各プロトタイプはそれ自身の適切なチェーンを持つことができます)、しかし優先順位の与えられた順序で (それは最初の定義のための順序でチェーンを検索します)、オブジェクトを持つことができることです。
これが理論的にどのように可能であるかを示すために、一次チェーンの端に二次チェーンを取り付けることによって達成することができますが、これはそれらの以前のプロトタイプのすべてのインスタンスに影響し、私が望むものではありません。
どうでしょうか?
どのように解決するのですか?
ECMAScript 6 では、多重継承を実現するために プロキシオブジェクト .
実装
function getDesc (obj, prop) {
var desc = Object.getOwnPropertyDescriptor(obj, prop);
return desc || (obj=Object.getPrototypeOf(obj) ? getDesc(obj, prop) : void 0);
}
function multiInherit (...protos) {
return Object.create(new Proxy(Object.create(null), {
has: (target, prop) => protos.some(obj => prop in obj),
get (target, prop, receiver) {
var obj = protos.find(obj => prop in obj);
return obj ? Reflect.get(obj, prop, receiver) : void 0;
},
set (target, prop, value, receiver) {
var obj = protos.find(obj => prop in obj);
return Reflect.set(obj || Object.create(null), prop, value, receiver);
},
*enumerate (target) { yield* this.ownKeys(target); },
ownKeys(target) {
var hash = Object.create(null);
for(var obj of protos) for(var p in obj) if(!hash[p]) hash[p] = true;
return Object.getOwnPropertyNames(hash);
},
getOwnPropertyDescriptor(target, prop) {
var obj = protos.find(obj => prop in obj);
var desc = obj ? getDesc(obj, prop) : void 0;
if(desc) desc.configurable = true;
return desc;
},
preventExtensions: (target) => false,
defineProperty: (target, prop, desc) => false,
}));
}
説明
プロキシオブジェクトは、ターゲットオブジェクトといくつかのトラップから構成され、基本的な操作に対するカスタム動作を定義します。
他のオブジェクトを継承したオブジェクトを作成する際には
Object.create(obj)
. しかし、この場合、多重継承をしたいので
obj
プロキシを使い、基本的な操作を適切なオブジェクトにリダイレクトしています。
こんなトラップを使っています。
-
このトラップは
has
トラップ は、トラップとしてin
演算子 . 私が使っているのはsome
を使って、少なくとも一つのプロトタイプがそのプロパティを含んでいるかどうかをチェックします。 -
は
get
トラップ はプロパティ値を取得するためのトラップです。使うのはfind
を使って、そのプロパティを含む最初のプロトタイプを見つけ、その値を返すか、適切なレシーバーでゲッターを呼び出します。これを処理するのがReflect.get
. もしプロトタイプがそのプロパティを含んでいなければ、私はundefined
. -
は
set
トラップ はプロパティ値を設定するためのトラップです。使用するのはfind
を使用して、そのプロパティを含む最初のプロトタイプを見つけ、適切なレシーバーでそのセッターを呼び出します。セッターがない場合や、そのプロパティを含むプロトタイプがない場合は、適切なレシーバーで値が定義されます。これを処理するのがReflect.set
. -
は
enumerate
トラップ は、トラップとしてfor...in
ループ . 列挙可能なプロパティは、最初のプロトタイプから、次に2番目のプロトタイプから、というように反復しています。一度反復されたプロパティは、再度反復することを避けるためにハッシュテーブルに格納します。
警告 : このトラップはES7ドラフトで削除され、ブラウザでは非推奨となっています。 -
は
ownKeys
トラップ は、トラップとしてObject.getOwnPropertyNames()
. ES7 からはfor...in
ループは[[GetPrototypeOf]]を呼び続け、それぞれの独自プロパティを取得します。そこで、すべてのプロトタイプのプロパティを反復させるために、このトラップを使用して、すべての列挙可能な継承されたプロパティを自分のプロパティのように表示させるようにしています。 -
は
getOwnPropertyDescriptor
トラップ は、トラップとしてObject.getOwnPropertyDescriptor()
. すべての列挙可能なプロパティを自身のプロパティのように表示させるためにownKeys
トラップは十分ではありません。for...in
ループは、それらが列挙可能かどうかをチェックするために記述子を取得します。そこで、私はfind
を使って、そのプロパティを含む最初のプロトタイプを見つけ、プロパティの所有者を見つけるまでそのプロトタイプチェーンを繰り返し、そのディスクリプタを返します。そのプロパティを含むプロトタイプがない場合、私はundefined
. 記述子は設定可能にするために変更されます。そうしないと、いくつかのプロキシ不変条件を破る可能性があります。 -
は
preventExtensions
とdefineProperty
トラップは、これらの操作によってプロキシのターゲットが変更されるのを防ぐためだけに 含まれています。そうでなければ、プロキシの不変条件を破ることになりかねません。
もっといろいろなトラップがありますが、私は使いません。
-
は
getPrototypeOf
トラップ を追加することができますが、複数のプロトタイプを返す適切な方法はありません。これはつまりinstanceof
も動作しないことを意味します。そこで、ターゲットのプロトタイプを取得させるのだが、最初はnullである。 -
は
setPrototypeOf
トラップ が追加され、プロトタイプを置き換えるオブジェクトの配列を受け入れることができます。これは読者のための練習として残されています。ここでは、私はそれがターゲットのプロトタイプを変更させるだけで、トラップはターゲットを使用しないので、あまり有用ではありません。 -
は
deleteProperty
トラップ は、自分のプロパティを削除するためのトラップです。プロキシは継承を表すので、これはあまり意味がないでしょう。とにかくプロパティがないはずのターゲットで削除を試みさせました。 -
は
isExtensible
トラップ は拡張性を取得するためのトラップです。不変量がターゲットと同じ拡張性を返すことを強制することを考えると、あまり有用ではない。なので、拡張性を持つであろうターゲットに操作をリダイレクトさせるだけにしている。 -
は
apply
とconstruct
トラップは、呼び出しやインスタンス化のためのトラップです。これらは、ターゲットが関数やコンストラクタである場合にのみ有効です。
例
// Creating objects
var o1, o2, o3,
obj = multiInherit(o1={a:1}, o2={b:2}, o3={a:3, b:3});
// Checking property existences
'a' in obj; // true (inherited from o1)
'b' in obj; // true (inherited from o2)
'c' in obj; // false (not found)
// Setting properties
obj.c = 3;
// Reading properties
obj.a; // 1 (inherited from o1)
obj.b; // 2 (inherited from o2)
obj.c; // 3 (own property)
obj.d; // undefined (not found)
// The inheritance is "live"
obj.a; // 1 (inherited from o1)
delete o1.a;
obj.a; // 3 (inherited from o3)
// Property enumeration
for(var p in obj) p; // "c", "b", "a"
関連
-
VUEグローバルフィルターの概念と留意点、基本的な使い方
-
[解決済み】gulp anythingを実行するたびに、アサーションエラーが発生します。- タスク関数を指定する必要があります
-
[解決済み] JavaScriptで "use strict "は何をするのか、その根拠は?
-
[解決済み] JavaScriptで文字列が部分文字列を含むかどうかを確認する方法は?
-
[解決済み] あるJavaScriptファイルを他のJavaScriptファイルにインクルードするにはどうすればよいですか?
-
[解決済み] JavaScriptでメールアドレスを検証するのに最適な方法は何ですか?
-
[解決済み] JavaScriptでタイムスタンプを取得する方法は?
-
[解決済み】JavaScriptの比較では、どちらの等号演算子(== vs ===)を使うべきですか?
-
[解決済み】JavaScriptで文字列の出現箇所をすべて置換する方法
-
[解決済み】オブジェクトからプロパティを削除する(JavaScript)
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
vue3.0プロジェクトのアーキテクチャを構築するための便利なツール
-
HTML+CSS+JavaScriptで簡単な三目並べゲームを作成する。
-
vueにおけるfilterの適用シーンについて解説します。
-
Vueのイベント処理とイベントモディファイアの解説
-
[解決済み】GETできない / Nodejsエラー
-
[解決済み】"フォームが接続されていないため、フォームの送信がキャンセルされました "というエラーの取得について
-
[解決済み】JavaScript TypeError: null のプロパティ 'style' を読み取ることができない
-
[解決済み】JavaScriptエラー(Uncaught SyntaxError: Unexpected end of input)
-
[解決済み】ERROR エラーです。スイッチのname属性が指定されていないフォームコントロールの値アクセッサがない
-
[解決済み】リクエストに失敗していないのに、「TypeError: failed to fetch」が表示される。