Gli Operatori in JavaScript

Concetti Chiave
  • Gli operatori in JavaScript sono simboli che combinano valori per produrre nuovi valori.
  • Alcuni operatori richiedono più di un operando, mentre altri possono funzionare con un solo operando.
  • L'associatività degli operatori determina l'ordine in cui lo stesso operatore concatenato viene valutato.
  • La priorità degli operatori determina quale operazione viene eseguita per prima in un'espressione complessa.

Panoramica degli Operatori

Gli operatori sono utilizzati per le espressioni aritmetiche di JavaScript, le espressioni di confronto, le espressioni logiche, le espressioni di assegnazione e altro ancora.

La tabella che segue riassume gli operatori forniti dal linguaggio:

Operatore Operazione Associatività Arità Tipi
++ Pre- o post-incremento R 1 lval → num
-- Pre- o post-decremento R 1 lval → num
- Nega numero R 1 num → num
+ Converti in numero R 1 any → num
~ Inverti bit R 1 int → int
! Inverti valore booleano R 1 bool → bool
delete Rimuovi una proprietà R 1 lval → bool
typeof Determina tipo di operando R 1 any → str
void Restituisci valore undefined R 1 any → undef
** Elevamento a potenza R 2 num,num → num
*, /, % Moltiplica, dividi, resto L 2 num,num → num
+, - Somma, sottrai L 2 num,num → num
+ Concatena stringhe L 2 str,str → str
<< Sposta a sinistra L 2 int,int → int
>> Sposta a destra con estensione segno L 2 int,int → int
>>> Sposta a destra con estensione zero L 2 int,int → int
<, <=,>, >= Confronta in ordine numerico L 2 num,num → bool
<, <=,>, >= Confronta in ordine alfabetico L 2 str,str → bool
instanceof Testa classe oggetto L 2 obj,func → bool
in Testa se la proprietà esiste L 2 any,obj → bool
== Testa uguaglianza non-rigorosa L 2 any,any → bool
!= Testa disuguaglianza non-rigorosa L 2 any,any → bool
=== Testa uguaglianza rigorosa L 2 any,any → bool
!== Testa disuguaglianza rigorosa L 2 any,any → bool
& Calcola AND bit a bit L 2 int,int → int
^ Calcola XOR bit a bit L 2 int,int → int
| Calcola OR bit a bit L 2 int,int → int
&& Calcola AND logico L 2 any,any → any
|| Calcola OR logico L 2 any,any → any
?? Scegli primo operando definito L 2 any,any → any
?: Scegli secondo o terzo operando R 3 bool,any,any → any
= Assegna a una variabile o proprietà R 2 lval,any → any
**=, *=, /=, %=, +=, -=, &=, ^=, |=, <<=, >>=, >>>= Opera e assegna R 2 lval,any → any
, Scarta primo operando, restituisci secondo L 2 any,any → any
Tabella 1: Operatori JavaScript

Notiamo che la maggior parte degli operatori è rappresentata da caratteri di punteggiatura come + e =. Alcuni, tuttavia, sono rappresentati da parole chiave come delete e instanceof. Gli operatori con parole chiave sono operatori regolari, proprio come quelli espressi con la punteggiatura; hanno semplicemente una sintassi meno succinta.

La tabella di sopra è organizzata per precedenza degli operatori. Gli operatori elencati per primi hanno una precedenza più alta di quelli elencati per ultimi. Gli operatori separati da una linea orizzontale hanno livelli di precedenza diversi. La colonna etichettata con Associatività indica l'associatività dell'operatore, che può essere L (da sinistra a destra) o R (da destra a sinistra), e la colonna Arità specifica il numero di operandi. La colonna etichettata Tipi elenca i tipi attesi degli operandi e (dopo il simbolo →) il tipo di risultato per l'operatore. Le sottosezioni che seguono spiegano i concetti di precedenza, associatività e tipo di operando.

Arità: Numero di Operandi

Gli operatori possono essere categorizzati in base al numero di operandi che si aspettano (la loro arità).

