Skip to content

Commit be65500

Browse files
Merge pull request #132 from MihailVasilev/MihailVasilev-patch-1
1-js/09-classes/07-mixins
2 parents a72e7e1 + f842429 commit be65500

2 files changed

Lines changed: 72 additions & 67 deletions

File tree

Lines changed: 64 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,26 @@
1-
# Mixins
1+
# Примеси
22

3-
In JavaScript we can only inherit from a single object. There can be only one `[[Prototype]]` for an object. And a class may extend only one other class.
3+
В JavaScript можно наследовать только от одного объекта. Объект имеет единственный `[[Prototype]]`. И класс может расширить только один другой класс.
44

5-
But sometimes that feels limiting. For instance, I have a class `StreetSweeper` and a class `Bicycle`, and want to make a `StreetSweepingBicycle`.
5+
Иногда это может ограничивать нас. Например, у нас есть класс `StreetSweeper` и класс `Bicycle`, а мы хотим создать `StreetSweepingBicycle`.
66

7-
Or, talking about programming, we have a class `Renderer` that implements templating and a class `EventEmitter` that implements event handling, and want to merge these functionalities together with a class `Page`, to make a page that can use templates and emit events.
7+
Или, говоря о программировании, у нас есть класс `User`, который реализует пользователей, и класс `EventEmitter`, реализующий события. Мы хотели бы добавить функционал класса `EventEmitter` к `User`, чтобы пользователи могли легко генерировать события.
88

9-
There's a concept that can help here, called "mixins".
9+
Для таких случаев существуют "примеси".
1010

11-
As defined in Wikipedia, a [mixin](https://en.wikipedia.org/wiki/Mixin) is a class that contains methods for use by other classes without having to be the parent class of those other classes.
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)) - это класс, методы которого предназначены для использования в других классах, причём без наследования от примеси.
1212

13-
In other words, a *mixin* provides methods that implement a certain behavior, but we do not use it alone, we use it to add the behavior to other classes.
13+
Другими словами, *примесь* определяет методы, которые реализуют определённое поведение. Мы не используем примесь саму по себе, она нужна, чтобы добавить другим классам больше функционала.
1414

15-
## A mixin example
15+
## Пример примеси
1616

17-
The simplest way to make a mixin in JavaScript is to make an object with useful methods, so that we can easily merge them into a prototype of any class.
17+
Простейший способ сделать примесь в JavaScript - это создать объект с полезными методами, которые затем могут быть легко добавлены в прототип любого класса.
1818

19-
For instance here the mixin `sayHiMixin` is used to add some "speech" for `User`:
19+
В примере ниже примесь `sayHiMixin` имеет методы для придания объектам класса `User` возможности вести разговор:
2020

2121
```js run
2222
*!*
23-
// mixin
23+
// примесь
2424
*/!*
2525
let sayHiMixin = {
2626
sayHi() {
@@ -32,34 +32,36 @@ let sayHiMixin = {
3232
};
3333

3434
*!*
35-
// usage:
35+
// использование:
3636
*/!*
3737
class User {
3838
constructor(name) {
3939
this.name = name;
4040
}
4141
}
4242

43-
// copy the methods
43+
// копируем методы
4444
Object.assign(User.prototype, sayHiMixin);
4545

46-
// now User can say hi
46+
// сейчас User может сказать Hello
4747
new User("Dude").sayHi(); // Hello Dude!
4848
```
4949

50-
There's no inheritance, but a simple method copying. So `User` may extend some other class and also include the mixin to "mix-in" the additional methods, like this:
50+
Это не наследование, а просто копирование методов. Таким образом, класс `User` может наследовать от другого класса, но при этом также включать в себя примеси, "подмешивающие" другие методы.
51+
52+
Например:
5153

5254
```js
5355
class User extends Person {
54-
// ...
56+
// ...
5557
}
5658

5759
Object.assign(User.prototype, sayHiMixin);
5860
```
5961

60-
Mixins can make use of inheritance inside themselves.
62+
Примеси могут наследовать друг другу.
6163

62-
For instance, here `sayHiMixin` inherits from `sayMixin`:
64+
В примере ниже `sayHiMixin` наследует от `sayMixin`:
6365

6466
```js run
6567
let sayMixin = {
@@ -69,11 +71,11 @@ let sayMixin = {
6971
};
7072

7173
let sayHiMixin = {
72-
__proto__: sayMixin, // (or we could use Object.create to set the prototype here)
74+
__proto__: sayMixin, // (или мы можем использовать Object.create для задания прототипа)
7375

7476
sayHi() {
7577
*!*
76-
// call parent method
78+
// вызываем метод родителя
7779
*/!*
7880
super.say(`Hello ${this.name}`);
7981
},
@@ -88,41 +90,43 @@ class User {
8890
}
8991
}
9092

91-
// copy the methods
93+
// копируем методы
9294
Object.assign(User.prototype, sayHiMixin);
9395

94-
// now User can say hi
96+
// сейчас User может сказать Hello
9597
new User("Dude").sayHi(); // Hello Dude!
9698
```
9799

