Giusto ieri ho scritto due righe a proposito di programmazione object-oriented in Javascript e oggi voglio tornare sull'argomento per fare un ulteriore passo verso un lavoro corretto e produttivo. Noi tutti quando scriviamo in C# o in un qualunque linguaggio realmente object oriented siamo abituati a gestire eventi asincroni nel contesto stesso in cui questi sono generati in modo tale che la loro invocazione e la loro gestione rimanga confinata all'interno di una sola istanza di una classe. In Javascript invece, questo non è immediatamente possibile; Spesso e volentieri ci si trova a dover gestire chiamate asincrone, o eventi generati dall'interfaccia e utilizzando un paradigma object oriented ci si trova a gestire eventi fuori del contesto dal quale essi sono generati. Mi rendo conto che spiegare a parole questo problema è difficile perciò vi propongo un esempio che chiarisca. Poniamo ad esempio di voler scrivere una classe che sia in grado di trasformare un elemento dell'interfaccia in un pulsante. Ad essa sarà passato in ingresso un elemento del DOM ed essa sarà in grado di agganciare l'evento onclick e gestirlo quando necessario:

   1: // HTML
   2:  
   3: <div id="myButton">Click Me!!!</div>
   4:  
   5: // Classe Javascript
   6:  
   7: ClickButton = function(message, element)
   8: {
   9:     this._message = message;
  10:     element.onclick = this._onclick;
  11: }
  12: ClickButton.prototype = 
  13: {
  14:     _onclick : function()
  15:     {
  16:         alert(this._message);
  17:     }   
  18: }
  19:  
  20: // USO
  21:  
  22: var bt = new ClickButton("Ciao!!!", $get("myButton"));

Guardando questo pezzetto di codice può sembrare tutto in perfetto ordine ma in realtà se provate a farlo girare in un qualsiasi browser il risultato sarà una message box con scritto "undefined" anzichè come ci si aspetterebbe un caloroso "Ciao!!!". Come dicevo pocanzi il problema è che Javascript non ha la nozione di contesto di una istanza, pur se noi passiamo ad essa un riferimento al metodo _onclick() in realtà stiamo semplicemente copiando in una variabile il corpo della funzione stessa che sarà completamente slegata dal contesto dell'istanza in cui si trova. Per ovviare a questo problema occorre passare all'evento non solo la funzione _onclick() ma anche un riferimento a "this" così che il runtime sia in grado di eseguirla correttamente. Per fare questo nella Microsoft AJAX Library esiste un metodo della classe statica "Function" che serve proprio a generare correttamente questa chiamata. Ecco come modificare la classe di cui sopra per farla funzionare:

   1: ClickButton = function(message, element)
   2: {
   3:     this._message = message;
   4:     element.onclick = Function.createDelegate(this, this._onclick);
   5: }
   6: ClickButton.prototype = 
   7: {
   8:     _onclick : function()
   9:     {
  10:         alert(this._message);
  11:     }   
  12: }

Con createDelegate() verrà generata una funzione che ha appunto lacapacità di invocare il metodo richiesto nel contesto della classe di appartenenza. La definizione del metodo createDelegate() pur con questo nome un po' altisonante altro non è che uno stratagemma per memorizzare il riferimento a this:

   1: Function.createDelegate = function(instance, method)
   2: {
   3:     return function()
   4:     {
   5:         return method.apply(instance, arguments);
   6:     }
   7: }

Chi ha provato ad usare Silverlight si sarà già imbattuto in qualcosa di analogo. In vari siti nella rete si trova la definizione di un metodo che è del tutto analogo a Function.createDelegate(). Ecco quindi un buon motivo per referenziare la Microsoft AJAX Library anche quando si lavora con Silverlight. Attenzione però che esiste almeno un evento che in Silverlight non potrete gestire con questo metodo. Si tratta dell'evento onResize dell'oggetto Host che tipicamente viene usato per riposizionare il layout al resize del controllo host.


Commenti (1) -

# | di .NET e di altre Amenit | 14.09.2007 - 10.04

Una classe Javascript per le pagine... ovvero, come organizzare meglio i propri script

Aggiungi Commento