Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Firefox 2.0 e Javascript 1.7

Le novità della versione 1.7 del noto linguaggio di scripting introdotte nell'ultima release del browser di Mozilla. Analisi di iteratori e generatori
Le novità della versione 1.7 del noto linguaggio di scripting introdotte nell'ultima release del browser di Mozilla. Analisi di iteratori e generatori
Link copiato negli appunti

L'ultima release del browser Firefox presenta diverse novità tra cui l'implementazione della versione 1.7 di JavaScript. Quelle che verranno analizzate in questo articolo saranno le novità introdotte in questa versione, descritte e ben documentate nella pagina di riferimento di Mozilla Developer Center.

La prima considerazione da fare è sul linguaggio stesso. Per anni JavaScript è rimasto praticamente invariato e la guerra dei browser non ha permesso allo stesso di maturare in modo conforme agli standard seguendo linee guida ben definite.

In questi anni però JavaScript è stato reintrodotto in modo massiccio in una enorme quantità di applicativi on-line ed è per questo che i progetti di sviluppo del linguaggio, in questo caso SpiderMonkey, stanno migliorando sotto ogni aspetto il linguaggio stesso, prendendo spunto dalla proposta ECMA di quarta generazione di Netscape.

Questa versione non si discosta molto dalle precedenti ma introduce novità interessanti adatte ad ottimizzare l'utilizzo di memoria ed aumentare la potenza del linguaggio attraverso una sintassi più vicina a Python che allo strict type introdotto, ad esempio, nella versione di ActionScript 2 o meglio ancora 3, anch'esso basato sulla proposta ECMA 4.

Come utilizzare la versione 1.7 di Javascript su Firefox

A differenza delle precedenti versioni di JavaScript, la versione 1.7 non è disponibile di default e richiede un tag script con attributi ben precisi che, se non presente, non permetterà allo sviluppatore di testare il linguaggio con le nuove caratteristiche.

In questo modo tutti i browser che non supportano la versione 1.7 di JavaScript non daranno alcun problema poichè non tenteranno nemmeno di interpretare il codice. Ecco il codice HTML da utilizzare per definire un blocco script con la versione 1.7 di JavaScript:

<script type="application/javascript;version=1.7" />

Che nel caso degli esempi proposti sarà scritto in questo modo:

<script type="application/javascript;version=1.7">
// qui il codice da utilizzare
</script>

Introduzione alle novità

Prima di passare al codice ed agli esempi è forse opportuno descrivere quali sono queste novità e a cosa servono.

La più importante e forse la più complicata da apprendere è il modo di sfruttare lo scope di una o più variabili. Sebbene sia argomento alla base di qualunque linguaggio, in questa versione più che mai è indispensabile comprenderlo al meglio. Un buon punto di partenza può essere questa discussione nel forum di HTML.it.

Lo scope delle variabili servirà a sfruttare al meglio generatori, e quindi iteratori, il nuovo modo di inizializzare o creare array e soprattutto ad utilizzare in modo corretto il costrutto let in tutte le sue varianti.

Generatori

Quando all'interno di una funzione si aggiunge la parola chiave yield, questa funzione viene definita un generatore. La sua caratteristica principale è quella di non essere eseguita come una funzione qualsiasi poiché sarà compito della variabile restituita dal generatore valutare e quindi eseguire quante volte è necessario il blocco scope dell'intera funzione. Vediamo un piccolo esempio pratico, ovvero come un generatore possa essere utilizzato per mostrare tutti i caratteri di una stringa:

function mostraCaratteri(testo){
    var index = 0;
    alert(testo);
    while(index < testo.length) {
        yield index;
        alert(testo.charAt(index++));
    };
};

// richiamo il generatore
mostraCaratteri("HTML.it");
// non accade niente

L'esempio mostra un generatore base di nome mostraCaratteri creato grazie alla dichiarazione yield all'interno del ciclo while. Va osservato come nemmeno la parte esterna al loop, ad esempio il primo alert, venga eseguito, a dimostrazione che un generatore non è da considerasi in nessun modo una normale funzione.

Iteratori

Per poter utilizzare quindi la funzione mostraCaratteri appena vista, bisogna sfruttare il suo valore di ritorno, ovvero il generatore-iteratore. Questa variabile avrà un metodo next in grado di restituire il valore della variabile yield e di eseguire una volta lo scope del generatore:

