* скорее всего, я что-нибудь да упустил, но уве��ен, в комментариях мне это подскажут
Эту статью я пишу для своих личных нужд. Планируется, что она будет содержать в себе ответы на все вопросы, которые мне задают студенты на эту тему. Если она пригодится кому-то ещё — здорово.

Содержание.
Ключевое слово
Заблуждения о
Существует несколько распространённых заблуждений относительно этого ключевого слова. Я хочу их быстренько развеять перед тем, как перейти к сути.
this — это лексический контекст.
Такое впечатление часто возникает у новичков. Им кажется, что
В общем случае это неправда. Это легко проверить.
this — это объект, которому принадлежит метод
Опять же, это правда во многих конкретных случаях, но не всегда. Важно то, каким способом вызывается функция, а не то, является ли она свойством какого-то объекта. Это понятно даже из очень простой логики: предположим, одна и та же функция является свойством одновременно двух объектов.
Так какой же из этих объектов будет её this'ом?
Важно! Даже если объект создан с помощью классов ES6, это совершенно не гарантирует, что у его метода всегда будет нужный
this — это джедайская техника, которую, изучив, нужно использовать везде
По возможности лучше избегать употребления
Здесь я постараюсь дать строгий и лаконичный алгоритм, с помощью которого даже неискушённый кодер сумеет понять, чему равняется
Вот, в общем-то, и всё. Определение значения
P.S. Пользователь Aingis подсказал, что в случае использования конструкции with объект, переданный в неё в качестве контекста, подменяет собой глобальный объект. Пожалуй, я не стану вносить это в свой классификатор, потому что шанс встретить
P.P.S. Пользователь rqrqrqrq подсказал, что у
Эту статью я пишу для своих личных нужд. Планируется, что она будет содержать в себе ответы на все вопросы, которые мне задают студенты на эту тему. Если она пригодится кому-то ещё — здорово.

