Skip to content

Commit 354d9d4

Browse files
committed
up
1 parent 816e5fa commit 354d9d4

File tree

8 files changed

+40
-16
lines changed

8 files changed

+40
-16
lines changed

1-js/5-deeper/2-closure/article.md

Lines changed: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -196,62 +196,86 @@ The questions may arise:
196196

197197
We'll answer the 1st question and the other ones will also become obvious.
198198

199-
**In Javascript, a function remembers the Lexical Environment where it was created.**
199+
**The rule: a function looks for a variable lexically from inside to outside.**
200200

201-
Technically, when a function is created, it gets a special hidden property `[[Environment]]` that keeps the reference to the current outer Lexical Environment. And when the function runs, this property is used as the outer lexical reference.
201+
So, for the example, above, the order will be:
202+
203+
![](lexical-search-order.png)
204+
205+
1. The variable is first searched among the locals of the nested function.
206+
2. Then in the variables of the outer function.
207+
3. Then in the outer function... And so on until it reaches globals, then the search stops.
208+
209+
No matter where the function is called, the rule is the same. If a variable is modified, it is modified on the place where it is found, so future accesses will get the updated variant.
210+
211+
To understand things even better, let's see how it is technically implemented.
212+
213+
## Environments in detail
214+
215+
Technically, when a function is created, it gets a special hidden property `[[Environment]]` that keeps the reference to the Lexical Environment where it is created. So it kind of remembers where it was made. And when the function runs, this property is used as the outer lexical reference, giving the direction for the search.
202216

203217
Let's see how it works in the example above, step-by-step:
204218

205-
1. When the script has just started, there is only global Lexical Environment:
219+
1. When the script has just started, and the execution flow is at the 1st line, there is only global Lexical Environment:
206220

207221
![](lexenv-nested-makecounter-1.png)
208222

209223
At this moment there is only `makeCounter` function. And it did not run yet.
210224

211-
2. The call to `makeCounter()` is performed:
225+
All functions "on birth" receive a hidden property `[[Environment]]` with the reference to the Lexical Environment of creation. For `makeCounter` that's the global one.
226+
227+
2. The code runs on, and the call to `makeCounter()` is performed:
212228

213229
![](lexenv-nested-makecounter-2.png)
214230

215-
The Lexical Environment for the call is created. It stores `count: 0`, the only local variable. The property `[[Environment]]` of `makeCounter` is used as an outer lexical reference.
231+
The Lexical Environment for the `makeCounter()` call is created. It stores local variables, in our case `count: 0` is the only local variable. The property `[[Environment]]` of `makeCounter` is used as an outer lexical reference for the new Lexical Environment.
216232

217233
Now we have two Lexical Environments: the first one is global, the second one is for the current `makeCounter` call, and it references the global one.
218234

219-
3. The nested function is created. It gets `[[Environment]]` property referencing the enclosing Lexical Environment (in which it is created).
235+
3. During the execution of `makeCounter()` the nested function is created. Here a Function Expression is used to define a function. But that doesn't matter. All functions get the `[[Environment]]` property that references the Lexical Environment where they are made. For our new nested function that is the current Lexical Environment of `makeCounter()`:
220236

221237
![](lexenv-nested-makecounter-3.png)
222238

223-
Please note that at the inner function was created, but not yet called. The code inside `function() { ... }` is not running.
239+
Please note that at the inner function was created, but not yet called. The code inside `function() { return count++; }` is not running.
224240

225241
So we still have two Lexical Environments.
226242

227-
4. Now the `counter` variable actually stores that tiny function. When we run `counter()`, the single line of code will be executed: `return count++`. The `makeCounter()` call has already finished, it will not get the control any more.
243+
4. The call to `makeCounter()` is finished, and the result (the tiny nested function) is assigned to the global variable `counter`. When we run `counter()`, the single line of code will be executed: `return count++`.
244+
245+
The `makeCounter()` call has already finished, its code will not get the control any more.
228246

229247
![](lexenv-nested-makecounter-4.png)
230248

231-
Please note that despite the function is called on the last line, outside of anything, its internal `[[Environment]]` property points back to the Lexical Environment where it was created.
249+
Please note that though the `counter()` function is called on the last line, outside of anything, its internal `[[Environment]]` property points back to the Lexical Environment where it was created...
232250

233251

234-
5. So, when `counter()` code actually executes, it gets an "empty" Lexical Environment (no local variables), and the `[[Environment]]` property is used for the outer lexical reference.
252+
5. ...So, when `counter()` code actually executes, it gets an "empty" Lexical Environment (no local variables). But the `[[Environment]]` is used for the outer lexical reference, giving it access to the variables of the former `makeCounter()` call:
235253

236254
![](lexenv-nested-makecounter-5.png)
237255

238-
Now when it looks for `count`, it finds it among the variables of the former `makeCounter` call. The funny thing is that `makeCounter()` finished some time ago. But its variables are still alive, and accessible from a nested function.
256+
Now if it accesses a variable, it first searches its own Lexical Environment (empty), then the Lexical Environment of the former `makeCounter()` call, then the global one.
257+
258+
When it looks for `count`, it finds it among the variables `makeCounter`, in the nearest outer Lexical Environment.
259+
260+
The funny thing is that `makeCounter()` finished some time ago. But its variables are still alive, and accessible from a nested function.
239261

240262
6. The call to `counter()` not only returns the value of `count`, but also increases it. Note that the modification is done "at place". The value of `count` is modified exactly in the environment where it was found.
241263

242264
![](lexenv-nested-makecounter-6.png)
243265

244-
When `counter()` finishes, its Lexical Environment is cleared from memory. There are no nested function or other reason to keep it. So we return to the point (4) with the only change -- the new value of `count`. The following calls all do the same.
245-
266+
When `counter()` finishes, its Lexical Environment is cleared from memory. There are no nested function or other reason to keep it. So we return to the previous step with the only change -- the new value of `count`. The following calls all do the same.
246267

247-
So when a function is called (no matter when, no matter where), it carries the reference to the outer Lexical Environment with it.
248268

249-
For instance, in the beginning of code above, we have only one global Lexical Environment.
250269

270+
```smart header="Closures"
271+
There is a general programming term "closure", that developers generally should know.
251272
273+
A [closure](https://en.wikipedia.org/wiki/Closure_(computer_programming)) is a function that remembers its outer variables and can access them. In some languages, that's not possible or need to be explicitly specified. But as explained above, in Javascript all functions are closures.
252274
275+
That is: all of them automatically remember where they are created using a hidden `[[Environment]]` property, and all of them can access outer variables.
253276
254-
A *closure* is a function that remembers its outer context and can access them.
277+
When on an interview a frontend developer gets a question about "what's a closure?", the valid answer would be a definition of the closure and an explanation that all functions in Javascript are closures, and maybe few more words about technical details: what are Lexical Environments and how they work.
278+
```
255279

256280

257281

315 Bytes
Loading
709 Bytes
Loading
261 Bytes
Loading
938 Bytes
Loading
11.1 KB
Loading
26.2 KB
Loading

figures.sketch

32 KB
Binary file not shown.

0 commit comments

Comments
 (0)