update classes

This commit is contained in:
2025-03-05 15:12:57 +03:00
parent 31caa0e6de
commit 8b08f95ce0
12 changed files with 421 additions and 0 deletions

View File

@@ -0,0 +1,51 @@
---
sidebar_position: 1
---
# Класс: базовый синтаксис
MyClass технически является функцией (той, которую мы определяем как constructor), в то время как методы, геттеры и сеттеры записываются в MyClass.prototype.
## Синтаксис «class»
```js
class MyClass {
// методы класса
constructor() { ... }
method1() { ... }
method2() { ... }
method3() { ... }
...
}
```
Затем используйте вызов `new MyClass()` для создания нового объекта со всеми перечисленными методами.
При этом автоматически вызывается метод `constructor()`, в нём мы можем инициализировать объект.
💥 **Методы в классе не разделяются запятой**
## Что такое класс?
В JavaScript класс это разновидность функции.
Вот что на самом деле делает конструкция `class User {...}`:
1. Создаёт функцию с именем `User`, которая становится результатом объявления класса. Код функции берётся из метода `constructor` (она будет пустой, если такого метода нет).
2. Сохраняет все методы, такие как `sayHi`, в `User.prototype`.
При вызове метода объекта `new User` он будет взят из прототипа, как описано в главе `F.prototype`. Таким образом, объекты new User имеют доступ к методам класса.
## Геттеры/сеттеры, другие сокращения
Как и в литеральных объектах, в классах можно объявлять вычисляемые свойства, геттеры/сеттеры и т.д.
При объявлении класса геттеры/сеттеры создаются на `User.prototype`
## Свойства классов
В приведённом выше примере у класса `User` были только методы.
```js
class User {
name = "Аноним";
sayHi() {
alert(`Привет, ${this.name}!`);
}
}
new User().sayHi();
```
Свойство name не устанавливается в `User.prototype`. Вместо этого оно создаётся оператором `new` перед запуском конструктора, это именно свойство объекта.

View File

@@ -0,0 +1,48 @@
---
sidebar_position: 2
---
# Наследование классов
Наследование классов это способ расширения одного класса другим классом.
Таким образом, мы можем добавить новый функционал к уже существующему.
1. Чтобы унаследовать от класса: class Child extends Parent:
-При этом Child.prototype.__proto__ будет равен Parent.prototype, так что методы будут унаследованы.
2. При переопределении конструктора:
- Обязателен вызов конструктора родителя super() в конструкторе Child до обращения к this.
3. При переопределении другого метода:
- Мы можем вызвать super.method() в методе Child для обращения к методу родителя Parent.
4. Внутренние детали:
- Методы запоминают свой объект во внутреннем свойстве [[HomeObject]]. Благодаря этому работает super, он в его прототипе ищет родительские методы.
- Поэтому копировать метод, использующий super, между разными объектами небезопасно.
- У стрелочных функций нет своего this и super, поэтому они «прозрачно» встраиваются во внешний контекст.
## Ключевое слово «extends»
Синтаксис создания класса допускает указывать после extends не только класс, но и любое выражение.
Пример вызова функции, которая генерирует родительский класс:
```js
function f(phrase) {
return class {
sayHi() { alert(phrase); }
};
}
class User extends f("Привет") {}
new User().sayHi(); // Привет
```
Здесь class User наследует от результата вызова f("Привет").
## Переопределение методов
```js
class Rabbit extends Animal {
stop() {
// ...теперь это будет использоваться для rabbit.stop()
// вместо stop() из класса Animal
}
}
```
У классов есть ключевое слово "super", чтобы сделать новый на его основе, изменяя или расширяя его функциональность
super.method(...) вызывает родительский метод.
super(...) для вызова родительского конструктора (работает только внутри нашего конструктора).

View File

@@ -0,0 +1,46 @@
---
sidebar_position: 3
---
# Статические свойства и методы
Статические методы используются для функциональности, принадлежат классу «в целом», а не относятся к конкретному объекту класса.
Например, метод для сравнения двух статей Article.compare(article1, article2) или фабричный метод Article.createTodays().
В объявлении класса они помечаются ключевым словом static.
Статические свойства используются в тех случаях, когда мы хотели бы сохранить данные на уровне класса, а не какого-то одного объекта.
```js
class MyClass {
static property = ...;
static method() {
...
}
}
```
Технически, статическое объявление это то же самое, что и присвоение классу:
```
MyClass.property = ...
MyClass.method = ...
```
Статические свойства и методы наследуются.
Для class B extends A прототип класса B указывает на A: B.[[Prototype]] = A. Таким образом, если поле не найдено в B, поиск продолжается в A.
## Статические свойства
Статические свойства также возможны, они выглядят как свойства класса, но с static в начале:
```js
class Article {
static publisher = "Илья Кантор";
}
alert( Article.publisher ); // Илья Кантор
```
Это то же самое, что и прямое присваивание Article:
```
Article.publisher = "Илья Кантор";
```
## Наследование статических свойств и методов
Статические свойства и методы наследуются.

View File

