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,44 @@
---
sidebar_position: 1
---
# Прототипное наследование
- В JavaScript все объекты имеют скрытое свойство `[[Prototype]]`, которое является либо другим объектом, либо `null`.
- Мы можем использовать `obj.__proto__` для доступа к нему (исторически обусловленный геттер/сеттер, есть другие способы, которые скоро будут рассмотрены).
- Объект, на который ссылается `[[Prototype]]`, называется «прототипом».
- Если мы хотим прочитать свойство `obj` или вызвать метод, которого не существует у `obj`, тогда JavaScript попытается найти его в прототипе.
- Операции записи/удаления работают непосредственно с объектом, они не используют прототип (если это обычное свойство, а не сеттер).
- Если мы вызываем `obj.method()`, а метод при этом взят из прототипа, то `this` всё равно ссылается на `obj`. Таким образом, методы всегда работают с текущим объектом, даже если они наследуются.
- Цикл `for..in` перебирает как свои, так и унаследованные свойства. Остальные методы получения ключей/значений работают только с собственными свойствами объекта.
## Prototype
В JavaScript объекты имеют специальное скрытое свойство `[[Prototype]]` (так оно названо в спецификации), которое либо равно `null`, либо ссылается на другой объект.\
Когда мы хотим прочитать свойство из object, а оно отсутствует, JavaScript автоматически берёт его из прототипа. В программировании такой механизм называется «прототипным наследованием».
Свойство `[[Prototype]]` является внутренним и скрытым, но есть много способов задать его.
Одним из них является использование `__proto__`
💥 Свойство `__proto__` — исторически обусловленный геттер/сеттер для `[[Prototype]]`\
Обратите внимание, что __proto__ — не то же самое, что внутреннее свойство [[Prototype]].
Это геттер/сеттер для `[[Prototype]]`. Позже мы увидим ситуации, когда это имеет значение, а пока давайте просто будем иметь это в виду, поскольку мы строим наше понимание языка JavaScript.
Свойство `__proto__` немного устарело, оно существует по историческим причинам. Современный JavaScript предполагает, что мы должны использовать функции `Object.getPrototypeOf/Object.setPrototypeOf` вместо того, чтобы получать/устанавливать прототип. Мы также рассмотрим эти функции позже.
По спецификации `__proto__` должен поддерживаться только браузерами, но по факту все среды, включая серверную, поддерживают его. Так что мы вполне безопасно его используем.
## Операция записи не использует прототип
Прототип используется только для чтения свойств. \
Операции записи/удаления работают напрямую с объектом.
## Значение «this»
***Неважно, где находится метод: в объекте или его прототипе. При вызове метода `this` — всегда объект перед точкой.***
Таким образом, вызов сеттера `admin.fullName=` в качестве `this` использует `admin`, а не `user`.
## Цикл for…in
Цикл `for..in` проходит не только по собственным, но и по унаследованным свойствам объекта.
Если унаследованные свойства нам не нужны, то мы можем отфильтровать их при помощи встроенного метода `obj.hasOwnProperty(key)`: он возвращает `true`, если у `obj` есть собственное, не унаследованное, свойство с именем `key`.

View File

@@ -0,0 +1,28 @@
---
sidebar_position: 2
---
# F.prototype
Как мы помним, новые объекты могут быть созданы с помощью функции-конструктора new F().
Если в F.prototype содержится объект, оператор new устанавливает его в качестве [[Prototype]] для нового объекта.
💥 На заметку:
JavaScript использовал прототипное наследование с момента своего появления. Это одна из основных особенностей языка.
Но раньше, в старые времена, прямого доступа к прототипу объекта не было. Надёжно работало только свойство "prototype" функции-конструктора, описанное в этой главе. Поэтому оно используется во многих скриптах.
- Свойство `F.prototype` (не путать с `[[Prototype]]`) устанавливает `[[Prototype]]` для новых объектов при вызове `new F()`.
- Значение `F.prototype` должно быть либо объектом, либо `null`. Другие значения не будут работать.
- Свойство `"prototype"` является особым, только когда оно назначено функции-конструктору, которая вызывается оператором `new`.
-
В обычных объектах prototype не является чем-то особенным:
```js
let user = {
name: "John",
prototype: "Bla-bla" // никакой магии нет - обычное свойство
};
```
По умолчанию все функции имеют `F.prototype = { constructor: F }`, поэтому мы можем получить конструктор объекта через свойство `"constructor"`.

View File

