web-dev-qa-db-ja.com

JavaScriptの継承:コンストラクターに引数がある場合

純粋なJavaScriptを使用して継承を行う、これは私が通常行うことです。

function A() {}
A.prototype.run = function () {};

function B() {}
B.prototype = new A;
B.prototype.constructor = B;

コンストラクターに渡す引数がないため、新しいAには不満はありません。現在、コンストラクターに渡す引数がある場合に継承を行うための適切な方法がわかりません。例えば、

function A(x, y) {}
A.prototype.run = function () {};

function B(x, y) {}
B.prototype = new A;
B.prototype.constructor = B;

次のような任意の値を渡すことができます。

B.prototype = new A(null, null);

場合によっては、Aのコンストラクターでxとyを検証する必要があります。極端な場合には、xまたはyをチェックするときにエラーをスローする必要があります。次に、Bが新しいAを使用してAから継承する方法はありません。

助言がありますか?

ありがとう!

46
Grace Shao

まあ、Aコンストラクターを実行せずにB.prototypeA.prototypeから継承するオブジェクトにしたい場合は、考えられるすべての副作用を回避するために、ダミーコンストラクターを使用してそれ、例えば:

function tmp() {}
tmp.prototype = A.prototype;
B.prototype = new tmp();
B.prototype.constructor = B;

この新しいオブジェクトの作成のロジックをカプセル化する関数を作成できます。例:

function inherit(o) {
  function F() {}; // Dummy constructor
  F.prototype = o; 
  return new F(); 
}

//...
B.prototype = inherit(A.prototype);
B.prototype.constructor = B;

最新のブラウザを対象とする場合、ECMAScript 5 Object.createメソッドを同じ目的で使用できます。

B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;
//..
23
CMS

問題は、Bのコンストラクターを呼び出すことができないため、Aのプロトタイプオブジェクトを簡単に作成できないことです。これは、_new B_が実行される前はコンストラクタのパラメータが不明であるためです。 BのプロトタイプにリンクするAのプロトタイプを作成するには、ダミーのコンストラクター関数が必要です。

_B.prototype = (function(parent){
    function protoCreator(){};
    protoCreator.prototype = parent.prototype;
    // Construct an object linking to A.prototype without calling constructor of A
    return new protoCreator();
})(A);
_

Bのプロトタイプオブジェクトを設定したら、AのコンストラクターでBのコンストラクターを呼び出す必要があります。

_function B(x, y) {
    // Replace arguments by an array with A's arguments in case A and B differ in parameters
    A.apply(this, arguments);
}
_

これで、new B(x, y)を呼び出してBをインスタンス化できるようになります。

Aでのパラメーター検証を含む完全に同じものについては、 a jsFiddle を参照してください。

元のコードでは_B.prototype.constructor = B_を設定しています。なぜあなたはこれをしているのか分かりません。 constructorプロパティは、prototypeプロパティが担当する継承階層には影響しません。名前付きコンストラクタをconstructorプロパティに含める場合は、コードを少し上から拡張する必要があります。

_// Create child's prototype – Without calling A
B.prototype = (function(parent, child){
    function protoCreator(){
        this.constructor = child.prototype.constructor
    };
    protoCreator.prototype = parent.prototype;
    return new protoCreator();
})(A, B);
_

_B.prototype_の最初の定義を使用すると、次の結果が得られます。

_var b = new B(4, 6);
b.constructor // A
console.info(b instanceof A); // true
console.info(b instanceof B); // true
_

拡張バージョン を使用すると、次のようになります。

_var b = new B(4, 6);
b.constructor // B
console.info(b instanceof A); // true
console.info(b instanceof B); // true
_

異なる出力の原因は、instanceofbのプロトタイプチェーン全体を追跡し、_A.prototype_または_B.prototype_(他の呼び出し)。 _b.constructor_プロトタイプは、インスタンスのプロトタイプを定義するために使用された関数を指します。なぜprotoCreatorを指さないのか疑問に思う場合、これは、そのプロトタイプが_A.prototype_の作成中に_B.prototype_で上書きされたためです。 更新された例 に示されている拡張定義は、このconstructorプロパティを修正して、より適切な(おそらくより期待されるため)関数を指すようにします。

日常的に使用する場合は、インスタンスのconstructorプロパティを完全に使用するという考えを破棄することをお勧めします。結果がより予測可能/期待されるため、代わりにinstanceofを使用してください。

6
Augustus Kling

これは古いトピックですが、とにかく返答するつもりだと思いました。それを行う2つの方法:

Pseudo Classicalの方法が最も一般的ですが、親コンストラクターを子コンストラクターで一度、プロトタイプを継承する間に一度呼び出す必要があるため、欠点があります。さらに、子のプロトタイプには親コンストラクターのすべてのプロパティが含まれ、子コンストラクターが呼び出されたときに上書きされます。私の個人的な選択はプロトタイプの継承です。

1.疑似古典的継承:

function A(x, y) {} 
A.prototype.run = function () {};
function B(x, y) {
    A.call(this,x,y);
}
B.prototype = new A();
B.prototype.constructor = B;

2.プロトタイプの継承:

function A(x, y) {}
A.prototype.run = function () {};
function B(x, y) {
    A.call(this,x,y);
}
B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;
5
A K

このことを考慮:

function B( x, y ) {
    var b = Object.create( new A( x, y ) );

    // augment b with properties or methods if you want to

    return b;
}

その後

var b = new B( 12, 13 );

これで、bAのインスタンスから継承され、A.prototypeから継承されます。

ライブデモ:http://jsfiddle.net/BfFkU/


Object.createはIE8には実装されていませんが、手動で簡単に実装できます。

if ( !Object.create ) {
    Object.create = function ( o ) {
        function F() {}
        F.prototype = o;
        return new F();
    };
}

これは、条件付きコメントを介してIE8以下でのみロードされるie8.jsファイル内に配置できます。

5
Šime Vidas