La confusione su this nasce da un equivoco: il suo valore dipende da come chiami la funzione, non da dove è scritta.

const user = {
  name: "Ale",
  saluta() { return "Ciao " + this.name; },
};

user.saluta();            // "Ciao Ale"  -> this = user
const f = user.saluta;
f();                      // "Ciao undefined" -> this perso

f.call(user);             // "Ciao Ale" -> this forzato
const legato = user.saluta.bind(user);
legato();                 // "Ciao Ale" -> this legato per sempre

Chiamata come metodo (user.saluta()): this è l'oggetto. Chiamata "sciolta" (f()): this si perde. Con call/apply/bind lo imposti tu.

Le arrow function sono l'eccezione salvavita: non hanno un proprio this, ereditano quello del contesto in cui sono definite. Per questo nei callback (setTimeout(() => this.x)) risolvono metà dei problemi.