tl; dr:
where does the variable live?
Nell'ambiente è stato definito in
Does it become a property of function3, or stored somewhere else in function3?
No.
Does JavaScript traverse some kind of closure chain, similarly to how it traverses the prototype chain?
Sì..
Is it stored in memory somewhere else?
Sì.
tl; dr. 2:
Funzioni mantenere un riferimento all'ambiente in cui vengono creati in Quando una funzione viene chiamata crea un nuovo ambiente il cui genitore è l'ambiente la funzione mantenuto il riferimento a.
più lunga spiegazione:
Ogni volta che viene eseguita una funzione di un nuovo lexical environment viene creato. L'ambiente ha due "campi": uno environment record in cui vengono tracciate tutte le variabili e un ambiente lessicale che fa riferimento, come suggerisce il nome, all '"ambiente lessicale genitore".
Così, quando il vostro esempio di codice viene valutata, lo stato iniziale della memoria (prima di eseguire qualsiasi cosa) potrebbe essere simile a questo (semplificato):
+-(Global) lexical environment-+ +-Environment Record-+
+-------------+----------------+ +---------+----------+
| Environment | *--------+---> |function1|undefined |
| Record | | +---------+----------+
+-------------+----------------+ |function3|undefined |
| Outer | | +---------+----------+
| lexical | (empty) |
| environment | |
+-------------+----------------+
L'ambiente globale non dispone qualsiasi ambiente esterno perché è in cima. function1
e function3
sono due collegamenti non ancora inizializzati (l'assegnazione non è stata ancora valutata).
Dopo aver creato la funzione (valutare function1 = function() { ... }
), la memoria aspetto:
+------------------------------------------------------------------------+
| |
v |
+-(Global) lexical environment-+ +-Environment Record-+ +-----Function Object-+---+
+-------------+----------------+ +---------+----------+ +---------------+-----+---+
| Environment | *--------+--->|function1| *-----+---->|[[Environment]]| * |
| Record | | +---------+----------+ +---------------+---------+
+-------------+----------------+ |function3|undefined | | name |function1|
| Outer | | +---------+----------+ +---------------+---------+
| lexical | (empty) |
| environment | |
+-------------+----------------+
Ora function1
ha un valore, un oggetto funzione. Gli oggetti funzione hanno proprietà interne multiple (ad esempio [[Environment]]
) ed esterne (ad esempio name
). Come suggerisce il nome, non è possibile accedere alle proprietà interne dal codice utente. La proprietà [[Environment]]
è molto importante.Nota come si fa riferimento all'ambiente lessicale in cui è stata creata la funzione!
Il passaggio successivo è in esecuzione function3 = function1()
, ovvero chiamando function2
. Come ho detto all'inizio, ogni volta che viene eseguita una funzione viene creato un nuovo ambiente lessicale. Diamo un'occhiata alla memoria subito dopo aver inserito la funzione:
+------------------------------------------------------------------------+
| |
v |
+-(Global) lexical environment-+ +-Environment Record-+ +-----Function Object-+---+
+-------------+----------------+ +---------+----------+ +---------------+-----+---+
| Environment | *--------+--->|function1| +---->|[[Environment]]| * |
| Record | | +---------+----------+ +---------------+---------+
+> +-------------+----------------+ |function3|undefined | | name |function1|
| | Outer | | +---------+----------+ +---------------+---------+
| | lexical | (empty) |
| | environment | |
| +-------------+----------------+
|
|
|
| +-----lexical environment------+ +-Environment Record-+
| +-------------+----------------+ +---------+----------+
| | Environment | *--------+--->|variable |undefined |
| | Record | | +---------+----------+
| +-------------+----------------+ |function2|undefined |
| | Outer | | +---------+----------+
| | lexical | * |
| | environment | | |
| +-------------+--------+-------+
| |
+-------------------------+
Questo sembra molto simile alla struttura dell'ambiente globale! Abbiamo un ambiente lessicale con un record di ambiente con due binding non personalizzati. Ma la grande differenza ora è che "l'ambiente lessicale esterno" punta all'ambiente lessicale globale. Come è possibile?
Quando si chiama function1
e si crea un nuovo ambiente lessicale, si imposta il valore del campo "ambiente lessicale esterno" dei nuovi ambienti sul valore del campo [[Environment]]
function1
. Questo è il caso in cui è stata creata la catena di portata .
Ora, dopo l'esecuzione function1
, la memoria ha questa struttura:
+------------------------------------------------------------------------+
| |
v |
+-(Global) lexical environment-+ +-Environment Record-+ +-----Function Object-+---+
+-------------+----------------+ +---------+----------+ +---------------+-----+---+
| Environment | *--------+--->|function1| *-----+---->|[[Environment]]| * |
| Record | | +---------+----------+ +---------------+---------+
+> +-------------+----------------+ |function3| | | | name |function1|
| | Outer | | +---------+---+------+ +---------------+---------+
| | lexical | (empty) | |
| | environment | | |
| +-------------+----------------+ +-------------------------+
| |
| +----------------------------------------------------------------+--------+
| v | |
| +-----lexical environment------+ +-Environment Record-+ v |
| +-------------+----------------+ +---------+----------+ |
| | Environment | *--------+--->|variable | 'foo' | +-----Function Object-+---+
| | Record | | +---------+----------+ +---------------+-----+---+
| +-------------+----------------+ |function2| *-----+---->|[[Environment]]| * |
| | Outer | | +---------+----------+ +---------------+---------+
| | lexical | * | | name |function2|
| | environment | | | +---------------+---------+
| +-------------+--------+-------+
| |
+-------------------------+
simili come function1
, function2
ha un riferimento per l'ambiente creato da chiamando function2
. Inoltre, function3
fa riferimento alla funzione che abbiamo creato perché la restituiamo da function1
.
Ultimo passo: chiamando function3('bar')
:
+------------------------------------------------------------------------+
| |
v |
+-(Global) lexical environment-+ +-Environment Record-+ +-----Function Object-+---+
+-------------+----------------+ +---------+----------+ +---------------+-----+---+
| Environment | *--------+--->|function1| *-----+---->|[[Environment]]| * |
| Record | | +---------+----------+ +---------------+---------+
+> +-------------+----------------+ |function3| | | | name |function1|
| | Outer | | +---------+---+------+ +---------------+---------+
| | lexical | (empty) | |
| | environment | | |
| +-------------+----------------+ +-------------------------+
| |
| +----------------------------------------------------------------+--------+
| v | |
| +-----lexical environment------+ +-Environment Record-+ v |
| +-------------+----------------+ +---------+----------+ |
| | Environment | *--------+--->|variable | 'foo' | +-----Function Object-+---+
| | Record | | +---------+----------+ +---------------+-----+---+
|+>+-------------+----------------+ |function2| *-----+---->|[[Environment]]| * |
|| | Outer | | +---------+----------+ +---------------+---------+
|| | lexical | * | | name |function2|
|| | environment | | | +---------------+---------+
|| +-------------+--------+-------+
++------------------------+
|
| +-----lexical environment------+ +-Environment Record-+
| +-------------+----------------+ +---------+----------+
| | Environment | *--------+--->|argument | 'bar' |
| | Record | | +---------+----------+
| +-------------+----------------+
| | Outer | |
| | lexical | * |
| | environment | | |
| +-------------+--------+-------+
+------------------------+
simile qui, un nuovo ambiente è stato creato e le sue "ambiente esterno lessicale" punti sul campo per l'ambiente creato quando function1
è stato chiamato.
Ora, cercare il valore di argument
è semplice, perché esiste nel record proprio dell'ambiente. Ma quando si guarda su variable
, accade quanto segue: Poiché non esiste nel record proprio dell'ambiente, guarda al suo record "ambiente lessicale esterno". Può farlo perché ha un riferimento ad esso.
Sì, è possibile confrontare questo con il prototipo-catena, tipo di, dove ogni nodo memorizza le variabili e i loro valori per questa particolare chiamata-funzione (importante: quella particolare chiamata-funzione, non solo la funzione), fino a lo scopo globale. È lo stesso meccanismo che ti consente di accedere a variabili da un ambito esterno, se non sono definite nel tuo ambito corrente. MA: non c'è possibilità di stampare/vedere/accedere o modificare questa cosa. È un costrutto dietro le quinte del tuo motore. – Thomas
possibile duplicato di [Come funzionano le chiusure JavaScript a un livello basso?] (Http://stackoverflow.com/q/31735129/1048572) o [Come le chiusure dei jerk sono raccolte illeggibili] (http://stackoverflow.com/q/19798803/1048572)? Vedi anche [Dove si trova la variabile nella chiusura memorizzata, stack o heap?] (Http://stackoverflow.com/q/29225834/1048572) e [chiusure Javascript su heap o stack?] (Http://stackoverflow.com)/q/16959342/1,048572 millions). – Bergi