48 Symbols (un nuovo tipo di dato)

Ecma Script 6 introduce un nuovo tipo di dato primitivo: symbols.

I simboli sono un nuovo tipo di valore, come i numeri e le stringhe, a differenza di questi, però, non posso assegnare un valore letterale, come faccio con le stringhe con le virgole o con i tipo number con i numeri.

let un_simbolo = "NON È UN SIMBOLO"; //stringa
let un_simbolo = 112.15; //costante numerica

Il modo per assegnare il valore ad un simbolo consiste nel richiederne la generazione tramite una funzione factory (costruttrice) symbol().

let un_simbolo = Symbol();

La caratteristica che rende particolari i simboli è la loro univocità, potrei creare una miriade di simboli nello script, ma il valore generato non sarà MAI ripetuto.

let un_simbolo = Symbol();
let un_altro_simbolo = Symbol();

Il valore associato al primo simbolo sarà obbligatoriamente diverso dal secondo, conseguentemente la seguente uguaglianza sarà sempre FALSE

writeln(un_simbolo === un_altro_simbolo); //false

Il valore del simbolo NON SI PUO’ stampare

writeln(un_simbolo); //ERRORE

Possiamo solo sapere che il simbolo ha un valore, non traducibile in una stringa o numero, ma tale valore sarà unico, ci è concesso, al massimo, agganciare un etichetta ad un simbolo

let un_simbolo = Symbol("il primo simbolo");
let un_altro_simbolo = Symbol("il secondo simbolo");

Per scopi di debug, a questo punto possiamo chiedere la conversione in stringa dell’ etichetta del simbolo

writeln( String(un_simbolo) );
writeln( String(un_altro_simbolo) );

Teniamo ben presente che queste sono solo etichette per distinguere i simboli, non i loro valori, se 2 simboli avessero la stessa etichetta

let un_simbolo = Symbol("il primo simbolo");
let un_altro_simbolo = Symbol("il primo simbolo");

sarebbero 2 simboli con valori diversi che condividerebbero la stesa etichetta. L’algoritmo di generazione dei simboli utilizza un meccanismo simile al GUID (Globally Unique Identifier), e la probabilità che venga generato lo stesso simbolo è praticamente zero.

Vediamo un caso pratico sull’uso dei simboli, dato il seguente oggetto

let oggetto =
{
  colore: "giallo",
  peso: 21
}

vogliamo aggiungere una proprietà con la certezza che nessun altro utilizzando il nostro codice, o noi utilizzandone uno di altri possa andare in conflitto con nomi già esistenti. Questo rende molto più semplice ampliare o estendere il codice.

Le proprietà che aggiungeremo con i simboli saranno ghost, proprietà nascoste, quindi avremo la certezza che non metteremo a repentaglio il funzionamento di qualsiasi codice a cui aggiungiamo proprietà.

let check = Symbol();

Creiamo e generiamo prima il simbolo e poi aggiungiamo tra parentesi quadre la proprietà associata alla variabile simbolo, a cui possiamo assegnare un qualsiasi valore

oggetto[check] = "OK";

Praticamente abbiamo aggiunto a oggetto una proprietà che ha un valore (il valore di check) unico a noi sconosciuto, ma recuperabile tramite la variabile check come chiave di accesso.

Ecco la stampa della proprietà, notare le parentesi quadre [ ]

writeln(oggetto[check]);

abbiamo dichiarato che i simboli come proprietà non influiscono sul meccanismo di funzionamento degli oggetti, a dimostrazione di ciò, facciamo un ulteriore esempio, aggiungendo un metodo all’oggetto

let oggetto =
   {
       colore: "giallo",
       peso: 21,

       scheda: function () {
           let s = "";
           for (x in this)
               if (x!=="scheda") s += x + " ";
           return s;
       }

   }

abbiamo aggiunto il metodo scheda() che ha lo scopo di fornirci una stringa descrittiva delle proprietà dell’oggetto, si utilizza questa forma del ciclo for

for (x in this)

per ogni proprietà ( x ) dell’oggetto ( in this ) a parte la funzione scheda()

if (x!=="scheda")

aggiungi la property alla stringa s con uno spazio

s += x + " ";

eseguendo il metodo, scopriamo che la property symbol , check,  non viene considerata

writeln( oggetto.scheda() );

Se avessi aggiunto la proprietà in modo tradizionale

oggetto.check2 = "eee";

questa sarebbe stata aggiunta nelle proprietà del metodo e potrebbe interferire con script o librerie che vogliamo personalizzare.

Grazie ai simboli le proprietà aggiunte sarebbero ignorate, come se non esistessero.

Esiste una funzione che restituisce un array di tutti i simboli utilizzati in un oggetto, tramite getOwnPropertySymbols

Object.getOwnPropertySymbols(oggetto);

Lo stesso meccanismo di aggiunta proprietà si può anche fare con i metodi

let report = Symbol();
let oggetto2 = 
{
prodotto: "xyz",
[report]() {return this.prodotto;} 

}

la variabile report ha un valore, sempre a noi sconosciuto, che non ci interessa, ma ci interessa associare report al nome della funzione che andremo a creare in oggetto2

writeln(oggetto2[report]());

Possiamo anche aggiungere il metodo a posteriori (come fatto con la proprietà check di oggetto)

oggetto2[report] = function() { return "Controllo effettuato!";}

nel nostro esempio la seconda funzione sovrascriverà la prima, quindi non stamperà più xyz, ma Controllo effettuato!

Testiamo l’enumerazione dei simboli dell’oggetto

writeln( String(Object.getOwnPropertySymbols(oggetto)[0]));