4

JavaScript プロトタイプの継承パターンに関するいくつかのチュートリアルを読みましたが、次の 2 つの中でどちらがベスト プラクティスであるかはわかりません。多くの人がこの継承パターンを行っていることに気付きました。

var A = function (){}
A.prototype = {} 

var B = function () {
    A.apply(this, arguments); // Calling the constructor of A
}
B.prototype = new A(); // Inherit from A through an instance

代わりに、代わりに次のパターンを実行するソースがいくつかあります。

var A = function (){}
A.prototype = {} 

var B = function () {
    A.apply(this, arguments); // Calling the constructor of A
}
for (var prop in A.prototype) {
    B.prototype[prop] = A.prototype[prop]; // Inherit from A by copying every property/methods from A
}

どちらのパターンも機能しますが、後者の継承パターン (つまり、親のプロトタイプから各プロパティ/メソッドをコピーする) を使用する人はめったにいません。プロパティ/メソッドを親から子に直接コピーすると何か問題がありますか? また、これら 2 つのパターンは本質的にいくつかの点で異なりますか?

ありがとうございました。

4

4 に答える 4

9

これらのパターンは非常に異なり、ご想像のとおり、最初のパターンの方が優れています(ただし、可能な限り最良ではありません)。比較してみましょう:

最もモダンで最高のパターン

B.prototype = Object.create(A.prototype);

これは、Object.create関数を使用してB.prototype、内部[[Prototype]]が。である新しいオブジェクトに設定しますA.prototype。これは基本的にまさにあなたが望むものです:それは適切なときにBインスタンスを委任させA.prototypeます。

元のパターン

B.prototype = new A();

ES5が登場する前は、これが以前のやり方でしたObject.create。(回避策はありましたが、広く使用されていなくても。)このアプローチの問題は、インスタンスのみのデータプロパティも最終的にB.prototypeになってしまうことです。これは望ましくありません。Aさらに、のコンストラクターを呼び出すことによる副作用が発生します。基本的に、このアプローチは、オブジェクトのインスタンス化とオブジェクトの構築という、関連しているが異なる2つの概念を混乱させます。

あなたの投稿からの非標準パターン

for (var prop in A.prototype) {
    B.prototype[prop] = A.prototype[prop];
}

このパターンにはいくつかの問題があります。

  • Aプロトタイプチェーンのあらゆる場所から、に至るまでObject、直接にプロパティをコピーしますB.prototype。これは、プロトタイプチェーンを単一のレベルに押しつぶすのではなく、プロトタイプチェーンを委任する必要があるというプロトタイプ継承の目的の多くを無効にします。
  • 列挙不可能なプロパティをスキップA.prototypeするため、列挙可能なプロパティとそのプロトタイプチェーンのみを取得します。for ... in
  • A.prototypeゲッター/セッター関数の代わりに、ゲッターまたはセッター(またはそのプロトタイプチェーン)で定義されたゲッターまたはセッターを混乱させ、それらの値をコピーするだけです。
  • あなたのコードを読もうとしている人にとっては奇妙に見えるでしょう。
于 2012-04-09T07:25:01.187 に答える
2

プロトタイプを使用せずに、ファクトリパターンを使用する別の方法を次に示します。

/* parent */
function Animal(name, legs)
{
    /* members and methods declared within a new object */
    var obj =
    {
        name: name,
        legs: legs,
        description: function ()
        {
            return this.name + " is an animal";
        },
        printNumLegs: function ()
        {
            return this.name + " has " + this.legs + " legs";
        }
    }

    return obj;
}

/* subclass */
function Cat()
{
    /* apply parent arguments in the context of the current object */
    var obj = Animal.apply(this, arguments);

    /* public member */
    obj.furColor = "black";
    /* private member */
    var sleeping = false;
    /* private method */
    function printSleepingState()
    {
        /* can access public member ('name') without it being passed as constructor argument */
        return obj.name + " is " + (sleeping ? "sleeping" : "not sleeping");
    }

    /* declare a new method */
    obj.printFurColor = function ()
    {
        return obj.name + " has " + obj.furColor + " fur";
    }

    /* overloading */

    /* save parent method if needed */
    var oldDescription = obj.description;

    obj.description = function ()
    {
        return oldDescription.apply(obj) + " and " + printSleepingState();
    }

    return obj;
}

/* create object without new */
var kitty = Cat("Kitty", 4);
kitty.furColor = "yellow";

とにかく「最良の」方法はありません...それはすべて好みの問題です。

于 2012-04-09T08:34:18.480 に答える
1

最初のバリアントは、一般的なプロトタイプの継承のように見えます。関数 A がいくつかの必要な引数を取る場合、関数 B のプロトタイプを設定するときにそれらを渡す必要があるという小さなマイナスがあります。また、プロトタイプの親オブジェクトのインスタンスも作成します。そのような動作が必要ない場合は、2 番目のバリアントが役に立ちます。

ところで: The third variant to make prototypal inheritance を見てください。これも時々非常に便利です。

編集:私が理解している限り、3 番目のバリアントは Domenic のソリューションの昔ながらのバリアントです (Object.create 関数を持たない古いブラウザー用)。

于 2012-04-09T07:08:18.087 に答える
0

この回答が遅れていることは承知していますが、将来の読者のためにコメントを追加すると思いました.

上記以外にもオプションがあることを付け加えておきます。

継承に代わるものはミックスインです。http://javascriptweblog.wordpress.com/2011/05/31/a-fresh-look-at-javascript-mixins/ にアクセスしてください。

ミックスインとアプローチ 2 の間にはいくつかの類似点がありますが、それらは異なります。多くの場合、Mixin は、複数の Mixin から 1 つのオブジェクトを混合する (動作を引き継ぐ) ことができるため、上記の継承手法よりもはるかに柔軟です。ミックスインは動作を追加しますが、通常、オブジェクトに状態を追加しません。

于 2014-01-16T08:47:44.220 に答える