Что происходит при вызове методов объекта, объявленных разными способами в JavaScript
Что проверяют
Понимаете ли вы, что this в JavaScript определяется способом вызова, а не местом объявления, и что стрелочная функция из этого правила выпадает. Следом почти всегда дают код с методом, переданным колбэком в setTimeout или в обработчик события, и просят сказать, что выведется.
Как отвечать
Разберите способы объявления по поведению:
- шорткат
greet() {}и свойствоgreet: function () {}:thisравен объекту слева от точки в момент вызова. Разница в деталях: шорткат нельзя вызвать черезnew, и внутри него работаетsuper; - стрелка
greet: () => {}: своегоthisнет, он берётся из окружающего кода. В литерале объекта это внешний контекст, а не сам объект; - метод класса: лежит в
Person.prototype, один на все экземпляры, неперечисляемый. Тело класса всегда в строгом режиме; - поле-стрелка
greet = () => {}: создаётся в каждом экземпляре заново,thisнавсегда привязан к экземпляру.
Главное следствие: метод, оторванный от объекта, теряет контекст.
const f = person.greet;
f(); // TypeError: this равен undefined
setTimeout(person.greet, 0); // та же потеряЛечат через person.greet.bind(person), обёртку () => person.greet() или поле-стрелку. У call и apply задача другая: один раз вызвать с явным this; на стрелку ни они, ни bind не действуют.
Про стрелку в литерале полезно сказать точно. В ES-модуле this на верхнем уровне равен undefined, и this.name бросит TypeError. В обычном скрипте браузера this это window, и вы получите window.name, то есть пустую строку. Популярный ответ «выведет undefined» верен разве что для CommonJS в Node.
Где спотыкаются
Считают, что this определяется там, где функция написана. Для обычных функций это неверно, для стрелок верно, и проверяют именно эту разницу.
Делают все методы класса полями-стрелками, «чтобы не терять this». Каждый экземпляр получает свою копию функции, в наследнике её не вызвать через super.greet(), и через прототип её не подменить в тестах.
Думают, что привязку можно сменить. Повторный bind на уже привязанной функции this не меняет.