Javascript中的类变量

时间:2008-11-04 08:07:10

标签: javascript oop inheritance

我在尝试让类变量在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,它是一种不同的语言。我只是好奇为什么这种行为可能会很好。

8 个答案:

答案 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 classAa2 = new classA,那么您可以对a1a2进行更改,而不会影响另一个。另一方面,改变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);

当你在原型中声明一个变量时,这个变量将被所有实例作为实例变量继承(就像方法一样),并且如果你在实例中改变它就会覆盖(就像方法一样)。