La maggior parte degli operatori JavaScript, come l'operatore di moltiplicazione *, sono operatori binari che combinano due espressioni in una singola espressione più complessa. Cioè, si aspettano due operandi. JavaScript supporta anche un numero di operatori unari, che convertono una singola espressione in una singola espressione più complessa. L'operatore nell'espressione −x è un operatore unario che esegue l'operazione di negazione sull'operando x.

Infine, JavaScript supporta un operatore ternario, l'operatore condizionale ?:, che combina tre espressioni in una singola espressione.

Tipo di Operando e Risultato

Alcuni operatori funzionano su valori di qualsiasi tipo, ma la maggior parte si aspetta che i loro operandi siano di un tipo specifico, e la maggior parte degli operatori restituisce un valore di un tipo specifico. La colonna Tipi nella tabella di sopra specifica i tipi di operando (prima della freccia) e il tipo di risultato (dopo la freccia) per gli operatori.

Gli operatori JavaScript solitamente convertono il tipo dei loro operandi secondo necessità. L'operatore di moltiplicazione * si aspetta operandi numerici, ma l'espressione "3" * "5" è valida perché JavaScript può convertire gli operandi in numeri. Il valore di questa espressione è il numero 15, non la stringa "15", ovviamente. Ricordiamo anche che ogni valore JavaScript è o truthy o falsy, quindi gli operatori che si aspettano operandi booleani funzioneranno con un operando di qualsiasi tipo.

Alcuni operatori si comportano diversamente a seconda del tipo degli operandi utilizzati con essi. Più notevolmente, l'operatore + addiziona operandi numerici ma concatena operandi stringa. Similmente, gli operatori di confronto come < eseguono confronti in ordine numerico o alfabetico a seconda del tipo degli operandi. Le descrizioni dei singoli operatori spiegano le loro dipendenze di tipo e specificano quali conversioni di tipo eseguono.

Notiamo che gli operatori di assegnazione e alcuni degli altri operatori elencati nella tabella si aspettano un operando di tipo lval. lvalue è un termine storico che significa "un'espressione che può legalmente apparire sul lato sinistro di un'espressione di assegnazione". In JavaScript, variabili, proprietà di oggetti ed elementi di array sono lvalue.

Effetti Collaterali degli Operatori

Valutare un'espressione semplice come 2 * 3 non influisce mai sullo stato del programma, e qualsiasi computazione futura che il programma esegue non sarà influenzata da quella valutazione. Alcune espressioni, tuttavia, hanno effetti collaterali, e la loro valutazione può influenzare il risultato di valutazioni future. Gli operatori di assegnazione sono l'esempio più ovvio: se assegnate un valore a una variabile o proprietà, questo cambia il valore di qualsiasi espressione che usa quella variabile o proprietà. Gli operatori ++ e -- di incremento e decremento sono simili, poiché eseguono un'assegnazione implicita. L'operatore delete ha anche effetti collaterali: eliminare una proprietà è come (ma non è la stessa cosa di) assegnare undefined alla proprietà.

Nessun altro operatore JavaScript ha effetti collaterali, ma l'invocazione di funzioni e le espressioni di creazione di oggetti avranno effetti collaterali se qualsiasi degli operatori usati nel corpo della funzione o del costruttore ha effetti collaterali.

Precedenza degli Operatori

Gli operatori elencati nella tabella di sopra sono disposti in ordine dalla precedenza più alta a quella più bassa, con linee orizzontali che separano gruppi di operatori allo stesso livello di precedenza. La precedenza degli operatori controlla l'ordine in cui vengono eseguite le operazioni. Gli operatori con precedenza più alta (più vicini alla parte superiore della tabella) vengono eseguiti prima di quelli con precedenza più bassa (più vicini alla parte inferiore).

Consideriamo la seguente espressione:

w = x + y*z;

L'operatore di moltiplicazione * ha una precedenza più alta dell'operatore di addizione +, quindi la moltiplicazione viene eseguita prima dell'addizione. Inoltre, l'operatore di assegnazione = ha la precedenza più bassa, quindi l'assegnazione viene eseguita dopo che tutte le operazioni sul lato destro sono completate.

