Подтвердить что ты не робот

Переменные класса в 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;

UPDATE: Таким образом, кажется, что все подтверждают тот факт, что, увеличивая переменную экземпляра, мы не влияем на прототип. Это прекрасно, это то, что я документировал в моем примере, но разве это не похоже на ошибку в дизайне языка? Почему это поведение желательно? Мне кажется странным, что, когда экземпляр var является undefined, мы следуем скрытой ссылке на прототип, где получаем значение var, но копируем его в объект экземпляра.

Я также понимаю, что это не java/С++/ruby ​​/python, это другой язык. Мне просто интересно, почему это поведение может быть хорошим.

4b9b3361

Ответ 1

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, не изменяя его. Другие экземпляры класса A будут продолжать показывать значение прототипа 1. Если вы удалили a.shared, вы снова сможете увидеть переменную прототипа, которая была скрыта за ней.

Ответ 2

Статические (классные) переменные могут быть выполнены следующим образом:

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) 

Ответ 3

Если вы хотите иметь переменную класса, что-то вроде статической переменной в Java, тогда вы можете объявить переменную в родительском классе, но тогда вы не должны обращаться к ней как к переменной дочерних объектов. В этой статье есть хороший пример класса Circle, имеющего переменную Circle.PI = 3.14, в то время как все экземпляры Circle получают доступ к ней как Circle.PI (вместо этого of c.PI).

Итак, я отвечаю, что если вы хотите иметь переменную класса shared в classA, тогда вы должны объявить переменную, разделяемую в 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());

Ответ 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 classA и a2 = new classA, вы можете вносить изменения как в a1, так и a2, не выполняя другие. С другой стороны, изменение classA.prototype будет видимым в обоих из них.
Переменная shared экземпляра a будет иметь значение по умолчанию, пока не будет присвоено новое значение. Значением по умолчанию является значение classA.prototype.shared.

Ответ 7

Это потому, что прототипы не являются определениями классов. Прототипные переменные не являются статическими переменными. Подумайте о прототипе слова. Это не модель, используемая для создания объекта - это пример объекта, который нужно дублировать.

Ответ 8

То, что вы определяете, не является переменной класса, это значение по умолчанию для переменной экземпляра.

Переменные класса должны быть определены непосредственно в классе, что означает непосредственно в функции constrctor.

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

Когда вы объявляете переменную в прототипе, эта переменная будет наследоваться всеми экземплярами как переменные экземпляра (точно так же, как методы) и будет переопределяться, если вы измените ее в экземпляре (точно так же, как методы).