Corso JavaScript (parte 4)
A cura di Gabriele Favrin
Articolo pubblicato su Amiga Life 111 (giugno 2000)
Nota bene: l'articolo proposto in questa pagina è
proprietà dell'autore e non può essere distribuito o
riutilizzato in alcun modo o forma senza il suo consenso scritto.
Le informazioni qui riportate risalgono alla data di pubblicazione
dell'articolo e non è detto che siano ancora valide o
che rispecchino lo stato attuale dei programmi, le posizioni delle persone
interpellate o quelle degli autori stessi.
Contenuti
Avviso
Questo corso descrive JavaScript 1.1. Sebbene le informazioni proposte siano valide come punto di partenza per la conoscenza del linguaggio, suggerisco agli interessati di seguire guide aggiornate alla versione più recente di JavaScript che offre funzionalità avanzate come la gestione di dati XML esterni e la completa manipolazione dell'aspetto del documento.
Introduzione
Nel corso delle puntate precedenti abbiamo descritto gli elementi di
base del linguaggio: principali comandi, funzioni, oggetti ed eventi;
offrendo inoltre una serie di esempi mirata a guidare la realizzazione
di script, dai più comuni, di puro intrattenimento, a
progetti avanzati.
Ci siamo volutamente concentrati su JavaScript 1.1 in quanto ben
rappresentante delle summenzionate basi del linguaggio. La scelta
inoltre non è limitante come può apparire:
JavaScript 1.1 è stato utilizzato per la definizione dello
standard di script (ECMA-262, di cui parliamo in seguito), pertanto
conoscerne il funzionamento consente di lavorare anche su
implementazioni differenti, come le nuove versioni di JavaScript stesso
o JScript.
JavaScript infatti si evolve e moltiplica. E le nuove versioni offrono
una serie davvero interessante di funzionalità, presentate
con sigle e termini differenti ma solo apparentemente complessi.
In questa quarta e ultima puntata parleremo, appunto, di evoluzione.
Cosa è stato aggiunto a JavaScript? Quali nuove
funzionalità ci mette a disposizione un linguaggio le cui
possibilità, speriamo sia ormai chiaro, sono solo
apparentemente limitate.
Va comunque detto che gli argomenti di cui ci occuperemo non sono,
almeno per ora, alla portata dei browser Amiga. Mettendo da parte AWeb,
tuttora limitato alla versione 1.1 del linguaggio, le implementazioni
JavaScript di IBrowse 2.2 e Voyager 3, pur se dichiarate compatibili
con JavaScript 1.3, ne supportano solo un sottoinsieme di
funzionalità e gli aggiornamenti rilasciati in questi mesi
non migliorano di molto la situazione. Vi invitiamo comunque a
proseguire la lettura, in quanto riteniamo sia importante aggiornarsi
sulle novità relative al linguaggio e che comunque, prima o
poi, saranno fruibili anche sul nostro sistema. Infine, se è
vero che Netscape e Internet Explorer non esistono in forma nativa su
Amiga, è altrettanto vero che fra Linux e gli emulatori
Macintosh le possibilità per sperimentare non mancano.
Ci piace considerare questo capitolo come un ultimo giorno di scuola
nel quale, invece di dover sostenere un esame, si discute
rilassatamente di cosa riserva il futuro (prossimo o remoto dipende da
noi utenti, non solo dagli autori di browser).
Evoluzioni di JavaScript
Pochi linguaggi possono vantare un'evoluzione simile a quella di
JavaScript. Netscape, l'azienda che lo ha ideato, ha
incrementato le funzionalità di base (comandi, oggetti,
ecc.) in ogni nuova versione, a volte anche stravolgendo il
funzionamento di cose implementate in precedenza (da qui l'importanza
di specificare a quale versione di JavaScript si riferisce uno script).
Poichè le specifiche di JavaScript sono pubbliche, non
è passato molto tempo perché altre aziende
realizzassero proprie versioni dello stesso. Fra tutte spicca JScript
della "solita" Microsoft.
Per evitare il diffondersi di linguaggi di script incompatibili fra
loro, come stava avvenendo, l'ECMA (European Computer Manufacturers
Association) ha realizzato le specifiche di quello che oggi
è noto con il nome di ECMAScript o ECMA-262. Si tratta del
cuore di un linguaggio di script a oggetti che, come già
detto, si basa sulla versione 1.1 di JavaScript. Chiunque intenda
implementare JavaScript in un proprio prodotto può basarsi
sulla direttive ECMA per realizzare un linguaggio standard che funzioni
analogamente su qualsiasi piattaforma. Almeno così dovrebbe
essere, visto che JScript di Microsoft, pur facendo sfoggio della
compatibilità ECMA-262, continua a gestire alcune funzioni a
modo proprio, generando piccole ma insidiose incompatibilità
(una su tutte riguarda le date).
La disponibilità di ECMAScript ha comunque frenato le smanie
di Netscape che, dopo aver alterato il comportamento di diversi
elementi di JavaScript 1.2 (realizzato prima che le specifiche ECMA
venissero rilasciate), è tornata sui propri passi,
riallineando JavaScript 1.3 a quanto prescritto dagli organismi
internazionali.
E' bene precisare che ECMAScript non è un linguaggio
utilizzabile in sostituzione a JavaScript o JScript: si tratta
piuttosto di una raccolta di specifiche e indicazioni inerenti le sole
funzioni base del linguaggio. Tali specifiche, naturalmente, non
limitano la possibilità, da parte degli sviluppatori, di
introdurre funzionalità peculiari nelle proprie
implementazioni del linguaggio. Le direttive ECMA, inoltre, tralasciano
completamente il modello a oggetti che rappresenta il documento HTML
(detto "DOM", ossia "document object model"), vitale per l'utilizzo di
JavaScript, che è stato invece standardizzato dal W3c, lo
stesso organismo che si occupa di definire l'HTML. Insomma, da semplice
trovata degli autori di un browser, JavaScript ormai è
diventato una cosa seria e codificata a livello internazionale!
ECMA ha presentato anche diverse direttive circa l'espansione delle
funzionalità del linguaggio tramite librerie. Una di queste
librerie, che farà inorridire i più (anche
perchè subito adottata da Microsoft per JScript), mette a
disposizione un oggetto il cui nome non lascia spazio a dubbi: "File
System Object". Tale innovazione, che a un primo sguardo sembrerebbe
snaturare JavaScript, al contrario amplia notevolmente le
potenzialità del linguaggio, avvicinandolo ad altri ben
più blasonati, quali ad esempio PERL o ASP. Peccato che,
oltre alla presenza, nel mondo, di tanti malintenzionati, questo nuovo
oggetto sia, per ora, peculiarità del solo browser di casa
Gates.
Altra caratteristica di recente introduzione, questa volta da parte di
Netscape (e disponibile soltanto sul loro browser), sono i JavaScript
Beams. Frutto della crescente integrazione con Java (ora JavaScript ha
accesso ad una API Java che consente a script ed applet lo scambio di
informazioni e funzioni), i JavaScript Beams, assimilabili ai Java
Beams, sono delle raccolte di funzioni esterne poste all'interno di
file testuali con estensione ".jsb". Ogni file contiene le varie
funzioni, scritte in puro JavaScript ed incapsulate all'interno di un
linguaggio di tipo SGML, quindi con una sintassi simile all'HTML. La
struttura e le regole di implementazione dei JSB avvicinano JavaScript
a Java, offrendo agli sviluppatori funzioni simili e una sorta di
gestione dei tipi di dati (anche se JavaScript resta un linguaggio
fondamentalmente libero dai vincoli di questo genere).
Sicurezza
Con JavaScript che procede a grandi passi verso una forte integrazione
con il mondo esterno al browser, la sicurezza acquista sempre maggiore
importanza. Vediamo dunque cosa si è fatto nelle varie
versioni del linguaggio per prevenire gli abusi.
Sicurezza non significa soltanto evitare che uno script danneggi il
sistema operativo o cancelli l'hard disk. Significa anche prevenire il
furto di informazioni. Senza andare troppo lontano, non sarebbe
difficile nascondere in una pagina uno script che raccolga informazioni
sulla lista dei siti visitati dall'utente in precedenza (oggetto
"history") o su altre finestre e relativi documenti (oggetti "window" e
"document", con tutte le proprietà del caso, compresi gli
eventuali moduli) e le invii, in e-mail o trasmettendo dati ad un
apposito script CGI, allo pseudo-hacker di turno.
Per evitare questa possibilità è stato
introdotto, sin da JavaScript 1.0, un controllo sulla provenienza dei
documenti. Ne abbiamo già parlato: uno script può
accedere soltanto a finestre e documenti provenienti dallo stesso
server. Il limite si estende a protocollo (differenziando "http://",
"https://" e "file://") e porta.
Uno script posto, ad esempio, su "http://www.amigalife.it/index.html"
potrà accedere, oltre che a finestre da esso generate, solo
a informazioni (a video) provenienti da "http://www.amigalife.it/". Non
potreebbe invece leggere un documento prelevato dall'ipotetico
indirizzo "http://amigalife.it/", nè da
"http://www.amigalife.it:81/".
Questo modello di sicurezza, pur se funzionale, presentava alcune
limitazioni allo sviluppo di script leciti. Per superarle è
stato introdotto, con JavaScript1.1, il concetto del "data tainting".
Abilitando la relativa opzione (che, per inciso, non è
supportata da nessun browser Amiga), l'accesso ai dati di altri
documenti, anche provenienti da server diversi, può essere
impostato selettivamente all'interno dello script. L'autore di uno
script può insomma consentire l'accesso all'intero documento
o a una parte delle informazioni ad esso inerenti (compreso quindi il
proprio script e le sue funzioni e variabili), servendosi delle
funzioni "taint()" ed "untain()". Chiariamo le cose con un esempio.
Innanzitutto è necessario controllare se il taint
è abilitato. Per farlo si verifica il risultato del metodo
"navigator.taintEnabled()". Se è "true" si può
procedere. Per evitare incompatibilità sarebbe anche
opportuno verificare se il metodo esiste con il classico: "if
(navigator.taintEnabled"). Su AWeb esiste e restituisce sempre false,
su Voyager 3 ed IBrowse 2.2 non esiste proprio.
Anche se il data tainting è abilitato, molti oggetti critici
("document", "window", "location" e tutti gli oggetti relativi ai
moduli) sono protetti dall'accesso indiscriminato. Possiamo abilitarne
l'interscambio utilizzando ad esempio "status=untaint(document.title)
".
In questo modo il titolo del documento nel quale si trova lo script
sarà disponibile ad altri script. Lo stesso si
può fare con variabili e oggetti. Omettendo l'argomento si
rende accessibile l'intero documento. "taint()" svolge invece la
funzione opposta, impedendo l'accesso a informazioni precedentemente
rese disponibili. Per maggiori informazioni rimandiamo alla
documentazione ufficiale JavaScript presente sul CD.
Il data tainting è destinato ai programmatori e solo un suo
utilizzo accorto può prevenire problemi. Come assicurazione
c'è da dire che i dati ottenuti in questo modo non possono
essere inviati ad altri server. Provando a farlo, infatti, l'utente
verrà avvertito da un requester che chiederà la
conferma dell'azione.
Nelle versioni più recenti di JavaScript è stato
introdotto un altro elemento di sicurezza, basato più sulla
"fiducia" che un nome porta con sé che su accorgimenti
tecnici. Analogamente a quanto già accade con gli applet
Java, anche gli script possono essere "firmati". Il meccanismo
è semplice: se navigando su una pagina si incontra uno
script, il browser ne verifica la provenienza e propone questa
informazione all'utente che è libero di decidere se
accettarne l'esecuzione. Al programmatore è sufficiente,
dopo aver realizzato uno script, utilizzare gli appositi tool Netscape
per ottenerne la certificazione. Chi naviga abitualmente su altre
piattaforme sa di cosa parliamo e conosce l'effetto rassicurante che
questo meccanismo porta nell'utente, almeno finché questi
non si trova davanti a un applet firmato e apparentemente sicuro che
però gli azzera l'hard disk...
Le nuove funzionalità di JavaScript
Dopo questa panoramica sulle molteplici evoluzioni di JavaScript, torniamo a parlare di programmazione e analizziamo alcuni degli elementi introdotti più recentemente nel linguaggio. Abbiamo scelto di occuparci di eventi ed espressioni regolari poiché rappresentano, a nostro giudizio, ottimi esempi di nuove o migliorate funzionalità.
Le espressioni regolari
Le espressioni regolari, già note agli esperti di script
AmigaDOS come "pattern" o "wild card", sono particolari sequenze di
caratteri che permettono di rappresentare una stringa tramite dei
simboli. Questa naturalmente è una forte semplificazione del
concetto dettata da ragioni di spazio. Per approfondire l'argomento
consigliamo la lettura dell'articolo "Le Espressioni Regolari" del
collega Fabio Rotondo, presente in questo stesso numero di Amiga Life.
Un esempio classico di espressioni regolari è offerto da
AmigaDOS: per visualizzare tutti i file presenti nella directory C: che
iniziano con "re" possiamo servirci di "List C:re#?". La sequenza "#?",
per AmigaDOS, equivale a "qualunque carattere o sequenza di caratteri".
Quindi, "re#?" equivale a tutte le stringhe in cui, dopo le lettere "r"
ed "e", seguono zero o più lettere (o numeri) di altro tipo.
A questa espressione corrispondono, fra gli altri, i file "Rename",
"Relabel" e "Remrad".
Anche JavaScript, a partire dalla versione 1.2, ha introdotto il
supporto per le espressioni regolari. Nello specifico contesto
risultano utili per verificare se una stringa immessa dall'utente
risponde alle nostre richieste. Con il semplice utilizzo del
metodo ".test()" dell'espressione regolare (si tratta di un oggetto)
è possibile accertare, ad esempio, se l'utente ha indicato
la chiocciolina nel suo indirizzo E-Mail ("esp.test(stringa)", con
espressione "/*@*/").
Le potenzialità delle espressioni regolari non si limitano
al controllo della presenza di uno o più caratteri, cosa che
si potrebbe ottenere più facilmente con il metodo
".indexOf()", presente sin dalla versione 1.1 di JavaScript. Vediamo
perché con un esempio.
Prima di proseguire è doveroso sottolineare che in questo
contesto il termine "espressione" è da considerarsi riferito
alle espressioni regolari e non alle espressioni JavaScript (es.:
"if (ciao() == true)
").
Per servirsi delle espressioni regolari si fa riferimento all'oggetto
"RegExp". A differenza di altri oggetti, anche creandone nuove istanze,
ne permane uno principale per ognuno degli script in esecuzione. La
ragione di questa anomalia è data dal fatto che le
operazioni relative alle espressioni regolari causano modifiche alle
proprietà del suddetto oggetto primario.
Il sistema più semplice per creare un'espressione regolare,
qualora non sia necessario utilizzarne differenti all'interno dello
script (in tal caso è necessario seguire un diverso
approccio, descritto nella documentazione del linguaggio) è
il seguente:
var esp=/espressione_regolare/
(notare l'uso di "/" che differenzia l'espressione da una normale
stringa).
Una volta definita l'espressione regolare, sia lo stesso oggetto "RegExp", sia l'oggetto "string", ci consentono di effettuare le varie operazioni. Vediamo ora in dettaglio alcuni dei numerosi caratteri utilizzabili per costruire l'espressione:
* | Equivale a zero o più occorrenze della lettera che lo precede. | Esempio: /car*o/ equivale sia a "caro", sia a "carro". |
+ | Equivale a una o più occorrenze della lettera che lo precede. | Esempio: /ami+ga/ equivale sia ad "amiga", sia ad "amiiiga", ma non ad "amga". |
\s | Equivale ad un singolo spazio. | Esempio: /Amiga\s\Life/ equivale ad "Amiga Life". |
\D | Equivale ad un numero. | Esempio: /Amiga\s\D/ equivale ad "Amiga 500" o "Amiga 3000", ma non ad "Amiga CDTV". |
\w | Equivale a un qualsiasi carattere alfanumerico. | Esempio: /ore\s\w/ equivale ad "ore tre" ma non ad "ore 3". |
Questi sono solo alcuni dei caratteri utilizzabili. Non li riportiamo
tutti per ragioni di spazio.
La cosa interessante è che i vari caratteri sono combinabili
fra loro. Ad esempio "/\w+\s\w+/" equivale ad una stringa composta
necessariamente da due gruppi di caratteri inframezzati da uno spazio.
Verificare la correttezza di una simile sequenza (ad esempio un nome e
cognome) è infinitamente più veloce
così rispetto ai metodi dell'oggetto "string".
Utilizzando le parentesi, poi, possiamo indicare a JavaScript che, al
momento del controllo dell'espressione, alcune parti della stessa
devono essere memorizzate all'interno delle proprietà
dell'oggetto "RegExp".
Se quindi l'espressione proposta diventasse "/(\w+)\s(\w+)/",
otterremmo, con una singola istruzione (oltretutto velocizzata dal
fatto che l'espressione è stata già interpretata
in fase di definizione), sia la verifica della correttezza della
stringa stessa, sia le sue due componenti (nome e cognome)
già isolate e disponibili come elementi di un array. E il
tutto, eventualmente, con o senza differenziazione fra maiuscole e
minuscole.
Un esempio concreto aiuterà a comprendere le
potenzialità di quanto esposto:
<html> <head> <script language="JavaScript1.2"> <!-- pattern=/(\w+)\s(\w+)/ function controlla() { var temp=document.anagrafici.info.value; if (pattern.test(temp)) alert("Nome:"+RegExp.$1+"\nCognome: "+RegExp.$2); else alert("Errore: inserire nome e cognome separati da uno spazio"); return true; } // --> </script> </head> <body> <form name="anagrafici" onSubmit="return false"> Inserire nome e cognome:<br> <input type="text" name="info" value="" onChange="controlla()"> </form> </body> </html>
Come si può notare, a seguito di un'operazione, l'oggetto
"RegExp" può essere interrogato e fornire informazioni sulla
stringa analizzata. In questo caso si è fatto riferimento ai
soli due elementi inseriti fra parentesi, ma sono molte le informazioni
disponibili, variabili a seconda delle operazioni effettuate.
Insomma, se usate con cognizione di causa, le espressioni regolari di
JavaScript semplificano notevolmente le operazioni di verifica e
controllo dei dati, pur se al prezzo della compatibilità
all'indietro.
Gli eventi
Di eventi si è parlato nella seconda parte del corso.
Sostanzialmente, molti elementi HTML possono invocare una funzione
quando si verifica un evento che li riguarda, ad esempio il passaggio
del mouse sull'elemento stesso.
Le nuove versioni di JavaScript hanno espanso il sistema degli eventi
in tre modi. Innanzitutto creando nuovi gestori di evento, quali, ad
esempio, "OnMouseUp" e "OnMouseDown" che consentono di rilevare non
solo il singolo clic, quanto le fasi dello stesso, permettendo la
creazione di interfacce grafiche più realistiche, ove le
icone (immagini) cambiano forma o colore fintanto che l'utente tiene
premuto il bottone del mouse sopra di esse.
E' stato poi introdotto l'oggetto "Event" contenente informazioni sul
momento in cui è occorso l'evento: eventuali tasti o bottoni
del mouse premuti, destinatario dell'evento, ecc.
Già con questo oggetto le potenzialità degli
eventi vengono ampliate: potremmo, ad esempio, fornire le nostre pagine
di una funzione automatica che apre i link su una nuova finestra
qualora l'utente, nel cliccarvi, tenga contemporaneanente prenuto il
tasto ALT.
I lettori più attenti avranno già storto il naso
di fronte a tanto entusiasmo, ricordando che per ottenere l'effetto
proposto si dovrebbe assegnare un gestore di evento ad ognuno dei link
presenti sulla pagina. In realtà ora non è
più così. L'ultima delle novità
relative agli eventi è forse anche la più
interessante. Sono ora disponibili, associati agli oggetti "window" e
"document", i metodi "captureEvents()", "releaseEvents()",
"routeEvent()" e "handleEvent()".
In pratica è possibile intercettare tutti gli eventi di un
certo tipo, ad esempio i clic del mouse, a livello di finestra o
documento, gestirli direttamente, trasmetterli al livello inferiore (la
gerarchia è: finestra, documento, suoi elementi) o ad uno
specifico elemento del documento, qualora questo preveda un gestore di
evento.
Ecco quindi come si presenterebbe uno script per realizzare quanto
descritto:
<html> <head> <script language="JavaScript1.2"> <!--- function gestioneEvento(e) { if (e.modifiers == 2) window.open(e.target); else document.location=e.target; return false; } //--> </script> </head> <body> <a href="http://www.cnn.com">cnn</a><br> </body> </html>
Analizziamo le parti di codice JavaScript 1.2/1.3 dello script:
"e" è il nome che abbiamo assegnato all'oggetto "Event"
generato in occasione dell'evento (prestare attenzione al fatto che
viene creato solo se l'evento è intercettato da un gestore
globale); "target" e "modifiers" sono due sue proprietà. La
prima è composta da una stringa che rappresenta l'elemento
sul quale è occorso l'evento (nel caso dei link è
l'URL) mentre la seconda indica i tasti premuti in quel momento.
Seguono, dopo la funzione, i comandi:
document.captureEvents(Event.MOUSEDOWN); document.onmousedown=gestioneEvento;
Il primo istruisce il documento per catturare l'evento "OnMouseDown"
(questo poiché all'evento "OnClick" spesso i browser
associano azioni differenti a seconda dei tasti premuti) mentre il
secondo imposta il gestore di evento "OnMouseDown" del documento
affinché esegua la nostra funzione.
E' importante notare come nella seconda linea non vadano specificate le
parentesi (altrimenti a "document.onmousedown" verrebbe assegnato il
risultato della funzione e non la funzione stessa) e l'uso delle
maiuscole e minuscole, non casuale. Attenzione anche al fatto che la
funzione verrà invocata in seguito a clic su qualunque
elemento HTML che preveda l'evento "OnClick", non soltanto sui link. In
uno script reale sarà quindi necessario verificare il
contenuto di "e.target" prima di procedere all'apertura del documento.
Esempi relativi sia all'utilizzo delle espressioni regolari, sia alle
nuove funzionalità degli eventi sono disponibili sul CD.
Conclusioni
Per ora è tutto. Ci auguriamo che questo corso sia risultato utile e vi rimandiamo a futuri eventuali approfondimenti sul tema, magari in occasione dei progressi dei browser Amiga in questo campo.
Ringraziamenti e note
Si ringraziano AmiTrix development per AWeb e Vapor (nella persona di Luca Danelon) per Voyager3. Grazie ad Angelo Verdone e Daniele Franza per la "prova su strada" del corso.
Ricordiamo che, per ragioni di spazio, gli esempi proposti non contengono tutti gli elementi HTML il cui uso è normalmente obbligatorio all'interno di un documento.