3

私は JavaScript のスキルを更新していて、オブジェクトが別のオブジェクトから継承できる方法を考え出しました。継承はツリー状にする必要があります。

Parent->Child->Child2

を拡張しましたFunction.prototype(これは悪い考えだと言わないでください。これは後で変更できます)

Function.prototype.extend = function(child)
{
    // save child prototype
    var childPrototype = child.prototype;
    // copy parent to child prototype
    child.prototype = Object.create(this.prototype);
    // merge child prototype
    for (var property in childPrototype) {
        child.prototype[property] = childPrototype[property];
    }
    child.prototype.constructor = child;
    child.prototype.$parent = this.prototype;
    return child;
};

親オブジェクト:

var Parent = (function()
{
    var Parent = function(x, y)
    {
        this.x = x;
        this.y = y;
        console.log('Parent constr', x, y);
    }

    Parent.prototype.move = function(x, y)
    {
        this.x += x;
        this.y += y;
        console.log('Parent moved.');
    };

    return Parent;

}());

第一子:

var Child = Parent.extend(function()
{
    var Child = function(x, y)
    {
        this.$parent.constructor(x, y);
        console.log('Child constr');
    }

    Child.prototype.print = function()
    {
        console.log('Child print', this.x, this.y);
    };

    // override
    Child.prototype.move = function(x, y)
    {
        this.$parent.move(x, y);
        console.log('Child moved.');
    };

    return Child;

}());

2 番目の子:

var Child2 = Child.extend(function()
{
    var Child2 = function(x, y)
    {
        this.$parent.constructor(x, y);
        console.log('Child2 constr');
    }

    // override
    Child2.prototype.move = function(x, y)
    {
        this.$parent.move(x, y); // call parent move
        console.log('Child2 moved.');
    };

    return Child2;

}());

ここまでは順調ですね。親のコンストラクターとメソッドを呼び出し、メソッドをオーバーライドすることさえできます。

var child = new Child2(1, 1);
child.move(1, 1);
child.print();

次の正しい出力が得られます。

Parent constr 1 1
Child constr
Child2 constr
Parent moved.
Child moved.
Child2 moved.
Child print 2 2

しかし、2 番目の子からオーバーライドをコメントアウトすると、次の出力が得られます。

Parent constr 1 1
Child constr
Child2 constr
Parent moved.
Child moved.
Child moved. -> WHY??
Child print 2 2

Child moved.2回出力される理由がわかりません。結果は正しいですが、何か奇妙なことが起こっています。

編集:

最後に、問題を調査してさらに掘り下げた後、私は素晴らしい解決策を見つけました:

// call method from parent object
Object.getPrototypeOf(Child2.prototype).move.apply(this, arguments);

に別の拡張機能を作成しましたFunction

Function.prototype.getParent = function()
{
    return Object.getPrototypeOf(this.prototype);
};

次に、たとえばChild2move メソッド:

Child2.prototype.move = function(x, y)
{        
    Child2.getParent().move.call(this, x, y);
};

だから私はもう必要が$parentなく、望ましい結果が得られます。

別の解決策は、親プロトタイプを直接呼び出すことです。

Child2.prototype.move = function(x, y)
{        
    Child.prototype.move.call(this, x, y);
};
4

2 に答える 2

1

apply を使用して正しいスコープで関数を呼び出す必要がありthisます。親にアクセスするために使用することも安全ではありません。これは、実際には JS のデバッグについて学習するための優れた問題です。ブレークポイントを使用して、すべてが起こっていることを確認できます。

あなたの最初の子を見て:

// override
Child.prototype.move = function(x, y)
{
    this.$parent.move(x, y);
    console.log('Child moved.');
};

を呼び出してこのコードに到達するとchild.move(1,1)、 は をthis指しchild、何をchild.$parent.move指すか? もちろん、最初の Child での実装です!

この 2 回目thisは親のプロトタイプを指しているので、これで問題ありません。

だからこそこうなったのです、どうしたらよいでしょうか?それはあなた次第です。私のデフォルトの考えだったので apply言いましthis.$parent.move.apply(this, arguments);た。これにより、その値が常に同じものを指していることを保証し、常に確実に $parent にアクセスできます。this

もちろん、これは実際のオブジェクト インスタンスにアクセスできないことを意味するため、純粋なユーティリティ関数に対してのみ有用ですが、これは実際には新しいことではありません。print (オブジェクト インスタンスの x および y メンバーにアクセスする) をオーバーライドし、親を呼び出そうとすると、それも壊れてしまいます。

于 2013-05-30T13:36:07.273 に答える
1

extends メソッドは、継承されたプロパティとメソッドを新しいオブジェクトのプロトタイプにアタッチします。親オブジェクトのプロトタイプと等しい $parent オブジェクトも作成します。これにより重複が発生することに注意してください。2 番目の子には、次の 3 つの move メソッドがあります。

child.protoype.move
child.$parent.move
child.$parent.$parent.move

オーバーライドをコメントアウトしても、移動メソッドは 3 つあります (ただし、2 つは同じ関数への参照です)。

したがって、child.move を実行すると、次のようになります。

child.prototype.move呼び出すchild.$parent.move呼び出すchild.$parent.$parent.move

child.prototype.move と child.$parent.move が両方とも同じ関数への参照であるという事実は、それらが 2 回実行されることを防ぎません。

于 2013-05-30T13:28:03.223 に答える