-
Notifications
You must be signed in to change notification settings - Fork 8k
/
index.md
390 lines (278 loc) · 22.8 KB
/
index.md
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
---
title: this
slug: Web/JavaScript/Reference/Operators/this
---
{{jsSidebar("Operators")}}
Поведение ключевого слова `this` в JavaScript несколько отличается по сравнению с остальными языками. Имеются также различия при использовании `this` в [строгом](/ru/docs/Web/JavaScript/Reference/Strict_mode) и нестрогом режиме.
В большинстве случаев значение `this` определяется тем, каким образом вызвана функция. Значение `this` не может быть установлено путём присваивания во время исполнения кода и может иметь разное значение при каждом вызове функции. В ES5 представлен метод {{jsxref('Function.prototype.bind()', 'bind()')}}, который используется для {{jsxref('Operators/this','привязки значения ключевого слова this независимо от того, как вызвана функция','Метод_bind')}}. Также в ES2015 представлены {{jsxref('Functions/Arrow_functions', 'стрелочные функции')}}, которые не создают собственные привязки к `this` (они сохраняют значение `this` лексического окружения, в котором были созданы).
{{EmbedInteractiveExample("pages/js/expressions-this.html")}}
## Синтаксис
```
this
```
### Значение
Свойство контекста выполнения кода (global, function или eval), которое в нестрогом режиме всегда является ссылкой на объект, а в строгом режиме может иметь любое значение.
## Global контекст
В глобальном контексте выполнения (за пределами каких-либо функций) `this` ссылается на глобальный объект вне зависимости от режима (строгий или нестрогий).
```js
// В браузерах, объект window также является объектом global:
console.log(this === window); // true
a = 37;
console.log(window.a); // 37
this.b = "MDN";
console.log(window.b); // "MDN"
console.log(b); // "MDN"
```
> **Примечание:** вы всегда можете легко получить объект global, используя глобальное свойство {{jsxref ("globalThis")}}, независимо от текущего контекста, в котором выполняется ваш код.
## Function контекст
В пределах функции значение `this` зависит от того, каким образом вызвана функция.
### Простой вызов
Поскольку следующий код не в {{jsxref('Strict_mode', 'строгом режиме')}}, и значение `this` не устанавливается вызовом, по умолчанию будет использоваться объект global, которым в браузере является `{{domxref('window')}}`.
```js
function f1() {
return this;
}
// В браузере:
f1() === window; // window - глобальный объект в браузере
// В Node:
f1() === global; // global - глобальный объект в Node
```
В строгом режиме, если значение `this` не установлено в контексте выполнения, оно остаётся `undefined`, как показано в следующем примере:
```js
function f2() {
"use strict"; // см. strict mode
return this;
}
f2() === undefined; // true
```
> **Примечание:** Во втором примере `this` должно иметь значение `{{jsxref("undefined")}}`, потому что функция `f2` была вызвана напрямую, а не как метод или свойство объекта (например, `window.f2()`). ��еализация этой особенности не поддерживалась в некоторых браузерах, когда они впервые начали поддерживать {{jsxref('Strict_mode', 'строгий режим')}}. В результате они некорректно возвращали объект `window`.
Для того, чтобы при вызове функции установить `this` в определённое значение, используйте {{jsxref('Function.prototype.call()', 'call()')}} или {{jsxref('Function.prototype.apply()', 'apply()')}}, как в следующих примерах.
### Пример 1
```js
// В качестве первого аргумента методов call или apply может быть передан объект,
// на который будет указывать this.
var obj = { a: "Custom" };
// Это свойство принадлежит глобальному объекту
var a = "Global";
function whatsThis() {
return this.a; //значение this зависит от контекста вызова функции
}
whatsThis(); // 'Global'
whatsThis.call(obj); // 'Custom'
whatsThis.apply(obj); // 'Custom'
```
### Пример 2
```js
function add(c, d) {
return this.a + this.b + c + d;
}
var o = { a: 1, b: 3 };
// Первый параметр - это объект для использования в качестве
// 'this', последующие параметры передаются как
// аргументы функции call
add.call(o, 5, 7); // 16
// Первый параметр - это объект для использования в качестве
// 'this', второй - массив, чьи члены используются
// в качестве аргументов функции call
add.apply(o, [10, 20]); // 34
```
Обратите внимание, что в нестрогом режиме, если значение, переданное в `call` или `apply как` `this`, не является объектом, будет сделана попытка преобразовать его в объект с помощью внутренней операции `ToObject`. Таким образом, если переданное значение является примитивом, таким как `7` или `'foo'`, оно будет преобразовано в `Object` с использованием связанного конструктора, так что примитивное число `7` будет преобразовано в объект так, как будто с помощью `new Number(7)`, а строка `'foo'` - как будто с помощью `new String('foo')`, например
```js
function bar() {
console.log(Object.prototype.toString.call(this));
}
bar.call(7); // [object Number]
bar.call("foo"); // [object String]
```
### Метод `bind()`
Вызов [`f.bind(someObject)`](/ru/docs/Web/JavaScript/Reference/Global_Objects/Function/bind) создаёт новую функцию с таким же телом и окружением, что и у `f`, но значение `this` указывает на первый аргумент `bind`, независимо от того, как вызывается функция.
```js
function f() {
return this.a;
}
const g = f.bind({ a: "qwerty" });
console.log(g()); // qwerty
const h = g.bind({ a: "yoo" }); // bind сработает только один раз!
console.log(h()); // qwerty
const o = { a: 37, f, g, h };
console.log(o.a, o.f(), o.g(), o.h()); // 37 37 qwerty qwerty
```
### `this` в стрелочных функциях
Стрелочные функции создают замыкания для значения `this` из окружающего контекста выполнения. В следующем примере мы создаём объект `obj` с методом `getThisGetter`, который возвращает функцию, которая возвращает значение `this`. Возвращаемая функция является стрелочной, поэтому её `this` связано с `this` окружающей функции. Значение `this` внутри `getThisGetter` может быть установлено при вызове, который, в свою очередь, устанавливает возвращаемое значение возвращаемой функции. Мы будем считать, что `getThisGetter` является нестрогой функцией, то есть она находится внутри нестрогого скрипта и не вложена в класс или строгую функцию.
```js
const obj = {
getThisGetter() {
const getter = () => this;
return getter;
},
};
```
Если вызвать `getThisGetter` как метод объекта `obj`, то это свяжет `this` с `obj` внутри его тела. Возвращаемая функция присвоена переменной `fn`. Теперь при вызове `fn` возвращаемое значение `this` по-прежнему задаётся вызовом `getThisGetter`, то есть `obj`. Если бы возвращаемая функция не была стрелочной, то при таких вызовах значение `this` было бы `globalThis`, поскольку `getThisGetter` не является строгой.
```js
const fn = obj.getThisGetter();
console.log(fn() === obj); // true
```
Но будьте осторожны при отвязывании метода `obj` без его вызова, потому что `getThisGetter` всё ещё метод, который имеет изменяющееся значение `this`. Вызов `fn2()()` в следующем примере возвращает `globalThis`, потому что он следует за `this` из `fn2()`, который является `globalThis`, поскольку вызывается без привязки к какому-либо объекту.
```js
const fn2 = obj.getThisGetter;
console.log(fn2()() === globalThis); // true в нестрогом режиме
```
Такое поведение очень полезно при определении обратных вызовов. Обычно каждое функциональное выражение создаёт свою собственную привязку `this`, которая перекрывает значение `this` окружающей области видимости. Если вам не важно значение `this`, вы можете определять функции как стрелочные и создавать привязки `this` только там, где это необходимо (например, в методах класса). Смотрите [пример с `setTimeout()`](/ru/docs/Web/JavaScript/Reference/Functions/Arrow_functions#больше_примеров).
### В методе объекта
Когда функция вызывается как метод объекта, используемое в этой функции ключевое слово `this` принимает значение объекта, по отношению к которому вызван метод.
В следующем примере, когда вызвано свойство `o.f()` , внутри функции `this` привязано к объекту `o.`
```js
var o = {
prop: 37,
f: function () {
return this.prop;
},
};
console.log(o.f()); // logs 37
```
Необходимо отметить, что на поведение `this` совсем не влияет то, как или где была определен�� функция. В предыдущем примере мы определили функцию внутри свойства `f` во время определения объекта `o`. Однако, мы могли бы также просто определить сначала функцию, а затем закрепить её за свойством `o.f`. В этом случае поведение `this` не изменится:
```js
var o = { prop: 37 };
function independent() {
return this.prop;
}
o.f = independent;
console.log(o.f()); // logs 37
```
Эти примеры показывают, что имеет значение только то, что функция была вызвана из свойства `f` объекта `o`.
Аналогично, привязывание `this` обуславливается наличием ближайшей ссылки на объект или свойство. В следующем примере, когда мы вызываем функцию, мы обращаемся к ней как к методу `g` объекта `o.b`. На этот раз во время выполнения, `this`, что находится внутри функции, будет ссылаться на `o.b`. Тот факт, что объект является членом объекта `o`, не имеет значения; важна только ближайшая ссылка.
```js
o.b = { g: independent, prop: 42 };
console.log(o.b.g()); // logs 42
```
#### `this` в цепочке object's prototype
Это же представление справедливо и для методов, определённых где-либо в цепочке object's prototype. Если метод находится в цепочке прототипов, то `this` ссылается на объект, на котором был вызван метод, т.е. так, словно метод является методом самого объекта, а не прототипа.
```js
var o = {
f: function () {
return this.a + this.b;
},
};
var p = Object.create(o);
p.a = 1;
p.b = 4;
console.log(p.f()); // 5
```
В этом примере объект, который присвоен переменной `p`, не имеет собственного свойства `f`, а наследует это свойство от своего прототипа. Однако, совершенно неважно, что поиск свойства f в конце концов обнаружит его на объекте `o`. Поскольку поиск начался с `p.f`, то и свойство `this` внутри функции `f` будет ссылаться на объект `p`. Таким образом, если `f` вызывается как метод `p`, то и `this` относится к `p`. Это полезная особенность прототипного наследования JS.
#### `this` с геттерами/сеттерами
Все те же утверждения справедливы, если функция вызывается из геттера или сеттера. Для функции, которая используется как геттер или сеттер `this` привязан к объекту, свойство которого необходимо извлечь через геттер/сеттер.
```js
function modulus() {
return Math.sqrt(this.re * this.re + this.im * this.im);
}
var o = {
re: 1,
im: -1,
get phase() {
return Math.atan2(this.im, this.re);
},
};
Object.defineProperty(o, "modulus", {
get: modulus,
enumerable: true,
configurable: true,
});
console.log(o.phase, o.modulus); // logs -0.78 1.4142
```
### В конструкторе
Когда функция используется как конструктор (с ключевым словом [`new`](/ru/docs/Web/JavaScript/Reference/Operators/new) ), `this` связано с создаваемым новым объектом.
Примечание: по умолчанию конструктор возвращает объект, на который ссылается `this`, но он может вернуть и другой объект (если возвращаемое значение не является объектом, тогда будет возвращён объект с `this`).
```js
/*
* Конструктор работает таким образом:
*
* function MyConstructor(){
* // фактический код, составляющий тело функции.
* // создание свойств с |this| по
* // желанию, определяя их значения; например,
* this.fum = "nom";
* // и т.д.
*
* // Если функция возвращает выражение,
* // возвращающее объект, этот объект будет
* // результатом выражения |new|. В обратном случае,
* // результат выражения - объект,
* // в данный момент привязанный к |this|
* // (т.е. наиболее часто встречающийся случай).
* }
*/
function C() {
this.a = 37;
}
var o = new C();
console.log(o.a); // logs 37
function C2() {
this.a = 37;
return { a: 38 };
}
o = new C2();
console.log(o.a); // logs 38
```
В последнем примере (`C2`), из-за того, что конструктор вернул объект, новый объект, к которому было привязано `this`, был просто отброшен. (Это фактически делает выражение "`this.a = 37;`" "мёртвым" кодом. Он не является буквально нерабочим, так как он выполняется, но он может быть изъят без каких-либо внешних эффектов.)
### `call` и `apply`
Когда в теле функции используется ключевое слово `this`, его значение может быть привязано к конкретному объекту в вызове при помощи методов [`call`](/ru/docs/Web/JavaScript/Reference/Global_Objects/Function/call) или [`apply`](/ru/docs/Web/JavaScript/Reference/Global_Objects/Function/apply), которые наследуются всеми функциями от `Function.prototype`.
```js
function add(c, d) {
return this.a + this.b + c + d;
}
var o = { a: 1, b: 3 };
// Первый параметр - это объект, который следует использовать как
// 'this', последующие параметры передаются
// как аргументы при вызове функции
add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16
// Первый параметр - объект, который следует использовать как
// 'this', второй параметр - массив,
// элементы которого используются как аргументы при вызове функции
add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34
```
Необходимо отметить, что если методам `call` и `apply` передаётся значение с `this`, которое не является при этом объектом, будет предпринята попытка конвертировать значение в объект, используя внутреннюю операцию `ToObject`. Если переданное значение является примитивным типом, например `7` или `'foo'`, оно будет преобразовано в объект с использованием родственного конструктора, так примитив `7` преобразовывается в объект через `new Number(7),` а строка `'foo'` в объект через `new String('foo'),` и т.д.
```js
function bar() {
console.log(Object.prototype.toString.call(this));
}
bar.call(7); // [object Number]
```
### Как обработчик событий DOM
Когда функция используется как обработчик событий, `this` присваивается элементу с которого начинается событие (некоторые браузеры не следуют этому соглашению для обработчиков, добавленных динамически с помощью всех методов, кроме `addEventListener`).
```js
// Когда вызывается как обработчик, связанный элемент становится синим
function bluify(e) {
// Всегда true
console.log(this === e.currentTarget);
// true, когда currentTarget и target один объект
console.log(this === e.target);
this.style.backgroundColor = "#A5D9F3";
}
// Получить список каждого элемента в документе
var elements = document.getElementsByTagName("*");
// Добавить bluify как обработчика кликов, чтобы при
// нажатии на элемент он становился синим
for (var i = 0; i < elements.length; i++) {
elements[i].addEventListener("click", bluify, false);
}
```
### В инлайновом обработчике событий
Когда код вызван из инлайнового обработчика, `this` указывает на DOM-элемент, в котором расположен код события:
```js
<button onclick="alert(this.tagName.toLowerCase());">Показать this</button>
```
Код выше выведет '`button`'. Следует отметить, что `this` будет указывать на DOM-элемент только во внешних (не вложенных) функциях:
```js
<button onclick="alert((function() {return this;} ()));">
Показать вложенный this
</button>
```
В этом случае `this` вложенной функции не будет установлен, так что будет возвращён global/window объект.
## Спецификации
{{Specifications}}
## Совместимость с браузерами
{{Compat}}
## Смотрите также
- [Строгий режим](/ru/docs/Web/JavaScript/Reference/Strict_mode)
- [All this](http://bjorn.tipling.com/all-this), статья о `this` в разном контексте
- [Краткое объяснение ключевого слова 'this' в JavaScript](https://rainsoft.io/gentle-explanation-of-this-in-javascript/)