web-dev-qa-db-ja.com

Javascriptのクラス変数

クラス変数をjavascriptで機能させるのに少し問題があります。

プロトタイプの継承モデルは理解できたと思いましたが、明らかに理解していませんでした。プロトタイプはオブジェクト間で共有されるので、それらの変数も共有されると思いました。

これが、このコードのビットが私を混乱させる理由です。

クラス変数を実装する正しい方法は何ですか?

function classA() {};

classA.prototype.shared = 0;

a = new classA;

//print both values to make sure that they are the same
classA.prototype.shared;
a.shared;

//increment class variable
classA.prototype.shared++;

//Verify that they are each 1 (Works)
classA.prototype.shared;
a.shared;

//now increment the other reference
a.shared++;

//Verify that they are each 2 (Doesn't Work)
classA.prototype.shared;
a.shared;

更新:つまり、インスタンスの変数をインクリメントすることでプロトタイプに影響を与えないという事実を誰もが確認しているようです。これは問題ありません。これは私の例で文書化したものですが、これは言語の設計のエラーのようには見えませんか?なぜこの動作が望ましいのでしょうか?インスタンスのvarが定義されていない場合、varの値を取得するプロトタイプへの非表示のリンクをたどりますが、それをインスタンスオブジェクトにコピーするのは奇妙だと思います。

また、これはJava/c ++/Ruby/pythonではなく、別の言語であることも理解しています。なぜこの動作が良いのか知りたいだけです。

17
esiegel
I assumed that since prototypes will be shared between objects then so will their variables.

彼らはそうですが、これは:

a.shared++

あなたがしていると思うことをしていません。実際、これは(おおよそ)次の糖構文です。

(a.shared= a.shared+1)-1

(-1は、実際にretrun値を使用しているということではなく、プリインクリメント値を返すことです。)

つまり、これは実際にはa.sharedへの割り当てを行っています。インスタンスメンバーに割り当てるときは、常にそのインスタンス自身のメンバーに書き込んでいます。notそのプロトタイプのいずれかのメンバーに触れています。それは言うことと同じです:

classA.prototype.shared= 1;
a.shared= 2;

したがって、新しいa.sharedは、prototype.sharedを変更せずに非表示にします。 classAの他のインスタンスは、引き続きプロトタイプの値1を表示します。a.sharedを削除すると、その背後に隠されていたプロトタイプの変数をもう一度見ることができます。

8
bobince

静的(クラスレベル)変数は次のように実行できます

function classA(){
    //initialize
}

classA.prototype.method1 = function(){
    //accessible from anywhere
    classA.static_var = 1;
    //accessible only from THIS object
    this.instance_var = 2;
}

classA.static_var = 1;  //This is the same variable that is accessed in method1()

出力がおかしいようですjavascriptがプロトタイプを処理する方法のため。任意のメソッドの呼び出し/インスタンス化されたオブジェクトの変数の取得最初にインスタンスをチェックし、次にプロトタイプをチェックします。つまり.

var a = new classA();
classA.prototype.stat = 1;

// checks a.stat which is undefined, then checks classA.prototype.stat which has a value
alert(a.stat); // (a.stat = undefined, a.prototype.stat = 1)

// after this a.stat will not check the prototype because it is defined in the object.
a.stat = 5;  // (a.stat = 5, a.prototype.stat = 1)

// this is essentially a.stat = a.stat + 1;
a.stat++; // (a.stat = 6, a.prototype.stat = 1) 
15

Javaの静的変数のようなクラス変数が必要な場合は、親クラスで変数を宣言できますが、子オブジェクトの変数としてアクセスしないでください。 この記事 は、変数Circle.PI = 3.14を持つクラスCircleの良い例ですが、Circleのすべてのインスタンスは(Circle.PIではなく)c.PIとしてアクセスします。

したがって、私の答えは、クラス変数sharedclassAに入れたい場合は、変数をclassAで共有することを宣言し、後でclassA.sharedの代わりにa.sharedを使用する必要があるということです。 。 a.sharedを変更しても、classA.sharedが変更されることはありません。

5
Chei

JavaScriptではオブジェクトを構築する関数である「クラス」にメンバーを配置するだけです。

function ClassA(x) { this.x = x; }
ClassA.shared = "";
ClassA.prototype.foo = function() {
    return ClassA.shared + this.x;
}

var inst1 = new ClassA("world");
var inst2 = new ClassA("mars");

ClassA.shared = "Hello ";
console.log(inst1.foo());
console.log(inst2.foo());
ClassA.shared = "Good bye ";
console.log(inst1.foo());
console.log(inst2.foo());
3
Frank Krueger

インスタンスを介してsharedプロパティをインクリメントすると、そのインスタンスのプロパティになります。これが、この動作が表示される理由です。

これを行うと、インスタンスを介してそのプロパティのプロトタイプにアクセスすることはなく、独自のプロパティにアクセスすることになります。

>>> function ConstructorA() {};
>>> ConstructorA.prototype.shared = 0;
>>> var a = new ConstructorA();
>>> ConstructorA.prototype.shared++;
>>> a.shared
1
>>> a.hasOwnProperty("shared")
false
>>> a.shared++;
>>> a.hasOwnProperty("shared")
true

これが正しい解決策が、これまでの多くの回答で示唆されているようにConstructorA.sharedを使用し、インスタンスではなくコンストラクター関数を介して常にアクセスすることである理由です。

JavaScriptにはクラスのようなものはないと考えると役立つかもしれません。 new演算子で作成された「インスタンス」は、特定のコンストラクター関数によって作成され、特定のプロトタイプチェーンを持つ単なるオブジェクトです。これが、a.sharedConstructorA.sharedにアクセスできない理由です。プロパティへのアクセスには、問題のオブジェクトで名前付きプロパティを探し、失敗し、プロトタイプチェーンを歩いてプロパティを探しますが、オブジェクトを作成したコンストラクター関数は、プロトタイプチェーンの一部ではありません。

2
Jonny Buchanan

プロトタイプはクラス定義ではないからです。プロトタイプ変数は静的変数ではありません。 Wordのプロトタイプについて考えてみてください。これは、オブジェクトの作成に使用されるモデルではありません。複製されるオブジェクトの例です。

1
fijiaaron

そのクラス(a = new classA)をインスタンス化する場合、そのインスタンスaを変更しても、基本クラス自体は変更されません。 classAのインスタンスはclassA.prototypeからすべてを継承しますが、それは逆方向には適用されません。aを変更してもclassAは変更されません。
a1 = new classAa2 = new classAのような2つのインスタンスがある場合、もう一方に影響を与えることなく、a1a2の両方に変更を加えることができます。一方、classA.prototypeの変更は、両方で表示されます。
インスタンスsharedの変数aは、新しい値が与えられるまでデフォルト値になります。デフォルト値はclassA.prototype.sharedの値です。

1
Chei

定義しているのはクラス変数ではなく、インスタンス変数のデフォルト値です。

クラス変数は、クラスで直接定義する必要があります。つまり、constrctor関数で直接定義する必要があります。

function ClassA()
{
    ClassA.countInstances = (ClassA.countInstances || 0) + 1;
}
var a1 = new ClassA();
alert(ClassA.countInstances);
var a2 = new ClassA();
alert(ClassA.countInstances);

プロトタイプで変数を宣言する場合、この変数はすべてのインスタンスにインスタンス変数として継承され(メソッドと同様)、インスタンスで変更するとオーバーライドされます(メソッドと同様)。

0
Vincent Robert