Quelle che seguono sono una serie di convenzioni e best practices JavaScript frutto di più
svariati anni di esperienza sul campo, sia nella direzione di progetti software
e web design, che nella gestione dei team di sviluppo.
Lo scopo di queste convenzioni non è solo quello di facilitare la produzione di
codice corretto e performante, ma soprattutto di aumentarne la leggibilità e dunque
di conseguenza la manutenibilità.
Come regola generale mi sono sempre attenuto alla direttiva che se dovevo scegliere
fra un costrutto semantico complesso ma performante e uno meno performante ma più
leggibile, ho sempre scelto il secondo; la manutenzione del software è ben risaputo
che ricade sui costi di produzione complessivi anche del 70%, ma purtroppo poiché
nel ciclo di vita è l'ultima ad essere affrontata, spesso si tende a rimandare il
problema, salvo poi trovarsi con programmi poco performanti o peggio ancora
error prone (cioè che per quanto ci si lavori continuano ad avere problemi).
Nel caso si operi da soli le convenzioni potrebbero sembrare non necessarie (credetemi
non è così), nel caso invece di team di sviluppo sono praticamente obbligatorie:
pensate a cosa accadrebbe se ogni programmatore scrivesse il codice alla sua maniera,
il risultato finale sarebbe una specie di Babele in cui nessuno saprebbe mettere
le mani. La regola a cui mi sono sempre attenuto con i miei team è leggendo
il codice non deve essere possibile riconoscere l'autore.
In questo articolo analizzeremo solo il caso JavaScript, cioè solo un sottoinsieme
di tutte le convenzioni che ho redatto negli anni, anche se molte di queste regole
possono essere applicate anche ad altri linguaggi di programmazione. Resta inteso
che molte delle seguenti sono solo convenzioni, dunque non "giuste" per definizione
e nemmeno l'unica scelta possibile: quello che è importante è che stabiliate delle
regole, quelle descritte qui a seguire o altre a vostro piacere.