Содержание.
Введение
Ключевое слово
this — одна из наиболее запутывающих особенностей языка JavaScript. Пришедшее из Java, оно было призвано помочь в реализации ООП. Я какое-то время писал на Java, и должен сказать, что за это время у меня, может быть, один раз возникло сомнение, чему равняется this в конкретном месте кода. В JavaScript такие сомнения могут возникать каждый день — по крайней мере, до того момента, как выучишь несколько простых, но неочевидных правил.Заблуждения о this
Существует несколько распространённых заблуждений относительно этого ключевого слова. Я хочу их быстренько развеять перед тем, как перейти к сути.
this — это лексический контекст.
Такое впечатление часто возникает у новичков. Им кажется, что
this — это объект, в котором, как свойства, хранятся все переменные в данной области видимости. Это заблуждение происходит из того, что в одном конкретном случае это, грубо говоря, так. Если мы находимся на самом верхнем уровне, то this ра��няется window (в случае обычного скрипта в браузере). А как мы знаем, все переменные, объявленные на верхнем уровне, доступны как свойства window.В общем случае это неправда. Это легко проверить.
function test(){
const a = "Кто прочитал этот текст в консоли, тот скоро умрёт";
console.log(this.a);
}
test(); // не волнуйтесь, никто не умрёт
this — это объект, которому принадлежит метод
Опять же, это правда во многих конкретных случаях, но не всегда. Важно то, каким способом вызывается функция, а не то, является ли она свойством какого-то объекта. Это понятно даже из очень простой логики: предположим, одна и та же функция является свойством одновременно двух объектов.
const f = function(){
console.log(this);
}
const object1 = {
method: f
};
const object2 = {
method: f
};
Так какой же из этих объектов будет её this'ом?
Важно! Даже если объект создан с помощью классов ES6, это совершенно не гарантирует, что у его метода всегда будет нужный
this. Пусть вас не вводит в заблуждение сходство с классами из «нормальных» языков.this — это джедайская техника, которую, изучив, нужно использовать везде
По возможности лучше избегать употребления
this. Единственный случай, когда оно полностью обосновано — это ООП. В прочих местах употребление this — это не то чтобы зло, но, как правило, излишество, запутывающее код. По сути this — это дополнительный, «минус первый» аргумент функции, который передаётся туда сложным, неочевидным новичку путём. С высокой вероятностью его можно заместить обычным аргументом, и станет только лучше.Как определить значение this
Здесь я постараюсь дать строгий и лаконичный алгоритм, с помощью которого даже неискушённый кодер сумеет понять, чему равняется
this в его конкретном случае. Более многословные пояснения буду спрятаны под спойлеры, дабы не захламлять визуальное пространство.- Мы находимся внутри функции?
- Да: смотрим следующий пункт.
- Нет:
thisравен глобальному объекту.
КомментарийВ коде верхнего уровня (не находящемся внутри никакой функции)thisвсегда ссылается на глобальный объект. В случае обычного скрипта в браузере это — объектwindow. Но вообще случаи бывают разные.
- Мы находимся внутри стрелочной функции?
- Да: значение
thisтакое же, как и в функции на уровень выше (т.е. содержащей данную). Вернитесь на предыдущий шаг и повторите алгоритм для неё. Если же функция не содержится ни в какой другой,this— глобальный объект. - Нет: смотрим следующий пункт.
КомментарийОдна из основных особенностей стрелочных функций — это так называемый «лексическийthis». Это значит, что значениеthisв стрелочной функции определяется исключительно тем, где (в каком лексическом контексте) она была создана, и никак не зависит от того, как впоследствии она была вызвана. Иногда это не то, что нам нужно, но чаще всего это делает стрелочные функции очень удобными и предсказуемыми.
- Да: значение
- Эта функция вызвана как конструктор (с помощью оператора new)?
- Да:
thisссылается на новый объект, находящийся «в процессе конструкции». - Нет: смотрим следующий пункт.
КомментарийПожалуй, стоит отдельно оговорить случай, когда речь идёт о конструкторе унаследованного ES6-класса. Тогда до вызоваsuper()значения уthisнет (обращение к нему вызовет ошибку), а после вызоваsuper()он равняется новому объекту родительского класса.
- Да:
- Эта функция создана с помощью метода bind?
- Да: значение
thisравняется значению первого аргумента, который мы передали в методbindпри создании данной функции. - Нет: смотрим следующий пункт.
КомментарийМетодbindсоздаёт копию функции, зафиксировав для неёthisи, опционально, несколько первых аргументов. На самом деле при этом создаётся не просто копия функции, а, цитирую, «экзотический объект BoundFunction». Экзотичность его проявляется, в частности, в том, что повторным вызовомbindмы уже не сможем изменитьthis. Поэтому, строго говоря, ответ в этом пункте надо было сформулировать так: если да, тоthisравняется первому аргументу первого вызоваbind, который привёл к созданию данной функции.
- Да: значение
- Эта функция передана куда-то в качестве колбэка или обработчика?
- Да: одному Господу известно, чему будет равен
thisпри её вызове. Идите читать документацию по той штуке, которая её станет вызывать. - Нет: смотрим следующий пункт.
КомментарийУ не стрелочной и не связанной (bound) функции значениеthisзависит от обстоятельств, в которых она была вызвана. Если вы не вызываете её лично, а передаёте куда-то, то в качествеthisможет быть или не быть подставлено неизвестное вам значение.
Примеры:
`use strict` document.addEventListener('keydown', function(){ console.log(this); }); // в этом случае this === document. при срабатывании обработчиков DOM-событий this равняется currentTarget [1, 2, 3].forEach(function(){ console.log(this); }); // а в этом случае никакого специального значени�� не будет, будет undefined. почему? об этом в самом конце. - Да: одному Господу известно, чему будет равен
- Эта функция вызвана с помощью метода apply или call?
- Да: в таком случае
thisравняется первому аргументу, переданному соответствующему методу. - Нет: смотрим следующий пункт.
КомментарийЕщё один способ явно задатьthis. Точнее, два. Однако в планеthisразницы междуapplyиcallнет никакой, разница только в том, как передаются остальные аргументы.
- Да: в таком случае
- Эта функция получена как значение свойства объекта и сразу же вызвана?
- Да:
thisравняется вышеупомянутому объекту. - Нет: смотрим следующий пункт.
КомментарийСобственно, из этого механизма (а также — из опыта работы с другими языками) растут ноги у убеждения, что "this— это объект, чей метод мы вызвали". Пожалуй, я просто напишу код.
'use strict'; const object1 = { method: function(){ console.log(this); } } const object2 = { method: object1.method } object1.method(); // в консоли будет object1 - мы получили функцию как свойство этого объекта и немедленно вызвали object1['method'](); // аналогичный результат. этот механизм не специфичен для точечной нотации object2.method(); // в консоли будет object2 - метод "не помнит", в каком объекте он был создан, ему важно только у какого объекта он вызван - Да:
- Код выполняется в строгом режиме? ('use strict', ES6 модуль)
- Да:
thisравняетсяundefined. - Нет:
thisравен глобальному объекту.
КомментарийЕсли мы дошли до этого пункта, значит,thisне задан ни одним из механизмов, которые позволяют его задать. Существуют различные заблуждения относительно того, как ещё может передаватьсяthis. Например, на собеседованиях мне часто говорят вот такую вещь:
const obj = { test: function(){ (function(){ console.log(this); })(); //немедленно вызываемая функция внутри другой функции } } obj.test(); // мне говорят, что в консоль выведется obj. это неправда
Или, как я уже говорил в секции «заблуждения», многие считают, что если функция является методом объекта, созданного с помощью классов ES6, то уж в ней-то this всегда будет равен этому объекту. Это тоже неправда.
Так вот, если мы дошли до этого пункта, значит, прочие обстоятельства вызова нашей функции не имеют значения. И всё сводится к тому, находимся мы в строгом режиме или нет.
Исторически в качестве «дефолтного»thisв такие функции передавался глобальный объект. Позже этот подход был признан небезопасным. В ES5 появился строгий режим, исправляющий многие проблемы более ранних версий ECMAScript. Он включается директивой 'use strict' в начале файла или функции. В таком режиме «дефолтное» значениеthis— этоundefined.
В ES6 модулях строгий режим включен по умолчанию.
Также существуют другие механизмы включения строгого режима, например, в NodeJS строгий режим для всех файлов можно вк��ючить флагом--use-strict.
- Да:
Вот, в общем-то, и всё. Определение значения
this, конечно, менее просто, чем хотелось бы, но и не так сложно, как могло показаться. Выучите этот алгоритм, как таблицу умножения — и у вас больше никогда не возникнет проблем с this. Такие дела.P.S. Пользователь Aingis подсказал, что в случае использования конструкции with объект, переданный в неё в качестве контекста, подменяет собой глобальный объект. Пожалуй, я не стану вносить это в свой классификатор, потому что шанс встретить
with в 2019+ году довольно мал. Но в любом случае это интересный момент.P.P.S. Пользователь rqrqrqrq подсказал, что у
new выше приоритет, чем у bind. Соответствующая правка в классификатор уже внесена. Спасибо!