Sei alla ricerca delle linee guida per creare nuove funzioni JavaScript in Icinga Web 2? Attraverso quest’articolo cercherò di spiegarti la struttura che la tua funzione dovrà avere per essere correttamente compilata da Icinga Web 2.
Se hai già analizzato il codice, ti sarai accorto che le funzioni JavaScript di Icinga sono caratterizzate dalla tipica struttura dei JavaScript Design Patterns.
I design pattern sono avanzate soluzioni per lo sviluppo object-oriented, che viene comunemente utilizzato nello sviluppo di soluzioni software. Il cruccio di ogni sviluppatore è quello di produrre codice che sia facilmente interpretabile, ri-utilizzabile e mantenibile e i design pattern risultano un ottimo strumento per risolvere questo problema: lo sviluppo basato su design pattern prevede la creazione di funzioni attraverso l’ausilio di blocchi base di codice, che non sono altro che le soluzioni di problemi/funzionalità che ricorrono frequentemente nel software, che possono essere riutilizzati all’interno di funzioni, con struttura piu complessa.
JavaScript è un linguaggio “ad oggetti” basato sul concetto di prototipo a differenza della maggior parte dei linguaggi di programmazione basati sul concetto di classe. A differenza dei linguaggi ad oggetti tradizionali, dove una classe definisce tutte le caratteristiche che può avere un oggetto, in JavaScript è possibile definire alcune caratteristiche per un oggetto ed arricchirlo di nuove proprietà e metodi, facendoli ereditare da altri oggetti sulla base di un concetto noto come “prototypical inheritance“, che in parole povere significa che un “data type” può essere creato definendo quella che viene chiamata funzione costruttore. Vediamo come, quanto finora descritto viene applicato in Icinga:
// Definition of constructor function var EventListener = function (icinga) { this.icinga = icinga; this.handlers = []; }; // Adding an "Event Handler" to the new function EventListener EventListener.prototype.on = function(evt, cond, fn, scope) { if (typeof cond === 'function') { scope = fn; fn = cond; cond = 'body'; } this.icinga.logger.debug('on: ' evt '(' cond ')'); this.handlers.push({ evt: evt, cond: cond, fn: fn, scope: scope }); };
Per evitare quindi di dover ridefinire da zero oggetti che hanno la stessa struttura possiamo ricorrere ad un costruttore. Un costruttore non è altro che una normale funzione JavaScript invocata mediante l’operatore new. Questa funzione definisce le proprietà del nostro oggetto assegnandole a se stessa (this
) in qualità di oggetto ed impostando i valori predefiniti.
La nuova proprietà “on” non è direttamente agganciata all’oggetto EventListener, ma accessibile come se fosse una sua proprietà. Questo grazie al meccanismo di prototyping che sta alla base dell’ereditarietà nella programmazione ad oggetti in JavaScript. In JavaScript, il prototipo di un oggetto è una sorta di riferimento ad un altro oggetto. Il meccanismo su cui si basa la prototypal inheritance è abbastanza semplice: se una proprietà non si trova in un oggetto viene cercata nel suo prototipo. Il prototipo di un oggetto può a sua volta avere un altro prototipo. In questo caso la ricerca di una proprietà o di un metodo risale la catena dei prototipi fino ad arrivare all’oggetto Object
, il prototipo base di tutti gli oggetti.
var ApplicationState = function (icinga) { Icinga.EventListener.call(this, icinga); this.on('rendered', this.onRendered, this); this.icinga = icinga; }; ApplicationState.prototype = new Icinga.EventListener();
Come si nota dall’esempio, anche in Icinga delle volte viene utilizzata l’ereditarietà multilivello degli oggetti. Questo succede (come nel caso sopra riportato) quando si vuole creare un oggetto che eredita da un altro costrutture funzioni e proprietà: è un ottimo metodo per il riutilizzo delle funzioni già esistenti.
Dopo questa breve introduzione alla programmaizone object oriented in JavaScript dovresti essere in grado di replicare il medesmo schema nelle tue nuove funzioni: nota che la scelta di questo tipo di pattern dà il meglio di sè in funzioni complesse, ma può essere utilizzata anche in algoritmi più semplici.