2

JavaScript プロトタイプの継承に関するページを読みましたが、検証を伴うコンストラクターの使用に対処するものは見つかりませんでした。このコンストラクターを動作させることはできましたが、理想的ではないことはわかっています。つまり、プロトタイプの継承を利用していません。

function Card(value) {
    if (!isNumber(value)) {
        value = Math.floor(Math.random() * 14) + 2;
    }

    this.value = value;
}

var card1 = new Card();
var card2 = new Card();
var card3 = new Card();

これにより、ランダムな値を持つ 3 つの Card オブジェクトが生成されます。ただし、私が理解している方法は、この方法で新しい Card オブジェクトを作成するたびに、コンストラクター コードをコピーしていることです。代わりにプロトタイプの継承を使用する必要がありますが、これは機能しません。

function Card(value) {
    this.value = value;
}

Object.defineProperty( Card, "value", {
    set: function (value) {
        if (!isNumber(value)) {
            value = Math.floor(Math.random() * 14) + 2;
        }

        this.value = value;
    }
});

これも機能しません:

Card.prototype.setValue = function (value) {
    if (!isNumber(value)) {
        value = Math.floor(Math.random() * 14) + 2;
    }

    this.value = value;
};

一つには、私はもう呼び出すことができませんnew Card()var card1 = new Card(); card1.setValue();これは非常に非効率的で醜いように思えます。しかし、実際の問題は、各 Card オブジェクトの value プロパティを同じ値に設定することです。ヘルプ!


編集

Bergi の提案に従って、コードを次のように変更しました。

function Card(value) {
    this.setValue(value);
}

Card.prototype.setValue = function (value) {
    if (!isNumber(value)) {
        value = Math.floor(Math.random() * 14) + 2;
    }

    this.value = value;
};

var card1 = new Card();
var card2 = new Card();
var card3 = new Card();

これにより、ランダムな値を持つ 3 つの Card オブジェクトが生成されます。これはすばらしいことです。後で setValue メソッドを呼び出すことができます。ただし、クラスを拡張しようとすると転送されないようです:

function SpecialCard(suit, value) {
    Card.call(this, value);

    this.suit = suit;
}

var specialCard1 = new SpecialCard("Club");
var specialCard2 = new SpecialCard("Diamond");
var specialCard3 = new SpecialCard("Spade");

エラーが発生しましthis.setValue is not a functionた。


編集 2

これはうまくいくようです:

function SpecialCard(suit, value) {
    Card.call(this, value);

    this.suit = suit;
}

SpecialCard.prototype = Object.create(Card.prototype);
SpecialCard.prototype.constructor = SpecialCard;

これは良い方法ですか?


最終編集!

Bergi と Norguard のおかげで、最終的にこの実装にたどり着きました。

function Card(value) {
    this.setValue = function (val) {
        if (!isNumber(val)) {
            val = Math.floor(Math.random() * 14) + 2;
        }

        this.value = val;
    };

    this.setValue(value);
}

function SpecialCard(suit, value) {
    Card.call(this, value);

    this.suit = suit;
}

Bergi は、プロトタイプ チェーンを継承できなかった理由を特定するのを手伝ってくれました。Norguard は、プロトタイプ チェーンをいじらないほうがよい理由を説明してくれました。コードがより簡潔で理解しやすいため、このアプローチが気に入っています。

4

2 に答える 2

2

私が理解している方法は、この方法で新しい Card オブジェクトを作成するたびに、コンストラクターコードをコピーしていることです

いいえ、実行しています。問題はありません。コンストラクターは完璧に動作します。これは、次のようになります。

