JavaScript e ‘this’: capire come usarla!

La parola chiave this. Una delle più potenti e importanti parole chiave in JavaScript è la parola this: purtroppo se non la si conosce bene e non si sa esattamente come funziona, è difficile da usare e da capire.

Ecco di seguito la traduzione dell’articolo The this keyword

Chi è il proprietario di ‘this’?

La domanda alla quale risponderemo in questo articolo è: a cosa si riferisce la parola this in
una funzione generica doSomething()?

Con questa funzione di esempio, vogliamo semplicemente cambiare lo stile (il colore) dell’elemento a cui il ‘this’ si riferisce. Eccola:

function doSomething() {
   this.style.color = '#cc0000';
}

Iniziamo col dire che in JavaScript this si riferisce sempre al proprietario della funzione che stiamo eseguendo, l’oggetto del quale la funzione rappresenta un metodo.
Quando definiamo la funzione doSomething() in una pagina, o meglio, nello scoping globale, il suo proprietario è la pagina, cioè l’oggetto window, l’oggetto globale di JavaScript.
Una proprietà onClick, invece, è una proprietà del elemento HTML a cui appartiene.

Questa “proprietà” è il risultato dell’approccio orientato agli oggetti di JavaScript.

------------ window --------------------------------------
|                                          / \           |
|                                           |            |
|                                          this          |
|   ----------------                        |            |
|   | HTML element | <-- this         -----------------  |
|   ----------------      |           | doSomething() |  |
|               |         |           -----------------  |
|          --------------------                          |
|          | onclick property |                          |
|          --------------------                          |
|                                                        |
----------------------------------------------------------

Se eseguiamo la funzione così com'è, riferendosi essa sempre all'oggetto window, cerca di cambiare la proprietà style.color della finestra, che non avendola, produrrà un errore.

Come facciamo? Possiamo...

...Copiare la funzione nella proprietà dell'elemento

Per fare in modo che la funzione vada a buon fine, dobbiamo fare in modo che essa sia 'posseduta' dal corretto elemento HTML.
In altre parole, dobbiamo copiare la funzione nella proprietà onClick dell'elemento, cosa di cui si prende cura la registrazione tradizionale dell'evento.

element.onclick = doSomething;

La funzione viene completamente copiata nella proprietà onclick, e adesso diventa un metodo.
Quindi, se il gestore dell'evento (event handler) viene eseguito, esso fa riferimento all'elemento HTML e il suo colore cambia.

Ecco sotto lo schema:

------------ window --------------------------------------
|                                                        |
|                                                        |
|                                                        |
|   ----------------                                     |
|   | HTML element | <-- this         -----------------  |
|   ----------------      |           | doSomething() |  |
|               |         |           -----------------  |
|          -----------------------          |            |
|          |copy of doSomething()|  <-- copy function    |
|          -----------------------                       |
|                                                        |
----------------------------------------------------------

Il punto cruciale è che possiamo copiare la funzione in diversi gestori di evento, gli 'event handlers' di elementi diversi, e ogni volta il 'this' farà riferimento all'elemento HTML corretto:

------------ window --------------------------------------
|                                                        |
|                                                        |
|                                                        |
|   ----------------                                     |
|   | HTML element | <-- this         -----------------  |
|   ----------------      |           | doSomething() |  |
|               |         |           -----------------  |
|          -----------------------          |            |
|          |copy of doSomething()|  <-- copy function    |
|          -----------------------          |            |
|                                           |            |
|   -----------------------                 |            |
|   | another HTML element| <-- this        |            |
|   -----------------------     |           |            |
|               |               |           |            |
|          -----------------------          |            |
|          |copy of doSomething()|  <-- copy function    |
|          -----------------------                       |
|                                                        |
----------------------------------------------------------

Attenzione ai corretti riferimenti!

Tuttavia, se si utilizza la registrazione dell'evento in linea, così:

	<button onclick="doSomething()"></button>

non si copia la funzione! Invece, si fa riferimento ad essa, e la differenza è fondamentale. La proprietà "onclick" non contiene la funzione reale, ma solo una chiamata di funzione, come se facessi così:

doSomething();

Come se si dicesse: "Vai a doSomething() ed eseguila."
Quando JavaScript arriva a doSomething(), la parola chiave 'this', ancora una volta si riferisce all'oggetto 'window' globale e la funzione restituisce messaggi di errore.

In sintesi, ecco cosa succede:

------------ window --------------------------------------
|                                          / \           |
|                                           |            |
|                                          this          |
|   ----------------                        |            |
|   | HTML element | <-- this         -----------------  |
|   ----------------      |           | doSomething() |  |
|               |         |           -----------------  |
|          -----------------------         / \           |
|          | go to doSomething() |          |            |
|          | and execute it      | ---- reference to     |
|          -----------------------       function        |
|                                                        |
----------------------------------------------------------

Quale è la differenza?

Se si desidera utilizzare "this" per accedere all'elemento HTML che sta gestendo l'evento, è necessario assicurarsi che la parola chiave "this" venga effettivamente scritta nella proprietà "onclick". Solo in questo caso, ci si riferisce all'elemento HTML a cui il gestore di eventi è registrato.
Quindi, se scriviamo:

element.onclick = doSomething;
alert(element.onclick)

otteniamo che:

function doSomething(){
	this.style.color = '#cc0000';
}

Come si può vedere, la parola chiave "this" è presente nel metodo onclick. Pertanto si riferisce all'elemento HTML. Ma se facciamo così:

<element onclick="doSomething()">
alert(element.onclick);

otteniamo:

function onclick(){
	doSomething();
}

In questo modo, abbiamo creato solo un riferimento alla funzione "doSomething()". La parola chiave "this" non è presente nel metodo "onclick", perciò non si riferisce all'elemento HTML.

Esempi su come "copiare"!

La parola "this" è scritto nel metodo onclick nei seguenti casi:

JavaScript

element.onclick = doSomething
element.addEventListener('click',doSomething,false)
element.onclick = function () {this.style.color = '#cc0000';}

HTML

<element onclick="this.style.color = '#cc0000';"&t;

Esempi su come "riferirsi"!

Nei seguenti casi, "this" si riferisce all'oggetto "window":

JavaScript

element.onclick = function () {doSomething()}
element.attachEvent('onclick',doSomething)

HTML

<element onclick="doSomething()"&t;

Notate la presenza di "attachEvent()". Lo svantaggio principale del modello di registrazione dell'evento Microsoft è che "attachEvent()" crea un riferimento alla funzione, non la non copia. Quindi a volte è impossibile sapere quale elemento HTML gestisce di fatto l'evento.

Come si combinano i due esempi?

Quando si utilizza la registrazione dell'evento in linea, è possibile anche inviare "this" alla "funzione", in modo che si possa ancora usarlo:

< onclick="doSomething(this)"&t;
function doSomething(obj) {
	// il "this" è presente nel gestore eventi, e viene inviato alla funzione
	// "obj" ora si riferisce l'elemento HTML, in modo che possiamo fare:
	obj.style.color = '#cc0000';
}

Ecco cosa ho usato per questo articolo: