update classes
This commit is contained in:
44
docs/javascript/05-prototypes/01-prototype-inheritance.md
Normal file
44
docs/javascript/05-prototypes/01-prototype-inheritance.md
Normal 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`.
|
||||
28
docs/javascript/05-prototypes/02-f-prototype.md
Normal file
28
docs/javascript/05-prototypes/02-f-prototype.md
Normal 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"`.
|
||||
66
docs/javascript/05-prototypes/03-build-in-prototypes.md
Normal file
66
docs/javascript/05-prototypes/03-build-in-prototypes.md
Normal 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` уже наследует от другого объекта. Помните, мы можем наследовать только от одного объекта одновременно.
|
||||
|
||||
Заимствование методов – гибкий способ, позволяющий смешивать функциональность разных объектов по необходимости.
|
||||
31
docs/javascript/05-prototypes/04-prototype-methods.md
Normal file
31
docs/javascript/05-prototypes/04-prototype-methods.md
Normal 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`.
|
||||
Reference in New Issue
Block a user