var iteratore = mostraCaratteri("HTML.it");
alert(iteratore.next());

La prima volta che si utilizza il metodo next verrà eseguita la parte del generatore precedente alla dichiarazione yield. In questo caso infatti l'alert sul metodo next restituirà 0 ed il codice all'interno del ciclo while non verrà eseguito, almeno non fino al prossimo next.

iteratore.next();

Un'ulteriore chiamata ed ecco che il ciclo while fa un primo passo, mostrando l'alert
con scritto "H" ed incrementando di uno la variabile index. Questa chiamata, se presente
all'interno di un alert, avrebbe infatti mostrato il valore 1, quello della variabile yield dopo la prima esecuzione del ciclo while.

Un modo per interagire al meglio è sfruttare try e catch poiché la fine della condizione del ciclo while del generatore causerà automaticamente un'eccezione di tipo StopIteration:

var iteratore = mostraCaratteri("HTML.it");
try {
    while (true)
        iteratore.next()
}
catch (err if err instanceof StopIteration) {
    alert("Fine dei caratteri");
}
catch (err) {
alert("Errore sconosciuto");
}

In questo caso è possibile gestire le eccezioni a seconda che siano state causate dalla fine dell'interazione o per altri motivi.

Dopo questo primo esempio è quindi possibile dire che un iteratore è un oggetto speciale capace di interagire con dei dati. Sebbene sembri una novità in realtà un iteratore è un oggetto invisibile che molti sviluppatori hanno usato inconsapevolmente ogni qualvolta, ad esempio, hanno scritto un loop di tipo for in o for each.

Oltre a poter gestire un generatore, l'iteratore è infatti anche in grado di interagire con oggetti di vario tipo come String, Array ed Object. Ovviamente non è necessario specificare una yield in questi casi: sarà sufficiente sfruttare l'apposita funzione Iterator, come nel seguente esempio:

var iteratore = Iterator(oggetto);

Prima di procedere, è importante focalizzare alcuni concetti. La successione delle informazioni restituite da un iteratore non è garantita in alcun modo e non ha una sequenza numerica ordinata, nemmeno con gli array. Sole stringhe potrebbero essere restituite in modo ordinato, ma non bisogna fare comunque a affidamento su questa casistica particolare. Ecco alcuni esempi di quanto appena detto:

var objI = Iterator({nome:"Andrea", cognome:"Giammarchi"});
alert(objI.next()); // ["nome", "Andrea"]
alert(objI.next()); // ["cognome", "Giammarchi"]
// nessuna successione chiave valore garantita
// (esempio cognome prima di nome per la c iniziale)
// avesse restituito prima cognome e poi nome non sarebbe stato un errore

var mioArray = [2];
mioArray[2] = 1;
mioArray[1] = 3;
var arrI = Iterator(mioArray);
alert(arrI.next()); // [0,2]
alert(arrI.next()); // [2,1]
alert(arrI.next()); // [1,3]
// nessun ordine garantito
// se avesse restituito le chiavi in sequenza sarebbe stato "un caso"

Salta subito all'occhio che la chiamata next dell'iteratore restituisce una coppia di chiavi/valori ma va ri-sottolineato che tale coppia non ha una successione garantita. Nel caso degli oggetti non c'è modo di sapere se la chiave "nome" sarà restituita prima o dopo la chiave "cognome" e questo comportamento è ancora più evidente con l'array, dove l'ordine sembra essere quello di assegnazione valori e non l'indice.

Stando a questa ultima constatazione, verrebbe da giustificare il comportamento anomalo con le stringhe, essendo queste a livello di interprete nient'altro che una sequenza (array) di caratteri immutabile. Ecco un esempio in grado di spiegare meglio quanto detto:

var mioarray = Iterator(
// sequenza di assegnazione ordinata
["H", "T", "M", "L", ".", "i", "t"]
);
var stringa = Iterator("HTML.it");

alert(mioarray.next()); // [0, "H"]
alert(stringa.next()); // [0, "H"]

alert(mioarray.next()); // [1, "T"]
alert(stringa.next()); // [1, "T"]

// ... da inizio a fine stringa

Presupporre che l'ordine sia rispettato è comunque un errore, anche se questo dovesse essere vero: il solo fatto che non è garantito consiglia di evitare di fare affidamento su questi ultimi concetti ed esempi poiché errati.

