I cookies sono quelle brutte cose che tanto hanno portato dolore a noi sviluppatori; servono principalmente per capire chi sei, cosa fai, e come vendere i tuoi dati, in ogni caso niente di grave. Chiudendo questo banner o continuando la navigazione sul sito acconsenti all'utilizzo dei cookies e la tua privacy sarà per sempre compromessa, ma tanto lo è già, quindi tanto vale. Leggi tuttoChiudi

Ventimila righe sotto i mari

Come amare uno strumento di lavoro al punto di odiarlo

NdA: questo articolo è sì tecnico, ma è scritto in modo che anche il più profano possa capire di quale argomento arcano stia parlando. Più che un'analisi tecnica è uno showcase del perché noi programmatori siamo stanchi anche se stiamo tutto il giorno seduti a pigiare tasti (apparentemente) a caso.

NdA parte 2: per questo articolo vedrete lo stile leggermente cambiato, soprattutto per quanto riguarda il desktop. Al posto delle solite immagini cretine ho voluto dare più spazio al codice e alle varie spiegazioni. Oltre a questo è stato aggiunto un syntax highlighter (visualizzatore di codice) per riuscire a leggere meglio le righe confuse che troverete sotto. Buona lettura.

I vecchi falegnami, dopo anni e anni spesi ad esercitare la loro arte, riconoscono gli strumenti al tatto, e ognuno di questi ha le proprie caratteristiche peculiari: un certo peso, una certa sensazione al tocco, a volte pure un certo profumo. Ma soprattutto ognuno di questi strumenti ha uno scopo ben preciso, il fine ultimo per il quale è stato costruito. E quel lavoro lo fa egregiamente proprio perché è stato costruito appositamente per esso.

Supponiamo ora di essere nel 2018, in cui i moderni falegnami hanno uno strumento unico per fare tutto. Devi piantare un chiodo? Usa un martello. Devi fare un buco? Usa un martello! Devi saldare due pezzi di metallo? Usa un cazzo di martello!

In quest'epoca costituita da bit e non più da legno i nuovi artigiani siamo noi programmatori, soprattutto i full-stack, che nella maggior parte dei casi hanno un solo tool per fare tutto, in questo caso Javascript. La community internazionale è divisa: c'è chi dice che Javascript è uno strumento maneggevole e versatile per tutti gli utilizzi che possono venire in mente, al contrario una grossa fetta sostiene che Javascript è un linguaggio per imparare a programmare e non per lavorarci.

Dal mio personale punto di vista Javascript è un linguaggio semplice, ma che se usato nel modo scorretto risulta estremamente disordinato, soprattutto per via della mancanza di variabili tipizzate (in fondo all'articolo c'è un comodo glossario per nabb... profani del codice). Grazie all'introduzione di TypeScript (un dialetto di Javascript creato dalla Microsoft) si è un po' colmata la lacuna, ma c'è da dire che è un'invenzione moderna rispetto al linguaggio stesso, e al momento ha una barriera d'entrata leggermente più ripida del Javascript vanilla (il classico per intenderci).

Dato di fatto è che negli ultimi anni sono usciti centinaia di metodi per utilizzare Javascript fuori dall'ambito Web:

Nonostante il range ricco e vario delle applicazioni in cui Javascript viene utilizzato, il linguaggio presenta alcuni casi limite che spesso è necessario conoscere per evitare di perdere ore su ore a fare debugging (vedi glossario) su letterali minchiate. Vi illustrerò qui le più comuni, le più divertenti e le più strane.

NdA parte 3: con la sintassi // -> intendo specificare il valore dell'operazione eseguita, ossia come eseguire un console.log della riga appena sopra.

La matematica è un opinione

Una componente fondamentale di ogni linguaggio di programmazione è la risoluzione di operazioni aritmetiche. Quindi partiamo con qualcosa di semplice:

0.1 + 0.2
// -> 0.30000000000000004

Cosa? Non abbiamo appena detto che una componente fondamentale è l'aritmetica? E perché allora viene sbagliata clamorosamente un'operazione così sempice?

Il problema non è del linguaggio in sé, ma si tratta di un problema matematico: in base 10 ogni numero frazionario è rappresentabile con la virgola solo se la base può essere espressa a fattori primi. Il bel casino è che in base 2, la base che normalmente si utilizza in informatica, l'unico fattore primo disponibile è ovviamente 2, quindi per le frazioni tipo 1/10 (0.1) o 1/5 (0.2) non c'è nessun problema, mentre per 1/6 (1.666666667 circa) il numero viene approssimato alla rappresentazione più vicina, nel nostro caso 0.30000000000000004. Questi matematici, hanno rovinato la matematica.

Approfondimenti:

Proprietà commutativa

Restiamo nell'aritmetica e testiamo la proprietà commutativa, utilizzando però dei tipi di dato diversi dai soliti numeri:

{} + []
// -> 0

Mmh, strano: perché mai dovrebbe risultare zero? Ignoriamo il fatto e continuiamo a verificare la proprietà commutativa:

[] + {}
// -> '[object Object]'