98-
Please note that the call to the parent method `super.say()` from `sayHiMixin` looks for the method in the prototype of that mixin, not the class.
100+
Обратим внимание, что при вызове родительского метода `super.say()` из `sayHiMixin` этот метод ищется в прототипе самой примеси, а не класса.
99101

100102
![](mixin-inheritance.png)
101103

102-
That's because methods from `sayHiMixin` have `[[HomeObject]]` set to it. So `super` actually means `sayHiMixin.__proto__`, not `User.__proto__`.
104+
Это связано с тем, что методы `sayHi` и `sayBye` были изначально созданы в объекте `sayHiMixin`. Так что их внутреннее свойство `[[HomeObject]]` ссылается на `sayHiMixin`, как показано на картинке выше.
103105

104-
## EventMixin
106+
Так как `super` ищет родительские методы в `[[HomeObject]].[[Prototype]]`, это означает `sayHiMixin.[[Prototype]]`, а не `User.[[Prototype]]`.
105107

106-
Now let's make a mixin for real life.
108+
## EventMixin
107109

108-
The important feature of many objects is working with events.
110+
А сейчас давайте создадим примесь, полезную в реальной жизни.
109111

110-
That is: an object should have a method to "generate an event" when something important happens to it, and other objects should be able to "listen" to such events.
112+
Многие объекты в браузерной разработке (и не только) обладают важной способностью - они могут генерировать события. События - отличный способ передачи информации всем, кто в ней заинтересован. Давайте создадим примесь, которая позволит легко добавлять функционал по работе с событиями любым классам/объектам.
111113

112-
An event must have a name and, optionally, bundle some additional data.
114+
- Примесь добавит метод `.trigger(name, [data])` для генерации события. Аргумент `name` - это имя события, за которым могут следовать другие аргументы с данными для события.
115+
- Также будет добавлен метод `.on(name, handler)`, который назначает обработчик для события с заданным именем. Обработчик будет вызван, когда произойдёт событие с указанным именем `name`, и получит данные из `.trigger`.
116+
- ...и метод `.off(name, handler)`, который удаляет обработчик указанного события.
113117

114-
For instance, an object `user` can generate an event `"login"` when the visitor logs in. And another object `calendar` may want to receive such events to load the calendar for the logged-in person.
118+
После того, как все методы примеси будут добавлены, объект `user` сможет сгенерировать событие `"login"` после входа пользователя в личный кабинет. А другой объект, к примеру, `calendar` сможет использовать это событие, чтобы показывать зашедшему пользователю актуальный для него календарь.
115119

116-
Or, a `menu` can generate the event `"select"` when a menu item is selected, and other objects may want to get that information and react on that event.
120+
Или `menu` может генерировать событие `"select"`, когда элемент меню выбран, а другие объекты могут назначать обработчики, чтобы реагировать на это событие, и т.п.
117121

118-
Events is a way to "share information" with anyone who wants it. They can be useful in any class, so let's make a mixin for them:
122+
Вот код примеси:
119123

120124
```js run
121125
let eventMixin = {
122126
/**
123-
* Subscribe to event, usage:
124-
* menu.on('select', function(item) { ... }
125-
*/
127+
* Подписаться на событие, использование:
128+
* menu.on('select', function(item) { ... }
129+
*/
126130
on(eventName, handler) {
127131
if (!this._eventHandlers) this._eventHandlers = {};
128132
if (!this._eventHandlers[eventName]) {
@@ -132,8 +136,8 @@ let eventMixin = {
132136
},
133137

134138
/**
135-
* Cancel the subscription, usage:
136-
* menu.off('select', handler)
139+
* Отменить подписку, использование:
140+
* menu.off('select', handler)
137141
*/
138142
off(eventName, handler) {
139143
let handlers = this._eventHandlers && this._eventHandlers[eventName];
@@ -146,60 +150,61 @@ let eventMixin = {
146150
},
147151

148152
/**
149-
* Generate the event and attach the data to it
150-
* this.trigger('select', data1, data2);
153+
* Сгенерировать событие с указанным именем и данными
154+
* this.trigger('select', data1, data2);
151155
*/
152156
trigger(eventName, ...args) {
153157
if (!this._eventHandlers || !this._eventHandlers[eventName]) {
154-
return; // no handlers for that event name
158+
return; // обработчиков для этого события нет
155159
}
156160

157-
// call the handlers
161+
// вызовем обработчики
158162
this._eventHandlers[eventName].forEach(handler => handler.apply(this, args));
159163
}
160164
};
161165
```
162166

163-
There are 3 methods here:
167+
Итак, у нас есть 3 метода:
168+
169+
1. `.on(eventName, handler)` -- назначает функцию `handler`, чтобы обработать событие с заданным именем. Обработчики хранятся в свойстве `_eventHandlers`, представляющим собой объект, в котором имя события является ключом, а массив обработчиков - значением.
164170