Qualora non interessi ricevere un array con chiave/valore ad ogni chiamata next, è possibile inizializzare la funzione Iterator con un secondo parametro di tipo booleano:

var iteratore = Iterator(oggetto, true);

In questo modo si riceverà solo la chiave successiva, sempre senza alcuna garanzia
sull'ordine di successione:

var alfabeto = ["a", "b", "c"];
var lettere = Iterator(alfabeto, true);
alert(lettere.next()); // 0
alert(alfabeto[lettere.next()]);// b

Per concludere con la funzione Iterator, c'è da tener presente che questa non accetta undefined o null, che con dati di tipo Boolean/Number/Date/RegExp non darà errore ma genererà l'eccezione StopIteraction alla prima chiamata next; mentre con le classi, prototipi di classe e gli altri tipi di oggetto non avrà alcun problema.

Comprendere gli Array

Durante la creazione di un array è possibile sfruttare un generatore al fine di popolare in un modo semplice e veloce la variabile in questione.

Facendo un breve ripasso si può ricordare che un iteratore è sempre stato presente in JavaScript, seppur nascosto, in ogni loop di tipo for in o for each. Da ciò si deduce che l'utilizzo di un ciclo for di questo tipo possa essere applicato ad un oggetto generico e quindi anche ad un generatore:

function caratteri(stringa){
    for(var index = 0; index < stringa.length; index++)
    // il generatore deve restituire il carattere successivo
    // durante l'interazione
    yield stringa.charAt(index);
};
// per ottenere tanti alert
// quanti sono i caratteri della stringa HTML.it
for(var carattere in caratteri("HTML.it"))
    alert(carattere);

Questo esempio potrebbe essere sfruttato anche per creare un array di caratteri, sicuramente inutile dato che il metodo di stringa split è molto più efficace e rapido, ma probabilmente interessante dal punto di vista descrittivo.

vararrayDaStringa = [carattere for(carattere in caratteri("HTML.it"))];
alert(arrayDaStringa); // H,T,M,L,.,i,t

Questo esempio è tanto semplice quanto rivoluzionario poiché permette una serie di operazioni durante la creazione di un array non possibili nelle precedenti versioni di JavaScript. Il primo punto da focalizzare è lo scope: tutto quello che è presente all'interno delle parentesi quadre è uno scope temporaneo ben definito in cui non è necessario dichiarare la variabile carattere poiché sarà comunque temporanea.

Il passo successivo è comprendere quali sono le possibilità durante la creazione dell'array:

array = [
  espressione => operazioni con la variabile temporanea,
      il valore che sarà inserito nella corrispettiva chiave
  for(
    la variabile temporanea da utilizzare nella prima espressione
    in
    oggetto/generatore
  )
  condizione => eventuale if per filtrare (o modificare) il dato
]

// in codice, altro esempio, stringa senza punti
var arrayDaStringa = [
  carattere
  for(
    carattere in caratteri("HTML.it")
  )
  if(
    carattere != "."
  )
];
alert(arrayDaStringa); // H,T,M,L,i,t

// esempio, stringa senza punti raddoppiata
var arrayDaStringa = [
  carattere + carattere
  for(
    carattere in caratteri("HTML.it")
  )
  if(
    carattere != "."
  )
];
alert(arrayDaStringa); // HH,TT,MM,LL,ii,tt

// esempio, da stringa a codice caratteri
var arrayDaStringa = [
  carattere.charCodeAt(0)
  for(
    carattere in caratteri("HTML.it")
  )
];
alert(arrayDaStringa); // 72,84,77,76,46,105,116

Esistono anche esempi numerici nella pagina della documentazione ufficiale ed è consigliabile visionarli qualora ci sia qualcosa di poco chiaro. Una volta compresi gli array, i generatori e gli iteratori, possiamo passare senza troppa difficoltà al let.

let

Tra le tante novità introdotte in Javascript 1.7, let è forse quella più interessante e probabilmente l'unica veramente inimitabile nelle precedenti versioni.

Il let nasce dall'esigenza di avere variabili a scope realmente locale. Dichiarare le variabili attraverso l'utilizzo di var non è infatti sufficiente a garantire che tali variabili non siano persistenti nelle restanti porzioni di scope. Un piccolo esempio potrà sicuramente spiegare ancora meglio questo concetto:

