Предисловие
На просторах интернета довольно много информации о том, как работает
this
, но мне всё время не хватало буквально чуть-чуть, чтобы до конца в этом разобраться.Возможно, именно эта статья станет для вас той, которая расставит всё по полочкам!
Без лишних слов
Мы разберем как простые, так и сложные примеры — так что всем будет интересно.
Начнем с определения.
Два основных тезиса, которые мы рассмотрим:This
— это ссылка на контекст исполнения функции. Таким образом получается, чтоthis
тесно связан именно с функциями и рассматривается относительно них. Вне функцииthis
будет ссылаться на глобальный контекст
Тезис 1Договоримся, что везде используется
Для функций, объявленных через function f( ) { },this
вычисляется в момент вызова и равен объекту перед точкой. Если такого объекта нет — тогдаthis
будет указывать на глобальный контекст (window)
Тезис 2
Для стрелочных функцийthis
определяется в момент их создания и больше никогда не изменяется
'use strict'
, поэтому в глобальной области видимости this
всегда будет undefined
, а не window
.Пример 1
Для начала посмотрим на самый простой пример.function globalFunc() {
console.log(this);
}
const globalArrowFunc = () => {
console.log(this);
}
console.log(this); // ?
globalFunc(); // ?
globalArrowFunc(); // ?
Ответ с объяснением
Простое обращение к
Функция, объявленная через function, указывает на window, потому что нет никакого объекта перед точкой.
Стрелочная функция указывает на window, потому что она была создана внутри глобального контекста (window).
console.log(this); // undefined
Простое обращение к
this
указывает на ближайший контекст исполнения — window.globalFunc(); // undefined
Функция, объявленная через function, указывает на window, потому что нет никакого объекта перед точкой.
globalArrowFunc(); // undefined
Стрелочная функция указывает на window, потому что она была создана внутри глобального контекста (window).
Пример 2
Давайте двинемся дальше и рассмотрим всё то же самое внутри объекта.const user = {
name: 'Bob',
userThis: this,
func() {
console.log(this);
},
arrowFunc: () => {
console.log(this);
}
};
console.log(user.userThis); // ?
user.func(); // ?
user.arrowFunc(); // ?
Ответ с объяснением
Здесь мы вновь обратим свое внимание на то, что
Здесь объектом перед точкой является user, поэтому
Поскольку стрелочная функция запомнила свой контекст в момент создания, то и при вызове она будет ссылаться именно на него. Таким образом мы получаем window.
Однако вы можете задать вопрос «Но почему в момент создания контекст был window, а не объект, внутри которого она была объявлена как метод?».
Стрелочная функция в момент создания ищет ближайший к ней контекст и запоминает его, а он у нас точно такой же как и в случае простого присвоения внутри
console.log(user.userThis); // undefined
Здесь мы вновь обратим свое внимание на то, что
this
имеет смысл только относительно функции, потому что this
указывает на контекст исполнения функции. Поскольку этот объект находится внутри глобального контекста (window), то и наше this
указывает на window.user.func(); // { user }
Здесь объектом перед точкой является user, поэтому
this
ссылается на объект user.user.arrowFunc(); // undefined
Поскольку стрелочная функция запомнила свой контекст в момент создания, то и при вызове она будет ссылаться именно на него. Таким образом мы получаем window.
Однако вы можете задать вопрос «Но почему в момент создания контекст был window, а не объект, внутри которого она была объявлена как метод?».
Стрелочная функция в момент создания ищет ближайший к ней контекст и запоминает его, а он у нас точно такой же как и в случае простого присвоения внутри
user.userThis
.Пример 3
А теперь давайте попробуем что-то посложнее. Создадим объект с методами, которые возвращают другие функции. Таким образом у нас получится 4 разных метода.Обычная функция, возвращающая обычную функцию — funcFunc.
Обычная функция, возвращающая стрелочную функцию — funcArrow.
Стрелочная функция, возвращающая обычную функцию — arrowFunc.
Стрелочная функция, возвращающая стрелочную функцию — arrowArrow.
const user = {
name: 'Bob',
funcFunc() {
return function() {
console.log(this);
}
},
funcArrow() {
return () => {
console.log(this);
}
},
arrowFunc: () => {
return function() {
console.log(this);
}
},
arrowArrow: () => {
return () => {
console.log(this);
}
},
};
user.funcFunc()(); // ?
user.funcArrow()(); // ?
user.arrowFunc()(); // ?
user.arrowArrow()(); // ?
Ответ с объяснением
Когда вызывается user.funcFunc() — нам возвращается функция function. Обратим внимание, что мы сразу же вызываем ее и в этот момент у нее нет никакого объекта перед точкой, а значит
Мы могли бы разбить это на 2 строки
const foo = user.funcFunc()
foo(); — нет объекта перед точкой
Когда мы вызвали метод funcArrow, то создали стрелочную функцию и в момент создания она запомнила окружающий ее контекст. Теперь она всегда будет его возвращать и никогда не потеряет.
Здесь все то же самое, что и первом примере. Нам не важно, каким образом была создана function. Важно лишь что в момент ее вызова у нее нет объекта перед точкой.
Как мы помним, внутри метода arrowArrow
user.funcFunc()(); // undefined
Когда вызывается user.funcFunc() — нам возвращается функция function. Обратим внимание, что мы сразу же вызываем ее и в этот момент у нее нет никакого объекта перед точкой, а значит
this
ссылается на window.Мы могли бы разбить это на 2 строки
const foo = user.funcFunc()
foo(); — нет объекта перед точкой
user.funcArrow()(); // { user }
Когда мы вызвали метод funcArrow, то создали стрелочную функцию и в момент создания она запомнила окружающий ее контекст. Теперь она всегда будет его возвращать и никогда не потеряет.
user.arrowFunc()(); // undefined
Здесь все то же самое, что и первом примере. Нам не важно, каким образом была создана function. Важно лишь что в момент ее вызова у нее нет объекта перед точкой.
user.arrowArrow()(); // undefined
Как мы помним, внутри метода arrowArrow
this
указывал на window. Таким образом и только что созданная стрелочная функция будет указывать на него же.Пример 4
Давайте еще немного поиграем с предыдущим примером. Теперь мы не просто вытащим новые функции и вызовем их, а запишем их в другой объект и вызовем из него.// Объект user остался без изменений
const user = {
name: 'Bob',
funcFunc() {
return function() {
console.log(this);
}
},
funcArrow() {
return () => {
console.log(this);
}
},
arrowFunc: () => {
return function() {
console.log(this);
}
},
arrowArrow: () => {
return () => {
console.log(this);
}
},
};
const user2 = {
name: 'Jim',
funcFunc: user.funcFunc(),
funcArrow: user.funcArrow(),
arrowFunc: user.arrowFunc(),
arrowArrow: user.arrowArrow()
}
user2.funcFunc(); // ?
user2.funcArrow(); // ?
user2.arrowFunc(); // ?
user2.arrowArrow(); // ?
Ответ с объяснением
В момент вызова перед точкой объект user2, поэтому на него и будет ссылаться
Когда мы вызвали user.funcArrow(), мы создали новую стрелочную функцию, но в момент создания она запомнила ближайший к ней контекст (user) и теперь она всегда и везде будет возвращать именно его.
Здесь точно то же самое, что и в первом случае. Мы вернули функци и вызвали ее. Объект перед точкой user2, поэтому
Когда мы вызвали user.arrowArrow(), то создали стрелочную функцию, которая запомнила ближайший контекст (window). Теперь она всегда будет указывать на него.
user2.funcFunc(); // { user2 }
В момент вызова перед точкой объект user2, поэтому на него и будет ссылаться
this
.user2.funcArrow(); // { user }
Когда мы вызвали user.funcArrow(), мы создали новую стрелочную функцию, но в момент создания она запомнила ближайший к ней контекст (user) и теперь она всегда и везде будет возвращать именно его.
user2.arrowFunc(); // { user2 }
Здесь точно то же самое, что и в первом случае. Мы вернули функци и вызвали ее. Объект перед точкой user2, поэтому
this
указывает именно на него.user2.arrowArrow(); // undefined
Когда мы вызвали user.arrowArrow(), то создали стрелочную функцию, которая запомнила ближайший контекст (window). Теперь она всегда будет указывать на него.
Ну вот и всё
Надеюсь, что после прочтения данного материала, определение контекста исполнения уже не так пугает вас, как раньше :)
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Статья вам помогла?
76.09% да35
23.91% нет11
0% я всё это уже знал0
Проголосовали 46 пользователей. Воздержались 2 пользователя.