update classes
This commit is contained in:
51
docs/javascript/06-classes/01-class-base.md
Normal file
51
docs/javascript/06-classes/01-class-base.md
Normal 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` перед запуском конструктора, это именно свойство объекта.
|
||||
48
docs/javascript/06-classes/02-inheritance-classes.md
Normal file
48
docs/javascript/06-classes/02-inheritance-classes.md
Normal 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(...) для вызова родительского конструктора (работает только внутри нашего конструктора).
|
||||
46
docs/javascript/06-classes/03-static-option-methods.md
Normal file
46
docs/javascript/06-classes/03-static-option-methods.md
Normal 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 = "Илья Кантор";
|
||||
```
|
||||
|
||||
## Наследование статических свойств и методов
|
||||
Статические свойства и методы наследуются.
|
||||
@@ -0,0 +1,46 @@
|
||||
---
|
||||
sidebar_position: 4
|
||||
---
|
||||
|
||||
# Приватные и защищённые методы и свойства
|
||||
В терминах ООП отделение внутреннего интерфейса от внешнего называется инкапсуляция.
|
||||
|
||||
Это даёт следующие выгоды:
|
||||
|
||||
**Защита для пользователей, чтобы они не выстрелили себе в ногу**
|
||||
|
||||
Если мы чётко отделим внутренний интерфейс, то разработчик класса сможет свободно менять его внутренние свойства и методы, даже не информируя пользователей…
|
||||
#### Поддерживаемость
|
||||
Если вы разработчик такого класса, то приятно знать, что приватные методы можно безопасно переименовывать, их параметры можно изменять и даже удалять, потому что от них не зависит никакой внешний код.
|
||||
|
||||
В новой версии вы можете полностью всё переписать, но пользователю будет легко обновиться, если внешний интерфейс остался такой же.
|
||||
|
||||
## Внутренний и внешний интерфейсы
|
||||
В объектно-ориентированном программировании свойства и методы разделены на 2 группы:
|
||||
|
||||
- Внутренний интерфейс – методы и свойства, доступные из других методов класса, но не снаружи класса.
|
||||
- Внешний интерфейс – методы и свойства, доступные снаружи класса.
|
||||
|
||||
Итак, всё, что нам нужно для использования объекта, это знать его внешний интерфейс. Мы можем совершенно не знать, как это работает внутри, и это здорово.
|
||||
|
||||
В JavaScript есть два типа полей (свойств и методов) объекта:
|
||||
- Публичные: доступны отовсюду. Они составляют внешний интерфейс. До этого момента мы использовали только публичные свойства и методы.
|
||||
- Приватные: доступны только внутри класса. Они для внутреннего интерфейса.
|
||||
|
||||
Защищённые поля не реализованы в JavaScript на уровне языка, но на практике они очень удобны, поэтому их эмулируют.
|
||||
|
||||
## Защищённое свойство «waterAmount»
|
||||
Защищённые свойства обычно начинаются с префикса `_`.
|
||||
|
||||
Это не синтаксис языка: есть хорошо известное соглашение между программистами, что такие свойства и методы не должны быть доступны извне. Большинство программистов следуют этому соглашению.
|
||||
|
||||
## Приватное свойство «#waterLimit»
|
||||
Есть новшество в языке JavaScript, которое почти добавлено в стандарт: оно добавляет поддержку приватных свойств и методов.
|
||||
|
||||
Приватные свойства и методы должны начинаться с #. Они доступны только внутри класса.
|
||||
|
||||
Например, в классе ниже есть приватное свойство #waterLimit и приватный метод #checkWater для проверки количества воды
|
||||
|
||||
На уровне языка # является специальным символом, который означает, что поле приватное. Мы не можем получить к нему доступ извне или из наследуемых классов.
|
||||
|
||||
Приватные поля не конфликтуют с публичными. У нас может быть два поля одновременно – приватное #waterAmount и публичное waterAmount.
|
||||
46
docs/javascript/06-classes/05-class-check.md
Normal file
46
docs/javascript/06-classes/05-class-check.md
Normal 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` для встроенных объектов, когда мы хотим получить тип в виде строки, а не просто сделать проверку.
|
||||
15
docs/javascript/06-classes/06-mixins.md
Normal file
15
docs/javascript/06-classes/06-mixins.md
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
sidebar_position: 6
|
||||
---
|
||||
|
||||
# Примеси
|
||||
По определению из Википедии, примесь – это класс, методы которого предназначены для использования в других классах, причём без наследования от примеси.
|
||||
|
||||
Примесь – общий термин в объектно-ориентированном программировании: класс, который содержит в себе методы для других классов.
|
||||
|
||||
С примесями могут возникнуть конфликты, если они перезаписывают существующие методы класса. Стоит помнить об этом и быть внимательнее при выборе имён для методов примеси, чтобы их избежать.
|
||||
|
||||
## Пример примеси
|
||||
Простейший способ реализовать примесь в JavaScript – это создать объект с полезными методами, которые затем могут быть легко добавлены в прототип любого класса.\
|
||||
Это не наследование, а просто копирование методов.
|
||||
Примеси могут наследовать друг друга.
|
||||
Reference in New Issue
Block a user