Skip to content

Variable scope, closure #180

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

Merged
merged 16 commits into from
Mar 12, 2023
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
The answer is: **Pete**.
Răspunsul este: **Pete**.

A function gets outer variables as they are now, it uses the most recent values.
O funcție primește variabilele exterioare așa cum sunt ele acum, folosește cele mai recente valori.

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.
Valorile vechi ale variabilelor nu sunt salvate nicăieri. Atunci când o funcție dorește o variabilă, aceasta ia valoarea curentă din propriul mediu lexical sau din cel extern.
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,22 @@ importance: 5

---

# Does a function pickup latest changes?
# Preia o funcție ultimele modificări?

The function sayHi uses an external variable name. When the function runs, which value is it going to use?
Funcția sayHi folosește un nume de variabilă externă. Când funcția se execută, ce valoare va folosi?

```js
let name = "John";

function sayHi() {
alert("Hi, " + name);
alert("Salut, " + name);
}

name = "Pete";

sayHi(); // what will it show: "John" or "Pete"?
sayHi(); // ce va afișa: "John" sau "Pete"?
```

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.
Astfel de situații sunt frecvente atât în browser cât și în dezvoltarea pe partea de server. O funcție poate fi programată pentru a fi executată mai târziu decât este creată, de exemplu după o acțiune a utilizatorului sau după o cerere de rețea.

So, the question is: does it pick up the latest changes?
Așadar, întrebarea este: preia cele mai recente modificări?
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() { // funcția shooter
alert( i ); // ar trebui să arate numărul său
};
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() { // funcția shooter
alert( i ); // ar trebui să arate numărul său
};
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](); // numărul shooter 0 arată 10
army[5](); // și numărul 5 de asemeni produce 10...
// ... toți shooters arată 10 în loc de 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] arată 0", function() {
army[0]();
assert(alert.calledWith(0));
});


