Il linguaggio Javascript - University of L`Aquila
Transcript
Il linguaggio Javascript - University of L`Aquila
Javascript Dispense per il corso di Ingegneria del Web Revisione 05/11 Giuseppe Della Penna ([email protected]) Dipartimento di Informatica Università degli studi dell'Aquila Corso di Ingegneria del Web Javascript: Tool di sviluppo Il Linguaggio Javascript Javascript è un linguaggio di scripting, cioè è progettato per lavorare all'interno di un host, ad esempio un browser, per automatizzarne e gestirne alcune funzionalità. Nonostante il nome, non ha nulla a che vedere con Java, ma è invece l'implementazione più riuscita e diffusa dello standard ECMAScript (http://www.ecmainternational.org/publications/standards/Ecma-262.htm), dovuta inizialmente a Netscape. Da notare che la piattaforma Microsoft possiede un proprio linguaggio di scripting, noto come JScript, che è anch'esso un'implementazione di ECMAScript. In ogni caso, Javascript è l'unico linguaggio di scripting supportato da tutti i browser moderni. Imparare a usare bene Javascript è complesso, in quanto il linguaggio è molto particolare in alcuni suoi aspetti. Tuttavia, la versatilità di Javascript lo rende uno strumento unico ed eccezionale per realizzare applicazioni web di grande impatto. Basti pensare alle applicazioni realizzate da Google Apps, che fanno larghissimo uso di questo linguaggio per programmare sistemi la cui logica lato client li rende molto simili alle normali applicazioni desktop. L'avvento della tecnologia AJAX, poi, ha reso Javascript ancor più famoso. L'utilità di Javascript non si ferma alla programmazione del browser: infatti, Javascript può essere incorporato in qualsiasi applicazione come linguaggio di scripting, ad esempio è possibile incorporare Javascript in qualsiasi applicazione Java usando la libreria Rhino (http://www.mozilla.org/rhino/). Come ultimo esempio delle potenzialità di Javascript... notate che quasi tutte le funzionalità estese di Firefox sono programmate con Javascript stesso! Un unico avvertimento va fatto, prima di cominciare a programmare: resistete sempre alla tentazione di inserire script inutili nella vostra pagina, e di realizzare con Javascript funzionalità che potrebbero essere ottenute con un uso accorto di HTML e CSS (ad esempio molti tipi di menu). Javascript può essere un enorme ostacolo all'accessibilità piena della vostra applicazione, sopratutto per i soliti problemi di compatibilità crossbrowser, che in Javascript sono sempre presenti, e che possono rendere un'applicazione totalmente basata sugli script inservibile sul prossimo browser (per non parlare dei precedenti!). Prevedete sempre la possibilità che le vostre pagine siano aperte da un browser in cui Javascript non funziona o è disattivato, e verificate se in questo caso i contenuti restano comunque accessibili! Tool di sviluppo Javascript si può scrivere con qualsiasi buon editor di testo. Netbeans, che usiamo già per lo sviluppo Java, dispone di un ottimo editor assistito per questo linguaggio. Tuttavia il tool indispensabile per lo sviluppo e il debugging di Javascript è l'add-on di Firefox "Firebug" (http://getfirebug.com/). Installatelo, e avrete una console da cui • • • provare codice Javascript generico eseguire interattivamente Javascript sulle vostre pagine visionare gli errori ed eseguire debugging degli script presenti sulle pagine web Revisione 05/11 Pagina 2/27 Corso di Ingegneria del Web Javascript: Usare Javascript nelle pagine HTML e inoltre • • • analizzare la struttura delle pagine web analizzare e modificare gli stili associati agli elementi delle pagine web analizzare e ottimizzare il traffico di rete effettuato dalla vostra applicazione web Date un'occhiata a http://getfirebug.com/doc/enablement/enablement1.4.html per capire come attivare Firebug. Leggete quindi http://getfirebug.com/cl.html per capire meglio come funziona la linea di comando immediata Javascript, da cui provare i vostri esempi. Infine, leggete http://getfirebug.com/js.html per capire come Firebug può aiutarvi in generale a gestire i vostri script. Usare Firebug è semplice: una volta installato, cliccate sul piccolo simbolo che si aggiungerà sulla statusbar del browser per aprire la console. Da qui potrete vedere tutti i messaggi di errore generati dai vostri script. Se volte eseguire del codice di esempio, come quello proposto in queste lezioni, potete scriverlo in maniera diretta nella riga di input presente in basso, e vederne i risultati direttamente sulla console. Usare Javascript nelle pagine HTML Per incorporare uno script in una pagina HTML, si utilizza il tag <script> con attributo type impostato al valore "text/javascript". Questo tag (si veda la documentazione di HTML) può essere inserito sia nella <head> del documento, sia all'interno del <body>. Discuteremo più avanti il significato di questi due approcci, tuttavia, quando possibile, è consigliabile caricare gli script sempre nella <head>. Visto che il codice Javascript contiene spesso caratteri HTML riservati come ">" o "&", si rende necessario usare la direttiva XML CDATA per isolare lo script indicando che il suo contenuto è testo semplice. Purtroppo, però, alcuni browser, anche moderni, non comprendono la direttiva CDATA, ovvero assumono un CDATA implicito all'interno del tag <script>. Per adattarsi a tutte queste varianti, il codice corretto per l'inserimento di uno script all'interno di una pagina web è il seguente <script type="text/javascript> //<![CDATA[ ... //]]> </script> In questo modo, gli script che non comprendono il significato speciale di CDATA lo ignoreranno in quanto inserito in un commento Javascript (righe che iniziano con "//"), mentre gli altri lo interpreteranno correttamente. Da notare che, per compatibilità con i browser più datati, il codice all'interno dello script viene a volte posto anche tra indicatori di commento HTML (<!-- -->). Questa procedura è ormai inutile, in quanto tutti i browser delle ultime generazioni conoscono il tag script, anche se a volte non sono in grado di interpretarlo. E' anche possibile caricare script esterni, lasciando il tag <script> vuoto e specificando la URI dello script tramite l'attributo src. Anche in questo caso,per compatibilità con alcuni browser, è opportuno non scrivere mai il tag <script> vuoto nella forma abbreviata, ma inserire sempre il tag di chiusura in maniera esplicita, come nell'esempio che segue <script type="text/javascript" src="..."/> <!-- NON SICURO --> <script type="text/javascript" src="..."></script> <!-- SICURO --> Revisione 05/11 Pagina 3/27 Corso di Ingegneria del Web Javascript: Tipi di dato Javascript Si possono inserire e importare più script diversi all'interno dello stesso documento, usando più tag <script>. Esistono inoltre alcuni attributi HTML in cui si può incorporare del codice, cioè • • gli attributi per la gestione degli eventi, come onclick, possono contenere frammenti di codice (ma non dichiarazioni), da eseguire al verificarsi dell'evento. l'attributo href del tag <a> può fare riferimento a una funzione Javascript con la sintassi: "Javascript:nomefunzione(argomenti)". In questo caso, il click del link eseguirà la chiamata alla funzione. Come regola generale, gli script andrebbero sempre posti in file esterni e importati usando la seconda forma dell'elemento <script>. Inoltre, l'assegnazione di codice agli attributi degli elementi HTML dovrebbe essere realizzata dal codice stesso, e non apparire tra il markup, come vedremo più avanti. Infine, l'uso di Javascript nell'attributo href del tag <a> andrebbe limitato, utilizzando in sua sostituzione l'evento onclick dello stesso elemento. Tutto questo per mantenere una rigorosa separazione tra codice HTML e Javascript. Esecuzione degli script nelle pagine HTML Come già accennato, il tag <script> può apparire sia nella <head>, dove viene normalmente posto per la maggior parte degli script, sia in qualunque punto del <body>. Ogni script può utilizzare liberamente funzioni e variabili dichiarate in altri script inseriti nella stessa pagina. Nello scegliere il punto in cui inserire uno script, bisogna tener presente che tutte le funzioni e le variabili dichiarate negli script diventano disponibili (quindi possono essere usate e chiamate) non appena il parser analizza il punto della pagina in cui sono dichiarate. Inoltre, se uno script contiene codice immediato, cioè scritto al di fuori di funzioni, questo viene eseguito non appena il parser analizza il punto della pagina in cui il codice compare. Inserire uno script in un certo punto del <body> serve quindi solo ad assicurarsi che esso venga valutato solo dopo che l'elemento HTML a cui si riferisce è stato caricato (assumendo quindi di porre lo script dopo l'elemento in questione). Tuttavia, invece che utilizzare questa tecnica, è spesso più pratico (nonché più "pulito", mantenendo HTML e Javscript maggiormente separati) eseguire qualsiasi script quando tutta la pagina HTML è stata caricata, e quindi tutto il contenuto del documento è disponibile per analisi e manipolazioni. Questo può essere realizzato utilizzando l'evento onload dell'oggetto window. Chiariremo più tardi il significato di questo codice, tuttavia per ora consideriamo il frammento che segue window.onload = function() { ... } inserendo questo frammento tra gli script nella <head> della nostra pagina, ci assicureremo che tutto il codice all'interno delle graffe sia eseguito quando il documento HTML è completamente caricato. Questo è quindi il luogo adatto per eseguire qualsiasi inizializzazione degli script contenuti nella pagina, modifica iniziale al documento stesso, ecc. Tipi di dato Javascript Javascript gestisce cinque diversi tipi di dato, che illustriamo qui di seguito. Da notare che non esiste tra i tipi base l'array, in quanto viene trattato come un particolare oggetto, e verrà illustrato più avanti. Revisione 05/11 Pagina 4/27 Corso di Ingegneria del Web Javascript: Variabili e Dichiarazione Numeri Non c'è distinzione tra interi e reali. Sono costanti numeriche tutte le espressioni che rappresentano un numero valido, con e senza virgola. Le funzioni parseInt e parseFloat possono essere usate per convertire stringhe in numeri. Stringhe Le stringhe sono particolari oggetti Javascript. Possono essere create implicitamente, attraverso una costante di tipo stringa, o esplicitamente tramite il costruttore dell'oggetto String. Sono costanti di tipo stringa i valori racchiusi tra virgolette (singole o doppie). Tutti i tipi di dato vengono automaticamente convertiti in stringa quando si trovano in un contesto che richieda la presenza di una stringa (ad esempio, nelle istruzioni di stampa, o quando vengono concatenati a un'altra stringa). Oggetti Gli oggetti sono un tipo di dato molto comune in Javascript, che tratteremo in dettaglio più avanti. Le variabili di tipo oggetto contengono in effetti dei riferimenti ad oggetti (come in Java). Più variabili possono quindi fare riferimento allo stesso oggetto. In Javascript il tipo oggetto è molto diverso da quello usato nei linguaggi di programmazione object-oriented. Non bisogna quindi pensare gli oggetti Javascript come veri oggetti, ma piuttosto come "contenitori di informazioni e funzioni" con un comportamento simile a quello dei veri oggetti. Null Il tipo nullo ha un unico valore, null. Booleani Le costanti boooeane sono true e false. Tutti gli altri tipi di dato possono venire convertiti, se necessario, in valori booleani in base alle regole che seguono: • • • • Se l'espressione ha un valore numerico diverso da zero è true. Se l'espressione ha un valore stringa non vuoto è true. Se l'espressione ha un valore oggetto è true. In tutti gli altri casi (numerico zero, stringa vuota, valore undefined o null) l'espressione è false. Variabili e Dichiarazione Le variabili Javascript sono identificate da sequenze alfanumeriche il cui primo carattere deve essere alfabetico. Le variabili non hanno un tipo: questo viene dedotto automaticamente dal valore assegnato alla variabile stessa (ad esempio come in PHP), e può cambiare nel tempo. Revisione 05/11 Pagina 5/27 Corso di Ingegneria del Web Javascript: Operatori Prima di utilizzare una variabile, è opportuno dichiararla utilizzando il costrutto var. Come in tutti i linguaggi di programmazione, anche in Javascript esiste il concetto di visibilità di una variabile. Le variabili globali sono visibili a tutto il codice, mentre quelle locali sono visibili solo all'interno del blocco (di codice) in cui sono dichiarate. Sono variabili globali tutte quelle dichiarate al di fuori delle funzioni. Inoltre, se si assegna un valore a una variabile non dichiarata, Javascript la crea automaticamente come variabile globale, indipendentemente dal codice all'interno del quale viene utilizzata: per questo motivo è fortemente sconsigliato usare variabili senza dichiararle. Il valore iniziale di una variabile è sempre lo speciale valore undefined. E' tuttavia possibile inizializzare la variabile inserendo un'assegnazione all'interno della sua dichiarazione. Vediamo qualche esempio. /* DICHIARAZIONI GLOBALI */ var x; //x è una variabile globale di tipo indefinito e con valore undefined var o = new Object(); //o è una variabile globale di tipo Object (vuota) var s = "pluto"; //s è una variabile globale di tipo String con valore "pluto" var n = 3; //n è una variabile globale di tipo Number con valore 3 m = n; //m è una variabile globale di tipo Number con valore 3 t = "paperino"; //t è una variabile globale di tipo String con valore "paperino" u = v; //u è una variabile globale con valore undefined (in quanto v non è a sua volta definita) var b = (3>2) //b è una variabile globale Boolean con valore true function f() {var x; } /* la variabile x è una variabile locale alla funzione f, e sovrascrive (rende invisibile) l'altra variabile x dichiarata globalmente */ Se si cerca di leggere il valore di una variabile mai dichiarata né assegnata, questo risulterà undefined. Da notare che la lettura di una variabile non ne provoca la dichiarazione automatica, come invece avviene con l'assegnamento. Operatori Gli operatori si applicano a variabili e costanti dei tipi di base Javascript per eseguire operazioni elementari, come quelle aritmetiche. Vediamo ora una lista commentata dei principali operatori presenti nel linguaggio. • • • + (somma) Oltre che a numeri, può essere applicata a stringhe, nel qual caso diventa l'operatore di concatenazione. Se in una somma almeno un operando è una stringa, gli altri vengono convertiti anch'essi in stringhe. - (differenza), / (quoziente), * (prodotto), % (modulo) = assegnamento, +=, -= (assegnamento con somma/differenza) Gli assegnamenti con somma/differenza hanno la stessa semantica dei loro corrispondenti in C o Java. L'assegnamento con somma può essere applicato anche a stringhe, esattamente come l'operatore di somma. Revisione 05/11 Pagina 6/27 Corso di Ingegneria del Web • • • • • • • • Javascript: Operatori ++ (incremento), -- (decremento) Hanno la stessa semantica dei loro corrispondenti in C o Java. >> (shift right), << (shift left), & (and), | (or), ^ (xor), ~ (not) Effettuano operazioni bit-a-bit tra operandi numerici (non si tratta di operatori logici: non confondeteli con i seguenti!) && (and), || (or), ! (not) Effettuano le comuni operazioni booleane and, or e not. in (appartenenza) Può essere applicato a oggetti o array (operando destro), per controllare se contengono se la proprietà o l'indice dati (operando sinistro). Si vedano gli esempi a seguire. > (maggiore), < (minore), >= (maggiore o uguale), <= (minore o uguale), == (uguale), != ( diverso) Funzioni di confronto tra valori numerici. Funzionano anche con le stringhe, per le quali si considera l'ordinamento lessicografico. typeof(...) (controllo tipo) Restituisce una stringa contenente il nome del tipo del suo argomento. Utile per capire che tipo ha dedotto Javascript per una certa espressione, o per verificare che un argomento sia del tipo giusto. void(...) (statement nullo) Esegue il codice passato come argomento, ma non restituisce l'eventuale valore di ritorno. Usato solo in casi molto particolari (si veda l'esempio a seguire). eval(...) (valutazione script) Esegue lo script passato nell'argomento stringa e restituisce il suo valore. Utilissimo per costruire dinamicamente un'espressione Javascript sotto forma di stringa e poi eseguirla. Vediamo qualche esempio che coinvolga questi operatori. var s = "tre " + 2; //s è la stringa "tre 2" s += "uno"; //s è la stringa "tre 2 uno" s > "ciao"; //l'espressione vale true, in quanto il valore di s è lessicograficamente successivo a "ciao" typeof(s); //restituisce "string" var o = {pippo: 1}; //questa espressione (che descriveremo più avanti) crea un oggetto con una proprietà "pippo" impostata a 1 ("pippo" in o); //l'espressione vale true, in quanto pippo è una proprietà di o void(0); //statement nullo void(f(x)); //esegue f(x) ed ignora il suo valore di ritorno eval("f(x)"); //esegue lo script, chiamando f(x) e restituendo il valore di ritorno della chiamata eval("3+1"); //restituisce 4 eval("var s = 1"); //dichiara globalmente la variabile s e le assegna il valore 1. Accade a volte di dover disabilitare l'azione di default di un collegamento <a>, ad esempio perchè al suo posto vogliamo semplicemente chiamare uno script dichiarato tramite l'attributo onclick Revisione 05/11 Pagina 7/27 Corso di Ingegneria del Web Javascript: Strutture di Controllo: Esecuzione condizionale dello stesso elemento. Per ottenere un collegamento "nullo" si usa spesso il valore "#" come href del tag <a>, tuttavia questo, soprattutto con certi browser, può comportare a uno scroll verticale della pagina. Il valore di href più corretto per disabilitare un link è invece la stringa "Javascript:void(0)". Con Javascript si può realizzare anche codice polimorfo, che accetta e gestisce (magari in maniera diversa) tipi diversi per le stesse variabili. E' sufficiente infatti testare con l'operatore typeof il tipo di queste variabili e procedere di conseguenza. /* vogliamo poter passare a un codice un array di numeri. Tuttavia, se l'array contiene un solo numero, è ammissibile che l'utente lo passi direttamente */ if (typeof(v)=='number') v = [v]; //converte v in un array da un elemento //il codice che segue sa gestire solo array di numeri Strutture di Controllo: Esecuzione condizionale Javascript dispone del costrutto if con la stessa sintassi di Java, cioè if (guardia) { ... } else { ... } Ad esempio: var s = "valore"; var b = 0; //costrutto if con condizione di tipo "misto" if (s && b > 0) { s = "ok" } else { b = 1; } La guardia può essere qualsiasi espressione valida, e viene convertita in valore booleano secondo le regole viste sopra. Questo metodo di conversione rende possibili alcune utilissime operazioni come la seguente: if (v) {...} //esegue il codice solo se v è definita e ha un valore non nullo è possibile usare questo statement per eseguire condizionalmente del codice solo se una variabile o una funzione sono disponibili. Infatti, se un identificatore non è definito, il suo valore undefined è convertito in false, mentre in tutti gli altri casi diventerà true, a meno che la variabile non rappresenti una stringa vuota o un valore zero o nullo. Se si vuole comprendere anche queste situazioni, è più opportuno usare l'espressione di test che segue, che utilizza il costrutto typeof: if (typeof(v)!='undefined') {...} //esegue il codice solo se v è definita Poichè Javascript è un linguaggio estremamente estendibile, e le sue librerie di base sono soggette a variazioni anche notevoli tra le varie implementazioni, testare se una funzione esiste prima di usarla può essere un ottimo primo passo per realizzare script crossbrowser. Infatti, a seconda del browser, spesso le stesse informazioni sono accessibili tramite metodi o proprietà con nomi e sintassi differenti. Tuttavia, potendone testare la presenza nel modo appena visto, è molto semplice capire quali proprietà sono presenti su ciascuna piattaforma. Javascript dispone inoltre del costrutto switch con la stessa sintassi di Java, cioè Revisione 05/11 Pagina 8/27 Corso di Ingegneria del Web Javascript: Strutture di Controllo: Loops switch (espressione) { case valore1: ... case valore2: ... default: ... } L'espressione viene valutata e confrontata con i valori dei diversi case. Vengono quindi eseguite le istruzioni a partire dal primo case con lo stesso valore dell'espressione. Se nessun case è selezionato, vengono eseguite le istruzioni del default, se presenti. Se si desidera limitare l'esecuzione a un gruppo di istruzioni, è necessario introdurre la parola chiave break. Ecco un esempio: var s = "valore"; switch (s) { case "ok": ... break; //questo case finisce qui case "error": //questo case continua sul default ... default: ... } Strutture di Controllo: Loops Javascript dispone dei costrutti di ciclo for, while e do...while, con la stessa sintassi di Java: for (inizializzazione; condizione; aggiornamento) { corpo } while(condizione) { corpo } do { corpo } while(condizione) Nel ciclo for vengono eseguite le istruzioni di inizializzazione, quindi se la condizione è vera viene eseguito il corpo dell'istruzione e di seguito le istruzioni di aggiornamento. Se la condizione è ancora vera il ciclo continua. Nel ciclo while il corpo viene eseguito se e finché la condizione è vera. Nel ciclo do...while il corpo viene eseguito almeno una volta, in quanto la condizione è testata al termine della sua esecuzione. Nel corpo dei loop è possibile usare le parole chiave break e continue rispettivamente per interromperne l'esecuzione o per saltare direttamente al ciclo successivo. Vediamo qualche semplice esempio. var i = 0; //ciclo for standard for (i=0; i<10; ++i) { j=j+1; } Revisione 05/11 Pagina 9/27 Corso di Ingegneria del Web Javascript: Funzioni //ciclo while while(i>0) { i=i-1; } //ciclo do do { i=i-1; } while(i>0); Il loop for...in In Javascript esiste anche un'interessante variante del loop for, simile ai costrutti foreach che sono presenti in altri linguaggi. Questo tipo di loop permette di iterare tra tutti i membri di un oggetto (poiché i metodi sono membri con un particolare tipo, anche questi verranno elencati dal loop) o gli elementi di un array (che, come vedremo, per Javascript sono molto simili agli oggetti). La sintassi da usare un questo caso è for (p in oggetto) {corpo} La variabile di loop p viene aggiornata ad ogni iterazione con il nome o l'indice del successivo elemento estratto da oggetto, quindi viene eseguito il corpo. E' da sottolineare, quindi, che p non itera in effetti sui membri veri e propri, ma sulle loro chiavi di accesso. Per accedere al valore del membro corrispondente, sarà sufficiente scrivere oggetto[p] (sintassi array), come vedremo in dettaglio più avanti. var o = new Object(); //itera tra le proprietà dell'oggetto o for (p in o) { o[p]; //preleva il valore della proprietà indicizzata dal loop (e dovrebbe usarla...) } Funzioni Le funzioni sono viste da Javascript come particolari tipi di oggetto. Per questo, oltre ad essere chiamate, possono essere passate come argomento ad altre funzioni, copiate, assegnate ad identificatori diversi e persino modificate a tempo di esecuzione. Dichiarazione In Javascript è possibile creare nuove funzioni con le seguenti sintassi: • • • Dichiarazione di funzione: sintassi function nome(parametri) {corpo} Funzioni anonime: sintassi function(parametri) {corpo} Oggetti funzione: sintassi new Function("parametri", "corpo") Il nome di una funzione può essere qualsiasi nome valido per una variabile. I parametri della funzione, se presenti, sono dichiarati tramite una lista di nomi (di variabile) separati da virgole. Le parentesi dopo il nome della funzione vanno sempre inserite, anche se la lista dei parametri è Revisione 05/11 Pagina 10/27 Corso di Ingegneria del Web Javascript: Funzioni vuota. Infine, il corpo della funzione è costituito da una sequenza di istruzioni Javascript valide (ogni istruzione è separata dalla successiva da un punto e virgola.). Vediamo degli esempi: //funzione senza parametri, dichiarazione diretta function f(x,y) {...} //funzione anonima assegnata a una variabile var h1 = function(x,y) {...} //oggetto funzione assegnato a una variabile var h2 = new Function("x,y","..."); Nel primo caso, la dichiarazione è simile a quella di molti linguaggi di programmazione. Nel secondo caso la funzione viene creata in maniera anonima (infatti la parola chiave function non è seguita da alcun identificatore): per utilizzarla in qualche modo, quindi, è necessario passarla come parametro a un'altra funzione o assegnarla ad una variabile, come nell'esempio qui sopra. Infine, nel terzo caso, la lista dei parametri e il corpo sono passati sotto forma di stringa, quindi possono essere anche generati in maniera dinamica. Anche in questo caso è necessario assegnare a una variabile la funzione appena creata. Va notato che, una volta assegnata una funzione a una variabile, questa variabile può essere usata a tutti gli effetti come la funzione assegnatagli, anche se si tratta di una sua copia. Nel corpo della funzione, come di consueto, è possibile utilizzare i parametri tramite il nome delle variabili ad essi associate. Vediamo un altro esempio: //funzione con due parametri, dichiarazione diretta function g(a,b) { var c = a + b; return c; } Riferimento Le funzioni Javascript sono in realtà variabili con valore di tipo Function (che, a sua volta, è un particolare tipo di oggetto). Per fare riferimento a una funzione (ed esempio, per invocarla) è quindi sufficiente usare il suo nome, o un'espressione equivalente che abbia valore di tipo Function. Una volta ottenuto il riferimento a una funzione è possibile chiamarla, specificando eventualmente dei parametri, passarla come argomento ad un'altra funzione, assegnarla ad altre variabili (che diventeranno altri riferimenti alla stessa funzione), accedere a tutte le proprietà del corrispondente oggetto Function, per modificarla o ridefinirla, o infine verificare se è effettivamente definita, come si farebbe con qualsiasi variabile. Vedremo queste operazioni dal vivo man mano che ne faremo uso. function app(f,x) { return f(x); } //la funzione app applica il secondo parametro al primo app(function(y){ return y+1; },3); //restituisce 4 Revisione 05/11 Pagina 11/27 Corso di Ingegneria del Web Javascript: Funzioni Chiamata Per richiamare una funzione, si accoda la lista dei parametri, tra parentesi, a un'espressione che ha fa riferimento alla funzione stessa. Questa espressione, nella maggior parte dei casi, è il nome di una variabile o del membro di oggetto a cui è stata assegnata, esplicitamente o implicitamente, una funzione. Gli argomenti sono passati alla funzione come una lista di espressioni valide separate da virgole. Si ricordi che, anche se la funzione non ha parametri, è comunque necessario specificare le due parentesi dopo il nome. Vediamo qualche esempio //definiamo qualche funzione function f() { var i; }; var h1 = function(a) { return a+1; }; var h2 = new Function("a","return a+1;"); //esempi di chiamata f(); //ritorna undefined, in quanto la funzione non ha alcun return esplicito var r = h1(3); //r=4 var r2 = h2(4); //r=5 (function(x){return x+1;})(3); //ritorna 4 L'ultimo esempio mostra come si può invocare come funzione qualsiasi espressione che restituisca un valore di tipo Function: viene infatti definita una funzione anonima, alla quale viene subito applicato l'operatore di invocazione, cioè le due parentesi con la lista dei parametri. La funzione esisterà solo per il tempo della chiamata, non essendo associata ad alcun identificatore. E' possibile omettere uno o più parametri al termine della lista degli argomenti. In questo caso, tali parametri varranno undefined nel corpo della funzione, e sarà quindi possibile testarli ed attribuite eventualmente valori di dafault a ciascuno di essi: function f(x,y) { //in questo modo, è come se y avesse un valore dei default if (typeof(y)=='undefined') y=0; return x+y; } f(3,1); //ritorna 4 f(3); //ritorna 3 f(); //ritorna undenfined o NaN (Not a Number), in quanto x non ha un default e undefined+0=undefined Passaggio di Parametri Il passaggio dei parametri alle funzioni Javascript avviene in maniera diversa a seconda del tipo del parametro stesso: • I tipi booleano, stringa, numero e null sono passati per valore. Nella funzione, cioè, è presente una copia del valore usato come argomento. Cambiamenti locali alla funzione non influenzano il valore dell'argomento usato nella chiamata alla funzione stessa. Revisione 05/11 Pagina 12/27 Corso di Ingegneria del Web • Javascript: Closures Il tipo oggetto è passato per riferimento. La manipolazione del contenuto dell'oggetto all'interno della funzione si riflette sull'oggetto usato come argomento. Ritorno Le funzioni restituiscono il controllo al chiamante al termine del loro blocco di istruzioni. E' possibile restituire un valore al chiamante, in modo da poter usare la funzione in espressioni più complesse, utilizzando la consueta parola chiave return seguita da un'espressione valida. Se la funzione non esegue alcuna return, Javascript sottintende un "return undefined" implicito. Closures Le funzioni Javascript, tra le loro tante peculiarità, hanno anche quella di generare delle closures, una caratteristica molto utile nella pratica. Una closure (chiusura) è, tecnicamente, un'espressione (tipicamente una funzione) associata a un contesto che valorizza le sue variabili libere. Tutto il codice Javascript viene eseguito in un contesto, compreso quello globale. Tale contesto viene utilizzato per dedurre il valore e lo stato di definizione di tutti gli identificatori. In particolare, ogni esecuzione di una funzione ha un contesto associato, che contiene i valori delle variabili globali e delle sue variabili locali. Una closure si crea proprio a partire da una funzione, quando quest'ultima restituisce come valore di ritorno una nuova funzione creata dinamicamente (cioè tramite gli ultimi due costrutti di definizione visti in precedenza). La closure, cioè la funzione creata all'interno di un'altra funzione e poi restituita, mantiene il contesto di esecuzione della funzione che l'ha creata. Questo significa che il contesto di ciascuna chiamata della funzione "generatrice" non viene distrutto all'uscita della funzione (cioè il valore delle sue variabili locali non viene dimenticato), come avviene in generale, ma conservato in memoria. La funzione closure potrà fare riferimento (in lettura e scrittura) ai parametri e alle variabili dichiarate nel contesto della funzione che l'ha creata. Inoltre, poiché ogni chiamata a funzione ha un suo contesto distinto, i valori "visti" dalla closure non saranno influenzati da successive chiamate alla stessa funzione generatrice. Vediamo un esempio: function generatrice(x) { var y = 3; return function(z) { return x+y+z; }; } var f1 var f2 f1(0); f2(0); = generatrice(1); = generatrice(2); //restituisce 1+3+0 = 4 //restituisce 2+3+0 = 5 Nell'esempio, la funzione generatrice, ad ogni chiamata, restituisce una funzione che somma il suo argomento al valore dell'argomento x e della variabile locale y della sua funzione generatrice. Se chiamiamo due volte generatrice, le funzioni risultanti saranno distinte, in quanto ottenute come closure da due invocazioni diverse della stessa funzione. L'esempio mostra infatti come le funzioni f1 ed f2 si comportino in maniera diversa, sebbene siano entrambe generate dalla stessa funzione generatrice. Revisione 05/11 Pagina 13/27 Corso di Ingegneria del Web Javascript: Closures Un uso comune per le closure è fornire alcuni parametri a una funzione che verrà eseguita in seguito: è il caso, ad esempio, delle funzioni passate come argomento a setTimeout (che verrà illustrata più avanti). In pratica, quando passiamo una funzione come argomento, o la assegniamo a una variabile, non possiamo fornirle parametri (inserire parametri verrebbe interpretato da Javascript come una chiamata alla funzione): al posto della funzione possiamo però usare una wrapper closure che la chiama con i parametri desiderati. Vediamo un esempio chiarificatore. Definiamo una funzione f con un argomento, e poi vogliamo assegnare alla variabile p la funzione f(3). Notare che non vogliamo assegnare a p il valore ritornato dalla chiamata f(3), ma il codice della funzione f applicato al parametro 3, in maniera che vanga ricalcolato ogni volta che invochiamo p. Infatti, poiché f usa al suo interno una variabile globale v, il suo valore di ritorno non dipende solo dagli argomenti. var v; //globale function f(x) { return x+v; //NOTA: il valore di ritorno dipende anche dalla variabile globale v } //vorremmo assegnare alla variabile p la FUNZIONE che restituisce f(3), cioè 3+v var p = f(3); p(); //ERRATO: p non punta a una funzione, ma al valore calcolato come f(3) al momento dell'assegnazione p = f; p(); //ERRATO: p è un riferimento a f, per cui necessita di un parametro (avremmo dovuto scrivere p(3)) //la funzione che segue genera e restituisce una nuova funzione che chiama f sul suo argomento y function closureGenF(y) { return function() { return f(y); } } p = closureGenF(3); p(); //CORRETTO: verrà chiamata f(3)! La soluzione è stata trovata con la generazione di una closure. Infatti, la funzione closureGenF restituisce una funzione che chiama f sul parametro y passato a closureGenF stessa. La funzione ritornata sarà una closure, e per questo sarà legata al particolare valore di y per cui è stata generata. l'espressione closureGenF(3) crea quindi la funzione che volevamo assegnare a p. Un altro esempio, che usa caratteristiche di Javascript che saranno più chiare di seguito, ma mostra un'applicazione realistica delle closure, è il seguente. //dobbiamo assegnare un handler per l'evento onclick a un elemento del DOM HTML htmlelement.onclick = f; /*NOTA: anche qui non si possono passare parametri! Accade spesso che si usino piccole varianti dello stesso so handler per elementi diversi tali varianti sono semplici da costruire a tempo di esecuzione (e spesso è necessario). Ad esempio, vogliamo associare a certi elementi degli handler che colorino in rosso un elemento ad essi associato Revisione 05/11 Pagina 14/27 Corso di Ingegneria del Web Javascript: Oggetti Javascript quando vengono cliccati.*/ function clickHandler(oToHighlight) { return function(e) { oToHighlight.style.backgroundColor="red"; } } element1.onclick = clickHandler(linkedelement1); element2.onclick = clickHandler(linkedelement2); In questo caso, ogni chiamata a clickHandler, a cui viene passato il riferimento a un certo elemento, ritorna una funzione che, se chiamata, colora di rosso l'elemento dato. Oggetti Javascript Javascript non è un linguaggio object oriented, e il suo concetto di oggetto è molto più simile a quello di un array associativo. In Javascript, infatti, non si possono definire classi, ma solo speciali funzioni dette costruttori che creano oggetti aventi determinati membri. Il nome della funzione costruttore è considerato il nome della classe dell'oggetto. Tra l'altro, gli oggetti Javascript possono contenere metodi, ma questi ultimi sono considerati anch'essi valori, in quanto non sono altro che oggetti di classe Function. Non esiste vera ereditarietà negli oggetti Javascript, e non è possibile dichiarare delle gerarchie. Tuttavia, Javascript contiene una classe base predefinita, chiamata Object. Gli oggetti Javascript si creano utilizzando l'operatore new applicato alla loro funzione costruttore. La funzione costruttore per l'oggetto base Object è predefinita in Javascript e può essere utilizzata per creare un "oggetto vuoto": var o = new Object(); Un metodo di creazione alternativo consiste nell'utilizzo del costrutto "parentesi graffe", che crea un oggetto in maniera implicita e lo riempie con una serie di proprietà e metodi inizializzati. La sintassi prevede una serie di coppie “nome” : valore, separate da virgole e inserite tra parentesi graffe. I nomi devono essere identificatori validi, mentre i valori possono essere espressioni qualsiasi. Ad esempio: var o = { “p” : “a”, “x” : 2, “f” : function() {return 0;}} L'esempio appena esposto crea un oggetto (assegnandolo poi alla variabile o) contenente una proprietà p con valore "a" (stringa), una proprietà x con valore 2 (numero) e un metodo f il cui codice è quello descritto nell'esempio sotto forma di funzione anonima. Le Proprietà Le proprietà di un oggetto Javascript possono contenere valori di qualsiasi tipo (comprese delle funzioni!). Per accedere a una proprietà, si possono usare due sintassi: • • Sintassi "a oggetti": oggetto.proprietà Sintassi "array": oggetto["proprietà"] Revisione 05/11 Pagina 15/27 Corso di Ingegneria del Web Javascript: Oggetti Javascript Ricordiamo che è disponibile anche lo speciale costrutto for...in per iterare tra le proprietà di un oggetto, e che è possibile verificare se un oggetto ha una determinata proprietà con l'espressione booleana proprietà in oggetto. var o = {“pippo”: “ciao”, “pluto”: 3}; //creazione implicita di un oggetto v = o["pluto"]; //equivalente a v = o2.pluto //accediamo a una proprietà il cui nome è calcolato, e solo se questa esiste var nome = "pippo"; if (nome in o) v=o2[nome]; //iteriamo su tutte le prorietà di o for(p in o) { ... } //p varrà, nell'ordine, pippo e poi pluto Se si tenta di leggere il valore di una proprietà non definita in un oggetto, si ottiene il valore undefined (come per ogni variabile non assegnata). Tuttavia, è possibile aggiungere dinamicamente proprietà agli oggetti semplicemente assegnando loro un valore. In pratica, Javascript crea automaticamente nuovi membri quando li si assegna, esattamente come crea nuove variabili globali. Tuttavia, si ricordi che non è possibile aggiungere proprietà a variabili che non siano di tipo oggetto. var o = new Object(); var v = o.pippo; //v è undefined, in quanto o non ha questa proprietà o.pluto = 3; //adesso o ha una proprietà "pluto", di tipo Number, con valore 3 v = o.pluto; //adesso v è una variabile di tipo Number e vale 3 v.paperino = "ciao"; //è un errore: una variabile può accettare l'aggiunta di proprietà solo se è di tipo oggetto! La possibilità di aggiungere proprietà in maniera dinamica è spesso utilizzata anche per uniformare l'interfaccia di oggetti di sistema, che i browser forniscono sotto forme differenti. Infatti è possibile aggiungere nuove proprietà anche agli oggetti restituiti da particolari funzioni del browser, magari deducendole da altre proprietà dell'oggetto stesso, in modo da dotarli di una serie standard di membri. Vediamo un esempio avanzato, che chiariremo più avanti nelle lezioni sul DOM HTML. /*l'oggetto event, passato quando browser intercetta la pressione di un tasto, contiene il codice del tasto premuto come valore di una proprietà il cui nome varia a seconda del browser stesso. con lo statement che segue, ci assicuriamo che esista sempre una proprietà keyCode con questo valore nell'oggetto event */ event.keyCode= (event.keyCode)? event.keyCode: ((event.charCode)? event.charCode: event.which); I Metodi I metodi di un oggetto Javascript sono semplicemente proprietà di tipo Function. Per accedere a un metodo si possono usare le stesse sintassi viste per le proprietà. Per chiamare un metodo basta accodare la lista dei parametri, tra parentesi, all'espressione di accesso al metodo. I metodi possono essere aggiunti in qualsiasi momento a un oggetto, esattamente come le proprietà. Per aggiungere un metodo a un oggetto, è sufficiente creare una proprietà col nome del metodo ed Revisione 05/11 Pagina 16/27 Corso di Ingegneria del Web Javascript: Oggetti Javascript assegnarvi una funzione già definita, oppure una funzione anonima, come negli esempi che seguono: var o = {}; //metodo alternativo a new Object per creare un oggetto vuoto o.f = function(x) { return x+1; }; //creazione metodo da funzione anonima function zero(y) {return 0;} o.g = zero; //creazione metodo a partire da funzione già definita o["h"] = zero; //il metodo h è lo stesso assegnato a g o.f; //restituisce un oggetto Function o.f(3); //ritorna 4 o["g"](4); //ritorna 0 I metodi, per far riferimento alle proprietà e ad altri metodi dell'oggetto in cui sono definiti, devono utilizzare la parola chiave this, come negli esempi che seguono. Va sottolineato che omettendo this, Javascript cercherà le variabili col nome dato all'interno del metodo (variabili locali) o tra le variabili globali, e non tra le proprietà dell'oggetto! var o = {“pippo”: “ciao”, “pluto”: 3, “metodo3”: function(x) {return pluto+x;}} o.metodo3(1); //restituisce undefined o NaN, in quanto pluto non è una variabile locale o globale a metodo3! var o2 = {“pippo”: “ciao”, “pluto”: 3, “metodo3”: function(x) {return this.pluto+x;}} o.metodo3(1); //restituisce 4; Funzioni Costruttore Una funzione costruttore è un tipo speciale di funzione all'interno della quale si utilizza la parola chiave this esclusivamente per assegnare proprietà di un (nuovo) oggetto. Una funzione costruttore, inoltre, non deve ritornare alcun valore. Le funzioni costruttore devono essere usate come argomento per l'operatore new, esattamente come i nomi degli oggetti standard di Javascript, e non dovrebbero mai essere richiamate direttamente (come normali funzioni). Quando si usa un costruttore con new, Javascript crea un oggetto vuoto derivato da Object ed applica ad esso la funzione costrutture, al cui interno this punta al nuovo oggetto: in questo modo, il costruttore può popolare l'oggetto, aggiungendo proprietà e metodi. Vediamo un esempio. //myObject è una funzione costruttore function myObject(a) { this.v = a+1; this.w = 0; this.m = function(x) {return this.v+x;} } /* L'oggetto o avrà due proprietà (v e w), una delle quali inizializzata tramite il parametro della funzione costruttore, e un metodo (m) che restituisce il valore della proprietà v sommata al suo argomento */ var o = new myObject(2); o.m(3); //ritorna 6; o.w = 7; //assegna un nuovo valore a una proprietà di o Revisione 05/11 Pagina 17/27 Corso di Ingegneria del Web Javascript: Oggetti Predefiniti Javascript //si possono sempre aggiungere altri membri all'istanza dell'oggetto: o.getW = function() {return this.w;} Si ricordi sempre che i metodi inseriti in un oggetto, per fare riferimento alle proprietà dell'oggetto stesso, devono riferirvisi attraverso this. Oggetti Predefiniti Javascript Le librerie standard di Javascript mettono a disposizione una serie di oggetti predefiniti, che possono essere di enorme aiuto alla programmazione. Vediamone una rassegna. String Gli oggetti String sono usati in Javascript per contenere stringhe di caratteri. Possono essere creati implicitamente, utilizzando una costante stringa, o esplicitamente tramite l'omonimo costruttore: s1 = "pluto"; s2 = new String("pippo"); I principali metodi e proprietà della classe String sono i seguenti. Tutti i metodi si applicano all'oggetto di tipo String sul quale sono invocati. • • • • • • • • • length restituisce la lunghezza della stringa. charAt(posizione) restituisce il carattere (stringa di lunghezza uno) alla posizione data (base zero). charCodeAt(posizione) come charAt, ma restituisce il codice ASCII del carattere. indexOf(s[,offset]) restituisce la posizione della prima occorrenza (a partire da offset, se specificato) di della sottostringa s nella stringa. Restituisce -1 se s non è presente. lastIndexOf(s[,offset]) come indexOf, ma restituisce la posizione dell'ultima occorrenza. substr(os[,l]) restituisce la sottostringa di lunghezza l (default, la massima possibile) che inizia os caratteri dall'inizio della stringa substring(os,oe) restituisce la sottostringa che inizia os caratteri e termina a oe caratteri dall'inizio della stringa toLowerCase() ritorna la stringa convertita in minuscolo toUpperCase() ritorna la stringa convertita in maiuscolo RegExp Javascript riconosce le espressioni regolari scritte nella sintassi Perl, che sono spesso molto utili per eseguire parsing e validazione dei dati immessi dall'utente. Revisione 05/11 Pagina 18/27 Javascript: Oggetti Predefiniti Javascript Corso di Ingegneria del Web Per informazioni approfondite sulla http://perldoc.perl.org/perlre.html sintassi delle espressioni regolari Perl-compatibili, si veda Per descrivere un'espressione regolare costante è sufficiente usare la sintassi /espressione/ (notare l'assenza di virgolette: gli unici delimitatori sono gli slash). Espressioni regolari variabili (cioè costruite a partire da espressioni stringa generate a tempo di esecuzione) possono essere create tramite il costruttore RegExp. E' possibile usare le espressioni regolari in vari metodi della classe String: • • • • match(r) restituisce un array con le sottostrighe che hanno fatto match con l'espressione regolare r. replace(r,s) restituisce una stringa in cui tutte le sottostringhe cha fanno match con r sono sostituite con la stringa s. search(r) restituisce la posizione della prima sottostringa che fa match con r, o -1 se non ci sono match. split(r[,m]) divide la stringa in una serie di segmenti distinti dai separatori specificati con l'espressione r e li restituisce come array. Se si indica un numero massima di occorrenze m, allora l'ultimo elemento dell'array conterrà la rimanente parte della stringa. var s = "ciao a tutti,mi chiamo paperino"; var tokens = s.split(/[ ,;!?]+/); //restituisce l'array ['ciao','a','tutti','mi','chiamo','paperino'] s.replace(/[, ]/,"-").toUpperCase(); //restituisce "CIAO-A TUTTI,MI CHIAMO PAPERINO" Per default, Javascript interrompe il processo di matching su una stringa appena trova un riscontro per l'espressione regolare. Infatti, nell'ultimo esempio, solo il primo spazio viene sostituito da un trattino. Per trovare tutti i riscontri possibili, usare il modificatore /g: var s = "ciao a tutti,mi chiamo paperino"; s.replace(/[, ]/g,"-").toUpperCase(); //restituisce "CIAO-A-TUTTI-MI-CHIAMO-PAPERINO" Si possono anche usare altri utili modificatori, ad esempio /i per rendere l'espressione regolare case insensitive. Si veda la guida alle espressioni regolari Perl per ulteriori informazioni. Array Gli Array sono oggetti Javascript predefiniti, non hanno una dimensione fissa (come invece avviene in molti linguaggi di programmazione) e possono contenere valori di qualsiasi tipo. Inoltre, ogni elemento dell'array può avere tipo diverso (in altre parole gli array non sono omogenei). Per creare un array si può usare • il costruttore Array, che accetta un numero arbitrario di parametri, corrispondenti agli elementi con cui l'array deve essere inizializzato Revisione 05/11 Pagina 19/27 Corso di Ingegneria del Web • Javascript: Oggetti Predefiniti Javascript la sintassi simbolica "parentesi quadre", in cui gli elementi dell'array sono listati, separati da virgole, tra sue parentesi quadre. var a1 = new Array(10,20,30); //dichiarazione con costrutto new var a2 = ["a","b","c"]; //dichiarazione implicita Se si assegna un valore a un particolare indice di un array, si creano implicitamente tutti gli elementi dell'array mancanti fino all'indice dato, inizializzandoli con undefined. In ogni momento è possibile verificare se un indice è presente nell'array e il valore associato ad esso è diverso da undefined con l'espressione booleana indice in variabile_array. Infine, per accedere a un elemento di un array si usa la comune sintassi variabile_array[indice]. Gli elementi sono numerati a partire da zero. var a = new Array(10,20,30); a[2]; //restituisce 30 (3 in a) //ha valore false (4 in a) //ha valore false a[4] = 0; //adesso l'array ha cinque elementi: [10,20,30,undefined,0] (3 in a) //ha ancora valore false (4 in a) //ha valore true if (4 in a) { a[4] = a[4]+1; } //usa un elemento solo se presente I principali metodi e proprietà della classe Array sono i seguenti: • • • • • • length restituisce la dimensione dell'array concat(e1,e2,e3,...) restituisce il nuovo array ottenuto concatenando gli elementi dati alla fine dell'array. join([separatore]) converte l'array in una stringa, concatenando la versione String di ciascun elemento tramite il separatore dato (default ","). reverse() restituisce una copia dell'array con gli elementi in ordine inverso slice(os[,l]) restituisce il sotto array di lunghezza l (default, fino alla fine dell'array sorgente) che inizia all'indice os. sort([sortfun]) restituisce l'array ordinato. La funzione opzionale sortfun può essere usata per specificare un criterio di ordinamento non standard. Tale funzione deve accettare due parametri e restituire 0 (elementi uguali), -1 (il primo è minore del secondo) o 1 (il primo è maggiore del secondo). for (i in a) { a[i]; } //itera nell'array a for (i=0; i<a.length; ++i) { a[i]; } //itera tra gli elementi dell'array Revisione 05/11 Pagina 20/27 Corso di Ingegneria del Web Javascript: Oggetti Predefiniti Javascript [3,1,2].sort( function(x,y){return x<y;}); //ordina l'array in maniera decrescente Molti linguaggi (ad esempio PHP) dispongono del concetto di array associativo, in cui gli elementi sono associati a chiavi generiche di tipo stringa, e non a semplici indici numerici. Questo costrutto nella pratica è molto utile, e si può facilmente ottenere anche in Javascript. Infatti, per avere un array associativo, è sufficiente creare dinamicamente delle proprietà (chiavi) a partire da un oggetto vuoto. La possibilità di accedere alle proprietà con la sintassi tipica degli array, cioè o["p"], rende in questo senso gli oggetti Javascript identici a un array associativo. //è un oggetto Javascript, ma lo usiamo come un array associativo! aa = {}; aa["nome"] = "Pippo"; aa["eta"] = 34; Date L'oggetto Date permette di manipolare valori di tipo data e ora. Dispone di diversi costruttori: • • • Date() inizializza l'oggetto alla data/ora corrente. Date(y,m,d,hh,mm,ss) inizializza l'oggetto alla data/ora d/m/y hh:mm:ss. Date(stringa) tenta di riconoscere la stringa come una data e inizializza l'oggetto di conseguenza. Gli oggetti Date possono essere confrontati tra loro con i normali operatori di confronto, inoltre i metodi degli oggetti Date permettono di leggerne e scriverne le componenti separatamente: ad esempio, possiamo usare i metodi getYear, getMonth, setYear, setMonth, getDay (restituisce il giorno della settimana), getDate (restituisce il giorno del mese), setDate (imposta il giorno del mese) Una caratteristica utile del metodo setDate è che, se il valore passato è maggiore del massimo consentito, la funzione gestisce automaticamente l'incremento del mese/anno della data. Se vogliamo quindi aggiungere dieci giorni a una data d, dovremo semplicemente scrivere d.setDate(d.getDate()+10). Se il valore passato a setDate è negativo, la data viene invece spostata nel passato. Vediamo altri esempi: //genera un saluto adeguato all'ora corrente e lo assegna alla variabile saluto var giorni = ["lun","mar","mer","gio","ven","sab"]; var mesi = ["gen","feb","mar","apr","mag","giu","lug","ago","set","ott","nov","dic"]; var oggi = new Date(); var data = giorni[oggi.getDay()] + " " + oggi.getDate() + " " + mesi[oggi.getMonth()] + " " +oggi.getFullYear(); var saluto; if (oggi.getHours() > 12) saluto = "Buona sera, oggi è il "+data; else saluto = "Buongiorno, oggi è il "+data; //calcola una la data di 70 giorni nel futuro futuro = new Date(); futuro.setDate(oggi.getDate()+70); Revisione 05/11 Pagina 21/27 Corso di Ingegneria del Web Javascript: Oggetti Predefiniti Javascript per i Browser Oggetti Predefiniti Javascript per i Browser Quando Javascript è eseguito all'interno di un browser, altri oggetti predefiniti, relativi al browser stesso e alla pagina visualizzata, sono disponibili per l'uso. Vediamo i più interessanti e utili. window L'oggetto window è il punto di accesso a tutti gli oggetti esposti dal browser. Si tratta dell'oggetto predefinito per lo scripting, il che significa che tutte le sue proprietà e i suoi metodi sono accessibili a livello globale, senza bisogno di specificare esplicitamente il nome dell'oggetto window. L'interfaccia di window contiene alcune funzionalità molto utili, tra cui • • • • alert(messaggio) mostra il messaggio dato in un dialog box (con il solo bottone OK). confirm(messaggio) mostra il messaggio dato in un dialog box con I bottoni OK e Cancel. La funzione ritorna true se l'utente preme OK, false altrimenti. prompt(messaggio,default) mostra il messaggio dato in un dialog box, insieme a un campo di input con valore iniziale default. Se l'utente preme OK, il contenuto del campo (anche vuoto) viene restituito dalla funzione, altrimenti la funzione restituisce null. setTimeout e setInterval permettono di impostare i timer (si veda dopo) //esegue un'azione solo se l'utente clicca OK if (window.confirm("Sei sicuro di voler lasciare questa pagina?")) {...} //chiede all'utente di inserire un dato e lo avverte se non è stato specificato nulla var citta = window.prompt("Luogo di Nascita","L'Aquila"); if (!citta) window.alert("Non hai specificato il luogo di nascita!"); La proprietà document permette di accedere al (modello a oggetti del) documento HTML visualizzato, di cui parleremo di seguito. Altre proprietà, come ad esempio statusbar, sono supportate in maniera diversa dai differenti browser. document L'oggetto document, accessibile tramite la proprietà omonima di window, rappresenta il documento visibile nel browser. La maggior parte dei metodi e delle proprietà offerti dall'oggetto document provengono dall'interfaccia Document, che verrà trattata nell'ambito del Document Object Model. Esistono tuttavia alcune proprietà utili proprie del solo oggetto document, ad esempio: • • La proprietà location contiene la URL di provenienza del documento corrente. La proprietà lastModified contiene la data dell'ultimo aggiornamento del documento corrente. Revisione 05/11 Pagina 22/27 Corso di Ingegneria del Web • • • Javascript: L'oggetto XMLHttpRequest Il metodo open() apre uno stream per la successiva scrittura di testo nel documento tramite write() e writeln(). All'apertura dello stream il contenuto corrente del documento viene cancellato. I metodi write(testo) e writeln(testo) accodano testo (o testo seguito da un ritorno a capo) al documento corrente. Se non è stata chiamata la open(), ne viene generata una implicita. Il metodo close() chiude lo stream di scrittura aperto con open() e forza la visualizzazione di quanto scritto nel documento con write() e writeln(). Ogni successiva operazione di scrittura genererà una nuova open() implicita. Attenzione: I metodi open, close, write e writeln non si possono usare in XHTML. L'oggetto document fornisce anche un sistema "proprietario" di Javascript per l'accesso alla struttura del documento visualizzato. Nei browser moderni, con supporto al W3C DOM, l'uso di questo sistema è tuttavia fortemente sconsigliato. //crea un documento contenente una semplice tabella - non funziona in modalità standards var i,j; document.open(); document.write("<table border=‘1'>"); for(i=0;i<10;++i) { document.write("<tr>"); for(j=0;j<10;++j) { document.write("<td>"+i+","+j+"</td>"); } document.write("</tr>"); } document.write("</table>"); document.close(); L'oggetto XMLHttpRequest L'oggetto XMLHttpRequest, originariamente introdotto da Internet Explorer, è ora supportato da tutti i browser più diffusi. Il suo scopo è quello di permettere al codice Javascript l'esecuzione di richieste HTTP verso il server (proprio come farebbe il browser) e la gestione dei dati risultanti. Questo oggetto è alla base delle tecniche AJAX, con cui gli script che controllano una pagina web possono dialogare col server senza la necessità di "cambiare pagina". Attenzione: per motivi di sicurezza, l'oggetto XMLHttpRequest può effettuare connessioni solo con l'host a cui appartiene la pagina in cui ha sede lo script! L'interfaccia di XMLHttpRequest è standard, ma esistono sistemi browser-dipendenti per accedere a questo oggetto. Se nel browser è definito il costruttore omonimo (lo si può verificare con l'espressione typeof(XMLHttpRequest) != "undefined"), è sufficiente eseguire una new. In Internet Explorer, si usa invece il costruttore ActiveXObject passandogli la stringa di identificazione dell'oggetto, che può essere "MSXML2.XmlHttp.6.0" (preferita) o "MSXML2.XmlHttp.3.0" (vecchie versioni del browser). Riassumendo, un modo crossbrowser per ottenere un riferimento a XMLHTTPRequest è dato dal frammento di codice che segue; function createRequest() { var ACTIVEXIDs=["MSXML2.XmlHttp.6.0","MSXML2.XmlHttp.3.0"]; if (typeof XMLHttpRequest != "undefined") { return new XMLHttpRequest(); Revisione 05/11 Pagina 23/27 Corso di Ingegneria del Web Javascript: L'oggetto XMLHttpRequest } else if (typeof ActiveXObject != "undefined") { for (var i=0; i < ACTIVEXIDs.length; i++) { try { return new ActiveXObject(ACTIVEXIDs[i]); } catch (oError) { //l'oggetto richiesto non esiste: proviamo il successivo } } alert("Impossibile creare una XMLHttpRequest"); } else { alert("Impossibile creare una XMLHttpRequest"); } } Il pattern d'uso di XMLHttpRequest è duplice, a seconda che si scelga la modalità di chiamata sincrona o asincrona: • • Modalità sincrona: la richiesta al server blocca lo script (e la pagina associata) finché non viene ricevuta la risposta. Modalità asincrona: la richiesta viene inviata, e lo script continua la sua esecuzione, venendo poi avvisato dell'arrivo della risposta tramite un evento. Uso Sincrono Per usare XMLHttpRequest in maniera sincrona, si prepara la richiesta usando il metodo open, a cui si passano il verbo HTTP e la URL da chiamare. Il terzo parametro deve essere false per avviare una richiesta sincrona. Si invia quindi la richiesta con il metodo send, che risulta bloccante. xhr.open("GET","http://pippo",false); xhr.send(null); Si controlla infine se la richiesta ha restituito un errore HTTP tramite la proprietà status e, in caso di successo, si accede ai dati restituiti dal server tramite la proprietà responseText if (xhr.status !=404) { //404 è il codice di errore "not found" di HTTP alert(xhr.responseText); } else { alert("errore"); } Uso Asincrono In questo caso, si prepara la richiesta usando il metodo open, a cui si passano come al solito il verbo HTTP e la URL da chiamare. Il terzo parametro in questo caso deve essere true per avviare una richiesta asincrona. Si imposta quindi l'handler da chiamare quando la richiesta sarà stata servita assegnando una funzione alla proprietà onreadystatechange. Infine si invia la richiesta con il metodo send (la chiamata ritorna immediatamente il controllo allo script). xhr.open("GET"," http://pippo",true); xhr.onreadystatechange = handler; xhr.send(null); All'interno dell'event handler dichiarato con onreadystatechange, che sarà chiamato ad ogni cambio di stato della richiesta, si verifica prima di tutto se la richiesta è stata effettivamente Revisione 05/11 Pagina 24/27 Corso di Ingegneria del Web Javascript: Gestione delle Eccezioni servita controllando che la proprietà readyState sia uguale a 4. In tal caso, si può verificare se l'esecuzione ha restituito un errore HTTP tramite la proprietà status e poi accedere ai dati restituiti dal server tramite la proprietà responseText, come già illustrato per il caso sincrono. function handler() { if (xhr.readyState==4) { if (xhr.status!=404) { alert(xhr.responseText); } else { alert("errore"); } } } In ogni momento, è possibile invocare il metodo abort per interrompere la richiesta HTTP in corso. E' ovviamente possibile attivare più chiamate asincrone contemporaneamente. In questo caso, ogni handler dovrà fare riferimento ad un'istanza diversa di XMLHttpRequest. Ajax e JSON Spesso, quando si scambiano dati con uno script tramite la XMLHttpRequest, accade che il server debba passare a Javascript strutture dati complesse, e non semplice testo o HTML. In questi casi, è utile usare la notazione JSON: in pratica, le strutture dati vengono trascritte testualmente usando la notazione "breve" Javascript per la definizione di oggetti ed array. Ad esempio, la stringa che segue definisce (e in Javascript crea) un array contenente due record aventi come campi "id" e "nome" [{id:1, nome:'pippo'},{id:2, nome:'pluto'}] Il server dovrà quindi codificare tutti i dati da inviare allo script in questo formato. Una volta ricevuti i dati, lo script potrà trasformarli facilmente in vere strutture dati sfruttando la capacità di Javascript di interpretare stringhe come fossero codice. In particolare, il metodo più sicuro per effettuare questa trasformazione è il seguente: dati = new Function("return "+xhr.responseText)(); In pratica, si crea una funzione anonima che ritorna le strutture generate dall'interpretazione dei dati in formato JSON, e la si chiama contestualmente (si noti infatti la coppia di parentesi dopo la chiusura del costruttore Function). Gestione delle Eccezioni Nelle versioni più recenti di Javascript è stato introdotto anche un sistema di gestione delle eccezioni in stile Java. Un’eccezione segnala un imprevisto, spesso un errore, all’interno della normale esecuzione del codice. Non ci proponiamo di dare qui una definizione completa del concetto di eccezione e del modo in cui può essere utilizzata per migliorare la struttura del codice, in particolare la gestione degli errori. Per approfondire questi argomenti, potete riferirvi allo stesso argomento presente su qualsiasi libro di programmazione object oriented. Un’eccezione può venire sollevata dalle librerie di Javascript o dal codice scritto dall’utente, attraverso la parola chiave throw. Una volta sollevata, un’eccezione risale lo stack di Javascript finché non viene gestita da un handler esplicito (dichiarato dall’utente) o implicito (inserito nel Revisione 05/11 Pagina 25/27 Corso di Ingegneria del Web Javascript: Funzioni Javascript Utili: i Timer runtime Javascript). Ciò significa che un’eccezione generata in una funzione, se non viene gestita all’interno di quest’ultima, si propagherà alle sue funzioni chiamanti, fino ad arrivare al runtime di Javascript, che la presenterà all’utente come un messaggio di errore (nelle modalità specificate dal browser). Per gestire le eccezioni generate (eventualmente) da un particolare blocco di codice, è possibile avvalersi del costrutto try…catch, che permette di dichiarare un handler associato a uno specifico blocco di codice. Qualsiasi eccezione sollevata all’interno del codice compreso tra try e catch verrà passata al codice di gestione dichiarato dopo catch. Le eccezioni generate da Javascipt e catturate dai blocchi catch sono oggetti la cui proprietà message riporta il messaggio di errore associato. try { …codice… } catch(e) { alert(“Eccezione sollevata: ”+ e.message); } Le eccezioni generate dall’utente tramite la parola chiave throw possono essere invece oggetti qualsiasi, che verranno presentati come tali al blocco catch. try { …codice… throw {“nome”: “pippo”, “valore”: 1}; } catch (ex) { alert(“Valore dell’eccezione: ”+ ex.valore); } Se ci si vuole assicurare che un certo codice sia eseguito sempre dopo il blocco protetto da try…catch, indipendentemente dal sollevamento di eccezioni, è possibile aggiungere al blocco la clausola finally. In tal caso, il codice finally verrà eseguito in ogni caso prima che l’interprete Javascript passi ad eseguire del codice diverso da quello inserito nel costrutto try…catch. try { …codice… } catch (eccezione) { …gestione dell’eccezione… } finally { …codice eseguito in ogni caso prima che l’esecuzione prosegua oltre il blocco try…catch… } Funzioni Javascript Utili: i Timer Javascript, tramite l'oggetto window, permette anche di eseguire azioni temporizzate. A questo scopo si usano i seguenti metodi. • • setTimeout(stringa_o_funzione,millisecondi,arg1,...,argN). La chiamata a questa funzione fa sì che, dopo il numero specificato di millisecondi, Javascript esegua il codice dato dal primo argomento, che può essere una stringa contenente del codice da valutare o il nome di una funzione da chiamare. In quest'ultimo caso, è possibile specificare opzionalmente una serie di argomenti (arg1...argN) da passare alla funzione. L'azione viene quindi eseguita una sola volta. setInterval(stringa_o_funzione,millisecondi,arg1,...,argN). La chiamata a questa funzione fa sì che, ogni millisecondi, Javascript esegua il codice dato Revisione 05/11 Pagina 26/27 Javascript: Funzioni Javascript Utili: i Timer Corso di Ingegneria del Web dal primo argomento, che può essere una stringa contenente del codice da valutare o il nome di una funzione da chiamare. In quest'ultimo caso, è possibile specificare opzionalmente una serie di argomenti (arg1...argN) da passare alla funzione. L'azione viene quindi eseguita periodicamente. Entrambe le funzioni possono essere chiamate più volte, e restituiscono un timer id (numerico), tramite il quale è possibile annullare le azioni usando le rispettive funzioni: • • clearTimeout(id) per le azioni avviate con setTimeout() clearInterval(id) per le azioni avviate con setInterval() Vediamo alcuni esempi d'uso per queste due funzioni: function saluta(nome) { alert("Ciao "+nome); } //Richiede il nome e saluta dopo cinque secondi var nome = prompt("Come ti chiami?"); if (nome) setTimeout(saluta,5000,nome); //avverte dell'ora corrente ogni minuto setInterval("d=new Date(); alert('Ora sono le '+d.getHours()+':'+d.getMinutes())",60000); This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/3.0/ or send a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA. Revisione 05/11 Pagina 27/27
Documenti analoghi
Javascript - University of L`Aquila
Passare come argomento una funzione ad un’altra funzione.
Assegnare una funzione a una o più variabili.
Accedere a tutti gli elementi della funzione, per modificarla o
ridefinirla, tramite le propr...