Ho fatto una domanda su Currying e sono state menzionate le chiusure. Cos'è una chiusura? Come si collega al curry?
Ho fatto una domanda su Currying e sono state menzionate le chiusure. Cos'è una chiusura? Come si collega al curry?
Risposte:
Quando si dichiara una variabile locale, quella variabile ha un ambito. In genere, le variabili locali esistono solo all'interno del blocco o della funzione in cui vengono dichiarate.
function() {
var a = 1;
console.log(a); // works
}
console.log(a); // fails
Se provo ad accedere a una variabile locale, la maggior parte delle lingue la cercherà nell'ambito corrente, quindi passa attraverso gli ambiti padre fino a raggiungere l'ambito radice.
var a = 1;
function() {
console.log(a); // works
}
console.log(a); // works
Al termine di un blocco o di una funzione, le sue variabili locali non sono più necessarie e di solito vengono eliminate dalla memoria.
È così che normalmente ci aspettiamo che le cose funzionino.
Una chiusura è un ambito persistente che si aggrappa alle variabili locali anche dopo che l'esecuzione del codice è stata spostata da quel blocco. Le lingue che supportano la chiusura (come JavaScript, Swift e Ruby) ti permetteranno di mantenere un riferimento a un ambito (compresi i relativi ambiti padre), anche dopo che il blocco in cui sono state dichiarate quelle variabili ha terminato l'esecuzione, a condizione che tu mantenga un riferimento a quel blocco o funzione da qualche parte.
L'oggetto scope e tutte le sue variabili locali sono legate alla funzione e persisteranno finché persiste tale funzione.
Questo ci dà la portabilità delle funzioni. Possiamo aspettarci che qualsiasi variabile che fosse nell'ambito quando la funzione è stata definita per la prima volta sia ancora nell'ambito quando in seguito chiamiamo la funzione, anche se la chiamiamo in un contesto completamente diverso.
Ecco un esempio davvero semplice in JavaScript che illustra il punto:
outer = function() {
var a = 1;
var inner = function() {
console.log(a);
}
return inner; // this returns a function
}
var fnc = outer(); // execute outer to get inner
fnc();
Qui ho definito una funzione all'interno di una funzione. La funzione interna ottiene l'accesso a tutte le variabili locali della funzione esterna, incluso a
. La variabile a
rientra nell'ambito della funzione interna.
Normalmente quando una funzione esce, tutte le sue variabili locali vengono spazzate via. Tuttavia, se restituiamo la funzione interna e la assegniamo a una variabile in fnc
modo che persista dopo la outer
sua uscita, anche tutte le variabili che erano nell'ambito nell'ambito della inner
definizione erano persistenti . La variabile a
è stata chiusa, è all'interno di una chiusura.
Si noti che la variabile a
è totalmente privata fnc
. Questo è un modo per creare variabili private in un linguaggio di programmazione funzionale come JavaScript.
Come potresti immaginare, quando lo chiamo fnc()
stampa il valore di a
, che è "1".
In una lingua senza chiusura, la variabile a
sarebbe stata raccolta e gettata via quando la funzione era outer
uscita. Chiamare fnc avrebbe generato un errore perché a
non esiste più.
In JavaScript, la variabile a
persiste perché l'ambito della variabile viene creato quando la funzione viene dichiarata per la prima volta e persiste finché la funzione continua ad esistere.
a
appartiene allo scopo di outer
. L'ambito di inner
ha un puntatore genitore all'ambito di outer
. fnc
è una variabile che punta a inner
. a
persiste finché fnc
persiste. a
è all'interno della chiusura.
Faccio un esempio (in JavaScript):
function makeCounter () {
var count = 0;
return function () {
count += 1;
return count;
}
}
var x = makeCounter();
x(); returns 1
x(); returns 2
...etc...
Ciò che questa funzione, makeCounter, fa è che restituisce una funzione, che abbiamo chiamato x, che verrà conteggiata da una ogni volta che viene chiamata. Dato che non stiamo fornendo alcun parametro a x, in qualche modo deve ricordare il conteggio. Sa dove trovarlo in base a quello che viene chiamato scoping lessicale - deve cercare il punto in cui è definito per trovare il valore. Questo valore "nascosto" è ciò che viene chiamato chiusura.
Ecco di nuovo il mio esempio di curry:
function add (a) {
return function (b) {
return a + b;
}
}
var add3 = add(3);
add3(4); returns 7
Quello che puoi vedere è che quando chiami aggiungi con il parametro a (che è 3), quel valore è contenuto nella chiusura della funzione restituita che stiamo definendo add3. In questo modo, quando chiamiamo add3, sa dove trovare il valore a per eseguire l'aggiunta.
La risposta di Kyle è abbastanza buona. Penso che l'unico ulteriore chiarimento sia che la chiusura è fondamentalmente un'istantanea dello stack nel punto in cui viene creata la funzione lambda. Quindi, quando la funzione viene rieseguita, lo stack viene ripristinato in quello stato prima di eseguire la funzione. Quindi, come menziona Kyle, quel valore nascosto ( count
) è disponibile quando viene eseguita la funzione lambda.
Prima di tutto, contrariamente a quanto la maggior parte delle persone qui ti dice, la chiusura non è una funzione ! Così che cosa è esso?
È un insieme di simboli definiti nel "contesto circostante" di una funzione (noto come il suo ambiente ) che lo rendono un'espressione CHIUSA (cioè un'espressione in cui ogni simbolo è definito e ha un valore, quindi può essere valutato).
Ad esempio, quando si dispone di una funzione JavaScript:
function closed(x) {
return x + 3;
}
è un'espressione chiusa perché tutti i simboli presenti in essa sono definiti in essa (i loro significati sono chiari), quindi puoi valutarla. In altre parole, è autonomo .
Ma se hai una funzione come questa:
function open(x) {
return x*y + 3;
}
è un'espressione aperta perché in essa sono presenti simboli che non sono stati definiti in essa. Vale a dire, y
. Quando osserviamo questa funzione, non possiamo dire cosa y
sia e cosa significhi, non ne conosciamo il valore, quindi non possiamo valutare questa espressione. Cioè non possiamo chiamare questa funzione finché non diciamo cosa y
dovrebbe significare in essa. Questa y
si chiama variabile libera .
Ciò y
richiede una definizione, ma questa definizione non fa parte della funzione - è definita da qualche altra parte, nel suo "contesto circostante" (noto anche come ambiente ). Almeno questo è ciò che speriamo: P
Ad esempio, potrebbe essere definito a livello globale:
var y = 7;
function open(x) {
return x*y + 3;
}
Oppure potrebbe essere definito in una funzione che lo avvolge:
var global = 2;
function wrapper(y) {
var w = "unused";
return function(x) {
return x*y + 3;
}
}
La parte dell'ambiente che dà alle variabili libere in un'espressione il loro significato, è la chiusura . Si chiama in questo modo, perché trasforma un'espressione aperta in un'espressione chiusa , fornendo queste definizioni mancanti per tutte le sue variabili libere , in modo da poterle valutare.
Nell'esempio sopra, la funzione interna (che non abbiamo dato un nome perché non ne avevamo bisogno) è un'espressione aperta perché la variabile y
in essa è libera - la sua definizione è al di fuori della funzione, nella funzione che la avvolge . L' ambiente per quella funzione anonima è l'insieme di variabili:
{
global: 2,
w: "unused",
y: [whatever has been passed to that wrapper function as its parameter `y`]
}
Ora, la chiusura è quella parte di questo ambiente che chiude la funzione interna fornendo le definizioni per tutte le sue variabili libere . Nel nostro caso, l'unica variabile libera nella funzione interna era y
, quindi la chiusura di quella funzione è questo sottoinsieme del suo ambiente:
{
y: [whatever has been passed to that wrapper function as its parameter `y`]
}
Gli altri due simboli definiti nell'ambiente non fanno parte della chiusura di quella funzione, perché non richiede l'esecuzione. Non sono necessari per chiuderlo .
Maggiori informazioni sulla teoria alla base di questo qui: https://stackoverflow.com/a/36878651/434562
Vale la pena notare che nell'esempio sopra, la funzione wrapper restituisce la sua funzione interna come valore. Il momento in cui chiamiamo questa funzione può essere remoto nel tempo dal momento in cui la funzione è stata definita (o creata). In particolare, la sua funzione di wrapping non è più in esecuzione e i suoi parametri che sono stati nello stack di chiamate non sono più presenti: P Questo crea un problema, perché la funzione interna deve y
essere lì quando viene chiamata! In altre parole, richiede le variabili dalla sua chiusura per sopravvivere in qualche modo alla funzione wrapper ed essere lì quando necessario. Pertanto, la funzione interna deve creare un'istantanea di queste variabili che ne rendono la chiusura e conservale in un luogo sicuro per un uso successivo. (Da qualche parte fuori dallo stack di chiamate.)
E questo è il motivo per cui le persone spesso confondono il termine chiusura come quel tipo speciale di funzione che può fare tali istantanee delle variabili esterne che usano, o la struttura di dati usata per memorizzare queste variabili per dopo. Ma spero che si capisce ora che sono non la chiusura in sé - sono solo modi per implementare le chiusure in un linguaggio di programmazione, o di meccanismi linguistici che permette le variabili dalla chiusura della funzione di essere lì quando necessario. Ci sono molte idee sbagliate intorno alle chiusure che (inutilmente) rendono questo argomento molto più confuso e complicato che in realtà sia.
Una chiusura è una funzione che può fare riferimento allo stato in un'altra funzione. Ad esempio, in Python, utilizza la chiusura "interna":
def outer (a):
b = "variable in outer()"
def inner (c):
print a, b, c
return inner
# Now the return value from outer() can be saved for later
func = outer ("test")
func (1) # prints "test variable in outer() 1
Per facilitare la comprensione delle chiusure potrebbe essere utile esaminare come potrebbero essere implementate in un linguaggio procedurale. Questa spiegazione seguirà un'implementazione semplicistica di chiusure in Scheme.
Per iniziare, devo introdurre il concetto di spazio dei nomi. Quando si immette un comando in un interprete Scheme, è necessario valutare i vari simboli nell'espressione e ottenere il loro valore. Esempio:
(define x 3)
(define y 4)
(+ x y) returns 7
Le espressioni definite memorizzano il valore 3 nello spot per x e il valore 4 nello spot per y. Quindi quando chiamiamo (+ xy), l'interprete cerca i valori nello spazio dei nomi ed è in grado di eseguire l'operazione e restituire 7.
Tuttavia, nello Schema ci sono espressioni che ti consentono di sovrascrivere temporaneamente il valore di un simbolo. Ecco un esempio:
(define x 3)
(define y 4)
(let ((x 5))
(+ x y)) returns 9
x returns 3
Quello che fa la parola chiave let è l'introduzione di un nuovo spazio dei nomi con x come valore 5. Noterai che è ancora in grado di vedere che y è 4, rendendo la somma restituita come 9. Puoi anche vedere che una volta che l'espressione è terminata x è tornato ad essere 3. In questo senso, x è stato temporaneamente mascherato dal valore locale.
I linguaggi procedurali e orientati agli oggetti hanno un concetto simile. Ogni volta che dichiari una variabile in una funzione che ha lo stesso nome di una variabile globale, ottieni lo stesso effetto.
Come lo implementeremmo? Un modo semplice è con un elenco collegato: la testa contiene il nuovo valore e la coda contiene il vecchio spazio dei nomi. Quando devi cercare un simbolo, inizi dalla testa e scendi lungo la coda.
Passiamo ora all'implementazione di funzioni di prima classe per il momento. Più o meno, una funzione è un insieme di istruzioni da eseguire quando la funzione viene chiamata culminante nel valore restituito. Quando leggiamo in una funzione, possiamo memorizzare queste istruzioni dietro le quinte ed eseguirle quando viene chiamata la funzione.
(define x 3)
(define (plus-x y)
(+ x y))
(let ((x 5))
(plus-x 4)) returns ?
Definiamo x come 3 e più-x come parametro, y, più il valore di x. Alla fine chiamiamo plus-x in un ambiente in cui x è stato mascherato da una nuova x, questa ha valore 5. Se memorizziamo semplicemente l'operazione, (+ xy), per la funzione plus-x, poiché siamo nel contesto di x essendo 5 il risultato restituito sarebbe 9. Questo è ciò che si chiama scoping dinamico.
Tuttavia, Scheme, Common Lisp e molte altre lingue hanno quello che viene chiamato scoping lessicale - oltre a memorizzare l'operazione (+ xy) memorizziamo anche lo spazio dei nomi in quel particolare punto. In questo modo, quando osserviamo i valori, possiamo vedere che x, in questo contesto, è davvero 3. Questa è una chiusura.
(define x 3)
(define (plus-x y)
(+ x y))
(let ((x 5))
(plus-x 4)) returns 7
In breve, possiamo usare un elenco collegato per memorizzare lo stato dello spazio dei nomi al momento della definizione della funzione, permettendoci di accedere alle variabili dagli ambiti racchiusi, oltre a fornirci la possibilità di mascherare localmente una variabile senza influire sul resto del programma.
Why do we want to access variables that are out of scope? when we say let x = 5, we want x to be 5 and not 3. What is happening?
Ecco un esempio reale del motivo per cui Closures prende a calci in culo ... Questo è direttamente dal mio codice Javascript. Vorrei illustrare.
Function.prototype.delay = function(ms /*[, arg...]*/) {
var fn = this,
args = Array.prototype.slice.call(arguments, 1);
return window.setTimeout(function() {
return fn.apply(fn, args);
}, ms);
};
Ed ecco come lo useresti:
var startPlayback = function(track) {
Player.play(track);
};
startPlayback(someTrack);
Ora immagina di voler ritardare la riproduzione, ad esempio 5 secondi dopo l'esecuzione di questo frammento di codice. Bene, è facile con delay
ed è la chiusura:
startPlayback.delay(5000, someTrack);
// Keep going, do other things
Quando chiami delay
con 5000
ms, viene eseguito il primo frammento e memorizza gli argomenti passati nella sua chiusura. Quindi, 5 secondi dopo, quando si verifica il setTimeout
callback, la chiusura mantiene ancora quelle variabili, quindi può chiamare la funzione originale con i parametri originali.
Questo è un tipo di curry o decorazione di funzioni.
Senza chiusure, dovresti in qualche modo mantenere lo stato di quelle variabili al di fuori della funzione, sporcando così il codice al di fuori della funzione con qualcosa che logicamente appartiene al suo interno. L'uso delle chiusure può migliorare notevolmente la qualità e la leggibilità del codice.
var pure = function pure(x){
return x
// only own environment is used
}
var foo = "bar"
var closure = function closure(){
return foo
// foo is a free variable from the outer environment
}
Una chiusura è una funzione e il suo ambito è assegnato (o usato come) a una variabile. Pertanto, la chiusura del nome: l'ambito e la funzione sono racchiusi e utilizzati proprio come qualsiasi altra entità.
Secondo Wikipedia, una chiusura è:
Tecniche per l'implementazione dell'associazione di nomi con ambito lessicale in linguaggi con funzioni di prima classe.
Cosa significa? Diamo un'occhiata ad alcune definizioni.
Spiegherò chiusure e altre definizioni correlate usando questo esempio:
function startAt(x) {
return function (y) {
return x + y;
}
}
var closure1 = startAt(1);
var closure2 = startAt(5);
console.log(closure1(3)); // 4 (x == 1, y == 3)
console.log(closure2(3)); // 8 (x == 5, y == 3)
Fondamentalmente ciò significa che possiamo usare funzioni come qualsiasi altra entità . Possiamo modificarli, passarli come argomenti, restituirli da funzioni o assegnarli a variabili. Tecnicamente parlando, sono cittadini di prima classe , da cui il nome: funzioni di prima classe.
Nell'esempio sopra, startAt
restituisce una funzione ( anonima ) a cui la funzione viene assegnata closure1
e closure2
. Quindi, come vedi, i trattamenti JavaScript funzionano esattamente come qualsiasi altra entità (cittadini di prima classe).
Vincolante Il è di scoprire quali dati una variabile (identificatore) i riferimenti . L'ambito è davvero importante qui, in quanto è ciò che determinerà come viene risolta un'associazione.
Nell'esempio sopra:
y
è vincolato a 3
.startAt
campo di applicazione, x
è associato 1
o 5
(a seconda della chiusura).All'interno dell'ambito della funzione anonima, x
non è vincolato a nessun valore, quindi deve essere risolto in un ambito superiore startAt
.
Come dice Wikipedia , l'ambito:
È la regione di un programma per computer in cui è valida l'associazione: in cui è possibile utilizzare il nome per fare riferimento all'entità .
Esistono due tecniche:
Per ulteriori spiegazioni, dai un'occhiata a questa domanda e dai un'occhiata a Wikipedia .
Nell'esempio sopra, possiamo vedere che JavaScript ha un ambito lessicale, perché quando x
viene risolto, l'associazione viene cercata nell'ambito superiore ( startAt
s), in base al codice sorgente (la funzione anonima che cerca x è definita all'interno startAt
) e non basato sullo stack di chiamate, il modo (l'ambito in cui) è stata chiamata la funzione.
Nel nostro esempio, quando chiamiamo startAt
, verrà restituita una funzione (di prima classe) che verrà assegnata closure1
e closure2
quindi verrà creata una chiusura, perché le variabili passate 1
e 5
verranno salvate nell'ambito startAt
dell'ambito, che verrà racchiuso con il valore restituito funzione anonima. Quando chiamiamo questa funzione anonima tramite closure1
e closure2
con lo stesso argomento ( 3
), il valore di y
verrà trovato immediatamente (poiché quello è il parametro di quella funzione), ma x
non è limitato nell'ambito della funzione anonima, quindi la risoluzione continua in l'ambito della funzione (lessicamente) superiore (che è stato salvato nella chiusura) dove x
si trova che è legato a uno 1
o5
. Ora sappiamo tutto per la somma in modo che il risultato possa essere restituito, quindi stampato.
Ora dovresti capire chiusure e come si comportano, che è una parte fondamentale di JavaScript.
Oh, e hai anche imparato di cosa tratta il curry : usi le funzioni (chiusure) per passare ogni argomento di un'operazione invece di usare una funzione con più parametri.
La chiusura è una funzionalità di JavaScript in cui una funzione ha accesso alle proprie variabili di ambito, accesso alle variabili di funzione esterne e accesso alle variabili globali.
La chiusura ha accesso al suo ambito di funzione esterno anche dopo il ritorno della funzione esterna. Ciò significa che una chiusura può ricordare e accedere a variabili e argomenti della sua funzione esterna anche dopo che la funzione è terminata.
La funzione interna può accedere alle variabili definite nel proprio ambito, nell'ambito della funzione esterna e ambito globale. E la funzione esterna può accedere alla variabile definita nel proprio ambito e nell'ambito globale.
Esempio di chiusura :
var globalValue = 5;
function functOuter() {
var outerFunctionValue = 10;
//Inner function has access to the outer function value
//and the global variables
function functInner() {
var innerFunctionValue = 5;
alert(globalValue + outerFunctionValue + innerFunctionValue);
}
functInner();
}
functOuter();
L'output sarà 20 quale somma della propria variabile di funzione interna, variabile di funzione esterna e valore della variabile globale.
In una situazione normale, le variabili sono vincolate dalla regola di scoping: le variabili locali funzionano solo all'interno della funzione definita. La chiusura è un modo per infrangere temporaneamente questa regola per comodità.
def n_times(a_thing)
return lambda{|n| a_thing * n}
end
nel codice sopra, lambda(|n| a_thing * n}
è la chiusura perché a_thing
viene indicata da lambda (un creatore di funzioni anonime).
Ora, se si inserisce la funzione anonima risultante in una variabile di funzione.
foo = n_times(4)
foo infrange la normale regola di scoping e inizia a usarne 4 internamente.
foo.call(3)
restituisce 12.
• Una chiusura è un sottoprogramma e l'ambiente di riferimento in cui è stato definito
- L'ambiente di riferimento è necessario se il sottoprogramma può essere richiamato da qualsiasi posizione arbitraria nel programma
- Un linguaggio con ambito statico che non consente sottoprogrammi nidificati non necessita di chiusure
- Le chiusure sono necessarie solo se un sottoprogramma può accedere alle variabili negli ambiti di annidamento e può essere chiamato da qualsiasi luogo
- Per supportare le chiusure, un'implementazione potrebbe dover fornire un'estensione illimitata ad alcune variabili (poiché un sottoprogramma può accedere a una variabile non locale che normalmente non è più attiva)
Esempio
function makeAdder(x) {
return function(y) {return x + y;}
}
var add10 = makeAdder(10);
var add5 = makeAdder(5);
document.write(″add 10 to 20: ″ + add10(20) +
″<br />″);
document.write(″add 5 to 20: ″ + add5(20) +
″<br />″);
Ecco un altro esempio di vita reale e l'utilizzo di un linguaggio di scripting popolare nei giochi: Lua. Avevo bisogno di cambiare leggermente il modo in cui una funzione di libreria funzionava per evitare un problema con stdin non disponibile.
local old_dofile = dofile
function dofile( filename )
if filename == nil then
error( 'Can not use default of stdin.' )
end
old_dofile( filename )
end
Il valore di old_dofile scompare quando questo blocco di codice termina il suo ambito (perché è locale), tuttavia il valore è stato racchiuso in una chiusura, quindi la nuova funzione di dofile ridefinita PUO 'accedervi, o meglio una copia memorizzata insieme alla funzione come un 'sopravvalutare'.
Da Lua.org :
Quando una funzione è scritta racchiusa in un'altra funzione, ha pieno accesso alle variabili locali dalla funzione che racchiude; questa funzione si chiama scoping lessicale. Anche se può sembrare ovvio, non lo è. L'ambito lessicale, oltre a funzioni di prima classe, è un concetto potente in un linguaggio di programmazione, ma pochi linguaggi supportano tale concetto.
Se vieni dal mondo Java, puoi confrontare una chiusura con una funzione membro di una classe. Guarda questo esempio
var f=function(){
var a=7;
var g=function(){
return a;
}
return g;
}
La funzione g
è una chiusura: si g
chiude a
. Quindi g
può essere confrontata con una funzione membro, a
può essere confrontata con un campo di classe e la funzione f
con una classe.
Chiusure Ogni volta che abbiamo una funzione definita all'interno di un'altra funzione, la funzione interna ha accesso alle variabili dichiarate nella funzione esterna. Le chiusure sono meglio spiegate con esempi. Nel Listato 2-18, puoi vedere che la funzione interna ha accesso a una variabile (variabileInOuterFunction) dall'ambito esterno. Le variabili nella funzione esterna sono state chiuse (o associate) dalla funzione interna. Da qui il termine chiusura. Il concetto in sé è abbastanza semplice e abbastanza intuitivo.
Listing 2-18:
function outerFunction(arg) {
var variableInOuterFunction = arg;
function bar() {
console.log(variableInOuterFunction); // Access a variable from the outer scope
}
// Call the local function to demonstrate that it has access to arg
bar();
}
outerFunction('hello closure!'); // logs hello closure!
fonte: http://index-of.es/Varios/Basarat%20Ali%20Syed%20(auth.)-Beginning%20Node.js-Apress%20(2014).pdf
Dai un'occhiata sotto il codice per capire la chiusura in modo più approfondito:
for(var i=0; i< 5; i++){
setTimeout(function(){
console.log(i);
}, 1000);
}
Qui cosa verrà prodotto? 0,1,2,3,4
non sarà 5,5,5,5,5
per via della chiusura
Quindi come risolverà? La risposta è sotto:
for(var i=0; i< 5; i++){
(function(j){ //using IIFE
setTimeout(function(){
console.log(j);
},1000);
})(i);
}
Consentitemi di spiegare semplicemente, quando una funzione creata non accade nulla fino a quando non viene chiamato così per il ciclo nel 1 ° codice chiamato 5 volte ma non chiamato immediatamente così quando viene chiamato cioè dopo 1 secondo e anche questo è asincrono, quindi prima che questo per il ciclo terminato e memorizzare il valore 5 in var ie infine esegui la setTimeout
funzione cinque volte e stampa5,5,5,5,5
Ecco come si risolve usando IIFE, ovvero l'espressione della funzione di invocazione immediata
(function(j){ //i is passed here
setTimeout(function(){
console.log(j);
},1000);
})(i); //look here it called immediate that is store i=0 for 1st loop, i=1 for 2nd loop, and so on and print 0,1,2,3,4
Per di più, si prega di comprendere il contesto di esecuzione per comprendere la chiusura.
Esiste un'altra soluzione per risolverlo usando let (funzione ES6) ma sotto la cappa sopra la funzione è funzionata
for(let i=0; i< 5; i++){
setTimeout(function(){
console.log(i);
},1000);
}
Output: 0,1,2,3,4
=> Altre spiegazioni:
In memoria, quando per il ciclo eseguire l'immagine, fare come di seguito:
Loop 1)
setTimeout(function(){
console.log(i);
},1000);
Loop 2)
setTimeout(function(){
console.log(i);
},1000);
Loop 3)
setTimeout(function(){
console.log(i);
},1000);
Loop 4)
setTimeout(function(){
console.log(i);
},1000);
Loop 5)
setTimeout(function(){
console.log(i);
},1000);
Qui non sono eseguito e quindi dopo il ciclo completo, var ho archiviato il valore 5 in memoria ma il suo ambito è sempre visibile nella sua funzione figlio, quindi quando la funzione viene eseguita al rovescio setTimeout
per cinque volte, viene stampata5,5,5,5,5
quindi per risolvere questo uso IIFE come spiegato sopra.
Currying: ti permette di valutare parzialmente una funzione passando solo in un sottoinsieme dei suoi argomenti. Considera questo:
function multiply (x, y) {
return x * y;
}
const double = multiply.bind(null, 2);
const eight = double(4);
eight == 8;
Chiusura: una chiusura non è altro che l'accesso a una variabile al di fuori dell'ambito di una funzione. È importante ricordare che una funzione all'interno di una funzione o di una funzione nidificata non è una chiusura. Le chiusure vengono sempre utilizzate quando è necessario accedere alle variabili al di fuori dell'ambito della funzione.
function apple(x){
function google(y,z) {
console.log(x*y);
}
google(7,2);
}
apple(3);
// the answer here will be 21
La chiusura è molto semplice. Possiamo considerarlo come segue: Closure = function + il suo ambiente lessicale
Considera la seguente funzione:
function init() {
var name = “Mozilla”;
}
Quale sarà la chiusura nel caso sopra? Funzione init () e variabili nel suo ambiente lessicale, ad esempio nome. Chiusura = init () + nome
Considera un'altra funzione:
function init() {
var name = “Mozilla”;
function displayName(){
alert(name);
}
displayName();
}
Quali saranno le chiusure qui? La funzione interna può accedere alle variabili della funzione esterna. displayName () può accedere al nome della variabile dichiarato nella funzione parent, init (). Tuttavia, se presenti, verranno utilizzate le stesse variabili locali in displayName ().
Chiusura 1: funzione init + (variabile nome + funzione displayName ()) -> ambito lessicale
Chiusura 2: funzione displayName + (variabile nome) -> ambito lessicale
Programmare significa semplicemente ricordare le cose.
Esempio
var a = 0;
a = a + 1; // => 1
a = a + 1; // => 2
a = a + 1; // => 3
Nel caso precedente, lo stato è memorizzato nella variabile "a". Seguiamo aggiungendo 1 a "a" più volte. Possiamo farlo solo perché siamo in grado di "ricordare" il valore. Il detentore dello stato, "a", mantiene quel valore in memoria.
Spesso, nei linguaggi di programmazione, si desidera tenere traccia delle cose, ricordare le informazioni e accedervi in un secondo momento.
Questo, in altre lingue , è comunemente realizzato attraverso l'uso di classi. Una classe, proprio come le variabili, tiene traccia del suo stato. E le istanze di quella classe, a loro volta, hanno anche uno stato al loro interno. Stato significa semplicemente informazioni che è possibile archiviare e recuperare in un secondo momento.
Esempio
class Bread {
constructor (weight) {
this.weight = weight;
}
render () {
return `My weight is ${this.weight}!`;
}
}
Come possiamo accedere a "peso" dal metodo "render"? Bene, grazie allo stato. Ogni istanza della classe Bread può rendere il proprio peso leggendolo dallo "stato", un luogo in memoria in cui è possibile memorizzare tali informazioni.
Ora, JavaScript è un linguaggio molto singolare che storicamente non ha classi (ora lo fa, ma sotto il cofano ci sono solo funzioni e variabili), quindi le Chiusure forniscono a JavaScript un modo per ricordare le cose e accedervi in un secondo momento.
Esempio
var n = 0;
var count = function () {
n = n + 1;
return n;
};
count(); // # 1
count(); // # 2
count(); // # 3
L'esempio sopra ha raggiunto l'obiettivo di "mantenere lo stato" con una variabile. Questo è fantastico! Tuttavia, questo ha lo svantaggio che la variabile (il "titolare" dello stato) è ora esposta. Possiamo fare di meglio. Possiamo usare le chiusure.
Esempio
var countGenerator = function () {
var n = 0;
var count = function () {
n = n + 1;
return n;
};
return count;
};
var count = countGenerator();
count(); // # 1
count(); // # 2
count(); // # 3
Ora la nostra funzione "conta" può contare. È in grado di farlo solo perché può "trattenere" lo stato. Lo stato in questo caso è la variabile "n". Questa variabile è ora chiusa. Chiuso nel tempo e nello spazio. Nel tempo perché non sarai mai in grado di recuperarlo, modificarlo, assegnargli un valore o interagire direttamente con esso. Nello spazio perché è nidificato geograficamente nella funzione "countGenerator".
Perché è fantastico? Perché senza coinvolgere altri strumenti sofisticati e complicati (ad es. Classi, metodi, istanze, ecc.) Siamo in grado di 1. nascondere 2. il controllo a distanza
Nascondiamo lo stato, la variabile "n", che la rende una variabile privata! Abbiamo anche creato un'API che può controllare questa variabile in un modo predefinito. In particolare, possiamo chiamare l'API in questo modo "count ()" e questo aggiunge 1 a "n" da una "distanza". In nessun modo, forma o forma chiunque potrà mai accedere a "n" se non attraverso l'API.
Le chiusure sono una parte importante del perché questo è.