我在尝试让类变量在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,它是一种不同的语言。我只是好奇为什么这种行为可能会很好。
答案 0 :(得分:15)
静态(类级别)变量可以像这样完成:
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处理原型的方式,您的输出似乎很奇怪。调用任何方法/ retreiving实例化对象的变量首先检查实例,然后检查原型。即。
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)
答案 1 :(得分:8)
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做了一个分配。当您分配给实例成员时,您总是写入该实例自己的成员,不触及其任何原型的任何成员。这跟说:
classA.prototype.shared= 1;
a.shared= 2;
所以你的新a.shared会隐藏prototype.shared而不会改变它。 classA的其他实例将继续显示原型的值1.如果删除a.shared,您将再次能够看到隐藏在其后面的原型变量。
答案 2 :(得分:5)
如果你想要一个类变量,比如Java中的静态变量,那么你可以在父类中声明一个变量,但是你不应该把它作为子对象的变量来访问。 This article有一个很好的示例,即Circle类具有变量Circle.PI = 3.14
,而Circle的所有实例都将其作为Circle.PI
(而不是c.PI
)进行访问。
所以我的答案是,如果你想在shared
中有一个类变量classA
,那么你应该在classA
中声明共享变量,之后你应该使用classA.shared
而不是a.shared
。更改a.shared
永远不会导致classA.shared
被更改。
答案 3 :(得分:3)
您只需将成员放在“类”上,该类在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());
答案 4 :(得分:2)
通过实例增加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
的原因 - 属性访问涉及查看有问题的对象的命名属性,并且失败,走原型链寻找属性,但是创建对象的构造函数不是原型链的一部分。
答案 5 :(得分:1)
如果您实例化该类(a = new classA
),那么修改该实例a
将不会更改基类本身。 classA
的实例将继承classA.prototype
的所有内容,但不会向后应用,更改a
不会更改classA
。
如果您有两个实例,例如a1 = new classA
和a2 = new classA
,那么您可以对a1
和a2
进行更改,而不会影响另一个。另一方面,改变classA.prototype
将在两者中都可见
实例shared
的变量a
将具有默认值,直到给出新值。默认值为classA.prototype.shared
。
答案 6 :(得分:1)
这是因为原型不是类定义。原型变量不是静态变量。想想原型这个词。它不是用于构建对象的模型 - 它是要复制的示例对象。
答案 7 :(得分:0)
您定义的不是类变量,它是实例变量的默认值。
类变量应该直接在类中定义,这意味着直接在constrctor函数中。
function ClassA()
{
ClassA.countInstances = (ClassA.countInstances || 0) + 1;
}
var a1 = new ClassA();
alert(ClassA.countInstances);
var a2 = new ClassA();
alert(ClassA.countInstances);
当你在原型中声明一个变量时,这个变量将被所有实例作为实例变量继承(就像方法一样),并且如果你在实例中改变它就会覆盖(就像方法一样)。