【问题标题】:Class Variables in JavascriptJavascript 中的类变量
【发布时间】:2010-09-20 15:30:25
【问题描述】:

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

【问题讨论】:

标签: javascript oop inheritance


【解决方案1】:

静态(类级别)变量可以这样完成

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) 

【讨论】:

  • 你运行你的第一个例子了吗?因为最后的 classA.static_var 不是 1 而是未定义!
  • @Hamidam classA.static_var 在你说a = new classA(); a.method1();之后被设置为1
【解决方案2】:
I assumed that since prototypes will be shared between objects then so will their variables.

他们是,但是这个:

a.shared++

没有做你认为它正在做的事情。实际上(大约)是以下的糖语法:

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

(-1 是返回预增量值,不是你实际使用的返回值,但仍然。)

所以这实际上是对 a.shared 进行分配。当您分配给一个实例成员时,您总是写给该实例自己的成员,触及其任何原型的任何成员。和说的一样:

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

所以你的新 a.shared 隐藏了prototype.shared 而不改变它。 classA 的其他实例将继续显示原型的值 1。如果您删除 a.shared,您将再次能够看到隐藏在其后面的原型变量。

【讨论】:

  • 为什么 javascript 会允许这样做?您不认为这是语言设计中的错误吗?我的意思是,当我们增加一个实例时,它的变量存在于原型中,我们首先必须获取原型中的 vars 值,但奇怪的是我们然后将值分配给实例?很奇怪。
  • 这有点奇怪,是的,但远非 JavaScript 中最奇怪的东西!这是一门充满陷阱的语言。
  • 当你分配给一个实例成员时,你总是写给那个实例自己的成员,而不是触及它的任何原型的任何成员。这就是我需要知道的,谢谢。
【解决方案3】:

如果你想要一个类变量,比如Java中的静态变量,那么你可以在父类中声明一个变量,但是你不应该将它作为子对象的变量来访问。 This article 有一个很好的例子,类 Circle 具有变量 Circle.PI = 3.14,而 Circle 的所有实例都以 Circle.PI(而不是 c.PI)访问它。

所以我的回答是,如果你想在classA 中有一个类变量shared,那么你应该在classA 中声明共享变量,然后你应该使用classA.shared 而不是a.shared。更改 a.shared 永远不会导致 classA.shared 被更改。

【讨论】:

    【解决方案4】:

    您只需将成员放在“类”上,在 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());
    

    【讨论】:

    • 这不起作用。尝试实例化一个实例 a = new classA。此变量将无权访问共享成员。
    • 不,因为它不是类成员,它是 ClassA 对象本身的属性。
    • 为什么会被否决?这是给出正确解决方案的答案之一。
    • 我也不明白投票否决。类变量与构造函数相关联。仅仅因为你必须使用ClassName.prop 语法并不意味着它是错误的。 C++ 和 C# 要求您使用相同的语法。
    • 我为否决投票道歉,但我最初这样做是因为您的回答没有解决我的问题。它没有显示共享变量的实例。这只是代码,即使这是在 javascript 中“共享”类变量的唯一方法,我也将其解释为错误。现在我无法撤消投票。
    【解决方案5】:

    通过实例增加 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 的原因 - 属性访问涉及查看有问题的对象以获取命名属性,如果失败,则遍历其原型链寻找属性,但创建的构造函数该对象不是原型链的一部分。

    【讨论】:

      【解决方案6】:

      如果您实例化该类 (a = new classA),那么修改该实例 a 不会更改基类本身。 classA 的实例将继承 classA.prototype 的所有内容,但这并不向后适用,更改 a 不会更改 classA
      如果您有两个实例,例如 a1 = new classAa2 = new classA,那么您可以同时更改 a1a2 而不会影响另一个。另一方面,更改 classA.prototype 将在它们两个中可见。
      实例a 的变量shared 将具有默认值,直到它被赋予一个新值。默认值为classA.prototype.shared

      【讨论】:

      • 这对我来说真的没有意义。一开始似乎“a”搜索变量 shared,没有它并在原型中找到它。但后来它实际上包含变量的副本。我的意思是在上面的代码运行之后,通过增加 classA.shared,“a”不受影响
      • 另外,不仅仅是“classA 的实例将继承 classA.prototype 的所有内容”。当我增加prototypes.shared 并且这也增加了a.shared 时,可以看出它实际上是在开始时链接的。
      • 我刚刚添加了第三段,描述 a.shared 将有一个默认值 classA.prototype.shared,直到它被赋予一个新值。
      • a.shared 没有默认值。 A.shared 在实例 A 中未定义,实际上可以在原型中找到。只是在你奇怪地写了a.shared++之后,在我的脑海中不正确,将值从原型复制到实例。现在forevermore shared存在于A中
      【解决方案7】:

      这是因为原型不是类定义。原型变量不是静态变量。想想原型这个词。它不是用于构建对象的模型——它是要复制的示例对象。

      【讨论】:

        【解决方案8】:

        您定义的不是类变量,而是实例变量的默认值。

        类变量应该直接在类中定义,即直接在构造函数中。

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2017-04-01
          • 2012-06-05
          • 2019-11-15
          • 2018-06-23
          • 1970-01-01
          • 2021-07-02
          相关资源
          最近更新 更多