// funzione generica
function generica(
    // inizio scope di funzione, eventuali argomenti compresi
){

    // variabile globale, senza var
    stringa = "HTML.it";

    // variabile a scope locale,
    // valida da questo punto fino alla fine della funzione
    var length = stringa.length;

    // loop per scrivere i caratteri della stringa
    for(
    // variabile locale
        var index = 0;

        index < length;

        index++
    ){
        alert(stringa.charAt(index));
    };

    // in questo punto della funzione ci sono 2 variabili
    alert(length === index);// true, vero

    // la variabile index non viene quindi dichiarata all'interno dello
    // scope del ciclo for ma fino alla fine dello scope di funzione

}; // fine scope di funzione
generica();

Ogni qualvolta si utilizzi un ciclo for o quando, in generale, si dichiara una variabile all'interno di un blocco specifico, questa sarà inevitabilmente persistente per tutto lo scope della funzione e non solo all'interno del blocco stesso. Oltre a richiedere una porzione più o meno consistente di memoria, questa variabile potrebbe erroneamente essere dichiarata con un nome di variabile già utilizzato o richiedere di scegliere un nome differente anche se il più adatto sarebbe stato quello scelto per un'altra variabile.

Nelle vecchie versioni gli sviluppatori più accorti hanno utilzzato metodi poco ortodossi per aggirare il problema, ricordandosi di risettare la variabile temporanea come null o undefined, sperando che il garbage collector fosse in grado di liberare quanto prima la memoria allocata da queste variabili non più utili. In una prospettiva futura in cui il JavaScript richiederà sempre più risorse e dove questi accorgimenti, l'introduzione di un nuovo scope potrebbe incrementare in maniera rilevante le prestazioni di applicativi complessi permettendo di scrivere perfino meno codice di prima (ad esempio non dovendo reimpostare tutte le variabili non più utili). Tutto ciò sia per semplicità sia per linearità sia per controllo del codice.

Per finire, il JavaScript è un linguaggio dinamico che permette di cambiare continuamente tipo di dato ad ogni variabile e senza accorgimenti ma questa operazione comporta, a livello di interprete, operazioni continue di verifica e cambio allocazioni e sarebbe quindi una buona pratica evitare quando possibile queste operazioni. Discorso poco interessante? Lo sono sicuramente di più gli esempi pratici di utilizzo del let:

var stringa = "HTML.it";
for(let index = 0; index < stringa.length; index++)
    alert(stringa.charAt(index));
alert(index); // errore

Questo esempio base mostra come la variabile index abbia un valore solo all'interno del ciclo for. È possibile utilizzare let come una dichiarazione di variabile var, permettendo quindi anche la creazione di più variabili.

for(let index = 0, slength = stringa.length; index < slength; index++)
    alert(stringa.charAt(index));

let quindi permette, se usato in questo modo, di creare una o più variabili a scope locale, dove per locale si intende un blocco di codice:

if(navigator) {
    let browser = navigator.userAgent;
    var binfo = browser.split(" ");
};
alert(binfo);
alert(browser); // errore

let può essere usato anche come blocco di codice stesso e permette di creare variabili temporanee tra parentesi.

var stringa = "HTML";
let(stringa = stringa){
    stringa += ".it"
    alert(stringa); // HTML.it
};
alert(stringa); // HTML

// identico a ...
let(var_scope_del_blocco = valore_qualunque){
    operazioni con var_scope_del_blocco

    ...

    fine scope di var_scope_del_blocco
};

// altro esempio
let(html = "HTML", it = "it") {
    alert([html, it].join(".")); // HTML.it
}

Mentre l'utilizzo di let prima di una variabile è chiamato definizione let (let definition), questi ultimi esempi sono chiamati let statements. Non resta che vedere l'ultima variante chiamata espressione let (let expression), in parole semplici uno statement senza graffe che permette quindi una sola espressione, indentata o in linea:

let(a = 1, b = 2)
alert(a + b); // 3

// oppure
alert("1 + 2 è uguale a " + let(a = 1, b = 2) a + b + " !!!");

Assegnazione con destrutturazione

