Metodi di classe come gestori di eventi in JavaScript?
-
04-07-2019 - |
Domanda
Esiste un metodo best practice o comune in JavaScript per avere membri della classe come gestori di eventi?
Considera il seguente esempio semplice:
<head>
<script language="javascript" type="text/javascript">
ClickCounter = function(buttonId) {
this._clickCount = 0;
document.getElementById(buttonId).onclick = this.buttonClicked;
}
ClickCounter.prototype = {
buttonClicked: function() {
this._clickCount++;
alert('the button was clicked ' + this._clickCount + ' times');
}
}
</script>
</head>
<body>
<input type="button" id="btn1" value="Click me" />
<script language="javascript" type="text/javascript">
var btn1counter = new ClickCounter('btn1');
</script>
</body>
Il gestore di eventi buttonClicked viene chiamato, ma il membro _clickCount è inaccessibile, oppure questo punta a qualche altro oggetto.
Qualche suggerimento / articolo / risorsa su questo tipo di problemi?
Soluzione
ClickCounter = function(buttonId) {
this._clickCount = 0;
var that = this;
document.getElementById(buttonId).onclick = function(){ that.buttonClicked() };
}
ClickCounter.prototype = {
buttonClicked: function() {
this._clickCount++;
alert('the button was clicked ' + this._clickCount + ' times');
}
}
EDIT quasi 10 anni dopo, con ES6, funzioni freccia e proprietà della classe
class ClickCounter {
count = 0;
constructor( buttonId ){
document.getElementById(buttonId)
.addEventListener( "click", this.buttonClicked );
}
buttonClicked = e => {
this.count += 1;
console.log(`clicked ${this.count} times`);
}
}
Altri suggerimenti
Una funzione collegata direttamente alla proprietà onclick avrà la proprietà this
del contesto di esecuzione che punta all'elemento.
Quando devi eseguire un evento element per eseguire un'istanza specifica di un oggetto (come un delegato alla .NET in .NET) allora avrai bisogno di una chiusura: -
function MyClass() {this.count = 0;}
MyClass.prototype.onclickHandler = function(target)
{
// use target when you need values from the object that had the handler attached
this.count++;
}
MyClass.prototype.attachOnclick = function(elem)
{
var self = this;
elem.onclick = function() {self.onclickHandler(this); }
elem = null; //prevents memleak
}
var o = new MyClass();
o.attachOnclick(document.getElementById('divThing'))
Non so perché Function.prototype.bind
non sia stato ancora menzionato qui. Quindi lascerò questo qui;)
ClickCounter = function(buttonId) {
this._clickCount = 0;
document.getElementById(buttonId).onclick = this.buttonClicked.bind(this);
}
ClickCounter.prototype = {
buttonClicked: function() {
this._clickCount++;
alert('the button was clicked ' + this._clickCount + ' times');
}
}
Puoi usare la sintassi fat-arrow, che si lega all'ambito lessicale della funzione
function doIt() {
this.f = () => {
console.log("f called ok");
this.g();
}
this.g = () => {
console.log("g called ok");
}
}
Successivamente puoi provare
var n = new doIt();
setTimeout(n.f,1000);
Puoi provarlo su babel o se il tuo browser supporta ES6 su JsFiddle .
Sfortunatamente la sintassi della classe ES6 non sembra consentire la creazione di funzioni legate in modo lessicale a questo. Personalmente penso che potrebbe anche farlo. EDIT: sembra che ci sia funzionalità sperimentale ES7 per consentirlo .