web-dev-qa-db-fra.com

Classe ES6: accès à 'this' avec 'addEventListener' appliqué à la méthode

Dans ce script es6, l'événement click ne fonctionne pas car la méthode sayHello est appelée avec this.Elm (<div>) comme this.

comment associer un événement à une méthode sans perdre la portée?

class player{
  constructor (name) {
    this.name = name;
    this.Elm = document.createElement('div');
    this.Elm.addEventListener('click', this.sayHello);
  }
  sayHello() {
    console.log(this.name + ' say: "hello!"'); // 'undefined say 'hello!"';
  }
  kill() {
    console.log(`RIP ${this.name} :'(`); 
    this.Elm.addClass('dead');
    this.Elm.removeEventListener('click', this.sayHello);
  }
}
40
Yukulélé

C’est une question générale du JS, mais le cœur de celle-ci est que

this.Elm.addEventListener('click', this.sayHello);

n'est pas différent de

var fn = this.sayHello;
this.Elm.addEventListener('click', fn);

Vous transmettez une fonction en tant que gestionnaire d'événements, mais vous ne vous êtes pas assuré que, lorsque fn serait appelé, this serait alors défini sur la valeur souhaitée. La manière la plus simple de le faire dans ES5 serait de

this.Elm.addEventListener('click', this.sayHello.bind(this));

ou dans ES6, en utilisant une fonction de flèche:

this.Elm.addEventListener('click', evt => this.sayHello(evt));

Notez cependant que ces deux solutions vont casser votre logique (déjà légèrement cassée) dans kill parce que

this.Elm.removeEventListener('click', /* what? */);

Vous n'avez plus aucune référence à la fonction que vous avez attachée, vous n'avez donc aucun moyen de supprimer le gestionnaire d'événements.

Je suggérerais deux options:

// Create a new function that is bound, and give it a new name
// so that the 'this.sayHello()' call still works.
this.boundSayHello = evt => this.sayHello(evt);
this.Elm.addEventListener('click', this.boundSayHello);
this.Elm.removeEventListener('click', this.boundSayHello);

ou

// Bind the function with the same name and use `.bind` instead of the
// arrow function option.
this.sayHello = this.sayHello.bind(this);
this.Elm.addEventListener('click', this.sayHello);
this.Elm.removeEventListener('click', this.sayHello);
94
loganfsmyth