Skip to content

Commit a0d751f

Browse files
committed
fixes
1 parent c4c3017 commit a0d751f

File tree

7 files changed

+95
-128
lines changed

7 files changed

+95
-128
lines changed

1-js/05-data-types/09-keys-values-entries/article.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ for (let value of Object.values(user)) {
8282
Например, у нас есть объект с ценами, и мы хотели бы их удвоить:
8383

8484
```js run
85-
let users = {
85+
let prices = {
8686
banana: 1,
8787
orange: 2,
8888
meat: 4,

1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/task.md

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,24 +10,23 @@ importance: 5
1010

1111
Давайте рассмотрим реальное приложение, чтобы лучше понять это требование и выяснить, откуда оно взято.
1212

13-
**Например, мы хотим отслеживать движения указателя.**
13+
**Например, мы хотим отслеживать движения мыши.**
1414

1515

1616
В браузере мы можем объявить функцию, которая будет запускаться при каждом движении указателя и получать его местоположение. Во время активного использования мыши эта функция запускается очень часто, это может происходить около 100 раз в секунду (каждые 10 мс).
1717

18-
**Отслеживающая функция должна обновлять некоторую информацию на веб-странице.**
18+
**Мы бы хотели обновлять информацию на странице при передвижениях.**
1919

20-
Функция обновления `update()` слишком ресурсоёмкая, чтобы делать это при каждом микродвижении. Также нет смысла делать это чаще, чем один раз в 100 мс.
20+
...Но функция обновления `update()` слишком ресурсоёмкая, чтобы делать это при каждом микродвижении. Да и нет смысла делать обновление чаще, чем один раз в 100 мс.
2121

22-
23-
Поэтому мы обернём вызов в декоратор: будем использовать throttle(update, 100) как функцию, которая будет запускаться при каждом перемещении указателя вместо оригинальной update(). Декоратор будет вызываться часто, но `update()` будет вызываться максимум раз в 100 мс.
22+
Поэтому мы обернём вызов в декоратор: будем использовать `throttle(update, 100)` как функцию, которая будет запускаться при каждом перемещении указателя вместо оригинальной `update()`. Декоратор будет вызываться часто, но передавать вызов в `update()` максимум раз в 100 мс.
2423

2524
Визуально это будет выглядеть вот так:
2625

27-
1. Для первого движения указателя декорированный вариант передаёт вызов в `update`. Это важно, т.к. пользователь сразу видит нашу реакцию на его перемещение.
26+
1. Для первого движения указателя декорированный вариант сразу передаёт вызов в `update`. Это важно, т.к. пользователь сразу видит нашу реакцию на его перемещение.
2827
2. Затем, когда указатель продолжает движение, в течение 100 мс ничего не происходит. Декорированный вариант игнорирует вызовы.
29-
3. По истечению 100 мс происходит ещё один вызов `update` с последними координатами.
30-
4. Затем, наконец, указатель где-то останавливается. Декорированный вариант ждёт, пока не истечёт 100 мс и затем вызывает `update` с последними координатами. Так что, пожалуй, самое главное то, что окончательные координаты указателя обработаны.
28+
3. По истечению 100 мс происходит ещё один вызов `update` с последними координатами.
29+
4. Затем, наконец, указатель где-то останавливается. Декорированный вариант ждёт, пока не истечёт 100 мс и затем вызывает `update` с последними координатами. Так что, что весьма важно, окончательные координаты указателя обработаны.
3130

3231
Пример кода:
3332

1-js/06-advanced-functions/09-call-apply-decorators/article.md

Lines changed: 45 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -238,84 +238,13 @@ worker.slow = cachingDecorator(worker.slow);
238238

239239
1. Реализовать новую (или использовать стороннюю) структуру данных для коллекции, которая которая более универсальна чем встроенный `Map`, и поддерживает множественные ключи.
240240
2. Использовать вложенные коллекции: `cache.set(min)` будет `Map` которая хранит пару `(max, result)`. Тогда получить `result` мы можем вызвав `cache.get(min).get(max)`.
241-
3. Соединить два значения в одно. В нашем конкретном случае мы можем просто использовать строку `"min,max"` как ключ к `Map`. Для гибкости, мы можем позволить передавать *функцию кеширования* в декоратор, которая знает, как сделать одно значение из многих.
242-
241+
3. Соединить два значения в одно. В нашем конкретном случае мы можем просто использовать строку `"min,max"` как ключ к `Map`. Для гибкости, мы можем позволить передавать *хеширующую функцию* в декоратор, которая знает, как сделать одно значение из многих.
243242

244243
Для многих практических применений третий вариант достаточно хорош, поэтому мы будем придерживаться его.
245244

246-
Вторая задача, которую нужно решить, - как передать множество аргументов в `func`? Пока что обёртка `function (x)` принимает один аргумент, и `func.call (this, x)` передаёт его.
247-
248-
Здесь мы можем использовать другой встроенный метод [func.apply](mdn:js/Function/apply).
249-
250-
Синтаксис:
251-
252-
```js
253-
func.apply(context, args)
254-
```
255-
256-
Он выполняет `func`, устанавливая`this=context`, и принимая в качестве списка аргументов объект-псевдомассив `args`.
257-
258-
Например, эти два вызова почти одинаковые:
259-
260-
```js
261-
func(1, 2, 3);
262-
func.apply(context, [1, 2, 3])
263-
```
264-
265-
Оба запускают `func`, передавая аргументы `1,2,3`. Но `apply` также устанавливает `this=context`.
266-
267-
Например, здесь `say` вызывается с `this=user` и `messageData` в качестве списка аргументов:
268-
269-
```js run
270-
function say(time, phrase) {
271-
alert(`[${time}] ${this.name}: ${phrase}`);
272-
}
273-
274-
let user = { name: "John" };
275-
276-
let messageData = ['10:00', 'Hello']; // становится time и phrase
277-
278-
*!*
279-
// this принимает значение user, messageData передаётся как список аргументов (time, phrase)
280-
say.apply(user, messageData); // [10:00] John: Hello (this=user)
281-
*/!*
282-
```
283-
284-
Единственная разница в синтаксисе между `call` и `apply` состоит в том, что `call` ожидает список аргументов, в то время как `apply` принимает псевдомассив.
285-
Мы уже знаем оператор расширения `...` из главы <info: rest-parameters-spread-operator>, который может передавать массив (или любой перебираемый объект) в виде списка аргументов. Поэтому, если мы используем его с `call`, мы можем достичь почти того же, что и `apply`.
286-
Эти два вызова почти эквивалентны:
287-
288-
```js
289-
let args = [1, 2, 3];
290-
291-
*!*
292-
func.call(context, ...args); // передаёт массив как список с оператором расширения
293-
func.apply(context, args); // тот же эффект
294-
*/!*
295-
```
296-
297-
Если мы посмотрим более внимательно, то между такими использованиями `call` и` apply` есть небольшая разница.
298-
299-
- Оператор расширения `...` позволяет передавать *перебираемый* объект `args` в виде списка в `call`.
300-
- В свою очередь `Apply` принимает только *псевдомассив* `args`.
301-
302-
Итак, эти вызовы дополняют друг друга. Там, где мы ожидаем итеративность, работает `call`, где мы ожидаем псевдомассив, работает `apply`.
303-
304-
И если `args` является перебираемый и похожим на реальный массив, то технически мы могли бы использовать любой из них, но `apply`, вероятно, будет быстрее, потому что это одна операция. Большинство движков JavaScript внутренне оптимизируют его лучше, чем пара `call + spread`.
305-
306-
Одним из наиболее важных применений `apply` является передача вызова другой функции, например так:
307-
308-
```js
309-
let wrapper = function() {
310-
return anotherFunction.apply(this, arguments);
311-
};
312-
```
313-
314-
Это называется *переадресация вызова*. Обёртка передаёт все, что получает: контекст `this` и аргументы для `anotherFunction` и возвращает её результат.
245+
Также нам понадобится заменить `func.call(this, x)` на `func.call(this, ...arguments)`, чтобы передавать все аргументы обёрнутой функции, а не только первый.
315246

316-
Когда внешний код вызывает такую 'обёртку', он неотличим от вызова исходной функции.
317-
318-
Теперь давайте заправим все это в более мощный `cachingDecorator`:
247+
Вот более мощный `cachingDecorator`:
319248

320249
```js run
321250
let worker = {
@@ -354,13 +283,52 @@ alert( worker.slow(3, 5) ); // работает
354283
alert( "Again " + worker.slow(3, 5) ); // аналогично (из кеша)
355284
```
356285

357-
Теперь обёртка оперирует любым количеством аргументов.
286+
Теперь он работает с любым количеством аргументов.
358287

359288
Есть два изменения:
360289

361290
- В строке `(*)` вызываем `hash` для создания одного ключа из `arguments`. Здесь мы используем простую функцию "объединения", которая превращает аргументы `(3, 5)` в ключ `" 3,5 "`. В более сложных случаях могут потребоваться другие функции хеширования.
362-
- Затем `(**)` используем `func.apply` для передачи как контекста, так и всех аргументов, полученных обёрткой (независимо от их количества), в исходную функцию.
291+
- Затем `(**)` используем `func.call(this, ...arguments)` для передачи как контекста, так и всех аргументов, полученных обёрткой (независимо от их количества), в исходную функцию.
292+
293+
Вместо `func.call(this, ...arguments)` мы могли бы написать `func.apply(this, arguments)`.
294+
295+
Синтаксис встроенного метода [func.apply](mdn:js/Function/apply):
296+
297+
```js
298+
func.apply(context, args)
299+
```
300+
301+
Он выполняет `func`, устанавливая `this=context` и принимая в качестве списка аргументов псевдомассив `args`.
302+
303+
Единственная разница в синтаксисе между `call` и `apply` состоит в том, что `call` ожидает список аргументов, в то время как `apply` принимает псевдомассив.
304+
305+
Эти два вызова почти эквивалентны:
306+
307+
```js
308+
func.call(context, ...args); // передаёт массив как список с оператором расширения
309+
func.apply(context, args); // тот же эффект
310+
```
311+
312+
Есть только одна небольшая разница:
313+
314+
- Оператор расширения `...` позволяет передавать *перебираемый* объект `args` в виде списка в `call`.
315+
- А `apply` принимает только *псевдомассив* `args`.
316+
317+
Так что эти вызовы дополняют друг друга. Для перебираемых объектов сработает `call`, а где мы ожидаем псевдомассив - `apply`.
318+
319+
А если у нас объект, который и то и другое, например, реальный массив, то технически мы можем бы использовать любой, но `apply`, вероятно, будет быстрее, потому что большинство движков JavaScript внутренне оптимизируют его лучше.
320+
321+
Передача всех аргументов вместе с контекстом другой функции называется "перенаправлением вызова" (call forwarding).
322+
323+
Простейший вид такого перенаправления:
324+
325+
```js
326+
let wrapper = function() {
327+
return func.apply(this, arguments);
328+
};
329+
```
363330

331+
При вызове `wrapper` из внешнего кода его не отличить от вызова исходной функции.
364332

365333
## Заимствование метода [#method-borrowing]
366334

@@ -414,7 +382,7 @@ hash(1, 2);
414382

415383
Почему это работает?
416384

417-
Это связано с тем, что внутренний алгоритм встроенного метода arr.join(glue) очень прост.
385+
Это связано с тем, что внутренний алгоритм встроенного метода `arr.join(glue)` очень прост.
418386
Взято из спецификации практически "как есть":
419387

420388
1. Пускай первым аргументом будет `glue` или, в случае отсутствия аргументов, им будет запятая `","`

1-js/06-advanced-functions/10-bind/article.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@ libs:
55

66
# Привязка контекста к функции
77

8-
При использовании `setTimeout` с методами объекта (или при передаче методов объекта) возникает известная проблема: "потеря `this`".
8+
При передаче методов объекта в качестве колбэков, например для `setTimeout`, возникает известная проблема - потеря `this`.
99

10-
Внезапно, `this` просто перестает работать правильно. Такая ситуация типична для новичков, но также случается и с опытными разработчиками.
10+
В этой главе мы посмотрим, как можно её решать.
1111

1212
## Потеря "this"
1313

14-
Мы уже знаем, что в JavaScript легко потерять `this`: когда метод передается где-то отдельно от объекта - `this` теряется.
14+
Мы уже видели примеры потери `this`. Как только метод передается отдельно от объекта - `this` теряется.
1515

1616
Вот как это может произойти с `setTimeout`:
1717

@@ -37,7 +37,7 @@ let f = user.sayHi;
3737
setTimeout(f, 1000); // контекст user потеряли
3838
```
3939

40-
Метод `setTimeout` в браузере имеет особенность: он устанавливает `this=window` для вызова функции (в Node.js `this` становится объектом таймера, но здесь это не имеет значения). Таким образом, для `this.firstName` он пытается получить `window.firstName`, которого не существует. В других подобных случаях, как мы увидим, обычно `this` просто становится `undefined`.
40+
Метод `setTimeout` в браузере имеет особенность: он устанавливает `this=window` для вызова функции (в Node.js `this` становится объектом таймера, но здесь это не имеет значения). Таким образом, для `this.firstName` он пытается получить `window.firstName`, которого не существует. В других подобных случаях обычно `this` просто становится `undefined`.
4141

4242
Задача довольно типичная - мы хотим передать метод объекта куда-то ещё (в этом конкретном случае - в планировщик), где он будет вызван. Как бы сделать так, чтобы он вызывался в правильном контексте?
4343

0 commit comments

Comments
 (0)