Negli ultimi anni si è assistito al proliferare di siti web che fanno un sempre più largo uso (spesso indiscriminato) di Javascript ed Ajax. Il progresso dei browser, con l'adeguamento graduale alle specifiche Ecmascript, e la comparsa di librerie Javascript come JQuery, Mootols, Prototype,YUI Library, Dojo, Ext, hanno consentito agli sviluppatori di creare con più facilità interfacce usabili e altamente interattive (spesso anche accessibili): finestre modali e lightbox, tooltip e messaggi di notifica, slideshow e gallerie di immagini, funzionalità drag&drop, menu a scomparsa, campi ad autocompletamento, la disponibilità di plugin di terze parti è praticamente infinita.
Un utilizzo improprio e massiccio di Javascript e la scrittura di codice poco efficiente possono però fortemente penalizzare le prestazioni di un sito, influenzando negativamente la velocità di caricamento delle pagine e il tempo di esecuzione degli script. Per tastare con mano un buon lavoro di ottimizzazione, basta navigare verso i siti web del momento: Facebook, Gmail, Google Maps, YouTube, Vimeo, FlickR, anche Ebay... tutte applicazioni che utilizzano una buona dose di Javascript e Ajax, ma che pure conservano una velocità di navigazione snella e piacevole.
Tra i fattori che condizionano le performance di un sito web, totalmente fuori dal nostro controllo e altamente condizionante è la velocità della connessione internet del navigatore. Anche la disponibilità di banda e la velocità del server in cui risiede il sito sono importanti, ma un hosting performante non può comunque sopperire a carenze di tipo progettuale. Allo sviluppatore non è dato inoltre di conoscere l'hardware, il sistema operativo e il browser dell'utente, elementi anche questi determinanti essendo Javascript un linguaggio eseguito sul client. In particolare ogni browser adotta un engine proprietario, con prestazioni anche notevolmente differenti: dovremo quindi trovare delle soluzioni di compromesso che consentano il risultato migliore sulle diverse piattaforme hardware e software.
Strumenti utili per lo sviluppatore
Proprio i browser (ma solo quelli più moderni) sono dotati di funzionalità o estensioni ormai indispensabili nello sviluppo di una applicazione web, anche dal punto di vista dell'ottimizzazione.
Per Mozilla Firefox c'è un plugin, Firebug (quì una guida interessante), eccone le principali caratteristiche: analisi della struttura HTML e dei CSS, DOM inspector, pannello risorse delle richieste HTTP, con informazioni sui file, sul peso degli elementi e sulla cache del browser. Inoltre è presente una vera e propria console per il debug degli errori Javascript e il profiling degli script, che mette a disposizione anche una serie di comandi; una volta abilitato, per accedere a Firebug basta cliccare sull'icona dello "scarafaggio" in basso a destra sulla barra di stato del browser.
Nello specifico è possibile estendere Firebug con due ulteriori plugin, sviluppati rispettivamente da Yahoo e Google, che permettono una analisi ancora più accurata delle pagine web e forniscono un report con suggerimenti per migliorarne le prestazioni: si tratta di Yslow e Page Speed.
Nelle più recenti versioni di Apple Safari e Google Chrome non è necessario installare plugin aggiuntivi, contengono infatti nativamente degli strumenti simili a Firebug: il menu sviluppo di Safari non viene mostrato di default ma va abilitato nelle preferenze, nella scheda Avanzate: analogamente le opzioni per sviluppatori di Chrome sono raggiungibili cliccando sul menu Pagina. Entrambi i browser mettono a disposizione un debugger e profiler Javascript (sono supportate le API della console di Firebug), un accurato pannello risorse e un DOM Inspector. In accoppiata con Chrome si può installare anche Speed Tracer, un tool specifico per monitorare le prestazioni delle pagine.
Con l'ultima versione (8), anche Internet Explorer si è dotato di una console degna di questo nome (accessibile premendo F12), dotata delle principali features offerte dai concorrenti; da sottolineare però la mancanza di un pannello risorse e il solo parziale supporto ai comandi delle API di Firebug.
Ridurre il tempo di caricamento
Siamo nel primo fondamentale livello di tuning della nostra applicazione: il tempo di caricamento di una pagina web (loadtime) è influenzato principalmente dal peso effettivo della pagina (e degli elementi che la compongono) e dal numero di richieste HTTP effettuate (per file Javascript, CSS, immagini, contenuti Ajax, oggetti Flash, etc).
Ridurre la complessità della struttura HTML del documento (DOOM complexity)
In linea generale la prassi consiste nel passare in esame il codice HTML, per eliminare i tag vuoti inutili, gli spazi bianchi, i commenti HTML, l'uso ridondante di nodi annidati (come ad esempio i <div>
). Sono da controllare anche gli stili CSS, che non devono essere dichiarati inline nel documento, ma vanno collocati in un file CSS esterno. La stessa cosa vale per il codice Javascript. Una struttura HTML più semplice, nel pieno rispetto della semantica, oltre a essere renderizzata più velocemente dal browser, giova anche al tempo di esecuzione dei nostri script se questi devono parsare il DOM per svolgere le varie operazioni.
Ottimizzare la grafica
È ovvio ricordare di salvare nei formati appropriati (gif, jpg, png) e comprimere adeguatamente le immagini e la grafica con gli appositi sofware di fotoritocco: si devono utilizzare thumbs non più grandi del necessario e non immagini scalate solo perché è possibile impostarne la larghezza e l'altezza in HTML. Per le immagini di background è assolutamente raccomandato l'uso degli sprites CSS: combinare più immagini di sfondo in una singola aiuta a contenere il numero delle richieste HTTP.
Sfruttare la cache del browser
L'utilizzo della cache consente un più rapido caricamento di una pagina web nelle visite successive alla prima e alleggerisce il carico sul server web: impostando una data di scadenza negli header HTTP per le risorse statiche (come file CSS, Javascript, oggetti flash, immagini) è possibile risparmiare richieste HTTP evitando lo scaricamento delle risorse da remoto.
Se il server web del nostro hosting è Apache, sfruttando il modulo Mod_Expires possiamo utilizzare un semplice file.htaccess (da caricare nella root del sito):
ExpiresActive on
ExpiresByType image/gif “access plus 5 days”
ExpiresByType image/png “access plus 5 days”
ExpiresByType image/jpeg “access plus 5 days”
ExpiresByType text/css “access plus 1 month”
ExpiresByType application/x-javascript “access plus 1 month”
ExpiresByType text/javascript “access plus 1 month”
ExpiresByType application/pdf “access plus 1 month”
ExpiresByType application/x-shockwave-flash “access plus 1 month”
FileETag none
A seconda della configurazione e della versione di Apache, potrebbe essere necessaria una diversi sintassi, con il modulo Mod_Headers:
# 3 mesi<FilesMatch ".(flv|gif|jpg|jpeg|png|ico|swf)$">Header set Cache-Control "max-age=7257600"</FilesMatch># 1 settimana<FilesMatch ".(js|css|pdf|txt)$">Header set Cache-Control "max-age=604800"</FilesMatch># 10 Minuti<FilesMatch ".(html|htm)$">Header set Cache-Control "max-age=600"</FilesMatch># No cache<FilesMatch ".(pl|php|cgi|spl)$">Header unset Cache-ControlHeader unset ExpiresHeader unset Last-ModifiedFileETag NoneHeader unset Pragma</FilesMatch>
Attraverso il pannello risorse di Firebug, Chrome o Safari è molto facile verificare il corretto funzionamento della cache e la sua influenza sul peso complessivo della pagina.
La compressione Gzip
Servire al browser dei file compressi dal server secondo l'algoritmo gzip è il modo migliore per ridurre il peso di Javascript e CSS, con rapporto di compressione prossimo al 75%! La configurazione di Apache con un file .htaccess è un gioco da ragazzi:
AddOutputFilterByType DEFLATE text/html text/plain text/xml application/xml application/xhtml+xml text/javascript text/css application/x-javascript
La compressione Gzip è ad esempio utilizzata nelle Google Ajax Libraries, una sorta di repository dei più comuni framework Javascript, in versione compressa e minimizzata pronta all'uso.
Minimizzare Javascript e CSS
Sempre alla ricerca della riduzione di peso, è possibile minimizzare file Javascript e CSS, eliminando commenti, spazi e ogni forma di indentazione. È ovvio che una tale pratica rende praticamente impossibili una successiva modifica del codice e il debug degli errori Javascript, quindi l'operazione di minimizzazione andrà effettuata solo alla fine del nostro lavoro, predisponendo una versione di sviluppo e una di produzione per ciascun file compresso. Uno dei migliori tool disponibili è lo YUI Compressor , che dispone di numerose opzioni per la compressione di Javascript e CSS. Essendo realizzato in Java il programma va eseguito da linea di comando; ci sono comunque anche una versione online e una comoda gui per Windows. Un altro validissimo "Javascript Minifier" è il Packer di Dean Edwards. In questo caso una raccomandazione importante: utilizzare solo shrink variables, senza spuntare l'opzione Base62 encode che pure garantirebbe un maggior rapporto di compressione. Questo per evitare di usare il costrutto eval()
che richiederebbe un maggiore lavoro al browser per decodificare e interpretare il codice.
Anche questa pratica produce ottimi risultati, arrivando in alcuni casi a una riduzione del peso complessivo vicina al 50%. I più diffusi framework Javascript e i relativi plugin sono spesso disponibili per il download oltre che in formato sorgente anche in versione già minimizzata pronta per l'uso.
Nelle tabelline della figura 2, ottenute attraverso l'analisi del pannello risorse di Firebug, è possibile verificare i risultati delle diverse modalità di compressione su una stessa pagina in cui vengono inclusi il framework Prototype e alcuni componenti della libreria Scriptaculous.
Concatenare File Javascript e CSS
Il browser di default non può aprire più di 2 o 4 connessioni simultanee verso il server nel download dei vari file inclusi in un documento: inoltre il browser per questioni procedurali, allo scopo di evitare conflitti tra script interdipendenti, può caricare un solo file Javascript alla volta, secondo l'ordine di inclusione nella pagina. Come già detto la riduzione del numero di richieste HTTP è una pratica caldamente incoraggiata: può essere allora molto proficuo unire i vari file Javascript utilizzati nella nostra applicazione in un unico monolitico file: e lo stesso vale per i vari fogli di stile CSS. Come per la minimizzazione, la portabilità del codice viene fortemente penalizzata, quindi anche in questo caso dovremo predisporre una versione di sviluppo e una di produzione.
Non sempre concatenare i file è la scelta giusta: se ad esempio numerosi script vengono utilizzati solo in alcune determinate pagine, può essere utile differenziarne il caricamento, onde evitare di appesantire inutilmente il resto del sito con larghe porzioni di codice Javascript inutilizzato.
Concatenare e comprimere i file con PHP
Gzoutput è un classe in PHP 4/5 (disponibile nella versione più aggiornata su PHP Classes) scritta da Andrea Giammarchi che in un colpo solo permette di attuare le tecniche descritte nelle precedenti sezioni: tramite la configurazione di un piccolo script PHP è possibile comprimere con gzip e concatenare e in un singolo file i nostri file Javascript e CSS senza alterarne la struttura. Non solo, i dati ottenuti possono essere memorizzati dallo script in una cartella sul server per evitare la rielaborazione nelle richieste successive, ed è possibile eliminare automaticamente dal codice spazi vuoti e commenti (con l'utilizzo di una classe aggiuntiva). Gzoutput dispone di numerosi metodi pubblici e opzioni avanzate che consentono diverse modalità di utilizzo, illustrate accuratamente nella documentazione: la via di implementazione più semplice e veloce prevede l'inclusione nel documento di un file che chiameremo "bootstrap.php" da collocare nella stessa directory dei file da trattare.
<html>
<head>
<title>Gzoutput</title>
<script type="text/javascript" src="js/bootstrap.php"></script>
</head>
<body>
<h1>Gzoutput: esempio di utilizzo</h1>
</body>
</html>
Il file "bootstrap.php" è molto semplice, essendo costituito dall'include con il percorso alla classe e da un array degli script da utilizzare (in questo caso, gli stessi dei precedenti esempi di codice), passato come primo argomento al metodo pubblico createFromList()
di Gzoutput; nel secondo argomento è necessario specificare il tipo di file, text/javascript, o text/css, se vogliamo concatenare diversi fogli di stile, predisponendo per essi un apposito "bootstrap.php" nella cartella in cui risiedono i CSS.
<?php
require("../include/GzOutput.class5.php");
GzOutput::createFromList(array("prototype.js", "scriptaculous.js", "effects.js", "dragdrop.js", "controls.js"), 'text/javascript');
?>
Come visibile nella figura 3, la riduzione di peso determinata da Gzoutput è analoga a quella ottenuta con la compressione gzip attraverso l'uso del file htaccess negli esempi precedenti.
Tra le alternative a Gzoutput, sempre utilizzando il linguaggio server PHP, sono da segnalare: Smartoptimizer, PHPSprocket (il porting di una gemma di Ruby) e Minify.
La seconda parte di questa guida di ottimizzazione sarà online la prossima settimana; vedremo l'utilizzo di due plugin (con Jquery e Prototype) per il benchmark degli script e numerosi esempi per migliorare l'efficienza e la qualità del nostro codice. Alla prossima!
Abbattere il tempo di esecuzione degli script.
Dopo la fase di "dimagrimento" della nostra applicazione, il lavoro di ottimizzazione non è per niente terminato. Infatti, potrebbe esserci ancora qualcosa a guastare la user-experience del sito: l'esecuzione degli script avviene talvolta con un piccolo ma percettibile delay rispetto al momento in cui avremmo voluto che avvenisse? Le animazioni e le dissolvenze sono "scattose"? La risposta alle interazioni dell'utente non è repentina? Peggio, la lentezza delle chiamate Ajax pregiudica il caricamento dei dati? È possibile allora che il nostro codice Javascript sia poco efficiente, gravando il browser con calcoli magari inutili o eccessive operazioni di parsing.
L'utilizzo di uno tra i più noti framework Javascript può aiutare ad evitare comuni problemi di performance (es. memory leaks), assicurando anche una maggiore compatibilità cross-browser: il continuo aggiornamento e il lavoro di sviluppo di un team di esperti programmatori sono una garanzia. I pregi di queste librerie rappresentano però un'arma a doppio taglio: l'estrema facilità di impiego e lo "snaturamento" del linguaggio Javascript possono condurre ad uso deleterio di questi strumenti. I bug prestazionali potrebbero derivare proprio dal nostro codice, quello realizzato da noi stessi, snippet che magari riutilizziamo nei vari progetti. Una semplice funzione riscritta diversamente può diminuire il tempo di esecuzione di uno script e alleggerire il carico di lavoro dell'engine Javascript del browser.
Possiamo testare le performance degli script tramite le API della console di Chrome, Safari e Firebug: con i comandi console.time
e console.timeEnd
è possibile visualizzare in un log il tempo di esecuzione di una funzione o di un ciclo (esempio):
<script>
console.time('constructor');
for ( var i=0; i < 100000; i++) {
var arr = new Array();
}
console.timeEnd('constructor');
console.time('literal');
for ( var i=0; i < 100000; i++) {
var arr = [];
}
console.timeEnd('literal');
</script>
Purtroppo per Internet Explorer, queste specifiche funzionalità non sono supportate nemmeno nell'ultima versione del browser Microsoft. Per supplire a questa mancanza, ho arrangiato un paio di plugin, uno basato su Prototype (esempio), l'altro su JQuery (esempio), che utilizzo nella fase di debug a seconda del progetto.
/* uso di jquery */
<script type="text/javascript" src="js/jquery-1.4.2.js"></script><script type="text/javascript" src="js/jquery.inherit-1.3.1.js"></script><script type="text/javascript" src="js/jquery-timer.js"></script>
/* uso di prototype */
<script type="text/javascript" src="js/prototype.js"></script><script type="text/javascript" src="js/prototype-timer.js"></script>
// common
var test = new timer();
test.start('example');
myFunc();
test.end('example');
In entrambi i casi per inizializare il benchmark basta istanziare la classe e richiamare il metodo start()
subito prima del codice da valutare; il metodo stop()
, va viceversa eseguito subito dopo e attiva l'output del log, nella console, per i browser che ne forniscono il supporto, e in un classico alert per gli altri. Naturalmente i due metodi richiedono il passaggio di un parametro identificativo, uno stesso nome univoco, permettendo così il test contemporaneo anche di più parti di codice. Sia chiaro: i risultati ottenuti sono spesso approssimativi e comunque non bisogna attribuire ad essi un valore assoluto.
Già confrontando i risultati ottenuti dal codice dei tre esempi precedenti, è possibile fare una importante constatazione: nella dichiarazione di un array (o di un oggetto Javascript), la forma letterale ([]
, {}
) è più performante del tradizionale costruttore (new Array()
, new Object()
).
DOM scripting.
Negli script che devono percorrere il documento alla ricerca degli elementi su cui scatenare un evento o svolgere una operazione, le funzionalità dei framework rendono la vita facile agli sviluppatori: non solo, il selector engine integrato (ad esempio Sizzle) permette di utilizzare i selettori CSS3 per accedere ai vari elementi della pagina, in maniera indifferente dal browser.
Attenzione però, proprio i selettori CSS vanno usati senza esagerazione: sono comodi, ma comunque più lenti di altre soluzioni, magari vecchio stile, ma efficaci. Il metodo di selezione più veloce in assoluto è invece il riferimento al nodo interessato tramite id (document.getElementById()
). In altri casi, in cui è necessario accedere a molteplici nodi, può essere preferibile il riferimento per tipo di tag (getElementsByTagName()
).
Comunque, volendo ottimizzare l'uso dei selettori CSS è bene fare attenzione alle regole utilizzate che non devono essere né troppo generiche (es. 'div', 'a') né troppo specifiche (es. 'div[class$="content"] div.module input:disabled'
).
Nel test seguente vengono applicati due script simili per rendere tutte le tabelle di una pagina a righe alternate. Mentre con i browser più moderni (leggi Firefox, Safari e Chrome) non si riscontrano differenze particolari, con Internet Explorer il codice in Javascript classico (esempio) è nettamente più veloce rispetto rispetto a quello (veramente conciso) che utilizza i metodi del framework JQuery (esempio). E anche utilizzando librerie alternative i risultati sarebbero comunque similari. Chiaramente si tratta di un esempio estremo, volutamente gravoso per l'engine del browser che deve attraversare ben 8 tabelle da 500 righe ciascuna; in casi normali la differenza temporale di esecuzione è meno evidente.
/* Classic DOM */ (veloce in IE)
var tables = document.getElementsByTagName("table");
for ( var t = 0; t < tables.length; t++ ) {
var rows = tables[t].getElementsByTagName("tr");
for ( var i = 1; i < rows.length; i += 2 )
rows[i].className += "oddrow";
}
/* Jquery */ (lento in IE)
$("table tr:nth-child(even)").addClass("oddrow");
/* Prototype */ (lento in IE)
$$('table tr:nth-child(even)').invoke('addClassName', 'oddrow');
Anche nella manipolazione del DOM, per la creazione, inserimento o aggiornamento di contenuti nel documento, è possibile tentare diversi approcci alla ricerca della migliore performance. Eterna è la diatriba sull'utilizzo del metodo innerHTML
: i puristi degli standard ci hanno sempre raccomandato di preferire le API del W3C DOM. Tuttavia, oggi il supporto ad innerHTML
è ormai presente su tutti i principali browser; inoltre, il codice, oltre ad avere una forma più concisa, è anche decisamente più veloce nell'esecuzione (specialmente con i browser Microsoft). Nel seguente esempio un confronto tra i due metodi applicati per inserire dinamicamente nel documento una tabella di 50 righe:
// metodo W3C DOM (lento)
var x = document.createElement('table');
x.setAttribute("class","myTable");
var y = x.appendChild(document.createElement('tbody'));
for (var i=0;i<50;i++) {
var z = y.appendChild(document.createElement('tr'));
for (var j=0;j<50;j++) {
var a = z.appendChild(document.createElement('td'));
a.appendChild(document.createTextNode('*'));
}
}
document.getElementById('placeholder-1').appendChild(x);
// innerHTML (veloce)
var string = '<table class="myTable"><tbody>';
for (var i=0;i<50;i++) {
string += '<tr>';
for (var j=0;j<50;j++) {
string += '<td>*</td>';
}
string += '</tr>';
}
string += '</tbody></table>';
document.getElementById('placeholder-2').innerHTML = string;
Termina qui la seconda parte dell'articolo. Concluderemo la settimana prossima parlando del formato di dati Ajax, della gestione delle stringhe, dell'utilizzo delle variabili e della gestione dell'evento onload
.
Il formato dei dati Ajax.
Ajax è sicuramente una delle funzionalità che più ha contribuito al successo e alla diffusione dell'utilizzo di Javascript nello sviluppo di interfacce web: in alcuni contesti può essere molto utile fornendo anche all'utente una piacevole sensazione di "prontezza" dell'applicazione, con l'aggiornamento asincrono dei contenuti delle pagine senza il classico refresh. Affinché questo avvenga le risposte provenienti dal server devono essere veloci: è bene dunque fare attenzione ai formati per veicolare questo tipo di dati che sono sostanzialmente tre, HTML, XML e JSON.
- HTML è il formato che garantisce response più veloci: i contenuti HTML possono essere statici o generati direttamente sul server e inseriti nel documento come semplici stringhe, tramite
innerHTML
, senza necessità di parsing. Lo svantaggio principale è che può occupare molta banda e aggravare le risposte con un peso maggiore in termini di kb; se la quantità di dati è elevata, probabilmente è preferibile utilizzare JSON o XML. - Il formato XML vanta una grande diffusione, essendo un metalinguaggio adattissimo a contenere dati strutturati; anch'esso (seppure in misura minore rispetto al precedente) presenta una forma piuttosto verbosa che può incidere sui tempi di trasferimento. Inoltre il codice Javascript per utilizzare i dati XML deve necessariamente effettuarne il parsing tramite i metodi del DOM e l'output è di fatto generato non più sul server ma sul client. Quindi la scelta di XML può rivelarsi quella meno favorevole, sia per i tempi di caricamento che per la velocità di esecuzione degli script.
- Molti esperti considerano JSON l'erede di XML come linguaggio per l'interscambio di dati, e in effetti sta riscuotendo un notevole successo: fondamentalmente esso utilizza la notazione letterale di Javascript per rappresentare i contenuti in oggetti e array e strutturalmente quindi è molto leggero. Leggere un oggetto Javascript, tramite
eval()
o i metodi specifici disponibili in ogni framework, è molto più semplice e veloce che parsare l'XML. Da ultimo, come XML, anche JSON è facilmente generabile lato server tramite linguaggi come PHP.
Nell'esempio seguente vengono utilizzate le funzionalità Ajax di JQuery per aggiornare i contenuti della pagina, attingendo a tre diversi sorgenti di dati, HTML, XML o JSON; dalla console sviluppatori del browser è possibile verificare il tempo e la dimensione delle varie risposte. In questo caso i risultati del benchmark sono piuttosto livellati; a causa della semplicità dei dati richiesti, non si riscontrano differenze apprezzabili nell'utilizzo dei diversi formati.
Utilizzo corretto delle variabili.
Le variabili Javascript sono dei contenitori atti a memorizzare varie informazioni da riutilizzare successivamente per operazioni di lettura o modifica. Mentre le variabili locali esistono solo nell'ambito della funzione in cui sono state dichiarate e vengono cancellate alla fine dell'esecuzione dello script liberando memoria, le variabili globali, una volta dichiarate, durano per tutta la durata della pagina e possono essere referenziate da qualsiasi punto all'interno del nostro codice. Se le informazioni veicolate nelle variabili non vengono frequentemente riutilizzate da più metodi degli script, l'utilizzo delle variabili globali comporta solo una maggiore spesa di risorse per l'engine del browser (esempio).
// variabili globali (lento)
var i, string = '';
function testGlobalVariables() {
for (i=0; i < 10000; i++) {
string += i;
}
}
// variabili locali (veloce)
function testLocalVariables() {
var i, string = '';
for (i=0; i < 10000; i++) {
string += i;
}
}
In caso di operazioni ripetitive di lettura o scrittura su elementi o proprietà out of scope, è una buona pratica memorizzarne il riferimento in una variabile locale esterna al ciclo (esempio).
// lento
function uncached() {
for (var i=0; i < 500; i++) {
document.getElementById('placeholder').innerHTML = window.location.href ;
}
}
// veloce
function cached() {
var url = window.location.href, div = document.getElementById('placeholder');
for (var i=0; i < 500; i++) {
div.innerHTML = url;
}
}
Gestione delle stringhe.
Operazioni ripetitive di concatenazione di più stringhe possono influire sulla velocità di esecuzione del codice; a seconda del contesto di utilizzo, bisogna tentare diversi approcci, perché anche in questo caso il comportamento dei browser non è uniforme. Il metodo più ovvio è solitamente anche il più performante, con l'assegnazione diretta tramite i simboli +=
. La tecnica che impiega un array temporaneo con i metodi push()
per aggiungere e join()
per unire le stringhe è molto razionale ed elegante, ma spesso non altrettanto efficiente. Una soluzione di compromesso può consistere nell utilizzare un indice per incrementare l'array senza invocare push()
. Ecco l'esempio con le tre tecniche applicate.
/* concatenazione di stringhe con += */
// veloce in IE
function test1() {
var mylongString = '';
for (var i = 0; i < 100000; i += 1) {
mylongString += 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
}
}
/* concatenazione di stringhe con array.push() */
// lento in IE
function test2() {
var temp = [];
for (var i = 0; i < 100000; i += 1) {
temp.push('ABCDEFGHIJKLMNOPQRSTUVWXYZ');
}
var mylongString = temp.join('');
}
/* concatenazione di stringhe know index */
// veloce in Firefox
function test3() {
var temp = [];
for (var i = 0; i < 100000; i += 1) {
temp[i] ='ABCDEFGHIJKLMNOPQRSTUVWXYZ';
}
var mylongString = temp.join('');
}
Un cenno infine alle espressioni regolari, croce e delizia di ogni programmatore, fenomenali strumenti per la comparazione e verifica delle stringhe, ma assai dispendiose; se se ne può fare a meno, sostituendole ad esempio con i semplici metodi delle stringhe, ben venga. In ogni caso, può portare giovamento precompilare la regular expressions, assegnandone il valore in una variabile globale riutilizzabile.
Non attendere onload.
Nella maggior parte dei casi, invece di attendere l'evento onload
, che è attivato solo quando è stato completato il caricamento di tutte le risorse (come le immagini, i fogli di stile inclusi, etc), i nostri script possono essere eseguiti appena l'albero del documento è stato completamente costruito. La tecnica più semplice consiste nell'includere tutto il blocco di script alla fine del documento, subito prima della chiusura del tag body
, come negli esempi di codice presentati. Alternativamente ogni framework Javascript dispone ormai di un proprio gestore domReady, che rappresenta il luogo ideale per attaccare tutti i gestori di evento ed eseguire altro codice, non appena il DOM è pronto.
/* Jquery */
$(document).ready(function() {
// Code
});
/* Prototype */
document.observe('dom:loaded', function() {
// Code
});
/* MooTools */
window.addEvent('domready', function() {
// Code
});
/* Yahoo! UI libarary */
Y.on("domready", function(e){
// Code
});
/* Ext JS */
Ext.onReady(function(){
// Code
});
Solo in casi limitati è necessario affidarsi ancora al gestore onload, come per il precaricamento delle immagini o quando è necessario ottenere delle informazioni sulle dimensioni degli elementi.
Considerazioni finali.
Gli aspetti del codice che abbiamo preso in esame, pur essendo tra i più critici per le prestazioni di una applicazione web, non sono un elenco esaustivo delle problematiche di performance riscontrabili nell'uso del linguaggio Javascript. Per un approfondimento degli argomenti, non mi rimane che segnalare allora alcune guide e risorse utili:
- Best Practices for Speeding Up Your Web Site (Developer Yahoo).
- Let's make the web faster (Google).
- Web Performance Best Practices (Google).
- Efficient Javascript
(Dev.Opera), con particolare riguardo alla sostituzione di costrutti deprecati comewith
,eval
etry catch
. - 10 Ways to Instantly Increase Your jQuery Performance (Net.Tuts)
- Il recente libro di Thomas Fuchs (mir.aculo.us), Javascript Performance Rocks.
Tutti gli esempi visti online sono disponibili anche per il download.