Skip to content

Commit b76c541

Browse files
committed
renovations
1 parent e194813 commit b76c541

5 files changed

Lines changed: 102 additions & 61 deletions

File tree

1-js/8-oop/1-about-oop/article.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,13 @@ var vasya = new User("Вася"); // создали пользователя
3434
vasya.sayHi(); // пользователь умеет говорить "Привет"
3535
```
3636

37-
Здесь мы видим ярко выраженную сущность -- `User` (посетитель).
37+
Здесь мы видим ярко выраженную сущность -- `User` (посетитель). Используя терминологию ООП, такие конструкторы часто называют *классами*, то есть можно сказать "класс `User`".
38+
39+
[smart header="Класс в ООП"]
40+
[Классом]("https://en.wikipedia.org/wiki/Class_(computer_programming)") в объектно-ориентированной разработке называют шаблон/программный код, предназначенный для создания объектов и методов.
41+
42+
В JavaScript классы можно организовать по-разному. Говорят, что класс `User` написан в "функциональном" стиле. Далее мы также увидим "прототипный" стиль.
43+
[/smart]
3844

3945
ООП -- это наука о том, как делать правильную архитектуру. У неё есть свои принципы, например [SOLID](https://ru.wikipedia.org/wiki/SOLID_%28%D0%BE%D0%B1%D1%8A%D0%B5%D0%BA%D1%82%D0%BD%D0%BE-%D0%BE%D1%80%D0%B8%D0%B5%D0%BD%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D0%BE%D0%B5_%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%29).
4046

@@ -47,4 +53,4 @@ vasya.sayHi(); // пользователь умеет говор
4753
<i>Э. Гамма, Р. Хелм, Р. Джонсон, Дж. Влиссидес.</i></li>
4854
</ul>
4955

50-
Здесь мы не имеем возможности углубиться в теорию ООП, но основных "китов", на которых оно стоит, потрогаем за усы вдумчиво и конкретно.
56+
Здесь мы не имеем возможности углубиться в теорию ООП, поэтому чтение таких книг рекомендуется. Хотя основные принципы, как использовать ООП правильно, мы, всё же, затронем.

1-js/8-oop/5-functional-inheritance/article.md

Lines changed: 59 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,17 @@
1717

1818
Именно поэтому, увидев новую технику, мы уже можем что-то с ней сделать, даже не читая инструкцию.
1919

20-
**Механизм наследования позволяет определить базовый класс `Машина`, в нём описать то, что свойственно всем машинам, а затем на его основе построить другие, более конкретные: `Кофеварка`, `Холодильник` и т.п.**
20+
Механизм наследования позволяет определить базовый класс `Машина`, в нём описать то, что свойственно всем машинам, а затем на его основе построить другие, более конкретные: `Кофеварка`, `Холодильник` и т.п.
2121

2222
[smart header="В веб-разработке всё так же"]
23-
В веб-разработке нам могут понадобиться классы `Меню`, `Табы`, `Диалог` и другие компоненты интерфейса.
23+
В веб-разработке нам могут понадобиться классы `Меню`, `Табы`, `Диалог` и другие компоненты интерфейса. В них всех обычно есть что-то общее.
2424

25-
Можно выделить полезный общий функционал в класс `Компонент` и наследовать их от него, чтобы не дублировать код. Это обычная практика, принятая во множестве библиотек.
25+
Можно выделить такой общий функционал в класс `Компонент` и наследовать их от него, чтобы не дублировать код.
2626
[/smart]
2727

2828
## Наследование от Machine
2929

30-
Например, у нас есть класс `Machine`, который реализует методы "включить" `enable()` и "выключить" `disable()`:
30+
Базовый класс "машина" `Machine` будет реализовывать общего вида методы "включить" `enable()` и "выключить" `disable()`:
3131

3232
```js
3333
function Machine() {
@@ -48,33 +48,33 @@ function Machine() {
4848
```js
4949
function CoffeeMachine(power) {
5050
*!*
51-
Machine.call(this);
51+
Machine.call(this); // отнаследовать
5252
*/!*
53+
5354
var waterAmount = 0;
5455

5556
this.setWaterAmount = function(amount) {
5657
waterAmount = amount;
5758
};
5859

59-
function onReady() {
60-
alert('Кофе готово!');
61-
}
62-
63-
this.run = function() {
64-
setTimeout(onReady, 1000);
65-
};
66-
6760
}
6861

6962
var coffeeMachine = new CoffeeMachine(10000);
63+
64+
*!*
7065
coffeeMachine.enable();
66+
coffeeMachine.setWaterAmount(100);
67+
coffeeMachine.disable();
68+
*/!*
7169
```
7270

73-
Наследование реализовано вызовом `Machine.call(this)` в начале `CoffeeMachine`.
71+
Наследование реализовано вызовом `Machine.call(this)` в начале конструктора `CoffeeMachine`.
7472

7573
Он вызывает функцию `Machine`, передавая ей в качестве контекста `this` текущий объект. `Machine`, в процессе выполнения, записывает в `this` различные полезные свойства и методы, в нашем случае `this.enable` и `this.disable`.
7674

77-
Далее `CoffeeMachine` продолжает выполнение и может добавить свои свойства и методы, а также пользоваться унаследованными.
75+
Далее конструктор `CoffeeMachine` продолжает выполнение и может добавить свои свойства и методы.
76+
77+
В результате мы получаем объект `coffeeMachine`, который включает в себя методы из `Machine` и `CoffeeMachine`.
7878

7979
## Защищённые свойства
8080

@@ -116,13 +116,13 @@ var coffeeMachine = new CoffeeMachine(10000);
116116

117117
**Чтобы наследник имел доступ к свойству, оно должно быть записано в `this`.**
118118

119-
**При этом, чтобы обозначить, что свойство является внутренним, его имя начинают с подчёркивания `_`.**
119+
При этом, чтобы обозначить, что свойство является внутренним, его имя начинают с подчёркивания `_`.
120120

121121
```js
122122
//+ run
123123
function Machine() {
124124
*!*
125-
this._enabled = false;
125+
this._enabled = false; // вместо var enabled
126126
*/!*
127127

128128
this.enable = function() {
@@ -147,30 +147,19 @@ function CoffeeMachine(power) {
147147
var coffeeMachine = new CoffeeMachine(10000);
148148
```
149149

150-
**Подчёркивание в начале свойства -- общепринятый знак, что свойство является внутренним, предназначенным лишь для доступа из самого объекта и его наследников. Такие свойства называют *защищёнными*.**
150+
Подчёркивание в начале свойства -- общепринятый знак, что свойство является внутренним, предназначенным лишь для доступа из самого объекта и его наследников. Такие свойства называют *защищёнными*.
151151

152-
Технически это, конечно, возможно, но приличный программист снаружи в такое свойство не полезет.
153-
154-
**Вообще, это стандартная практика: конструктор сохраняет свои параметры в свойствах объекта. Иначе наследники не будут иметь к ним доступ.**
152+
Технически, залезть в него из внешнего кода, конечно, возможно, но приличный программист так делать не будет.
155153

156154
## Перенос свойства в защищённые
157155

158-
В коде выше есть свойство `power`. Сейчас мы его тоже сделаем защищённым и перенесём в `Machine`, поскольку "мощность" свойственна всем машинам, а не только кофеварке:
156+
У `CoffeeMachine` есть приватное свойство `power`. Сейчас мы его тоже сделаем защищённым и перенесём в `Machine`, поскольку "мощность" свойственна всем машинам, а не только кофеварке.
159157

160158
```js
161159
//+ run
162-
function CoffeeMachine(power) {
163-
*!*
164-
Machine.apply(this, arguments); // (1)
165-
*/!*
166-
167-
alert(this._enabled); // false
168-
alert(this._power); // 10000
169-
}
170-
171160
function Machine(power) {
172161
*!*
173-
this._power = power; // (2)
162+
this._power = power; // (1)
174163
*/!*
175164

176165
this._enabled = false;
@@ -184,34 +173,45 @@ function Machine(power) {
184173
};
185174
}
186175

176+
function CoffeeMachine(power) {
177+
*!*
178+
Machine.apply(this, arguments); // (2)
179+
*/!*
180+
181+
alert(this._enabled); // false
182+
alert(this._power); // 10000
183+
}
184+
187185
var coffeeMachine = new CoffeeMachine(10000);
188186
```
189187

190-
В коде выше при вызове `new CoffeeMachine(10000)` в строке `(1)` кофеварка передаёт аргументы и контекст родителю вызовом `Machine.apply(this, arguments)`.
188+
Теперь все машины `Machine` имеют мощность `power`. Обратим внимание, что мы из параметра конструктора сразу скопировали её в объект в строке `(1)`. Иначе она была бы недоступна из наследников.
191189

192-
Можно было бы использовать `Machine.call(this, power)`, но использование `apply` гарантирует передачу всех аргументов, мало ли, вдруг мы в будущем захотим их добавить.
190+
В строке `(2)` мы теперь вызываем не просто `Machine.call(this)`, а расширенный вариант: `Machine.apply(this, arguments)`, который вызывает `Machine` в текущем контексте вместе с передачей текущих аргументов.
193191

194-
Далее конструктор `Machine` в строке `(2)` сохраняет `power` в свойстве объекта `this._power`, благодаря этому кофеварка, когда наследование перейдёт обратно к `CoffeeMachine`, сможет сразу обращаться к нему.
192+
Можно было бы использовать и более простой вызов `Machine.call(this, power)`, но использование `apply` гарантирует передачу всех аргументов, вдруг их количество увеличится -- не надо будет переписывать.
195193

196194
## Переопределение методов
197195

198196
Итак, мы получили класс `CoffeeMachine`, который наследует от `Machine`.
199197

200-
Аналогичным образом мы можем унаследовать от `Machine` холодильник `Fridge`, микроволновку `MicroOven` и другие классы, которые разделяют общий "машинный" функционал.
198+
Аналогичным образом мы можем унаследовать от `Machine` холодильник `Fridge`, микроволновку `MicroOven` и другие классы, которые разделяют общий "машинный" функционал, то есть имеют мощность и их можно включать/выключать.
201199

202200
Для этого достаточно вызвать `Machine` текущем контексте, а затем добавить свои методы.
203201

204202
```js
205203
// Fridge может добавить и свои аргументы,
206204
// которые в Machine не будут использованы
207205
function Fridge(power, temperature) {
208-
Machine.call(this, arguments);
206+
Machine.apply(this, arguments);
209207

210208
// ...
211209
}
212210
```
213211

214-
Кроме создания новых методов, можно заменить унаследованные на свои:
212+
Бывает так, что реализация конкретного метода машины в наследнике имеет свои особенности.
213+
214+
Можно, конечно, объявить в `CoffeeMachine` свой `enable`:
215215

216216
```js
217217
function CoffeeMachine(power, capacity) {
@@ -224,7 +224,7 @@ function CoffeeMachine(power, capacity) {
224224
}
225225
```
226226

227-
...Однако, как правило, мы хотим не заменить, а *расширить* метод родителя. Например, сделать так, чтобы при включении кофеварка тут же запускалась.
227+
...Однако, как правило, мы хотим не заменить, а *расширить* метод родителя, добавить к нему что-то. Например, сделать так, чтобы при включении кофеварка тут же запускалась.
228228

229229
Для этого метод родителя предварительно копируют в переменную, и затем вызывают внутри нового `enable` -- там, где считают нужным:
230230

@@ -247,10 +247,10 @@ function CoffeeMachine(power) {
247247
**Общая схема переопределения метода (по строкам выделенного фрагмента кода):**
248248

249249
<ol>
250-
<li>Мы скопировали доставшийся от родителя метод `enable` в переменную, например `parentEnable`.</li>
251-
<li>Заменили метод `this.enable()` на свою функцию...</li>
252-
<li>Которая по-прежнему реализует старый функционал через вызов `parentEnable`...</li>
253-
<li>И в дополнение к нему делает что-то своё, например запускает приготовление кофе.</li>
250+
<li>Копируем доставшийся от родителя метод `this.enable` в переменную, например `parentEnable`.</li>
251+
<li>Заменяем `this.enable` на свою функцию...</li>
252+
<li>...Которая по-прежнему реализует старый функционал через вызов `parentEnable`.</li>
253+
<li>...И в дополнение к нему делает что-то своё, например запускает приготовление кофе.</li>
254254
</ol>
255255

256256
Обратим внимание на строку `(3)`.
@@ -266,15 +266,17 @@ function Machine(power) {
266266

267267
*!*
268268
var self = this;
269+
*/!*
269270

270271
this.enable = function() {
272+
*!*
271273
// используем внешнюю переменную вместо this
272274
self._enabled = true;
273-
};
274275
*/!*
276+
};
275277

276278
this.disable = function() {
277-
this._enabled = false;
279+
self._enabled = false;
278280
};
279281

280282
}
@@ -325,14 +327,14 @@ coffeeMachine.enable();
325327
```js
326328
function Machine(params) {
327329
// локальные переменные и функции доступны только внутри Machine
328-
var private;
330+
var privateProperty;
329331

330332
// публичные доступны снаружи
331-
this.public = ...;
333+
this.publicProperty = ...;
332334

333335
// защищённые доступны внутри Machine и для потомков
334336
// мы договариваемся не трогать их снаружи
335-
this._protected = ...
337+
this._protectedProperty = ...
336338
}
337339

338340
var machine = new Machine(...)
@@ -349,12 +351,12 @@ function CoffeeMachine(params) {
349351
Machine.apply(this, arguments);
350352
*/!*
351353

352-
this.coffeePublic = ...
354+
this.coffeePublicProperty = ...
353355
}
354356

355357
var coffeeMachine = new CoffeeMachine(...);
356-
coffeeMachine.public();
357-
coffeeMachine.coffeePublic();
358+
coffeeMachine.publicProperty();
359+
coffeeMachine.coffeePublicProperty();
358360
```
359361

360362
</li>
@@ -365,9 +367,9 @@ function CoffeeMachine(params) {
365367
Machine.apply(this, arguments);
366368

367369
*!*
368-
var parentProtected = this._protected;
369-
this._protected = function(args) {
370-
parentProtected.call(this, args); // (*)
370+
var parentProtected = this._protectedProperty;
371+
this._protectedProperty = function(args) {
372+
parentProtected.apply(this, args); // (*)
371373
// ...
372374
};
373375
*/!*
@@ -389,11 +391,11 @@ function Machine(params) {
389391
</li>
390392
</ol>
391393

392-
**В следующих главах мы будем изучать прототипный подход, который обладаем рядом преимуществ, по сравнению с функциональным.**
394+
Надо сказать, что способ наследования, описанный в этой главе, используется нечасто.
393395

394-
Но функциональный тоже бывает полезен.
396+
В следующих главах мы будем изучать прототипный подход, который обладаем рядом преимуществ.
395397

396-
В своей практике разработки я обычно наследую функционально в тех случаях, когда *уже* есть какой-то код, который на нём построен. К примеру, уже существуют классы, написанные сторонними разработчиками, которые можно доопределить или расширить только так.
398+
Но знать и понимать его необходимо, поскольку во многих существующих библиотеках классы написаны в функциональном стиле, и расширять/наследовать от них можно только так.
397399

398400

399401

1-js/9-prototypes/1-prototype/article.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
Если один объект имеет специальную ссылку `__proto__` на другой объект, то при чтении свойства из него, если свойство отсутствует в самом объекте, оно ищется в объекте `__proto__`.
1111

12-
Свойство доступно во всех браузерах, кроме IE10-. Впрочем, в старых IE оно, на самом деле, тоже есть, но требуются чуть более сложные способы для работы с ним, которые мы рассмотрим позднее.
12+
Свойство `__proto__` доступно во всех браузерах, кроме IE10-, а в более старых IE оно, конечно же, тоже есть, но напрямую к нему не обратиться, требуются чуть более сложные способы, которые мы рассмотрим позднее.
1313

1414
Пример кода (кроме IE10-):
1515

0 commit comments

Comments
 (0)