Estendere gli oggetti in Javascript
- In Javascript, è comune estendere un oggetto copiando le proprietà da un altro oggetto.
- Si può usare un ciclo
for...of
per iterare sulle chiavi di un oggetto e copiare le proprietà in un altro oggetto. - Il metodo
Object.assign()
è un modo semplice e veloce per copiare le proprietà da uno o più oggetti a un altro. - Si può usare lo spread operator per copiare le proprietà tra oggetti in modo più conciso.
- Si possono implementare funzioni personalizzate per unire, limitare o sottrarre proprietà tra oggetti.
Estendere gli Oggetti
Una delle esigenze che sorge spesso nei programmi JavaScript è quella di copiare le proprietà di un oggetto in un altro.
Il modo più semplice per farlo è mostrato in questo esempio:
// Oggetto sorgente
let sorgente = {
nome: "Mario",
età: 30,
professione: "Sviluppatore"
};
// Oggetto destinazione
let destinazione = {
hobby: "Tennis"
};
// Copia delle proprietà da sorgente a destinazione
for (let key of Object.keys(sorgente)) {
destinazione[key] = sorgente[key];
}
// Stampa dell'oggetto destinazione
console.log(destinazione);
Eseguendo questo codice, l'oggetto destinazione
conterrà le proprietà dell'oggetto sorgente
, risultando in:
{
hobby: "Tennis",
nome: "Mario",
età: 30,
professione: "Sviluppatore"
}
In pratica, abbiamo usato un ciclo for...of
per iterare sulle chiavi dell'oggetto sorgente
e assegnare i valori corrispondenti all'oggetto destinazione
.
Dal momento che questa è una delle operazioni più comuni in JavaScript, esistono anche metodi predefiniti per facilitare questa operazione. Originariamente, molti framework mettevano a disposizione metodi come extend
per effettuare questa operazione, ma ora, ES6 ha introdotto il metodo Object.assign()
che semplifica notevolmente il processo.
Il metodo Object.assign()
si aspetta due o più oggetti come argomenti. Il primo oggetto sarà l'oggetto di destinazione e verrà modificato. Gli altri oggetti saranno gli oggetti sorgente da cui copiare le proprietà e non saranno modificati. Il primo oggetto verrà restituito come risultato.
Object.assign
, in pratica, per ogni oggetto sorgente copia le own properties enumerabili di quell'oggetto nell'oggetto di destinazione. Dal momento che prende le proprietà degli oggetti nell'ordine in cui sono stati forniti alla funzione, se ci sono proprietà con lo stesso nome, quelle degli oggetti sorgente successivi sovrascriveranno quelle degli oggetti precedenti.
Un punto importante da tenere presente è che Object.assign()
copia le proprietà utilizzando i metodi getter e setter se presenti (li studieremo in dettaglio nelle prossime lezioni). Pertanto, se è presente un metodo getter per una proprietà di un oggetto sorgente oppure, viceversa, esiste un metodo setter per una proprietà nell'oggetto di destinazione, questi verranno invocati durante la copia. Tuttavia tali metodi non verranno copiati.
Un'applicazione pratica di Object.assign()
è quella di assegnare delle proprietà di default a un oggetto. Ad esempio, supponiamo di avere a disposizione un oggetto, chiamato ad esempio opzioniDefault
, che contiene le opzioni di configurazione per un componente con i loro valori di default.
Si può creare un oggetto nuovo opzioni
a partire da opzioniDefault
e poi sovrascrivere le proprietà che si desidera modificare:
let opzioniDefault = {
colore: "blu",
dimensione: "media",
visibile: true
};
let opzioni = Object.assign({}, opzioniDefault, {
colore: "rosso", // Sovrascrive il colore di default
visibile: false // Nasconde l'elemento
});
console.log(opzioni);
Eseguendo questo codice, l'oggetto opzioni
conterrà le proprietà di opzioniDefault
, ma con le modifiche specificate:
{
colore: "rosso",
dimensione: "media",
visibile: false
}
Una cosa fondamentale da notare è l'ordine con cui abbiamo passato gli oggetti a Object.assign()
. L'oggetto vuoto {}
è passato come primo argomento, il che significa che le proprietà di opzioniDefault
vengono copiate in un nuovo oggetto, e poi le proprietà specificate nell'oggetto successivo sovrascrivono quelle di default. Se avessimo invertito opzioniDefault
e l'oggetto con le modifiche, le proprietà di default sarebbero state sovrascritte da quelle di default, e non avremmo ottenuto il risultato desiderato.
Questa operazione è talmente comune che ES6 ha introdotto anche la sintassi di spread operator per semplificare ulteriormente la copia delle proprietà tra oggetti. Studieremo questa sintassi nelle prossime lezioni.
Si potrebbe anche pensare di scrivere una funzione merge
che, piuttosto che restituire un nuovo oggetto introducendo overhead, modifica direttamente l'oggetto di destinazione e copia le proprietà solo se queste non sono presenti nella destinazione. possiamo implementare questa funzione in questo modo:
// Funzione merge
// Copia solo le proprietà che non sono già presenti nell'oggetto di destinazione
function merge(destinazione, ...sorgenti) {
for (let sorgente of sorgenti) {
for (let key of Object.keys(sorgente)) {
if (!(key in destinazione)) {
destinazione[key] = sorgente[key];
}
}
}
return destinazione;
}
// Esempio di utilizzo
let opzioniDefault = {
colore: "blu",
dimensione: "media",
visibile: true
};
let opzioni = {
colore: "rosso" // Sovrascrive il colore di default
};
merge(opzioni, opzioniDefault);
console.log(opzioni);
Eseguendo questo codice, l'oggetto opzioni
conterrà le proprietà di opzioniDefault
, ma con il colore sovrascritto:
{
colore: "rosso",
dimensione: "media",
visibile: true
}
Allo stesso modo possiamo implementare una funzione limita
che rimuove le proprietà da un oggetto che non sono presenti in un altro oggetto, mantenendo solo le proprietà comuni:
// Funzione limita
// Rimuove le proprietà da destinazione che non sono presenti in sorgente
function limita(destinazione, sorgente) {
for (let key of Object.keys(destinazione)) {
if (!(key in sorgente)) {
delete destinazione[key];
}
}
return destinazione;
}
// Esempio di utilizzo
let opzioniConsentite = {
colore: null, // Non importa il valore, serve solo come chiave
dimensione: null,
visibile: null
};
let opzioni = {
colore: "rosso",
peso: "leggero" // Proprietà non presente in opzioniDefault
};
limita(opzioni, opzioniConsentite);
console.log(opzioni);
Eseguendo questo codice, l'oggetto opzioni
conterrà solo le proprietà comuni a opzioniConsentite
, rimuovendo la proprietà peso
:
{
colore: "rosso"
}
Infine, si potrebbe pensare di implementare una funzione sottrai
che rimuove le proprietà da un oggetto che sono presenti in un altro oggetto, mantenendo solo le proprietà uniche:
// Funzione sottrai
// Rimuove le proprietà da destinazione che sono presenti in sorgente
function sottrai(destinazione, sorgente) {
for (let key of Object.keys(sorgente)) {
if (key in destinazione) {
delete destinazione[key];
}
}
return destinazione;
}
// Esempio di utilizzo
let opzioniDaRimuovere = {
colore: null, // Non importa il valore, serve solo come chiave
dimensione: null
};
let opzioni = {
colore: "blu",
dimensione: "media",
visibile: true
};
sottrai(opzioni, valoriDaRimuovere);
console.log(opzioni);
Eseguendo questo codice, l'oggetto opzioni
conterrà solo le proprietà che non sono presenti in opzioniDaRimuovere
, rimuovendo le proprietà colore
e dimensione
:
{
visibile: true
}