Operatore new in JavaScript
Scopri come le funzioni costruttore e l'operatore new creano object in JavaScript: passi, regola del valore restituito, new.target e convenzioni di denominazione.
Introduzione ai costruttori e all'operatore new
In JavaScript, i costruttori sono funzioni progettate per inizializzare object appena creati. Svolgono un ruolo fondamentale nella programmazione orientata agli oggetti, consentendo agli sviluppatori di definire proprietà e comportamenti che gli object di una determinata classe devono avere. L'operatore new viene utilizzato per creare un'istanza di un object basandosi su una funzione costruttore, configurando un nuovo ambiente object basato sul prototipo specificato ed eseguendo il costruttore per inizializzare il nuovo object.
Come funzionano i costruttori
Una funzione costruttore in JavaScript assomiglia a qualsiasi altra funzione, ma per convenzione viene denominata con una lettera maiuscola per distinguerla dalle funzioni normali. Quando l'operatore new invoca una funzione costruttore, avvengono quattro cose dietro le quinte, approssimativamente equivalenti a questo pseudo-codice:
function User(name) {
// this = {}; (1) an empty object is created and assigned to this
// this.__proto__ = User.prototype; (2) prototype is linked
this.name = name; // (3) the constructor body runs, adding properties to this
// return this; (4) this is returned automatically
}- Viene creato un nuovo object vuoto e assegnato a
this. - Il prototipo viene collegato: il
[[Prototype]]interno del nuovo object viene impostato sulla proprietàprototypedel costruttore, in modo da ereditare le proprietà e i metodi definiti lì. - Il corpo del costruttore viene eseguito: la funzione viene eseguita con gli argomenti passati, e
thisfa riferimento al nuovo object appena creato, quindi assegnazioni comethis.name = nameaggiungono proprietà ad esso. - L'object viene restituito:
thisviene restituito automaticamente a meno che il costruttore non restituisca esplicitamente un object diverso (vedi La regola del valore restituito di seguito).
Il JavaScript moderno utilizza la sintassi class per definire costruttori e metodi in modo più intuitivo. Questo fornisce un approccio più diretto, basato su classi, simile ad altri linguaggi di programmazione.
Esempio: funzione costruttore di base
Spiegazione: In questo esempio, User è una funzione costruttore che inizializza name, age e un metodo greet sugli object appena creati. L'istruzione new User('John', 30) crea una nuova istanza di User con il nome "John" e l'età 30. All'interno di greet, this fa riferimento all'object su cui è stato chiamato il metodo.
La regola del valore restituito
I costruttori normalmente non usano return — il nuovo object (this) viene restituito automaticamente. Tuttavia return ha un comportamento speciale e facile da trascurare all'interno di un costruttore:
- Se
returnè seguito da un object, quell'object viene restituito al posto dithis. - Se
returnè seguito da un primitivo (string, number, boolean,undefined, ecc.), viene ignorato ethisviene restituito come di consueto.
Spiegazione: WithObject restituisce un object semplice, quindi quell'object sostituisce completamente l'istanza. WithPrimitive restituisce una string, che viene ignorata, quindi viene restituito il this originale (con name: 'Alice'). Raramente ci si affida a questo comportamento, ma spiega risultati inaspettati quando un costruttore restituisce accidentalmente un valore.
Rilevare new con new.target
All'interno di qualsiasi funzione, new.target è undefined quando la funzione viene chiamata normalmente ed è uguale alla funzione stessa quando viene chiamata con new. Questo consente a un costruttore di rilevare come è stato invocato — utile per imporre (o consentire silenziosamente) la parola chiave new.
Spiegazione: Poiché Modal controlla new.target, la chiamata Modal('Without new') senza new viene trasparentemente inoltrata a new Modal(...), quindi b è comunque una vera istanza di Modal. (Nota: molti team preferiscono non farlo e lasciare che un new mancante fallisca in modo evidente.)
Quando usare un costruttore?
Usa una funzione costruttore (o una class) quando hai bisogno di creare molti object della stessa forma — più utenti, auto, widget DOM, ecc. Il costruttore centralizza la logica di configurazione in modo che ogni istanza venga costruita nello stesso modo e condivida i metodi attraverso il prototipo.
Se hai bisogno di un singolo object, un object letterale semplice { ... } è più conveniente. Per un object una-tantum che beneficia comunque di un corpo costruttore, puoi anche usare un costruttore anonimo:
Spiegazione: La function anonima viene eseguita una volta come costruttore e non viene salvata da nessuna parte, quindi non può essere riutilizzata — è solo un modo per incapsulare una configurazione complessa una-tantum.
Funzioni costruttore vs. classi
Il codice moderno preferisce solitamente la sintassi class, che è essenzialmente zucchero sintattico sopra le funzioni costruttore e i prototipi. I due frammenti di codice seguenti sono equivalenti:
Le classi aggiungono vantaggi reali rispetto alle funzioni costruttore: i metodi non sono enumerabili per impostazione predefinita, il corpo viene eseguito in strict mode, chiamare una classe senza new genera un errore, e extends/super rendono l'ereditarietà molto più pulita. Le funzioni costruttore rimangono importanti da comprendere perché le classi sono costruite sullo stesso meccanismo di prototipo, e le incontrerai ancora nel codice più vecchio.
Uso dei costruttori per object complessi
I costruttori possono essere utilizzati per configurare relazioni più complesse tra object, inclusi metodi che interagiscono con altre proprietà degli object.
Esempio: costruttore con metodi
Spiegazione: Il costruttore Car configura ogni object auto con proprietà specifiche e un metodo che mostra le informazioni sull'auto.
Esempio: metodi del prototipo
Spiegazione: Aggiungendo introduce al prototipo di Employee, tutte le istanze condividono lo stesso metodo, il che è più efficiente dal punto di vista della memoria rispetto a definirlo direttamente nel costruttore.
Si raccomanda di utilizzare le classi ES6 per definire object e costruttori per un codice più pulito e leggibile.
Best practice con i costruttori
Quando si lavora con i costruttori in JavaScript, seguire alcune best practice può migliorare notevolmente la leggibilità, l'efficienza e la scalabilità del codice. Di seguito le pratiche vengono illustrate con esempi dettagliati e spiegazioni:
1. Convenzione di denominazione
Best Practice: Inizia sempre i nomi dei costruttori con una lettera maiuscola per differenziarli dalle funzioni normali. Questa è una convenzione comune in JavaScript e in molti altri linguaggi di programmazione che aiuta gli sviluppatori a identificare rapidamente le funzioni costruttore.
Esempio:
Spiegazione: La funzione costruttore Laptop inizia con una lettera maiuscola, indicando che è destinata ad essere utilizzata con l'operatore new per creare nuovi object.
2. Separazione della logica
Best Practice: Per i metodi che non richiedono l'accesso ai dati individuali dell'istanza, definiscili sul prototipo del costruttore anziché all'interno del costruttore stesso. Questo approccio risparmia memoria perché tutte le istanze condividono lo stesso metodo invece che ogni istanza crei una nuova funzione in memoria.
Esempio:
Spiegazione: Il metodo describe viene aggiunto al prototipo di Book, il che significa che tutte le istanze di Book condividono lo stesso metodo describe. Questo è più efficiente rispetto a definire describe all'interno del costruttore, il che comporterebbe una nuova funzione per ogni istanza di book.
3. Valori restituiti
Best Practice: Evita di restituire valori dai costruttori. I costruttori JavaScript restituiscono automaticamente la nuova istanza dell'object a meno che non venga restituito esplicitamente un object diverso. Restituire valori non-object (come una string o un number) non avrà alcun effetto e la nuova istanza verrà comunque restituita.
Esempio:
Spiegazione: Nonostante il tentativo di restituire una string dal costruttore Player, JavaScript ignora questo valore restituito perché non è un object. La nuova istanza di Player viene restituita come previsto.
Conclusione
Comprendere e utilizzare i costruttori e l'operatore new in JavaScript è essenziale per una programmazione orientata agli oggetti efficace. Seguendo le convenzioni e le best practice descritte qui, gli sviluppatori possono creare codice organizzato, efficiente e scalabile. I costruttori forniscono un potente meccanismo per inizializzare nuovi object e definire il loro comportamento in modo strutturato e intuitivo.
Per approfondire, esplora come i costruttori condividono il comportamento attraverso l'ereditarietà prototipale, come la sintassi class si basa su queste idee, e come una funzione è anche un object con le proprie proprietà.