クラス変数を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ではなく、別の言語であることも理解しています。なぜこの動作が良いのか知りたいだけです。
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を削除すると、その背後に隠されていたプロトタイプの変数をもう一度見ることができます。
静的(クラスレベル)変数は次のように実行できます:
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)
Javaの静的変数のようなクラス変数が必要な場合は、親クラスで変数を宣言できますが、子オブジェクトの変数としてアクセスしないでください。 この記事 は、変数Circle.PI = 3.14
を持つクラスCircleの良い例ですが、Circleのすべてのインスタンスは(Circle.PI
ではなく)c.PI
としてアクセスします。
したがって、私の答えは、クラス変数shared
をclassA
に入れたい場合は、変数をclassA
で共有することを宣言し、後でclassA.shared
の代わりにa.shared
を使用する必要があるということです。 。 a.shared
を変更しても、classA.shared
が変更されることはありません。
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());
インスタンスを介して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.shared
がConstructorA.shared
にアクセスできない理由です。プロパティへのアクセスには、問題のオブジェクトで名前付きプロパティを探し、失敗し、プロトタイプチェーンを歩いてプロパティを探しますが、オブジェクトを作成したコンストラクター関数は、プロトタイプチェーンの一部ではありません。
プロトタイプはクラス定義ではないからです。プロトタイプ変数は静的変数ではありません。 Wordのプロトタイプについて考えてみてください。これは、オブジェクトの作成に使用されるモデルではありません。複製されるオブジェクトの例です。
そのクラス(a = new classA
)をインスタンス化する場合、そのインスタンスa
を変更しても、基本クラス自体は変更されません。 classA
のインスタンスはclassA.prototype
からすべてを継承しますが、それは逆方向には適用されません。a
を変更してもclassA
は変更されません。a1 = new classA
とa2 = new classA
のような2つのインスタンスがある場合、もう一方に影響を与えることなく、a1
とa2
の両方に変更を加えることができます。一方、classA.prototype
の変更は、両方で表示されます。
インスタンスshared
の変数a
は、新しい値が与えられるまでデフォルト値になります。デフォルト値はclassA.prototype.shared
の値です。
定義しているのはクラス変数ではなく、インスタンス変数のデフォルト値です。
クラス変数は、クラスで直接定義する必要があります。つまり、constrctor関数で直接定義する必要があります。
function ClassA()
{
ClassA.countInstances = (ClassA.countInstances || 0) + 1;
}
var a1 = new ClassA();
alert(ClassA.countInstances);
var a2 = new ClassA();
alert(ClassA.countInstances);
プロトタイプで変数を宣言する場合、この変数はすべてのインスタンスにインスタンス変数として継承され(メソッドと同様)、インスタンスで変更するとオーバーライドされます(メソッドと同様)。