Skip to content

Commit 02350db

Browse files
authored
Variable scope, closure (#219)
1 parent 7a139f3 commit 02350db

File tree

31 files changed

+303
-304
lines changed

31 files changed

+303
-304
lines changed
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
The answer is: **Pete**.
1+
Відповідь: **Петро**.
22

3-
A function gets outer variables as they are now, it uses the most recent values.
3+
Функція отримує зовнішні змінні такими, якими вони є зараз, тобто вона використовує останні значення.
44

5-
Old variable values are not saved anywhere. When a function wants a variable, it takes the current value from its own Lexical Environment or the outer one.
5+
Старі значення змінних ніде не зберігаються. Коли функція потребує змінної, вона бере поточне значення зі свого власного або зовнішнього лексичного середовища.

1-js/06-advanced-functions/03-closure/1-closure-latest-changes/task.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,22 @@ importance: 5
22

33
---
44

5-
# Does a function pickup latest changes?
5+
# Чи побачить функція останні зміни?
66

7-
The function sayHi uses an external variable name. When the function runs, which value is it going to use?
7+
Функція `sayHi` використовує зовнішню змінну. Яке значення буде використано під час виконання функції?
88

99
```js
10-
let name = "John";
10+
let name = "Іван";
1111

1212
function sayHi() {
13-
alert("Hi, " + name);
13+
alert("Привіт, " + name);
1414
}
1515

16-
name = "Pete";
16+
name = "Петро";
1717

18-
sayHi(); // what will it show: "John" or "Pete"?
18+
sayHi(); // що вона покаже "Іван" чи "Петро"?
1919
```
2020

21-
Such situations are common both in browser and server-side development. A function may be scheduled to execute later than it is created, for instance after a user action or a network request.
21+
Такі ситуації поширені як у браузері, так і в серверній розробці. Функцію можна запланувати на виконання пізніше, ніж вона створена, наприклад, після дії користувача або запиту мережі.
2222

23-
So, the question is: does it pick up the latest changes?
23+
Отже, виникає питання: чи побачить функція останні зміни?

1-js/06-advanced-functions/03-closure/10-make-army/_js.view/solution.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ function makeArmy() {
33
let shooters = [];
44

55
for(let i = 0; i < 10; i++) {
6-
let shooter = function() { // shooter function
7-
alert( i ); // should show its number
6+
let shooter = function() { // функція shooter
7+
alert( i ); // має показати свій номер
88
};
99
shooters.push(shooter);
1010
}

1-js/06-advanced-functions/03-closure/10-make-army/_js.view/source.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ function makeArmy() {
33

44
let i = 0;
55
while (i < 10) {
6-
let shooter = function() { // shooter function
7-
alert( i ); // should show its number
6+
let shooter = function() { // функція shooter
7+
alert( i ); // має показати свій номер
88
};
99
shooters.push(shooter);
1010
i++;
@@ -16,7 +16,7 @@ function makeArmy() {
1616
/*
1717
let army = makeArmy();
1818
19-
army[0](); // the shooter number 0 shows 10
20-
army[5](); // and number 5 also outputs 10...
21-
// ... all shooters show 10 instead of their 0, 1, 2, 3...
19+
army[0](); // стрілець під номером 0 показує 10
20+
army[5](); // п’ятий стрілець показує 10...
21+
// ... всі стрільці показують 10 замість своїх номерів 0, 1, 2, 3...
2222
*/

1-js/06-advanced-functions/03-closure/10-make-army/_js.view/test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@ describe("army", function() {
77
window.alert = sinon.stub(window, "alert");
88
});
99

10-
it("army[0] shows 0", function() {
10+
it("army[0] показує 0", function() {
1111
army[0]();
1212
assert(alert.calledWith(0));
1313
});
1414

1515

16-
it("army[5] shows 5", function() {
16+
it("army[5] показує 5", function() {
1717
army[5]();
1818
assert(alert.calledWith(5));
1919
});
Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11

2-
Let's examine what exactly happens inside `makeArmy`, and the solution will become obvious.
2+
Давайте розберемося, що саме відбувається всередині функції `makeArmy`, і рішення стане очевидним.
33

4-
1. It creates an empty array `shooters`:
4+
1. Функція створює порожній масив `shooters`:
55

66
```js
77
let shooters = [];
88
```
9-
2. Fills it with functions via `shooters.push(function)` in the loop.
9+
2. Наповнює його функціями у циклі через `shooters.push(function)`.
1010

11-
Every element is a function, so the resulting array looks like this:
11+
Кожен елемент є функцією, тому отриманий масив виглядає так:
1212

1313
```js no-beautify
1414
shooters = [
@@ -25,40 +25,40 @@ Let's examine what exactly happens inside `makeArmy`, and the solution will beco
2525
];
2626
```
2727

28-
3. The array is returned from the function.
28+
3. Функція повертає масив.
2929

30-
Then, later, the call to any member, e.g. `army[5]()` will get the element `army[5]` from the array (which is a function) and calls it.
30+
Потім, виклик будь-якого елемента масиву, наприклад `army[5]()` отримає елемент `army[5]` з масиву (який є функцією) і викликає її.
3131

32-
Now why do all such functions show the same value, `10`?
32+
Чому всі функції показують однакове значення, `10`?
3333

34-
That's because there's no local variable `i` inside `shooter` functions. When such a function is called, it takes `i` from its outer lexical environment.
34+
Зверніть увагу, що всередині функцій `shooter` немає локальної змінної `i`. Коли така функція викликається, вона приймає `i` зі свого зовнішнього лексичного середовища.
3535

36-
Then, what will be the value of `i`?
36+
Тоді яке буде значення `i`?
3737

38-
If we look at the source:
38+
Якщо ми подивимося на код:
3939

4040
```js
4141
function makeArmy() {
4242
...
4343
let i = 0;
4444
while (i < 10) {
45-
let shooter = function() { // shooter function
46-
alert( i ); // should show its number
45+
let shooter = function() { // функція shooter
46+
alert( i ); // має показати свій номер
4747
};
48-
shooters.push(shooter); // add function to the array
48+
shooters.push(shooter); // додати функцію до масиву
4949
i++;
5050
}
5151
...
5252
}
5353
```
5454

55-
We can see that all `shooter` functions are created in the lexical environment of `makeArmy()` function. But when `army[5]()` is called, `makeArmy` has already finished its job, and the final value of `i` is `10` (`while` stops at `i=10`).
55+
Ми бачимо що усі функції `shooter` створені в лексичному середовищі функції `makeArmy()`. Але коли ми викликаємо `army[5]()`, функція `makeArmy` вже закінчила свою роботу, і остаточне значення `i` це `10` (цикл `while` зупиняється на `i=10`).
5656

57-
As the result, all `shooter` functions get the same value from the outer lexical environment and that is, the last value, `i=10`.
57+
В результаті всі функції `shooter` отримують однакове значення із зовнішнього лексичного середовища, тобто останнє значення, `i=10`.
5858

5959
![](lexenv-makearmy-empty.svg)
6060

61-
As you can see above, on each iteration of a `while {...}` block, a new lexical environment is created. So, to fix this, we can copy the value of `i` into a variable within the `while {...}` block, like this:
61+
Як ви можете бачити вище, на кожній ітерації циклу `while {...}`, створюється нове лексичне середовище. Отже, щоб виправити це, ми можемо скопіювати значення `i` у змінну всередині блоку `while {...}`, ось так:
6262

6363
```js run
6464
function makeArmy() {
@@ -69,8 +69,8 @@ Let's examine what exactly happens inside `makeArmy`, and the solution will beco
6969
*!*
7070
let j = i;
7171
*/!*
72-
let shooter = function() { // shooter function
73-
alert( *!*j*/!* ); // should show its number
72+
let shooter = function() { // функція shooter
73+
alert( *!*j*/!* ); // має показати свій номер
7474
};
7575
shooters.push(shooter);
7676
i++;
@@ -81,18 +81,18 @@ Let's examine what exactly happens inside `makeArmy`, and the solution will beco
8181
8282
let army = makeArmy();
8383
84-
// Now the code works correctly
84+
// Тепер код працює правильно
8585
army[0](); // 0
8686
army[5](); // 5
8787
```
8888

89-
Here `let j = i` declares an "iteration-local" variable `j` and copies `i` into it. Primitives are copied "by value", so we actually get an independent copy of `i`, belonging to the current loop iteration.
89+
Тут `let j = i` оголошує локальну змінну `j` та копіює до неї номер ітерації зі змінної `i`. Примітиви копіюються "за значенням", тому ми фактично отримуємо незалежну копію `i`, що належить до поточної ітерації циклу.
9090

91-
The shooters work correctly, because the value of `i` now lives a little bit closer. Not in `makeArmy()` Lexical Environment, but in the Lexical Environment that corresponds to the current loop iteration:
91+
Функції тепер працюють правильно, тому що змінна `i` "живе" трохи ближче. Не в лексичному середовищі виклику `makeArmy()`, але в лексичному середовищі, яке відповідає поточній ітерації циклу:
9292

9393
![](lexenv-makearmy-while-fixed.svg)
9494

95-
Such a problem could also be avoided if we used `for` in the beginning, like this:
95+
Такої проблеми також можна було б уникнути, якби ми використали цикл `for` з самого початку, ось так:
9696

9797
```js run demo
9898
function makeArmy() {
@@ -102,8 +102,8 @@ Let's examine what exactly happens inside `makeArmy`, and the solution will beco
102102
*!*
103103
for(let i = 0; i < 10; i++) {
104104
*/!*
105-
let shooter = function() { // shooter function
106-
alert( i ); // should show its number
105+
let shooter = function() { // функція shooter
106+
alert( i ); // має показати свій номер
107107
};
108108
shooters.push(shooter);
109109
}
@@ -117,13 +117,13 @@ Let's examine what exactly happens inside `makeArmy`, and the solution will beco
117117
army[5](); // 5
118118
```
119119

120-
That's essentially the same, because `for` on each iteration generates a new lexical environment, with its own variable `i`. So `shooter` generated in every iteration references its own `i`, from that very iteration.
120+
Це, по суті, те саме, тому що `for` на кожній ітерації створює нове лексичне середовище зі своєю змінною `i`. Тому `shooter` згенерований на кожній ітерації бере посилання на змінну `i`, з тієї самої ітерації.
121121

122122
![](lexenv-makearmy-for-fixed.svg)
123123

124-
Now, as you've put so much effort into reading this, and the final recipe is so simple - just use `for`, you may wonder -- was it worth that?
124+
Тепер, коли ви доклали так багато зусиль, щоб прочитати це, остаточний рецепт такий простий -- використовуйте цикл `for`, ви можете задатися питанням -- чи було воно того варте?
125125

126-
Well, if you could easily answer the question, you wouldn't read the solution. So, hopefully this task must have helped you to understand things a bit better.
126+
Ну, якби ви могли легко відповісти на запитання, ви б не читали рішення. Тож, сподіваюся, це завдання допомогло вам трохи краще зрозуміти як все працює.
127127

128-
Besides, there are indeed cases when one prefers `while` to `for`, and other scenarios, where such problems are real.
128+
Крім того, на практиці бувають випадки, коли віддають перевагу `while` замість `for`, та інші сценарії, де такі проблеми є реальними.
129129

1-js/06-advanced-functions/03-closure/10-make-army/task.md

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,40 +2,40 @@ importance: 5
22

33
---
44

5-
# Army of functions
5+
# Армія функцій
66

7-
The following code creates an array of `shooters`.
7+
Наступний код створює масив `shooters`.
88

9-
Every function is meant to output its number. But something is wrong...
9+
Кожна функція має вивести свій номер. Але щось не так...
1010

1111
```js run
1212
function makeArmy() {
1313
let shooters = [];
1414

1515
let i = 0;
1616
while (i < 10) {
17-
let shooter = function() { // create a shooter function,
18-
alert( i ); // that should show its number
17+
let shooter = function() { // створюємо функцію стрільця,
18+
alert( i ); // що має показувати свій номер
1919
};
20-
shooters.push(shooter); // and add it to the array
20+
shooters.push(shooter); // додаємо її до масиву
2121
i++;
2222
}
2323

24-
// ...and return the array of shooters
24+
// ...і повертаємо масив стрільців
2525
return shooters;
2626
}
2727

2828
let army = makeArmy();
2929

3030
*!*
31-
// all shooters show 10 instead of their numbers 0, 1, 2, 3...
32-
army[0](); // 10 from the shooter number 0
33-
army[1](); // 10 from the shooter number 1
34-
army[2](); // 10 ...and so on.
31+
// всі стрільці показують 10 замість своїх номерів 0, 1, 2, 3...
32+
army[0](); // 10 від стрільця за номером 0
33+
army[1](); // 10 від стрільця за номером 1
34+
army[2](); // 10 ...і так далі.
3535
*/!*
3636
```
3737

38-
Why do all of the shooters show the same value?
38+
Чому всі функції показують однакове значення?
3939

40-
Fix the code so that they work as intended.
40+
Виправте код так, щоб він працював як передбачалося.
4141

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
The answer is: **Pete**.
1+
Відповідь: **Петро**.
22

3-
The `work()` function in the code below gets `name` from the place of its origin through the outer lexical environment reference:
3+
Функція `work()` в коді нижче отримує `name` від місця його походження через посилання на зовнішнє лексичне середовище:
44

55
![](lexenv-nested-work.svg)
66

7-
So, the result is `"Pete"` here.
7+
Отже, відповіддю буде `"Петро"`.
88

9-
But if there were no `let name` in `makeWorker()`, then the search would go outside and take the global variable as we can see from the chain above. In that case the result would be `"John"`.
9+
Але якби не було `let name` у `makeWorker()`, тоді пошук вийшов би за межі лексичного середовища та взяв би глобальну змінну, як ми бачимо з ланцюжка вище. В такому випадку відповідь була б `"Іван"`.

1-js/06-advanced-functions/03-closure/2-closure-variable-access/task.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,28 @@ importance: 5
22

33
---
44

5-
# Which variables are available?
5+
# Які змінні доступні?
66

7-
The function `makeWorker` below makes another function and returns it. That new function can be called from somewhere else.
7+
Функція `makeWorker` створює іншу функцію і повертає її. Цю нову функцію можна викликати ще звідкись.
88

9-
Will it have access to the outer variables from its creation place, or the invocation place, or both?
9+
Чи матиме вона доступ до зовнішніх змінних з місця створення, з місця виклику, чи з обох?
1010

1111
```js
1212
function makeWorker() {
13-
let name = "Pete";
13+
let name = "Петро";
1414

1515
return function() {
1616
alert(name);
1717
};
1818
}
1919

20-
let name = "John";
20+
let name = "Іван";
2121

22-
// create a function
22+
// створити функцію
2323
let work = makeWorker();
2424

25-
// call it
26-
work(); // what will it show?
25+
// викликати її
26+
work(); // Що вона покаже?
2727
```
2828

29-
Which value it will show? "Pete" or "John"?
29+
Яке значення вона покаже? "Петро" чи "Іван"?
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
The answer: **0,1.**
1+
Відповідь: **0,1.**
22

3-
Functions `counter` and `counter2` are created by different invocations of `makeCounter`.
3+
Функції `counter` і `counter2` створюються різними викликами `makeCounter`.
44

5-
So they have independent outer Lexical Environments, each one has its own `count`.
5+
Отже, вони мають незалежні зовнішні лексичні середовища, кожне з яких має свою власну змінну `count`.

0 commit comments

Comments
 (0)