it("army[5] shows 5", function() {
it("army[5] arată 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.
Să examinăm ce se întâmplă mai exact în `makeArmy`, iar soluția va deveni evidentă.

1. It creates an empty array `shooters`:
1. Creează o matrice goală `shooters`:

```js
let shooters = [];
```
2. Fills it with functions via `shooters.push(function)` in the loop.
2. O umple cu funcții prin `shooters.push(function)` în buclă.

Every element is a function, so the resulting array looks like this:
Fiecare element este o funcție, astfel încât matricea rezultată arată astfel:

```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. Matricea este returnată din funcție.

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.
Apoi, mai târziu, apelarea oricărui membru, e.g. `army[5]()` va obține elementul `army[5]` din matrice (care este o funcție) și îl apelează.

Now why do all such functions show the same value, `10`?
Acum de ce toate aceste funcții arată aceeași valoare, `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.
Asta pentru că nu există o variabilă locală `i` în interiorul funcțiilor `shooter`. Atunci când o astfel de funcție este apelată, aceasta preia `i` din mediul său lexical exterior.

Then, what will be the value of `i`?
Atunci, care va fi valoarea lui `i`?

If we look at the source:
Dacă ne uităm la sursă:

```js
function makeArmy() {
...
let i = 0;
while (i < 10) {
let shooter = function() { // shooter function
alert( i ); // should show its number
let shooter = function() { // funcția shooter
alert( i ); // ar trebui să arate numărul său
};
shooters.push(shooter); // add function to the array
shooters.push(shooter); // adaugă funcția la matrice
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`).
Putem vedea că toate funcțiile `shooter` sunt create în mediul lexical al funcției `makeArmy()`. Dar când este apelată `army[5]()`, `makeArmy` și-a terminat deja treaba, iar valoarea finală a lui `i` este `10` (`while` se oprește la `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`.
Ca și rezultat, toate funcțiile `shooter` obțin aceeași valoare din mediul lexical extern și anume, ultima valoare, `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:
După cum puteți vedea mai sus, la fiecare iterație a unui bloc `while {...}`, un nou mediu lexical este creat. Așadar, pentru a remedia acest lucru, putem copia valoarea lui `i` într-o variabilă în cadrul blocului `while {...}`, astfel:

```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() { // funcția shooter
alert( *!*j*/!* ); // ar trebui să arate numărul său
};
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
// Acum codul funcționează corect
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.
Aici `let j = i` declară o variabilă `j` "de iterație locală" și copiază `i` în ea. Primitivele sunt copiate "după valoare", astfel încât obținem de fapt o copie independentă a lui `i`, aparținând iterației curente a buclei.

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:
Shooters funcționează corect, deoarece valoarea lui `i` trăiește acum un pic mai aproape. Nu în mediul lexical `makeArmy()`, ci în Mediul Lexical care corespunde iterației buclei curente:

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

Such a problem could also be avoided if we used `for` in the beginning, like this:
O astfel de problemă ar putea fi evitată și dacă am folosi `for` la început, astfel:

```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() { // funcția shooter
alert( i ); // ar trebui să arate numărul său
};
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.
În esență, este același lucru, deoarece `for` generează la fiecare iterație un nou mediu lexical, cu propria sa variabilă `i`. Astfel, `shooter` generat în fiecare iterație face referire la propriul `i`, chiar din acea iterație.

![](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?
Acum, având în vedere că ați depus atât de mult efort pentru a citi acest lucru, iar rețeta finală este atât de simplă - folosiți doar `for`, vă puteți întreba -- s-a meritat?

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.
Ei bine, dacă ați putea răspunde cu ușurință la această întrebare, nu ați citi soluția. Așadar, sperăm că această sarcină să vă fi ajutat să înțelegeți un pic mai bine lucrurile.

Besides, there are indeed cases when one prefers `while` to `for`, and other scenarios, where such problems are real.
În rest, există într-adevăr cazuri în care se preferă `while` în locul lui `for`, precum și alte scenarii, în care astfel de probleme sunt reale.S

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
# Armata de funcții

The following code creates an array of `shooters`.
Următorul cod creează o matrice de `shooters`.

Every function is meant to output its number. But something is wrong...
Fiecare funcție este menită să emită numărul său. Dar ceva nu este în regulă...

```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() { // crează o funcție shooter,
alert( i ); // care ar trebui să arate numărul său
};
shooters.push(shooter); // and add it to the array
shooters.push(shooter); // și adăugați-l la matrice
i++;
}

// ...and return the array of shooters
// ...și returnează matricea de 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.
// toți shooters arată 10 în loc de numerele lor 0, 1, 2, 3...
army[0](); // 10 de la shooter cu numărul 0
army[1](); // 10 de la shooter cu numărul 1
army[2](); // 10 ...și așa mai departe.
*/!*
```

Why do all of the shooters show the same value?
De ce toți shooters arată aceeași valoare?

Fix the code so that they work as intended.
Remediați codul astfel încât acestea să funcționeze așa cum a fost intenționat.

Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
The answer is: **Pete**.
Răspunsul este: **Pete**.

The `work()` function in the code below gets `name` from the place of its origin through the outer lexical environment reference:
Funcția `work()` din codul de mai jos obține `name` din locul de origine prin intermediul referinței mediului lexical extern:

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

So, the result is `"Pete"` here.
Așadar, rezultatul este `"Pete"` aici.

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"`.
Dar dacă nu ar exista `let name` în `makeWorker()`, atunci căutarea ar merge în exterior și ar lua variabila globală, așa cum putem vedea din lanțul de mai sus. În acest caz, rezultatul ar fi `"John"`.
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ importance: 5

---

# Which variables are available?
# Ce variabile sunt disponibile?

The function `makeWorker` below makes another function and returns it. That new function can be called from somewhere else.
Funcția `makeWorker` de mai jos creează o altă funcție și o returnează. Această nouă funcție poate fi apelată din altă parte.

Will it have access to the outer variables from its creation place, or the invocation place, or both?
Va avea acces la variabilele exterioare de la locul de creare, de la locul de invocare, sau ambele?

```js
function makeWorker() {
Expand All @@ -19,11 +19,11 @@ function makeWorker() {

let name = "John";

// create a function
// creați o funcție
let work = makeWorker();

// call it
work(); // what will it show?
// apelați-o
work(); // ce va arăta?
```

Which value it will show? "Pete" or "John"?
Ce valoare va arăta? "Pete" sau "John"?
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
The answer: **0,1.**
Răspunsul: **0,1.**

Functions `counter` and `counter2` are created by different invocations of `makeCounter`.
Funcțiile `counter` și `counter2` sunt create prin invocări diferite ale lui `makeCounter`.

So they have independent outer Lexical Environments, each one has its own `count`.
Deci ele au Medii Lexicale exterioare independente, fiecare având propriul `count`.
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ importance: 5

---

# Are counters independent?
# Sunt contoarele independente?

Here we make two counters: `counter` and `counter2` using the same `makeCounter` function.
Aici facem două contoare: `counter` și `counter2` folosind aceeași funcție `makeCounter`.

Are they independent? What is the second counter going to show? `0,1` or `2,3` or something else?
Sunt ele independente? Ce va arăta cel de-al doilea contor? `0,1` sau `2,3` sau altceva?

```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.
Cu siguranță că va funcționa foarte bine.

Both nested functions are created within the same outer Lexical Environment, so they share access to the same `count` variable:
Ambele funcții imbricate sunt create în cadrul aceluiași Mediu Lexical exterior, astfel încât au acces comun la aceeași variabilă `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
# Obiect contor

Here a counter object is made with the help of the constructor function.
Aici se creează un obiect contor cu ajutorul funcției constructor.

Will it work? What will it show?
Va funcționa? Ce va arăta?

```js
function Counter() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
The result is **an error**.
Rezultatul este **o eroare**.

The function `sayHi` is declared inside the `if`, so it only lives inside it. There is no `sayHi` outside.
Funcția `sayHi` este declarată în interiorul lui `if`, deci trăiește doar în interiorul acestuia. Nu există `sayHi` în exterior.
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
importance: 5

---
# Function in if
# Funcție în if

Look at the code. What will be the result of the call at the last line?
Priviți codul. Care va fi rezultatul apelului de la ultima linie?

```js run
let phrase = "Hello";
let phrase = "Bună ziua";

if (true) {
let user = "John";
Expand Down
Loading