You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: 1-js/5-deeper/2-closure/article.md
+40-16Lines changed: 40 additions & 16 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -196,62 +196,86 @@ The questions may arise:
196
196
197
197
We'll answer the 1st question and the other ones will also become obvious.
198
198
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.**
200
200
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
+

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.
202
216
203
217
Let's see how it works in the example above, step-by-step:
204
218
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:
206
220
207
221

208
222
209
223
At this moment there is only `makeCounter` function. And it did not run yet.
210
224
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:
212
228
213
229

214
230
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.
216
232
217
233
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.
218
234
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()`:
220
236
221
237

222
238
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.
224
240
225
241
So we still have two Lexical Environments.
226
242
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.
228
246
229
247

230
248
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...
232
250
233
251
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:
235
253
236
254

237
255
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.
239
261
240
262
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.
241
263
242
264

243
265
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.
246
267
247
-
So when a function is called (no matter when, no matter where), it carries the reference to the outer Lexical Environment with it.
248
268
249
-
For instance, in the beginning of code above, we have only one global Lexical Environment.
250
269
270
+
```smart header="Closures"
271
+
There is a general programming term "closure", that developers generally should know.
251
272
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.
252
274
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.
253
276
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.
0 commit comments