Gli operatori binari sono quelli che necessitano di due operandi.
Operatori aritmetici
Gli operatori aritmetici in JavaScript sono del tutto uguali a quelli usati in matematica, anche nella precedenza degli operatori (prima * e / poi + e -).
L'operatore + è particolare perchè agisce sui numeri facendone la somma ma agisce anche sulle stringhe concatenandole. Quando invece si trova ad operare con diversi tipi di dato i risultati sono variabili, la precedenza viene data alla conversione in stringa.
var a = 5 + 3;
var b = "Buona" + "notte";
var c = "Buona" + 5;
var d = "Buona" + "9";
var e = "9" + "notte";
var f = "2" + 6; //non viene trasformato il "2" in numero ma il 6 in stringa
var g = 6 + "2"; //non viene trasformato il "2" in numero ma il 6 in stringa
var h = 7 + true; //il true viene trasformato in numero
var i = "Buona" + true;
var j = "4" + true;
console.log(a); //8
console.log(b); //"Buonanotte"
console.log(c); //"Buona5"
console.log(d); //"Buona9"
console.log(e); //"9notte"
console.log(f); //"26"
console.log(g); //"62"
console.log(h); //8
console.log(i); //"Buonatrue"
console.log(j); //"4true"
Nella tabella sottostante ci sono i risultati dell'operazione "valore nella riga" + "valore nella colonna":
| riga + colonna | undefined | 5 | "ciao" | "3" | true | null | {} |
| undefined | NaN | NaN | "undefinedciao" | NaN | NaN | "undefined[object Object]" | |
| 5 | NaN | 10 | "5ciao" | 6 | 5 | "5[object Object]" | |
| "ciao" | "ciaoundefined" | "ciao5" | "ciaociao" | "ciaotrue" | "ciaonull" | "ciao[object Object]" | |
| "3" | |||||||
| true | NaN | 6 | "trueciao" | 2 | 1 | "true[object Object]" | |
| {} | "[object Object]undefined" | "[object Object]5" | "[object Object]ciao" | "[object Object]true" | "[object Object]null" | "[object Object][object Object]" |
Quando si cerca di sommare valori che non possono essere convertiti in stringa o in numero il risultato è il valore speciale NaN (not a number).
Con es6 è stata introdotta una nuova modalità per concatenare stringhe e variabili: le template strings: esse hanno il vantaggio di rendere più facile la concatenazione delle stringhe e di preservare le interruzioni di linea
var numero = 7;
var str1 = "Una settimana è formata da " + numero + " giorni"; //vecchio metodo
var str2 = `Una settimana è formata da ${numero} giorni`; //nuovo metodo
L'operatore - invece non ha alcun effetto sulle stringhe ma cerca di convertire i valori che deve sottrarre in numero, altrimenti restituisce il valore NaN. Ci sono alcune particolarità relative alle sottrazioni dovute al fatto che i numeri vengono convertiti dal sistema binario a quello decimale.
var a = 5 - 3;
var b = 5 - "3"; //la stringa "3" viene trasformata in numero
var c = "9" - "3";
var d = "9" - true; //il booleano true viene trasformato in numero
var e = "9" - null; //il valore null viene convertito in 0
var f = 0.3 - 0.2; //non fa 0.1 !!!!
console.log(a): //2
console.log(b): //2
console.log(c): //6
console.log(d): //8
console.log(e): //9
console.log(f); //0.09999999999999998
Nella tabella sottostante ci sono i risultati dell'operazione "valore nella riga" - "valore nella colonna":
| riga - colonna | undefined | 5 | "ciao" | "3" | true | null | {} |
| undefined | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 5 | NaN | 0 | NaN | 2 | 4 | 5 | NaN |
| "ciao" | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| "3" | NaN | -2 | NaN | 0 | 2 | 3 | NaN |
| true | NaN | -4 | NaN | -2 | 0 | 1 | NaN |
| {} | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
Gli operatori * (moltiplicazione) e / (divisione) si comportano allo stesso modo dell'operatore -.
Quando in una divisione il divisore è zero, se anche il dividendo è uguale a zero si ottiene NaN, se il divisore è diverso da zero, si ottiene Infinity oppure -Infinity a seconda del segno del dividendo.
Infine abbiamo l'operatore % (modulo) che restituisce il resto della divisione tra i due operandi.
var a = 10 % 3;
console.log(a); //1
Operatori di assegnazione
Gli operatori di assegnazione servono per definire il valore di una variabile. Il simbolo = si usa per indicare che la variabile a sinistra di esso assume il valore di ciò che è indicato a destra, che può essere un numero, un testo , un'altra variabile o il risultato di una espressione o una funzione.
Quando stiamo assegnando ad una variabile un valore primitivo ne stiamo assegnando il valore, quando invece stiamo assegnando un non primitivo ne stiamo assegnando il riferimento in memoria.
var a = 5;
var b = prova(); //b vale il valore ritornato dalla funzione chiamata prova
var c = 1;
var d = 2;
var e = 3;
c = d = e; //l'operazione di assegnazione ritorna il valore assegnato
console.log(c); //3
console.log(d); //3
console.log(e); //3
var f;
console.log(f = 5); //5 l'operazione di assegnazione ritorna il valore assegnato
Con es6 è stato introdotto un altro modo per assegnare valori alle variabili, il destructuring assignment. E' utile usare questo nuovo metodo quando si deve scambiare il valore di due variabili
//vecchio metodo
var vocali = ["a", "e", "i", "o", "u"];
var a = vocali[0];
var e = vocali[1];
//nuovo metodo
var vocali = ["a", "e", "i", "o", "u"];
var [ a, e ] = vocali ; //alle variabili a ed e vengono assegnati i primi due valori del'array
var giorni = ["lunedì", "martedì", "mercoledì", "giovedì", "venerdì", "sabato", "domenica"];
var [ lun, , merc ] = vocali ; //alle variabili lun e merc vengono assegnati il primo e il terzo valore del'array (notare lo spazio vuoto tra lun e merc
var colori = ["giallo", "rosso", "verde", "blu", "viola"];
var [ g, , v ] = colori; //g vale "giallo" e v vale "verde"
//avremmo anche potuto usare il metodo di assegnazione normale
var g = colori[0];
var v = colori[2];
//con il vecchio metodo per scambiare il valore di g e v avremmo avuto bisogno di una terza variabile d'appoggio
//var tmp = g; g = v; v = tmp;
//con il nuovo metodo:
[ v, g ] = [ g, v ];
console.log(g); //verde
console.log(v); //giallo
var [primo, ...altri] = [1, 2, 3, 4]; //posso anche definire l'array sul momento
console.log(primo); //1
console.log(altri);// [2, 3, 4] //la variabile altri sarà un array di tutti i restanti elementi che non sono stati espressamente assegnati grazie allo spread operator (i tre puntini)
var persona = {
nome: "Mario",
congome: "Rossi",
anni: 20
}
//vecchio metodo
var nome = persona.nome;
var cognome = persona.cognome;
var anni = persona.anni;
//nuovo metodo
var { nome, cognome, anni } = persona; //con questo metodo le variabili devono avere lo stesso nome delle proprietà dell'oggetto
//posso anche dar dei valori di default
var { nome, cognome, anni, lavoro = "impiegato" } = persona;
console.log(lavoro); //impiegato
function creaPersona({ nome, cognome, anni}) {
// console.log(nome); //"Mario"
// console.log(cognome); //"Rossi"
// console.log(anni); //20
}
creaPersona(persona); //come parametro passiamo tutto l'oggetto persona
Possiamo anche combinare l'operatore di assegnazione con gli operatori aritmetici. Continuano a valere le particolarità dei singoli operatori aritmetici
var a = 30;
console.log(a); //30
a += 5;
console.log(a); //35
a -= 2;
console.log(a); //33
a *= 2;
console.log(a); //66
a /= 3;
console.log(a); //22
a %= 3;
console.log(a); //1 (22 diviso 3 fa 7 col resto di 1)
a += "ciao";
console.log(a); //"1ciao"
Operatori di confronto
Gli operatori di confronto servono per confrontare il valore ed eventualmente il tipo di due variabili. Il risultato di una espressione di confronto è sempre un valore booleano, cioè vero o falso.
L'operatore == serve per confrontare il valore di due variabili, se queste sono di tipo diverso prima uno dei due tipi viene convertito. Il valore NaN non è mai uguale a nulla, neanche a se stesso
| == | undefined | null | false | true | 0 | "0" | 1 | "1" | "" | {} | [] | NaN |
| undefined | true | true | false | false | false | false | false | false | false | false | false | false |
| null | true | true | false | false | false | false | false | false | false | false | false | false |
| false | false | false | true | false | true | true | false | false | true | false | true | false |
| true | false | false | false | true | false | false | true | true | false | false | false | false |
| 0 | false | false | true | false | true | true | false | false | true | false | true | false |
| "0" | false | false | true | false | true | true | false | false | false | false | false | false |
| 1 | false | false | false | true | false | false | true | true | false | false | false | false |
| "1" | false | false | false | true | false | false | true | true | false | false | false | false |
| "" | false | false | true | false | true | false | false | false | true | false | true | false |
| {} | false | false | false | false | false | false | false | false | false | false | false | false |
| [] | false | false | true | false | true | false | false | false | true | false | false | false |
| NaN | false | false | false | false | false | false | false | false | false | false | false | false |
L'operatore === serve per confrontare il tipo di due variabili e, se questo corrisponde, il loro valore. Perciò alcuni confronti che con l'operatore == davano true come ad esempio false == 0 con il === daranno false.
//Attenzione!!
typeof undeclaredVar === 'undefined'; // true
undeclaredVar === undefined; // ReferenceError
3 === 3.0; // true
Per fare confronti che coinvolgono il valore NaN il modo migliore è usare il metodo isNaN della classe Number:
var a;
var b;
var c = a + b;
console.log(Number.isNaN(c)); // true
Gli opposti di == e === sono != e !== e funzionano con la stessa logica ma danno esito positivo quando le variabili sono diverse.
Gli operatori > e < confrontano se un valore è maggiore o minore di un altro mentre >= e <= danno risultato positivo anche se i due valori sono uguali.
A parte il confronto tra numeri, spesso capita di confrontare delle date:
var inizio= new Date(1990, 0, 1);
var fine = new Date(1999, 11, 31);
console.log(inizio == new Date(1990, 0, 1)); //false, non sono tipi primitivi quindi hanno diversi indirizzi di memoria
console.log(inizio > fine); //false
console.log(inizio < fine); //true
console.log(fine - inizio); //315446400000 la differenza in millisecondi tra le due date (vengono convertite in numero)
console.log(inizio + fine); //"Mon Jan 01 1990 00:00:00 GMT+0100 (Ora standard dell’Europa centrale)Fri Dec 31 1999 00:00:00 GMT+0100 (Ora standard dell’Europa centrale)" le due date vengono convertite in stringa e concatenate
console.log(inizio + 31); //"Mon Jan 01 1990 00:00:00 GMT+0100 (Ora standard dell’Europa centrale)31" il numero 31 viene convertito in stringa
Quando invece dobbiamo confrontare due oggetti dobbiamo ricordarci che i loro valori sono passati per riferimento:
var p1 = {nome: "Mario"};
var p2 = {nome: "Mario"};
console.log(p1 === p2); // false
Operatori logici
Gli operatori logici si usano nelle istruzioni condizionali. Funzionano convertendo in valori booleani gli elementi dell'espressione e poi eseguendo un confronto.
L'operatore && (AND) dà un valore positivo se entrambi gli operandi sono valori truthy, cioè valori che convertiti in booleano danno true. Viene controllato prima l'operatore a sinistra e se e solo se questo è truthy viene controllato anche quello a destra.
Si può usare l'operatore && anche al di fuori di un confronto per condizionare l'esecuzione del codice che segue. Ad esempio:
var user;
user && user.name = "Mario"; //questa istruzione non verrà eseguita perchè user è undefined che convertito in booleano dà false perciò il resto del codice viene ignorato
console.log(0 && 4); //verrà stampato il valore 0 perchè è un valore che convertito in booleano dà false perciò il resto del codice viene ignorato
console.log('a' && '' && 0); // verrà stampata la stringa vuota perchè è il primo valore che convertito in booleano dà false
L'operatore || (OR) dà un valore positivo se uno dei due operandi è un valore truthy.
Si può usare l'operatore || anche al di fuori di un confronto sfruttando il fatto che entrambi gli operandi vengono valutati:
var a;
var b = null;
var c = false;
var d = a || b || c || 5;
console.log(d); //d vale 5 perchè grazie all'operatore || viene assegnato il primo valore truthy
I valori che convertiti in booleano danno false sono: undefined, null, 0 (zero), "", NaN, tutti gli altri sono truthy (ovviamente tranne il booleano false). Questo significa che se questi valori sono utilizzati come condizione di un if questo verrà valutato come false.
var a;
var b = null;
var c = false;
var d = 0;
var e = "";
//nessun console.log verrà eseguito perchè tutti gli if verranno valutati come false
if(a) console.log(a);
if(b) console.log(b);
if(c) console.log(c);
if(d) console.log(d);
if(e) console.log(e);
//tutti i seguenti console.log verranno eseguiti perchè un array vuoto oppure un oggetto vuoto oppure
//una funzione che ritorna un valore falsy sono comunque valutati come truthy
var f = [];
var g = {};
var e = function prova() { return false};
if(f) console.log(f);
if(g) console.log(g);
if(e) console.log(e);
Gli operatori typeof e instanceof
L'operatore typeof ritorna una stringa contenente il tipo di dato di cui fa parte il valore di una variabile. Si posiziona prima dell'operando (vedi pagina sui tipi di dato).
L'operatore instanceof ritorna true se nella prototype chain dell'oggetto alla sinistra dell'operatore si trova il costruttore indicato alla destra dell'operatore. Per semplicità diciamo che instanceof serve per controllare se un oggetto è istanza di una certa classe.
var a;
console.log(a instanceof Object); //false, è un tipo primitivo
var b = null;
console.log(b instanceof Object); //false, è un tipo primitivo
var c = true;
console.log(c instanceof Boolean); //false, è un tipo primitivo anche se si può fare new Boolean() e in quel caso instanceof Boolean è true
console.log(c instanceof Object); //false
var d = 0;
console.log(d instanceof Number); //false, è un tipo primitivo anche se si può fare new Number() e in quel caso instanceof Number è true
console.log(d instanceof Object); //false
var e = "0";
console.log(e instanceof String); //false, è un tipo primitivo, anche se si può fare new String() e in quel caso instanceof String è true
console.log(e instanceof Object); //false
var f = "";
console.log(f instanceof String); //false, è un tipo primitivo, anche se si può fare new String() e in quel caso instanceof String è true
console.log(f instanceof Object); //false
var g = [];
console.log(g instanceof Array); //true
console.log(g instanceof Object); //true
var h = {};
console.log(h instanceof Object); //true, un object literal ha sempre Object nel suo prototype
var i = NaN;
console.log(i instanceof Number); //false
console.log(i instanceof Object); //false
var j = new Date();
console.log(j instanceof Date); //true
console.log(j instanceof Object); //true
var k = function() {return false};
console.log(k instanceof Function); //true
console.log(k instanceof Object); //true
class Animal {};
var animal = new Animal();
console.log(animal instanceof Animal); //true
console.log(animal instanceof Object); //true
class Cat extends Animal {};
var cat = new Cat();
console.log(cat instanceof Cat); //true
console.log(cat instanceof Animal); //true
console.log(cat instanceof Object); //true
Per controllare il tipo di una variabile nè typeof nè instanceof sono una buona soluzione, la cosa migliore è sfruttare il metodo toString() della classe Object e il metodo call()
function myTypeOf(value) {
return Object.prototype.toString.call(value).slice(8, -1);
}
console.log(myTypeOf(a));//Undefined
console.log(myTypeOf(b));//Null
console.log(myTypeOf(c));//Boolean
console.log(myTypeOf(d));//Number
console.log(myTypeOf(e));//String
console.log(myTypeOf(f));//String
console.log(myTypeOf(g));//Array
console.log(myTypeOf(h));//Object
console.log(myTypeOf(i));//Number
console.log(myTypeOf(j));//Date
console.log(myTypeOf(k));//Function