◐ Shell
reader mode source ↗
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
File filter
Conversations
Jump to
Diff view
Apply and reload
Show whitespace
Diff view
Apply and reload
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,9 +2,9 @@ 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?

```js
let name = "John";
Expand All @@ -15,9 +15,9 @@ function sayHi() {

name = "Pete";

sayHi(); // what will it show: "John" or "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.

So, the question is: does it pick up the latest changes?
58 changes: 29 additions & 29 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.

1. It creates an empty array `shooters`:

```js
let shooters = [];
```
2. Fills it with functions via `shooters.push(function)` in the loop.

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.

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.

Now why do all such functions show the same value, `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.

Then, what will be the value of `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
};
shooters.push(shooter); // add function to the array
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`).

As the result, all `shooter` functions get the same value from the outer lexical environment and that is, the last value, `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:

```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
};
shooters.push(shooter);
i++;
@@ -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.

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:

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

Such a problem could also be avoided if we used `for` in the beginning, like this:

```js run demo
function makeArmy() {
@@ -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
};
shooters.push(shooter);
}
@@ -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.

![](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?

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.

28 changes: 14 additions & 14 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`.

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
};
shooters.push(shooter); // and add it to the array
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.
*/!*
```

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
Expand Up @@ -2,11 +2,11 @@ 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.

Will it have access to the outer variables from its creation place, or the invocation place, or both?

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

let name = "John";

// create a function
let work = makeWorker();

// call it
work(); // what will it show?
```

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.**

Functions `counter` and `counter2` are created by different invocations of `makeCounter`.

So they have independent outer Lexical Environments, each one has its own `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.

Are they independent? What is the second counter going to show? `0,1` or `2,3` or something else?

```js
function makeCounter() {
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.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

# Function in if

Look at the code. What will be the result of the call at the last line?

```js run
let phrase = "Hello";
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
For the second parentheses to work, the first ones must return a function.

Like this:

```js run
function sum(a) {

return function(b) {
return a + b; // takes "a" from the outer lexical environment
};

}
Expand Down
8 changes: 4 additions & 4 deletions 1-js/06-advanced-functions/03-closure/6-closure-sum/task.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ importance: 4

---

# Sum with closures

Write function `sum` that works like this: `sum(a)(b) = a+b`.

Yes, exactly this way, using double parentheses (not a mistype).

For instance:

```js
sum(1)(2) = 3
Expand Down
Loading
Toggle all file notes Toggle all file annotations