Altra innovazione di rilievo è la possibilità di creare più variabili con una sola operazione, che implica quindi la possibilità di far tornare più risultati ad una funzione. Sebbene tornare più valori sia una operazione comune (basti pensare agli array) non lo è altrettanto associare direttamente questi valori ad altre variabili:

function Sviluppatore(){
    return ["Andrea", "Giammarchi"];
};
var [nome, cognome] = Sviluppatore();

alert(nome);// Andrea
alert(cognome); // Giammarchi

Questa operazione può risultare estremamente comoda per una notevole quantità di operazioni comuni. Non è possibile infatti replicare il tutto se non assegnando ad una variabile apposita il risultato della funzione Sviluppatore per poi creare una variabile nome con chiave 0 del risultato ed una cognome con chiave 1. Niente di più quotidiano quanto macchinoso mentre in questo modo si possono fare operazioni diverse in modo semplice ed indolore:

// cambio di valori
var a = 1,
b = 2;

[a, b] = [b, a];

alert(a); // 2
alert(b); // 1

L'assegnazione può anche essere parziale ed è possibile quindi sfruttare solo alcuni valori:

function Sviluppatore(){
    return ["Andrea", "Giammarchi", 28];
};
var [, cognome, eta] = Sviluppatore();

alert(cognome); // Giammarchi
alert(eta);

[, , eta] = Sviluppatore();
alert(eta); // 28

var [nome, , eta] = Sviluppatore();
alert(nome);// Andrea
alert(eta); // 28

Un altro esempio sicuramente interessante è quello proposto dalla documentazione stessa, inerente la possibilità di assegnare valori differenti restituiti da una espressione regolare:

varurl = "http://html.it/index.html";
let(parsedURL = /^(w+)://([^/]+)/(.*)$/.exec(url)) {
    var [, protocol, fullhost, fullpath] = parsedURL;
};

alert(protocol.concat(" - ", fullhost, " - ", fullpath));
// http - html.it - index.html

L'assegnazione in destrutturazione permette anche di interagire con oggetti, stringhe o array in modo più comprensibile:

var developer = {name:"Andrea", surname:"Giammarchi"};
for(let [chiave, valore] in developer)
    alert(chiave.concat(" => ", valore));

L'ultima possibilità offerta è quella di sfruttare la destrutturazione per oggetti di vario tipo. Ad esempio è possibile andare a prendere solo determinati valori di un oggetto definendo il nome da utilizzare e l'alias con il valore:

var persone = [
    {name: "Andrea", surname: "Giammarchi"},
    {name: "Andrea", surname: "Marzilli"}
];

for each(let {surname: cognome} in persone)
    alert(cognome);

Quest'ultimo esempio mostrerà solo il cognome degli oggetti presenti nell'array di persone. Il successivo, invece utilizzerà il for in, senza each, al fine di restituire tutte le informazioni del caso:

for(let [index, {name: nome, surname: cognome}] in persone)
    alert("Posizione: " + index + "nPersona: " + nome + " " + cognome);

Abbastanza scontato quindi dedurre che la destrutturazione può essere anche innestata per coppie di chiavi e valori, o per meglio dire, alias:

var persone = [
    {name: "Andrea", surname: "Giammarchi", skills:{
        graphic:"bad",
        beer:"excelent"
    }},
    {name: "Andrea", surname: "Marzilli", skills:{
        graphic:"good",
        beer:"unknown"
    }}
];

for each(let {name: nome, surname: cognome, skills:{beer: birra}} in persone)
    alert("Persona: " + nome + " " + cognome + "nLivello di birra: " + birra);

Considerazioni finali

È presumibilmente scontato che queste novità facciano gola agli sviluppatori JavaScript ed il fatto che ancora non sia possibile utilizzarle in produzione per quegli applicativi che vorrebbero essere compatibili anche con altri browser lascia sicuramente un po' di amaro in bocca.

L'idea è sempre quella che se tutti i browser seguissero gli standard e si evolvessero anche nel JavaScript come sta facendo Firefox e come farà prossimamente ancora di più Mozilla, il Web avrebbe a disposizione potenzialità migliori, più performanti e meno dispendiose, sia per lo sviluppo che per la quantità di codice. Non resta che continuare il passaparola, Get Firefox, al fine di contribuire alla diffusione di uno dei browser migliori in termini di sicurezza e tecnologia in circolazione.

Ti consigliamo anche