Cl

Closure in JavaScript

1 esercizio

Informazioni su Closure

Le closure sono un pattern di programmazione in JavaScript che permette di usare le variabili di uno scope lessicale esterno all'interno di un blocco di codice annidato. JavaScript supporta le closure in modo trasparente, e spesso vengono usate senza sapere cosa siano.

// Top-level declarations are global-scope
const dozen = 12;

// Functions create a new function-scope and block-scope.
// Referencing the outer variable here is a closure.
function nDozen(n) {
  return dozen * n;
}

Il nome closure deriva storicamente dal calcolo lambda ed è stato reso popolare da Scheme (fonte) per indicare le associazioni di variabili aperte e chiuse di una funzione.

Perché usare le closure in JavaScript

  1. Privacy dei dati / Incapsulamento dei dati

    • A differenza di altri linguaggi, nel 2020 non c'era modo di specificare variabili private. Quindi le closure possono essere usate per emulare efficacemente le variabili private (c'era una proposta per introdurre una notazione per le variabili private, che potrebbe essere diventata standard ormai).
    // Consider this function:
    
    const saveNumber = (number) => {
      // The saved value is unaccessible by the outer lexical scope.
      const value = number;
    
      // We can provide access to the primitive value with a function, but the original will never change
      return () => value;
    };
    
    // Attempting to set the variable outside of its lexical scope results in an error
    value = 42;
    
  2. Applicazione parziale

    • Le funzioni possono restituire funzioni e, quando una funzione restituita usa l'argomento della funzione che l'ha creata, questo è un esempio di uso di una closure per realizzare un'applicazione parziale. A volte questo si chiama currying di una funzione.
    // The arguments are applied one-by-one, using closures to arrive at the final result
    function partialBuildUri(scheme) {
      return function (domain) {
        return function (path) {
          return `${scheme}://${domain}/${path}`;
        };
      };
    }
    
    // A function could apply them all at once.
    function buildUri(scheme, domain, path) {
      return partialBuildUri(scheme)(domain)(path);
    }
    
    // Or apply a few to create a flexible system of functions for reuse.
    function buildHttpsExercismUri(path) {
      return partialBuildUri('https')('exercism.org');
    }
    

Scope lessicale delle variabili

In qualsiasi linguaggio di programmazione, le closure sono strettamente legate allo scope delle variabili. In JavaScript, ci sono 3 tipi di scope lessicale per le variabili:

  1. Scope globale: sono le variabili dichiarate al di fuori delle funzioni e dei blocchi.
  2. Scope di funzione: sono le variabili dichiarate all'interno di una funzione usando la parola chiave var.
  3. Scope di blocco: sono le variabili dichiarate all'interno di un blocco ({...}) usando le parole chiave let e const.

In pratica, una closure si verifica spesso quando una funzione (o un blocco) usa una variabile di uno scope esterno. In JavaScript questo è supportato in modo trasparente, quindi le closure vengono spesso usate senza un riferimento esplicito.

Scope di funzione

La parola chiave var definisce una variabile con scope di funzione. Questo significa che le variabili definite da var sono disponibili ovunque nella funzione in cui sono definite e in qualsiasi scope lessicale annidato (sia di funzione che di blocco).

{
  var five = 5;
}

// This will succeed because 'five' exists in the same scope
var tenTimes = five * 10;

Scope di blocco

Le parole chiave let e const definiscono variabili con scope di blocco. let definisce una variabile mutabile, il cui valore può essere assegnato più volte e che può essere definita ma non inizializzata. const definisce una variabile che deve essere inizializzata quando viene definita, e il cui valore in seguito non può cambiare. Se alla variabile const viene assegnato un valore primitivo, non può essere mutata. Se alla variabile const viene assegnato un valore di riferimento, come un array o un object, il riferimento non può cambiare, ma il contenuto dell'array può essere mutato.

Considera questo blocco:

// 'two' is defined at the top-level, therefore available to the global-scope
const two = 2;

{
  // This succeeds, as 'two' is available from the outer lexical scope
  const oneLess = two - 1;
}

// This throws an error because oneLess is not defined outside of the previous inner lexical scope
console.log(oneLess);

Anche se possiamo usare le parentesi graffe ({...}) arbitrariamente per creare blocchi, il più delle volte le troviamo intorno a istruzioni if, funzioni e classi.

const PI = 3.14;

// This function uses a closure for the value of PI (π)
function areaOfCircle(radius) {
  return PI * radius * radius;
}
Modifica tramite GitHub Il collegamento si apre in una nuova finestra o scheda

Impara Closure