Menu

module.exports ed exports in Node.js, con esempi

module.exports è il valore che un file CommonJS consegna a chi lo carica con require(). exports è una scorciatoia che punta allo stesso oggetto, finché non lo riassegni.

Questa pagina include editor eseguibili: modifica, esegui e vedi subito l'output.

module.exports è il valore che un file Node.js dà a qualsiasi altro file che lo carica con require(). All'inizio è un oggetto vuoto: aggiungigli proprietà per esportare più cose, oppure assegnagli un nuovo valore (una funzione, una classe) per esportarne una sola. exports è una variabile scorciatoia che punta allo stesso oggetto, quindi exports.add = ... funziona, ma exports = ... rompe il collegamento e non esporta niente.

Ha stampato 5 20 3.14159, poi [ 'add', 'multiply', 'PI' ]. Tutte e tre le righe di math.js hanno aggiunto proprietà allo stesso oggetto, sia passando da module.exports sia da exports, e require('./math') ha restituito quell'oggetto.

Esportare una funzione, una classe o un oggetto

Quando un modulo ha una sola cosa principale, assegnala direttamente a module.exports. Il chiamante riceve così la funzione stessa, non un oggetto che la contiene:

Ha stampato Hello, Ada e Grace (user). Se vedi TypeError: greet is not a function, il chiamante ha usato la forma sbagliata: scrivi const { greet } = require(...) per una proprietà di un oggetto e const greet = require(...) per un valore assegnato.

module.exports ed exports: la trappola della riassegnazione

Prima che il tuo file venga eseguito, Node.js imposta exports = module.exports. Entrambi i nomi puntano a un unico oggetto, e require restituisce module.exports. Assegnare un nuovo valore a exports cambia solo la variabile locale:

broken.js ha esportato {}, perché ora exports punta a un nuovo oggetto che nessuno restituisce. working.js ha esportato { version: 2 }.

ScriviEsportato?
module.exports.x = 1Sì
exports.x = 1Sì, stesso oggetto
module.exports = { x: 1 }Sì, sostituisce l'oggetto
exports = { x: 1 }No, il valore va perso

La regola più semplice: scrivi sempre module.exports. Funziona in tutti i casi, e il codice dice quello che fa.

require restituisce lo stesso oggetto in cache

Node.js esegue il codice di un modulo una volta sola, la prima volta che viene richiesto, e mette in cache module.exports. Ogni require successivo dello stesso file restituisce quello stesso oggetto, quindi lo stato dentro un modulo è condiviso da tutti i file che lo caricano:

counter.js is running è comparso una volta sola, b.next() ha restituito 3 e a === b è true, perché la chiave della cache è il path del file risolto e ./counter e ./counter.js puntano allo stesso file. Per questo una connessione al database o un oggetto di configurazione creato in un modulo è condiviso in tutta l'app.

module.exports e i moduli ES

I moduli ES usano invece export e import. Le forme corrispondono così:

// CommonJS                              // ES modules
module.exports = greet;                  export default greet;
module.exports.add = add;                export { add };
const greet = require('./greet');        import greet from './greet.js';
const { add } = require('./math');       import { add } from './math.js';

Un modulo ES può fare import di un file CommonJS: il suo module.exports arriva come import di default. Da Node.js 22.12, anche require() può caricare un modulo ES che non ha await al livello principale. Per capire quale scegliere, vedi CommonJS e moduli ES.

Errori comuni

Mescolare exports.x con module.exports = ... nello stesso file. L'assegnazione sostituisce l'oggetto, quindi ogni exports.x scritto prima sparisce. Scegli uno stile per file.

Esportare un valore che cambia dopo. module.exports = { count } copia il numero al momento del caricamento; quando il modulo aggiorna count, i chiamanti vedono ancora il vecchio valore. Esporta una funzione che lo restituisce, come fa counter.js qui sopra.

Dimenticare ./. require('math') cerca un pacchetto in node_modules, non math.js accanto al tuo file. I file locali iniziano sempre con ./ o ../.

Domande frequenti

Posso usare module.exports ed export nello stesso file?

No. Un file è CommonJS (require, module.exports) oppure un modulo ES (import, export). Node.js lo decide in base all'estensione (.cjs o .mjs) o al campo "type" in package.json.

Perché require restituisce un oggetto vuoto?

Di solito il modulo ha scritto exports = ... invece di module.exports = ..., oppure due moduli si richiedono a vicenda. In un require circolare uno dei due riceve il module.exports dell'altro prima che quel file abbia finito di essere eseguito.

Serve l'estensione .js in require?

No. require('./math') prova ./math, poi ./math.js, ./math.json e ./math.node, poi una cartella ./math con un index.js. In un import di un modulo ES l'estensione è obbligatoria.

Come ricarico un modulo che require ha già messo in cache?

Cancella la sua voce con delete require.cache[require.resolve('./math')] e richiedilo di nuovo. Serve soprattutto nei test e negli strumenti di sviluppo; il codice dell'applicazione non dovrebbe contarci.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

Inizia