Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial
  • Lezione 44 di 112
  • livello avanzato
Indice lezioni

Utilizzare il tipo Symbol

Scenari di utilizzo del tipo di dato Symbol: per identificare oggetti in modo univoco, per definire enumerazioni o per evitare collisioni di nomi.
Scenari di utilizzo del tipo di dato Symbol: per identificare oggetti in modo univoco, per definire enumerazioni o per evitare collisioni di nomi.
Link copiato negli appunti

Abbiamo visto le caratteristiche principali dei simboli ed abbiamo visto come è possibile identificarli utilizzando delle descrizioni. Ma che utilizzo concreto possiamo farne nelle nostre applicazioni JavaScript? Proviamo a suggerire i possibili utilizzi con qualche esempio.

Supponiamo di voler identificare un oggetto in maniera univoca assegnando un valore alla sua proprietà id:

var item = { descrizione: "Prodotto A", prezzo: 24.5 };
item.id = nuovoId();

L'approccio classico consiste nel prevedere una funzione, nuovoId() nel nostro esempio, che gestisca un contatore globale da incrementare ad ogni nuova richiesta per un nuovo identificatore. Con i simboli possiamo evitare il ricorso a questa funzione scrivendo semplicemente:

item.id = Symbol();

È il runtime di JavaScript a generare per noi un valore univoco.

Un altro possibile utilizzo è nella definizione di enumerazioni. Dal momento che JavaScript non supporta nativamente le enumerazioni, un approccio molto diffuso prevede di definire delle variabili come nel seguente esempio:

var giorniDellaSettimana = {
	lunedi:    1,
	martedi:   2,
	mercoledi: 3,
	giovedi:   4,
	venerdi:   5,
	sabato:    6,
	domenica:  7
};

Un problema con questo approccio è che eventuali funzioni che utilizzano i giorni della settimana definiti in questo modo stanno lavorando in realtà con valori interi e potrebbero essere utilizzati male, volontariamente o involontariamente. Immaginiamo la seguente funzione:

function isGiornoLavorativo(giorno) {

	return (giorno != giorniDellaSettimana.sabato && giorno != giorniDellaSettimana.domenica);
}
console.log(isGiornoLavorativo(giorniDellaSettimana.lunedi)); //true
console.log(isGiornoLavorativo(giorniDellaSettimana.sabato)); //false

Essa restituisce true o false in base al fatto che il valore dell'enumerazione rappresenta un giorno lavorativo o meno. Nulla vieta però di passare alla funzione direttamente un numero o il risultato di una somma:

console.log(isGiornoLavorativo(3));    // true
console.log(isGiornoLavorativo(2+5));  //false

Questo crea non poca confusione. Possiamo evitare questo definendo l'enumerazione tramite simboli:

var giorniDellaSettimana = {

	lunedi:    Symbol(),
	martedi:   Symbol(),
	mercoledi: Symbol(),
	giovedi:   Symbol(),
	venerdi:   Symbol(),
	sabato:    Symbol(),
	domenica:  Symbol()
};

Questo fa sì che a ciascun elemento dell'enumerazione venga assegnato un valore univoco ed è solo quel valore che rappresenta quello specifico giorno della settimana. Siamo "costretti

Un altro utilizzo è rappresentato dalla possibilità di definire proprietà per un oggetto senza il rischio di collisione di nomi. Supponiamo di avere necessità di aggiungere a runtime una proprietà ad un oggetto esistente, ad esempio per assegnare un valore che riutilizzeremo in un altro punto dell'applicazione:

var item = new Item();

item.dataControllo = new Date();

In questo esempio creiamo la proprietà dataControllo item dataControllo item item dataControllo

In queste situazioni è meglio ricorrere ai simboli:

var item = new Item();
var dataControllo = Symbol();

item[dataControllo] = new Date();

In questo modo abbiamo la garanzia di non incorrere in conflitti di nomi nell'aggiunta di una nuova proprietà all'oggetto. Tra l'altro, la nuova proprietà non sarà visibile nell'elenco delle proprietà dell'oggetto accessibile tramite Object.getOwnPropertyNames()

Possiamo comunque accedere alle proprieta definite tramite simboli sfruttando il metodo Object.getOwnPropertySymbols():

var props = Object.getOwnPropertySymbols(item);	

console.log(props.length);    // risultato: 1
console.log(item[props[0]]);  // risultato: valore della data assegnata

Un altro interessante utilizzo è quello del simbolo predefinito Symbol.iterator iterabile for...of

function Collection() {};
var collection = new Collection();

collection[0] = 123;
collection[1] = "test";
collection[2] = 222;

Per elencare sulla console il contenuto della collezione dovremmo ricorrere al metodo Object.getOwnPropertyNames()

var props = Object.getOwnPropertyNames(collection);
for (var i = 1; i < props.length; i++ ) { 

	console.log(collection[props[i]]);
}

Possiamo semplificare la vita a chi utilizzerà la nostra collezione rendendola iterabile. Per fare ciò, dobbiamo definire un metodo nel costruttore che abbia come nome Symbol.iterator

function Collection() {};
Collection.prototype[Symbol.iterator] = function() {

	var self = this;
	var i = 0;
	return {
		next: function() {
			if (self[i] !== undefined) {
				return { value: self[i++] };
			} else {

				return { done: true };
			}
		}
	}
}

Come possiamo vedere, il nostro metodo restituisce un oggetto con un metodo next() i next() value done true

Con l'implementazione di questo metodo, gli utilizzatori della nostra collezione potranno semplicemente utilizzare for...of per accedere al suo contenuto:

for (var x of collection) {

	console.log(x);
}

In conclusione, questo nuovo tipo di dato è a prima vista un po' incomprensibile, ma una volta che abbiamo individuato le sue caratteristiche di base possiamo sfruttarlo per implementare funzionalità molto interessanti.

Ti consigliamo anche