Ok, questo è decisamente inaspettato. Cosa vuol dire '[object Object]'? E perché non risulta zero come prima? Come detto sopra, Javascript non è un linguaggio fortemente tipizzato (vedi glossario) e gran parte del tempo lo passa a capire di che tipo è una variabile. Nel primo caso il nostro caro Javascript interpreta {}, che rappresenta un oggetto (vedi glossario) vuoto, senza nessuna chiave e valore, come un numero, e successivamente l'array (vedi glossario) senza elementi come una stringa vuota, che per qualche motivo equivale a sommare zero. Ma la cosa folle è che non funziona se il primo operatore è già numerico:

0 + ''
// -> '0'

Il secondo caso è più facile: l'array vuoto (vedi glossario) viene interpretato come stringa (come è tutt'ora nel linguaggio C) e, sommandolo ad un oggetto vuoto, anche quest'ultimo verrà considerato come stringa, infatti [object Object] è la rappresentazione letterale dell'oggetto vuoto. Niente di trascendentale.

The NaN is a lie

In Javascript NaN è l'abbreviazione di not a number, ossia il risultato di un'operazione aritmetica errata. Il modo più facile di ottenerlo è questo:

1 - 'a'
// -> NaN

Stiamo cercando di sottrarre una stringa da un numero intero, quindi il risultato non è calcolabile e ci torna giustamente NaN. Ma il valore NaN a volte ha comportamenti molto strani:

typeof NaN
// -> 'number'

Come? Il tipo di not a number è number? Questa cosa ha veramente poco senso. Ma le cose strane non si fermano qui:

NaN === NaN
// -> false

Sì esatto. NaN non è uguale a sé stesso. Com'è possibile? In entrambi i casi si tratta di standard definiti da Ecmascript (vedi glossario):

Lunghezza di cosa?

Altro caso strano, riconducibile a quello precedente: qui si abusa del casting (vedi glossario) tra un tipo e l'altro di variabile per restituire poi risultati inaspettati, in questo caso costruzione di parole partendo da ben pochi simboli:

(!+[]+[]+![]).length
// -> 9

Perché mai dovrebbe uscire 9? Assistiamo qui ad un tripudio di casi particolari, partendo dai due fondamentali, ossia !+[] restituisce true e ![] restituisce false. Partendo da queste due istruzioni base si usa un intelligente gioco di conversione a stringhe e concatenamenti per costruire una parola. Ecco cosa succede nel dettaglio, istruzione per istruzione:

![] === false
!+[] === true
[]+[] === ''
![]+[] === 'false'
!+[]+[] === 'true'
!+[]+[]+![] === 'truefalse'
'truefalse'.length === 9

Un altro esempio, usando la stessa tecnica, è il seguente, con l'aggiunta dell'istruzione [][[]] che torna undefined:

(![]+[])[+[]]+(![]+[])[+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]
// -> 'fail'

Nello specifico si convertono i tre valori true, false e undefined in stringa per poi selezionare il carattere corretto usando le parentesi quadre, come se fosse un array (vedi glossario).

Tutta questione di basi

A volte non ci si può sempre fidare della tipizzazione implicita (vedi glossario) che fornisce Javascript. Un esempio comune è il volersi assicurare che una variabile sia un numero partendo da una stringa; in questo caso si utilizza la funzione parseInt, che però a volte dà risultati eccentrici:

parseInt(null, 24)
// -> 23

Per quale motivo al mondo dovrebbe restituire 23?

In questo caso è la funzione vera e propria che si comporta in maniera inusuale: il primo parametro (null) viene interpretato come stringa, il secondo parametro invece è la base nella quale il numero nel primo parametro è rappresentato, nel nostro caso base 24.

Il problema è che null viene interpretato come stringa, e la prima lettera in base 24 vale proprio 23, e il resto di lettere, per esempio la U che in base 24 non esiste, viene ignorata silenziosamente senza dare alcun errore. Questo comportamento è error-prone, ossia può causare errori difficili da scovare per noi povere bestie di programmatori. Maledetti coloro che scrissero le specifiche.

Approfondimenti:

Batman!

Questa più che essere una particolarità del linguaggio è una stronzata che spesso e volentieri uso per tirarmela con i colleghi:

new Array(16).join('a' - 1) + ' Batman!';
// -> 'NaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaNNaN Batman!'

A parte il risultato divertente possiamo eseguire step-by-step la riga per capire perché porta a quel risultato:

// Crea un array (vedi glossario) di 16 elementi
new Array(16);
// Concatena gli elementi come stringa usando il valore specificato come separatore
// All'interno delle parentesi, come visto sopra, sottraendo un numero da una stringa torna sempre NaN
.join('a' - 1)
// Concatena la parte finale della stringa
+ ' Batman!';

Tre semplici istruzioni per un risultato sempre divertente. Minima spesa, massima resa. E poi fate pure un figurone.

exit(0)

Ci sono stati forti influenze durante la scrittura di questo pezzo, soprattutto per l'ultima parte, principalmente dal celeberrimo talk "Wat" di Gary Bernhardt, che propone in chiave divertente i concetti ribaditi sopra. Anche la raccolta di WTFJS e la repository omonima hanno ispirato qualche punto saliente.

Nel caso vi venisse in mente qualche altra puttanata di Javascript fatemelo sapere, e magari un giorno potrò ringraziarvi per non avermi lasciato a morire sopra il debugger.

Glossario

Torna alla Home