This commit is contained in:
Ilya Kantor 2017-03-21 14:41:49 +03:00
parent 4ae129054e
commit ab9ab64bd5
476 changed files with 3370 additions and 532 deletions

View file

@ -0,0 +1,40 @@
The solution using a loop:
```js run
function sumTo(n) {
let sum = 0;
for (let i = 1; i <= n; i++) {
sum += i;
}
return sum;
}
alert( sumTo(100) );
```
The solution using recursion:
```js run
function sumTo(n) {
if (n == 1) return 1;
return n + sumTo(n - 1);
}
alert( sumTo(100) );
```
The solution using the formula: `sumTo(n) = n*(n+1)/2`:
```js run
function sumTo(n) {
return n * (n + 1) / 2;
}
alert( sumTo(100) );
```
P.S. Naturally, the formula is the fastest solution. It uses only 3 operations for any number `n`. The math helps!
The loop variant is the second in terms of speed. In both the recursive and the loop variant we sum the same numbers. But the recursion involves nested calls and execution stack management. That also takes resources, so it's slower.
P.P.S. The standard describes a "tail call" optimization: if the recursive call is the very last one in the function (like in `sumTo` above), then the outer function will not need to resume the execution and we don't need to remember its execution context. In that case `sumTo(100000)` is countable. But if your JavaScript engine does not support it, there will be an error: maximum stack size exceeded, because there's usually a limitation on the total stack size.

View file