@@ -0,0 +1,46 @@
---
sidebar_position: 4
---
# Приватные и защищённые методы и свойства
В терминах ООП отделение внутреннего интерфейса от внешнего называется инкапсуляция.
Это даёт следующие выгоды:
**Защита для пользователей, чтобы они не выстрелили себе в ногу**
Если мы чётко отделим внутренний интерфейс, то разработчик класса сможет свободно менять его внутренние свойства и методы, даже не информируя пользователей…
#### Поддерживаемость
Если вы разработчик такого класса, то приятно знать, что приватные методы можно безопасно переименовывать, их параметры можно изменять и даже удалять, потому что от них не зависит никакой внешний код.
В новой версии вы можете полностью всё переписать, но пользователю будет легко обновиться, если внешний интерфейс остался такой же.
## Внутренний и внешний интерфейсы
В объектно-ориентированном программировании свойства и методы разделены на 2 группы:
- Внутренний интерфейс методы и свойства, доступные из других методов класса, но не снаружи класса.
- Внешний интерфейс методы и свойства, доступные снаружи класса.
Итак, всё, что нам нужно для использования объекта, это знать его внешний интерфейс. Мы можем совершенно не знать, как это работает внутри, и это здорово.
В JavaScript есть два типа полей (свойств и методов) объекта:
- Публичные: доступны отовсюду. Они составляют внешний интерфейс. До этого момента мы использовали только публичные свойства и методы.
- Приватные: доступны только внутри класса. Они для внутреннего интерфейса.
Защищённые поля не реализованы в JavaScript на уровне языка, но на практике они очень удобны, поэтому их эмулируют.
## Защищённое свойство «waterAmount»
Защищённые свойства обычно начинаются с префикса `_`.
Это не синтаксис языка: есть хорошо известное соглашение между программистами, что такие свойства и методы не должны быть доступны извне. Большинство программистов следуют этому соглашению.
## Приватное свойство «#waterLimit»
Есть новшество в языке JavaScript, которое почти добавлено в стандарт: оно добавляет поддержку приватных свойств и методов.
Приватные свойства и методы должны начинаться с #. Они доступны только внутри класса.
Например, в классе ниже есть приватное свойство #waterLimit и приватный метод #checkWater для проверки количества воды
На уровне языка # является специальным символом, который означает, что поле приватное. Мы не можем получить к нему доступ извне или из наследуемых классов.
Приватные поля не конфликтуют с публичными. У нас может быть два поля одновременно приватное #waterAmount и публичное waterAmount.

View File

@@ -0,0 +1,46 @@
---
sidebar_position: 5
---
# Проверка класса: "instanceof"
| | работает для | возвращает |
|-------------|----------------------------------------------------------------|------------|
| typeof | примитивов | строка |
| {}.toString | примитивов, встроенных объектов, объектов с Symbol.toStringTag | строка |
| instanceof | объектов | true/false |
Как мы можем видеть, технически `{}.toString` «более продвинут», чем `typeof`.
А оператор `instanceof` отличный выбор, когда мы работаем с иерархией классов и хотим делать проверки с учётом наследования.
## Оператор instanceof
```js
obj instanceof Class
```
Оператор вернёт `true`, если `obj` принадлежит классу Class или наследующему от него.
## Object.prototype.toString возвращает тип
У `toString` имеются скрытые возможности, которые делают метод гораздо более мощным. Мы можем использовать его как расширенную версию `typeof` и как альтернативу `instanceof`.
Согласно спецификации встроенный метод `toString` может быть позаимствован у объекта и вызван в контексте любого другого значения. И результат зависит от типа этого значения.
- Для числа это будет [object Number]
- Для булева типа это будет [object Boolean]
- Для null: [object Null]
- Для undefined: [object Undefined]
- Для массивов: [object Array]
- …и т.д. (поведение настраивается).
## Symbol.toStringTag
Поведение метода объектов `toString` можно настраивать, используя специальное свойство объекта `Symbol.toStringTag`.
```js
// toStringTag для браузерного объекта и класса
alert( window[Symbol.toStringTag]); // window
alert( XMLHttpRequest.prototype[Symbol.toStringTag] ); // XMLHttpRequest
alert( {}.toString.call(window) ); // [object Window]
alert( {}.toString.call(new XMLHttpRequest()) ); // [object XMLHttpRequest]
```
В итоге мы получили «typeof на стероидах», который не только работает с примитивными типами данных, но также и со встроенными объектами, и даже может быть настроен.
Можно использовать `{}.toString.call` вместо `instanceof` для встроенных объектов, когда мы хотим получить тип в виде строки, а не просто сделать проверку.

View File

@@ -0,0 +1,15 @@
---
sidebar_position: 6
---
# Примеси
По определению из Википедии, примесь это класс, методы которого предназначены для использования в других классах, причём без наследования от примеси.
Примесь общий термин в объектно-ориентированном программировании: класс, который содержит в себе методы для других классов.
С примесями могут возникнуть конфликты, если они перезаписывают существующие методы класса. Стоит помнить об этом и быть внимательнее при выборе имён для методов примеси, чтобы их избежать.
## Пример примеси
Простейший способ реализовать примесь в JavaScript это создать объект с полезными методами, которые затем могут быть легко добавлены в прототип любого класса.\
Это не наследование, а просто копирование методов.
Примеси могут наследовать друг друга.