Предисловие
На просторах интернета довольно много информации о том, как работает
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.47%да39
23.53%нет12
0%я всё это уже знал0
Проголосовал 51 пользователь. Воздержались 2 пользователя.