@ -0,0 +1,36 @@
importance: 5
---
# Sum all numbers till the given one
Write a function `sumTo(n)` that calculates the sum of numbers `1 + 2 + ... + n`.
For instance:
```js no-beautify
sumTo(1) = 1
sumTo(2) = 2 + 1 = 3
sumTo(3) = 3 + 2 + 1 = 6
sumTo(4) = 4 + 3 + 2 + 1 = 10
...
sumTo(100) = 100 + 99 + ... + 2 + 1 = 5050
```
Make 3 solution variants:
1. Using a for loop.
2. Using a recursion, cause `sumTo(n) = n + sumTo(n-1)` for `n > 1`.
3. Using the [arithmetic progression](https://en.wikipedia.org/wiki/Arithmetic_progression) formula.
An example of the result:
```js
function sumTo(n) { /*... your code ... */ }
alert( sumTo(100) ); // 5050
```
P.S. Which solution variant is the fastest? The slowest? Why?
P.P.S. Can we use recursion to count `sumTo(100000)`?

View file

@ -0,0 +1,21 @@
By definition, a factorial is `n!` can be written as `n * (n-1)!`.
In other words, the result of `factorial(n)` can be calculated as `n` multiplied by the result of `factorial(n-1)`. And the call for `n-1` can recursively descend lower, and lower, till `1`.
```js run
function factorial(n) {
return (n != 1) ? n * factorial(n - 1) : 1;
}
alert( factorial(5) ); // 120
```
The basis of recursion is the value `1`. We can also make `0` the basis here, doesn't matter much, but gives one more recursive step:
```js run
function factorial(n) {
return n ? n * factorial(n - 1) : 1;
}
alert( factorial(5) ); // 120
```

View file

@ -0,0 +1,31 @@
importance: 4
---
# Calculate factorial
The [factorial](https://en.wikipedia.org/wiki/Factorial) of a natural number is a number multiplied by `"number minus one"`, then by `"number minus two"`, and so on till `1`. The factorial of `n` is denoted as `n!`
We can write a definition of factorial like this:
```js
n! = n * (n - 1) * (n - 2) * ...*1
```
Values of factorials for different `n`:
```js
1! = 1
2! = 2 * 1 = 2
3! = 3 * 2 * 1 = 6
4! = 4 * 3 * 2 * 1 = 24
5! = 5 * 4 * 3 * 2 * 1 = 120
```
The task is to write a function `factorial(n)` that calculates `n!` using recursive calls.
```js
alert( factorial(5) ); // 120
```
P.S. Hint: `n!` can be written as `n * (n-1)!` For instance: `3! = 3*2! = 3*2*1! = 6`

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

View file

@ -0,0 +1,110 @@
The first solution we could try here is the recursive one.
Fibonacci numbers are recursive by definition:
```js run
function fib(n) {
return n <= 1 ? n : fib(n - 1) + fib(n - 2);
}
alert( fib(3) ); // 2
alert( fib(7) ); // 13
// fib(77); // will be extremely slow!
```
...But for big values of `n` it's very slow. For instance, `fib(77)` may hang up the engine for some time eating all CPU resources.
That's because the function makes too many subcalls. The same values are re-evaluated again and again.
For instance, let's see a piece of calculations for `fib(5)`:
```js no-beautify
...
fib(5) = fib(4) + fib(3)
fib(4) = fib(3) + fib(2)
...
```
Here we can see that the value of `fib(3)` is needed for both `fib(5)` and `fib(4)`. So `fib(3)` will be called and evaluated two times completely independently.
Here's the full recursion tree:
![fibonacci recursion tree](fibonacci-recursion-tree.png)
We can clearly notice that `fib(3)` is evaluated two times and `fib(2)` is evaluated three times. The total amount of computations grows much faster than `n`, making it enormous even for `n=77`.
We can optimize that by remembering already-evaluated values: if a value of say `fib(3)` is calculated once, then we can just reuse it in future computations.
Another variant would be to give up recursion and use a totally different loop-based algorithm.
Instead of going from `n` down to lower values, we can make a loop that starts from `1` and `2`, then gets `fib(3)` as their sum, then `fib(4)` as the sum of two previous values, then `fib(5)` and goes up and up, till it gets to the needed value. On each step we only need to remember two previous values.
Here are the steps of the new algorithm in details.
The start:
```js
// a = fib(1), b = fib(2), these values are by definition 1
let a = 1, b = 1;
// get c = fib(3) as their sum
let c = a + b;
/* we now have fib(1), fib(2), fib(3)
a b c
1, 1, 2
*/
```
Now we want to get `fib(4) = fib(2) + fib(3)`.
Let's shift the variables: `a,b` will get `fib(2),fib(3)`, and `c` will get their sum:
```js no-beautify
a = b; // now a = fib(2)
b = c; // now b = fib(3)
c = a + b; // c = fib(4)
/* now we have the sequence:
a b c
1, 1, 2, 3
*/
```
The next step gives another sequence number:
```js no-beautify
a = b; // now a = fib(3)
b = c; // now b = fib(4)
c = a + b; // c = fib(5)
/* now the sequence is (one more number):
a b c
1, 1, 2, 3, 5
*/
```
...And so on until we get the needed value. That's much faster than recursion and involves no duplicate computations.
The full code:
```js run
function fib(n) {
let a = 1;
let b = 1;
for (let i = 3; i <= n; i++) {
let c = a + b;
a = b;
b = c;
}
return b;
}
alert( fib(3) ); // 2
alert( fib(7) ); // 13
alert( fib(77) ); // 5527939700884757
```
The loop starts with `i=3`, because the first and the second sequence values are hard-coded into variables `a=1`, `b=1`.
The approach is called [dynamic programming bottom-up](https://en.wikipedia.org/wiki/Dynamic_programming).

View file

@ -0,0 +1,25 @@
importance: 5
---
# Fibonacci numbers
The sequence of [Fibonacci numbers](https://en.wikipedia.org/wiki/Fibonacci_number) has the formula <code>F<sub>n</sub> = F<sub>n-1</sub> + F<sub>n-2</sub></code>. In other words, the next number is a sum of the two preceding ones.
First two numbers are `1`, then `2(1+1)`, then `3(1+2)`, `5(2+3)` and so on: `1, 1, 2, 3, 5, 8, 13, 21...`.
Fibonacci numbers are related to the [Golden ratio](https://en.wikipedia.org/wiki/Golden_ratio) and many natural phenomena around us.
Write a function `fib(n)` that returns the `n-th` Fibonacci number.
An example of work:
```js
function fib(n) { /* your code */ }
alert(fib(3)); // 2
alert(fib(7)); // 13
alert(fib(77)); // 5527939700884757
```
P.S. The function should be fast. The call to `fib(77)` should take no more than a fraction of a second.

View file

@ -0,0 +1,88 @@
# Loop-based solution
The loop-based variant of the solution:
```js run
let list = {
value: 1,
next: {
value: 2,
next: {
value: 3,
next: {
value: 4,
next: null
}
}
}
};
function printList(list) {
let tmp = list;
while (tmp) {
alert(tmp.value);
tmp = tmp.next;
}
}
printList(list);
```
Please note that we use a temporary variable `tmp` to walk over the list. Technically, we could use a function parameter `list` instead:
```js
function printList(list) {
while(*!*list*/!*) {
alert(list.value);
list = list.next;
}
}
```
...But that would be unwise. In the future we may need to extend a function, do something else with the list. If we change `list`, then we loose such ability.
Talking about good variable names, `list` here is the list itself. The first element of it. And it should remain like that. That's clear and reliable.
From the other side, the role of `tmp` is exclusively a list traversal, like `i` in the `for` loop.
# Recursive solution
The recursive variant of `printList(list)` follows a simple logic: to output a list we should output the current element `list`, then do the same for `list.next`:
```js run
let list = {
value: 1,
next: {
value: 2,
next: {
value: 3,
next: {
value: 4,
next: null
}
}
}
};
function printList(list) {
alert(list.value); // output the current item
if (list.next) {
printList(list.next); // do the same for the rest of the list
}
}
printList(list);
```
Now what's better?
Technically, the loop is more effective. These two variants do the same, but the loop does not spend resources for nested function calls.
From the other side, the recursive variant is shorter and sometimes easier to understand.

View file

@ -0,0 +1,29 @@
importance: 5
---
# Output a single-linked list
Let's say we have a single-linked list (as described in the chapter <info:recursion>):
```js
let list = {
value: 1,
next: {
value: 2,
next: {
value: 3,
next: {
value: 4,
next: null
}
}
}
};
```
Write a function `printList(list)` that outputs list items one-by-one.
Make two variants of the solution: using a loop and using recursion.
What's better: with recursion or without it?

View file

@ -0,0 +1,74 @@
# Using a recursion
The recursive logic is a little bit tricky here.
We need to first output the rest of the list and *then* output the current one:
```js run
let list = {
value: 1,
next: {
value: 2,
next: {
value: 3,
next: {
value: 4,
next: null
}
}
}
};
function printReverseList(list) {
if (list.next) {
printReverseList(list.next);
}
alert(list.value);
}
printReverseList(list);
```
# Using a loop
The loop variant is also a little bit more complicated then the direct output.
There is no way to get the last value in our `list`. We also can't "go back".
So what we can do is to first go through the items in the direct order and rememeber them in an array, and then output what we remembered in the reverse order:
```js run
let list = {
value: 1,
next: {
value: 2,
next: {
value: 3,
next: {
value: 4,
next: null
}
}
}
};
function printReverseList(list) {
let arr = [];
let tmp = list;
while (tmp) {
arr.push(tmp.value);
tmp = tmp.next;
}
for (let i = arr.length - 1; i >= 0; i--) {
alert( arr[i] );
}
}
printReverseList(list);
```
Please note that the recursive solution actually does exactly the same: it follows the list, remembers the items in the chain of nested calls (in the execution context stack), and then outputs them.

View file

@ -0,0 +1,9 @@
importance: 5
---
# Output a single-linked list in the reverse order
Output a single-linked list from the previous task <info:task/output-single-linked-list> in the reverse order.
Make two solutions: using a loop and using a recursion.

View file

@ -0,0 +1,534 @@
# Recursion and stack
Let's return to functions and study them more in-depth.
Our first topic will be *recursion*.
If you are not new to programming, then it is probably familiar and you could skip this chapter.
Recursion is a programming pattern that is useful in situations when a task can be naturally split into several tasks of the same kind, but simpler. Or when a task can be simplified into an easy action plus a simpler variant of the same task. Or, as we'll see soon, to deal with certain data structures.
When a function solves a task, in the process it can call many other functions. A partial case of this is when a function calls *itself*. That's called *recursion*.
[cut]
## Two ways of thinking
For something simple to start with -- let's write a function `pow(x, n)` that raises `x` to a natural power of `n`. In other words, multiplies `x` by itself `n` times.
```js
pow(2, 2) = 4
pow(2, 3) = 8
pow(2, 4) = 16
```
There are two ways to implement it.
1. Iterative thinking: the `for` loop:
```js run
function pow(x, n) {
let result = 1;
// multiply result by x n times in the loop
for(let i = 0; i < n; i++) {
result *= x;
}
return result;
}
alert( pow(2, 3) ); // 8
```
2. Recursive thinking: simplify the task and call self:
```js run
function pow(x, n) {
if (n == 1) {
return x;
} else {
return x * pow(x, n - 1);
}
}
alert( pow(2, 3) ); // 8
```
Please note how the recursive variant is fundamentally different.
When `pow(x, n)` is called, the execution splits into two branches:
```js
if n==1 = x
/
pow(x, n) =
\
else = x * pow(x, n - 1)
```
1. If `n==1`, then everything is trivial. It is called *the base* of recursion, because it immediately produces the obvious result: `pow(x, 1)` equals `x`.
2. Otherwise, we can represent `pow(x, n)` as `x * pow(x, n-1)`. In maths, one would write <code>x<sup>n</sup> = x * x<sup>n-1</sup></code>. This is called *a recursive step*: we transform the task into a simpler action (multiplication by `x`) and a simpler call of the same task (`pow` with lower `n`). Next steps simplify it further and further untill `n` reaches `1`.
We can also say that `pow` *recursively calls itself* till `n == 1`.
![recursive diagram of pow](recursion-pow.png)
For example, to calculate `pow(2, 4)` the recursive variant does these steps:
1. `pow(2, 4) = 2 * pow(2, 3)`
2. `pow(2, 3) = 2 * pow(2, 2)`
3. `pow(2, 2) = 2 * pow(2, 1)`
4. `pow(2, 1) = 2`
So, the recursion reduces a function call to a simpler one, and then -- to even more simpler, and so on, until the result becomes obvious.
````smart header="Recursion is usually shorter"
A recursive solution is usually shorter than an iterative one.
Here we can rewrite the same using the ternary `?` operator instead of `if` to make `pow(x, n)` more terse and still very readable:
```js run
function pow(x, n) {
return (n == 1) ? x : (x * pow(x, n-1));
}
```
````
The maximal number of nested calls (including the first one) is called *recursion depth*. In our case, it there will be exactly `n`.
The maximal recursion depth is limited by JavaScript engine. We can make sure about 10000, some engines allow more, but 100000 is probably out of limit for the majority of them. There are automatic optimizations that help to alleviate this ("tail calls optimizations"), but they are not yet supported everywhere and work only in simple cases.
That limits the application of recursion, but it still remains very wide. There are many tasks where recursive way of thinking gives simpler code, easier to maintain.
## The execution stack
Now let's examine how recursive calls work. For that we'll look under the hood of functions.
The information about a function run is stored in its *execution context*.
The [execution context](https://tc39.github.io/ecma262/#sec-execution-contexts) is an internal data structure that contains details about the execution of a function: where the control flow is now, the current variables, the value of `this` (we don't use it here) and few other internal details.
One function call has exactly one execution context associated with it.
When a function makes a nested call, the following happens:
- The current function is paused.
- The execution context associated with it is remembered in a special data structure called *execution context stack*.
- The nested call executes.
- After it ends, the old execution context is retrieved from the stack, and the outer function is resumed from where it stopped.
Let's see what happens during the `pow(2, 3)` call.
### pow(2, 3)
In the beginning of the call `pow(2, 3)` the execution context will store variables: `x = 2, n = 3`, the execution flow is at line `1` of the function.
We can sketch it as:
<ul class="function-execution-context-list">
<li>
<span class="function-execution-context">Context: { x: 2, n: 3, at line 1 }</span>
<span class="function-execution-context-call">pow(2, 3)</span>
</li>
</ul>
That's when the function starts to execute. The condition `n == 1` is falsy, so the flow continues into the second branch of `if`:
```js run
function pow(x, n) {
if (n == 1) {
return x;
} else {
*!*
return x * pow(x, n - 1);
*/!*
}
}
alert( pow(2, 3) );
```
The variables are same, but the line changes, so the context is now:
<ul class="function-execution-context-list">
<li>
<span class="function-execution-context">Context: { x: 2, n: 3, at line 5 }</span>
<span class="function-execution-context-call">pow(2, 3)</span>
</li>
</ul>
To calculate `x*pow(x, n-1)`, we need to make a subcall of `pow` with new arguments `pow(2, 2)`.
### pow(2, 2)
To do a nested call, JavaScript remembers the current execution context in the *execution context stack*.
Here we call the same function `pow`, but it absolutely doesn't matter. The process is the same for all functions:
1. The current context is "remembered" on top of the stack.
2. The new context is created for the subcall.
3. When the subcall is finished -- the previous context is popped from the stack, and its execution continues.
Here's the context stack when we entered the subcall `pow(2, 2)`:
<ul class="function-execution-context-list">
<li>
<span class="function-execution-context">Context: { x: 2, n: 2, at line 1 }</span>
<span class="function-execution-context-call">pow(2, 2)</span>
</li>
<li>
<span class="function-execution-context">Context: { x: 2, n: 3, at line 5 }</span>
<span class="function-execution-context-call">pow(2, 3)</span>
</li>
</ul>
The new current execution context is on top (and bold), and previous remembered contexts are below.
When we finish the subcall -- it is easy to resume the previous context, because it keeps both variables and the exact place of the code where it stopped. Here in the picture we use the word "line", but of course it's more precise.
### pow(2, 1)
The process repeats: a new subcall is made at line `5`, now with arguments `x=2`, `n=1`.
A new execution context is created, the previous one is pushed on top of the stack:
<ul class="function-execution-context-list">
<li>
<span class="function-execution-context">Context: { x: 2, n: 1, at line 1 }</span>
<span class="function-execution-context-call">pow(2, 1)</span>
</li>
<li>
<span class="function-execution-context">Context: { x: 2, n: 2, at line 5 }</span>
<span class="function-execution-context-call">pow(2, 2)</span>
</li>
<li>
<span class="function-execution-context">Context: { x: 2, n: 3, at line 5 }</span>
<span class="function-execution-context-call">pow(2, 3)</span>
</li>
</ul>
There are 2 old contexts now and 1 currently running for `pow(2, 1)`.
### The exit
During the execution of `pow(2, 1)`, unlike before, the condition `n == 1` is truthy, so the first branch of `if` works:
```js
function pow(x, n) {
if (n == 1) {
*!*
return x;
*/!*
} else {
return x * pow(x, n - 1);
}
}
```
There are no more nested calls, so the function finishes, returning `2`.
As the function finishes, its execution context is not needed any more, so it's removed from the memory. The previous one is restored off-top of the stack:
<ul class="function-execution-context-list">
<li>
<span class="function-execution-context">Context: { x: 2, n: 2, at line 5 }</span>
<span class="function-execution-context-call">pow(2, 2)</span>
</li>
<li>
<span class="function-execution-context">Context: { x: 2, n: 3, at line 5 }</span>
<span class="function-execution-context-call">pow(2, 3)</span>
</li>
</ul>
The execution of `pow(2, 2)` is resumed. It has the result of the subcall `pow(2, 1)`, so it also can finish the evaluation of `x * pow(x, n-1)`, returning `4`.
Then the previous context is restored:
<ul class="function-execution-context-list">
<li>
<span class="function-execution-context">Context: { x: 2, n: 3, at line 5 }</span>
<span class="function-execution-context-call">pow(2, 3)</span>
</li>
</ul>
When it finishes, we have a result of `pow(2, 3) = 8`.
The recursion depth in this case was: **3**.
As we can see from the illustrations above, recursion depth equals the maximal number of context in the stack.
Note the memory requirements. Contexts take memory. In our case, raising to the power of `n` actually requires the memory for `n` contexts, for all lower values of `n`.
A loop-based algorithm is more memory-saving:
```js
function pow(x, n) {
let result = 1;
for(let i = 0; i < n; i++) {
result *= x;
}
return result;
}
```
The iterative `pow` uses a single context changing `i` and `result` in the process. Its memory requirements are small, fixed and do not depend on `n`.
**Any recursion can be rewritten as a loop. The loop variant usually can be made more effective.**
...But sometimes the rewrite is non-trivial, especially when function uses different recursive subcalls depending on conditions and merges their results or when the branching is more intricate. And the optimization may be unneeded and totally not worth the efforts.
Recursion can give a shorter code, easier to understand and support. Optimizations are not required in every place, mostly we need a good code, that's why it's used.
## Recursive traversals
Another great application of the recursion is a recursive traversal.
Imagine, we have an company. The staff structure can be presented as an object:
```js
let company = {
sales: [{
name: 'John',
salary: 1000
}, {
name: 'Alice',
salary: 600
}],
development: {
sites: [{
name: 'Peter',
salary: 2000
}, {
name: 'Alex',
salary: 1800
}],
internals: [{
name: 'Jack',
salary: 1300
}]
}
};
```
In other words, a company has departments.
- A department may have an array of staff. For instance, `sales` department has 2 employees: John and Alice.
- Or a department may split into subdepartments, like `development` has two branches: `sites` and `internals`. Each of them has the own staff.
- It is also possible that when a subdepartment grows, it divides into subsubdepartments (or teams).
For instance, the `sites` department in the future may be split into teams for `siteA` and `siteB`. And they, potentially, can split even more. That's not on the picture, just something to have in mind.
Now let's say we want a function to get the sum of all salaries. How can we do that?
An iterative approach is not easy, because the structure is not simple. The first idea may be to make a `for` loop over `company` with nested subloop over 1st level departments. But then we need more nested subloops to iterate over the staff in 2nd level departments like `sites`. ...And then another subloop inside those for 3rd level departments that might appear in the future? Should we stop on level 3 or make 4 levels of loops? If we put 3-4 nested subloops in the code to traverse a single object, it becomes rather ugly.
Let's try recursion.
As we can see, when our function gets a department to sum, there are two possible cases:
1. Either it's a "simple" department with an *array of people* -- then we can sum the salaries in a simple loop.
2. Or it's *an object with `N` subdepartments* -- then we can make `N` recursive calls to get the sum for each of the subdeps and combine the results.
The (1) is the base of recursion, the trivial case.
The (2) is the recursive step. A complex task is split into subtasks for smaller departments. They may in turn split again, but sooner or later the split will finish at (1).
The algorithm is probably even easier to read from the code:
```js run
let company = { // the same object, compressed for brevity
sales: [{name: 'John', salary: 1000}, {name: 'Alice', salary: 600 }],
development: {
sites: [{name: 'Peter', salary: 2000}, {name: 'Alex', salary: 1800 }],
internals: [{name: 'Jack', salary: 1300}]
}
};
// The function to do the job
*!*
function sumSalaries(department) {
if (Array.isArray(department)) { // case (1)
return department.reduce((prev, current) => prev + current.salary, 0); // sum the array
} else { // case (2)
let sum = 0;
for(let subdep of Object.values(department)) {
sum += sumSalaries(subdep); // recursively call for subdepartments, sum the results
}
return sum;
}
}
*/!*
alert(sumSalaries(company)); // 6700
```
The code is short and easy to understand (hopefully?). That's the power of recursion. It also works for any level of subdepartment nesting.
Here's the diagram of calls:
![recursive salaries](recursive-salaries.png)
We can easily see the principle: for an object `{...}` subcalls are made, while arrays `[...]` are the "leaves" of the recursion tree, they give immediate result.
Note that the code uses smart features that we've covered before:
- Method `arr.reduce` explained in the chapter <info:array-methods> to get the sum of the array.
- Loop `for(val of Object.values(obj))` to iterate over object values: `Object.values` returns an array of them.
## Recursive structures
A recursive (recursively-defined) data structure is a structure that replicates itself in parts.
We've just seen it in the example of a company structure above.
A company *department* is:
- Either an array of people.
- Or an object with *departments*.
For web-developers there are much better known examples: HTML and XML documents.
In the HTML document, an *HTML-tag* may contain a list of:
- Text pieces.
- HTML-comments.
- Other *HTML-tags* (that in turn may contain text pieces/comments or other tags etc).
That's once again a recursive definition.
For better understanding, we'll cover one more recursive structure named "Linked list" that might be a better alternative for arrays in some cases.
### Linked list
Imagine, we want to store an ordered list of objects.
The natural choice would be an array:
```js
let arr = [obj1, obj2, obj3];
```
...But there's a problem with arrays. The "delete element" and "insert element" operations are expensive. For instance, `arr.unshift(obj)` operation has to renumber all elements to make room for a new `obj`, and if the array is big, it takes time. Same with `arr.shift()`.
The only structural modifications that do not require mass-renumbering are those that operate with the end of array: `arr.push/pop`. So an array can be quite slow for big queues.
Alternatively, if we really need fast insertion/deletion, we can choose another data structure called a [linked list](https://en.wikipedia.org/wiki/Linked_list).
The *linked list element* is recursively defined as an object with:
- `value`.
- `next` property referencing the next *linked list element* or `null` if that's the end.
For instance:
```js
let list = {
value: 1,
next: {
value: 2,
next: {
value: 3,
next: {
value: 4,
next: null
}
}
}
};
```
Graphical representation of the list:
![linked list](linked-list.png)
An alternative code for creation:
```js no-beautify
let list = { value: 1 };
list.next = { value: 2 };
list.next.next = { value: 3 };
list.next.next.next = { value: 4 };
```
Here we can even more clearer see that there are multiple objects, each one has the `value` and `next` pointing to the neighbour. The `list` variable is the first object in the chain, so following `next` pointers from it we can reach any element.
The list can be easily split into multiple parts and later joined back:
```js
let secondList = list.next.next;
list.next.next = null;
```
![linked list split](linked-list-split.png)
To join:
```js
list.next.next = secondList;
```
And surely we can insert or remove items in any place.
For instance, to prepend a new value, we need to update the head of the list:
```js
let list = { value: 1 };
list.next = { value: 2 };
list.next.next = { value: 3 };
list.next.next.next = { value: 4 };
*!*
// prepend the new value to the list
list = { value: "new item", next: list };
*/!*
```
![linked list](linked-list-0.png)
To remove a value from the middle, change `next` of the previous one:
```js
list.next = list.next.next;
```
![linked list](linked-list-remove-1.png)
We made `list.next` jump over `1` to value `2`. The value `1` is now excluded from the chain. If it's not stored anywhere else, it will be automatically removed from the memory.
Unlike arrays, there's no mass-renumbering, we can easily rearrange elements.
Naturally, lists are not always better than arrays. Otherwise everyone would use only lists.
The main drawback is that we can't easily access an element by its number. In an array that's easy: `arr[n]` is a direct reference. But in the list we need to start from the first item and go `next` `N` times to get the Nth element.
...But we don't always need such operations. For instance, when we need a queue or even a [deque](https://en.wikipedia.org/wiki/Double-ended_queue) -- the ordered structure that must allow very fast adding/removing elements from both ends.
Sometimes it's worth to add another variable named `tail` to track the last element of the list (and update it when adding/removing elements from the end). For large sets of elements the speed difference versus arrays is huge.
## Summary
Terms:
- *Recursion* is a programming term that means a "self-calling" function. Such functions can be used to solve certain tasks in elegant ways.
When a function calls itself, that's called a *recursion step*. The *basis* of recursion is function arguments that make the task so simple that the function does not make further calls.
- A [recursively-defined](https://en.wikipedia.org/wiki/Recursive_data_type) data structure is a data structure that can be defined using itself.
For instance, the linked list can be defined as a data structure consisting of an object referencing a list (or null).
```js
list = { value, next -> list }
```
Trees like HTML elements tree or the department tree from this chapter are also naturally recursive: they branch and every branch can have other branches.
Recursive functions can be used to walk them as we've seen in the `sumSalary` example.
Any recursive function can be rewritten into an iterative one. And that's sometimes required to optimize stuff. But for many tasks a recursive solution is fast enough and easier to write and support.

View file

@ -0,0 +1,26 @@
<style>
.function-execution-context-list {
margin: 0;
padding: 0;
overflow: auto;
}
.function-execution-context {
border: 1px solid black;
font-family: "Consolas", monospace;
padding: 4px 6px;
margin: 0 4px;
}
.function-execution-context-call {
color: gray;
}
.function-execution-context-call::before {
content: ' call: ';
}
.function-execution-context-list li:first-child {
font-weight: bold;
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

View file

@ -0,0 +1,246 @@
# Rest parameters and spread operator
Many JavaScript built-in functions support on arbitrary number of arguments.
For instance:
- `Math.max(arg1, arg2, ..., argN)` -- returns the greatest of the arguments.
- `Object.assign(dest, src1, ..., srcN)` -- copies properties from `src1..N` into `dest`.
- ...and so on.
In this chapter we'll see how to do the same. And, more important, how to feel comfortable working with such functions and arrays.
[cut]
## Rest parameters `...`
A function can be called with any number of arguments, no matter how it is defined.
Like here:
```js run
function sum(a, b) {
return a + b;
}
alert( sum(1, 2, 3, 4, 5) );
```
There will be no error because of "excessive" arguments. But of course in the result only the first two will be counted.
The rest parameters can be mentioned in a function definition with three dots `...`. They literally mean: "gather the remaining parameters into an array".
For instance, to gather all arguments into array `args`:
```js run
function sumAll(...args) { // args is the name for the array
let sum = 0;
for(let arg of args) sum += arg;
return sum;
}
alert( sumAll(1) ); // 1
alert( sumAll(1, 2) ); // 3
alert( sumAll(1, 2, 3) ); // 6
```
We can choose to get first parameters as variables, and gather only the rest.
Here the first two arguments go into variables and the rest goes to `titles` array:
```js run
function showName(firstName, lastName, ...titles) {
alert( firstName + ' ' + lastName ); // Julius Caesar
// the rest = ["Consul", "Imperator"]
alert( rest[0] ); // Consul
alert( rest[1] ); // Imperator
alert( rest.length ); // 2
}
showName("Julius", "Caesar", "Consul", "Imperator");
```
````warn header="The rest parameters must be at the end"
The rest parameters gather all remaining arguments, so the following has no sense:
```js
function f(arg1, ...rest, arg2) { // arg2 after ...rest ?!
// error
}
```
The `...rest` must always be the last.
````
## The "arguments" variable
There is also a special array-like object named `arguments` that contains all arguments by their index.
For instance:
```js run
function showName() {
alert( arguments.length );
alert( arguments[0] );
alert( arguments[1] );
// it's iterable
// for(let arg of arguments) alert(arg);
}
// shows: 2, Julius, Caesar
showName("Julius", "Caesar");
// shows: 1, Ilya, undefined (no second argument)
showName("Ilya");
```
In old times, rest parameters did not exist in the language, and `arguments` was the only way to get all arguments of the function no matter of their total number.
And it still works, we can use it.
But the downside is that though `arguments` is both array-like and iterable, it's not an array. It does not support array methods, so we can't say call `arguments.map(...)`.
Also, it always has all arguments in it, we can't capture them partially, like we did with rest parameters.
So when we need these features, then rest parameters are preferred.
````smart header="Arrow functions do not have `\"arguments\"`"
If we access the `arguments` object from an arrow function, it takes them from the outer "normal" function.
Here's an example:
```js run
function f() {
let showArg = () => alert(arguments[0]);
showArg();
}
f(1); // 1
```
As we remember, arrow functions don't have own `this`. Now we know they don't have the special `arguments` object too.
````
## Spread operator [#spread-operator]
We've just seen how to get an array from the list of parameters.
But sometimes we need to do exactly the reverse.
For instance, there's a built-in function [Math.max](mdn:js/Math/max) that returns the greatest number from the list:
```js run
alert( Math.max(3, 5, 1) ); // 5
```
Now let's say we have an array `[3, 5, 1]`. How to call `Math.max` with it?
Passing it "as it" won't work, because `Math.max` expects a list of numeric arguments, not a single array:
```js run
let arr = [3, 5, 1];
*!*
alert( Math.max(arr) ); // NaN
*/!*
```
...And surely we can't manually list items in the code `Math.max(arg[0], arg[1], arg[2])`, because we may be unsure how much are there. As our script executes, there might be many, or there might be none. Also that would be ugly.
*Spread operator* to the rescue. It looks similar to rest parameters, also using `...`, but does quite the opposite.
When `...arr` is used in the function call, it "expands" an iterable object `arr` into the list of arguments.
For `Math.max`:
```js run
let arr = [3, 5, 1];
alert( Math.max(...arr) ); // 5 (spread turns array into a list of arguments)
```
We also can pass multiple iterables this way:
```js run
let arr1 = [1, -2, 3, 4];
let arr2 = [8, 3, -8, 1];
alert( Math.max(...arr1, ...arr2) ); // 8
```
...And even combine the spread operator with normal values:
```js run
let arr1 = [1, -2, 3, 4];
let arr2 = [8, 3, -8, 1];
alert( Math.max(1, ...arr1, 2, ...arr2, 25) ); // 25
```
Also spread operator can be used to merge arrays:
```js run
let arr = [3, 5, 1];
let arr2 = [8, 9, 15];
*!*
let merged = [0, ...arr, 2, ...arr2];
*/!*
alert(merged); // 0,3,5,1,2,8,9,15 (0, then arr, then 2, then arr2)
```
In the examples above we used an array to demonstrate the spread operator, but any iterable will do.
For instance, here we use spread operator to turn the string into array of characters:
```js run
let str = "Hello";
alert( [...str] ); // H,e,l,l,o
```
The spread operator internally uses iterators to gather elements, the same way as `for..of` does.
So, for a string, `for..of` returns characters and `...str` becomes `"h","e","l","l","o"`. The list of characters is passed to array initializer `[...str]`.
For this particular task we could also use `Array.from`, because it converts an iterable (like a string) into an array:
```js run
let str = "Hello";
// Array.from converts an iterable into an array
alert( Array.from(str) ); // H,e,l,l,o
```
The result is the same as `[...str]`.
But there's a subtle difference between `Array.from(obj)` and `[...obj]`:
- `Array.from` operates on both array-likes and iterables.
- The spread operator operates only on iterables.
So, for the task of turning something into an array, `Array.from` appears more universal.
## Summary
When we see `"..."` in the code, it is either rest parameters or the spread operator.
There's an easy way to distinguish between them:
- When `...` is at the end of function parameters, it's "rest parameters" and gathers the rest of the list into the array.
- When `...` occurs in a function call or alike, it's called a "spread operator" and expands an array into the list.
Use patterns:
- Rest parameters are used to create functions that accept any number of arguments.
- The spread operator is used to pass an array to functions that normally require a list of many arguments.
Together they help to travel between a list and an array of parameters with ease.
All arguments of a function call are also available in "old-style" `arguments`: array-like iterable object.

View file

@ -0,0 +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 it's own `count`.

View file

@ -0,0 +1,31 @@
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() {
let count = 0;
return function() {
return count++;
};
}
let counter = makeCounter();
let counter2 = makeCounter();
alert( counter() ); // 0
alert( counter() ); // 1
*!*
alert( counter2() ); // ?
alert( counter2() ); // ?
*/!*
```

View file

@ -0,0 +1,24 @@
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:
```js run
function Counter() {
let count = 0;
this.up = function() {
return ++count;
};
this.down = function() {
return --count;
};
}
let counter = new Counter();
alert( counter.up() ); // 1
alert( counter.up() ); // 2
alert( counter.down() ); // 1
```

View file

@ -0,0 +1,29 @@
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() {
let count = 0;
this.up = function() {
return ++count;
};
this.down = function() {
return --count;
};
}
let counter = new Counter();
alert( counter.up() ); // ?
alert( counter.up() ); // ?
alert( counter.down() ); // ?
```

View file

@ -0,0 +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.

View file

@ -0,0 +1,20 @@
# Function in if
Look at the code. What will be result of the call at the last line?
```js run
let phrase = "Hello";
if (true) {
let user = "John";
function sayHi() {
alert(`${phrase}, ${user}`);
}
}
*!*
sayHi();
*/!*
```

View file

@ -0,0 +1,17 @@
For the second brackets 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
};
}
alert( sum(1)(2) ); // 3
alert( sum(5)(-1) ); // 4
```

View file

@ -0,0 +1,17 @@
importance: 4
---
# Sum with closures
Write function `sum` that works like this: `sum(a)(b) = a+b`.
Yes, exactly this way, via double brackets (not a mistype).
For instance:
```js
sum(1)(2) = 3
sum(5)(-1) = 4
```

View file

@ -0,0 +1,8 @@
function inArray(arr) {
return x => arr.includes(x);
}
function inBetween(a, b) {
return x => (x >= a && x <= b);
}

View file

@ -0,0 +1,10 @@
let arr = [1, 2, 3, 4, 5, 6, 7];
function inBetween(a, b) {
// ...your code...
}
function inArray(arr) {
// ...your code...
}

View file

@ -0,0 +1,21 @@
describe("inArray", function() {
let arr = [1, 2, 3, 4, 5, 6, 7];
it("returns the filter for values in array", function() {
let filter = inArray(arr);
assert.isTrue(filter(5));
assert.isFalse(filter(0));
});
});
describe("inBetween", function() {
it("returns the filter for values between", function() {
let filter = inBetween(3, 6);
assert.isTrue(filter(5));
assert.isFalse(filter(0));
});
});

View file

@ -0,0 +1,26 @@
# Filter inBetween
```js run
function inBetween(a, b) {
return function(x) {
return x >= a && x <= b;
};
}
let arr = [1, 2, 3, 4, 5, 6, 7];
alert( arr.filter(inBetween(3, 6)) ); // 3,4,5,6
```
# Filter inArray
```js run
function inArray(arr) {
return function(x) {
return arr.includes(x);
};
}
let arr = [1, 2, 3, 4, 5, 6, 7];
alert( arr.filter(inArray([1, 2, 10])) ); // 1,2
```

View file

@ -0,0 +1,29 @@
importance: 5
---
# Filter through function
We have a built-in method `arr.filter(f)` for arrays. It filters all elements through the function `f`. If it returns `true`, then such an element is returned in the resulting array.
Make a set of "ready to use" filters:
- `inBetween(a, b)` -- between `a` and `b` or equal to them (inclusively).
- `inArray([...])` -- in the given array.
The usage must be like this:
- `arr.filter(inBetween(3,6))` -- selects only values between 3 and 6.
- `arr.filter(inArray([1,2,3]))` -- selects only elements matching with one of the members of `[1,2,3]`.
For instance:
```js
/* .. your code for inBetween and inArray */
let arr = [1, 2, 3, 4, 5, 6, 7];
alert( arr.filter(inBetween(3, 6)) ); // 3,4,5,6
alert( arr.filter(inArray([1, 2, 10])) ); // 1,2
```

View file

@ -0,0 +1,22 @@
```js run
let users = [
{ name: "John", age: 20, surname: "Johnson" },
{ name: "Pete", age: 18, surname: "Peterson" },
{ name: "Ann", age: 19, surname: "Hathaway" }
];
*!*
function byField(field) {
return (a, b) => a[field] > b[field] ? 1 : -1;
}
*/!*
users.sort(byField('name'));
users.forEach(user => alert(user.name)); // Ann, John, Pete
users.sort(byField('age'));
users.forEach(user => alert(user.name)); // Pete, Ann, John
```

View file

@ -0,0 +1,36 @@
importance: 5
---
# Sort by field
We've got an array of objects to sort:
```js
let users = [
{ name: "John", age: 20, surname: "Johnson" },
{ name: "Pete", age: 18, surname: "Peterson" },
{ name: "Ann", age: 19, surname: "Hathaway" }
];
```
The usual way to do that would be:
```js
// by name (Ann, John, Pete)
users.sort((a, b) => a.name > b.name ? 1 : -1);
// by age (Pete, Ann, John)
users.sort((a, b) => a.age > b.age ? 1 : -1);
```
Can we make it even more verbose, like this?
```js
users.sort(byField('name'));
users.sort(byField('age'));
```
So, instead of writing a function, just put `byField(fieldName)`.
Write the function `byField` that can be used for that.

View file

@ -0,0 +1,13 @@
function makeArmy() {
let shooters = [];
for(let i = 0; i < 10; i++) {
let shooter = function() { // shooter function
alert( i ); // should show its number
};
shooters.push(shooter);
}
return shooters;
}

View file

@ -0,0 +1,22 @@
function makeArmy() {
let shooters = [];
let i = 0;
while (i < 10) {
let shooter = function() { // shooter function
alert( i ); // should show its number
};
shooters.push(shooter);
i++;
}
return shooters;
}
/*
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...
*/

View file

@ -0,0 +1,25 @@
describe("army", function() {
let army;
before(function() {
army = makeArmy();
window.alert = sinon.stub(window, "alert");
});
it("army[0] shows 0", function() {
army[0]();
assert(alert.calledWith(0));
});
it("army[5] shows 5", function() {
army[5]();
assert(alert.calledWith(5));
});
after(function() {
window.alert.restore();
});
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

View file

@ -0,0 +1,121 @@
Let's examine what's done inside `makeArmy`, and the solution will become obvious.
1. It creates an empty array `shooters`:
```js
let shooters = [];
```
2. Fills it in the loop via `shooters.push(function...)`.
Every element is a function, so the resulting array looks like this:
```js no-beautify
shooters = [
function () { alert(i); },
function () { alert(i); },
function () { alert(i); },
function () { alert(i); },
function () { alert(i); },
function () { alert(i); },
function () { alert(i); },
function () { alert(i); },
function () { alert(i); },
function () { alert(i); }
];
```
3. The array is returned from the function.
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?
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.
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
};
...
}
...
}
```
...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`).
As a result, all `shooter` functions get from the outer lexical envrironment the same, last value `i=10`.
The fix can be very simple:
```js run
function makeArmy() {
let shooters = [];
*!*
for(let i = 0; i < 10; i++) {
*/!*
let shooter = function() { // shooter function
alert( i ); // should show its number
};
shooters.push(shooter);
}
return shooters;
}
let army = makeArmy();
army[0](); // 0
army[5](); // 5
```
Now it works correctly, because every time the code block in `for (..) {...}` is executed, a new Lexical Environment is created for it, with the corresponding value of `i`.
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. A `shooter` gets the value exactly from the one where it was created.
![](lexenv-makearmy.png)
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.

View file

@ -0,0 +1,35 @@
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() { // shooter function
alert( i ); // should show its number
};
shooters.push(shooter);
i++;
}
return shooters;
}
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...
```
Why all shooters show the same? Fix the code so that they work as intended.

View file

@ -0,0 +1,776 @@
# Closure
JavaScript is a very function-oriented language, it gives a lot of freedom. A function can be created at one moment, then passed as a value to another variable or function and called from a totally different place much later.
We know that a function can access variables outside of it. And this feature is used quite often.
But what happens when outer variables change? Does a function get a new value or the old one?
Also, what happens when a variable with the function travels to another place of the code and is called from there -- will it get access to outer variables in the new place?
There is no general programming answer for these questions. Different languages behave differently. Here we'll cover JavaScript.
[cut]
## A couple of questions
Let's formulate two questions for the seed, and then study internal mechanics piece-by-piece, so that you will be able to answer them and have no problems with more complex ones in the future.
1. The function `sayHi` uses an external variable `name`. The variable changes before function runs. Will it pick up a new variant?
```js
let name = "John";
function sayHi() {
alert("Hi, " + name);
}
name = "Pete";
*!*
sayHi(); // what will it show: "John" or "Pete"?
*/!*
```
Such situations are common in both browser and server-side development. A function may be scheduled to execute later than it is created: on user action or after a network request etc.
So, the the question is: does it pick up latest changes?
2. The function `makeWorker` makes another function and returns it. That new function can be called from somewhere else. Will it have access to outer variables from its creation place or the invocation place or maybe both?
```js
function makeWorker() {
let name = "Pete";
return function() {
alert(name);
};
}
let name = "John";
// create a function
let work = makeWorker();
// call it
*!*
work(); // what will it show? "Pete" (name where created) or "John" (name where called)?
*/!*
```
## Lexical Environment
To understand what's going on, let's first discuss what a "variable" technically is.
In JavaScript, every running function, code block and the script as a whole have an associated object named *Lexical Environment*.
The Lexical Environment object consists of two parts:
1. *Environment Record* -- an object that has all local variables as its properties (and some other information like the value of `this`).
2. An reference to the *outer lexical environment*, usually the one associated with the code lexically right outside of it (outside of the current figure brackets).
So, a "variable" is just a property of the special internal object, Environment Record. "To get or change a variable" means "to get or change the property of that object".
For instance, in this simple code, there is only one Lexical Environment:
![lexical environment](lexical-environment-global.png)
This is a so-called global Lexical Environment, associated with the whole script. For browsers, all `<script>` tags share the same global environment.
On the picture above the rectangle means Environment Record (variable store) and the arrow means the outer reference. The global Lexical Environment has no outer one, so that's `null`.
Here's the bigger picture of how `let` variables work:
![lexical environment](lexical-environment-global-2.png)
1. When a script starts, the Lexical Environment is empty.
2. The `let phrase` definition appears. Now it initially has no value, so `undefined` is stored.
3. The `phrase` is assigned.
4. The `phrase` changes the value.
Everything looks simple for now, right?
To summarize:
- A variable is a property of a special internal object, associated with the currently executing block/function/script.
- Working with variables is actually working with the properties of that object.
### Function Declaration
Function Declarations are special. Unlike `let` variables, they are processed not when the execution reaches them, but much earlier: when a Lexical Environment is created. For the global Lexical Environment, it means the moment when the script is started.
...And that is why we can call a function declaration before it is defined, like `say` in the code below:
![lexical environment](lexical-environment-global-3.png)
Here we have `say` function defined from the beginning of the script, and `let` appears a bit later.
### Inner and outer Lexical Environment
Now we have a call to `say()` accessing the outer variables let's see the details of what's going on.
When a function runs, a new function Lexical Environment is created automatically for variables and parameters of the call.
<!--
```js
let phrase = "Hello";
function say(name) {
alert( `${phrase}, ${name}` );
}
say("John"); // Hello, John
```
-->
Here's the picture for `say("John")`:
![lexical environment](lexical-environment-simple.png)
The `outer` reference points to the environment outside of the function: the global one.
So during the function call we have two Lexical Environments: the inner (for the function call) and the outer (global):
- The inner Lexical Environment corresponds to the current `say` execution. It has a single variable: `name`.
- The outer Lexical Environment is the "right outside" of the function declaration. Here the function is defined in the global Lexical Environment, so this is it.
**When a code wants to access a variable -- it is first searched in the inner Lexical Environment, then in the outer one, and further until the end of the chain.**
If a variable is not found anywhere, that's an error in strict mode (without `use strict` an assignment to an undefined variable is technically possible, but that's a legacy feature, not recommended).
Let's see what it means for our example:
- When the `alert` inside `say` wants to access `name`, it is found immediately in the function Lexical Environment.
- When the code wants to access `phrase`, then there is no `phrase` locally, so follows the `outer` reference and finds it globally.
![lexical environment lookup](lexical-environment-simple-lookup.png)
Now we can give the answer to the first seed question.
**A function sees external variables as they are now.**
That's because of the described mechanism. Old variable values are not saved anywhere. When a function wants them, it takes the current values from its own or an outer Lexical Environment.
So the answer is, of course, `Pete`:
```js run
let name = "John";
function sayHi() {
alert("Hi, " + name);
}
name = "Pete"; // (*)
*!*
sayHi(); // Pete
*/!*
```
The execution flow of the code above:
1. The global Lexical Envrionment has `name: "John"`.
2. At the line `(*)` the global variable is changed, now it has `name: "Pete"`.
3. When the function `say()`, is executed and takes `name` from outside. Here that's from the global Lexical Environment where it's already `"Pete"`.
```smart header="One call -- one Lexical Environment"
Please note that a new function Lexical Environment is created each time a function runs.
And if a function is called multiple times, then each invocation will have its own Lexical Environment, with local variables and parameters specific for that very run.
```
```smart header="Lexical Environment is a specification object"
"Lexical Environment" is a specification object. We can't get this object in our code and manipulate it directly. JavaScript engines also may optimize it, discard variables that are unused in the code and perform other stuff.
```
## Nested functions
A function is called "nested" when it is created inside another function.
Technically, that is easily possible.
We can use it to organize the code, like this:
```js
function sayHiBye(firstName, lastName) {
// helper nested function to use below
function getFullName() {
return firstName + " " + lastName;
}
alert( "Hello, " + getFullName() );
alert( "Bye, " + getFullName() );
}
```
Here the *nested* function `getFullName()` is made for convenience. It has access to outer variables and so can return the full name.
What's more interesting, a nested function can be returned: as a property of an object or as a result by itself. And then used somewhere else. But no matter where, it still keeps the access to the same outer variables.
An example with a constructor function (from the chapter <info:constructor-new>):
```js run
// constructor function returns a new object
function User(name) {
// the method is created as a nested function
this.sayHi = function() {
alert(name);
};
}
let user = new User("John");
user.sayHi();
```
An example with returning a function:
```js run
function makeCounter() {
let count = 0;
return function() {
return count++;
};
}
let counter = makeCounter();
alert( counter() ); // 0
alert( counter() ); // 1
alert( counter() ); // 2
```
We'll continue on with `makeCounter` example. It creates the "counter" function that returns the next number on each invocation. Despite being simple, such code structure still has practical applications, for instance, a [pseudorandom number generator](https://en.wikipedia.org/wiki/Pseudorandom_number_generator).
When the inner function runs, the variable in `count++` is searched from inside out.
For the example above, the order will be:
![](lexical-search-order.png)
1. The locals of the nested function.
2. The variables of the outer function.
3. ...And further until it reaches globals.
As a rule, if an outer variable is modified, then it is changed on the place where it is found. So `count++` finds the outer variable and increases it "at place" (as a property of the corresponding Environment Record) every time.
The questions may arise:
1. Can we somehow reset the counter from the outer code?
2. What if we call `makeCounter()` multiple times -- are the resulting `counter` functions independent or they share the same `count`?
Try to answer them before going on reading.
...Are you done?
Okay, here we go with the answers.
1. There is no way. The `counter` is a local function variable, we can't access it from outside.
2. For every call to `makeCounter()` a new function Lexical Environment is created, with its own `counter`. So the resulting `counter` functions are independent.
Probably, the situation with outer variables is quite clear for you as of now. But my teaching experience shows that still there are problems sometimes. And in more complex situations a solid in-depth understanding of internals may be needed. So here you go.
## Environments in detail
For a more in-depth understanding, this section elaborates `makeCounter` example in detail, adding some missing pieces.
Here's what's going on step-by-step:
1. When the script has just started, there is only global Lexical Environment:
![](lexenv-nested-makecounter-1.png)
At this moment there is only `makeCounter` function. And it did not run yet.
Now let's add one piece that we didn't cover yet.
All functions "on birth" receive a hidden property `[[Environment]]` with the reference to the Lexical Environment of their creation. So a function kind of remembers where it was made. In the future, when the function runs, `[[Environment]]` is used as the outer lexical reference.
Here, `makeCounter` is created in the global Lexical Environment, so `[[Environment]]` keeps the reference to it.
2. Then the code runs on, and the call to `makeCounter()` is performed. Here's the state when we are at the first line inside `makeCounter()`:
![](lexenv-nested-makecounter-2.png)
The Lexical Environment for the `makeCounter()` call is created.
As all Lexical Environments, it stores two things:
- Environment Record with local variables, in our case `count` is the only local variable (after the line with `let count` is processed).
- The outer lexical reference, which is set using `[[Environment]]` of the function. Here `[[Environment]]` of `makeCounter` references the global Lexical Environment.
So, now we have two Lexical Environments: the first one is global, the second one is for the current `makeCounter` call, with the outer reference to global.
3. During the execution of `makeCounter()`, a tiny nested function is created.
It doesn't matter how the function is created: using Function Declaration or Function Expression or in an object literal as a method. All functions get the `[[Environment]]` property that references the Lexical Environment where they are made.
For our new nested function that is the current Lexical Environment of `makeCounter()`:
![](lexenv-nested-makecounter-3.png)
Please note that at the inner function was created, but not yet called. The code inside `function() { return count++; }` is not running.
So we still have two Lexical Environments. And a function which has `[[Environment]]` referencing to the inner one of them.
4. As the execution goes on, the call to `makeCounter()` finishes, and the result (the tiny nested function) is assigned to the global variable `counter`:
![](lexenv-nested-makecounter-4.png)
That function has only one line: `return count++`, that will be executed when we run it.
5. When the `counter()` is called, an "empty" Lexical Environment is created for it. It has no local variables by itself. But the `[[Environment]]` of `counter` is used for the outer reference, so it has access to the variables of the former `makeCounter()` call, where it was created:
![](lexenv-nested-makecounter-5.png)
Now if it accesses a variable, it first searches its own Lexical Environment (empty), then the Lexical Environment of the former `makeCounter()` call, then the global one.
When it looks for `count`, it finds it among the variables `makeCounter`, in the nearest outer Lexical Environment.
Please note how memory management works here. When `makeCounter()` call finished some time ago, its Lexical Environment was retained in memory, because there's a nested function with `[[Environment]]` referencing it.
Generally, a Lexical Environment object lives until there is a function which may use it. And when there are none, it is cleared.
6. The call to `counter()` not only returns the value of `count`, but also increases it. Note that the modification is done "at place". The value of `count` is modified exactly in the environment where it was found.
![](lexenv-nested-makecounter-6.png)
So we return to the previous step with the only change -- the new value of `count`. The following calls all do the same.
7. Next `counter()` invocations do the same.
The answer to the second seed question from the beginning of the chapter should now be obvious.
The `work()` function in the code below uses the `name` from the place of its origin through the outer lexical environment reference:
![](lexenv-nested-work.png)
So, the result is `"Pete"` here.
...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 it would be `"John"`.
```smart header="Closures"
There is a general programming term "closure", that developers generally should know.
A [closure](https://en.wikipedia.org/wiki/Closure_(computer_programming)) is a function that remembers its outer variables and can access them. In some languages, that's not possible, or a function should be written in a special way to make it happen. But as explained above, in JavaScript all functions are naturally closures (there is only one exclusion, to be covered in <info:new-function>).
That is: they automatically remember where they are created using a hidden `[[Environment]]` property, and all of them can access outer variables.
When on an interview a frontend developer gets a question about "what's a closure?", a valid answer would be a definition of the closure and an explanation that all functions in JavaScript are closures, and maybe few more words about technical details: the `[[Environment]]` property and how Lexical Environments work.
```
## Code blocks and loops, IIFE
The examples above concentrated on functions. But Lexical Environments also exist for code blocks `{...}`.
They are created when a code block runs and contain block-local variables.
In the example below, when the execution goes into `if` block, the new Lexical Environment is created for it:
<!--
```js run
let phrase = "Hello";
if (true) {
let user = "John";
alert(`${phrase}, ${user}`); // Hello, John
}
alert(user); // Error, can't see such variable!
```
-->
![](lexenv-if.png)
The new Lexical Environment gets the enclosing one as the outer reference, so `phrase` can be found. But all variables and Function Expressions declared inside `if`, will reside in that Lexical Environment.
After `if` finishes, the `alert` below won't see the `user`.
For a loop, every run has a separate Lexical Environment. The loop variable is its part:
```js run
for(let i = 0; i < 10; i++) {
// Each loop has its own Lexical Environment
// {i: value}
}
```
We also can use a "bare" code block to isolate variables.
For instance, in-browser all scripts share the same global area. So if we create a global variable in one script, it becomes available to others. That may be a source of conflicts if two scripts use the same variable name and overwrite each other.
If we don't want that, we can use a code block to isolate the whole script or an area in it:
```js run
{
// do some job with local variables that should not be seen outside
let message = "Hello";
alert(message); // Hello
}
alert(message); // Error: message is not defined
```
In old scripts, you can find immediately-invoked function expressions (abbreviated as IIFE) used for this purpose.
They look like this:
```js run
(function() {
let message = "Hello";
alert(message); // Hello
})();
```
Here a Function Expression is created and immediately called. So the code executes right now and has its own private variables.
The Function Expression is wrapped with brackets `(function {...})`, because when JavaScript meets `"function"` in the main code flow, it understands it as a start of Function Declaration. But a Function Declaration must have a name, so there will be an error:
```js run
// Error: Unexpected token (
function() { // <-- JavaScript cannot find function name, meets ( and gives error
let message = "Hello";
alert(message); // Hello
}();
```
We can say "okay, let it be Function Declaration, let's add a name", but it won't work. JavaScript does not allow Function Declarations to be called immediately:
```js run
// syntax error because of brackets below
function go() {
}(); // <-- can't call Function Declaration immediately
```
So the brackets are needed to show JavaScript that the function is created in the context of another expression, and hence it's a Function Expression. Needs no name and can be called immediately.
There are other ways to tell JavaScript that we mean Function Expression:
```js run
// Ways to create IIFE
(function() {
alert("Brackets around the function");
}*!*)*/!*();
(function() {
alert("Brackets around the whole thing");
}()*!*)*/!*;
*!*!*/!*function() {
alert("Bitwise NOT operator starts the expression");
}();
*!*+*/!*function() {
alert("Unary plus starts the expression");
}();
```
In all cases above we declare a Function Expression and run it immediately.
## Garbage collection
Lexical Environment objects that we've been talking about are subjects to same memory management rules as regular values.
- Usually, Lexical Environment is cleaned up after the function run. For instance:
```js
function f() {
let value1 = 123;
let value2 = 456;
}
f();
```
It's obvious that both values are unaccessible after the end of `f()`. Formally, there are no references to Lexical Environment object with them, so it gets cleaned up.
- But if there's a nested function that is still reachable after the end of `f`, then its `[[Environment]]` reference keeps the outer lexical environment alive as well:
```js
function f() {
let value = 123;
function g() {}
*!*
return g;
*/!*
}
let g = f(); // g is reachable, and keeps the outer lexical environment in memory
```
- If `f()` is called many times, and resulting functions are saved, then the corresponding Lexical Environment objects will also be retained in memory. All 3 of them in the code below:
```js
function f() {
let value = Math.random();
return function() {};
}
// 3 functions in array, every of them links to LexicalEnvrironment
// from the corresponding f() run
// LE LE LE
let arr = [f(), f(), f()];
```
- A Lexical Environment object dies when no nested functions remain that reference it. In the code below, after `g` becomes unreachable, it dies with it:
```js
function f() {
let value = 123;
function g() {}
return g;
}
let g = f(); // while g is alive
// there corresponding Lexical Environment lives
g = null; // ...and now the memory is cleaned up
```
### Real-life optimizations
As we've seen, in theory while a function is alive, all outer variabels are also retained.
But in practice, JavaScript engines try to optimize that. They analyze variable usage and if it's easy to see that an outer variable is not used -- it is removed.
**An important side effect in V8 (Chrome, Opera) is that such variable will become unavailable in debugging.**
Try running the example below with the open Developer Tools in Chrome.
When it pauses, in console type `alert(value)`.
```js run
function f() {
let value = Math.random();
function g() {
debugger; // in console: type alert( value ); No such variable!
}
return g;
}
let g = f();
g();
```
As you could see -- there is no such variable! In theory, it should be accessible, but the engine optimized it out.
That may lead to funny (if not such time-consuming) debugging issues. One of them -- we can see a same-named outer variable instead of the expected one:
```js run global
let value = "Surprise!";
function f() {
let value = "the closest value";
function g() {
debugger; // in console: type alert( value ); Surprise!
}
return g;
}
let g = f();
g();
```
```warn header="See ya!"
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 of debugger, but a special feature of V8. Maybe it will be changed sometimes.
You always can check for it by running examples on this page.
```
## The old "var"
In the very first chapter about [variables](info:variables), we mentioned three ways of variable declaration:
1. `let`
2. `const`
3. `var`
`let` and `const` behave exactly the same way in terms of Lexical Environments.
But `var` is a very different beast, that originates from very old times. It's generally not used in modern scripts, but still lurks in the old ones. If you don't plan meeting such scripts you may even skip this subsection or postpone reading it, but then there's a chance that it bites you some time.
From the first sight, `var` behaves similar to `let`. That is, declares a variable:
```js run
function sayHi() {
let phrase = "Hello"; // local variable, "var" instead of "let"
alert(phrase); // Hello
}
sayHi();
alert(phrase); // Error, phrase is not defined
```
...But here are the differences.
`var` variables are either funciton-wide or global, they are visible through blocks.
: For instance:
```js
if (true) {
let test = true; // use "var" instead of "let"
}
*!*
alert(test); // true, the variable lives after if
*/!*
```
If we used `let test` on the 2nd line, then it wouldn't be visible to `alert`. But `var` ignores code blocks, so we've got a global `test`.
The same thing for loops: `var` can not be block or loop-local:
```js
for(let i = 0; i < 10; i++) {
// ...
}
*!*
alert(i); // 10, "i" is visible after loop, it's a global variable
*/!*
```
If a code block in inside a function, then `var` becomes a function-level variable:
```js
function sayHi() {
if (true) {
let phrase = "Hello";
}
alert(phrase); // works
}
sayHi();
alert(phrase); // Error: phrase is not defined
```
As we can see, `var` pierces through `if`, `for` or other code blocks. That's because long time ago in JavaScript blocks had no Lexical Environments. And `var` is a reminiscence of that.
`var` declarations are processed when the function starts (or script starts for globals).
: In other words, `var` variables are defined from the beginning of the function.
So this code:
```js
function sayHi() {
phrase = "Hello";
alert(phrase);
*!*
let phrase;
*/!*
}
```
...Is technically the same as this:
```js
function sayHi() {
*!*
let phrase;
*/!*
phrase = "Hello";
alert(phrase);
}
```
...Or even as this (remember, code blocks are ignored):
```js
function sayHi() {
phrase = "Hello";
*!*
if (false) {
let phrase;
}
*/!*
alert(phrase);
}
```
People also call such behavior "hoisting" (raising), because all `var` are "hoisted" (raised) to the top of the function.
So in the example above, `if (false)` branch never executes, but that doesn't matter. The `var` inside it is processed in the beginning of the function.
**The important thing is that declarations are hoisted, but assignments are not.**
For instance:
```js run
function sayHi() {
alert(phrase);
*!*
let phrase = "Hello";
*/!*
}
sayHi();
```
The line `let phrase = "Hello"` has two actions in it: variable declaration `var` and assignment `=`.
The declaration is processed at the start of function execution (hoisted), but the assignment is not. So the code works essentially like this:
```js run
function sayHi() {
*!*
let phrase; // variable is declared from the top...
*/!*
alert(phrase); // undefined
*!*
phrase = "Hello"; // ...but assigned when the execution reaches this line.
*/!*
}
sayHi();
```
**In other words, all `var` variables exist, but are `undefined` at the beginning of a function.**
They are assigned later as the execution reaches assignments.
In the example above, `alert` runs without an error, because the variable `phrase` exists. But its value is not yet assigned, so it shows `undefined`.
```warn header="Why we should not use `var`"
Special behaviors of described in this section make using `var` inconvenient most of time. First, we can't create block-local variables. And hoisting just creates more space for errors. So, once again, for new scripts `var` is used exceptionally rarely.
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View file

@ -0,0 +1,171 @@
# Global object
When JavaScript was created, there was an idea of a "global object" that provides all global variables and functions. It was planned that multiple in-browser scripts would use that single global object and share variables through it.
Since then, JavaScript greatly evolved, and that idea of linking code through global variables became much less appealing. In modern JavaScript, the concept of modules too its place.
But the global object still remains in the specification.
In a browser it is named "window", for Node.JS it is "global", for other environments it may have another name.
It does two things:
1. Provides access to built-in functions and values, defined by the specification and the environment.
For instance, we can call `alert` directly or as a method of `window`:
```js run
alert("Hello");
// the same as
window.alert("Hello");
```
The same applies to other built-ins. E.g. we can use `window.Array` instead of `Array`.
2. Provides access to global Function Declarations and `var` variables. We can read them and write using its properties, for instance:
<!-- no-strict to move variables out of eval -->
```js untrusted run no-strict refresh
let phrase = "Hello";
function sayHi() {
alert(phrase);
}
// can read from window
alert( window.phrase ); // Hello (global var)
alert( window.sayHi ); // function (global function declaration)
// can write to window (creates a new sglobal variable)
window.test = 5;
alert(test); // 5
```
...But the global object does not have variables declared with `let/const`!
```js untrusted run no-strict refresh
*!*let*/!* user = "John";
alert(user); // John
alert(window.user); // undefined, don't have let
alert("user" in window); // false
```
```smart header="The global object is not a global Environment Record"
In versions of ECMAScript prior to ES-2015, there were no `let/const` variables, only `var`. And global object was used as a global Environment Record (wordings were a bit different, but that's the gist).
But starting from ES-2015, these entities are split apart. There's a global Lexical Environment with its Environment Record. And there's a global object that provides *some* of global variables.
As a practical difference, global `let/const` variables are definitively properties of the global Environment Record, but they do not exist in the global object.
Naturally, that's because the idea of a global object as a way to access "all global things" comes from ancient times. Nowadays is not considered to be a good thing. Modern language features like `let/const` do not make friends with it, but old ones are still compatible.
```
## Uses of "window"
In server-side environments like Node.JS, the `global` object is used exceptionally rarely. Probably it would be fair to say "never".
In-browser `window` is sometimes used though.
Usually, it's not a good idea to use it, but here are some examples you can meet.
1. To access exactly the global variable if the function has the local one with the same name.
```js untrusted run no-strict refresh
let user = "Global";
function sayHi() {
let user = "Local";
*!*
alert(window.user); // Global
*/!*
}
sayHi();
```
Such use is a workaround. Would be better to name variables differently, that won't require use to write the code it this way. And please note `"var"` before `user`. The trick doesn't work with `let` variables.
2. To check if a certain global variable or a builtin exists.
For instance, we want to check whether a global function `XMLHttpRequest` exists.
We can't write `if (XMLHttpRequest)`, because if there's no `XMLHttpRequest`, there will be an error (variable not defined).
But we can read it from `window.XMLHttpRequest`:
```js run
if (window.XMLHttpRequest) {
alert('XMLHttpRequest exists!')
}
```
If there is no such global function then `window.XMLHttpRequest` is just a non-existing object property. That's `undefined`, no error, so it works.
We can also write the test without `window`:
```js
if (typeof XMLHttpRequest == 'function') {
/* is there a function XMLHttpRequest? */
}
```
This doesn't use `window`, but is (theoretically) less reliable, because `typeof` may use a local XMLHttpRequest, and we want the global one.
3. To take the variable from the right window. That's probably the most valid use case.
A browser may open multiple windows and tabs. A window may also embed another one in `<iframe>`. Every browser window has its own `window` object and global variables. JavaScript allows windows that come from the same site (same protocol, host, port) to access variables from each other.
That use is a little bit beyound our scope for now, but it looks like:
```html run
<iframe src="/" id="iframe"></iframe>
<script>
alert( innerWidth ); // get innerWidth property of the current window (browser only)
alert( Array ); // get Array of the current window (javascript core builtin)
// when the iframe loads...
iframe.onload = function() {
// get width of the iframe window
*!*
alert( iframe.contentWindow.innerWidth );
*/!*
// get the builtin Array from the iframe window
*!*
alert( iframe.contentWindow.Array );
*/!*
};
</script>
```
Here, first two alerts use the current window, and the latter two take variables from `iframe` window. Can be any variables if `iframe` originates from the same protocol/host/port.
## "this" and global object
Sometimes, the value of `this` is exactly the global object. That's rarely used, but some scripts rely on that.
1. In the browser, the value of `this` in the global area is `window`:
```js run
// outside of functions
alert( this === window ); // true
```
Other, non-browser environments, may use another value for `this` in such cases.
2. When a function with `this` is called in not-strict mode, it gets the global object as `this`:
```js run no-strict
// not in strict mode (!)
function f() {
alert(this); // [object Window]
}
f(); // called without an object
```
By specification, `this` in this case must be the global object, even in non-browser environments like Node.JS. That's for compatibility with old scripts, in strict mode `this` would be `undefined`.

View file

@ -0,0 +1,13 @@
function makeCounter() {
let count = 0;
function counter() {
return count++;
}
counter.set = value => count = value;
counter.decrease = () => count--;
return counter;
}

View file

@ -0,0 +1,18 @@
function makeCounter() {
let count = 0;
// ... your code ...
}
let counter = makeCounter();
alert( counter() ); // 0
alert( counter() ); // 1
counter.set(10); // set the new count
alert( counter() ); // 10
counter.decrease(); // decrease the count by 1
alert( counter() ); // 10 (instead of 11)

View file

@ -0,0 +1,41 @@
describe("counter", function() {
it("increases from call to call", function() {
let counter = makeCounter();
assert.equal( counter(), 0 );
assert.equal( counter(), 1 );
assert.equal( counter(), 2 );
});
describe("counter.set", function() {
it("sets the count", function() {
let counter = makeCounter();
counter.set(10);
assert.equal( counter(), 10 );
assert.equal( counter(), 11 );
});
});
describe("counter.decrease", function() {
it("decreases the count", function() {
let counter = makeCounter();
counter.set(10);
assert.equal( counter(), 10 );
counter.decrease();
assert.equal( counter(), 10 );
});
});
});

View file

@ -0,0 +1,2 @@
The solution uses `count` in the local variable, but addition methods are written right into the `counter`. They share the same outer lexical environment and also can access the current `count`.

View file

@ -0,0 +1,15 @@
importance: 5
---
# Set and decrease for counter
Modify the code of `makeCounter()` so that the counter can also decrease and set the number:
- `counter()` should return the next number (as before).
- `counter.set(value)` should set the `count` to `value`.
- `counter.decrease(value)` should decrease the `count` by 1.
See the sandbox code for the complete usage example.
P.S. You can use either a closure or the function property to keep the current count. Or write both variants.

View file

@ -0,0 +1,55 @@
1. For the whole thing to work *anyhow*, the result of `sum` must be function.
2. That function must keep in memory the current value between calls.
3. According to the task, the function must become the number when used in `==`. Functions are objects, so the conversion happens as described in the chapter <info:object-toprimitive>, and we can provide our own method that returns the number.
Now the code:
```js run
function sum(a) {
let currentSum = a;
function f(b) {
currentSum += b;
return f;
}
f.toString = function() {
return currentSum;
};
return f;
}
alert( sum(1)(2) ); // 3
alert( sum(5)(-1)(2) ); // 6
alert( sum(6)(-1)(-2)(-3) ); // 0
alert( sum(0)(1)(2)(3)(4)(5) ); // 15
```
Please note that the `sum` function actually works only once. It returns function `f`.
Then, on each subsequent call, `f` adds its parameter to the sum `currentSum`, and returns itself.
**There is no recursion in the last line of `f`.**
Here is what recursion looks like:
```js
function f(b) {
currentSum += b;
return f(); // <-- recursive call
}
```
And in our case, we just return the function, without calling it:
```js
function f(b) {
currentSum += b;
return f; // <-- does not call itself, returns itself
}
```
This `f` will be used in the next call, again return itself, so many times as needed. Then, when used as a number or a string -- the `toString` returns the `currentSum`. We could also use `Symbol.toPrimitive` or `valueOf` here for the conversion.

View file

@ -0,0 +1,17 @@
importance: 2
---
# Sum with an arbitrary amount of brackets
Write function `sum` that would work like this:
```js
sum(1)(2) == 3; // 1 + 2
sum(1)(2)(3) == 6; // 1 + 2 + 3
sum(5)(-1)(2) == 6
sum(6)(-1)(-2)(-3) == 0
sum(0)(1)(2)(3)(4)(5) == 15
```
P.S. Hint: you may need to setup custom object to primitive conversion for your function.

View file

@ -0,0 +1,354 @@
# Function object, NFE
As we already know, functions in JavaScript are values.
Every value in JavaScript has the type. What type of value is a function?
In JavaScript, a function is an object.
A good way to imagine functions is as callable "action objects". We can not only call them, but also treat them as objects: add/remove properties, pass by reference etc.
## The "name" property
Function objects contain few sometimes-useable properties.
For instance, a function name is accessible as the "name" property:
```js run
function sayHi() {
alert("Hi");
}
alert(sayHi.name); // sayHi
```
What's more funny, the name-assigning logic is smart. It also sticks the right name to function that are used in assignments:
```js run
let sayHi = function() {
alert("Hi");
}
alert(sayHi.name); // sayHi (works!)
```
Also works if the assignment is done via a default value:
```js run
function f(sayHi = function() {}) {
alert(sayHi.name); // sayHi (works!)
}
f();
```
In the specification, this feature is called a "contextual name". If the function does not provide one, then in an assignment is figured out from the context.
Object methods have names too:
```js run
let user = {
sayHi() {
// ...
},
sayBye: function() {
// ...
}
}
alert(user.sayHi.name); // sayHi
alert(user.sayBye.name); // sayBye
```
There's no magic though. There are cases when there's no way to figure out the right name.
Then it's empty, like here:
```js
// function created inside array
let arr = [function() {}];
alert( arr[0].name ); // <empty string>
// the engine has no way to set up the right name, so there is none
```
In practice, most functions do have a name.
## The "length" property
There is another built-in property "length" that returns the number of function parameters, for instance:
```js run
function f1(a) {}
function f2(a, b) {}
function many(a, b, ...more) {}
alert(f1.length); // 1
alert(f2.length); // 2
alert(many.length); // 2
```
Here we can see that rest parameters are not counted.
The `length` property is sometimes used for introspection in functions that operate on other functions.
For instance, in the code below `ask` function accepts a `question` to ask and an arbitrary number of `handler` functions to call.
When a user answers, it calls the handlers. We can pass two kinds of handlers:
- A zero-argument function, then it is only called for a positive answer.
- A function with arguments, then it is called in any case and gets the answer.
The idea is that we have a simple no-arguments handler syntax for positive cases (most often variant), but allow to provide universal handlers as well.
To call `handlers` the right way, we examine the `length` property:
```js run
function ask(question, ...handlers) {
let isYes = confirm(question);
for(let handler of handlers) {
if (handler.length == 0) {
if (isYes) handler();
} else {
handler(isYes);
}
}
}
// for positive answer, both handlers are called
// for negative answer, only the second one
ask("Question?", () => alert('You said yes'), result => alert(result));
```
This is a particular case of so-called [polymorphism](https://en.wikipedia.org/wiki/Polymorphism_(computer_science)) -- treating arguments differently depending on their type or, in our case depending on the `length`. The idea does have a use in JavaScript libraries.
## Custom properties
We can also add properties of our own.
Here we add the `counter` property to track the total calls count:
```js run
function sayHi() {
alert("Hi");
*!*
// let's count how many times we run
sayHi.counter++;
*/!*
}
sayHi.counter = 0; // initial value
sayHi(); // Hi
sayHi(); // Hi
alert( `Called ${sayHi.counter} times` ); // Called 2 times
```
```warn header="A property is not a variable"
A property assigned to a function like `sayHi.counter = 0` does *not* define a local variable `counter` inside it. In other words, a property `counter` and a variable `let counter` are two unrelated things.
We can treat a function as an object, store properties in it, but that has no effect on its execution. Variables never use function properties and vise versa. These are just parallel words.
```
Function properties can replace the closure sometimes. For instance, we can rewrite the counter example from the chapter <info:closure> to use a function property:
```js run
function makeCounter() {
// instead of:
// let count = 0
function counter() {
return counter.count++;
};
counter.count = 0;
return counter;
}
let counter = makeCounter();
alert( counter() ); // 0
alert( counter() ); // 1
```
The `count` is now stored in the function directly, not in its outer Lexical Environment.
Is it worse or better than using the closure?
The main difference is that if the value of `count` lives in an outer variable, then an external code is unable to access it. Only nested functions may modify it. And if it's bound to function, then such thing is possible:
```js run
function makeCounter() {
function counter() {
return counter.count++;
};
counter.count = 0;
return counter;
}
let counter = makeCounter();
*!*
counter.count = 10;
alert( counter() ); // 10
*/!*
```
So it depends on our aims which variant to choose.
## Named Function Expression
Named Function Expression or, shortly, NFE, is a term for Function Expressions that have a name.
For instance, let's take an ordinary Function Expression:
```js
let sayHi = function(who) {
alert(`Hello, ${who}`);
};
```
...And add a name to it:
```js
let sayHi = function *!*func*/!*(who) {
alert(`Hello, ${who}`);
};
```
Did we do anything sane here? What's the role of that additional `"func"` name?
First let's note, that we still have a Function Expression. Adding the name `"func"` after `function` did not make it a Function Declaration, because it is still created as a part of an assignment expression.
Adding such a name also did not break anything.
The function is still available as `sayHi()`:
```js run
let sayHi = function *!*func*/!*(who) {
alert(`Hello, ${who}`);
};
sayHi("John"); // Hello, John
```
There are two special things about the name `func`:
1. It allows to reference the function from inside itself.
2. It is not visible outside of the function.
For instance, the function `sayHi` below re-calls itself with `"Guest"` if no `who` is provided:
```js run
let sayHi = function *!*func*/!*(who) {
if (who) {
alert(`Hello, ${who}`);
} else {
*!*
func("Guest"); // use func to re-call itself
*/!*
}
};
sayHi(); // Hello, Guest
// But this won't work:
func(); // Error, func is not defined (not visible outside of the function)
```
Why do we use `func`? Maybe just use `sayHi` for the nested call?
Actually, in most cases we can:
```js
let sayHi = function(who) {
if (who) {
alert(`Hello, ${who}`);
} else {
*!*
sayHi("Guest");
*/!*
}
};
```
The problem with that code is that the value of `sayHi` may change. The function may go to another variable, and the code will start to give errors:
```js run
let sayHi = function(who) {
if (who) {
alert(`Hello, ${who}`);
} else {
*!*
sayHi("Guest"); // Error: sayHi is not a function
*/!*
}
};
let welcome = sayHi;
sayHi = null;
welcome(); // Error, the nested sayHi call doesn't work any more!
```
That happens because the function takes `sayHi` from its outer lexical environment. There's no local `sayHi`, so the outer variable is used. And at the moment of the call that outer `sayHi` is `null`.
The optional name which we can put into the Function Expression is exactly meant to solve this kind of problems.
Let's use it to fix the code:
```js run
let sayHi = function *!*func*/!*(who) {
if (who) {
alert(`Hello, ${who}`);
} else {
*!*
func("Guest"); // Now all fine
*/!*
}
};
let welcome = sayHi;
sayHi = null;
welcome(); // Hello, Guest (nested call works)
```
Now it works, because the name `"func"` is function-local. It is not taken from outside (and not visible there). The specification guarantees that it always references the current function.
The outer code still has it's variable `sayHi` or `welcome` later. And `func` is an "internal function name", how it calls itself privately.
```smart header="There's no such thing for Function Declaration"
The "internal name" feature described here is only available for Function Expressions, not to Function Declarations. For Function Declarations, there's just no syntax possibility to add a one more "internal" name.
Sometimes, when we need a reliable internal name, it's the reason to rewrite a Function Declaration to Named Function Expression form.
```
## Summary
Functions are objects.
Here we covered their properties:
- `name` -- the function name. Exists not only when given in the function definition, but also for assignments and object properties.
- `length` -- the number of arguments in the function definition. Rest parameters are not counted.
If the function is declared as a Function Expression (not in the main code flow), and it carries the name, then it is called Named Function Expression. The name can be used inside to reference itself, for recursive calls or such.
Also, functions may carry additional properties. Many well-known JavaScript libraries make a great use of this feature.
They create a "main" function and attach many other "helper" functions to it. For instance, the [jquery](https://jquery.com) library creates a function named `$`. The [lodash](https://lodash.com) library creates a function `_`. And then adds `_.clone`, `_.keyBy` and other properties to (see the [docs](https://lodash.com/docs) when you want learn more about them). Actually, they do it to less pollute the global space, so that a single library gives only one global variable. That lowers the chance of possible naming conflicts.
So, a function can do a useful job by itself and also carry a bunch of other functionality in properties.

View file

@ -0,0 +1,139 @@
# The "new Function" syntax
There's one more way to create a function. It's rarely used, but sometimes there's no alternative.
[cut]
## The syntax
The syntax for creating a function:
```js
let func = new Function('a', 'b', 'return a + b');
```
All arguments of `new Function` are strings. Parameters go first, and the body is the last.
For instance:
```js run
let sum = new Function('arg1', 'arg2', 'return arg1 + arg2');
alert( sum(1, 2) ); // 3
```
If there are no arguments, then there will be only body:
```js run
let sayHi = new Function('alert("Hello")');
sayHi(); // Hello
```
The major difference from other ways we've seen -- the function is created literally from a string, that is passed at run time.
All previous declarations required us, programmers, to write the function code in the script.
But `new Function` allows to turn any string into a function, for example we can receive a new function from the server and then execute it:
```js
let str = ... receive the code from the server dynamically ...
let func = new Function(str);
func();
```
It is used in very specific cases, like when we receive the code from the server, or to dynamically compile a function from a template. The need for that usually arises at advanced stages of development.
## The closure
Usually, a function remembers where it was born in the special property `[[Environment]]`. It references the Lexical Environment from where it's created.
But when a function is created using `new Function`, its `[[Environment]]` references not the current Lexical Environment, but instead the global one.
```js run
function getFunc() {
let value = "test";
*!*
let func = new Function('alert(value)');
*/!*
return func;
}
getFunc()(); // error: value is not defined
```
Compare it with the regular behavior:
```js run
function getFunc() {
let value = "test";
*!*
let func = function() { alert(value); };
*/!*
return func;
}
getFunc()(); // *!*"test"*/!*, from the Lexical Environment of getFunc
```
This special feature of `new Function` looks strange, but appears very useful in practice.
Imagine that we really have to create a function from the string. The code of that function is not known at the time of writing the script (that's why we don't use regular functions), but will be known in the process of execution. We may receive it from the server or from another source.
Our new function needs to interact with the main script.
Maybe we want it to be able to access outer local variables?
But the problem is that before JavaScript is published to production, it's compressed using a *minifier* -- a special program that shrinks code by removing extra comments, spaces and -- what's important, renames local variables into shorter ones.
For instance, if a function has `let userName`, minifier replaces it `let a` (or another letter if this one is occupied), and does it everywhere. That's usually a safe thing to do, because the variable is local, nothing outside the function can access it. And inside the function minifier replaces every mention of it. Minifiers are smart, they analyze the code structure, not just find-and-replace, so that's ok.
...But if `new Function` could access outer variables, then it would be unable to find `userName`.
**Even if we could access outer lexical environment in `new Function`, we would have problems with minifiers.**
The "special feature" of `new Function` saves us from mistakes.
And it enforces better code. If we need to pass something to a function, created by `new Function`, we should pass it explicitly as arguments.
The "sum" function actually does that right:
```js run
*!*
let sum = new Function('a', 'b', ' return a + b; ');
*/!*
let a = 1, b = 2;
*!*
// outer values are passed as arguments
alert( sum(a, b) ); // 3
*/!*
```
## Summary
The syntax:
```js
let func = new Function(arg1, arg2, ..., body);
```
For historical reasons, arguments can also be given as a comma-separated list.
These three mean the same:
```js
new Function('a', 'b', ' return a + b; '); // basic syntax
new Function('a,b', ' return a + b; '); // comma-separated
new Function('a , b', ' return a + b; '); // comma-separated with spaces
```
Functions created with `new Function`, have `[[Environment]]` referencing the global Lexical Environment, not the outer one. Hence, they can not use outer variables. But that's actually good, because it saves us from errors. Explicit parameters passing is a much better thing architecturally and has no problems with minifiers.

View file

@ -0,0 +1,42 @@
Using `setInterval`:
```js run
function printNumbers(from, to) {
let current = from;
let timerId = setInterval(function() {
alert(current);
if (current == to) {
clearInterval(timerId);
}
current++;
}, 1000);
}
// usage:
printNumbers(5, 10);
```
Using recursive `setTimeout`:
```js run
function printNumbers(from, to) {
let current = from;
setTimeout(function go() {
alert(current);
if (current < to) {
setTimeout(go, 1000);
}
current++;
}, 1000);
}
// usage:
printNumbers(5, 10);
```
Note that in both solutions, there is an initial delay before the first output. Sometimes we need to add a line to make the first output immediately, that's easy to do.

View file

@ -0,0 +1,13 @@
importance: 5
---
# Output every second
Write a function `printNumbers(from, to)` that outputs a number every second, starting from `from` and ending with `two`.
Make two variants of the solution.
1. Using `setInterval`.
2. Using recursive `setTimeout`.

View file

@ -0,0 +1,23 @@
```js run
let i = 0;
let start = Date.now();
let timer = setInterval(count, 0);
function count() {
for(let j = 0; j < 1000000; j++) {
i++;
}
if (i == 1000000000) {
alert("Done in " + (Date.now() - start) + 'ms');
cancelInterval(timer);
}
}
```

View file

@ -0,0 +1,32 @@
importance: 4
---
# Rewrite setTimeout with setInterval
Here's the function that uses nested `setTimeout` to split a job into pieces.
Rewrite it to `setInterval`:
```js run
let i = 0;
let start = Date.now();
function count() {
if (i == 1000000000) {
alert("Done in " + (Date.now() - start) + 'ms');
} else {
setTimeout(count, 0);
}
// a piece of heavy job
for(let j = 0; j < 1000000; j++) {
i++;
}
}
count();
```

View file

@ -0,0 +1,15 @@
Any `setTimeout` will run only after the current code has finished.
The `i` will be the last one: `100000000`.
```js run
let i = 0;
setTimeout(() => alert(i), 100); // 100000000
// assume that the time to execute this function is >100ms
for(let j = 0; j < 100000000; j++) {
i++;
}
```

View file

@ -0,0 +1,27 @@
importance: 5
---
# What will setTimeout show?
In the code below there's a `setTimeout` call scheduled, then a heavy calculation is run, that takes more than 100ms to finish.
When the scheduled function will run?
1. After the loop.
2. Before the loop.
3. In the beginning of the loop.
What `alert` is going to show?
```js
let i = 0;
setTimeout(() => alert(i), 100); // ?
// assume that the time to execute this function is >100ms
for(let j = 0; j < 100000000; j++) {
i++;
}
```

View file

@ -0,0 +1,463 @@
# Scheduling: setTimeout and setInterval
We may decide to execute a function not right now, but at a certain time later. That's called "scheduling a call".
There are two methods for it:
- `setTimeout` allows to run a function once after the given interval of time.
- `setInterval` allows to run a function regularly with the given interval between the runs.
These methods are not a part of JavaScript specification. But most environments have the internal scheduler and provide these methods. In particular, they are supported in all browsers and Node.JS.
[cut]
## setTimeout
The syntax:
```js
let timerId = setTimeout(func|code, delay[, arg1, arg2...])
```
Parameters:
`func|code`
: Function or a string of code to execute.
Usually, that's a function. For historical reasons, a string of code can be passed, but that's not recommended.
`delay`
: The delay before run, in milliseconds (1000 ms = 1 second).
`arg1`, `arg2`...
: Arguments for the function (not supported in IE9-)
For instance, this code calls `sayHi()` after one second:
```js run
function sayHi() {
alert('Hello');
}
*!*
setTimeout(sayHi, 1000);
*/!*
```
With arguments:
```js run
function sayHi(phrase, who) {
alert( phrase + ', ' + who );
}
*!*
setTimeout(sayHi, 1000, "John", "Hello"); // Hello, John
*/!*
```
If the first argument is a string, then JavaScript creates a function from it.
So, this will also work:
```js run no-beautify
setTimeout("alert('Hello')", 1000);
```
But using strings is not recommended, use functions instead of them, like this:
```js run no-beautify
setTimeout(() => alert('Hello'), 1000);
```
````smart header="Pass a function, but don't run it"
Novice developers sometimes make a mistake by adding brackets `()` after the function:
```js
// wrong!
setTimeout(sayHi(), 1000);
```
That doesn't work, because `setTimeout` expects a reference to function. And here `sayHi()` runs the function, and the *result of its execution* is passed to `setTimeout`. In our case the result of `sayHi()` is `undefined` (the function returns nothing), so nothing is scheduled.
````
### Canceling with clearTimeout
A call to `setTimeout` returns a "timer identifier" `timerId` that we can use to cancel the execution.
The syntax to cancel:
```js
let timerId = setTimeout(...);
clearTimeout(timerId);
```
In the code below we schedule the function and then cancel it (changed our mind). As a result, nothing happens:
```js run no-beautify
let timerId = setTimeout(() => alert("never happens"), 1000);
alert(timerId); // timer identifier
clearTimeout(timerId);
alert(timerId); // same identifier (doesn't become null after canceling)
```
As we can see from `alert` output, in a browser the timer identifier is a number. In other environments, that can be something else. For instance, Node.JS returns a timer object with additional methods.
Again, there is no universal specification for these methods, so that's fine.
For browsers, timers are described in the [timers section](https://www.w3.org/TR/html5/webappapis.html#timers) of HTML5 standard.
## setInterval
Method `setInterval` has the same syntax as `setTimeout`:
```js
let timerId = setInterval(func|code, delay[, arg1, arg2...])
```
All arguments have the same meaning. But unlike `setTimeout` it runs the function not only once, but regularly after the given interval of time.
To stop further calls, we should call `clearInterval(timerId)`.
The following example will show the message every 2 seconds. After 5 seconds, the output is stopped:
```js run
// repeat with the interval of 2 seconds
let timerId = setInterval(() => alert('tick'), 2000);
// after 5 seconds stop
setTimeout(() => { clearInterval(timerId); alert('stop'); }, 5000);
```
```smart header="Modal windows freeze time in Chrome/Opera/Safari"
In browsers IE and Firefox the internal timer continues "ticking" while showing `alert/confirm/prompt`, but in Chrome, Opera and Safari the internal timer becomes "frozen".
So if you run the code above and don't dismiss the `alert` window for some time, then in Firefox/IE next `alert` will be shown immediately as you do it (2 seconds passed from the previous invocation), and in Chrome/Opera/Safari -- after 2 more seconds (timer did not tick during the `alert`).
```
## Recursive setTimeout
There are two ways of running something regularly.
One is `setInterval`. The other one is a recursive `setTimeout`, like this:
```js
/** instead of:
let timerId = setInterval(() => alert('tick'), 2000);
*/
let timerId = setTimeout(function tick() {
alert('tick');
*!*
timerId = setTimeout(tick, 2000); // (*)
*/!*
}, 2000);
```
The `setTimeout` above schedules next call right at the end of the current one `(*)`.
Recursive `setTimeout` is more flexible method than `setInterval`. This way the next call may be scheduled differently, depending on the results of the current one.
For instance, we need to write a service that each 5 seconds sends a request to server asking for data, but in case if the server is overloaded, it should increase the interval to 10, 20, 60 seconds...
Here's the pseudocode:
```js
let delay = 5000;
let timerId = setTimeout(function request() {
...send request...
if (request failed due to server overload) {
delay *= 2;
}
timerId = setTimeout(tick, delay);
}, delay);
```
And if we regulary have CPU-hungry tasks, then we can measure the time taken by the execition and plan the next call sooner or later.
**Recursive `setTimeout` guarantees a delay before the executions, `setInterval` -- does not.**
Let's compare two code fragments. The first one uses `setInterval`:
```js
let i = 1;
setInterval(function() {
func(i);
}, 100);
```
The second one uses recursive `setTimeout`:
```js
let i = 1;
setTimeout(function run() {
func(i);
setTimeout(run, 100);
}, 100);
```
For `setInterval` the internal scheduler will run `func(i)` every 100ms:
![](setinterval-interval.png)
Did you notice?...
**The real delay between `func` calls for `setInterval` is less than in the code!**
That's natural, because the time taken by `func` execution "consumes" a part of the interval.
It is possible that `func` execution turns out to be longer than we expected and takes more than 100ms.
In this case the engine waits for `func` to complete, then checks the scheduler and if the time is up, then runs it again *immediately*.
In the edge case, if the function always executes longer than `delay` ms, then the calls will happen without pause at all.
And here is the picture for recursive `setTimeout`:
![](settimeout-interval.png)
**Recursive `setTimeout` guarantees the fixed delay (here 100ms).**
That's because a new call is planned at the end of the previous one.
````smart header="Garbage collection"
When a function is passed in `setInterval/setTimeout`, an internal reference is created to it and saved in the scheduler. It prevents the function form being garbage collected, even if there are no other references to it.
```js
// the function stays in memory until the scheduler calls it
setTimeout(function() {...}, 100);
```
For `setInterval` the function stays in memory until `cancelInterval` is called.
There's a side-effect. A function references the outer lexical environment, so, while it lives, outer variables live too. They may take much more memory than the function itself. So when we don't need the scheduled function any more, it's better to cancel it, even if it's very small.
````
## setTimeout(...,0)
There's a special use case: `setTimeout(func, 0)`.
This schedules the execution of `func` as soon as possible. But scheduler will invoke it only after the current code is complete.
So the function is scheduled to run "right after" the current code. In other words, *asynchronously*.
For instance, this outputs "Hello", then immediately "World":
```js run
setTimeout(() => alert("World"), 0);
alert("Hello");
```
The first line "puts the call into calendar after 0ms". But the scheduler will only "check the calendar" after the current code is complete, so `"Hello"` is first, and `"World"` -- after it.
### Splitting CPU-hungry tasks
There's a trick to split CPU-hungry task using `setTimeout`.
For instance, syntax highlighting script (used to colorize code examples on this page) is quite CPU-heavy. To hightlight the code, it performs the analysis, creates many colored elements, adds them to the document -- for a big text that takes a lot. It may even cause the browser to "hang", that's unacceptable.
So we can split the long text to pieces. First 100 lines, then plan another 100 lines using `setTimeout(...,0)`, and so on.
For clarity, let's take a simpler example for consideration. We have a function to count from `1` to `1000000000`.
If you run it, the CPU will hang. For server-side JS that's clearly noticeable, and if you are running it in-browser, then try to click other buttons on the page -- you'll see that whole JavaScript actually is paused, no other actions work until it finishes.
```js run
let i = 0;
let start = Date.now();
function count() {
// do a heavy job
for(let j = 0; j < 1e9; j++) {
i++;
}
alert("Done in " + (Date.now() - start) + 'ms');
}
count();
```
The browser may even show "the script takes too long" warning (but hopefully won't, the number is not very big).
Let's split the job using the nested `setTimeout`:
```js run
let i = 0;
let start = Date.now();
function count() {
// do a piece of the heavy job (*)
do {
i++;
} while (i % 1e6 != 0);
if (i == 1e9) {
alert("Done in " + (Date.now() - start) + 'ms');
} else {
setTimeout(count, 0); // schedule the new call (**)
}
}
count();
```
Now the browser UI is fully functional during the "counting" process.
We do a part of the job `(*)`:
1. First run: `i=1...1000000`.
2. Second run: `i=1000001..2000000`.
3. ...and so on, the `while` checks if `i` is evenly divided by `100000`.
Then the next call is scheduled in `(*)` if we're not done yet.
Pauses between `count` executions provide just enough "breath" for the JavaScript engine to do something else, to react on other user actions.
The notable thing is that both variants: with and without splitting the job by `setInterval` -- are comparable in speed. There's no much difference in the overall counting time.
To make them closer let's make an improvement.
We'll move the scheduling in the beginning of the `count()`:
```js run
let i = 0;
let start = Date.now();
function count() {
// move the scheduling at the beginning
if (i < 1e9 - 1e6) {
setTimeout(count, 0); // schedule the new call
}
do {
i++;
} while (i % 1e6 != 0);
if (i == 1e9) {
alert("Done in " + (Date.now() - start) + 'ms');
}
}
count();
```
Now when we start to `count()` and know that we'll need to `count()` more -- we schedule that immediately, before doing the job.
If you run it, easy to notice that it takes significantly less time.
````smart header="Minimal delay of nested timers in-browser"
In the browser, there's a limitation of how often nested timers can run. The [HTML5 standard](https://www.w3.org/TR/html5/webappapis.html#timers) says: "after five nested timers..., the interval is forced to be at least four milliseconds.".
Let's demonstrate what it means by the example below. The `setTimeout` call in it re-schedules itself after `0ms`. Each call remembers the real time from the previous one in the `times` array. What the real delays look like? Let's see:
```js run
let start = Date.now();
let times = [];
setTimeout(function run() {
times.push(Date.now() - start); // remember delay from the previous call
if (start + 100 < Date.now()) alert(times); // show the delays after 100ms
else setTimeout(run, 0); // else re-schedule
}, 0);
// an example of the output:
// 1,1,1,1,9,15,20,24,30,35,40,45,50,55,59,64,70,75,80,85,90,95,100
```
First timers run immediately (just as written in the spec), and then the delay comes into play and we see `9, 15, 20, 24...`.
That limitation comes from ancient times and many scripts rely on it, so it exists for historical reasons.
For server-side JavaScript, that limitation does not exist, and there exist other ways to schedule an immediate asynchronous job, like [process.nextTick](https://nodejs.org/api/process.html) and [setImmediate](https://nodejs.org/api/timers.html) for Node.JS. So the notion is browser-specific only.
````
### Allowing the browser to render
Another benefit for in-browser scripts is that they can show a progress bar or something to the user. That's because the browser usually does all "repainting" after the script is complete.
So if we do a single huge function then even it changes something, the changes are not reflected in the document till it finishes.
Here's the demo:
```html run
<div id="progress"></div>
<script>
let i = 0;
function count() {
for(let j = 0; j < 1e6; j++) {
i++;
// put the current i into the <div>
// (we'll talk more about innerHTML in the specific chapter, should be obvious here)
progress.innerHTML = i;
}
}
count();
</script>
```
If you run it, the changes to `i` will show up after the whole count finishes.
And if we use `setTimeout` to split it into pieces then changes are applied in-between the runs, so this looks better:
```html run
<div id="progress"></div>
<script>
let i = 0;
function count() {
// do a piece of the heavy job (*)
do {
i++;
progress.innerHTML = i;
} while (i % 1e3 != 0);
if (i < 1e9) {
setTimeout(count, 0);
}
}
count();
</script>
```
Now the `<div>` shows increasing values of `i`.
## Summary
- Methods `setInterval(func, delay, ...args)` and `setTimeout(func, delay, ...args)` allow to run the `func` regularly/once after `delay` milliseconds.
- To cancel the execution, we should call `clearInterval/clearTimeout` with the value returned by `setInterval/setTimeout`.
- Nested `setTimeout` calls is a more flexible alternative to `setInterval`. Also they can guarantee the minimal time *between* the executions.
- Zero-timeout scheduling `setTimeout(...,0)` is used to schedule the call "as soon as possible, but after the current code is complete".
Some use cases of `setTimeout(...,0)`:
- To split CPU-hungry tasks into pieces, so that the script doesn't "hang"
- To let the browser do something else while the process is going on (paint the progress bar).
Please note that all scheduling methods do not *guarantee* the exact delay. We should not rely on that in the scheduled code.
For example, the in-browser timer may slow down for a lot of reasons:
- The CPU is overloaded.
- The browser tab is in the background mode.
- The laptop is on battery.
All that may decrease the minimal timer resolution (the minimal delay) to 300ms or even 1000ms depending on the browser and settings.

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View file

@ -0,0 +1,11 @@
function spy(func) {
function wrapper(...args) {
wrapper.calls.push(args);
return func.apply(this, arguments);
}
wrapper.calls = [];
return wrapper;
}

Some files were not shown because too many files have changed in this diff Show more