165-
1. `.on(eventName, handler)` -- assigns function `handler` to run when the event with that name happens. The handlers are stored in the `_eventHandlers` property.
166-
2. `.off(eventName, handler)` -- removes the function from the handlers list.
167-
3. `.trigger(eventName, ...args)` -- generates the event: all assigned handlers are called and `args` are passed as arguments to them.
171+
2. `.off(eventName, handler)` -- убирает функцию из списка обработчиков.
168172

173+
3. `.trigger(eventName, ...args)` -- генерирует событие: все назначенные обработчики из `_eventHandlers[eventName]` вызываются, и `...args` передаются им в качестве аргументов.
169174

170-
Usage:
175+
Использование:
171176

172177
```js run
173-
// Make a class
178+
// Создадим класс
174179
class Menu {
175180
choose(value) {
176181
this.trigger("select", value);
177182
}
178183
}
179-
// Add the mixin
184+
// Добавим примесь с методами для событий
180185
Object.assign(Menu.prototype, eventMixin);
181186

182187
let menu = new Menu();
183188

184-
// call the handler on selection:
189+
// Добавить обработчик, который будет вызван при событии "select":
185190
*!*
186-
menu.on("select", value => alert(`Value selected: ${value}`));
191+
menu.on("select", value => alert(`Выбранное значение: ${value}`));
187192
*/!*
188193

189-
// triggers the event => shows Value selected: 123
190-
menu.choose("123"); // value selected
194+
// Генерирует событие => обработчик выше запускается и выводит:
195+
menu.choose("123"); // Выбранное значение: 123
191196
```
192197

193-
Now if we have the code interested to react on user selection, we can bind it with `menu.on(...)`.
198+
Теперь если у нас есть код, заинтересованный в событии `"select"`, то он может слушать его с помощью `menu.on(...)`.
194199

195-
And the `eventMixin` can add such behavior to as many classes as we'd like, without interfering with the inheritance chain.
200+
А `eventMixin` позволяет легко добавить такое поведение в любой класс без вмешательства в цепочку наследования.
196201

197-
## Summary
202+
## Итого
198203

199-
*Mixin* -- is a generic object-oriented programming term: a class that contains methods for other classes.
204+
*Примесь* -- общий термин в объектно-ориентированном программировании: класс, который содержит в себе методы для других классов.
200205

201-
Some other languages like e.g. python allow to create mixins using multiple inheritance. JavaScript does not support multiple inheritance, but mixins can be implemented by copying them into the prototype.
206+
В других языках (например, Python) разрешается создавать примеси, используя множественное наследование. JavaScript не поддерживает множественное наследование, но с помощью примесей мы можем реализовать нечто похожее, скопировав методы в прототип.
202207

203-
We can use mixins as a way to augment a class by multiple behaviors, like event-handling as we have seen above.
208+
Мы можем использовать примеси для расширения функционала классов, например, для обработки событий, как мы сделали это выше.
204209

205-
Mixins may become a point of conflict if they occasionally overwrite native class methods. So generally one should think well about the naming for a mixin, to minimize such possibility.
210+
С примесями могут возникнуть проблемы, если они перезаписывают существующие методы класса. Стоит помнить об этом и быть внимательнее при выборе имён для методов примеси, чтобы избежать конфликтов.

1-js/09-classes/07-mixins/head.html

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
let eventMixin = {
33

44
/**
5-
* Subscribe to event, usage:
6-
* menu.on('select', function(item) { ... }
5+
* Подписаться на событие, использование:
6+
* menu.on('select', function(item) { ... }
77
*/
88
on(eventName, handler) {
99
if (!this._eventHandlers) this._eventHandlers = {};
@@ -14,8 +14,8 @@
1414
},
1515

1616
/**
17-
* Cancel the subscription, usage:
18-
* menu.off('select', handler)
17+
* Отменить подписку, использование:
18+
* menu.off('select', handler)
1919
*/
2020
off(eventName, handler) {
2121
let handlers = this._eventHandlers && this._eventHandlers[eventName];
@@ -28,15 +28,15 @@
2828
},
2929

3030
/**
31-
* Generate the event and attach the data to it
32-
* this.trigger('select', data1, data2);
31+
* Сгенерировать событие с указанным именем и данными
32+
* this.trigger('select', data1, data2);
3333
*/
3434
trigger(eventName, ...args) {
3535
if (!this._eventHandlers || !this._eventHandlers[eventName]) {
36-
return; // no handlers for that event name
36+
return; // обработчиков для этого события нет
3737
}
3838

39-
// call the handlers
39+
// вызовем обработчики
4040
this._eventHandlers[eventName].forEach(handler => handler.apply(this, args));
4141
}
4242
};

0 commit comments

Comments
 (0)