Chiusure da fronte a retro

Le chiusure sono spesso viste come un'arte arcana nella terra di JavaScript. Una volta masterizzati, ti permettono di scrivere un JavaScript davvero sorprendente. Questo articolo ti farà conoscere la magia delle chiusure di JavaScript.


Che cosa è una chiusura?

Una delle verità chiave di JavaScript è quella qualunque cosa è un oggetto. Questo, ovviamente, include funzioni.

Una chiusura non è altro che un oggetto funzione con un ambito correlato in cui vengono risolte le variabili della funzione.

Le chiusure prendono il loro nome a causa del modo in cui loro vicino sui loro contenuti. Considera il seguente bit di JavaScript:

 topping = "anchovi"; function pizzaParty (numSlices) var topping = "pepperoni", innerFunction = function () var topping = "ham"; console.log ("... Ma metti" + topping + "on" + numSlices + "slices"); ; console.log ("Questa pizza riguarda esclusivamente" + topping); innerFunction ();  pizzaParty (3);

Se apri la tua console preferita ed esegui quel ragazzaccio, verrai accolto con un delizioso messaggio sull'effetto di "Questa pizza è tutta dedicata ai peperoni ... Ma metti il ​​prosciutto su 3 fette". Questo esempio illustra alcuni concetti chiave di JavaScript cruciali per ottenere una sospensione delle chiusure.

Una chiusura è un oggetto funzione

Quanti oggetti funzione ci sono nel codice sopra? Bene ... abbiamo il nostro Pizzaparty funzione e nidificata in quella funzione è innerFunction. La matematica non è sempre stata la mia causa forte, ma 1 + 1 = 2 nel mio libro. Ogni oggetto funzione ha il proprio set di variabili, che sono risolte in ogni funzione scopo.

Una chiusura ha il suo ambito

Le chiusure non possono essere pienamente comprese senza una solida base di applicazione. Il meccanismo di scope di JavaScript è ciò che consente a ciascuna funzione di avere il proprio guarnizione variabile, e senza di esso potremmo avere troppi peperoni, troppo poco prosciutto, o * ansimi * ... alcune acciughe alla nostra pizza party. Usiamo una rapida illustrazione per illustrare meglio questa idea.

Le funzioni vengono eseguite utilizzando l'ambito che era in vigore al momento della definizione della funzione. Non ha nulla a che fare con lo scope in effetto quando viene chiamata la funzione.

Accessibilità variabile funziona all'esterno

Le frecce verdi mostrano che l'accessibilità funziona dall'esterno dentro. Le variabili definite nell'ambito al di fuori di una funzione sono accessibili dall'interno.

Se dovessimo omettere il guarnizione variabile dall'interno del Pizzaparty funzione, quindi avremmo ricevuto un messaggio del tipo "Questa pizza è tutta per gli anchovi", ma da allora Pizzaparty ha un guarnizione variabile all'interno del proprio ambito; quei ventosi salati non si avvicinano mai alla nostra festa della pizza.

Allo stesso modo, il numSlices il parametro è accessibile dall'interno innerFunction perché è definito nell'ambito sopra, in questo caso l'ambito di Pizzaparty.

L'accessibilità variabile non funziona all'interno

Le frecce rosse mostrano che le variabili nell'ambito di una funzione non sono mai accessibili al di fuori di quella funzione. Questo è il caso solo quando una variabile soddisfa una delle seguenti condizioni:

  1. Il var la parola chiave è in uso.
  2. La variabile è un parametro della funzione o di una funzione esterna.
  3. La variabile è una funzione nidificata.

Omettendo il var parola chiave quando si imposta una variabile causerà JavaScript per impostare la variabile denominata più vicino nelle funzioni esterne fino all'ambito globale. Quindi, usando il nostro esempio, il prosciutto guarnizione nel innerFunction non è possibile accedere da Pizzaparty, e i peperoni guarnizione nel Pizzaparty non è possibile accedere allo spazio globale in cui si trova l'anchovi.

JavaScript utilizza Scoping Lexical

Lo scope lessicale indica che le funzioni vengono eseguite utilizzando lo scope variabile in effetti quando la funzione era definito. Non ha nulla a che fare con lo scope in effetto quando viene chiamata la funzione. Questo fatto è cruciale per sbloccare il potere delle chiusure.

