Master the JavaScript Interview: Cos’è una chiusura?

“Master the JavaScript Interview” è una serie di post progettati per preparare i candidati alle domande più comuni che probabilmente incontreranno quando faranno domanda per una posizione JavaScript di medio o alto livello. Queste sono domande che uso frequentemente nelle interviste reali.

Lancio la serie con la domanda da 40 mila dollari. Se rispondi male a questa domanda, c’è una buona possibilità che tu non venga assunto. Se vieni assunto, c’è una buona probabilità che tu venga assunto come sviluppatore junior, indipendentemente da quanto tempo hai lavorato come sviluppatore di software. In media, gli sviluppatori junior vengono pagati $40k/anno in meno rispetto agli ingegneri del software più esperti.

Le chiusure sono importanti perché controllano ciò che è e non è nello scope di una particolare funzione, insieme a quali variabili sono condivise tra funzioni sorelle nello stesso scope. Capire come le variabili e le funzioni si relazionano tra loro è fondamentale per capire cosa sta succedendo nel vostro codice, sia nello stile di programmazione funzionale che in quello orientato agli oggetti.

Il motivo per cui mancare questa domanda è così svantaggioso in un colloquio è che le incomprensioni su come funzionano le chiusure sono una bandiera rossa piuttosto chiara che può rivelare una mancanza di esperienza profonda, non solo in JavaScript, ma in qualsiasi linguaggio che si basa molto sulle chiusure (Haskell, C#, Python, ecc …).

Codificare in JavaScript senza una comprensione delle chiusure è come cercare di parlare inglese senza una comprensione delle regole grammaticali – potreste essere in grado di far passare le vostre idee, ma probabilmente in modo un po’ goffo.

Sarete anche vulnerabili ai malintesi quando cercherete di capire ciò che qualcun altro ha scritto.

Non solo dovreste sapere cos’è una chiusura, dovreste sapere perché è importante, ed essere in grado di rispondere facilmente a diversi possibili casi d’uso per le chiusure.

Le chiusure sono frequentemente usate in JavaScript per la privacy dei dati degli oggetti, nei gestori di eventi e nelle funzioni di callback, e in applicazioni parziali, currying, e altri modelli di programmazione funzionale.

Se non sai rispondere a questa domanda, potrebbe costarti il lavoro, o ~$40k/year.

Preparati per un rapido follow-up: “Puoi citare due usi comuni delle chiusure?”

Che cos’è una chiusura?

Una chiusura è la combinazione di una funzione raggruppata (racchiusa) con riferimenti al suo stato circostante (l’ambiente lessicale). In altre parole, una chiusura vi dà accesso all’ambito di una funzione esterna da una funzione interna. In JavaScript, le chiusure sono create ogni volta che viene creata una funzione, al momento della creazione della funzione.

Per usare una chiusura, definite una funzione dentro un’altra funzione ed esponetela. Per esporre una funzione, restituitela o passatela ad un’altra funzione.

La funzione interna avrà accesso alle variabili nello scope della funzione esterna, anche dopo che la funzione esterna è ritornata.

Usare le chiusure (Esempi)

Tra le altre cose, le chiusure sono comunemente usate per dare agli oggetti privacy dei dati. La privacy dei dati è una proprietà essenziale che ci aiuta a programmare per un’interfaccia, non per un’implementazione. Questo è un concetto importante che ci aiuta a costruire software più robusto, perché i dettagli dell’implementazione hanno più probabilità di cambiare in modi che rompono rispetto ai contratti di interfaccia.

“Programma su un’interfaccia, non su un’implementazione.”
Design Patterns: Elements of Reusable Object Oriented Software

In JavaScript, le chiusure sono il meccanismo principale usato per abilitare la privacy dei dati. Quando si usano le chiusure per la privacy dei dati, le variabili racchiuse sono solo nell’ambito della funzione contenente (esterna). Non si può arrivare ai dati da un ambito esterno se non attraverso i metodi privilegiati dell’oggetto. In JavaScript, qualsiasi metodo esposto definito all’interno dello scope della chiusura è privilegiato. Per esempio:

Giocate con questo in JSBin. (Non vedete nessun output? Copiate e incollate questo HTML nel pannello HTML.)

Nell’esempio sopra, il metodo `.get()` è definito all’interno dello scope di `getSecret()`, il che gli dà accesso a qualsiasi variabile di `getSecret()`, e lo rende un metodo privilegiato. In questo caso, il parametro, `secret`.

Gli oggetti non sono l’unico modo per produrre privacy dei dati. Le chiusure possono anche essere usate per creare funzioni statiche i cui valori di ritorno possono essere influenzati dal loro stato interno, ad esempio:

const secret = msg => () => msg;

Disponibile su JSBin. (Non vedete nessun output? Copiate e incollate questo HTML nel riquadro HTML.)

Nella programmazione funzionale, le chiusure sono spesso usate per applicazioni parziali & currying. Questo richiede alcune definizioni:

Applicazione: Il processo di applicazione di una funzione ai suoi argomenti per produrre un valore di ritorno.

Applicazione parziale: Il processo di applicazione di una funzione ad alcuni dei suoi argomenti. La funzione parzialmente applicata viene restituita per un uso successivo. L’applicazione parziale fissa (applica parzialmente la funzione a) uno o più argomenti all’interno della funzione restituita, e la funzione restituita prende i parametri rimanenti come argomenti per completare l’applicazione della funzione.

L’applicazione parziale sfrutta lo scopo della chiusura per fissare i parametri. Potete scrivere una funzione generica che applicherà parzialmente gli argomenti alla funzione di destinazione. Avrà la seguente firma:

partialApply(targetFunction: Function, ...fixedArgs: Any) =>
functionWithFewerParams(...remainingArgs: Any)

Se avete bisogno di aiuto per leggere la firma di cui sopra, consultate Rtype: Reading Function Signatures.

Prenderà una funzione che prende qualsiasi numero di argomenti, seguita da argomenti che vogliamo applicare parzialmente alla funzione, e restituisce una funzione che prenderà gli argomenti rimanenti.

Un esempio vi aiuterà. Diciamo che avete una funzione che aggiunge due numeri:

const add = (a, b) => a + b;

Ora volete una funzione che aggiunga 10 a qualsiasi numero. La chiameremo `add10()`. Il risultato di `add10(5)` dovrebbe essere `15`. La nostra funzione `partialApply()` può farlo accadere:

const add10 = partialApply(add, 10);
add10(5);

In questo esempio, l’argomento `10` diventa un parametro fisso ricordato all’interno dello scope di chiusura `add10()`.

Guardiamo una possibile implementazione di `partialApply()`:

Disponibile su JSBin. (Non vedete nessun output? Copiate e incollate questo HTML nel riquadro HTML.)

Come potete vedere, restituisce semplicemente una funzione che mantiene l’accesso agli argomenti `fixedArgs` che sono stati passati nella funzione `partialApply()`.

Tocca a voi

Questo post ha un video di accompagnamento e dei compiti di pratica per i membri di EricElliottJS.com. Se sei già membro, accedi e fai pratica ora.

Se non sei membro, iscriviti oggi stesso.

Esplora la serie

  • Che cos’è una chiusura?
  • Che differenza c’è tra eredità di classe e prototipale?
  • Che cos’è una funzione pura?
  • Che cos’è la composizione di funzioni?
  • Che cos’è la programmazione funzionale?
  • Che cos’è una promessa?
  • Soft Skills

Aggiornamenti:
Luglio 2019 – Introduzione chiarita per spiegare perché rispondere male a questa domanda potrebbe costarti un lavoro o molti soldi di stipendio.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *