3

そのため、JavaScriptコードにバグがあり、適度に夢中になっています。私はJavaScriptで古典的な継承を模倣しようとしています(ここで読んだ投稿の半分は、JavaScriptをそのようなフレームワークにねじらないように言っていますが、私のアプリケーションには、サーバー側のphpコードの継承構造)。そして、ほとんどの場合、すべてが機能しているように見えます。クラスを拡張するために使用している関数は次のとおりです。

Function.prototype.inheritsFrom = function(parentClass) {
//:: Ordinary Classes
if (parentClass.constructor == Function) {
    this.prototype             = new parentClass();
    this.prototype.constructor = this;
    this.prototype.parent      = parentClass.prototype;

//:: Abstract Classes
} else {
    this.prototype             = parentClass;
    this.prototype.constructor = this;
    this.prototype.parent      = parentClass;
}
return this;
}

私自身の作品ではなく、オンラインで見つけましたが、うまく機能しています。追加する必要があるのは、プロトタイプチェーンで親メソッドを検索するための「スーパー」関数だけでした。前述のコードの親構造は、すべての場合に機能するとは限りません。

いずれにせよ、コードのバグを修正していると、ベース「クラス」のメソッドの1つが、すべてのサブクラスから作成されたすべてのインスタンスの同じ変数にアクセス/変更していることがわかりました。つまり、1つのインスタンスが、そのオブジェクトコンテキストに対してローカルである必要があるこの変数を変更するときはいつでも、実際には、すべてのインスタンス間で共有されるいくつかの変数を変更します。基本クラスは次のとおりです。

    function DataManipulatorControl() {
    //|| Private Members ||//

    var that = this;



    //|| Properties ||//

    //|| Root ID
    this.rootID = function(value) {
        if (value !== undefined) {
            if (_root_id !== null) {
                alert('@ ' + _root_id);
                $('#' + _root_id).prop('js_object', null);
            }
            _root_id = value;
            $('#' + _root_id).prop('js_object', this);
        }
        return _root_id;
    }
    var _root_id = null;

    // other properties/methods
}



//|| Class: DataManipulatorContainerControl
function DataManipulatorContainerControl() {
    //|| Private Members ||//

    var that = this;


    // subclass properties/methods
}
DataManipulatorContainerControl.inheritsFrom(DataManipulatorControl);

前述したように、これらのプロトタイプの新しいインスタンスを作成すると、1つのインスタンスのrootIDを変更すると、すべてのインスタンスのrootIDが変更されることがわかりました。私の最初の考えは、どこかで「var」を忘れてしまったこと、そして私の関数がグローバルコンテキストにアクセスしていることでした。しかし、そうではないようです。次の考えは、プロトタイプのローカル変数を使用しているということです。プロトタイプのコンストラクター呼び出しのローカル変数は、スコープ内に既にあるメソッドからアクセスしない限り、プロトタイプのコンストラクター呼び出しの外部からアクセスできないようにする必要があるため、これも私にはあまり意味がありません。もちろん、rootID()関数にはスコープがありますが、プロトタイプではなく、呼び出し元のオブジェクトのオブジェクトコンテキストを使用して実行されるという印象を受けました。

だから、私は非常に混乱しています。この問題に当てることができるどんな光も、多くの感謝を呼び起こすでしょう。

編集:PRBが提供する記事では、この問題をきれいに解決するソリューションについて説明しています-ほとんどの場合。この記事では、すべてを正しく初期化するには、子クラスのコンストラクター内から親クラスのコンストラクターも呼び出す必要があると述べています。その結果、すべてのメソッドが新しく作成され、親クラスのローカル変数の独自のバージョンがクロージャに含まれます。

このアプローチには1つの欠点があるようです(すべてのインスタンスで関数を複製することによる効率の問題は別として)。超機能を試みてプロトタイプチェーンから「オーバーライドされた」関数を呼び出そうとすると、この問題が再発します。プロトタイプは、以前と同様に、単一のオブジェクトのインスタンスであり、関数のバージョンを呼び出そうとすると、インスタンスのローカル変数にアクセスしようとします。

すべてのデータを公開することを除けば、それでも私が見た中で最高の解決策です:-)。

4

2 に答える 2

2

この行の使用はおそらく問題です:

this.prototype             = new parentClass();

これは、関数のすべてのインスタンスが、parentClass()で定義された同じメモリBLOBを共有することを意味します。これが、1つの値を変更すると、それらすべてに影響する理由です。

于 2012-11-17T22:28:20.427 に答える
0

さて、それで私は夕食の間それについて考えていました、そして私は何が起こっているかについてのハンドルを持っているかもしれないと思います。オブジェクトコンテキストが変化するために関数で使用できる変数と、クロージャのために関数で使用できる変数を混同していると思います。

PRBが指摘したように、私のrootID()関数は、インスタンス化されたプロトタイプから常にメモリのブロブにアクセスします。_root_idは、基本クラスのコンストラクターに対してローカルな変数として作成されるため、その基本クラスがインスタンス化されてプロトタイプになると、プロトタイプの関数を使用するすべてのサブクラスは、結果として、コンストラクターで作成された1つの変数に対して読み取り/書き込みを行います。 。

したがって、これはプロトタイプを作成するための有効な方法ですが、コンストラクターのローカル変数を使用してオブジェクトのデータを非表示にすると、サブクラスでは正しく機能しません。むしろ、オブジェクトのコンテキストが変化すると変化するように、「this」の変数の読み取り/書き込みを行う必要があります。これを処理するためのより良い方法、つまりデータの隠蔽に従う方法を誰かが知っている場合は、遠慮なくコメントしてください。私が一緒に仕事をしている開発者の多くは、アクセサーを介してではなく、データメンバーに直接アクセスすることに何の不安もありません。コードの保守が困難になります:-/。

とにかく、説明してくれたPRBに感謝します!〜ネイト

于 2012-11-17T23:15:58.403 に答える