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
Ошибка возникает в строке `(*)`. Функция пытается получить доступ к `this.someMethod` и завершается с ошибкой. Видите почему?
112
112
113
-
Причина в том, что в строке `(**)` декоратор вызывает оригинальную функцию как `func(x)`, а в этом случае функция получает `this = undefined`.
113
+
Причина в том, что в строке `(**)` декоратор вызывает оригинальную функцию как `func(x)`, а в этом случае функция получает `this=undefined`.
114
114
115
115
Мы бы наблюдали похожий симптом, если бы попытались запустить:
116
116
@@ -142,7 +142,7 @@ func.call(obj, 1, 2, 3)
142
142
143
143
Они оба вызывают `func` с аргументами `1`, `2` и `3`. Единственное отличие состоит в том, что `func.call` также устанавливает `this` в `obj`.
144
144
145
-
Например, в приведенном ниже коде мы вызываем `sayHi` в контексте различных объектов: `sayHi.call(user)` запускает `sayHi`, передавая `this = user`, а следующая строка устанавливает `this = admin`:
145
+
Например, в приведенном ниже коде мы вызываем `sayHi` в контексте различных объектов: `sayHi.call(user)` запускает `sayHi`, передавая `this=user`, а следующая строка устанавливает `this=admin`:
146
146
147
147
```js run
148
148
functionsayHi() {
@@ -153,8 +153,8 @@ let user = { name: "John" };
153
153
let admin = { name:"Admin" };
154
154
155
155
// используем 'call' для передачи различных объектов как 'this'
156
-
sayHi.call( user ); // this = John
157
-
sayHi.call( admin ); // this = Admin
156
+
sayHi.call( user ); // this=John
157
+
sayHi.call( admin ); // this=Admin
158
158
```
159
159
160
160
Здесь мы используем `call` для вызова `say` с заданным контекстом и аргументом 'phrase':
@@ -204,22 +204,22 @@ function cachingDecorator(func) {
204
204
worker.slow=cachingDecorator(worker.slow); // теперь сделаем её кеширующей
205
205
206
206
alert( worker.slow(2) ); // работает
207
-
alert( worker.slow(2) ); // работает, не вызывая первоначальную функцию (кэшируется)
207
+
alert( worker.slow(2) ); // работает, не вызывая первоначальную функцию (кешируется)
208
208
```
209
209
210
210
Теперь всё в порядке.
211
211
212
212
Чтобы всё было понятно, давайте посмотрим глубже, как передается `this`:
213
213
214
214
1. После *декорации*`worker.slow` становится обёрткой `function (x) { ... }`.
215
-
2. Так что при выполнении `worker.slow(2)` обёртка получает `2` в качестве аргумента, и `this = worker` (это объект перед точкой).
215
+
2. Так что при выполнении `worker.slow(2)` обёртка получает `2` в качестве аргумента, и `this=worker` (это объект перед точкой).
216
216
3. Внутри обёртки, если результат еще не кэширован, `func.call(this, x)` передает текущий `this` (`= worker`) и текущий аргумент (`= 2`) в оригинальную функцию.
217
217
218
218
## Использование нескольких аргументов с "func.apply"
219
219
220
220
Теперь давайте сделаем `cachingDecorator` еще более универсальным. До сих пор он работал только с функциями с одним аргументом.
221
221
222
-
Как же кэшировать метод с несколькими аргументами `worker.slow`?
222
+
Как же кешировать метод с несколькими аргументами `worker.slow`?
Во-первых, - как использовать оба аргумента `min` и `max` для ключа в коллекции `cache`? Ранее для одного аргумента `x` мы могли просто сохранить результат `cache.set(x, result)` и вызвать `cache.get(x)` чтобы получить его. Но теперь нам нужно запомнить результат для *комбинации аргументов*`(min,max)`. Нативный `Map` принимает только одно значение как ключ.
237
+
Во-первых, - как использовать оба аргумента `min` и `max` для ключа в коллекции `cache`? Ранее для одного аргумента `x` мы могли просто сохранить результат `cache.set(x, result)` и вызвать `cache.get(x)` чтобы получить его. Но теперь нам нужно запомнить результат для *комбинации аргументов*`(min,max)`. Нативный `Map` принимает только одно значение как ключ.
@@ -301,7 +301,7 @@ func.apply(context, args); // тот же эффект
301
301
- Оператор расширения `...` позволяет передавать *итеративный* объект `args` в виде списка в `call`.
302
302
- В свою очередь `Apply` принимает только *массивоподобный* объект `args`.
303
303
304
-
Итак, эти вызовы дополняют друг друга. Там, где мы ожидаем итеративность, работает `call`, где мы ожидаем массивоподобный объект, работает`apply`.
304
+
Итак, эти вызовы дополняют друг друга. Там, где мы ожидаем итеративность, работает `call`, где мы ожидаем массивоподобный объект, работает`apply`.
305
305
306
306
И если `args` является итеративным и похожим на реальный массив, то технически мы могли бы использовать любой из них, но `apply`, вероятно, будет быстрее, потому что это одна операция. Большинство движков JavaScript внутренне оптимизируют его лучше, чем пара `call + spread`.
- В строке `(*)` вызываем `hash` для создания одного ключа из `arguments`. Здесь мы используем простую функцию "объединения", которая превращает аргументы `(3, 5)` в ключ `" 3,5 "`. В более сложных случаях могут потребоваться другие функции хеширования
363
+
- В строке `(*)` вызываем `hash` для создания одного ключа из `arguments`. Здесь мы используем простую функцию "объединения", которая превращает аргументы `(3, 5)` в ключ `" 3,5 "`. В более сложных случаях могут потребоваться другие функции хеширования.
364
364
- Затем `(**)` используем `func.apply` для передачи как контекста, так и всех аргументов, полученных обёрткой (независимо от их количества), в исходную функцию.
365
365
366
366
@@ -427,9 +427,9 @@ hash(1, 2);
427
427
6. ...выполнять до тех пор, пока `this.length` элементов не будет склеено.
428
428
7. Вернуть `result`.
429
429
430
-
Таким образом, технически он принимает `this` и объединяет `this[0]`, `this[1]`... и т.д. вместе. Он намеренно написан так, что допускает любое подобное массиву `this` (не случайно, многие методы следуют этой практике). Вот почему он также работает с `this = arguments`.
430
+
Таким образом, технически он принимает `this` и объединяет `this[0]`, `this[1]`... и т.д. вместе. Он намеренно написан так, что допускает любое подобное массиву `this` (не случайно, многие методы следуют этой практике). Вот почему он также работает с `this=arguments`.
431
431
432
-
## Резюме
432
+
## Итого
433
433
434
434
*Декоратор* - это обертка вокруг функции, которая изменяет поведение последней. Основная работа по-прежнему выполняется функцией.
435
435
@@ -450,7 +450,7 @@ let wrapper = function(original, arguments) {
450
450
}
451
451
```
452
452
453
-
Мы также рассмотрели пример *заимствования метода*, когда мы вызываем метод у объекта в контексте другого объекта. Весьма распространено заимствовать методы массива и применять их к аргументам. Альтернативным способом является использование объекта рест-параметров`...args`, который является реальным массивом.
453
+
Мы также рассмотрели пример *заимствования метода*, когда мы вызываем метод у объекта в контексте другого объекта. Весьма распространено заимствовать методы массива и применять их к аргументам. В качестве альтернативы можно использовать объект с остаточными параметрами`...args`, который является реальным массивом.
454
454
455
455
456
456
В реальной практике очень много декораторов. Проверьте, насколько хорошо вы их освоили, решая задачи этой главы.
0 commit comments