La precedenza degli operatori può essere superata con l'uso esplicito di parentesi. Per forzare l'addizione nell'esempio precedente ad essere eseguita per prima, scriviamo:

w = (x + y)*z;

Si noti che l'accesso alle proprietà e le espressioni di invocazione hanno precedenza più alta di qualsiasi operatore elencato nella tabella. Consideriamo questa espressione:

// mio è un oggetto con una proprietà chiamata funzioni il cui valore è un
// array di funzioni. Invochiamo la funzione numero x, passandole l'argomento
// y, e poi chiediamo il tipo del valore restituito.
typeof mio.funzioni[x](y)

Sebbene typeof sia uno degli operatori con priorità più alta, l'operazione typeof viene eseguita sul risultato dell'accesso alla proprietà, dell'indice dell'array e dell'invocazione della funzione, tutte operazioni che hanno priorità più alta degli operatori.

In pratica, se non siete affatto sicuri della precedenza dei vostri operatori, la cosa più semplice da fare è usare le parentesi per rendere esplicito l'ordine di valutazione. Le regole che è importante conoscere sono queste: moltiplicazione e divisione vengono eseguite prima di addizione e sottrazione, e l'assegnazione ha precedenza molto bassa e viene quasi sempre eseguita per ultima.

Quando vengono aggiunti nuovi operatori a JavaScript, non sempre si adattano naturalmente a questo schema di precedenza. L'operatore ?? è mostrato nella tabella come avente precedenza più bassa di || e &&, ma, in realtà, la sua precedenza relativa a quegli operatori non è definita, e ES2020 richiede di usare esplicitamente le parentesi se si mescola ?? con || o &&. Allo stesso modo, il nuovo operatore di elevamento a potenza ** non ha una precedenza ben definita relativa all'operatore di negazione unaria, e dovete usare le parentesi quando combinate la negazione con l'elevamento a potenza.

Associatività degli Operatori

Nella tabella di sopra, la colonna etichettata con Associatività specifica l'associatività dell'operatore. Un valore di L specifica l'associatività da sinistra a destra, e un valore di R specifica l'associatività da destra a sinistra. L'associatività di un operatore specifica l'ordine in cui vengono eseguite le operazioni con la stessa precedenza. L'associatività da sinistra a destra significa che le operazioni vengono eseguite da sinistra a destra. Ad esempio, l'operatore di sottrazione ha associatività da sinistra a destra, quindi:

w = x - y - z;

è uguale a:

w = ((x - y) - z);

D'altra parte, le seguenti espressioni:

y = a ** b ** c;
x = ~-y;
w = x = y = z;
q = a?b:c?d:e?f:g;

sono equivalenti a:

y = (a ** (b ** c));
x = ~(-y);
w = (x = (y = z));
q = a?b:(c?d:(e?f:g));

perché gli operatori di esponenziazione, unario, assegnazione e condizionale ternario hanno associatività da destra a sinistra.

Ordine di Valutazione

La precedenza degli operatori e l'associatività specificano l'ordine in cui le operazioni vengono eseguite in un'espressione complessa, ma non specificano l'ordine in cui le sottoespressioni vengono valutate. JavaScript valuta sempre le espressioni in ordine rigorosamente da sinistra a destra. Nell'espressione w = x + y * z, per esempio, la sottoespressione w viene valutata prima, seguita da x, y e z. Poi i valori di y e z vengono moltiplicati, aggiunti al valore di x, e assegnati alla variabile o proprietà specificata dall'espressione w. Aggiungere parentesi alle espressioni può cambiare l'ordine relativo della moltiplicazione, addizione e assegnamento, ma non l'ordine di valutazione da sinistra a destra.

L'ordine di valutazione fa differenza solo se qualcuna delle espressioni che vengono valutate ha effetti collaterali che influenzano il valore di un'altra espressione. Se l'espressione x incrementa una variabile che viene usata dall'espressione z, allora il fatto che x venga valutata prima di z è importante.