Merge pull request #2081 from MuhammedZakir/master
Restructure the Solution for 'Army of Functions' task and Fix Typos
This commit is contained in:
commit
0168147c81
8 changed files with 136 additions and 49 deletions
|
@ -9,7 +9,8 @@ let b = "2"; // prompt("Second number?", 2);
|
||||||
alert(a + b); // 12
|
alert(a + b); // 12
|
||||||
```
|
```
|
||||||
|
|
||||||
What we should to is to convert strings to numbers before `+`. For example, using `Number()` or prepending them with `+`.
|
What we should do is to convert strings to numbers before `+`. For example, using `Number()` or
|
||||||
|
prepending them with `+`.
|
||||||
|
|
||||||
For example, right before `prompt`:
|
For example, right before `prompt`:
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,6 @@ do {
|
||||||
The loop `do..while` repeats while both checks are truthy:
|
The loop `do..while` repeats while both checks are truthy:
|
||||||
|
|
||||||
1. The check for `num <= 100` -- that is, the entered value is still not greater than `100`.
|
1. The check for `num <= 100` -- that is, the entered value is still not greater than `100`.
|
||||||
2. The check `&& num` is false when `num` is `null` or a empty string. Then the `while` loop stops too.
|
2. The check `&& num` is false when `num` is `null` or an empty string. Then the `while` loop stops too.
|
||||||
|
|
||||||
P.S. If `num` is `null` then `num <= 100` is `true`, so without the 2nd check the loop wouldn't stop if the user clicks CANCEL. Both checks are required.
|
P.S. If `num` is `null` then `num <= 100` is `true`, so without the 2nd check the loop wouldn't stop if the user clicks CANCEL. Both checks are required.
|
||||||
|
|
|
@ -7,7 +7,7 @@ function makeUser() {
|
||||||
name: "John",
|
name: "John",
|
||||||
ref: this
|
ref: this
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
let user = makeUser();
|
let user = makeUser();
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ function makeUser() {
|
||||||
}
|
}
|
||||||
*/!*
|
*/!*
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
let user = makeUser();
|
let user = makeUser();
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ function makeUser() {
|
||||||
name: "John",
|
name: "John",
|
||||||
ref: this
|
ref: this
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
let user = makeUser();
|
let user = makeUser();
|
||||||
|
|
||||||
|
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |
|
@ -29,9 +29,9 @@ Let's examine what's done inside `makeArmy`, and the solution will become obviou
|
||||||
|
|
||||||
Then, later, the call to `army[5]()` will get the element `army[5]` from the array (it will be a function) and call it.
|
Then, later, the call to `army[5]()` will get the element `army[5]` from the array (it will be a function) and call it.
|
||||||
|
|
||||||
Now why all such functions show the same?
|
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.
|
That's because there's no local variable `i` inside `shooter` functions and neither in the code block of `while {...}`. When such a function is called, it takes `i` from its outer lexical environment.
|
||||||
|
|
||||||
What will be the value of `i`?
|
What will be the value of `i`?
|
||||||
|
|
||||||
|
@ -51,11 +51,43 @@ function makeArmy() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
...We can see that it lives in the lexical environment associated with the current `makeArmy()` run. But when `army[5]()` is called, `makeArmy` has already finished its job, and `i` has the last value: `10` (the end of `while`).
|
We can see that it lives in the lexical environment associated with the current `makeArmy()` run. But when `army[5]()` is called, `makeArmy` has already finished its job, and the final value of `i` is `10` (at the end of `while`).
|
||||||
|
|
||||||
As a result, all `shooter` functions get from the outer lexical envrironment the same, last value `i=10`.
|
As a result, all `shooter` functions get the same value from the outer lexical environment and that is, the last value, `i=10`.
|
||||||
|
|
||||||
We can fix it by moving the variable definition into the loop:
|

|
||||||
|
|
||||||
|
As you can see above, on each iteration of a `while {...} ` block, a new lexical environment is created. This implies that as long as we store the value of `i` in a variable in the `while {...}` block, created Lexical Environment will have that variable with value of `i`.
|
||||||
|
|
||||||
|
|
||||||
|
```js run
|
||||||
|
function makeArmy() {
|
||||||
|
let shooters = [];
|
||||||
|
|
||||||
|
let i = 0;
|
||||||
|
while (i < 10) {
|
||||||
|
*!*
|
||||||
|
let j = i;
|
||||||
|
*/!*
|
||||||
|
let shooter = function() { // shooter function
|
||||||
|
alert( *!*j*/!* ); // should show its number
|
||||||
|
};
|
||||||
|
shooters.push(shooter);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return shooters;
|
||||||
|
}
|
||||||
|
|
||||||
|
let army = makeArmy();
|
||||||
|
|
||||||
|
army[0](); // 0
|
||||||
|
army[5](); // 5
|
||||||
|
```
|
||||||
|
|
||||||
|
Here `let j = i` makes a loop body local `j` and copies the value of `i` to it. Primitives are copied "by value", so we actually get a complete independent copy of `i`, belonging to the current loop iteration.
|
||||||
|
|
||||||
|
In `for` loop, this is written as:
|
||||||
|
|
||||||
```js run demo
|
```js run demo
|
||||||
function makeArmy() {
|
function makeArmy() {
|
||||||
|
@ -80,41 +112,8 @@ army[0](); // 0
|
||||||
army[5](); // 5
|
army[5](); // 5
|
||||||
```
|
```
|
||||||
|
|
||||||
Now it works correctly, because every time the code block in `for (let i=0...) {...}` is executed, a new Lexical Environment is created for it, with the corresponding variable `i`.
|
In this case, on each iteration, a new lexical environment is created for it, with variable i and its current value.
|
||||||
|
|
||||||
So, the value of `i` now lives a little bit closer. Not in `makeArmy()` Lexical Environment, but in the Lexical Environment that corresponds the current loop iteration. That's why now it works.
|
So, the value of `i` now lives a little bit closer. Not in `makeArmy()` Lexical Environment, but in the Lexical Environment that corresponds the current loop iteration.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Here we rewrote `while` into `for`.
|
|
||||||
|
|
||||||
Another trick could be possible, let's see it for better understanding of the subject:
|
|
||||||
|
|
||||||
```js run
|
|
||||||
function makeArmy() {
|
|
||||||
let shooters = [];
|
|
||||||
|
|
||||||
let i = 0;
|
|
||||||
while (i < 10) {
|
|
||||||
*!*
|
|
||||||
let j = i;
|
|
||||||
*/!*
|
|
||||||
let shooter = function() { // shooter function
|
|
||||||
alert( *!*j*/!* ); // should show its number
|
|
||||||
};
|
|
||||||
shooters.push(shooter);
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return shooters;
|
|
||||||
}
|
|
||||||
|
|
||||||
let army = makeArmy();
|
|
||||||
|
|
||||||
army[0](); // 0
|
|
||||||
army[5](); // 5
|
|
||||||
```
|
|
||||||
|
|
||||||
The `while` loop, just like `for`, makes a new Lexical Environment for each run. So here we make sure that it gets the right value for a `shooter`.
|
|
||||||
|
|
||||||
We copy `let j = i`. This makes a loop body local `j` and copies the value of `i` to it. Primitives are copied "by value", so we actually get a complete independent copy of `i`, belonging to the current loop iteration.
|
|
||||||
|
|
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 61 KiB |
|
@ -314,7 +314,7 @@ When on an interview, a frontend developer gets a question about "what's a closu
|
||||||
|
|
||||||
Usually, a Lexical Environment is removed from memory with all the variables after the function call finishes. That's because there are no references to it. As any JavaScript object, it's only kept in memory while it's reachable.
|
Usually, a Lexical Environment is removed from memory with all the variables after the function call finishes. That's because there are no references to it. As any JavaScript object, it's only kept in memory while it's reachable.
|
||||||
|
|
||||||
...But if there's a nested function that is still reachable after the end of a function, then it has `[[Environment]]` property that references the lexical environment.
|
However, if there's a nested function that is still reachable after the end of a function, then it has `[[Environment]]` property that references the lexical environment.
|
||||||
|
|
||||||
In that case the Lexical Environment is still reachable even after the completion of the function, so it stays alive.
|
In that case the Lexical Environment is still reachable even after the completion of the function, so it stays alive.
|
||||||
|
|
||||||
|
@ -333,7 +333,7 @@ let g = f(); // g.[[Environment]] stores a reference to the Lexical Environment
|
||||||
// of the corresponding f() call
|
// of the corresponding f() call
|
||||||
```
|
```
|
||||||
|
|
||||||
Please note that if `f()` is called many times, and resulting functions are saved, then all corresponding Lexical Environment objects will also be retained in memory. All 3 of them in the code below:
|
Please note that if `f()` is called many times, and resulting functions are saved, then all corresponding Lexical Environment objects will also be retained in memory. In the code below, all 3 of them:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
function f() {
|
function f() {
|
||||||
|
@ -415,4 +415,4 @@ g();
|
||||||
|
|
||||||
This feature of V8 is good to know. If you are debugging with Chrome/Opera, sooner or later you will meet it.
|
This feature of V8 is good to know. If you are debugging with Chrome/Opera, sooner or later you will meet it.
|
||||||
|
|
||||||
That is not a bug in the debugger, but rather a special feature of V8. Perhaps it will be changed sometime. You always can check for it by running the examples on this page.
|
That is not a bug in the debugger, but rather a special feature of V8. Perhaps it will be changed sometime. You can always check for it by running the examples on this page.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue