Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Variable scope, closure #219

Merged
merged 62 commits into from
Nov 15, 2021
Merged
Show file tree
Hide file tree
Changes from 61 commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
0cc7335
1-04-03: Garbage collection part 1
dolgachio Jul 15, 2021
9becadb
1-04-03: Garbage Collection part 2
dolgachio Sep 2, 2021
6b2daeb
1-04-03: Garbage Collection part 3
dolgachio Sep 6, 2021
9ad7406
Update 1-js/04-object-basics/03-garbage-collection/article.md
tarasyyyk Sep 6, 2021
9ce1a95
Update 1-js/04-object-basics/03-garbage-collection/article.md
tarasyyyk Sep 6, 2021
ba980b7
Merge branch 'master' of github.com:javascript-tutorial/uk.javascript…
dolgachio Sep 17, 2021
74ad282
1-js/06-advanced-functions/03-closure: translate p. 1
dolgachio Sep 20, 2021
076b1da
Merge branch 'master' of github.com:stas-dolgachov/uk.javascript.info
dolgachio Sep 20, 2021
911cecd
1-js/06-advanced-functions/03-closure: translate p. 2
dolgachio Sep 23, 2021
cfdfb59
1-js/06-advanced-functions/03-closure: translate p. 3
dolgachio Nov 10, 2021
1faad9d
1-js/06-advanced-functions/03-closure: transkate tasks
dolgachio Nov 14, 2021
1304815
Merge branch 'javascript-tutorial:master' into master
dolgachio Nov 14, 2021
784d79b
Update 1-js/06-advanced-functions/03-closure/1-closure-latest-changes…
dolgachio Nov 14, 2021
742c311
Update 1-js/06-advanced-functions/03-closure/1-closure-latest-changes…
dolgachio Nov 14, 2021
7ba32b0
Update 1-js/06-advanced-functions/03-closure/10-make-army/_js.view/so…
dolgachio Nov 14, 2021
3ba3722
Update 1-js/06-advanced-functions/03-closure/article.md
dolgachio Nov 14, 2021
4212bd9
Update 1-js/06-advanced-functions/03-closure/3-counter-independent/so…
dolgachio Nov 14, 2021
c43b4b5
Update 1-js/06-advanced-functions/03-closure/10-make-army/_js.view/so…
dolgachio Nov 14, 2021
a21be02
Update 1-js/06-advanced-functions/03-closure/10-make-army/_js.view/so…
dolgachio Nov 14, 2021
e2e86d0
Update 1-js/06-advanced-functions/03-closure/article.md
dolgachio Nov 14, 2021
dd02f54
Update 1-js/06-advanced-functions/03-closure/article.md
dolgachio Nov 14, 2021
bf35432
Update 1-js/06-advanced-functions/03-closure/10-make-army/solution.md
dolgachio Nov 14, 2021
c721a91
Update 1-js/06-advanced-functions/03-closure/10-make-army/solution.md
dolgachio Nov 14, 2021
b7a7d7c
Update 1-js/06-advanced-functions/03-closure/10-make-army/solution.md
dolgachio Nov 14, 2021
d0f3a1e
Update 1-js/06-advanced-functions/03-closure/10-make-army/solution.md
dolgachio Nov 14, 2021
7c7fbe6
Update 1-js/06-advanced-functions/03-closure/10-make-army/task.md
dolgachio Nov 14, 2021
a10f53c
Update 1-js/06-advanced-functions/03-closure/2-closure-variable-acces…
dolgachio Nov 14, 2021
bdfe39b
Update 1-js/06-advanced-functions/03-closure/3-counter-independent/ta…
dolgachio Nov 14, 2021
871c5ad
Update 1-js/06-advanced-functions/03-closure/4-counter-object-indepen…
dolgachio Nov 14, 2021
6d5980f
Update 1-js/06-advanced-functions/03-closure/6-closure-sum/task.md
dolgachio Nov 14, 2021
a8cbf1a
Update 1-js/06-advanced-functions/03-closure/7-let-scope/solution.md
dolgachio Nov 14, 2021
5a6aa8b
Update 1-js/06-advanced-functions/03-closure/7-let-scope/solution.md
dolgachio Nov 14, 2021
65611e5
Update 1-js/06-advanced-functions/03-closure/7-let-scope/solution.md
dolgachio Nov 14, 2021
1c82c1b
Update 1-js/06-advanced-functions/03-closure/7-let-scope/solution.md
dolgachio Nov 14, 2021
9c34751
Update 1-js/06-advanced-functions/03-closure/7-let-scope/task.md
dolgachio Nov 14, 2021
9959f67
Update 1-js/06-advanced-functions/03-closure/8-filter-through-functio…
dolgachio Nov 14, 2021
8b187b5
Update 1-js/06-advanced-functions/03-closure/9-sort-by-field/task.md
dolgachio Nov 14, 2021
46e1841
Update 1-js/06-advanced-functions/03-closure/8-filter-through-functio…
dolgachio Nov 14, 2021
1f60cdb
Update 1-js/06-advanced-functions/03-closure/9-sort-by-field/_js.view…
dolgachio Nov 14, 2021
404e7c4
Update 1-js/06-advanced-functions/03-closure/9-sort-by-field/_js.view…
dolgachio Nov 14, 2021
143e355
Update 1-js/06-advanced-functions/03-closure/9-sort-by-field/_js.view…
dolgachio Nov 14, 2021
4fee958
Update 1-js/06-advanced-functions/03-closure/9-sort-by-field/_js.view…
dolgachio Nov 14, 2021
bb2fb19
Update 1-js/06-advanced-functions/03-closure/9-sort-by-field/task.md
dolgachio Nov 14, 2021
79a696e
Update 1-js/06-advanced-functions/03-closure/9-sort-by-field/task.md
dolgachio Nov 14, 2021
6c08021
Update 1-js/06-advanced-functions/03-closure/9-sort-by-field/task.md
dolgachio Nov 14, 2021
9dd0815
Update 1-js/06-advanced-functions/03-closure/article.md
dolgachio Nov 14, 2021
2b538a3
Update 1-js/06-advanced-functions/03-closure/article.md
dolgachio Nov 14, 2021
21082c9
Update 1-js/06-advanced-functions/03-closure/article.md
dolgachio Nov 14, 2021
b7dca2c
Update 1-js/06-advanced-functions/03-closure/article.md
dolgachio Nov 14, 2021
33edfa4
Update 1-js/06-advanced-functions/03-closure/article.md
dolgachio Nov 14, 2021
75abb03
Update 1-js/06-advanced-functions/03-closure/article.md
dolgachio Nov 14, 2021
2ab537c
Update 1-js/06-advanced-functions/03-closure/article.md
dolgachio Nov 14, 2021
2f864c6
Update 1-js/06-advanced-functions/03-closure/article.md
dolgachio Nov 14, 2021
f73e73b
Update 1-js/06-advanced-functions/03-closure/article.md
dolgachio Nov 14, 2021
3a76506
Update 1-js/06-advanced-functions/03-closure/article.md
dolgachio Nov 14, 2021
6335aa3
Update 1-js/06-advanced-functions/03-closure/article.md
dolgachio Nov 14, 2021
27db556
Revert image translation
dolgachio Nov 14, 2021
75d356c
Update 1-js/06-advanced-functions/03-closure/2-closure-variable-acces…
tarasyyyk Nov 15, 2021
1453e09
Update 1-js/06-advanced-functions/03-closure/2-closure-variable-acces…
tarasyyyk Nov 15, 2021
8db01eb
Update task.md
tarasyyyk Nov 15, 2021
7096c73
Update 1-js/06-advanced-functions/03-closure/2-closure-variable-acces…
tarasyyyk Nov 15, 2021
5ab27d4
Update solution.md
tarasyyyk Nov 15, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
The answer is: **Pete**.
Відповідь: **Петро**.

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

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.
Старі значення змінних ніде не зберігаються. Коли функція потребує змінної, вона бере поточне значення зі свого власного або зовнішнього лексичного середовища.
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,22 @@ importance: 5

---

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

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

```js
let name = "John";
let name = "Іван";

function sayHi() {
alert("Hi, " + name);
alert("Привіт, " + name);
}

name = "Pete";
name = "Петро";

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

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.
Такі ситуації поширені як у браузері, так і в серверній розробці. Функцію можна запланувати на виконання пізніше, ніж вона створена, наприклад, після дії користувача або запиту мережі.

So, the question is: does it pick up the latest changes?
Отже, виникає питання: чи побачить функція останні зміни?
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ function makeArmy() {
let shooters = [];

for(let i = 0; i < 10; i++) {
let shooter = function() { // shooter function
alert( i ); // should show its number
let shooter = function() { // функція shooter
alert( i ); // має показати свій номер
};
shooters.push(shooter);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ function makeArmy() {

let i = 0;
while (i < 10) {
let shooter = function() { // shooter function
alert( i ); // should show its number
let shooter = function() { // функція shooter
alert( i ); // має показати свій номер
};
shooters.push(shooter);
i++;
Expand All @@ -16,7 +16,7 @@ function makeArmy() {
/*
let army = makeArmy();

army[0](); // the shooter number 0 shows 10
army[5](); // and number 5 also outputs 10...
// ... all shooters show 10 instead of their 0, 1, 2, 3...
army[0](); // стрілець під номером 0 показує 10
army[5](); // п’ятий стрілець показує 10...
// ... всі стрільці показують 10 замість своїх номерів 0, 1, 2, 3...
*/
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ describe("army", function() {
window.alert = sinon.stub(window, "alert");
});

it("army[0] shows 0", function() {
it("army[0] показує 0", function() {
army[0]();
assert(alert.calledWith(0));
});


it("army[5] shows 5", function() {
it("army[5] показує 5", function() {
army[5]();
assert(alert.calledWith(5));
});
Expand Down
56 changes: 28 additions & 28 deletions 1-js/06-advanced-functions/03-closure/10-make-army/solution.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@

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

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

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

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

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

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

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.
Потім, виклик будь-якого елемента масиву, наприклад `army[5]()` отримає елемент `army[5]` з масиву (який є функцією) і викликає її.

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

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.
Зверніть увагу, що всередині функцій `shooter` немає локальної змінної `i`. Коли така функція викликається, вона приймає `i` зі свого зовнішнього лексичного середовища.

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

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

```js
function makeArmy() {
...
let i = 0;
while (i < 10) {
let shooter = function() { // shooter function
alert( i ); // should show its number
let shooter = function() { // функція shooter
alert( i ); // має показати свій номер
};
shooters.push(shooter); // add function to the array
shooters.push(shooter); // додати функцію до масиву
i++;
}
...
}
```

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`).
Ми бачимо що усі функції `shooter` створені в лексичному середовищі функції `makeArmy()`. Але коли ми викликаємо `army[5]()`, функція `makeArmy` вже закінчила свою роботу, і остаточне значення `i` це `10` (цикл `while` зупиняється на `i=10`).

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

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

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:
Як ви можете бачити вище, на кожній ітерації циклу `while {...}`, створюється нове лексичне середовище. Отже, щоб виправити це, ми можемо скопіювати значення `i` у змінну всередині блоку `while {...}`, ось так:

```js run
function makeArmy() {
Expand All @@ -69,8 +69,8 @@ Let's examine what exactly happens inside `makeArmy`, and the solution will beco
*!*
let j = i;
*/!*
let shooter = function() { // shooter function
alert( *!*j*/!* ); // should show its number
let shooter = function() { // функція shooter
alert( *!*j*/!* ); // має показати свій номер
};
shooters.push(shooter);
i++;
Expand All @@ -81,18 +81,18 @@ Let's examine what exactly happens inside `makeArmy`, and the solution will beco

let army = makeArmy();

// Now the code works correctly
// Тепер код працює правильно
army[0](); // 0
army[5](); // 5
```

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.
Тут `let j = i` оголошує локальну змінну `j` та копіює до неї номер ітерації зі змінної `i`. Примітиви копіюються "за значенням", тому ми фактично отримуємо незалежну копію `i`, що належить до поточної ітерації циклу.

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:
Функції тепер працюють правильно, тому що змінна `i` "живе" трохи ближче. Не в лексичному середовищі виклику `makeArmy()`, але в лексичному середовищі, яке відповідає поточній ітерації циклу:

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

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

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

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.
Це, по суті, те саме, тому що `for` на кожній ітерації створює нове лексичне середовище зі своєю змінною `i`. Тому `shooter` згенерований на кожній ітерації бере посилання на змінну `i`, з тієї самої ітерації.

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

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?
Тепер, коли ви доклали так багато зусиль, щоб прочитати це, остаточний рецепт такий простий -- використовуйте цикл `for`, ви можете задатися питанням -- чи було воно того варте?

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.
Ну, якби ви могли легко відповісти на запитання, ви б не читали рішення. Тож, сподіваюся, це завдання допомогло вам трохи краще зрозуміти як все працює.

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

26 changes: 13 additions & 13 deletions 1-js/06-advanced-functions/03-closure/10-make-army/task.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,40 @@ importance: 5

---

# Army of functions
# Армія функцій

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

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

```js run
function makeArmy() {
let shooters = [];

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

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

let army = makeArmy();

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

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

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

Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
The answer is: **Pete**.
Відповідь: **Петро**.

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

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

So, the result is `"Pete"` here.
Отже, відповіддю буде `"Pete"`.

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"`.
Але якби не було `let name` у `makeWorker()`, тоді пошук вийшов би за межі лексичного середовища та взяв би глобальну змінну, як ми бачимо з ланцюжка вище. В такому випадку відповідь була б `"John"`.
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,28 @@ importance: 5

---

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

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

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

```js
function makeWorker() {
let name = "Pete";
let name = "Петро";

return function() {
alert(name);
};
}

let name = "John";
let name = "Іван";

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

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

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

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

So they have independent outer Lexical Environments, each one has its own `count`.
Отже, вони мають незалежні зовнішні лексичні середовища, кожне з яких має свою власну змінну `count`.
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ importance: 5

---

# Are counters independent?
# Чи лічильники незалежні?

Here we make two counters: `counter` and `counter2` using the same `makeCounter` function.
Тут ми створюємо два лічильника: `counter` та `counter2` використовуючи однакову функцію `makeCounter`.

Are they independent? What is the second counter going to show? `0,1` or `2,3` or something else?
Вони незалежні? Що покаже другий лічильник? `0,1` чи `2,3` чи щось інше?

```js
function makeCounter() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Surely it will work just fine.
Безумовно, він буде чудово працювати.

Both nested functions are created within the same outer Lexical Environment, so they share access to the same `count` variable:
Обидві вкладені функції створюються в межах єдиного зовнішнього лексичного середовища, тому вони мають спільний доступ до однієї змінної `count`:

```js run
function Counter() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ importance: 5

---

# Counter object
# Об’єкт лічильника

Here a counter object is made with the help of the constructor function.
Тут лічильник створюється за допомогою функції конструктора.

Will it work? What will it show?
Чи буде він працювати? Що він покаже?

```js
function Counter() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
The result is **an error**.
Результатом буде **помилка**.

The function `sayHi` is declared inside the `if`, so it only lives inside it. There is no `sayHi` outside.
Функція `sayHi` оголошується всередині `if`, тому вона доступна тільки всередині нього. Зовні функції `sayHi` не існує.
Loading