Почему в этом случае происходит потеря значения this?
Читаю об этом на learn.js и ничего не понимаю.
Можно ли как-то простыми словами объяснить почему во втором случае происходит потеря значения this?
let user = {
name: "Джон",
hi() { console.log(this.name); },
bye() { console.log("Пока"); }
};
user.hi(); // вызов метода работает хорошо
(user.name == "Джон" ? user.hi : user.bye)(); // Ошибка!
Тут, насколько я понимаю, если значение свойства name Джон, то происходит выбор первого значения - user.hi. Дальше открываются скобки и происходит запуск метода объекта.
Чем глобально первый запуск отличается от второго? Первый выглядит так: user.hi(). Второй в итоге будет выглядеть так: (user.hi()).
Ответы (2 шт):
(user.name == "Джон" ? user.hi : user.bye)();
Можно считать сокращённой записью для:
let func;
if(user.name == "Джон") func = user.hi;
else func = user.bye;
func(); // контекст вызова потерян
Просто в певом случае ссылка на метод сохраняется не в переменную, в как неименованное промежуточное значение, полученное из выражения в скобках.
user.hi() — В первом случае, когда происходит обычный вызов метода, this ссылается на «объект перед точкой» → user;
Этот механизм работает во всех случаях (почти *), где используется вызов объект.метод();
(user.name == "Джон" ? user.hi : user.bye)(); — Не одно и то же, что и (user.hi()) или (user.hi)().
Здесь сначала выполняется выражение внутри скобок, возвращается свойство hi из объекта user (ссылка на функцию), к которому затем применяется оператор вызова функции ()
А у ссылки на функцию нет никакого контекста (нет объекта перед точкой, и не важно, откуда была получена ссылка). Т.е. ('x' ? user.hi : user.bye) всего лишь один из способов получения ссылки. Ошибка будет возникать и в случае обычного копирования в переменную:
"use strict";
let user = {
hi() { console.log(this.name); },
};
let func = user.hi; // Ссылка на функцию скопирована в переменную func
func(); // У ссылки уже нет никакого контекста (ни точки, ни объекта перед ней)
setTimeout(user.hi, 1000) — тоже частая вариация такой ошибки. Вытащили функцию hi из объекта, передали в setTimeout - контекст потерялся.
Правильно: setTimeout(() => user.hi(), 1000) — передается функция () => user.hi() которая будет вызвана через секунду и сама вызовет user.hi(), контекст никуда не денется.
* Раз в никогда может возникнуть ситуация, когда в объект попала функция, у которой есть свой жестко привязанный контекст:
function fn() {
console.log(this.a);
}
/***/
let obj1 = {
a: 100,
};
let obj2 = {
a: 999,
func: fn.bind(obj1),
}
// Function.bind(obj) создает и возвращает новую функцию, внутри которой
// this всегда будет равен obj, независимо от способа вызова;
obj2.func();
obj2.func.call(obj2);