@@ -0,0 +1,66 @@
---
sidebar_position: 3
---
# Встроенные прототипы
- Все встроенные объекты следуют одному шаблону:
- Методы хранятся в прототипах (Array.prototype, Object.prototype, Date.prototype и т.д.).
- Сами объекты хранят только данные (элементы массивов, свойства объектов, даты).
- Примитивы также хранят свои методы в прототипах объектов-обёрток: Number.prototype, String.prototype, Boolean.prototype. Только у значений undefined и null нет объектов-обёрток.
- Встроенные прототипы могут быть изменены или дополнены новыми методами. Но не рекомендуется менять их. Единственная допустимая причина это добавление нового метода из стандарта, который ещё не поддерживается движком JavaScript.
## Object.prototype
Краткая нотация `obj = {}` это то же самое, что и `obj = new Object()`, где `Object` встроенная функция-конструктор для объектов с собственным свойством `prototype`, которое ссылается на огромный объект с методом `toString` и другими.
```js
let obj = {};
alert( obj ); // "[object Object]" ?
```
Таким образом, когда вызывается obj.toString(), метод берётся из Object.prototype.
## Другие встроенные прототипы
Другие встроенные объекты, такие как Array, Date, Function и другие, также хранят свои методы в прототипах.
## Примитивы
Самое сложное происходит со строками, числами и булевыми значениями.
Если мы попытаемся получить доступ к их свойствам, то тогда будет создан временный объект-обёртка с использованием встроенных конструкторов String, Number и Boolean, который предоставит методы и после этого исчезнет.
Эти объекты создаются невидимо для нас, и большая часть движков оптимизирует этот процесс, но спецификация описывает это именно таким образом. Методы этих объектов также находятся в прототипах, доступных как String.prototype, Number.prototype и Boolean.prototype.
💥 **Значения null и undefined не имеют объектов-обёрток**
## Изменение встроенных прототипов
Встроенные прототипы можно изменять. Например, если добавить метод к String.prototype, метод становится доступен для всех строк:
```js
String.prototype.show = function() {
alert(this);
};
"BOOM!".show(); // BOOM!
```
💥 Важно:
Прототипы глобальны, поэтому очень легко могут возникнуть конфликты. Если две библиотеки добавляют метод String.prototype.show, то одна из них перепишет метод другой.
Изменение встроенных прототипов считается плохой идеей.
*** В современном программировании есть только один случай, в котором одобряется изменение встроенных прототипов. Это создание полифилов.***
## Заимствование у прототипов
Некоторые методы встроенных прототипов часто одалживают.
Например, если мы создаём объект, похожий на массив (псевдомассив), мы можем скопировать некоторые методы из Array в этот объект.
```js
let obj = {
0: "Hello",
1: "world!",
length: 2,
};
obj.join = Array.prototype.join;
alert( obj.join(',') ); // Hello,world!
```
Это работает, потому что для внутреннего алгоритма встроенного метода `join` важны только корректность индексов и свойство `length`, он не проверяет, является ли объект на самом деле массивом. И многие встроенные методы работают так же.
Альтернативная возможность мы можем унаследовать от массива, установив `obj.__proto__` как `Array.prototype`, таким образом все методы `Array` станут автоматически доступны в `obj`.
Но это будет невозможно, если `obj` уже наследует от другого объекта. Помните, мы можем наследовать только от одного объекта одновременно.
Заимствование методов гибкий способ, позволяющий смешивать функциональность разных объектов по необходимости.

View File

@@ -0,0 +1,31 @@
---
sidebar_position: 4
---
# Методы прототипов, объекты без свойства __proto__
Свойство `__proto__` считается устаревшим, и по стандарту оно должно поддерживаться только браузерами.
Современные же методы это:
- `Object.create(proto[, descriptors])` создаёт пустой объект со свойством [[Prototype]], указанным как proto, и необязательными дескрипторами свойств `descriptors`.
- `Object.getPrototypeOf(obj)` возвращает свойство `[[Prototype]]` объекта `obj`.
- `Object.setPrototypeOf(obj, proto)` устанавливает свойство `[[Prototype]]` объекта `obj` как `proto`.
Эти методы нужно использовать вместо `__proto__`.
Встроенный геттер/сеттер `__proto__` не безопасен, если мы хотим использовать созданные пользователями ключи в объекте. Как минимум потому, что пользователь может ввести "__proto__" как ключ, от чего может возникнуть ошибка. Если повезёт последствия будут лёгкими, но, вообще говоря, они непредсказуемы.
Так что мы можем использовать либо `Object.create(null)` для создания «простейшего» объекта, либо использовать коллекцию `Map`.
Кроме этого, `Object.create` даёт нам лёгкий способ создать поверхностную копию объекта со всеми дескрипторами:
`__proto__` это геттер/сеттер для свойства `[[Prototype]]`, и находится он в `Object.prototype`, как и другие методы.
Ещё методы:
- `Object.keys(obj)` / `Object.values(obj)` / `Object.entries(obj)` возвращают массив всех перечисляемых собственных строковых ключей/значений/пар ключ-значение.
- `Object.getOwnPropertySymbols(obj)` возвращает массив всех собственных символьных ключей.
- `Object.getOwnPropertyNames(obj)` возвращает массив всех собственных строковых ключей.
- `Reflect.ownKeys(obj)` возвращает массив всех собственных ключей.
- `obj.hasOwnProperty(key)`: возвращает `true`, если у `obj` есть собственное (не унаследованное) свойство с именем `key`.
Все методы, которые возвращают свойства объектов (такие как `Object.keys` и другие), возвращают «собственные» свойства. Если мы хотим получить и унаследованные, можно воспользоваться циклом `for..in`.