You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: 1-js/09-classes/07-mixins/article.md
+28-24Lines changed: 28 additions & 24 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,20 +1,20 @@
1
1
# Примеси
2
2
3
-
В JavaScript мы можем использовать наследование только от одного объекта. Объект имеет единственный `[[Prototype]]`. И класс может расширить только один другой класс.
3
+
В JavaScript можно наследовать только от одного объекта. Объект имеет единственный `[[Prototype]]`. И класс может расширить только один другой класс.
4
4
5
5
Иногда это может ограничивать нас. Например, у нас есть класс `StreetSweeper` и класс `Bicycle`, а мы хотим создать `StreetSweepingBicycle`.
6
6
7
7
Или, говоря о программировании, у нас есть класс `Renderer`, который реализует шаблон, и класс `EventEmitter`, реализующий событие. Мы хотели бы объединить их функционал в классе `Page`, чтобы создать страницу, которая будет использовать шаблон и запускать событие.
8
8
9
9
Для таких случаев существуют "примеси".
10
10
11
-
Как говорится в Википедии, [примесь](https://ru.wikipedia.org/wiki/%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%81%D1%8C_(%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5))является классом, который имеет методы, и мы можем использовать эти методы в других классах, не будучи родителем тех классов.
11
+
По определению из Википедии, [примесь](https://ru.wikipedia.org/wiki/%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%81%D1%8C_(%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5))- это класс, методы которого предназначены для использования в других классах, причём без наследования от примеси.
12
12
13
13
Другими словами, *примесь* определяет методы, которые реализуют определённое поведение. Мы не используем примесь саму по себе, она нужна, чтобы добавить другим классам больше функционала.
14
14
15
15
## Пример примеси
16
16
17
-
Простой путь создания примеси в JavaScript - это создание объекта с полезными методами, так чтобы мы легко могли добавить их в прототип любого класса.
17
+
Простейший способ сделать примесь в JavaScript - это создать объект с полезными методами, которые затем могут быть легко добавлены в прототип любого класса.
18
18
19
19
В примере ниже примесь `sayHiMixin` имеет методы для придания объектам класса `User` возможности вести разговор:
Это не наследование, а просто скопированные методы. Таким образом, класс `User` может расширять другие классы и также содержать в себе методы от примеси, например:
50
+
Это не наследование, а просто копирование методов. Таким образом, класс `User` может расширять другие классы, но при этом также включать в себя примеси, "подмешивающие" другие методы.
51
+
52
+
Например:
51
53
52
54
```js
53
55
classUserextendsPerson {
@@ -57,7 +59,7 @@ class User extends Person {
57
59
Object.assign(User.prototype, sayHiMixin);
58
60
```
59
61
60
-
Примеси могут использовать наследование между собой.
62
+
Примеси могут наследовать друг другу.
61
63
62
64
В примере ниже `sayHiMixin` наследуется от `sayMixin`:
63
65
@@ -69,7 +71,7 @@ let sayMixin = {
69
71
};
70
72
71
73
let sayHiMixin = {
72
-
__proto__: sayMixin, // (или мы можем использовать Object.create для определения прототипа)
74
+
__proto__: sayMixin, // (или мы можем использовать Object.create для задания прототипа)
Обратите внимание, что мы вызвали родительский метод`super.say()` из `sayHiMixin` в прототипе другой примеси, а не в классе.
100
+
Обратим внимание, что при вызове родительского метода`super.say()` из `sayHiMixin`этот метод ищется в прототипе самой примеси, а не класса.
99
101
100
102

101
103
102
-
Это связано с тем, что методы из `sayHiMixin` имеют установленное свойство `[[HomeObject]]`. А `super`на самом деле означает `sayHiMixin.__ proto__`, а не `User.__ proto__`.
104
+
Это связано с тем, что методы `sayHi` и `sayBye` были изначально созданы в объекте `sayHiMixin`. Так что их внутреннее свойство `[[HomeObject]]` ссылается на `sayHiMixin`, как показано на картинке выше.
103
105
104
-
## EventMixin
106
+
Вызов `super` ищет родительские методы в `[[HomeObject]].__proto__`, что означает `sayHiMixin.__proto__`, а не `User.__proto__`.
105
107
106
-
Сейчас давайте создадим примесь для реального случая.
108
+
## EventMixin
107
109
108
-
Важная особенность при работе с объектами - это работа с событиями.
110
+
А сейчас давайте создадим примесь, полезную в реальной жизни.
109
111
110
-
То есть, объект должен иметь метод для "генерации события", когда с ним происходит что-то важное, а другие объекты должны иметь возможность "слушать" такие события.
112
+
Многие объекты в браузерной разработке (и не только) обладают важной способностью - они могут работать с событиями. События - это способ передачи информации всем, кто в ней заинтересован. Давайте создадим примесь, которая позволит легко добавлять функционал по работе с событиями любым классам/объектам.
111
113
112
-
Событие должно иметь имя и, опционально, какие-то дополнительные данные, ассоциированные с ним.
114
+
- Примесь добавит метод `.trigger(name, [data])` для генерации события. Аргумент `name` - это имя события, за которым следует опциональный аргумент `data`.
115
+
- Также будет добавлен метод `.on(name, handler)`, который назначает обработчик для события с заданным именем. Обработчик будет вызван, когда событие произойдёт.
116
+
- ...и метод `.off(name, handler)`, который удаляет обработчик указанного события.
113
117
114
-
Например, объект `user`может генерировать событие `"login"`, когда посетитель входит. А другой объект `calendar` может захотеть получать событие `"login"`, чтобы загрузить календарь для вошедшего пользователя.
118
+
После того, как все методы примеси будут добавлены, объект `user`сможет сгенерировать событие `"login"` после входа пользователя в личный кабинет. Объект `calendar`тоже может рассчитывать на событие `"login"`, чтобы показывать зашедшему пользователю актуальный для него календарь.
115
119
116
120
Или `menu` может генерировать событие `"select"`, когда элемент меню выбран, а другие объекты могут захотеть получить эту информацию и тоже отреагировать на это событие.
117
121
118
-
События — это способ "поделиться информацией" со всеми, кто этого захочет. Они могут быть полезны в любом классе, поэтому давайте сделаем для них пример:
122
+
Рассмотрим это всё на примере:
119
123
120
124
```js run
121
125
let eventMixin = {
122
126
/**
123
-
* Подпишемся на событие, используя:
127
+
* Подписаться на событие, использование:
124
128
* menu.on('select', function(item) { ... }
125
129
*/
126
130
on(eventName, handler) {
@@ -132,7 +136,7 @@ let eventMixin = {
132
136
},
133
137
134
138
/**
135
-
* Отменим подписку, используя:
139
+
* Отменить подписку, использование:
136
140
* menu.off('select', handler)
137
141
*/
138
142
off(eventName, handler) {
@@ -146,7 +150,7 @@ let eventMixin = {
146
150
},
147
151
148
152
/**
149
-
* Запустим событие и добавим к нему данные
153
+
* Сгенерировать событие с указанным именем и данными
150
154
* this.trigger('select', data1, data2);
151
155
*/
152
156
trigger(eventName, ...args) {
@@ -160,13 +164,13 @@ let eventMixin = {
160
164
};
161
165
```
162
166
163
-
Итак, у нас 3 метода:
167
+
Итак, у нас есть 3 метода:
164
168
165
-
1.`.on(eventName, handler)` -- назначили функцию `handler`, чтобы обработать событие с заданным именем. Храним обработчики в свойстве `_eventHandlers`.
169
+
1.`.on(eventName, handler)` -- назначает функцию `handler`, чтобы обработать событие с заданным именем. Обработчики хранятся в свойстве `_eventHandlers`, представляющим собой объект, в котором имя события является ключом, а массив обработчиков - значением.
166
170
167
-
2.`.off(eventName, handler)` -- убираем функцию из списка обработчиков.
171
+
2.`.off(eventName, handler)` -- убирает функцию из списка обработчиков.
168
172
169
-
3.`.trigger(eventName, ...args)` -- генерируем событие: все назначенные обработчики вызываются, и `args` передаётся в обработчики в качестве аргумента.
173
+
3.`.trigger(eventName, ...args)` -- генерирует событие: все назначенные обработчики из `_eventHandlers[eventName]`вызываются, и `args` передаётся им в качестве аргумента.
170
174
171
175
Использование:
172
176
@@ -191,9 +195,9 @@ menu.on("select", value => alert(`Выбранное значение: ${value}`
191
195
menu.choose("123"); // выбранное значение
192
196
```
193
197
194
-
Если у нас есть код, заинтересованный в событии "select", то мы можем привязать его с помощью `menu.on(...)`.
198
+
Если у нас есть код, заинтересованный в событии "select", то он может подписаться на него с помощью `menu.on(...)`.
195
199
196
-
И `eventMixin`может добавить такое поведение в любой класс без вмешательства в цепочку наследования.
200
+
И `eventMixin`позволяет легко добавить такое поведение в любой класс без вмешательства в цепочку наследования.
0 commit comments