Ora che comprendiamo cos'è una chiusura e quale ambito significano le chiusure, analizziamo alcuni casi d'uso classici.


Utilizzo di chiusure per la privacy

Le chiusure sono il modo per nascondere il tuo codice agli occhi del pubblico. Con chiusure, puoi facilmente avere membri privati ​​che sono protetti dal mondo esterno:

 (function (exports) function myPrivateMultiplyFunction (num, num2) return num * num2; // equivalente a window.multiply = function (num1, num2) ... exports.multiply = function (num1, num2) console.log (myPrivateMultiplyFunction (num1, num2));) (finestra);

Con chiusure, puoi facilmente avere membri privati ​​che sono protetti dal mondo esterno.

Scopriamolo. Il nostro oggetto funzione di livello superiore è una funzione anonima:

 (funzione (esportazioni) ) (finestra);

Invochiamo subito questa funzione anonima. Lo passiamo al contesto globale (finestra in questo caso) in modo che possiamo "esportare" una funzione pubblica, ma nascondere tutto il resto. Perché la funzione myPrivateMultiplyFunction è una funzione annidata, esiste solo nell'ambito della nostra chiusura; quindi possiamo usarlo ovunque all'interno di questo ambito e solo in questo ambito.

JavaScript terrà un riferimento alla nostra funzione privata da utilizzare all'interno della funzione di moltiplicazione, ma myPrivateMultiplyFunction non è possibile accedere al di fuori della chiusura. Proviamo questo:

 multiply (2,6) // => 12 myPrivateMultiplyFunction (2,6) // => ReferenceError: myPrivateMultiplyFunction non è definito

La chiusura ci ha permesso di definire una funzione per uso privato, pur continuando a consentirci di controllare ciò che il resto del mondo vede. Cos'altro possono fare le chiusure?


Usando le chiusure per la meta-programmazione

Le chiusure sono abbastanza utili quando si tratta di generare codice. Stanco di ricordare tutti quei fastidiosi codici chiave per gli eventi della tastiera? Una tecnica comune è usare una mappa chiave:

 var KeyMap = "Enter": 13, "Shift": 16, "Tab": 9, "LeftArrow": 37;

Quindi, nel nostro evento tastiera, vogliamo verificare se è stato premuto un determinato tasto:

 var txtInput = document.getElementById ('myTextInput'); txtInput.onkeypress = function (e) var code = e.keyCode || e.which // tariffa normale per ottenere il tasto premuto if (code === KeyMap.Enter) console.log (txtInput.value); 

Catturare un momento nel tempo

L'esempio sopra riportato non è il peggiore, ma possiamo usare meta-programmazione e chiusure per creare una soluzione ancora migliore. Usando il nostro esistente kEYMAP oggetto, possiamo generare alcune funzioni utili:

 per (chiave var in KeyMap) // accesso oggetto con accessor di matrice per impostare il nome della funzione "dyanamic" KeyMap ["is" + key] = (funzione (confronta) return function (ev) var code = ev.keyCode | | ev.which; codice di ritorno === compare;) (KeyMap [chiave]); 

Le chiusure sono così potenti perché possono catturare la variabile locale e i collegamenti dei parametri della funzione in cui sono definiti.

Questo ciclo genera un è funzione per ogni chiave in kEYMAP, e il nostro txtInput.onkeypress la funzione diventa un po 'più leggibile:

 var txtInput = document.getElementById ('myTextInput'); txtInput.onkeypress = function (e) if (KeyMap.isEnter (e)) console.log (txtInput.value); 

La magia inizia qui:

 KeyMap ["è" + chiave] = (funzione (confronta) ) (KeyMap [tasto]); // invoca immediatamente e passa il valore corrente in KeyMap [tasto]

Mentre aggiriamo le chiavi kEYMAP, passiamo il valore referenziato da quella chiave alla funzione esterna anonima e lo invochiamo immediatamente. Questo lega quel valore al confrontare parametro di questa funzione.

La chiusura a cui siamo interessati è quella che stiamo ritornando dall'interno della funzione anonima:

 return function (ev) var code = ev.keyCode || ev.which; codice di ritorno === confronta; 

Ricorda, le funzioni sono eseguite con lo scope che era al loro posto quando sono state definite. Il confrontare parametro è legato al kEYMAP valore che era presente durante l'iterazione del ciclo, e quindi la nostra chiusura annidata è in grado di catturarla. Scattiamo un'istantanea nel tempo dello scope che era in un effetto in quel momento.

Le funzioni che abbiamo creato ci permettono di saltare la configurazione del codice variabile ogni volta che vogliamo controllare il codice chiave, e ora abbiamo funzioni comode e leggibili da usare.


Usando le chiusure per estendere la lingua

A questo punto, dovrebbe essere relativamente facile vedere che le chiusure sono vitali per scrivere JavaScript di prima qualità. Applichiamo ciò che sappiamo sulle chiusure per aumentare uno dei tipi nativi di JavaScript (gasp!). Concentrandoci sugli oggetti funzione, aumentiamo il nativo Funzione genere:

 Function.prototype.cached = function () var self = this, // "this" fa riferimento alla funzione originale cache = ; // la nostra cache locale restituisce la funzione di restituzione dello storage cache (args) if (args in cache) restituisce la cache [args]; return cache [args] = self (args); ; ;

Questa piccola gemma consente a qualsiasi funzione di creare una versione cache di se stessa. Puoi vedere che la funzione restituisce una funzione stessa, quindi questo miglioramento può essere applicato e utilizzato in questo modo:

 Math.sin = Math.sin.cached (); Math.sin (1) // => 0.8414709848078965 Math.sin (1) // => 0.8414709848078965 questa volta estratto dalla cache

Notare le abilità di chiusura che entrano in gioco. Abbiamo un locale nascondiglio variabile che viene mantenuta privata e protetta dal mondo esterno. Ciò impedirà qualsiasi manomissione che potrebbe invalidare la nostra cache.

La chiusura restituita ha accesso ai collegamenti della funzione esterna, e ciò significa che siamo in grado di restituire una funzione con accesso completo alla cache all'interno, oltre alla funzione originale! Questa piccola funzione può fare meraviglie per le prestazioni. Questa particolare estensione è impostata per gestire un argomento, ma mi piacerebbe vedere la tua pugnalata a una funzione cache a più argomenti.


Chiusure in natura

Come bonus aggiuntivo, diamo un'occhiata a un paio di usi di chiusure in natura.

jQuery

A volte, il famoso jQuery $ factory non è disponibile (si pensi a WordPress), e noi vogliamo usarlo nel modo in cui lo facciamo tipicamente. Piuttosto che raggiungere jQuery.noConflict, possiamo usare una chiusura per consentire alle funzioni interne di accedere al nostro $ legame ai parametri.

 (function ($) $ (document) .ready (function () // business as usual ...);) (jQuery);

Backbone.js

Su progetti Backbone.js di grandi dimensioni, potrebbe essere preferibile avere i propri modelli di applicazioni privati ​​e quindi esporre un'API pubblica nella visualizzazione principale dell'applicazione. Usando una chiusura, puoi facilmente ottenere questa privacy.

 (function (exports) var Product = Backbone.Model.extend (urlRoot: '/ products',); var ProductList = Backbone.Collection.extend (url: '/ products', model: Product); var Products = new ProductList; var ShoppingCartView = Backbone.View.extend (addProduct: function (prodotto, opts) return CartItems.create (product, opts);, removeProduct: function (product, opts) Products.remove (prodotto , opts);, getProduct: function (productId) return Products.get (productId);, getProducts: function () return Products.models;); // esporta solo la vista dell'applicazione principale exports.ShoppingCart = nuova ShoppingCartView;) (finestra);

Conclusione

Un breve riepilogo di ciò che abbiamo imparato:

  • Una chiusura non è altro che un oggetto funzione con un ambito.
  • Le chiusure prendono il loro nome dal modo in cui "chiudono" il contenuto.
  • Le chiusure incassano in grande tempo sull'ambito lessicale di JavaScript.
  • Le chiusure sono il modo per raggiungere la privacy in JavaScript.
  • Le chiusure sono in grado di acquisire la variabile locale e i collegamenti dei parametri di una funzione esterna.
  • JavaScript può essere potenziato con una certa magia di chiusura.
  • Le chiusure possono essere utilizzate con molte delle tue librerie preferite per renderle ancora più interessanti!

Grazie mille per aver letto! Sentiti libero di fare qualsiasi domanda. Ora godiamoci la festa della pizza!