Мы также можем назначить метод всему классу. Такие методы называются статическими .
В объявлении класса они предваряются ключевым словом static
, например:
класс Пользователь { статический staticMethod() { предупреждение (этот === Пользователь); } } Пользователь.статическийМетод(); // истинный
Фактически это то же самое, что и непосредственное присвоение его как свойства:
класс Пользователь { } User.staticMethod = функция() { предупреждение (этот === Пользователь); }; Пользователь.статическийМетод(); // истинный
Значением this
в вызове User.staticMethod()
является сам конструктор класса User
(правило «объект перед точкой»).
Обычно статические методы используются для реализации функций, принадлежащих классу в целом, а не какому-то конкретному его объекту.
Например, у нас есть объекты Article
и нам нужна функция для их сравнения.
Естественным решением было бы добавить статический метод Article.compare
:
класс Статья { конструктор(название, дата) { this.title = заголовок; this.date = дата; } статическое сравнение(статьяA, статьяB) { вернуть статьюА.дата - статьяБ.дата; } } // использование пусть статьи = [ новая статья("HTML", новая дата(2019, 1, 1)), новая статья("CSS", новая дата(2019, 0, 1)), новая статья("JavaScript", новая дата(2019, 11, 1)) ]; статьи.сортировать(статьи.сравнить); оповещение(статьи[0].title); // CSS
Здесь метод Article.compare
стоит «над» статьями, как средство их сравнения. Это метод не статьи, а всего класса.
Другим примером может быть так называемый «фабричный» метод.
Допустим, нам нужно несколько способов создания статьи:
Создать по заданным параметрам ( title
, date
и т.д.).
Создайте пустую статью с сегодняшней датой.
…или еще как-нибудь.
Первый способ может быть реализован конструктором. А для второго мы можем сделать статический метод класса.
Например, Article.createTodays()
здесь:
класс Статья { конструктор(название, дата) { this.title = заголовок; this.date = дата; } статический createTodays() { // помните, это = статья return new this("Сегодняшний дайджест", новая дата()); } } пусть статья = Article.createTodays(); предупреждение(статья.заголовок); // Сегодняшний дайджест
Теперь каждый раз, когда нам нужно создать дайджест за сегодняшний день, мы можем вызвать Article.createTodays()
. Еще раз, это не метод статьи, а метод всего класса.
Статические методы также используются в классах, связанных с базой данных, для поиска/сохранения/удаления записей из базы данных, например:
// предполагается, что Article — это специальный класс для управления статьями // статический метод удаления статьи по id: Article.remove({id: 12345});
Статические методы недоступны для отдельных объектов.
Статические методы можно вызывать для классов, а не для отдельных объектов.
Например, такой код не будет работать:
// ... статья.createTodays(); /// Ошибка:article.createTodays не является функцией
Недавнее дополнение
Это недавнее дополнение к языку. Примеры работают в последней версии Chrome.
Статические свойства также возможны, они выглядят как обычные свойства класса, но перед ними стоит static
:
класс Статья { staticPublisher="Илья Кантор"; } оповещение(Статья.издатель); // Илья Кантор
Это то же самое, что и прямое присвоение Article
:
Article.publisher = "Илья Кантор";
Статические свойства и методы наследуются.
Например, Animal.compare
и Animal.planet
в приведенном ниже коде наследуются и доступны как Rabbit.compare
и Rabbit.planet
:
класс Животное { статическая планета = «Земля»; конструктор(имя, скорость) { this.speed = скорость; это.имя = имя; } бежать (скорость = 0) { this.speed += скорость; alert(`${this.name} работает со скоростью ${this.speed}.`); } статическое сравнение(животноеА, животноеБ) { вернуть животноеА.скорость - животноеБ.скорость; } } // Наследовать от Animal класс Rabbit расширяет Animal { скрывать() { alert(`${this.name} скрывается!`); } } пусть кролики = [ новый Кролик("Белый Кролик", 10), новый Кролик("Чёрный Кролик", 5) ]; кролики.сортировать(Кролик.сравнить); кролики[0].run(); // Черный Кролик бежит со скоростью 5. оповещение(Кролик.планета); // Земля
Теперь, когда мы вызываем Rabbit.compare
, будет вызываться унаследованный Animal.compare
.
Как это работает? Опять же, используя прототипы. Как вы, возможно, уже догадались, extends
дают Rabbit
ссылку [[Prototype]]
на Animal
.
Итак, Rabbit extends Animal
создавая две ссылки [[Prototype]]
:
Функция Rabbit
прототипически наследуется от функции Animal
.
Rabbit.prototype
прототипически наследуется от Animal.prototype
.
В результате наследование работает как для обычных, так и для статических методов.
Давайте проверим это по коду:
класс Животное {} класс Rabbit расширяет Animal {} // для статики alert(Rabbit.__proto__ === Animal); // истинный // для обычных методов alert(Rabbit.prototype.__proto__ === Animal.prototype); // истинный
Статические методы используются для функционала, который принадлежит классу «в целом». Это не относится к конкретному экземпляру класса.
Например, метод сравнения Article.compare(article1, article2)
или фабричный метод Article.createTodays()
.
В объявлении класса они помечены словом static
.
Статические свойства используются, когда мы хотим хранить данные уровня класса, также не привязанные к экземпляру.
Синтаксис:
класс MyClass { статическое свойство = ...; статический метод() { ... } }
Технически статическое объявление аналогично присвоению самому классу:
МойКласс.свойство = ... МойКласс.метод = ...
Статические свойства и методы наследуются.
Для class B extends A
прототип самого класса B
указывает на A
: B.[[Prototype]] = A
. Таким образом, если поле не найдено в B
, поиск продолжается в A
важность: 3
Как мы знаем, все объекты обычно наследуются от Object.prototype
и получают доступ к «универсальным» методам объекта, таким как hasOwnProperty
и т. д.
Например:
класс Кролик { конструктор(имя) { это.имя = имя; } } let Rabbit = new Rabbit("Раб"); // метод hasOwnProperty взят из Object.prototype alert( Rabbit.hasOwnProperty('имя')); // истинный
Но если мы напишем это явно, как "class Rabbit extends Object"
, то результат будет отличаться от простого "class Rabbit"
?
Какая разница?
Вот пример такого кода (не работает — зачем? поправьте?):
класс Rabbit расширяет Object { конструктор(имя) { это.имя = имя; } } let Rabbit = new Rabbit("Раб"); alert( Rabbit.hasOwnProperty('имя')); // Ошибка
Во-первых, давайте посмотрим, почему последний код не работает.
Причина станет очевидной, если мы попробуем ее запустить. Конструктор наследующего класса должен вызвать super()
. В противном случае "this"
не будет «определено».
Итак, вот исправление:
класс Rabbit расширяет Object { конструктор(имя) { супер(); // нужно вызвать родительский конструктор при наследовании это.имя = имя; } } let Rabbit = new Rabbit("Раб"); alert( Rabbit.hasOwnProperty('имя')); // истинный
Но это еще не все.
Даже после исправления сохраняется важное различие между "class Rabbit extends Object"
и class Rabbit
.
Как мы знаем, синтаксис «extends» устанавливает два прототипа:
Между "prototype"
функций-конструкторов (для методов).
Между самими функциями-конструкторами (для статических методов).
В случае, когда class Rabbit extends Object
это означает:
класс Rabbit расширяет Object {} alert( Rabbit.prototype.__proto__ === Object.prototype ); // (1) правда предупреждение( Rabbit.__proto__ === Объект); // (2) правда
Итак, Rabbit
теперь предоставляет доступ к статическим методам Object
через Rabbit
, например:
класс Rabbit расширяет Object {} // обычно мы вызываем Object.getOwnPropertyNames предупреждение ( Rabbit.getOwnPropertyNames({a: 1, b: 2})); // а, б
Но если у нас нет extends Object
, то Rabbit.__proto__
не имеет значения Object
.
Вот демо:
класс Кролик {} alert( Rabbit.prototype.__proto__ === Object.prototype ); // (1) правда предупреждение( Rabbit.__proto__ === Объект); // (2) ложь (!) alert( Rabbit.__proto__ === Function.prototype ); // как любая функция по умолчанию // ошибка, в Rabbit такой функции нет предупреждение ( Rabbit.getOwnPropertyNames({a: 1, b: 2})); // Ошибка
Таким образом, в этом случае Rabbit
не предоставляет доступ к статическим методам Object
.
Кстати, Function.prototype
также имеет «универсальные» функциональные методы, такие как call
, bind
и т. д. В конечном итоге они доступны в обоих случаях, поскольку для встроенного конструктора Object
Object.__proto__ === Function.prototype
.
Вот картинка:
Итак, если коротко, то есть два отличия:
класс Кролик | класс Rabbit расширяет Object |
---|---|
– | нужно вызвать super() в конструкторе |
Rabbit.__proto__ === Function.prototype | Rabbit.__proto__ === Object |