diff --git a/1-js/6-objects-more/7-bind/article.md b/1-js/6-objects-more/7-bind/article.md
index c21511fa..4c283f65 100644
--- a/1-js/6-objects-more/7-bind/article.md
+++ b/1-js/6-objects-more/7-bind/article.md
@@ -73,11 +73,31 @@ setTimeout(function() {
*/!*
```
-Теперь код работает, так как `user` достаётся из замыкания.
+Теперь код работает, так как `user` достаётся из замыкания.
+
+Это решение также позволяет передать дополнительные аргументы:
+
+
+```js
+//+ run
+var user = {
+ firstName: "Вася",
+ sayHi: function(who) {
+ alert(this.firstName + ": Привет, " + who);
+ }
+};
+
+*!*
+setTimeout(function() {
+ user.sayHi("Петя"); // Вася: Привет, Петя
+}, 1000);
+*/!*
+```
+
Но тут же появляется и уязвимое место в структуре кода!
-**А что, если до срабатывания `setTimeout` в переменную `user` будет записано другое значение? К примеру, какой-то другой пользователь... В этом случае вызов неожиданно будет совсем не тот!**
+А что, если до срабатывания `setTimeout` (ведь есть целая секунда) в переменную `user` будет записано другое значение? К примеру, в другом месте кода будет присвоено `user=(другой пользователь)`... В этом случае вызов неожиданно будет совсем не тот!
Хорошо бы гарантировать правильность контекста.
@@ -87,23 +107,32 @@ setTimeout(function() {
```js
function bind(func, context) {
- return function() { // (*)
+ return function() { // (*)
return func.apply(context, arguments);
};
}
```
-Результатом вызова `bind(func, context)`, как видно из кода, является анонимная функция функция `(*)`, вот она отдельно:
+Посмотрим, что она делает, как работает, на таком примере:
+
+```js
+var oldSayHi = user.sayHi;
+var sayHi = bind(oldSayHi, user);
+```
+
+Результатом `bind(oldSayHi, user)`, как видно из кода, будет анонимная функция `(*)`, вот она отдельно:
```js
function() { // (*)
- return func.apply(context, arguments);
+ return oldSayHi.apply(user, arguments);
};
```
-Если её вызвать с какими-то аргументами, то она сама ничего не делает, а "передаёт вызов" в `func`. Здесь используется `apply`, чтобы вызвать `func` с теми же аргументами, которые получила эта анонимная функция и с контекстом `context`, который берётся из замыкания (был задан при вызове `bind`).
+Она запишется в переменную `sayHi`.
-Иными словами, в результате вызова `bind` мы получаем "функцию-обёртку", которая прозрачно передаёт вызов в `func`, с фиксированным контекстом `context`.
+Далее, если её вызвать с какими-то аргументами, например `sayHi("Петя")`, то она "передаёт вызов" в `oldSayHi` -- используется `.apply(user, arguments)`, чтобы передать в качестве контекста `user` (он будет взят из замыкания) и текущие аргументы `arguments`.
+
+Иными словами, в результате вызова `bind(func, context)` мы получаем "функцию-обёртку", которая прозрачно передаёт вызов в `func`, с теми же аргументами, но фиксированным контекстом `context`.
Пример с `bind`:
@@ -117,7 +146,7 @@ function bind(func, context) {
var user = {
firstName: "Вася",
- sayHi: function() {
+ sayHi: function() {
alert(this.firstName);
}
};
@@ -127,8 +156,32 @@ setTimeout( bind(user.sayHi, user), 1000 );
*/!*
```
-Теперь всё в порядке! В `setTimeout` пошла обёртка, фиксирующая контекст.
+Теперь всё в порядке!
+Вызов `bind(user.sayHi, user)` возвращает такую функцию-обёртку, которая гарантированно вызовет `user.sayHi` в контексте `user`. В данном случае, через 1000мс.
+
+Причём, если вызвать обёртку с аргументами -- они пойдут в `user.sayHi` без изменений, фиксирован лишь контекст.
+
+```js
+//+ run
+var user = {
+ firstName: "Вася",
+ sayHi: function(who) {
+ alert(this.firstName + ": Привет, " + who);
+ }
+};
+
+var sayHi = bind(user.sayHi, user);
+
+*!*
+sayHi("Петя"); // Вася: Привет, Петя
+sayHi("Маша"); // Вася: Привет, Маша
+*/!*
+```
+
+В примере выше продемонстрирована другая частая цель использования `bind` -- "привязать" функцию к контексту, чтобы в дальнейшем "не таскать за собой" объект, а просто вызывать `sayHi`.
+
+Результат `bind` можно передавать в любое место кода, вызывать как обычную функцию, он "помнит" свой контекст.
## Решение 3: встроенный метод bind [#bind]
@@ -151,7 +204,7 @@ var wrapper = func.bind(context[, arg1, arg2...])
Если указаны аргументы `arg1, arg2...` -- они будут прибавлены к каждому вызову новой функции, причем встанут *перед* теми, которые указаны при вызове.
-Результат вызова `func.bind(context)` аналогичен вызову `bind(func, context)`, описанному выше. То есть, `wrapper` -- это обёртка, фиксирующая контекст и передающая вызовы в `func`. Также можно указать аргументы, тогда и они будут фиксированы, а новые будут уже за ними, но об этом чуть позже.
+Результат вызова `func.bind(context)` аналогичен вызову `bind(func, context)`, описанному выше. То есть, `wrapper` -- это обёртка, фиксирующая контекст и передающая вызовы в `func`. Также можно указать аргументы, тогда и они будут фиксированы, но об этом чуть позже.
Пример со встроенным методом `bind`:
@@ -166,13 +219,14 @@ var user = {
*!*
// setTimeout( bind(user.sayHi, user), 1000 );
-
setTimeout( user.sayHi.bind(user), 1000 ); // аналог через встроенный метод
*/!*
```
Получили простой и надёжный способ привязать контекст, причём даже встроенный в JavaScript.
+Далее мы будем использовать именно встроенный метод `bind`.
+
[smart header="Привязать всё: `bindAll`"]
Если у объекта много методов и мы планируем их активно передавать, то можно привязать контекст для них всех в цикле:
@@ -236,8 +290,9 @@ alert( triple(5) ); // = mul(3, 5) = 15
При помощи `bind` мы можем получить из функции её "частный вариант" как самостоятельную функцию и дальше передать в `setTimeout` или сделать с ней что-то ещё.
+Наш выигрыш в этом состоит в том, что эта самостоятельная функция, во-первых, имеет понятное имя (`double`, `triple`), а во-вторых, повторные вызовы позволяют не указывать каждый раз первый аргумент, он уже фиксирован благодаря `bind`.
-## Функция дла задач
+## Функция ask для задач
В задачах этого раздела предполагается, что объявлена следующая "функция вопросов" `ask`:
@@ -251,7 +306,7 @@ function ask(question, answer, ok, fail) {
Её назначение -- задать вопрос `question` и, если ответ совпадёт с `answer`, то запустить функцию `ok()`, а иначе -- функцию `fail()`.
-В реальном проекте она будет сложнее, вместо `alert/prompt` -- вывод красивого JavaScript-диалога с рамочками, кнопочками и так далее, но это нам сейчас не нужно.
+Несмотря на внешнюю простоту, функции такого вида активно используются в реальных проектах. Конечно, они будут сложнее, вместо `alert/prompt` -- вывод красивого JavaScript-диалога с рамочками, кнопочками и так далее, но это нам сейчас не нужно.
Пример использования:
@@ -272,9 +327,24 @@ function die() {
## Итого
-Функции и контекст к JavaScript -- как шнурки и кроссовки. Если мы куда-то отправляем шнурки (например в `setTimeout`), то кроссовки сами за ними не побегут.
+
+- Функция сама по себе не запоминает контекст выполнения.
+- Чтобы гарантировать правильный контекст для вызова `obj.func()`, нужно использовать функцию-обёртку, задать её через анонимную функцию:
+```js
+setTimeout(function() {
+ obj.func();
+})
+```
+
+- ...Либо использовать `bind`:
-Нужно либо передать их дополнительно, либо привязать одно к другому вызовом `bind`, либо завернуть в замыкание.
+```js
+setTimeout( obj.func.bind(obj) );
+```
+
+- Вызов `bind` часто используют для привязки функции к контексту, чтобы затем присвоить её в обычную переменную и вызывать уже без явного указания объекта.
+- Вызов `bind` также позволяет фиксировать первые аргументы функции ("каррировать" её), и таким образом из общей функции получить её "частные" варианты -- чтобы использовать их многократно без повтора одних и тех же аргументов каждый раз.
+
[head]
```
diff --git a/2-ui/1-document/7-basic-dom-node-properties/article.md b/2-ui/1-document/7-basic-dom-node-properties/article.md
index 11dc99a5..c680fd0f 100644
--- a/2-ui/1-document/7-basic-dom-node-properties/article.md
+++ b/2-ui/1-document/7-basic-dom-node-properties/article.md
@@ -407,7 +407,7 @@ chatDiv.innerHTML += "Как дела?";
**Иными словами, `elem.textContent` возвращает конкатенацию всех текстовых узлов внутри `elem`.**
-Не сказать, чтобы эта информация была часто востребована.
+Не сказать, чтобы эта информация была часто востребована.
**Гораздо полезнее возможность записать текст в элемент, причём именно как текст!**
diff --git a/2-ui/1-document/9-attributes-and-custom-properties/article.md b/2-ui/1-document/9-attributes-and-custom-properties/article.md
index f97879ff..62900197 100644
--- a/2-ui/1-document/9-attributes-and-custom-properties/article.md
+++ b/2-ui/1-document/9-attributes-and-custom-properties/article.md
@@ -372,7 +372,7 @@ div.classList.add('order-state-canceled');
Проще говоря, значение атрибута -- произвольная строка, значение класса -- это "есть" или "нет", поэтому естественно, что атрибуты "мощнее" и бывают удобнее классов как в JS так и в CSS.
-## Свойство dataSet, data-атрибуты
+## Свойство dataset, data-атрибуты
С помощью нестандартных атрибутов можно привязать к элементу данные, которые будут доступны в JavaScript.
diff --git a/2-ui/2-events-and-interfaces/2-events-and-timing-depth/article.md b/2-ui/2-events-and-interfaces/2-events-and-timing-depth/article.md
index 7d9265ac..5c719796 100644
--- a/2-ui/2-events-and-interfaces/2-events-and-timing-depth/article.md
+++ b/2-ui/2-events-and-interfaces/2-events-and-timing-depth/article.md
@@ -1,15 +1,15 @@
# Порядок обработки событий
-События могут возникать не только по очереди, но и пачкой, по многу сразу. Возможно и такое, что во время обработки одного события возникают другие.
+События могут возникать не только по очереди, но и "пачкой" по много сразу. Возможно и такое, что во время обработки одного события возникают другие, например пока выполнялся код для `onclick` -- посетитель провёл мышкой, а это уже `mousemove`.
-Здесь и далее, очень важно понимать, как браузер обычно работает с событиями и важные исключения из этого правила. Это мы и разберём в этой главе.
+Здесь мы разберём, как браузер обычно работает с одновременно возникающими событиями и какие есть исключения из общего правила.
[cut]
## Главный поток
В каждом окне выполняется только один *главный* поток, который занимается выполнением JavaScript, отрисовкой и работой с DOM.
-Он выполняет команды последовательно и блокируется при выводе модальных окон, таких как `alert`.
+Он выполняет команды последовательно, может делать только одно дело одновременно и блокируется при выводе модальных окон, таких как `alert`.
[smart header="Дополнительные потоки тоже есть"]
@@ -21,9 +21,9 @@
[smart header="Web Workers"]
Существует спецификация Web Workers, которая позволяет запускать дополнительные JavaScript-процессы(workers).
-Они могут обмениваться сообщениями с главным процессом, но их переменные полностью независимы.
+Они могут обмениваться сообщениями с главным процессом, но у них свои переменные, и работают они также сами по себе.
-В частности, дополнительные процессы не имеют доступа к DOM, поэтому они полезны, преимущественно, при вычислениях, чтобы загрузить несколько ядер/процессоров одновременно.
+Такие дополнительные процессы не имеют доступа к DOM, поэтому они полезны, преимущественно, при вычислениях, чтобы загрузить несколько ядер/процессоров одновременно.
[/smart]
## Очередь событий
@@ -36,18 +36,17 @@
**Когда происходит событие, оно попадает в очередь.**
-Внутри браузера существует главный внутренний цикл, который проверяет очередь и обрабатывает события, запускает соответствующие обработчики и т.п.
+Внутри браузера непрерывно работает "главный внутренний цикл", который следит за состоянием очереди и обрабатывает события, запускает соответствующие обработчики и т.п.
**Иногда события добавляются в очередь сразу пачкой.**
Например, при клике на элементе генерируется несколько событий:
- Сначала `mousedown` -- нажата кнопка мыши.
-- Затем `mouseup` -- кнопка мыши отпущена.
-- Так как это было над одним элементом, то дополнительно генерируется `click`
+- Затем `mouseup` -- кнопка мыши отпущена и, так как это было над одним элементом, то дополнительно генерируется `click` (два события сразу).
-
+[online]
В действии:
```html
@@ -61,14 +60,17 @@
area.onclick = function(e) { this.value += "click\n"; this.scrollTop = 1e9; };
```
+[/online]
-Таким образом, при нажатии кнопки мыши в очередь попадёт событие `mousedown`, а при отпускании -- сразу два события: `mouseup` и `click`. Браузер сначала обработает первое, а потом -- второе.
+Таким образом, при нажатии кнопки мыши в очередь попадёт событие `mousedown`, а при отпускании -- сразу два события: `mouseup` и `click`. Браузер обработает их строго одно за другим: `mousedown` -> `mouseup` -> `click`.
-**При этом каждое событие из очереди обрабатывается полностью отдельно от других.**
+При этом каждое событие из очереди обрабатывается полностью отдельно от других.
## Вложенные (синхронные) события
-В тех случаях, когда событие инициируется не посетителем, а кодом, то оно, как правило, обрабатывается синхронно, то есть прямо сейчас.
+Обычно возникающие события "становятся в очередь".
+
+Но в тех случаях, когда событие инициируется не посетителем, а кодом, то оно, как правило, обрабатывается синхронно, то есть прямо сейчас.
Рассмотрим в качестве примера событие `onfocus`.
@@ -96,9 +98,9 @@
```
-В главе [](/focus-blur) мы познакомимся с этим событием подробнее, а пока -- нажмите на кнопку в примере ниже. При этом обработчик `onclick` вызовет метод `focus()` на текстовом поле `text`.
+В главе [](/focus-blur) мы познакомимся с этим событием подробнее, а пока -- нажмите на кнопку в примере ниже.
-**Событие `onfocus`, инициированное вызовом `text.focus()`, будет обработано синхронно, прямо сейчас, до завершения `onclick`.**
+При этом обработчик `onclick` вызовет метод `focus()` на текстовом поле `text`. Код обработчика `onfocus`, который при этом запустится, сработает синхронно, прямо сейчас, до завершения `onclick`.
```html
@@ -121,12 +123,13 @@
```
-При клике на кнопке в примере выше будет видно, что управление вошло в `onclick`, затем перешло в `onfocus`, затем вышло из `onclick`.
+При клике на кнопке в примере выше будет видно, что управление вошло в `onclick`, затем перешло в `onfocus`, затем вышло из `onclick`.
-**Так ведут себя все браузеры, кроме IE.**
+[warn header="Исключение в IE"]
+Так ведут себя все браузеры, кроме IE.
В нём событие `onfocus` -- всегда асинхронное, так что будет сначала полностью обработан клик, а потом -- фокус. В остальных -- фокус вызовется посередине клика. Попробуйте кликнуть в IE и в другом браузере, чтобы увидеть разницу.
-
+[/warn]
## Делаем события асинхронными через setTimeout(...,0)
@@ -134,7 +137,7 @@
Можно добиться и этого.
-Один вариант -- просто переместить строку `text.focus()` вниз кода обработчика.
+Один вариант -- просто переместить строку `text.focus()` вниз кода обработчика `onclick`.
Если это неудобно, можно запланировать `text.focus()` чуть позже через `setTimeout(..., 0)`, вот так
@@ -173,3 +176,6 @@
Синхронными являются вложенные события, инициированные из кода.
Чтобы сделать событие гарантированно асинхронным, используется вызов через `setTimeout(func, 0)`.
+
+Отложенный вызов через `setTimeout(func, 0)` используется не только в событиях, а вообще -- всегда, когда мы хотим, чтобы некая функция `func` сработала после того, как текущий скрипт завершится.
+
diff --git a/2-ui/2-events-and-interfaces/3-obtaining-event-object/1-move-ball-field/task.md b/2-ui/2-events-and-interfaces/3-obtaining-event-object/1-move-ball-field/task.md
index 6387e34f..aac26089 100644
--- a/2-ui/2-events-and-interfaces/3-obtaining-event-object/1-move-ball-field/task.md
+++ b/2-ui/2-events-and-interfaces/3-obtaining-event-object/1-move-ball-field/task.md
@@ -11,7 +11,7 @@
Мяч после перелёта должен становиться центром ровно под курсор мыши, если это возможно без вылета за край поля.
CSS-анимация не обязательна, но желательна.
Мяч должен останавливаться у границ поля, ни в коем случае не вылетать за них.
-При прокрутке страницы ничего не должно ломаться.
+При прокрутке страницы с полем ничего не должно ломаться.
Замечания:
diff --git a/2-ui/2-events-and-interfaces/3-obtaining-event-object/article.md b/2-ui/2-events-and-interfaces/3-obtaining-event-object/article.md
index b7ee8645..8e8f472f 100644
--- a/2-ui/2-events-and-interfaces/3-obtaining-event-object/article.md
+++ b/2-ui/2-events-and-interfaces/3-obtaining-event-object/article.md
@@ -2,12 +2,12 @@
Чтобы хорошо обработать событие, недостаточно знать о том, что это -- "клик" или "нажатие клавиши". Могут понадобиться детали: координаты курсора, введённый символ и другие, в зависимости от события.
-**Детали произошедшего браузер записывает в "объект события", который передаётся первым аргументом в обработчик.**
+Детали произошедшего браузер записывает в "объект события", который передаётся первым аргументом в обработчик.
[cut]
-## Получение объекта события
+## Свойства объекта события
-Пример ниже демонстрирует использования объекта события:
+Пример ниже демонстрирует использование объекта события:
```html
@@ -15,6 +15,7 @@