Как использовать статическую переменную в классе ES6?

Я пытаюсь использовать статическую переменную в es6. Я хотел бы объявить статическую переменную count в классе Animal и увеличить ее. Однако я не мог объявить статическую переменную через static count = 0;, поэтому я попробовал другой способ:

class Animal {
  constructor() {
    this.count = 0;
  }

  static increaseCount() {
    this.count += 1;
  }

  static getCount() {
    return this.count;
  }
}

console.log(Animal.increaseCount()); // undefined
console.log(Animal.getCount()); // NaN

Я ожидал, что console.log(Animal.getCount()); будет 1, но это не работает. Как объявить статическую переменную и изменить ее, вызвав метод?


person Simon Park    schedule 17.07.2018    source источник
comment
см. stackoverflow.com/questions/1535631/   -  person Sébastien Palud    schedule 17.07.2018


Ответы (6)


В вашем классе нет статических переменных (если под статической переменной вы подразумеваете статическое свойство). getCount возвращает NaN (после вызова increaseCount), потому что Animal изначально не имеет свойства count. Затем increaseCount делает undefined + 1, что равно NaN. Экземпляры, созданные new Animal, изначально имеют свойство count, а Animal — только после вызова increaseCount. this в методе static относится к самому классу Animal (функция-конструктор) (если вы вызываете его через Animal.methodName(...)).

Вы можете дать Animal свойство count:

Animal.count = 0;

Живой пример:

class Animal {
  constructor() {
  }

  static increaseCount() {
    this.count += 1;
  }

  static getCount() {
    return this.count;
  }
}
Animal.count = 0;

Animal.increaseCount();
console.log(Animal.getCount());
Animal.increaseCount();
console.log(Animal.getCount());

С предложением по полям статического класса (в настоящее время на этапе 3) вы можете сделать что декларативно с static count = 0; в Animal. Живой пример (Конфигурация Babel Stack Snippets, кажется, поддерживает его):

class Animal {
  constructor() {
  }

  static count = 0;
  
  static increaseCount() {
    this.count += 1;
  }

  static getCount() {
    return this.count;
  }
}

Animal.increaseCount();
console.log(Animal.getCount());
Animal.increaseCount();
console.log(Animal.getCount());

С предложением private static (находится на этапе 3 и активно внедряется) вы можно даже сделать count приватным:

class Animal {
  constructor() {
  }

  static #count = 0;

  static increaseCount() {
    this.#count += 1;
  }

  static getCount() {
    return this.#count;
  }
}

Animal.increaseCount();
console.log(Animal.getCount());
Animal.increaseCount();
console.log(Animal.getCount());

Стек Snippets' Babel конфигурация не поддерживает, но вы можете запустить его жить в их REPL.


Боковое примечание: использование this в статическом методе для ссылки на класс (функция-конструктор) немного сложно, если есть подклассы, потому что, например, если бы у вас было:

class Mammal extends Animal {}

а потом

Mammal.increaseCount();

this в пределах increaseCount (который он наследует от Animal) относится к Mammal, а не Animal.

Если вам нужно такое поведение, используйте this. Если нет, используйте Animal в этих static методах.

person T.J. Crowder    schedule 17.07.2018
comment
Разрешено ли static count = 0; в Animal классе? Это вызывает SyntaxError: Unexpected token. Я использую Babel с Webpack. - person Simon Park; 17.07.2018
comment
@Caesium133. Как я уже сказал выше, это часть предложения по полям статического класса который в настоящее время находится на этапе 3 процесса (поэтому его еще нет в спецификации, и движки только сейчас хотят его добавить). - person T.J. Crowder; 17.07.2018
comment
Как вы помещаете увеличение Count в конструктор нового животного? не поэтому ли, в конце концов, люди обычно хотят счетчики на занятиях? Кажется, вы не показываете тот случай. (хотя я полагаю, что count действительно должно быть свойством коллекции, а не класса-экземпляра - сделать count статическим свойством класса - это своего рода «малобюджетная» коллекция, а? - person johny why; 12.04.2020
comment
@johnywhy - Похоже, это не вариант использования ОП. - person T.J. Crowder; 12.04.2020
comment
static count = 0; кажется, работает в эти дни... я сплю? однако он сильно отличается от статического - person OhadR; 14.04.2020
comment
@OhadR - Нет, ты не спишь. :-) V8 (в Chrome, Node.js, Brave, Chromium, новом Edge и т. д.) и SpiderMonkey (в Firefox 75+) начали поддерживать static общедоступные поля. Safari все еще продолжается. - person T.J. Crowder; 14.04.2020

Как упоминалось в других ответах, this.count относится к свойству instance в constructor. Для инициализации свойства static необходимо установить Animal.count.