問題は、価値を創造するときにのみ発生します。関数を呼び出すたびに、プライベート変数などの独自の値のセットが作成されます (何もありません)。別の特別な値である特権メソッドを作成しない限り、通常、それらはガベージコレクションされます。これは、それが存在するスコープへの参照を保持する公開された関数です。そして、はい、すべてのオブジェクトにはそのような関数の独自の「コピー」があります。プライベート変数にアクセスしないものすべてをプロトタイプにプッシュする必要がある理由。

 Object.defineProperty( Card, "value", ...

待って、いいえ。ここでは、コンストラクターの function にプロパティを定義しますCard。これはあなたが望むものではありません。このコードをインスタンスで呼び出すことはできますが、評価するときにthis.value = value;それ自体を再帰的に呼び出すことに注意してください。

 Card.prototype.setValue = function(){ ... }

これは良さそうです。Cardたとえば、インスタンスの値を変更するときなど、後で検証コードを使用するときに、オブジェクトでこのメソッドが必要になる可能性がありCardます (私はそうは思いませんが、わかりませんか?)。

しかし、その後、 new Card() を呼び出すことができなくなりました

ああ、確かにできます。メソッドはすべてのインスタンスに継承されCard、これにはコンストラクターが適用されたインスタンス ( this) が含まれます。そこから簡単に呼び出すことができるので、コンストラクターを次のように宣言します。

function Card(val) {
    this.setValue(val);
}
Card.prototype...

クラスを拡張しようとしても転送されないようです。

はい、そうではありません。コンストラクター関数を呼び出しても、プロトタイプ チェーンは設定されません。newキーワードを使用すると、継承されたオブジェクトがインスタンス化され、コンストラクターが適用されます。あなたのコードでは、SpecialCards はSpecialCard.prototypeオブジェクトから継承します (それ自体はデフォルトの Object プロトタイプから継承します)。これで、通常のカードと同じオブジェクトに設定するか、そのオブジェクトから継承させることができます。

SpecialCard.prototype = Card.prototype;

したがって、すべてのインスタンスが同じオブジェクトから継承されます。つまり、SpecialCards には、通常の s にはない (プロトタイプからの) 特別なメソッドがCardありません... また、instanceof演算子は正しく機能しなくなります。

したがって、より良い解決策があります。s プロトタイプ オブジェクトが!SpecialCardから継承するようにしCard.prototypeます。これは、この仕事を正確に行うように設計されている(すべてのブラウザーでサポートされているわけではありません。回避策Object.createが必要になる場合があります) を使用して実行できます。

SpecialCard.prototype = Object.create(Card.prototype, {
    constructor: {value:SpecialCard}
});
SpecialCard.prototype.specialMethod = ... // now possible
于 2012-07-26T05:09:09.083 に答える
2

コンストラクターに関しては、各カードはコンストラクター内で定義されたメソッドの独自の一意のコピーを取得します。

this.doStuffToMyPrivateVars = function () { };

また

var doStuffAsAPrivateFunction = function () {};

それらが独自の一意のコピーを取得する理由は、オブジェクト自体と同時にインスタンス化された関数の一意のコピーのみが、囲まれた値にアクセスできるためです。

それらをプロトタイプチェーンに入れることで、次のことができます。

  1. それらを 1 つのコピーに制限します (作成後にインスタンスごとに手動でオーバーライドしない限り)
  2. すべてのプライベート変数にアクセスするメソッドの機能を削除します
  3. プログラムの途中ですべてのインスタンスのプロトタイプ メソッド/プロパティを変更することで、友人や家族を簡単にイライラさせられるようにします。

実際には、古い Blackberry や古い iPod Touch で動作するゲームを作成する予定がない限り、同封された関数の余分なオーバーヘッドについてあまり心配する必要はありません。

また、日々の JS プログラミングでは、適切にカプセル化されたオブジェクトによる追加のセキュリティに加えて、モジュール/revealing-module パターンとクロージャによるサンドボックス化の追加の利点が、メソッドの冗長コピーを関数に添付するコストを大幅に上回ります。

また、本当に心配している場合は、エンティティ/システム パターンを確認することをお勧めします。ここでは、エンティティはほとんど単なるデータ オブジェクトです (プライバシーが必要な場合は、独自の get/set メソッドを使用します)... ...そして、特定の種類のエンティティはそれぞれ、そのエンティティ/コンポーネント タイプ用にカスタムメイドされたシステムに登録されます。

IE: デッキ内の各カードを定義する Card-Entity があります。各カードにはCardValueComponent、、、、、、CardWorldPositionComponentなどがCardRenderableComponentあります。CardClickableComponent

CardWorldPositionComponent = { x : 238, y : 600 };

次に、これらの各コンポーネントがシステムに登録されます。

CardWorldPositionSystem.register(this.c_worldPos);

各システムは、コンポーネントに格納された値に対して通常実行されるすべてのメソッドを保持します。システム (コンポーネントではなく) は、必要に応じて、同じエンティティによって共有されるコンポーネント間でデータをやり取りするためにチャットを行ったり来たりします (つまり、スペードのエースの位置/値/画像が異なるシステムからクエリされる可能性があるため、誰もが最新の状態に保たれています)。

次に、各オブジェクトを更新する代わりに、伝統的に次のようになります。

Game.Update = function (timestamp) { forEach(cards, function (card) { card.update(timestamp); }); };
Game.Draw = function (timestamp, renderer) { forEach(cards, function (card) { card.draw(renderer); }); };

今ではもっと似ています:

CardValuesUpdate();
CardImagesUpdate();
CardPositionsUpdate();
RenderCardsToScreen();

従来の更新の内部では、各項目が独自の入力処理/移動/モデル更新/スプライトシートアニメーション/AI などを処理し、各サブシステムを次々と更新し、各サブシステムがそれぞれを通過します。そのサブシステムに登録されたコンポーネントを持つエンティティを次々と。

そのため、固有の関数の数に対するメモリ使用量が少なくなります。しかし、それを行う方法について考えるという点では、それは非常に異なる宇宙です.

于 2012-07-26T05:53:24.163 に答える