The rest of the article is only at a click distance...
To continue reading, share this article with your followers
- Tutto il codice (i commenti, i nomi di variabile, i nomi di classe, ecc...)
va scritto in Inglese.
- Una lingua va stabilita affinché tutto il team usi la stessa (immaginate
se i commenti fossero scritti un lingue differenti). L'Inglese spesso si rivela
la scelta migliore, infatti i linguaggi di programmazione sono in Inglese e
dunque i commenti e le variabili si compongono meglio con essi.
- Non omettere il punto e virgola al termine di ogni comando.
- Quando si combina codice HTML con codice JavaScript, delimitare le stringhe
HTML con doppi apici e le stringhe JavaScript con apici singoli:
<a href="" onclick="alert('You clicked me!')">
- I nomi delle variabili e delle funzioni (per una convenzione
usata globalmente) sono lower camel case, ovvero non usano gli underscore
ed iniziano per lettera minuscola.
I nomi delle variabili e delle funzioni (per una convenzione
usata globalmente) sono lower camel case, ovvero non usano gli underscore
ed iniziano per lettera minuscola.
I nomi delle classi iniziano con una lettera maiuscola.
Quando si tratta di eventi ed event handler,
tutte le parole sono minuscole (es. onmyeventhandler()
).
- Queste sono convenzioni adottate globalmente.
- I nomi delle funzioni e delle proprietà private devono
iniziare con un underscore.
- Viene utilizzata poiché che in JavaScript non esiste il concetto di privato.
- I nomi dei namespace (vedi più avanti le raccomandazioni
sui namespace) dovrebbero essere del seguente
formato: CompanyName.TechnologyName[.Design], ad esempio
ACME.BusinessObject.Design
- Limitare la larghezza delle righe.
- Non deve essere necessario utilizzare la barra di scorrimento orizzontale
per leggere il codice, ne beneficerà la velocità di lettura e comprensione.
- L'indentatura del codice non deve avere incrementi
superiori ad una tabulazione.
- Serve ad evitare di dover fare formattazioni articolate difficili da manutenere
e soprattutto illeggibili su un normale schermo.
- Le parentesi { e } vanno allineate verticalmente.
- I pro rispetto al non andare a capo con la parentesi aperta sono che a colpo
d'occhio è più facile individuare i blocchi annidati di codice, i contro ovviamente
sono che si allunga di una riga il codice con ogni parentesi aperta. L'importante
qui è che semplicemente facciate una scelta e la manteniate.
- Raggruppare tutte le dichiarazioni all'inizio della funzione.
- Poiché le variabili JavaScript non hanno block-level scope (cioè
sono definite in tutta la funzione, indipendentemente dal punto in cui sono
dichiarate), tanto vale raggruppare tutte le dichiarazioni all'inizio per avere
rapidamente un sommario.
- La spaziatura verticale all’interno delle funzioni non
deve essere superiore alla linea singola.
La spaziatura orizzontale non deve essere superiore al singolo
spazio.
Gli operatori vanno separati con uno spazio, mentre le parentesi vanno attaccate
alle espressioni.
- Mantenere queste semplici regole potrebbe tornare utile in futuro in eventuali
ricerche e sostituzioni (ad esempio con le espressioni regolari):
a=b+c; // NO
a = b + c;
if ( a == c || ( ( a < b ) && ( d > f ) ) ) // NO
if (a == c || ((a < b) && (d > f)))
- Ogni funzione (il cui nome non sia già sufficientemente esplicativo) deve
avere un commento immediatamente sopra al testo della definizione.
IsEmpty = function() { ... };
Per la precedente funzione probabilmente non serve nessun commento, il nome
è già di per se esplicativo.
customers = new Array();
- I commenti sono essenziali e devono essere frequenti (circa uno ogni 4 righe),
ma non devono essere inutili. Un buon commento offre una visione astratta sul
codice che lo segue, non ripete quello che il codice già "dice".
if (customers.count > 1000)
alert('Numero massimo di clienti raggiunto!');
Quello precedente è un pessimo commento, primo perché fa riferimento a oggetti
e proprietà (invece che a concetti astratti), secondo perché fa riferimento
al valore di una costante (potrebbe anche cambiare un giorno) e infine perché
fa riferimento alla funzione alert
invece che al concetto.
Giusto sarebbe stato:
if (customers.Count > 1000)
alert('Numero massimo di clienti raggiunto!');
- Se una condizione all'interno di un ciclo ritorna sempre lo stesso valore,
evitare di ricalcolarla ad ogni iterazione.
for (i = 0; i < getTimeConsumingValue(); i++) // NO
tcv = getTimeConsumingValue();
for (i = 0; i < tcv; ++i)
- Dichiarare sempre le variabili locali utilizzando
var
ed inizializzarle
al momento della loro dichiarazione.
- Serve ad evitare di creare o sovrascrivere variabili globali.
- Inizializzare sempre le variabili come minimo a
null
e non
usare quelle indefinite.
- Una variabile non definita, non viene automaticamente inizializzata
a
null.
- Non dimenticarsi mai di inizializzare le costanti.
- Niente vieterebbe di dichiarare una costante senza inizializzarla.
- Una funzione non deve fare niente di più di quello che dice il suo nome,
non ci devono essere effetti collaterali.
- Da un metodo come
cusomers.add
ci si aspetta che serva ad aggiungere
un nuovo cliente all'anagrafica. Altri effetti collaterali non devono esserci,
ad esempio il metodo già che c'è non deve eliminare dall'anagrafica
i clienti non più utilizzati.
- Le funzioni devono essere dotati di un alto livello di coesione.
- La funzione
sin()
che calcola il seno di un angolo è un ottimo
esempio di coesione, getFieldAndLockFileAndGetTime()
è una funzione
che fa troppe cose.
- Le funzioni devono avere il minimo livello di accoppiamento (una funzione
deve dipendere il meno possibile dagli altri).
- Un cattivo esempio di accoppiamento è una funzione A che dipende da B che
dipende da C: un errore introdotto da C si ripercuote in maniera esponenziale.
Altri accoppiamenti pericolosi sono quelli circolari, cioè A che dipende da
B che dipende da A.
- Evitare di usare il comando
width
.
- Spesso l'interprete JavaScript non riesce ad ottimizzare il codice e ne
risulta un esecuzione più lenta.
- La priorità nella scrittura del codice va data alla leggibilità
e alla manutenibilità, solo in seconda battuta alle prestazioni.
Non snaturare il linguaggio di programmazione e non cimentarsi in trucchi virtuosi
e arcani.
- Nascondere un codice illeggibile dietro alla scusa "così è più performante"
non è accettabile!
- In generale il codice JavaScript dovrebbe essere non intrusivo,
cioè la pagina web dovrebbe poter funzionare anche senza di esso.
- JavaScript dovrebbe servire solo a dare funzionalità aggiuntive, nel caso
il dispositivo o il browser non supportino JavaScript, la pagina dovrebbe comunque
essere in grado di visualizzare i contenuti.
- JavaScript non ha il concetto di namespace, ma è possibile
(e raccomandabile) simularlo nella seguente maniera:
-
var MyNamespace = {};
MyNamespace.myClassInstance = {
myMethod1 = function(myParam){};
};
MyNamespace.myClassInstance.myMethod2 = function(myParam){};
Si noti nell'esempio precedente che myClassInstance
non
è una dichiarazione, ma una vera e propria inizializzazione (è per questo che
il nome inizia per minuscola).
Una dichiarazione è la seguente:
var MyNamespace =
{
function MyClass()
{
this.myInstanceMethod = function(myParam){};
this.myProperty = 1;
}
};
var myClassInstance = new MyNamespace.MyClass();
Il precedente codice però crea i metodi come istanze (instance methods)
di un'ipotetica istanza di MyClass
, con l'effetto collaterale
che lo stesso metodo viene creato separatamente ad ogni istanza (ci sono più
copie dello stesso metodo).
Nel caso si prevedano più istanze della stessa classe, usare l'oggetto
prototype
:
var MyNamespace =
{
function MyClass()
{
this.myProperty = 1;
}
MyClass.prototype.myMethod() = function(myParam){};
};
var myClassInstance = new MyNamespace.MyClass();
Attenzione che la dichiarazione var MyNamespace = {}
essendo
a tutti gli effetti la creazione di un oggetto, resetta completamente eventuali
altre definizioni di sotto-oggetti; per evitare questo spiacevole effetto collaterale,
tutti i file che dichiarano classi nello stesso namespace devono usare il seguente
codice:
var MyNamespace;
if (!MyNamespace) MyNamespace = {};
L'equivalente di using
in C++ non esiste, ma si può fare
a meno di usare il namespace e riferirsi direttamente alla classe con il seguente
trucco:
var MyClass = MyNamespace.MyClass;
Il trucco precedente non andrebbe mai usato da chi crea il namespace e le
classi, ma solo dai loro utilizzatori.
La regola dovrebbe essere: "Un modulo non deve mai aggiungere più di
un singolo simbolo al namespace globale".
Il seguente esempio, usando una funzione anonima, dichiara ed esegue del codice
senza generare conflitti con eventuali variabili/metodi esistenti:
function({})();
- Scegliere i nomi delle classi e delle variabili dal dominio del
problema.
- Il nome dovrebbe normalmente rappresentare il dato astratto, non la specifica
implementazione (non è una pratica Object Oriented).
Ad esempio se si decide di creare una classe che racchiuda l'anagrafica
dei clienti, il fatto che l'implementazione si basi su una hash table non
deve rispecchiarsi nel nome, altrimenti se un giorno si decidesse di modificare
la classe utilizzando ad esempio un B-tree, ci si troverebbe costretti a cambiarne
il nome.
Dunque non va bene il nome CustomersHashTable
, ma piuttosto
CustomersList
o meglio ancora Customers
.
Proseguendo con il ragionamento, al di là del nome, comunque l'implementazione
deve essere sconosciuta all'esterno della classe e i metodi (ed i loro nomi)
devono nascondere la specifica soluzione scelta, non ci deve essere niente visto
dall'esterno che lasci intuire l'implementazione.
- Evitare le abbreviazioni incomprensibili tipo i nomi senza vocali (ad esempio
quelli tanto amati in Linux).
- Tra scegliere un nome corto e incomprensibile e uno lungo ma chiaro, scegliere
sempre il secondo.
- Le funzioni con effetti collaterali devono avere un nome che descriva ad
un giusto livello di astrazione tutti i compiti eseguiti.
Usare un verbo seguito da un complemento oggetto per le funzioni non membro.
Usare solo un verbo per le funzioni membro lasciando l'oggetto implicito.
Per le funzione che ritornano un risultato, usare una descrizione del risultato.
- Continuando con l'esempio della classe
Customers
, il metodo
per cercare un cliente nella lista non deve contenere il complemento oggetto
poiché è implicito, cioè va bene Costomers.Find
ma non Customers.FindCustomer
.
- Non esagerare con il numero di parametri.
- Tutti i parametri devono essere utilizzati dalla funzione e valutare se
per caso non sia necessaria una struttura per raggrupparli logicamente.
- Il paradigma divide-et-impera va usato con oculatezza.
- Aumenta sicuramente la leggibilità, ma è stato provato essere causa di più
errori: bisogna sicuramente evitare di scrivere metodi di 1000 righe, ma bisogna
anche evitare di scrivere metodi con una sola istruzione o, peggio ancora, la
cui unica istruzione è quella di invocare un altro metodo.
- Quando si dichiarano oggetti sarebbe consigliabile definire sempre un costruttore
e, se l'oggetto viene utilizzato in posti diversi, anche un prototipo.
- Quando si definisce una classe, sarebbe buona pratica definire anche i relativi
toString()
, valueOf()
, equals()
e
compareTo()
.
- Ove possibile usare sempre la comparazione per equivalenza.
- Sia per ragioni di performance che per evitare inutili conversioni.
if (x == 5 && y != 4) // NO
if (x === 5 && y !== 4)
- Nelle condizioni di tipo if...else se possibile evitare di usare
la condizione diverso o negazione per evitare di fare calcoli aggiuntivi.
-
if (a != b)
{
MyMethod1();
...
}
else
{
MyMethod2();
...
}
meglio la finezza di usare
if (a == b)
{
MyMethod2();
...
}
else
{
MyMethod1();
...
}
- La migliore interfaccia utente è nessuna interfaccia utente.
-
- Questo significa che gli UI designer devono dare per assunto un livello
culturale bassissimo nei loro utenti, come se questo fosse il primo programma
che usano nella loro vita, dovrebbero evitare di fare domande inutili e
tentare di prevenire gli errori piuttosto che invitare a correggerli a posteriori.
Ad esempio:
- Quando un utente chiude la finestra del programma, evitare la domanda "Vuoi
uscire dal programma?", a meno che l'uscita non provochi la perdita
di dati non salvati
- Se un controllo prevede l'inserimento di uno specifico insieme di
valori (solo numerici, valute, date, ecc...), prevenire all'origine
il fatto che l'utente possa inserirne di errati e non farglieli inserire
e poi dirgli "Valore non valido!".
- Se un pulsante non può essere premuto in un determinato stato, disabilitarlo
piuttosto che lasciare all'utente la possibilità di premerlo per poi
mostrargli il messaggio "Non puoi premere questo pulsante!"
- Quando ci si rivolge all'utente (ad esempio negli
alert
)
usare sempre lo stesso stile, cioè o impersonale o diretto (all'americana).
- Impersonale: "Salvare i dati prima di uscire?"
Diretto: "Vuoi salvare i dati prima di uscire?"
- Posizionare esternamente ai file HTML le istruzioni JavaScript.
- In teoria la soluzione inline è più efficiente, ma posizionare script e
stili in file esterni garantisce che vengano mantenuti in cache anche per le
visite successive. Dunque, meno spesso si prevede che un utente visiti la pagina,
più conviene metterli in linea. A meno che non vengano utilizzati anche in altre
pagine, in questo caso ovviamente posizionarli esternamente è sicuramente più
vantaggioso. L’unico caso in cui forse vale la pena tenerli in linea è la home
page, la pagina in cui è più importante che mai la rapidità del caricamento.
- Minimizzare gli script.
- La minimizzazione degli script aumenta la velocità del loro scaricamento.
Anche l’offuscamento ne riduce ulteriormente la dimensione, ma con il rischio
di introdurre bug e rendere inoltre difficile il debugging.
- Inserire gli script alla fine.
- Nel caso degli script viene bloccato il rendering progressivo di tutti i
contenuti che li seguono. Gli scaricamenti paralleli si disattivano mentre si
scaricano gli script, uno di essi potrebbe infatti invocare
document.wite
e alterare il contenuto della pagina.