Предложение полей классов предоставляет синтаксический сахар для Animal.count = 0, который доступен с транспилерами (Babel и т. д.) :

class Animal {
  static count = 0;
  ...
}

Альтернативой в ES6 является использование начальных значений, в этом случае начальное значение Animal.count не нужно задавать явно, например:

class Animal {    
  static increaseCount() {
    this.count = this.getCount() + 1;
  }

  static getCount() {
    return this.count || 0;
  }
}

Методы доступа не приветствуются в классах JavaScript — для этого нужны дескрипторы getter/setter:

class Animal {    
  static increaseCount() {
    this.count += 1;
  }

  static get count() {
    return this._count || 0;
  }

  static set count(v) {
    this._count = v;
  }
}

Статический класс считается антишаблоном в JavaScript, потому что не используются состояние или другие признаки, специфичные для классов. В случае, если должен быть только один экземпляр, следует использовать простой объект (если нет других проблем, которые могут выиграть от class):

const animal = {    
  increaseCount() {
    this.count += 1;
  },

  get count() {
    return this._count || 0;
  },

  set count(v) {
    this._count = v;
  }
};
person Estus Flask    schedule 17.07.2018
comment
Но разве ваши функции получения и установки не являются методами доступа? - person johny why; 12.04.2020
comment
Разве ваш getCount не является методом доступа? - person johny why; 12.04.2020
comment
Ваш класс Animal с геттером и сеттером возвращает 0 с: let b = new Animal; console.log(Animal.count); - person johny why; 12.04.2020
comment
@johnywhy Это ожидаемое поведение. Это статический класс. Он должен использоваться как Animal.increaseCount(); console.log(Animal.count === 1). getCount называется методом доступа, count определяется парой get и set и называется дескриптором доступа. - person Estus Flask; 12.04.2020
comment
Я бы сказал, чтобы быть более полезным, ваш класс с get/set должен увеличивать себя при создании нового экземпляра. Ваш окончательный стиль объекта на самом деле не возвращает значение _count, если _count == 0. Он возвращает 0, когда _count на самом деле не определено. Это ложный результат. Я бы посчитал это ошибкой. Поскольку этот объект требует внешней инициализации count равным 0, кажется, что это ничем не отличается от простого использования переменной. Является ли объект, который действует так же, как переменная, считается антишаблоном? - person johny why; 22.04.2020
comment
@johnywhy Спасибо, что заметили, была опечатка, это должно быть this.count, а не this._count в увеличенииCount и других членах, кроме get/set count. get count() { return this._count || 0 } в основном совпадает с _count: 0, get count() { return this._count }. Я предпочитаю || 0, потому что для ввода требуется меньше символов, а также исправляет некоторые недопустимые значения, вместо этого можно изменить на this._count = +v || 0 в set для лучшего согласования значений. Это зависит от случая, если это антипаттерн. Объект может быть расширен и более тестируем. И его поведение может быть изменено в любое время, если это необходимо. - person Estus Flask; 22.04.2020

Статические свойства класса и свойства данных прототипа должны быть определены вне объявления ClassBody.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes

class Animal {

  static increaseCount() {
    Animal.count += 1;
  }

  static getCount() {
    return Animal.count;
  }
}

Animal.count = 0;

Animal.increaseCount();
console.log(Animal.getCount()); // undefined

person Farooq Hanif    schedule 17.07.2018
comment
Это нормально, но последняя строка кода возвращает 1 , а не undefined. Это может ввести в заблуждение новичка - person Umbert; 24.03.2020
comment
Сегодня вам больше не нужно объявлять это снаружи - person Eric; 20.02.2021

Чтобы установить статическую переменную, установите ее на самом объекте Animal. На данный момент в Javascript вы не можете напрямую объявлять статические свойства внутри классов, как вы можете объявлять статические методы.

class Animal {
    constructor() {
    }

    static increaseCount() {
        this.count += 1;
    }

    static getCount() {
        return this.count;
    }
}
Animal.count = 0;
console.log(Animal.increaseCount());
console.log(Animal.getCount()); 
person mpm    schedule 17.07.2018

вы можете использовать замыкания для имитации статических переменных

const Animal= (() => {
    let count= 0;

    class Animal {
        constructor() {}

        static increaseCount() {
            count += 1;
        }

        static getCount() {
            return count;
        }
    }

    return Animal;
})();

console.log(Animal.getCount());
Animal.increaseCount();
console.log(Animal.getCount());

person madhur acharya    schedule 01.10.2020

Если вы хотите иметь добавочные идентификаторы:

 constructor() {
    super(template);
    if (typeof MyClass.nextId == 'undefined') {
    MyClass.nextId = 0;
    }
    this._id = `${MyClass.nextId++}`;
 }
person dotista2008    schedule 08.07.2021