(253-1) (that's `9007199254740991`), or less than -(-253-1) for negatives. It's a technical limitation caused by their internal representation.
+
+For most purposes that's quite enough, but sometimes we need really big numbers, e.g. for cryptography or microsecond-precision timestamps.
+
+`BigInt` type was recently added to the language to represent integers of arbitrary length.
+
+A `BigInt` value is created by appending `n` to the end of an integer:
+
+```js
+// the "n" at the end means it's a BigInt
+const bigInt = 1234567890123456789012345678901234567890n;
+```
+
+As `BigInt` numbers are rarely needed, we don't cover them here, but devoted them a separate chapter `Hello`.
-Double and single quotes are "simple" quotes. There's no difference between them in JavaScript.
+Double and single quotes are "simple" quotes. There's practically no difference between them in JavaScript.
Backticks are "extended functionality" quotes. They allow us to embed variables and expressions into a string by wrapping them in `${…}`, for example:
@@ -102,12 +125,12 @@ alert( "the result is ${1 + 2}" ); // the result is ${1 + 2} (double quotes do n
We'll cover strings more thoroughly in the chapter a > b, a < b.
- Greater/less than or equals: a >= b, a <= b.
-- Equals: `a == b` (please note the double equals sign `=`. A single symbol `a = b` would mean an assignment).
-- Not equals. In maths the notation is ≠, but in JavaScript it's written as an assignment with an exclamation sign before it: a != b.
+- Equals: `a == b`, please note the double equality sign `=` means the equality test, while a single one `a = b` means an assignment.
+- Not equals. In maths the notation is ≠, but in JavaScript it's written as a != b.
+
+In this article we'll learn more about different types of comparisons, how JavaScript makes them, including important peculiarities.
## Boolean is the result
-Like all other operators, a comparison returns a value. In this case, the value is a boolean.
+All comparison operators return a boolean value:
- `true` -- means "yes", "correct" or "the truth".
- `false` -- means "no", "wrong" or "not the truth".
diff --git a/1-js/02-first-steps/11-logical-operators/2-alert-or/solution.md b/1-js/02-first-steps/11-logical-operators/2-alert-or/solution.md
index 8f4d664e8..f85b56366 100644
--- a/1-js/02-first-steps/11-logical-operators/2-alert-or/solution.md
+++ b/1-js/02-first-steps/11-logical-operators/2-alert-or/solution.md
@@ -6,7 +6,7 @@ alert( alert(1) || 2 || alert(3) );
The call to `alert` does not return a value. Or, in other words, it returns `undefined`.
-1. The first OR `||` evaluates it's left operand `alert(1)`. That shows the first message with `1`.
+1. The first OR `||` evaluates its left operand `alert(1)`. That shows the first message with `1`.
2. The `alert` returns `undefined`, so OR goes on to the second operand searching for a truthy value.
3. The second operand `2` is truthy, so the execution is halted, `2` is returned and then shown by the outer alert.
diff --git a/1-js/02-first-steps/11-logical-operators/9-check-login/ifelse_task.svg b/1-js/02-first-steps/11-logical-operators/9-check-login/ifelse_task.svg
index cbc8c7840..ca3e0aead 100644
--- a/1-js/02-first-steps/11-logical-operators/9-check-login/ifelse_task.svg
+++ b/1-js/02-first-steps/11-logical-operators/9-check-login/ifelse_task.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/1-js/02-first-steps/11-logical-operators/article.md b/1-js/02-first-steps/11-logical-operators/article.md
index 25f8ff7f5..f45cbe456 100644
--- a/1-js/02-first-steps/11-logical-operators/article.md
+++ b/1-js/02-first-steps/11-logical-operators/article.md
@@ -90,10 +90,10 @@ For instance:
```js run
alert( 1 || 0 ); // 1 (1 is truthy)
-alert( true || 'no matter what' ); // (true is truthy)
alert( null || 1 ); // 1 (1 is the first truthy value)
alert( null || 0 || 1 ); // 1 (the first truthy value)
+
alert( undefined || null || 0 ); // 0 (all falsy, returns the last value)
```
@@ -101,53 +101,40 @@ This leads to some interesting usage compared to a "pure, classical, boolean-onl
1. **Getting the first truthy value from a list of variables or expressions.**
- Imagine we have a list of variables which can either contain data or be `null/undefined`. How can we find the first one with data?
+ For instance, we have `firstName`, `lastName` and `nickName` variables, all optional.
- We can use OR `||`:
+ Let's use OR `||` to choose the one that has the data and show it (or `anonymous` if nothing set):
```js run
- let currentUser = null;
- let defaultUser = "John";
+ let firstName = "";
+ let lastName = "";
+ let nickName = "SuperCoder";
*!*
- let name = currentUser || defaultUser || "unnamed";
+ alert( firstName || lastName || nickName || "Anonymous"); // SuperCoder
*/!*
-
- alert( name ); // selects "John" – the first truthy value
```
- If both `currentUser` and `defaultUser` were falsy, `"unnamed"` would be the result.
-2. **Short-circuit evaluation.**
-
- Operands can be not only values, but arbitrary expressions. OR evaluates and tests them from left to right. The evaluation stops when a truthy value is reached, and the value is returned. This process is called "a short-circuit evaluation" because it goes as short as possible from left to right.
+ If all variables were falsy, `Anonymous` would show up.
- This is clearly seen when the expression given as the second argument has a side effect like a variable assignment.
+2. **Short-circuit evaluation.**
- In the example below, `x` does not get assigned:
+ Another feature of OR `||` operator is the so-called "short-circuit" evaluation.
- ```js run no-beautify
- let x;
+ It means that `||` processes its arguments until the first truthy value is reached, and then the value is returned immediately, without even touching the other argument.
- *!*true*/!* || (x = 1);
+ That importance of this feature becomes obvious if an operand isn't just a value, but an expression with a side effect, such as a variable assignment or a function call.
- alert(x); // undefined, because (x = 1) not evaluated
- ```
-
- If, instead, the first argument is `false`, `||` evaluates the second one, thus running the assignment:
+ In the example below, only the second message is printed:
```js run no-beautify
- let x;
-
- *!*false*/!* || (x = 1);
-
- alert(x); // 1
+ *!*true*/!* || alert("not printed");
+ *!*false*/!* || alert("printed");
```
- An assignment is a simple case. There may be side effects, that won't show up if the evaluation doesn't reach them.
+ In the first line, the OR `||` operator stops the evaluation immediately upon seeing `true`, so the `alert` isn't run.
- As we can see, such a use case is a "shorter way of doing `if`". The first operand is converted to boolean. If it's false, the second one is evaluated.
-
- Most of time, it's better to use a "regular" `if` to keep the code easy to understand, but sometimes this can be handy.
+ Sometimes, people use this feature to execute commands only if the condition on the left part is falsy.
## && (AND)
@@ -236,7 +223,8 @@ The precedence of AND `&&` operator is higher than OR `||`.
So the code `a && b || c && d` is essentially the same as if the `&&` expressions were in parentheses: `(a && b) || (c && d)`.
````
-Just like OR, the AND `&&` operator can sometimes replace `if`.
+````warn header="Don't replace `if` with || or &&"
+Sometimes, people use the AND `&&` operator as a "shorter to write `if`".
For instance:
@@ -253,14 +241,12 @@ So we basically have an analogue for:
```js run
let x = 1;
-if (x > 0) {
- alert( 'Greater than zero!' );
-}
+if (x > 0) alert( 'Greater than zero!' );
```
-The variant with `&&` appears shorter. But `if` is more obvious and tends to be a little bit more readable.
+Although, the variant with `&&` appears shorter, `if` is more obvious and tends to be a little bit more readable. So we recommend using every construct for its purpose: use `if` if we want if and use `&&` if we want AND.
+````
-So we recommend using every construct for its purpose: use `if` if we want if and use `&&` if we want AND.
## ! (NOT)
diff --git a/1-js/02-first-steps/12-nullish-coalescing-operator/article.md b/1-js/02-first-steps/12-nullish-coalescing-operator/article.md
new file mode 100644
index 000000000..f0f6687a3
--- /dev/null
+++ b/1-js/02-first-steps/12-nullish-coalescing-operator/article.md
@@ -0,0 +1,130 @@
+# Nullish coalescing operator '??'
+
+[recent browser="new"]
+
+The nullish coalescing operator `??` provides a short syntax for selecting a first "defined" variable from the list.
+
+The result of `a ?? b` is:
+- `a` if it's not `null` or `undefined`,
+- `b`, otherwise.
+
+So, `x = a ?? b` is a short equivalent to:
+
+```js
+x = (a !== null && a !== undefined) ? a : b;
+```
+
+Here's a longer example.
+
+Imagine, we have a user, and there are variables `firstName`, `lastName` or `nickName` for their first name, last name and the nick name. All of them may be undefined, if the user decided not to enter any value.
+
+We'd like to display the user name: one of these three variables, or show "Anonymous" if nothing is set.
+
+Let's use the `??` operator to select the first defined one:
+
+```js run
+let firstName = null;
+let lastName = null;
+let nickName = "Supercoder";
+
+// show the first not-null/undefined value
+*!*
+alert(firstName ?? lastName ?? nickName ?? "Anonymous"); // Supercoder
+*/!*
+```
+
+## Comparison with ||
+
+The OR `||` operator can be used in the same way as `??`. Actually, we can replace `??` with `||` in the code above and get the same result, as it was described in the [previous chapter](info:logical-operators#or-finds-the-first-truthy-value).
+
+The important difference is that:
+- `||` returns the first *truthy* value.
+- `??` returns the first *defined* value.
+
+This matters a lot when we'd like to treat `null/undefined` differently from `0`.
+
+For example, consider this:
+
+```js
+height = height ?? 100;
+```
+
+This sets `height` to `100` if it's not defined.
+
+Let's compare it with `||`:
+
+```js run
+let height = 0;
+
+alert(height || 100); // 100
+alert(height ?? 100); // 0
+```
+
+Here, `height || 100` treats zero height as unset, same as `null`, `undefined` or any other falsy value. So the result is `100`.
+
+The `height ?? 100` returns `100` only if `height` is exactly `null` or `undefined`. So the `alert` shows the height value `0` "as is".
+
+Which behavior is better depends on a particular use case. When zero height is a valid value, then `??` is preferrable.
+
+## Precedence
+
+The precedence of the `??` operator is rather low: `7` in the [MDN table](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence#Table).
+
+So `??` is evaluated after most other operations, but before `=` and `?`.
+
+If we need to choose a value with `??` in a complex expression, then consider adding parentheses:
+
+```js run
+let height = null;
+let width = null;
+
+// important: use parentheses
+let area = (height ?? 100) * (width ?? 50);
+
+alert(area); // 5000
+```
+
+Otherwise, if we omit parentheses, `*` has the higher precedence than `??` and would run first.
+
+That would work be the same as:
+
+```js
+// probably not correct
+let area = height ?? (100 * width) ?? 50;
+```
+
+There's also a related language-level limitation.
+
+**Due to safety reasons, it's forbidden to use `??` together with `&&` and `||` operators.**
+
+The code below triggers a syntax error:
+
+```js run
+let x = 1 && 2 ?? 3; // Syntax error
+```
+
+The limitation is surely debatable, but it was added to the language specification with the purpose to avoid programming mistakes, as people start to switch to `??` from `||`.
+
+Use explicit parentheses to work around it:
+
+```js run
+*!*
+let x = (1 && 2) ?? 3; // Works
+*/!*
+
+alert(x); // 2
+```
+
+## Summary
+
+- The nullish coalescing operator `??` provides a short way to choose a "defined" value from the list.
+
+ It's used to assign default values to variables:
+
+ ```js
+ // set height=100, if height is null or undefined
+ height = height ?? 100;
+ ```
+
+- The operator `??` has a very low precedence, a bit higher than `?` and `=`.
+- It's forbidden to use it with `||` or `&&` without explicit parentheses.
diff --git a/1-js/02-first-steps/12-while-for/1-loop-last-value/solution.md b/1-js/02-first-steps/13-while-for/1-loop-last-value/solution.md
similarity index 100%
rename from 1-js/02-first-steps/12-while-for/1-loop-last-value/solution.md
rename to 1-js/02-first-steps/13-while-for/1-loop-last-value/solution.md
diff --git a/1-js/02-first-steps/12-while-for/1-loop-last-value/task.md b/1-js/02-first-steps/13-while-for/1-loop-last-value/task.md
similarity index 100%
rename from 1-js/02-first-steps/12-while-for/1-loop-last-value/task.md
rename to 1-js/02-first-steps/13-while-for/1-loop-last-value/task.md
diff --git a/1-js/02-first-steps/12-while-for/2-which-value-while/solution.md b/1-js/02-first-steps/13-while-for/2-which-value-while/solution.md
similarity index 100%
rename from 1-js/02-first-steps/12-while-for/2-which-value-while/solution.md
rename to 1-js/02-first-steps/13-while-for/2-which-value-while/solution.md
diff --git a/1-js/02-first-steps/12-while-for/2-which-value-while/task.md b/1-js/02-first-steps/13-while-for/2-which-value-while/task.md
similarity index 100%
rename from 1-js/02-first-steps/12-while-for/2-which-value-while/task.md
rename to 1-js/02-first-steps/13-while-for/2-which-value-while/task.md
diff --git a/1-js/02-first-steps/12-while-for/3-which-value-for/solution.md b/1-js/02-first-steps/13-while-for/3-which-value-for/solution.md
similarity index 100%
rename from 1-js/02-first-steps/12-while-for/3-which-value-for/solution.md
rename to 1-js/02-first-steps/13-while-for/3-which-value-for/solution.md
diff --git a/1-js/02-first-steps/12-while-for/3-which-value-for/task.md b/1-js/02-first-steps/13-while-for/3-which-value-for/task.md
similarity index 100%
rename from 1-js/02-first-steps/12-while-for/3-which-value-for/task.md
rename to 1-js/02-first-steps/13-while-for/3-which-value-for/task.md
diff --git a/1-js/02-first-steps/12-while-for/4-for-even/solution.md b/1-js/02-first-steps/13-while-for/4-for-even/solution.md
similarity index 100%
rename from 1-js/02-first-steps/12-while-for/4-for-even/solution.md
rename to 1-js/02-first-steps/13-while-for/4-for-even/solution.md
diff --git a/1-js/02-first-steps/12-while-for/4-for-even/task.md b/1-js/02-first-steps/13-while-for/4-for-even/task.md
similarity index 100%
rename from 1-js/02-first-steps/12-while-for/4-for-even/task.md
rename to 1-js/02-first-steps/13-while-for/4-for-even/task.md
diff --git a/1-js/02-first-steps/12-while-for/5-replace-for-while/solution.md b/1-js/02-first-steps/13-while-for/5-replace-for-while/solution.md
similarity index 100%
rename from 1-js/02-first-steps/12-while-for/5-replace-for-while/solution.md
rename to 1-js/02-first-steps/13-while-for/5-replace-for-while/solution.md
diff --git a/1-js/02-first-steps/12-while-for/5-replace-for-while/task.md b/1-js/02-first-steps/13-while-for/5-replace-for-while/task.md
similarity index 100%
rename from 1-js/02-first-steps/12-while-for/5-replace-for-while/task.md
rename to 1-js/02-first-steps/13-while-for/5-replace-for-while/task.md
diff --git a/1-js/02-first-steps/12-while-for/6-repeat-until-correct/solution.md b/1-js/02-first-steps/13-while-for/6-repeat-until-correct/solution.md
similarity index 100%
rename from 1-js/02-first-steps/12-while-for/6-repeat-until-correct/solution.md
rename to 1-js/02-first-steps/13-while-for/6-repeat-until-correct/solution.md
diff --git a/1-js/02-first-steps/12-while-for/6-repeat-until-correct/task.md b/1-js/02-first-steps/13-while-for/6-repeat-until-correct/task.md
similarity index 100%
rename from 1-js/02-first-steps/12-while-for/6-repeat-until-correct/task.md
rename to 1-js/02-first-steps/13-while-for/6-repeat-until-correct/task.md
diff --git a/1-js/02-first-steps/12-while-for/7-list-primes/solution.md b/1-js/02-first-steps/13-while-for/7-list-primes/solution.md
similarity index 100%
rename from 1-js/02-first-steps/12-while-for/7-list-primes/solution.md
rename to 1-js/02-first-steps/13-while-for/7-list-primes/solution.md
diff --git a/1-js/02-first-steps/12-while-for/7-list-primes/task.md b/1-js/02-first-steps/13-while-for/7-list-primes/task.md
similarity index 100%
rename from 1-js/02-first-steps/12-while-for/7-list-primes/task.md
rename to 1-js/02-first-steps/13-while-for/7-list-primes/task.md
diff --git a/1-js/02-first-steps/12-while-for/article.md b/1-js/02-first-steps/13-while-for/article.md
similarity index 98%
rename from 1-js/02-first-steps/12-while-for/article.md
rename to 1-js/02-first-steps/13-while-for/article.md
index 382adadac..b3e3953b8 100644
--- a/1-js/02-first-steps/12-while-for/article.md
+++ b/1-js/02-first-steps/13-while-for/article.md
@@ -256,7 +256,7 @@ For even values of `i`, the `continue` directive stops executing the body and pa
````smart header="The `continue` directive helps decrease nesting"
A loop that shows odd values could look like this:
-```js
+```js run
for (let i = 0; i < 10; i++) {
if (i % 2) {
@@ -268,7 +268,7 @@ for (let i = 0; i < 10; i++) {
From a technical point of view, this is identical to the example above. Surely, we can just wrap the code in an `if` block instead of using `continue`.
-But as a side-effect, this created one more level of nesting (the `alert` call inside the curly braces). If the code inside of`if` is longer than a few lines, that may decrease the overall readability.
+But as a side-effect, this created one more level of nesting (the `alert` call inside the curly braces). If the code inside of `if` is longer than a few lines, that may decrease the overall readability.
````
````warn header="No `break/continue` to the right side of '?'"
diff --git a/1-js/02-first-steps/13-switch/1-rewrite-switch-if-else/solution.md b/1-js/02-first-steps/14-switch/1-rewrite-switch-if-else/solution.md
similarity index 100%
rename from 1-js/02-first-steps/13-switch/1-rewrite-switch-if-else/solution.md
rename to 1-js/02-first-steps/14-switch/1-rewrite-switch-if-else/solution.md
diff --git a/1-js/02-first-steps/13-switch/1-rewrite-switch-if-else/task.md b/1-js/02-first-steps/14-switch/1-rewrite-switch-if-else/task.md
similarity index 100%
rename from 1-js/02-first-steps/13-switch/1-rewrite-switch-if-else/task.md
rename to 1-js/02-first-steps/14-switch/1-rewrite-switch-if-else/task.md
diff --git a/1-js/02-first-steps/13-switch/2-rewrite-if-switch/solution.md b/1-js/02-first-steps/14-switch/2-rewrite-if-switch/solution.md
similarity index 100%
rename from 1-js/02-first-steps/13-switch/2-rewrite-if-switch/solution.md
rename to 1-js/02-first-steps/14-switch/2-rewrite-if-switch/solution.md
diff --git a/1-js/02-first-steps/13-switch/2-rewrite-if-switch/task.md b/1-js/02-first-steps/14-switch/2-rewrite-if-switch/task.md
similarity index 100%
rename from 1-js/02-first-steps/13-switch/2-rewrite-if-switch/task.md
rename to 1-js/02-first-steps/14-switch/2-rewrite-if-switch/task.md
diff --git a/1-js/02-first-steps/13-switch/article.md b/1-js/02-first-steps/14-switch/article.md
similarity index 99%
rename from 1-js/02-first-steps/13-switch/article.md
rename to 1-js/02-first-steps/14-switch/article.md
index dec40a537..314c6cef8 100644
--- a/1-js/02-first-steps/13-switch/article.md
+++ b/1-js/02-first-steps/14-switch/article.md
@@ -117,7 +117,7 @@ Several variants of `case` which share the same code can be grouped.
For example, if we want the same code to run for `case 3` and `case 5`:
```js run no-beautify
-let a = 2 + 2;
+let a = 3;
switch (a) {
case 4:
diff --git a/1-js/02-first-steps/14-function-basics/1-if-else-required/solution.md b/1-js/02-first-steps/15-function-basics/1-if-else-required/solution.md
similarity index 100%
rename from 1-js/02-first-steps/14-function-basics/1-if-else-required/solution.md
rename to 1-js/02-first-steps/15-function-basics/1-if-else-required/solution.md
diff --git a/1-js/02-first-steps/14-function-basics/1-if-else-required/task.md b/1-js/02-first-steps/15-function-basics/1-if-else-required/task.md
similarity index 100%
rename from 1-js/02-first-steps/14-function-basics/1-if-else-required/task.md
rename to 1-js/02-first-steps/15-function-basics/1-if-else-required/task.md
diff --git a/1-js/02-first-steps/14-function-basics/2-rewrite-function-question-or/solution.md b/1-js/02-first-steps/15-function-basics/2-rewrite-function-question-or/solution.md
similarity index 100%
rename from 1-js/02-first-steps/14-function-basics/2-rewrite-function-question-or/solution.md
rename to 1-js/02-first-steps/15-function-basics/2-rewrite-function-question-or/solution.md
diff --git a/1-js/02-first-steps/14-function-basics/2-rewrite-function-question-or/task.md b/1-js/02-first-steps/15-function-basics/2-rewrite-function-question-or/task.md
similarity index 100%
rename from 1-js/02-first-steps/14-function-basics/2-rewrite-function-question-or/task.md
rename to 1-js/02-first-steps/15-function-basics/2-rewrite-function-question-or/task.md
diff --git a/1-js/02-first-steps/14-function-basics/3-min/solution.md b/1-js/02-first-steps/15-function-basics/3-min/solution.md
similarity index 100%
rename from 1-js/02-first-steps/14-function-basics/3-min/solution.md
rename to 1-js/02-first-steps/15-function-basics/3-min/solution.md
diff --git a/1-js/02-first-steps/14-function-basics/3-min/task.md b/1-js/02-first-steps/15-function-basics/3-min/task.md
similarity index 100%
rename from 1-js/02-first-steps/14-function-basics/3-min/task.md
rename to 1-js/02-first-steps/15-function-basics/3-min/task.md
diff --git a/1-js/02-first-steps/14-function-basics/4-pow/solution.md b/1-js/02-first-steps/15-function-basics/4-pow/solution.md
similarity index 100%
rename from 1-js/02-first-steps/14-function-basics/4-pow/solution.md
rename to 1-js/02-first-steps/15-function-basics/4-pow/solution.md
diff --git a/1-js/02-first-steps/14-function-basics/4-pow/task.md b/1-js/02-first-steps/15-function-basics/4-pow/task.md
similarity index 100%
rename from 1-js/02-first-steps/14-function-basics/4-pow/task.md
rename to 1-js/02-first-steps/15-function-basics/4-pow/task.md
diff --git a/1-js/02-first-steps/14-function-basics/article.md b/1-js/02-first-steps/15-function-basics/article.md
similarity index 93%
rename from 1-js/02-first-steps/14-function-basics/article.md
rename to 1-js/02-first-steps/15-function-basics/article.md
index b1881e311..b56fbc67d 100644
--- a/1-js/02-first-steps/14-function-basics/article.md
+++ b/1-js/02-first-steps/15-function-basics/article.md
@@ -214,36 +214,48 @@ In JavaScript, a default parameter is evaluated every time the function is calle
In the example above, `anotherFunction()` is called every time `showMessage()` is called without the `text` parameter.
```
-````smart header="Default parameters old-style"
-Old editions of JavaScript did not support default parameters. So there are alternative ways to support them, that you can find mostly in the old scripts.
+### Alternative default parameters
-For instance, an explicit check for being `undefined`:
+Sometimes it makes sense to set default values for parameters not in the function declaration, but at a later stage, during its execution.
-```js
-function showMessage(from, text) {
+To check for an omitted parameter, we can compare it with `undefined`:
+
+```js run
+function showMessage(text) {
*!*
if (text === undefined) {
- text = 'no text given';
+ text = 'empty message';
}
*/!*
- alert( from + ": " + text );
+ alert(text);
}
+
+showMessage(); // empty message
```
-...Or the `||` operator:
+...Or we could use the `||` operator:
```js
-function showMessage(from, text) {
- // if text is falsy then text gets the "default" value
- text = text || 'no text given';
+// if text parameter is omitted or "" is passed, set it to 'empty'
+function showMessage(text) {
+ text = text || 'empty';
...
}
```
+Modern JavaScript engines support the [nullish coalescing operator](info:nullish-coalescing-operator) `??`, it's better when falsy values, such as `0`, are considered regular:
-````
+```js run
+// if there's no "count" parameter, show "unknown"
+function showCount(count) {
+ alert(count ?? "unknown");
+}
+showCount(0); // 0
+showCount(null); // unknown
+showCount(); // unknown
+```
## Returning a value
@@ -266,7 +278,7 @@ There may be many occurrences of `return` in a single function. For instance:
```js run
function checkAge(age) {
- if (age > 18) {
+ if (age >= 18) {
*!*
return true;
*/!*
diff --git a/1-js/02-first-steps/15-function-expressions/article.md b/1-js/02-first-steps/16-function-expressions/article.md
similarity index 100%
rename from 1-js/02-first-steps/15-function-expressions/article.md
rename to 1-js/02-first-steps/16-function-expressions/article.md
diff --git a/1-js/02-first-steps/16-arrow-functions-basics/1-rewrite-arrow/solution.md b/1-js/02-first-steps/17-arrow-functions-basics/1-rewrite-arrow/solution.md
similarity index 100%
rename from 1-js/02-first-steps/16-arrow-functions-basics/1-rewrite-arrow/solution.md
rename to 1-js/02-first-steps/17-arrow-functions-basics/1-rewrite-arrow/solution.md
diff --git a/1-js/02-first-steps/16-arrow-functions-basics/1-rewrite-arrow/task.md b/1-js/02-first-steps/17-arrow-functions-basics/1-rewrite-arrow/task.md
similarity index 100%
rename from 1-js/02-first-steps/16-arrow-functions-basics/1-rewrite-arrow/task.md
rename to 1-js/02-first-steps/17-arrow-functions-basics/1-rewrite-arrow/task.md
diff --git a/1-js/02-first-steps/16-arrow-functions-basics/article.md b/1-js/02-first-steps/17-arrow-functions-basics/article.md
similarity index 99%
rename from 1-js/02-first-steps/16-arrow-functions-basics/article.md
rename to 1-js/02-first-steps/17-arrow-functions-basics/article.md
index 02090f3c1..e0fb5bda5 100644
--- a/1-js/02-first-steps/16-arrow-functions-basics/article.md
+++ b/1-js/02-first-steps/17-arrow-functions-basics/article.md
@@ -67,7 +67,7 @@ let welcome = (age < 18) ?
() => alert('Hello') :
() => alert("Greetings!");
-welcome(); // ok now
+welcome();
```
Arrow functions may appear unfamiliar and not very readable at first, but that quickly changes as the eyes get used to the structure.
diff --git a/1-js/02-first-steps/17-javascript-specials/article.md b/1-js/02-first-steps/18-javascript-specials/article.md
similarity index 96%
rename from 1-js/02-first-steps/17-javascript-specials/article.md
rename to 1-js/02-first-steps/18-javascript-specials/article.md
index cfc043d7d..91be0aa45 100644
--- a/1-js/02-first-steps/17-javascript-specials/article.md
+++ b/1-js/02-first-steps/18-javascript-specials/article.md
@@ -81,9 +81,10 @@ let x = 5;
x = "John";
```
-There are 7 data types:
+There are 8 data types:
- `number` for both floating-point and integer numbers,
+- `bigint` for integer numbers of arbitrary length,
- `string` for strings,
- `boolean` for logical values: `true/false`,
- `null` -- a type with a single value `null`, meaning "empty" or "does not exist",
@@ -151,6 +152,9 @@ Conditional
Logical operators
: Logical AND `&&` and OR `||` perform short-circuit evaluation and then return the value where it stopped (not necessary `true`/`false`). Logical NOT `!` converts the operand to boolean type and returns the inverse value.
+Nullish coalescing operator
+: The `??` operator provides a way to choose a defined value from a list of variables. The result of `a ?? b` is `a` unless it's `null/undefined`, then `b`.
+
Comparisons
: Equality check `==` for values of different types converts them to a number (except `null` and `undefined` that equal each other and nothing else), so these are equal:
@@ -170,7 +174,7 @@ Comparisons
Other operators
: There are few others, like a comma operator.
-More in: 253 or be less than -253. As bigints are used in few special areas, we devote them a special chapter handler is called on this input:
+
+
+Debounced function debounce(handler, 1000) is called on this input:
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/1-js/06-advanced-functions/09-call-apply-decorators/03-debounce/solution.md b/1-js/06-advanced-functions/09-call-apply-decorators/03-debounce/solution.md
index 4f5867ded..83e75f315 100644
--- a/1-js/06-advanced-functions/09-call-apply-decorators/03-debounce/solution.md
+++ b/1-js/06-advanced-functions/09-call-apply-decorators/03-debounce/solution.md
@@ -1,28 +1,13 @@
```js demo
-function debounce(f, ms) {
-
- let isCooldown = false;
-
+function debounce(func, ms) {
+ let timeout;
return function() {
- if (isCooldown) return;
-
- f.apply(this, arguments);
-
- isCooldown = true;
-
- setTimeout(() => isCooldown = false, ms);
+ clearTimeout(timeout);
+ timeout = setTimeout(() => func.apply(this, arguments), ms);
};
-
}
-```
-
-A call to `debounce` returns a wrapper. There may be two states:
-- `isCooldown = false` -- ready to run.
-- `isCooldown = true` -- waiting for the timeout.
-
-In the first call `isCooldown` is falsy, so the call proceeds, and the state changes to `true`.
+```
-While `isCooldown` is true, all other calls are ignored.
+A call to `debounce` returns a wrapper. When called, it schedules the original function call after given `ms` and cancels the previous such timeout.
-Then `setTimeout` reverts it to `false` after the given delay.
diff --git a/1-js/06-advanced-functions/09-call-apply-decorators/03-debounce/task.md b/1-js/06-advanced-functions/09-call-apply-decorators/03-debounce/task.md
index 2620f1c71..550bf52da 100644
--- a/1-js/06-advanced-functions/09-call-apply-decorators/03-debounce/task.md
+++ b/1-js/06-advanced-functions/09-call-apply-decorators/03-debounce/task.md
@@ -4,21 +4,48 @@ importance: 5
# Debounce decorator
-The result of `debounce(f, ms)` decorator should be a wrapper that passes the call to `f` at maximum once per `ms` milliseconds.
+The result of `debounce(f, ms)` decorator is a wrapper that suspends calls to `f` until there's `ms` milliseconds of inactivity (no calls, "cooldown period"), then invokes `f` once with the latest arguments.
-In other words, when we call a "debounced" function, it guarantees that all future calls to the function made less than `ms` milliseconds after the previous call will be ignored.
+For instance, we had a function `f` and replaced it with `f = debounce(f, 1000)`.
-For instance:
+Then if the wrapped function is called at 0ms, 200ms and 500ms, and then there are no calls, then the actual `f` will be only called once, at 1500ms. That is: after the cooldown period of 1000ms from the last call.
-```js no-beautify
-let f = debounce(alert, 1000);
+
-f(1); // runs immediately
-f(2); // ignored
+...And it will get the arguments of the very last call, other calls are ignored.
-setTimeout( () => f(3), 100); // ignored ( only 100 ms passed )
-setTimeout( () => f(4), 1100); // runs
-setTimeout( () => f(5), 1500); // ignored (less than 1000 ms from the last run)
+Here's the code for it (uses the debounce decorator from the [Lodash library](https://lodash.com/docs/4.17.15#debounce):
+
+```js
+let f = _.debounce(alert, 1000);
+
+f("a");
+setTimeout( () => f("b"), 200);
+setTimeout( () => f("c"), 500);
+// debounced function waits 1000ms after the last call and then runs: alert("c")
```
-In practice `debounce` is useful for functions that retrieve/update something when we know that nothing new can be done in such a short period of time, so it's better not to waste resources.
+
+Now a practical example. Let's say, the user types something, and we'd like to send a request to the server when the input is finished.
+
+There's no point in sending the request for every character typed. Instead we'd like to wait, and then process the whole result.
+
+In a web-browser, we can setup an event handler -- a function that's called on every change of an input field. Normally, an event handler is called very often, for every typed key. But if we `debounce` it by 1000ms, then it will be only called once, after 1000ms after the last input.
+
+```online
+
+In this live example, the handler puts the result into a box below, try it:
+
+[iframe border=1 src="debounce" height=200]
+
+See? The second input calls the debounced function, so its content is processed after 1000ms from the last input.
+```
+
+So, `debounce` is a great way to process a sequence of events: be it a sequence of key presses, mouse movements or something else.
+
+
+It waits the given time after the last call, and then runs its function, that can process the result.
+
+The task is to implement `debounce` decorator.
+
+Hint: that's just a few lines if you think about it :)
\ No newline at end of file
diff --git a/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/task.md b/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/task.md
index 9e08874af..6cb664fdb 100644
--- a/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/task.md
+++ b/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/task.md
@@ -4,16 +4,19 @@ importance: 5
# Throttle decorator
-Create a "throttling" decorator `throttle(f, ms)` -- that returns a wrapper, passing the call to `f` at maximum once per `ms` milliseconds. Those calls that fall into the "cooldown" period, are ignored.
+Create a "throttling" decorator `throttle(f, ms)` -- that returns a wrapper.
-**The difference with `debounce` -- if an ignored call is the last during the cooldown, then it executes at the end of the delay.**
+When it's called multiple times, it passes the call to `f` at maximum once per `ms` milliseconds.
+
+The difference with debounce is that it's completely different decorator:
+- `debounce` runs the function once after the "cooldown" period. Good for processing the final result.
+- `throttle` runs it not more often than given `ms` time. Good for regular updates that shouldn't be very often.
Let's check the real-life application to better understand that requirement and to see where it comes from.
**For instance, we want to track mouse movements.**
In a browser we can setup a function to run at every mouse movement and get the pointer location as it moves. During an active mouse usage, this function usually runs very frequently, can be something like 100 times per second (every 10 ms).
-
**We'd like to update some information on the web-page when the pointer moves.**
...But updating function `update()` is too heavy to do it on every micro-movement. There is also no sense in updating more often than once per 100ms.
diff --git a/1-js/06-advanced-functions/09-call-apply-decorators/article.md b/1-js/06-advanced-functions/09-call-apply-decorators/article.md
index 373500e13..d0dda4df1 100644
--- a/1-js/06-advanced-functions/09-call-apply-decorators/article.md
+++ b/1-js/06-advanced-functions/09-call-apply-decorators/article.md
@@ -149,8 +149,8 @@ let user = { name: "John" };
let admin = { name: "Admin" };
// use call to pass different objects as "this"
-sayHi.call( user ); // this = John
-sayHi.call( admin ); // this = Admin
+sayHi.call( user ); // John
+sayHi.call( admin ); // Admin
```
And here we use `call` to call `say` with the given context and phrase:
@@ -209,7 +209,7 @@ To make it all clear, let's see more deeply how `this` is passed along:
2. So when `worker.slow(2)` is executed, the wrapper gets `2` as an argument and `this=worker` (it's the object before dot).
3. Inside the wrapper, assuming the result is not yet cached, `func.call(this, x)` passes the current `this` (`=worker`) and the current argument (`=2`) to the original method.
-## Going multi-argument with "func.apply"
+## Going multi-argument
Now let's make `cachingDecorator` even more universal. Till now it was working only with single-argument functions.
@@ -236,7 +236,7 @@ There are many solutions possible:
For many practical applications, the 3rd variant is good enough, so we'll stick to it.
-Also we need to replace `func.call(this, x)` with `func.call(this, ...arguments)`, to pass all arguments to the wrapped function call, not just the first one.
+Also we need to pass not just `x`, but all arguments in `func.call`. Let's recall that in a `function()` we can get a pseudo-array of its arguments as `arguments`, so `func.call(this, x)` should be replaced with `func.call(this, ...arguments)`.
Here's a more powerful `cachingDecorator`:
@@ -284,6 +284,8 @@ There are two changes:
- In the line `(*)` it calls `hash` to create a single key from `arguments`. Here we use a simple "joining" function that turns arguments `(3, 5)` into the key `"3,5"`. More complex cases may require other hashing functions.
- Then `(**)` uses `func.call(this, ...arguments)` to pass both the context and all arguments the wrapper got (not just the first one) to the original function.
+## func.apply
+
Instead of `func.call(this, ...arguments)` we could use `func.apply(this, arguments)`.
The syntax of built-in method [func.apply](mdn:js/Function/apply) is:
@@ -299,18 +301,18 @@ The only syntax difference between `call` and `apply` is that `call` expects a l
So these two calls are almost equivalent:
```js
-func.call(context, ...args); // pass an array as list with spread operator
-func.apply(context, args); // is same as using apply
+func.call(context, ...args); // pass an array as list with spread syntax
+func.apply(context, args); // is same as using call
```
-There's only a minor difference:
+There's only a subtle difference:
-- The spread operator `...` allows to pass *iterable* `args` as the list to `call`.
+- The spread syntax `...` allows to pass *iterable* `args` as the list to `call`.
- The `apply` accepts only *array-like* `args`.
-So, these calls complement each other. Where we expect an iterable, `call` works, where we expect an array-like, `apply` works.
+So, where we expect an iterable, `call` works, and where we expect an array-like, `apply` works.
-And for objects that are both iterable and array-like, like a real array, we technically could use any of them, but `apply` will probably be faster, because most JavaScript engines internally optimize it better.
+And for objects that are both iterable and array-like, like a real array, we can use any of them, but `apply` will probably be faster, because most JavaScript engines internally optimize it better.
Passing all arguments along with the context to another function is called *call forwarding*.
@@ -344,7 +346,7 @@ function hash(args) {
}
```
-...Unfortunately, that won't work. Because we are calling `hash(arguments)` and `arguments` object is both iterable and array-like, but not a real array.
+...Unfortunately, that won't work. Because we are calling `hash(arguments)`, and `arguments` object is both iterable and array-like, but not a real array.
So calling `join` on it would fail, as we can see below:
diff --git a/1-js/06-advanced-functions/10-bind/article.md b/1-js/06-advanced-functions/10-bind/article.md
index 16a50942d..787c7d68e 100644
--- a/1-js/06-advanced-functions/10-bind/article.md
+++ b/1-js/06-advanced-functions/10-bind/article.md
@@ -279,7 +279,7 @@ What if we'd like to fix some arguments, but not the context `this`? For example
The native `bind` does not allow that. We can't just omit the context and jump to arguments.
-Fortunately, a helper function `partial` for binding only arguments can be easily implemented.
+Fortunately, a function `partial` for binding only arguments can be easily implemented.
Like this:
@@ -313,7 +313,7 @@ The result of `partial(func[, arg1, arg2...])` call is a wrapper `(*)` that call
- Then gives it `...argsBound` -- arguments from the `partial` call (`"10:00"`)
- Then gives it `...args` -- arguments given to the wrapper (`"Hello"`)
-So easy to do it with the spread operator, right?
+So easy to do it with the spread syntax, right?
Also there's a ready [_.partial](https://lodash.com/docs#partial) implementation from lodash library.
diff --git a/1-js/07-object-properties/01-property-descriptors/article.md b/1-js/07-object-properties/01-property-descriptors/article.md
index e894f0662..3593bffae 100644
--- a/1-js/07-object-properties/01-property-descriptors/article.md
+++ b/1-js/07-object-properties/01-property-descriptors/article.md
@@ -66,7 +66,7 @@ Object.defineProperty(obj, propertyName, descriptor)
: The object and its property to apply the descriptor.
`descriptor`
-: Property descriptor to apply.
+: Property descriptor object to apply.
If the property exists, `defineProperty` updates its flags. Otherwise, it creates the property with the given value and flags; in that case, if a flag is not supplied, it is assumed `false`.
diff --git a/1-js/07-object-properties/02-property-accessors/article.md b/1-js/07-object-properties/02-property-accessors/article.md
index 726529c5b..45b9e70ed 100644
--- a/1-js/07-object-properties/02-property-accessors/article.md
+++ b/1-js/07-object-properties/02-property-accessors/article.md
@@ -1,11 +1,11 @@
# Property getters and setters
-There are two kinds of properties.
+There are two kinds of object properties.
The first kind is *data properties*. We already know how to work with them. All properties that we've been using until now were data properties.
-The second type of properties is something new. It's *accessor properties*. They are essentially functions that work on getting and setting a value, but look like regular properties to an external code.
+The second type of properties is something new. It's *accessor properties*. They are essentially functions that execute on getting and setting a value, but look like regular properties to an external code.
## Getters and setters
@@ -53,7 +53,7 @@ alert(user.fullName); // John Smith
*/!*
```
-From outside, an accessor property looks like a regular one. That's the idea of accessor properties. We don't *call* `user.fullName` as a function, we *read* it normally: the getter runs behind the scenes.
+From the outside, an accessor property looks like a regular one. That's the idea of accessor properties. We don't *call* `user.fullName` as a function, we *read* it normally: the getter runs behind the scenes.
As of now, `fullName` has only a getter. If we attempt to assign `user.fullName=`, there will be an error:
@@ -94,11 +94,7 @@ alert(user.name); // Alice
alert(user.surname); // Cooper
```
-As the result, we have a "virtual" property `fullName`. It is readable and writable, but in fact does not exist.
-
-```smart header="No way to handle `delete`"
-There's no similar method to handle deletion of an accessor property. Only getter/setter methods may exist.
-```
+As the result, we have a "virtual" property `fullName`. It is readable and writable.
## Accessor descriptors
@@ -138,7 +134,7 @@ alert(user.fullName); // John Smith
for(let key in user) alert(key); // name, surname
```
-Please note once again that a property can be either an accessor (has `get/set` methods) or a data property (has a `value`), not both.
+Please note that a property can be either an accessor (has `get/set` methods) or a data property (has a `value`), not both.
If we try to supply both `get` and `value` in the same descriptor, there will be an error:
diff --git a/1-js/08-prototypes/01-prototype-inheritance/article.md b/1-js/08-prototypes/01-prototype-inheritance/article.md
index 69e7c5f5c..710390f15 100644
--- a/1-js/08-prototypes/01-prototype-inheritance/article.md
+++ b/1-js/08-prototypes/01-prototype-inheritance/article.md
@@ -16,7 +16,7 @@ The prototype is a little bit "magical". When we want to read a property from `o
The property `[[Prototype]]` is internal and hidden, but there are many ways to set it.
-One of them is to use `__proto__`, like this:
+One of them is to use the special name `__proto__`, like this:
```js run
let animal = {
@@ -32,7 +32,7 @@ rabbit.__proto__ = animal;
```
```smart header="`__proto__` is a historical getter/setter for `[[Prototype]]`"
-Please note that `__proto__` is *not the same* as `[[Prototype]]`. That's a getter/setter for it.
+Please note that `__proto__` is *not the same* as `[[Prototype]]`. It's a getter/setter for it.
It exists for historical reasons. In modern language it is replaced with functions `Object.getPrototypeOf/Object.setPrototypeOf` that also get/set the prototype. We'll study the reasons for that and these functions later.
diff --git a/1-js/08-prototypes/02-function-prototype/article.md b/1-js/08-prototypes/02-function-prototype/article.md
index c106d1d90..b1ef51826 100644
--- a/1-js/08-prototypes/02-function-prototype/article.md
+++ b/1-js/08-prototypes/02-function-prototype/article.md
@@ -41,7 +41,7 @@ That's the resulting picture:
On the picture, `"prototype"` is a horizontal arrow, meaning a regular property, and `[[Prototype]]` is vertical, meaning the inheritance of `rabbit` from `animal`.
```smart header="`F.prototype` only used at `new F` time"
-`F.prototype` property is only used when `new F` is called, it assigns `[[Prototype]]` of the new object. After that, there's no connection between `F.prototype` and the new object. Think of it as a "one-time gift".
+`F.prototype` property is only used when `new F` is called, it assigns `[[Prototype]]` of the new object.
If, after the creation, `F.prototype` property changes (`F.prototype =