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
As we can see, the output shows not "John" as `this.firstName`, but`undefined`!
31
+
При запуске этого кода мы видим, что вызов `this.firstName` возвращает не "Вася", а`undefined`!
32
32
33
-
That's because `setTimeout`got the function `user.sayHi`, separately from the object. The last line can be rewritten as:
33
+
Это произошло потому, что `setTimeout`получил функцию `sayHi`, отдельно от объекта `user` (именно здесь функция и потеряла контекст). То есть последняя строка может быть переписана как:
34
34
35
35
```js
36
36
let f =user.sayHi;
37
-
setTimeout(f, 1000); //lost user context
37
+
setTimeout(f, 1000); //контекст user потеряли
38
38
```
39
39
40
-
The method `setTimeout`in-browser is a little special: it sets`this=window`for the function call (for Node.js,`this`becomes the timer object, but doesn't really matter here). So for `this.firstName`it tries to get `window.firstName`, which does not exist. In other similar cases as we'll see, usually`this`just becomes`undefined`.
40
+
Метод `setTimeout`в браузере имеет особенность: он устанавливает`this=window`для вызова функции (для Node.js `this`становится объектом таймера, но здесь это не имеет значения). Таким образом, для `this.firstName`он пытается получить `window.firstName`, которого не существует. В других подобных случаях, как мы увидим, обычно`this`просто становится`undefined`.
41
41
42
-
The task is quite typical -- we want to pass an object method somewhere else (here -- to the scheduler) where it will be called. How to make sure that it will be called in the right context?
42
+
Задача довольно типичная - мы хотим передать метод объекта куда-то ещё (в этом конкретном случае - в планировщик), где он будет вызван. Как бы сделать так, чтобы он вызывался в правильном контексте?
43
43
44
-
## Solution 1: a wrapper
44
+
## Решение 1: сделать функцию-обёртку
45
45
46
-
The simplest solution is to use a wrapping function:
46
+
Самый простой вариант решения – это обернуть вызов в анонимную функцию, создав замыкание:
47
47
48
48
```js run
49
49
let user = {
50
-
firstName:"John",
50
+
firstName:"Вася",
51
51
sayHi() {
52
-
alert(`Hello, ${this.firstName}!`);
52
+
alert(`Привет, ${this.firstName}!`);
53
53
}
54
54
};
55
55
56
56
*!*
57
57
setTimeout(function() {
58
-
user.sayHi(); //Hello, John!
58
+
user.sayHi(); //Привет, Вася!
59
59
}, 1000);
60
60
*/!*
61
61
```
62
62
63
-
Now it works, because it receives`user`from the outer lexical environment, and then calls the method normally.
63
+
Теперь код работает корректно, так как объект`user`достаётся из замыкания, а затем вызывается его метод `sayHi`.
Looks fine, but a slight vulnerability appears in our code structure.
72
-
73
-
What if before `setTimeout` triggers (there's one second delay!) `user` changes value? Then, suddenly, it will call the wrong object!
71
+
Выглядит хорошо, но теперь в нашем коде появилась небольшая уязвимость.
74
72
73
+
Что произойдёт, если до момента срабатывания `setTimeout` (ведь задержка составляет целую секунду!) в переменную `user` будет записано другое значение? Тогда вызов неожиданно будет совсем не тот!
75
74
76
75
```js run
77
76
let user = {
78
-
firstName:"John",
77
+
firstName:"Вася",
79
78
sayHi() {
80
-
alert(`Hello, ${this.firstName}!`);
79
+
alert(`Привет, ${this.firstName}!`);
81
80
}
82
81
};
83
82
84
83
setTimeout(() =>user.sayHi(), 1000);
85
84
86
-
// ...within 1 second
87
-
user = { sayHi() { alert("Another user in setTimeout!"); } };
85
+
// ...в течение 1 секунды
86
+
user = { sayHi() { alert("Другой пользователь в 'setTimeout'!"); } };
88
87
89
-
//Another user in setTimeout?!?
88
+
//Другой пользователь в 'setTimeout'!
90
89
```
91
90
92
-
The next solution guarantees that such thing won't happen.
91
+
Следующее решение гарантирует, что такого не случится.
93
92
94
-
## Solution 2: bind
93
+
## Решение 2: привязать контекст с помощью bind
95
94
96
-
Functions provide a built-in method [bind](mdn:js/Function/bind) that allows to fix`this`.
95
+
В современном JavaScript у функций есть встроенный метод [bind](mdn:js/Function/bind), который позволяет исправить`this`.
97
96
98
-
The basic syntax is:
97
+
<!-- The basic syntax is: -->
98
+
Базовый синтаксис `bind`:
99
99
100
100
```js
101
-
//more complex syntax will be little later
101
+
//полный синтаксис будет представлен немного позже
102
102
let boundFunc =func.bind(context);
103
-
````
103
+
```
104
104
105
-
The result of`func.bind(context)`is a special function-like "exotic object", that is callable as function and transparently passes the call to `func` setting `this=context`.
105
+
Результатом вызова `func.bind(context)`является подобный функции специальный «экзотический объект», который вызывается как функция и прозрачно передает вызов в `func`, при этом устанавливая`this=context`.
106
106
107
-
In other words, calling `boundFunc` is like `func` with fixed `this`.
107
+
Другими словами, вызов`boundFunc`подобен вызову`func`с фиксированным`this`.
108
108
109
-
For instance, here `funcUser` passes a call to `func` with `this=user`:
109
+
Например, здесь`funcUser`передает вызов в `func`, фиксируя`this=user`:
110
110
111
111
```js run
112
112
let user = {
113
-
firstName: "John"
113
+
firstName:"Вася"
114
114
};
115
115
116
116
functionfunc() {
@@ -119,71 +119,70 @@ function func() {
119
119
120
120
*!*
121
121
let funcUser =func.bind(user);
122
-
funcUser(); //John
122
+
funcUser(); //Вася
123
123
*/!*
124
124
```
125
125
126
-
Here`func.bind(user)`as a "bound variant" of `func`, with fixed`this=user`.
126
+
Здесь`func.bind(user)`- это «связанный вариант» `func`, с фиксированным`this=user`.
127
127
128
-
All arguments are passed to the original `func` "as is", for instance:
128
+
Все аргументы передаются исходному методу `func` "как есть", например:
129
129
130
130
```js run
131
131
let user = {
132
-
firstName:"John"
132
+
firstName:"Вася"
133
133
};
134
134
135
135
functionfunc(phrase) {
136
136
alert(phrase +', '+this.firstName);
137
137
}
138
138
139
-
//bind this to user
139
+
//привязка this к user
140
140
let funcUser =func.bind(user);
141
141
142
142
*!*
143
-
funcUser("Hello"); //Hello, John (argument "Hello" is passed, and this=user)
143
+
funcUser("Привет"); //Привет, Вася (аргумент "Привет" передан, при этом this = user)
144
144
*/!*
145
145
```
146
146
147
-
Now let's try with an object method:
148
-
147
+
Теперь давайте попробуем с методом объекта:
149
148
150
149
```js run
151
150
let user = {
152
-
firstName:"John",
151
+
firstName:"Вася",
153
152
sayHi() {
154
-
alert(`Hello, ${this.firstName}!`);
153
+
alert(`Привет, ${this.firstName}!`);
155
154
}
156
155
};
157
156
158
157
*!*
159
158
let sayHi =user.sayHi.bind(user); // (*)
160
159
*/!*
161
160
162
-
sayHi(); //Hello, John!
161
+
sayHi(); //Привет, Вася!
163
162
164
-
setTimeout(sayHi, 1000); //Hello, John!
163
+
setTimeout(sayHi, 1000); //Привет, Вася!
165
164
```
166
165
167
-
In the line `(*)`we take the method `user.sayHi`and bind it to`user`. The `sayHi` is a "bound" function, that can be called alone or passed to`setTimeout` -- doesn't matter, the context will be right.
166
+
В строке `(*)`мы берем метод `user.sayHi`и привязываем его к`user`. `SayHi` - это «связанная» функция, которая может быть вызвана отдельно или передана в`setTimeout` - не имеет значения, контекст всегда будет правильным.
168
167
169
-
Here we can see that arguments are passed "as is", only `this` is fixed by `bind`:
168
+
Здесь мы можем видеть, что `bind` исправляет только `this`, а аргументы передаются «как есть»:
170
169
171
170
```js run
172
171
let user = {
173
-
firstName:"John",
172
+
firstName:"Вася",
174
173
say(phrase) {
175
174
alert(`${phrase}, ${this.firstName}!`);
176
175
}
177
176
};
178
177
179
178
let say =user.say.bind(user);
180
179
181
-
say("Hello"); //Hello, John ("Hello" argument is passed to say)
182
-
say("Bye"); //Bye, John ("Bye" is passed to say)
180
+
say("Привет"); //Привет, Вася (аргумент "Привет" передан в функцию "say")
181
+
say("Пока"); //Пока, Вася (аргумент "Пока" передан в функцию "say")
183
182
```
184
183
185
-
````smart header="Convenience method: `bindAll`"
186
-
If an object has many methods and we plan to actively pass it around, then we could bind them all in a loop:
184
+
````smart header="Удобный метод: `bindAll`"
185
+
Если у объекта много методов и мы планируем их активно передавать, то можно привязать контекст для них всех в цикле:
187
186
188
187
```js
189
188
for (let key in user) {
@@ -193,11 +192,12 @@ for (let key in user) {
193
192
}
194
193
```
195
194
196
-
JavaScript libraries also provide functions for convenient mass binding , e.g.[_.bindAll(obj)](http://lodash.com/docs#bindAll)in lodash.
195
+
Некоторые JS-библиотеки предоставляют встроенные функции для удобной массовой привязки контекста, например[_.bindAll(obj)](http://lodash.com/docs#bindAll)в lodash.
197
196
````
198
197
199
-
## Summary
198
+
<!-- ## Summary -->
199
+
## Итого
200
200
201
-
Method `func.bind(context, ...args)` returns a "bound variant" of function `func` that fixes the context `this` and first arguments if given.
201
+
Метод `func.bind(context, ...args)` возвращает «связанный вариант» функции `func`, который фиксирует контекст `this` и первые аргументы, если они заданы.
202
202
203
-
Usually we apply `bind` to fix `this` in an object method, so that we can pass it somewhere. For example, to `setTimeout`. There are more reasons to `bind` in the modern development, we'll meet them later.
203
+
Обычно мы применяем `bind`, для исправления `this` в методе объекта, чтобы мы могли передать его куда-нибудь. Например, в `setTimeout`. В современной разработке существуют и другие причины для «связывания», мы встретимся с ними позже.
0 commit comments