You can read through part 1 here
In order for everyone not to forget, I briefly summarize the concept of the lexical environment:
Lexical Environment is an anonymous object found in every object in JavaScript, it contains variables in a scope and references to the external environment.
Oke? Now comes the definition of Closure:
A Closure is a function that can remember and access its lexical environment
even outside of that lexical environment
.
Let’s see the following example:
1 2 3 4 5 6 7 8 9 10 11 12 13 | <span class="token keyword">function</span> <span class="token function">say</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> term <span class="token operator">=</span> <span class="token string">"I would like to say:"</span> <span class="token punctuation">;</span> <span class="token keyword">function</span> <span class="token function">stuff</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> <span class="token template-string"><span class="token template-punctuation string">`</span> <span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span> term <span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> Hello Hi Ha Ya</span> <span class="token template-punctuation string">`</span></span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> stuff <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">let</span> s <span class="token operator">=</span> <span class="token function">say</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token function">s</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">></span> <span class="token constant">I</span> would like to say <span class="token operator">:</span> Hello Hi Ha Ya |
We can see that the stuff
function is returned by the say
function, and we assign s
to the stuff
function itself (not the return value of the stuff
function).
So, the stuff
function, after being assigned to s
, is no longer running in the lexical environment
it was declared with.
After the say
function is run, we would think the information in its lexical environment
will disappear, but because the stuff
function still exists and contains a connection to the external lexical environment
(here the say
function, This information is still kept in. Someone calls this connection chain the closure
.
Closures in JavaScript are more common than you might think
We will take the following 1 example:
1 2 3 4 5 6 7 8 9 10 11 | <span class="token keyword">function</span> <span class="token function">wait</span> <span class="token punctuation">(</span> <span class="token parameter">message</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">setTimeout</span> <span class="token punctuation">(</span> <span class="token keyword">function</span> <span class="token function">timer</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> message <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">,</span> <span class="token number">1000</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token function">wait</span> <span class="token punctuation">(</span> <span class="token string">"Hello, closure!"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token operator">></span> Hello <span class="token punctuation">,</span> closure <span class="token operator">!</span> |
Here is an example taken directly from the book You Dont Know Javascript . Anyone who has ever touched Javascript has used the setTimeout
function. In this example, we have an innermost function called timer
and will pass it into setTimeout
to run after 1 second. This is a usage that I think is quite popular. Just like in the previous example, you might think that after 1 second, the message
parameter of the wait
function should have disappeared, but the timer
function here still has a string connected to the lexical environment of wait
, and prevents message
is deleted by the Garbage Collector.
Closure and loop
What do you think the following loop will print out?
1 2 3 4 5 6 | <span class="token keyword">for</span> <span class="token punctuation">(</span> <span class="token keyword">var</span> i <span class="token operator">=</span> <span class="token number">1</span> <span class="token punctuation">;</span> i <span class="token operator"><=</span> <span class="token number">5</span> <span class="token punctuation">;</span> i <span class="token operator">++</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">setTimeout</span> <span class="token punctuation">(</span> <span class="token keyword">function</span> <span class="token function">timer</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> i <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">,</span> i <span class="token operator">*</span> <span class="token number">1000</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
The real result is that it will print the number 6
5 times, every 1 second. Ie the i
that the setTimeout
receives is different from the i
that the timer
receives, why is that?
First, where did 6
come from? It results when the loop encounters a condition that does not satisfy i <= 5
, ie i = 6
, then it terminates and the new setTimeout
function is run. And if you replace i*1000
with 0
, the setTimeout
function will still run after the loop completes. So how to print exactly as I want?
What we want here is probably that each loop captures its own variable i
, but for now, all 5 iterations of this loop are sharing the same external lexical environment
that has only one i
when the loop is running. accomplished. So what we need is a lexical environment
that can contain this variable i
.
1 2 3 4 5 6 7 8 9 | <span class="token keyword">for</span> <span class="token punctuation">(</span> <span class="token keyword">var</span> i <span class="token operator">=</span> <span class="token number">1</span> <span class="token punctuation">;</span> i <span class="token operator"><=</span> <span class="token number">5</span> <span class="token punctuation">;</span> i <span class="token operator">++</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token punctuation">(</span> <span class="token keyword">function</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> j <span class="token operator">=</span> i <span class="token punctuation">;</span> <span class="token function">setTimeout</span> <span class="token punctuation">(</span> <span class="token keyword">function</span> <span class="token function">timer</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> j <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">,</span> j <span class="token operator">*</span> <span class="token number">1000</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Or
1 2 3 4 5 6 7 8 | <span class="token keyword">for</span> <span class="token punctuation">(</span> <span class="token keyword">var</span> i <span class="token operator">=</span> <span class="token number">1</span> <span class="token punctuation">;</span> i <span class="token operator"><=</span> <span class="token number">5</span> <span class="token punctuation">;</span> i <span class="token operator">++</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token punctuation">(</span> <span class="token keyword">function</span> <span class="token punctuation">(</span> <span class="token parameter">j</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">setTimeout</span> <span class="token punctuation">(</span> <span class="token keyword">function</span> <span class="token function">timer</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> j <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">,</span> j <span class="token operator">*</span> <span class="token number">1000</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">(</span> i <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
This syntax:
1 2 3 4 | (function(){ ... })(); |
As an IIFE , using an IIFE in a loop creates a new lexical environment
around the setTimeout
function in each round, giving it the variable i
it needs.
What is the closure used?
There are many real life situations that often use closures, you should try to read the Javascript I wrote to see if there is any place to use it. Many people use it to write thedebounce
and throttle
functions
Those are the most basic things that I know about Closure. You can refer to here and here . One more interesting thing with the example above
1 2 3 4 5 6 | <span class="token keyword">for</span> <span class="token punctuation">(</span> <span class="token keyword">var</span> i <span class="token operator">=</span> <span class="token number">1</span> <span class="token punctuation">;</span> i <span class="token operator"><=</span> <span class="token number">5</span> <span class="token punctuation">;</span> i <span class="token operator">++</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">setTimeout</span> <span class="token punctuation">(</span> <span class="token keyword">function</span> <span class="token function">timer</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> i <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">,</span> i <span class="token operator">*</span> <span class="token number">1000</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
When you replace var
with let
, another behavior happens. You try to find out for yourself why that is okay.
Thanks for reading.