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

ОБНОВИТЬ: Кажется, что все подтверждают тот факт, что, увеличивая переменную экземпляра, мы не влияем на прототип. Это нормально, это то, что я задокументировал в моем примере, но разве это не похоже на ошибку в дизайне языка? Почему такое поведение было бы желательно? Мне кажется странным, что когда переменная экземпляра не определена, мы переходим по скрытой ссылке на прототип, где получаем значение переменной, но копируем его в объект экземпляра.

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

Предлагаю вам прочитать это: 3site.eu/doc

kentaromiura 04.11.2008 16:18
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
17
1
36 954
8
Перейти к ответу Данный вопрос помечен как решенный

Ответы 8

Если вы создаете экземпляр этого класса (a = new classA), то изменение этого экземпляра a не изменит сам базовый класс. Экземпляры classA унаследуют все от classA.prototype, но это не применяется в обратном направлении, изменение a не изменит classA.
Если у вас есть два экземпляра, например a1 = new classA и a2 = new classA, вы можете вносить изменения как в a1, так и в a2, не влияя на другой. С другой стороны, изменение classA.prototype будет видно в обоих из них. Переменная shared экземпляра a будет иметь значение по умолчанию, пока ей не будет присвоено новое значение. Значение по умолчанию - значение classA.prototype.shared.

Для меня это действительно не имеет смысла. Вначале кажется, что «a» ищет разделяемую переменную, не имеет ее и находит ее в прототипе. Но позже он фактически содержит копию переменной. Я имею в виду, что после запуска этого кода выше, увеличивая classA.shared, "a" не изменяется

esiegel 04.11.2008 11:40

Кроме того, дело не только в том, что «экземпляр classA унаследует все от classA.prototype». Он фактически связан в начале, что можно увидеть, когда я увеличиваю prototypes.shared, а также увеличиваю a.shared.

esiegel 04.11.2008 11:52

Я только что добавил третий абзац, описывающий, что a.shared будет иметь значение по умолчанию classA.prototype.shared, пока ему не будет присвоено новое значение.

Chei 04.11.2008 12:26

a.shared не имеет значения по умолчанию. A.shared не определен в экземпляре A и фактически находится в прототипе. Просто после того, как вы напишете a.shared ++ это странно и неправильно, на мой взгляд, скопируйте значение из прототипа в экземпляр. Теперь Forevermore Shared существует в A

esiegel 04.11.2008 19:56

Вы просто помещаете член прямо в «класс», который в 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.info(inst1.foo());
console.info(inst2.foo());
ClassA.shared = "Good bye ";
console.info(inst1.foo());
console.info(inst2.foo());

Это не работает. Попробуйте создать экземпляр a = new classA. Эта переменная не будет иметь доступа к общему члену.

esiegel 04.11.2008 11:42

Нет, потому что это не член класса, это свойство самого объекта ClassA.

roryf 04.11.2008 12:50

Почему это отклоняется? Это один из ответов, который дает правильное решение.

Jonny Buchanan 04.11.2008 14:55

Я тоже не понимаю голосования. Переменные класса связаны с конструкторами. Тот факт, что вам нужно использовать синтаксис ClassName.prop, не означает, что это неправильно. C++ и C# требуют использования одного и того же синтаксиса.

Frank Krueger 04.11.2008 19:51

Прошу прощения за то, что голосование отклонено, но я сделал это изначально, потому что ваш ответ не отвечал на мой вопрос. Не было показано экземпляров, совместно использующих переменную. Это был просто код, и я понял, что это неправильно, хотя это единственный способ «поделиться» переменными класса в javascript. Теперь я не могу отменить голосование.

esiegel 05.11.2008 05:42

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

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

Вы запустили свой первый пример? Потому что classA.static_var в конце не 1, а undefined!

mmm 17.11.2011 20:23

@Hamidam classA.static_var получает значение 1 после того, как вы скажете a = new classA(); a.method1();

Paul A Jungwirth 20.09.2012 20:17

Увеличение свойства 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 - доступ к свойству включает просмотр рассматриваемого объекта для названного свойства и, в случае неудачи, обход его цепочки прототипов в поисках свойства, но функция-конструктор, создавшая объект, не является t часть цепочки прототипов.

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

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

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

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

Ответ принят как подходящий
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 в прототипе, но, как ни странно, мы затем присваиваем значение экземпляру? СТРАННЫЙ.

esiegel 04.11.2008 19:42

Да, это немного странно, но далеко не самая странная вещь в JavaScript! Это язык, полный ловушек.

bobince 04.11.2008 20:39
Когда вы назначаете член экземпляра, вы всегда пишете собственным членам этого экземпляра, не касаясь каких-либо элементов любого из его прототипов. Это то, что мне нужно было знать, спасибо.
Lasse Christiansen 01.07.2014 12:41

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

Другие вопросы по теме