diff --git a/1-js/02-first-steps/04-variables/3-uppercast-constant/task.md b/1-js/02-first-steps/04-variables/3-uppercast-constant/task.md
index 47762391c..7fd50a0b6 100644
--- a/1-js/02-first-steps/04-variables/3-uppercast-constant/task.md
+++ b/1-js/02-first-steps/04-variables/3-uppercast-constant/task.md
@@ -12,13 +12,24 @@ const datumNarození = '18.04.1982';
const věk = nějakýKód(datumNarození);
```
+<<<<<<< HEAD
Zde máme konstantu `datumNarození` a pomocí nějakého kódu se z této proměnné vypočítá `věk` (kód není pro stručnost uveden, na podrobnostech zde nezáleží).
+=======
+Here we have a constant `birthday` for the date, and also the `age` constant.
+
+The `age` is calculated from `birthday` using `someCode()`, which means a function call that we didn't explain yet (we will soon!), but the details don't matter here, the point is that `age` is calculated somehow based on the `birthday`.
+>>>>>>> 18b1314af4e0ead5a2b10bb4bacd24cecbb3f18e
Bylo by správné použít pro název proměnné `datumNarození` velká písmena? A pro `věk`? Nebo dokonce pro obě?
```js
+<<<<<<< HEAD
const DATUM_NAROZENÍ = '18.04.1982'; // velkými písmeny?
const VĚK = nějakýKód(DATUM_NAROZENÍ); // velkými písmeny?
-```
+=======
+const BIRTHDAY = '18.04.1982'; // make birthday uppercase?
+const AGE = someCode(BIRTHDAY); // make age uppercase?
+>>>>>>> 18b1314af4e0ead5a2b10bb4bacd24cecbb3f18e
+```
diff --git a/1-js/02-first-steps/08-operators/3-primitive-conversions-questions/solution.md b/1-js/02-first-steps/08-operators/3-primitive-conversions-questions/solution.md
index ff417168e..770f4db91 100644
--- a/1-js/02-first-steps/08-operators/3-primitive-conversions-questions/solution.md
+++ b/1-js/02-first-steps/08-operators/3-primitive-conversions-questions/solution.md
@@ -16,6 +16,7 @@ undefined + 1 = NaN // (6)
" \t \n" - 2 = -2 // (7)
```
+<<<<<<< HEAD
1. Sčítání s řetězcem `"" + 1` převede `1` na řetězec: `"" + 1 = "1"`, pak tedy budeme mít `"1" + 0` a použije se stejné pravidlo.
2. Odčítání `-` (stejně jako většina matematických operací) pracuje jen s čísly, takže převede prázdný řetězec `""` na `0`.
3. Sčítání s řetězcem připojí k řetězci číslo `5`.
@@ -23,3 +24,12 @@ undefined + 1 = NaN // (6)
5. `null` se převede na číslo `0`.
6. `undefined` se převede na číslo `NaN`.
7. Když se řetězec převádí na číslo, mezerové znaky se z jeho začátku a konce odříznou. V tomto případě se celý řetězec skládá z mezerových znaků, konkrétně `\t`, `\n` a „obyčejné“ mezery mezi nimi. Stejně jako prázdný řetězec se tedy převede na `0`.
+=======
+1. The addition with a string `"" + 1` converts `1` to a string: `"" + 1 = "1"`, and then we have `"1" + 0`, the same rule is applied.
+2. The subtraction `-` (like most math operations) only works with numbers, it converts an empty string `""` to `0`.
+3. The addition with a string appends the number `5` to the string.
+4. The subtraction always converts to numbers, so it makes `" -9 "` a number `-9` (ignoring spaces around it).
+5. `null` becomes `0` after the numeric conversion.
+6. `undefined` becomes `NaN` after the numeric conversion.
+7. Space characters are trimmed off string start and end when a string is converted to a number. Here the whole string consists of space characters, such as `\t`, `\n` and a "regular" space between them. So, similarly to an empty string, it becomes `0`.
+>>>>>>> 18b1314af4e0ead5a2b10bb4bacd24cecbb3f18e
diff --git a/1-js/04-object-basics/02-object-copy/article.md b/1-js/04-object-basics/02-object-copy/article.md
index cc2f606a9..e80f748ab 100644
--- a/1-js/04-object-basics/02-object-copy/article.md
+++ b/1-js/04-object-basics/02-object-copy/article.md
@@ -160,16 +160,17 @@ We can also use the method [Object.assign](https://developer.mozilla.org/en-US/d
The syntax is:
```js
-Object.assign(dest, src1[, src2, src3...])
+Object.assign(dest, ...sources)
```
- The first argument `dest` is a target object.
-- Further arguments `src1, ..., srcN` (can be as many as needed) are source objects.
-- It copies the properties of all source objects `src1, ..., srcN` into the target `dest`. In other words, properties of all arguments starting from the second are copied into the first object.
-- The call returns `dest`.
+- Further arguments is a list of source objects.
-For instance, we can use it to merge several objects into one:
-```js
+It copies the properties of all source objects into the target `dest`, and then returns it as the result.
+
+For example, we have `user` object, let's add a couple of permissions to it:
+
+```js run
let user = { name: "John" };
let permissions1 = { canView: true };
@@ -181,6 +182,9 @@ Object.assign(user, permissions1, permissions2);
*/!*
// now user = { name: "John", canView: true, canEdit: true }
+alert(user.name); // John
+alert(user.canView); // true
+alert(user.canEdit); // true
```
If the copied property name already exists, it gets overwritten:
@@ -193,9 +197,9 @@ Object.assign(user, { name: "Pete" });
alert(user.name); // now user = { name: "Pete" }
```
-We also can use `Object.assign` to replace `for..in` loop for simple cloning:
+We also can use `Object.assign` to perform a simple object cloning:
-```js
+```js run
let user = {
name: "John",
age: 30
@@ -204,9 +208,12 @@ let user = {
*!*
let clone = Object.assign({}, user);
*/!*
+
+alert(clone.name); // John
+alert(clone.age); // 30
```
-It copies all properties of `user` into the empty object and returns it.
+Here it copies all properties of `user` into the empty object and returns it.
There are also other methods of cloning an object, e.g. using the [spread syntax](info:rest-parameters-spread) `clone = {...user}`, covered later in the tutorial.
diff --git a/1-js/04-object-basics/06-constructor-new/1-two-functions-one-object/task.md b/1-js/04-object-basics/06-constructor-new/1-two-functions-one-object/task.md
index d80113acc..e932a201a 100644
--- a/1-js/04-object-basics/06-constructor-new/1-two-functions-one-object/task.md
+++ b/1-js/04-object-basics/06-constructor-new/1-two-functions-one-object/task.md
@@ -10,8 +10,8 @@ Is it possible to create functions `A` and `B` so that `new A() == new B()`?
function A() { ... }
function B() { ... }
-let a = new A;
-let b = new B;
+let a = new A();
+let b = new B();
alert( a == b ); // true
```
diff --git a/1-js/04-object-basics/06-constructor-new/2-calculator-constructor/task.md b/1-js/04-object-basics/06-constructor-new/2-calculator-constructor/task.md
index 60e7c373e..c862bec40 100644
--- a/1-js/04-object-basics/06-constructor-new/2-calculator-constructor/task.md
+++ b/1-js/04-object-basics/06-constructor-new/2-calculator-constructor/task.md
@@ -6,7 +6,7 @@ importance: 5
Create a constructor function `Calculator` that creates objects with 3 methods:
-- `read()` asks for two values using `prompt` and remembers them in object properties.
+- `read()` prompts for two values and saves them as object properties with names `a` and `b` respectively.
- `sum()` returns the sum of these properties.
- `mul()` returns the multiplication product of these properties.
diff --git a/1-js/04-object-basics/06-constructor-new/article.md b/1-js/04-object-basics/06-constructor-new/article.md
index f3e9c3ec0..a335464f1 100644
--- a/1-js/04-object-basics/06-constructor-new/article.md
+++ b/1-js/04-object-basics/06-constructor-new/article.md
@@ -171,7 +171,7 @@ alert( new SmallUser().name ); // John
Usually constructors don't have a `return` statement. Here we mention the special behavior with returning objects mainly for the sake of completeness.
````smart header="Omitting parentheses"
-By the way, we can omit parentheses after `new`, if it has no arguments:
+By the way, we can omit parentheses after `new`:
```js
let user = new User; // <-- no parentheses
diff --git a/1-js/05-data-types/02-number/2-why-rounded-down/solution.md b/1-js/05-data-types/02-number/2-why-rounded-down/solution.md
index a17a4671a..4bcd74512 100644
--- a/1-js/05-data-types/02-number/2-why-rounded-down/solution.md
+++ b/1-js/05-data-types/02-number/2-why-rounded-down/solution.md
@@ -28,6 +28,6 @@ Note that `63.5` has no precision loss at all. That's because the decimal part `
```js run
-alert( Math.round(6.35 * 10) / 10); // 6.35 -> 63.5 -> 64(rounded) -> 6.4
+alert( Math.round(6.35 * 10) / 10 ); // 6.35 -> 63.5 -> 64(rounded) -> 6.4
```
diff --git a/1-js/05-data-types/02-number/article.md b/1-js/05-data-types/02-number/article.md
index 8e130f745..c704bd980 100644
--- a/1-js/05-data-types/02-number/article.md
+++ b/1-js/05-data-types/02-number/article.md
@@ -352,7 +352,7 @@ Please note that an empty or a space-only string is treated as `0` in all numeri
```js run
alert( Number.isFinite(123) ); // true
- alert( Number.isFinite(Infinity) ); //false
+ alert( Number.isFinite(Infinity) ); // false
alert( Number.isFinite(2 / 0) ); // false
// Note the difference:
@@ -367,7 +367,7 @@ In a way, `Number.isNaN` and `Number.isFinite` are simpler and more straightforw
There is a special built-in method `Object.is` that compares values like `===`, but is more reliable for two edge cases:
1. It works with `NaN`: `Object.is(NaN, NaN) === true`, that's a good thing.
-2. Values `0` and `-0` are different: `Object.is(0, -0) === false`, technically that's true, because internally the number has a sign bit that may be different even if all other bits are zeroes.
+2. Values `0` and `-0` are different: `Object.is(0, -0) === false`, technically that's correct, because internally the number has a sign bit that may be different even if all other bits are zeroes.
In all other cases, `Object.is(a, b)` is the same as `a === b`.
diff --git a/1-js/05-data-types/03-string/1-ucfirst/solution.md b/1-js/05-data-types/03-string/1-ucfirst/solution.md
index f7a332d0d..be5dd2aaf 100644
--- a/1-js/05-data-types/03-string/1-ucfirst/solution.md
+++ b/1-js/05-data-types/03-string/1-ucfirst/solution.md
@@ -8,12 +8,7 @@ let newStr = str[0].toUpperCase() + str.slice(1);
There's a small problem though. If `str` is empty, then `str[0]` is `undefined`, and as `undefined` doesn't have the `toUpperCase()` method, we'll get an error.
-There are two variants here:
-
-1. Use `str.charAt(0)`, as it always returns a string (maybe empty).
-2. Add a test for an empty string.
-
-Here's the 2nd variant:
+The easiest way out is to add a test for an empty string, like this:
```js run demo
function ucFirst(str) {
@@ -24,4 +19,3 @@ function ucFirst(str) {
alert( ucFirst("john") ); // John
```
-
diff --git a/1-js/05-data-types/03-string/article.md b/1-js/05-data-types/03-string/article.md
index 255eb29a7..618f8ef3b 100644
--- a/1-js/05-data-types/03-string/article.md
+++ b/1-js/05-data-types/03-string/article.md
@@ -50,7 +50,7 @@ let guestList = "Guests: // Error: Unexpected token ILLEGAL
Single and double quotes come from ancient times of language creation, when the need for multiline strings was not taken into account. Backticks appeared much later and thus are more versatile.
-Backticks also allow us to specify a "template function" before the first backtick. The syntax is: func`string`
. The function `func` is called automatically, receives the string and embedded expressions and can process them. This is called "tagged templates". This feature makes it easier to implement custom templating, but is rarely used in practice. You can read more about it in the [manual](mdn:/JavaScript/Reference/Template_literals#Tagged_templates).
+Backticks also allow us to specify a "template function" before the first backtick. The syntax is: func`string`
. The function `func` is called automatically, receives the string and embedded expressions and can process them. This feature is called "tagged templates", it's rarely seen, but you can read about it in the MDN: [Template literals](mdn:/JavaScript/Reference/Template_literals#Tagged_templates).
## Special characters
@@ -59,10 +59,10 @@ It is still possible to create multiline strings with single and double quotes b
```js run
let guestList = "Guests:\n * John\n * Pete\n * Mary";
-alert(guestList); // a multiline list of guests
+alert(guestList); // a multiline list of guests, same as above
```
-For example, these two lines are equal, just written differently:
+As a simpler example, these two lines are equal, just written differently:
```js run
let str1 = "Hello\nWorld"; // two lines using a "newline symbol"
@@ -74,33 +74,26 @@ World`;
alert(str1 == str2); // true
```
-There are other, less common "special" characters.
-
-Here's the full list:
+There are other, less common special characters:
| Character | Description |
|-----------|-------------|
|`\n`|New line|
|`\r`|In Windows text files a combination of two characters `\r\n` represents a new break, while on non-Windows OS it's just `\n`. That's for historical reasons, most Windows software also understands `\n`. |
-|`\'`, `\"`|Quotes|
+|`\'`, `\"`, \\`
|Quotes|
|`\\`|Backslash|
|`\t`|Tab|
-|`\b`, `\f`, `\v`| Backspace, Form Feed, Vertical Tab -- kept for compatibility, not used nowadays. |
-|`\xXX`|Unicode character with the given hexadecimal Unicode `XX`, e.g. `'\x7A'` is the same as `'z'`.|
-|`\uXXXX`|A Unicode symbol with the hex code `XXXX` in UTF-16 encoding, for instance `\u00A9` -- is a Unicode for the copyright symbol `©`. It must be exactly 4 hex digits. |
-|`\u{X…XXXXXX}` (1 to 6 hex characters)|A Unicode symbol with the given UTF-32 encoding. Some rare characters are encoded with two Unicode symbols, taking 4 bytes. This way we can insert long codes. |
+|`\b`, `\f`, `\v`| Backspace, Form Feed, Vertical Tab -- mentioned for completeness, coming from old times, not used nowadays (you can forget them right now). |
+
+As you can see, all special characters start with a backslash character `\`. It is also called an "escape character".
-Examples with Unicode:
+Because it's so special, if we need to show an actual backslash `\` within the string, we need to double it:
```js run
-alert( "\u00A9" ); // ©
-alert( "\u{20331}" ); // 佫, a rare Chinese hieroglyph (long Unicode)
-alert( "\u{1F60D}" ); // 😍, a smiling face symbol (another long Unicode)
+alert( `The backslash: \\` ); // The backslash: \
```
-All special characters start with a backslash character `\`. It is also called an "escape character".
-
-We might also use it if we wanted to insert a quote into the string.
+So-called "escaped" quotes `\'`, `\"`, \\`
are used to insert a quote into the same-quoted string.
For instance:
@@ -113,18 +106,10 @@ As you can see, we have to prepend the inner quote by the backslash `\'`, becaus
Of course, only the quotes that are the same as the enclosing ones need to be escaped. So, as a more elegant solution, we could switch to double quotes or backticks instead:
```js run
-alert( `I'm the Walrus!` ); // I'm the Walrus!
+alert( "I'm the Walrus!" ); // I'm the Walrus!
```
-Note that the backslash `\` serves for the correct reading of the string by JavaScript, then disappears. The in-memory string has no `\`. You can clearly see that in `alert` from the examples above.
-
-But what if we need to show an actual backslash `\` within the string?
-
-That's possible, but we need to double it like `\\`:
-
-```js run
-alert( `The backslash: \\` ); // The backslash: \
-```
+Besides these special characters, there's also a special notation for Unicode codes `\u…`, it's rarely used and is covered in the optional chapter about [Unicode](info:unicode).
## String length
@@ -139,33 +124,36 @@ Note that `\n` is a single "special" character, so the length is indeed `3`.
```warn header="`length` is a property"
People with a background in some other languages sometimes mistype by calling `str.length()` instead of just `str.length`. That doesn't work.
-Please note that `str.length` is a numeric property, not a function. There is no need to add parenthesis after it.
+Please note that `str.length` is a numeric property, not a function. There is no need to add parenthesis after it. Not `.length()`, but `.length`.
```
## Accessing characters
-To get a character at position `pos`, use square brackets `[pos]` or call the method [str.charAt(pos)](mdn:js/String/charAt). The first character starts from the zero position:
+To get a character at position `pos`, use square brackets `[pos]` or call the method [str.at(pos)](mdn:js/String/at). The first character starts from the zero position:
```js run
let str = `Hello`;
// the first character
alert( str[0] ); // H
-alert( str.charAt(0) ); // H
+alert( str.at(0) ); // H
// the last character
alert( str[str.length - 1] ); // o
+alert( str.at(-1) );
```
-The square brackets are a modern way of getting a character, while `charAt` exists mostly for historical reasons.
+As you can see, the `.at(pos)` method has a benefit of allowing negative position. If `pos` is negative, then it's counted from the end of the string.
-The only difference between them is that if no character is found, `[]` returns `undefined`, and `charAt` returns an empty string:
+So `.at(-1)` means the last character, and `.at(-2)` is the one before it, etc.
+
+The square brackets always return `undefined` for negative indexes, for instance:
```js run
let str = `Hello`;
-alert( str[1000] ); // undefined
-alert( str.charAt(1000) ); // '' (an empty string)
+alert( str[-2] ); // undefined
+alert( str.at(-2) ); // l
```
We can also iterate over characters using `for..of`:
@@ -310,45 +298,6 @@ if (str.indexOf("Widget") != -1) {
}
```
-#### The bitwise NOT trick
-
-One of the old tricks used here is the [bitwise NOT](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_NOT) `~` operator. It converts the number to a 32-bit integer (removes the decimal part if exists) and then reverses all bits in its binary representation.
-
-In practice, that means a simple thing: for 32-bit integers `~n` equals `-(n+1)`.
-
-For instance:
-
-```js run
-alert( ~2 ); // -3, the same as -(2+1)
-alert( ~1 ); // -2, the same as -(1+1)
-alert( ~0 ); // -1, the same as -(0+1)
-*!*
-alert( ~-1 ); // 0, the same as -(-1+1)
-*/!*
-```
-
-As we can see, `~n` is zero only if `n == -1` (that's for any 32-bit signed integer `n`).
-
-So, the test `if ( ~str.indexOf("...") )` is truthy only if the result of `indexOf` is not `-1`. In other words, when there is a match.
-
-People use it to shorten `indexOf` checks:
-
-```js run
-let str = "Widget";
-
-if (~str.indexOf("Widget")) {
- alert( 'Found it!' ); // works
-}
-```
-
-It is usually not recommended to use language features in a non-obvious way, but this particular trick is widely used in old code, so we should understand it.
-
-Just remember: `if (~str.indexOf(...))` reads as "if found".
-
-To be precise though, as big numbers are truncated to 32 bits by `~` operator, there exist other numbers that give `0`, the smallest is `~4294967295=0`. That makes such check correct only if a string is not that long.
-
-Right now we can see this trick only in the old code, as modern JavaScript provides `.includes` method (see below).
-
### includes, startsWith, endsWith
The more modern method [str.includes(substr, pos)](mdn:js/String/includes) returns `true/false` depending on whether `str` contains `substr` within.
@@ -407,9 +356,9 @@ There are 3 methods in JavaScript to get a substring: `substring`, `substr` and
```
`str.substring(start [, end])`
-: Returns the part of the string *between* `start` and `end`.
+: Returns the part of the string *between* `start` and `end` (not including `end`).
- This is almost the same as `slice`, but it allows `start` to be greater than `end`.
+ This is almost the same as `slice`, but it allows `start` to be greater than `end` (in this case it simply swaps `start` and `end` values).
For instance:
@@ -452,13 +401,15 @@ Let's recap these methods to avoid any confusion:
| method | selects... | negatives |
|--------|-----------|-----------|
| `slice(start, end)` | from `start` to `end` (not including `end`) | allows negatives |
-| `substring(start, end)` | between `start` and `end` | negative values mean `0` |
+| `substring(start, end)` | between `start` and `end` (not including `end`)| negative values mean `0` |
| `substr(start, length)` | from `start` get `length` characters | allows negative `start` |
```smart header="Which one to choose?"
All of them can do the job. Formally, `substr` has a minor drawback: it is described not in the core JavaScript specification, but in Annex B, which covers browser-only features that exist mainly for historical reasons. So, non-browser environments may fail to support it. But in practice it works everywhere.
-Of the other two variants, `slice` is a little bit more flexible, it allows negative arguments and shorter to write. So, it's enough to remember solely `slice` of these three methods.
+Of the other two variants, `slice` is a little bit more flexible, it allows negative arguments and shorter to write.
+
+So, for practical use it's enough to remember only `slice`.
```
## Comparing strings
@@ -481,17 +432,18 @@ Although, there are some oddities.
This may lead to strange results if we sort these country names. Usually people would expect `Zealand` to come after `Österreich` in the list.
-To understand what happens, let's review the internal representation of strings in JavaScript.
+To understand what happens, we should be aware that strings in Javascript are encoded using [UTF-16](https://en.wikipedia.org/wiki/UTF-16). That is: each character has a corresponding numeric code.
-All strings are encoded using [UTF-16](https://en.wikipedia.org/wiki/UTF-16). That is: each character has a corresponding numeric code. There are special methods that allow to get the character for the code and back.
+There are special methods that allow to get the character for the code and back:
`str.codePointAt(pos)`
-: Returns the code for the character at position `pos`:
+: Returns a decimal number representing the code for the character at position `pos`:
```js run
// different case letters have different codes
- alert( "z".codePointAt(0) ); // 122
alert( "Z".codePointAt(0) ); // 90
+ alert( "z".codePointAt(0) ); // 122
+ alert( "z".codePointAt(0).toString(16) ); // 7a (if we need a hexadecimal value)
```
`String.fromCodePoint(code)`
@@ -499,13 +451,7 @@ All strings are encoded using [UTF-16](https://en.wikipedia.org/wiki/UTF-16). Th
```js run
alert( String.fromCodePoint(90) ); // Z
- ```
-
- We can also add Unicode characters by their codes using `\u` followed by the hex code:
-
- ```js run
- // 90 is 5a in hexadecimal system
- alert( '\u005a' ); // Z
+ alert( String.fromCodePoint(0x5a) ); // Z (we can also use a hex value as an argument)
```
Now let's see the characters with codes `65..220` (the latin alphabet and a little bit extra) by making a string of them:
@@ -517,6 +463,7 @@ for (let i = 65; i <= 220; i++) {
str += String.fromCodePoint(i);
}
alert( str );
+// Output:
// ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
// ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜ
```
@@ -536,7 +483,7 @@ The "right" algorithm to do string comparisons is more complex than it may seem,
So, the browser needs to know the language to compare.
-Luckily, all modern browsers (IE10- requires the additional library [Intl.js](https://github.com/andyearnshaw/Intl.js/)) support the internationalization standard [ECMA-402](http://www.ecma-international.org/ecma-402/1.0/ECMA-402.pdf).
+Luckily, modern browsers support the internationalization standard [ECMA-402](https://www.ecma-international.org/publications-and-standards/standards/ecma-402/).
It provides a special method to compare strings in different languages, following their rules.
@@ -554,118 +501,10 @@ alert( 'Österreich'.localeCompare('Zealand') ); // -1
This method actually has two additional arguments specified in [the documentation](mdn:js/String/localeCompare), which allows it to specify the language (by default taken from the environment, letter order depends on the language) and setup additional rules like case sensitivity or should `"a"` and `"á"` be treated as the same etc.
-## Internals, Unicode
-
-```warn header="Advanced knowledge"
-The section goes deeper into string internals. This knowledge will be useful for you if you plan to deal with emoji, rare mathematical or hieroglyphic characters or other rare symbols.
-
-You can skip the section if you don't plan to support them.
-```
-
-### Surrogate pairs
-
-All frequently used characters have 2-byte codes. Letters in most european languages, numbers, and even most hieroglyphs, have a 2-byte representation.
-
-But 2 bytes only allow 65536 combinations and that's not enough for every possible symbol. So rare symbols are encoded with a pair of 2-byte characters called "a surrogate pair".
-
-The length of such symbols is `2`:
-
-```js run
-alert( '𝒳'.length ); // 2, MATHEMATICAL SCRIPT CAPITAL X
-alert( '😂'.length ); // 2, FACE WITH TEARS OF JOY
-alert( '𩷶'.length ); // 2, a rare Chinese hieroglyph
-```
-
-Note that surrogate pairs did not exist at the time when JavaScript was created, and thus are not correctly processed by the language!
-
-We actually have a single symbol in each of the strings above, but the `length` shows a length of `2`.
-
-`String.fromCodePoint` and `str.codePointAt` are few rare methods that deal with surrogate pairs right. They recently appeared in the language. Before them, there were only [String.fromCharCode](mdn:js/String/fromCharCode) and [str.charCodeAt](mdn:js/String/charCodeAt). These methods are actually the same as `fromCodePoint/codePointAt`, but don't work with surrogate pairs.
-
-Getting a symbol can be tricky, because surrogate pairs are treated as two characters:
-
-```js run
-alert( '𝒳'[0] ); // strange symbols...
-alert( '𝒳'[1] ); // ...pieces of the surrogate pair
-```
-
-Note that pieces of the surrogate pair have no meaning without each other. So the alerts in the example above actually display garbage.
-
-Technically, surrogate pairs are also detectable by their codes: if a character has the code in the interval of `0xd800..0xdbff`, then it is the first part of the surrogate pair. The next character (second part) must have the code in interval `0xdc00..0xdfff`. These intervals are reserved exclusively for surrogate pairs by the standard.
-
-In the case above:
-
-```js run
-// charCodeAt is not surrogate-pair aware, so it gives codes for parts
-
-alert( '𝒳'.charCodeAt(0).toString(16) ); // d835, between 0xd800 and 0xdbff
-alert( '𝒳'.charCodeAt(1).toString(16) ); // dcb3, between 0xdc00 and 0xdfff
-```
-
-You will find more ways to deal with surrogate pairs later in the chapter . There are probably special libraries for that too, but nothing famous enough to suggest here.
-
-### Diacritical marks and normalization
-
-In many languages, there are symbols that are composed of the base character with a mark above/under it.
-
-For instance, the letter `a` can be the base character for: `àáâäãåā`. Most common "composite" character have their own code in the UTF-16 table. But not all of them, because there are too many possible combinations.
-
-To support arbitrary compositions, UTF-16 allows us to use several Unicode characters: the base character followed by one or many "mark" characters that "decorate" it.
-
-For instance, if we have `S` followed by the special "dot above" character (code `\u0307`), it is shown as Ṡ.
-
-```js run
-alert( 'S\u0307' ); // Ṡ
-```
-
-If we need an additional mark above the letter (or below it) -- no problem, just add the necessary mark character.
-
-For instance, if we append a character "dot below" (code `\u0323`), then we'll have "S with dots above and below": `Ṩ`.
-
-For example:
-
-```js run
-alert( 'S\u0307\u0323' ); // Ṩ
-```
-
-This provides great flexibility, but also an interesting problem: two characters may visually look the same, but be represented with different Unicode compositions.
-
-For instance:
-
-```js run
-let s1 = 'S\u0307\u0323'; // Ṩ, S + dot above + dot below
-let s2 = 'S\u0323\u0307'; // Ṩ, S + dot below + dot above
-
-alert( `s1: ${s1}, s2: ${s2}` );
-
-alert( s1 == s2 ); // false though the characters look identical (?!)
-```
-
-To solve this, there exists a "Unicode normalization" algorithm that brings each string to the single "normal" form.
-
-It is implemented by [str.normalize()](mdn:js/String/normalize).
-
-```js run
-alert( "S\u0307\u0323".normalize() == "S\u0323\u0307".normalize() ); // true
-```
-
-It's funny that in our situation `normalize()` actually brings together a sequence of 3 characters to one: `\u1e68` (S with two dots).
-
-```js run
-alert( "S\u0307\u0323".normalize().length ); // 1
-
-alert( "S\u0307\u0323".normalize() == "\u1e68" ); // true
-```
-
-In reality, this is not always the case. The reason being that the symbol `Ṩ` is "common enough", so UTF-16 creators included it in the main table and gave it the code.
-
-If you want to learn more about normalization rules and variants -- they are described in the appendix of the Unicode standard: [Unicode Normalization Forms](http://www.unicode.org/reports/tr15/), but for most practical purposes the information from this section is enough.
-
## Summary
- There are 3 types of quotes. Backticks allow a string to span multiple lines and embed expressions `${…}`.
-- Strings in JavaScript are encoded using UTF-16.
-- We can use special characters like `\n` and insert letters by their Unicode using `\u...`.
+- We can use special characters, such as a line break `\n`.
- To get a character, use: `[]`.
- To get a substring, use: `slice` or `substring`.
- To lowercase/uppercase a string, use: `toLowerCase/toUpperCase`.
@@ -679,3 +518,5 @@ There are several other helpful methods in strings:
- ...and more to be found in the [manual](mdn:js/String).
Strings also have methods for doing search/replace with regular expressions. But that's big topic, so it's explained in a separate tutorial section .
+
+Also, as of now it's important to know that strings are based on Unicode encoding, and hence there're issues with comparisons. There's more about Unicode in the chapter .
\ No newline at end of file
diff --git a/1-js/05-data-types/04-array/10-maximal-subarray/solution.md b/1-js/05-data-types/04-array/10-maximal-subarray/solution.md
index befd80296..7e1ca3bde 100644
--- a/1-js/05-data-types/04-array/10-maximal-subarray/solution.md
+++ b/1-js/05-data-types/04-array/10-maximal-subarray/solution.md
@@ -59,7 +59,7 @@ alert( getMaxSubSum([100, -9, 2, -3, 5]) ); // 100
The solution has a time complexity of [O(n2)](https://en.wikipedia.org/wiki/Big_O_notation). In other words, if we increase the array size 2 times, the algorithm will work 4 times longer.
-For big arrays (1000, 10000 or more items) such algorithms can lead to a serious sluggishness.
+For big arrays (1000, 10000 or more items) such algorithms can lead to serious sluggishness.
# Fast solution
@@ -91,4 +91,4 @@ alert( getMaxSubSum([-1, -2, -3]) ); // 0
The algorithm requires exactly 1 array pass, so the time complexity is O(n).
-You can find more detail information about the algorithm here: [Maximum subarray problem](http://en.wikipedia.org/wiki/Maximum_subarray_problem). If it's still not obvious why that works, then please trace the algorithm on the examples above, see how it works, that's better than any words.
+You can find more detailed information about the algorithm here: [Maximum subarray problem](http://en.wikipedia.org/wiki/Maximum_subarray_problem). If it's still not obvious why that works, then please trace the algorithm on the examples above, see how it works, that's better than any words.
diff --git a/1-js/05-data-types/04-array/2-create-array/task.md b/1-js/05-data-types/04-array/2-create-array/task.md
index 16d14071f..d4551c79c 100644
--- a/1-js/05-data-types/04-array/2-create-array/task.md
+++ b/1-js/05-data-types/04-array/2-create-array/task.md
@@ -8,7 +8,7 @@ Let's try 5 array operations.
1. Create an array `styles` with items "Jazz" and "Blues".
2. Append "Rock-n-Roll" to the end.
-3. Replace the value in the middle by "Classics". Your code for finding the middle value should work for any arrays with odd length.
+3. Replace the value in the middle with "Classics". Your code for finding the middle value should work for any arrays with odd length.
4. Strip off the first value of the array and show it.
5. Prepend `Rap` and `Reggae` to the array.
diff --git a/1-js/05-data-types/07-map-set/article.md b/1-js/05-data-types/07-map-set/article.md
index bd6cad562..354070888 100644
--- a/1-js/05-data-types/07-map-set/article.md
+++ b/1-js/05-data-types/07-map-set/article.md
@@ -15,12 +15,12 @@ But that's not enough for real life. That's why `Map` and `Set` also exist.
Methods and properties are:
- `new Map()` -- creates the map.
-- `map.set(key, value)` -- stores the value by the key.
-- `map.get(key)` -- returns the value by the key, `undefined` if `key` doesn't exist in map.
-- `map.has(key)` -- returns `true` if the `key` exists, `false` otherwise.
-- `map.delete(key)` -- removes the value by the key.
-- `map.clear()` -- removes everything from the map.
-- `map.size` -- returns the current element count.
+- [`map.set(key, value)`](mdn:js/Map/set) -- stores the value by the key.
+- [`map.get(key)`](mdn:js/Map/get) -- returns the value by the key, `undefined` if `key` doesn't exist in map.
+- [`map.has(key)`](mdn:js/Map/has) -- returns `true` if the `key` exists, `false` otherwise.
+- [`map.delete(key)`](mdn:js/Map/delete) -- removes the value by the key.
+- [`map.clear()`](mdn:js/Map/clear) -- removes everything from the map.
+- [`map.size`](mdn:js/Map/size) -- returns the current element count.
For instance:
@@ -105,9 +105,9 @@ map.set('1', 'str1')
For looping over a `map`, there are 3 methods:
-- `map.keys()` -- returns an iterable for keys,
-- `map.values()` -- returns an iterable for values,
-- `map.entries()` -- returns an iterable for entries `[key, value]`, it's used by default in `for..of`.
+- [`map.keys()`](mdn:js/Map/keys) -- returns an iterable for keys,
+- [`map.values()`](mdn:js/Map/values) -- returns an iterable for values,
+- [`map.entries()`](mdn:js/Map/entries) -- returns an iterable for entries `[key, value]`, it's used by default in `for..of`.
For instance:
@@ -238,11 +238,11 @@ A `Set` is a special type collection - "set of values" (without keys), where eac
Its main methods are:
- `new Set(iterable)` -- creates the set, and if an `iterable` object is provided (usually an array), copies values from it into the set.
-- `set.add(value)` -- adds a value, returns the set itself.
-- `set.delete(value)` -- removes the value, returns `true` if `value` existed at the moment of the call, otherwise `false`.
-- `set.has(value)` -- returns `true` if the value exists in the set, otherwise `false`.
-- `set.clear()` -- removes everything from the set.
-- `set.size` -- is the elements count.
+- [`set.add(value)`](mdn:js/Set/add) -- adds a value, returns the set itself.
+- [`set.delete(value)`](mdn:js/Set/delete) -- removes the value, returns `true` if `value` existed at the moment of the call, otherwise `false`.
+- [`set.has(value)`](mdn:js/Set/has) -- returns `true` if the value exists in the set, otherwise `false`.
+- [`set.clear()`](mdn:js/Set/clear) -- removes everything from the set.
+- [`set.size`](mdn:js/Set/size) -- is the elements count.
The main feature is that repeated calls of `set.add(value)` with the same value don't do anything. That's the reason why each value appears in a `Set` only once.
@@ -291,13 +291,13 @@ set.forEach((value, valueAgain, set) => {
Note the funny thing. The callback function passed in `forEach` has 3 arguments: a `value`, then *the same value* `valueAgain`, and then the target object. Indeed, the same value appears in the arguments twice.
-That's for compatibility with `Map` where the callback passed `forEach` has three arguments. Looks a bit strange, for sure. But may help to replace `Map` with `Set` in certain cases with ease, and vice versa.
+That's for compatibility with `Map` where the callback passed `forEach` has three arguments. Looks a bit strange, for sure. But this may help to replace `Map` with `Set` in certain cases with ease, and vice versa.
The same methods `Map` has for iterators are also supported:
-- `set.keys()` -- returns an iterable object for values,
-- `set.values()` -- same as `set.keys()`, for compatibility with `Map`,
-- `set.entries()` -- returns an iterable object for entries `[value, value]`, exists for compatibility with `Map`.
+- [`set.keys()`](mdn:js/Set/keys) -- returns an iterable object for values,
+- [`set.values()`](mdn:js/Set/values) -- same as `set.keys()`, for compatibility with `Map`,
+- [`set.entries()`](mdn:js/Set/entries) -- returns an iterable object for entries `[value, value]`, exists for compatibility with `Map`.
## Summary
@@ -306,12 +306,12 @@ The same methods `Map` has for iterators are also supported:
Methods and properties:
- `new Map([iterable])` -- creates the map, with optional `iterable` (e.g. array) of `[key,value]` pairs for initialization.
-- `map.set(key, value)` -- stores the value by the key, returns the map itself.
-- `map.get(key)` -- returns the value by the key, `undefined` if `key` doesn't exist in map.
-- `map.has(key)` -- returns `true` if the `key` exists, `false` otherwise.
-- `map.delete(key)` -- removes the value by the key, returns `true` if `key` existed at the moment of the call, otherwise `false`.
-- `map.clear()` -- removes everything from the map.
-- `map.size` -- returns the current element count.
+- [`map.set(key, value)`](mdn:js/Map/set) -- stores the value by the key, returns the map itself.
+- [`map.get(key)`](mdn:js/Map/get) -- returns the value by the key, `undefined` if `key` doesn't exist in map.
+- [`map.has(key)`](mdn:js/Map/has) -- returns `true` if the `key` exists, `false` otherwise.
+- [`map.delete(key)`](mdn:js/Map/delete) -- removes the value by the key, returns `true` if `key` existed at the moment of the call, otherwise `false`.
+- [`map.clear()`](mdn:js/Map/clear) -- removes everything from the map.
+- [`map.size`](mdn:js/Map/size) -- returns the current element count.
The differences from a regular `Object`:
@@ -323,10 +323,10 @@ The differences from a regular `Object`:
Methods and properties:
- `new Set([iterable])` -- creates the set, with optional `iterable` (e.g. array) of values for initialization.
-- `set.add(value)` -- adds a value (does nothing if `value` exists), returns the set itself.
-- `set.delete(value)` -- removes the value, returns `true` if `value` existed at the moment of the call, otherwise `false`.
-- `set.has(value)` -- returns `true` if the value exists in the set, otherwise `false`.
-- `set.clear()` -- removes everything from the set.
-- `set.size` -- is the elements count.
+- [`set.add(value)`](mdn:js/Set/add) -- adds a value (does nothing if `value` exists), returns the set itself.
+- [`set.delete(value)`](mdn:js/Set/delete) -- removes the value, returns `true` if `value` existed at the moment of the call, otherwise `false`.
+- [`set.has(value)`](mdn:js/Set/has) -- returns `true` if the value exists in the set, otherwise `false`.
+- [`set.clear()`](mdn:js/Set/clear) -- removes everything from the set.
+- [`set.size`](mdn:js/Set/size) -- is the elements count.
Iteration over `Map` and `Set` is always in the insertion order, so we can't say that these collections are unordered, but we can't reorder elements or directly get an element by its number.
diff --git a/1-js/06-advanced-functions/01-recursion/article.md b/1-js/06-advanced-functions/01-recursion/article.md
index b7992f162..5ae894474 100644
--- a/1-js/06-advanced-functions/01-recursion/article.md
+++ b/1-js/06-advanced-functions/01-recursion/article.md
@@ -61,7 +61,7 @@ When `pow(x, n)` is called, the execution splits into two branches:
if n==1 = x
/
pow(x, n) =
- \
+ \
else = x * pow(x, n - 1)
```
@@ -285,7 +285,7 @@ The iterative `pow` uses a single context changing `i` and `result` in the proce
**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.
+...But sometimes the rewrite is non-trivial, especially when a 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.
diff --git a/1-js/06-advanced-functions/08-settimeout-setinterval/article.md b/1-js/06-advanced-functions/08-settimeout-setinterval/article.md
index fa1d98cb9..5a40238b1 100644
--- a/1-js/06-advanced-functions/08-settimeout-setinterval/article.md
+++ b/1-js/06-advanced-functions/08-settimeout-setinterval/article.md
@@ -102,7 +102,7 @@ As we can see from `alert` output, in a browser the timer identifier is a number
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.
+For browsers, timers are described in the [timers section](https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#timers) of HTML Living Standard.
## setInterval
@@ -256,7 +256,7 @@ The first line "puts the call into calendar after 0ms". But the scheduler will o
There are also advanced browser-related use cases of zero-delay timeout, that we'll discuss in the chapter .
````smart header="Zero delay is in fact not zero (in a browser)"
-In the browser, there's a limitation of how often nested timers can run. The [HTML5 standard](https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#timers) says: "after five nested timers, the interval is forced to be at least 4 milliseconds.".
+In the browser, there's a limitation of how often nested timers can run. The [HTML Living Standard](https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#timers) says: "after five nested timers, the interval is forced to be at least 4 milliseconds.".
Let's demonstrate what it means with the example below. The `setTimeout` call in it re-schedules itself with zero delay. Each call remembers the real time from the previous one in the `times` array. What do the real delays look like? Let's see:
diff --git a/1-js/12-generators-iterators/1-generators/01-pseudo-random-generator/solution.md b/1-js/12-generators-iterators/1-generators/01-pseudo-random-generator/solution.md
index af2ad0eed..4355d0cfc 100644
--- a/1-js/12-generators-iterators/1-generators/01-pseudo-random-generator/solution.md
+++ b/1-js/12-generators-iterators/1-generators/01-pseudo-random-generator/solution.md
@@ -3,7 +3,7 @@ function* pseudoRandom(seed) {
let value = seed;
while(true) {
- value = value * 16807 % 2147483647
+ value = value * 16807 % 2147483647;
yield value;
}
diff --git a/1-js/13-modules/02-import-export/article.md b/1-js/13-modules/02-import-export/article.md
index 8af1b3b10..ccbf18cf5 100644
--- a/1-js/13-modules/02-import-export/article.md
+++ b/1-js/13-modules/02-import-export/article.md
@@ -46,7 +46,7 @@ Also, we can put `export` separately.
Here we first declare, and then export:
-```js
+```js
// 📁 say.js
function sayHi(user) {
alert(`Hello, ${user}!`);
@@ -93,25 +93,14 @@ At first sight, "import everything" seems such a cool thing, short to write, why
Well, there are few reasons.
-1. Modern build tools ([webpack](https://webpack.js.org/) and others) bundle modules together and optimize them to speedup loading and remove unused stuff.
-
- Let's say, we added a 3rd-party library `say.js` to our project with many functions:
- ```js
- // 📁 say.js
- export function sayHi() { ... }
- export function sayBye() { ... }
- export function becomeSilent() { ... }
- ```
+1. Explicitly listing what to import gives shorter names: `sayHi()` instead of `say.sayHi()`.
+2. Explicit list of imports gives better overview of the code structure: what is used and where. It makes code support and refactoring easier.
- Now if we only use one of `say.js` functions in our project:
- ```js
- // 📁 main.js
- import {sayHi} from './say.js';
- ```
- ...Then the optimizer will see that and remove the other functions from the bundled code, thus making the build smaller. That is called "tree-shaking".
+```smart header="Don't be afraid to import too much"
+Modern build tools, such as [webpack](https://webpack.js.org/) and others, bundle modules together and optimize them to speedup loading. They also removed unused imports.
-2. Explicitly listing what to import gives shorter names: `sayHi()` instead of `say.sayHi()`.
-3. Explicit list of imports gives better overview of the code structure: what is used and where. It makes code support and refactoring easier.
+For instance, if you `import * as library` from a huge code library, and then use only few methods, then unused ones [will not be included](https://github.com/webpack/webpack/tree/main/examples/harmony-unused#examplejs) into the optimzed bundle.
+```
## Import "as"
@@ -224,7 +213,7 @@ Without `default`, such an export would give an error:
export class { // Error! (non-default export needs a name)
constructor() {}
}
-```
+```
### The "default" name
@@ -326,7 +315,7 @@ Imagine, we're writing a "package": a folder with a lot of modules, with some of
The file structure could be like this:
```
auth/
- index.js
+ index.js
user.js
helpers.js
tests/
@@ -372,7 +361,7 @@ The syntax `export ... from ...` is just a shorter notation for such import-expo
```js
// 📁 auth/index.js
-// re-export login/logout
+// re-export login/logout
export {login, logout} from './helpers.js';
// re-export the default export as User
@@ -380,7 +369,7 @@ export {default as User} from './user.js';
...
```
-The notable difference of `export ... from` compared to `import/export` is that re-exported modules aren't available in the current file. So inside the above example of `auth/index.js` we can't use re-exported `login/logout` functions.
+The notable difference of `export ... from` compared to `import/export` is that re-exported modules aren't available in the current file. So inside the above example of `auth/index.js` we can't use re-exported `login/logout` functions.
### Re-exporting the default export
@@ -399,7 +388,7 @@ We can come across two problems with it:
1. `export User from './user.js'` won't work. That would lead to a syntax error.
- To re-export the default export, we have to write `export {default as User}`, as in the example above.
+ To re-export the default export, we have to write `export {default as User}`, as in the example above.
2. `export * from './user.js'` re-exports only named exports, but ignores the default one.
@@ -430,7 +419,7 @@ Import:
- Importing named exports:
- `import {x [as y], ...} from "module"`
-- Importing the default export:
+- Importing the default export:
- `import x from "module"`
- `import {default as x} from "module"`
- Import all:
diff --git a/1-js/99-js-misc/04-reference-type/article.md b/1-js/99-js-misc/04-reference-type/article.md
index 6861837a9..894db8fc6 100644
--- a/1-js/99-js-misc/04-reference-type/article.md
+++ b/1-js/99-js-misc/04-reference-type/article.md
@@ -87,7 +87,7 @@ The result of a property access `user.hi` is not a function, but a value of Refe
(user, "hi", true)
```
-When parentheses `()` are called on the Reference Type, they receive the full information about the object and its method, and can set the right `this` (`=user` in this case).
+When parentheses `()` are called on the Reference Type, they receive the full information about the object and its method, and can set the right `this` (`user` in this case).
Reference type is a special "intermediary" internal type, with the purpose to pass information from dot `.` to calling parentheses `()`.
diff --git a/1-js/99-js-misc/06-unicode/article.md b/1-js/99-js-misc/06-unicode/article.md
new file mode 100644
index 000000000..2396fcfaf
--- /dev/null
+++ b/1-js/99-js-misc/06-unicode/article.md
@@ -0,0 +1,172 @@
+
+# Unicode, String internals
+
+```warn header="Advanced knowledge"
+The section goes deeper into string internals. This knowledge will be useful for you if you plan to deal with emoji, rare mathematical or hieroglyphic characters or other rare symbols.
+```
+
+As we already know, JavaScript strings are based on [Unicode](https://en.wikipedia.org/wiki/Unicode): each character is represented by a byte sequence of 1-4 bytes.
+
+JavaScript allows us to insert a character into a string by specifying its hexadecimal Unicode code with one of these three notations:
+
+- `\xXX`
+
+ `XX` must be two hexadecimal digits with value between `00` and `FF`, then it's character whose Unicode code is `XX`.
+
+ Because the `\xXX` notation supports only two digits, it can be used only for the first 256 Unicode characters.
+
+ These first 256 characters include latin alphabet, most basic syntax characters and some others. For example, `"\x7A"` is the same as `"z"` (Unicode `U+007A`).
+
+ ```js run
+ alert( "\x7A" ); // z
+ alert( "\xA9" ); // ©, the copyright symbol
+ ```
+
+- `\uXXXX`
+ `XXXX` must be exactly 4 hex digits with the value between `0000` and `FFFF`, then `\uXXXX` is a character whose Unicode code is `XXXX` .
+
+ Characters with Unicode value greater than `U+FFFF` can also be represented with this notation, but in this case we will need to use a so called surrogate pair (we will talk about surrogate pairs later in this chapter).
+
+ ```js run
+ alert( "\u00A9" ); // ©, the same as \xA9, using the 4-digit hex notation
+ alert( "\u044F" ); // я, the cyrillic alphabet letter
+ alert( "\u2191" ); // ↑, the arrow up symbol
+ ```
+
+- `\u{X…XXXXXX}`
+
+ `X…XXXXXX` must be a hexadecimal value of 1 to 6 bytes between `0` and `10FFFF` (the highest code point defined by Unicode). This notation allows us to easily represent all existing Unicode characters.
+
+ ```js run
+ alert( "\u{20331}" ); // 佫, a rare Chinese hieroglyph (long Unicode)
+ alert( "\u{1F60D}" ); // 😍, a smiling face symbol (another long Unicode)
+ ```
+
+## Surrogate pairs
+
+All frequently used characters have 2-byte codes. Letters in most european languages, numbers, and even most hieroglyphs, have a 2-byte representation.
+
+Initially, JavaScript was based on UTF-16 encoding that only allowed 2 bytes per character. But 2 bytes only allow 65536 combinations and that's not enough for every possible symbol of Unicode.
+
+So rare symbols that require more than 2 bytes are encoded with a pair of 2-byte characters called "a surrogate pair".
+
+As a side effect, the length of such symbols is `2`:
+
+```js run
+alert( '𝒳'.length ); // 2, MATHEMATICAL SCRIPT CAPITAL X
+alert( '😂'.length ); // 2, FACE WITH TEARS OF JOY
+alert( '𩷶'.length ); // 2, a rare Chinese hieroglyph
+```
+
+That's because surrogate pairs did not exist at the time when JavaScript was created, and thus are not correctly processed by the language!
+
+We actually have a single symbol in each of the strings above, but the `length` property shows a length of `2`.
+
+Getting a symbol can also be tricky, because most language features treat surrogate pairs as two characters.
+
+For example, here we can see two odd characters in the output:
+
+```js run
+alert( '𝒳'[0] ); // shows strange symbols...
+alert( '𝒳'[1] ); // ...pieces of the surrogate pair
+```
+
+Pieces of a surrogate pair have no meaning without each other. So the alerts in the example above actually display garbage.
+
+Technically, surrogate pairs are also detectable by their codes: if a character has the code in the interval of `0xd800..0xdbff`, then it is the first part of the surrogate pair. The next character (second part) must have the code in interval `0xdc00..0xdfff`. These intervals are reserved exclusively for surrogate pairs by the standard.
+
+So the methods `String.fromCodePoint` and `str.codePointAt` were added in JavaScript to deal with surrogate pairs.
+
+They are essentially the same as [String.fromCharCode](mdn:js/String/fromCharCode) and [str.charCodeAt](mdn:js/String/charCodeAt), but they treat surrogate pairs correctly.
+
+One can see the difference here:
+
+```js run
+// charCodeAt is not surrogate-pair aware, so it gives codes for the 1st part of 𝒳:
+
+alert( '𝒳'.charCodeAt(0).toString(16) ); // d835
+
+// codePointAt is surrogate-pair aware
+alert( '𝒳'.codePointAt(0).toString(16) ); // 1d4b3, reads both parts of the surrogate pair
+```
+
+That said, if we take from position 1 (and that's rather incorrect here), then they both return only the 2nd part of the pair:
+
+```js run
+alert( '𝒳'.charCodeAt(1).toString(16) ); // dcb3
+alert( '𝒳'.codePointAt(1).toString(16) ); // dcb3
+// meaningless 2nd half of the pair
+```
+
+You will find more ways to deal with surrogate pairs later in the chapter . There are probably special libraries for that too, but nothing famous enough to suggest here.
+
+````warn header="Takeaway: splitting strings at an arbitrary point is dangerous"
+We can't just split a string at an arbitrary position, e.g. take `str.slice(0, 4)` and expect it to be a valid string, e.g.:
+
+```js run
+alert( 'hi 😂'.slice(0, 4) ); // hi [?]
+```
+
+Here we can see a garbage character (first half of the smile surrogate pair) in the output.
+
+Just be aware of it if you intend to reliably work with surrogate pairs. May not be a big problem, but at least you should understand what happens.
+````
+
+## Diacritical marks and normalization
+
+In many languages, there are symbols that are composed of the base character with a mark above/under it.
+
+For instance, the letter `a` can be the base character for these characters: `àáâäãåā`.
+
+Most common "composite" characters have their own code in the Unicode table. But not all of them, because there are too many possible combinations.
+
+To support arbitrary compositions, Unicode standard allows us to use several Unicode characters: the base character followed by one or many "mark" characters that "decorate" it.
+
+For instance, if we have `S` followed by the special "dot above" character (code `\u0307`), it is shown as Ṡ.
+
+```js run
+alert( 'S\u0307' ); // Ṡ
+```
+
+If we need an additional mark above the letter (or below it) -- no problem, just add the necessary mark character.
+
+For instance, if we append a character "dot below" (code `\u0323`), then we'll have "S with dots above and below": `Ṩ`.
+
+For example:
+
+```js run
+alert( 'S\u0307\u0323' ); // Ṩ
+```
+
+This provides great flexibility, but also an interesting problem: two characters may visually look the same, but be represented with different Unicode compositions.
+
+For instance:
+
+```js run
+let s1 = 'S\u0307\u0323'; // Ṩ, S + dot above + dot below
+let s2 = 'S\u0323\u0307'; // Ṩ, S + dot below + dot above
+
+alert( `s1: ${s1}, s2: ${s2}` );
+
+alert( s1 == s2 ); // false though the characters look identical (?!)
+```
+
+To solve this, there exists a "Unicode normalization" algorithm that brings each string to the single "normal" form.
+
+It is implemented by [str.normalize()](mdn:js/String/normalize).
+
+```js run
+alert( "S\u0307\u0323".normalize() == "S\u0323\u0307".normalize() ); // true
+```
+
+It's funny that in our situation `normalize()` actually brings together a sequence of 3 characters to one: `\u1e68` (S with two dots).
+
+```js run
+alert( "S\u0307\u0323".normalize().length ); // 1
+
+alert( "S\u0307\u0323".normalize() == "\u1e68" ); // true
+```
+
+In reality, this is not always the case. The reason being that the symbol `Ṩ` is "common enough", so Unicode creators included it in the main table and gave it the code.
+
+If you want to learn more about normalization rules and variants -- they are described in the appendix of the Unicode standard: [Unicode Normalization Forms](https://www.unicode.org/reports/tr15/), but for most practical purposes the information from this section is enough.
diff --git a/2-ui/1-document/08-styles-and-classes/article.md b/2-ui/1-document/08-styles-and-classes/article.md
index 644583c34..46aaa3b00 100644
--- a/2-ui/1-document/08-styles-and-classes/article.md
+++ b/2-ui/1-document/08-styles-and-classes/article.md
@@ -269,20 +269,6 @@ So nowadays `getComputedStyle` actually returns the resolved value of the proper
We should always ask for the exact property that we want, like `paddingLeft` or `marginTop` or `borderTopWidth`. Otherwise the correct result is not guaranteed.
For instance, if there are properties `paddingLeft/paddingTop`, then what should we get for `getComputedStyle(elem).padding`? Nothing, or maybe a "generated" value from known paddings? There's no standard rule here.
-
-There are other inconsistencies. As an example, some browsers (Chrome) show `10px` in the document below, and some of them (Firefox) -- do not:
-
-```html run
-
-
-```
````
```smart header="Styles applied to `:visited` links are hidden!"
diff --git a/2-ui/2-events/01-introduction-browser-events/article.md b/2-ui/2-events/01-introduction-browser-events/article.md
index f75f6c11f..4eca222aa 100644
--- a/2-ui/2-events/01-introduction-browser-events/article.md
+++ b/2-ui/2-events/01-introduction-browser-events/article.md
@@ -24,7 +24,7 @@ Here's a list of the most useful DOM events, just to take a look at:
**CSS events:**
- `transitionend` -- when a CSS-animation finishes.
-There are many other events. We'll get into more details of particular events in next chapters.
+There are many other events. We'll get into more details of particular events in upcoming chapters.
## Event handlers
@@ -195,7 +195,7 @@ Assign a handler to `elem.onclick`, not `elem.ONCLICK`, because DOM properties a
## addEventListener
-The fundamental problem of the aforementioned ways to assign handlers -- we can't assign multiple handlers to one event.
+The fundamental problem of the aforementioned ways to assign handlers is that we *can't assign multiple handlers to one event*.
Let's say, one part of our code wants to highlight a button on click, and another one wants to show a message on the same click.
@@ -207,7 +207,7 @@ input.onclick = function() { alert(1); }
input.onclick = function() { alert(2); } // replaces the previous handler
```
-Developers of web standards understood that long ago and suggested an alternative way of managing handlers using special methods `addEventListener` and `removeEventListener`. They are free of such a problem.
+Developers of web standards understood that long ago and suggested an alternative way of managing handlers using the special methods `addEventListener` and `removeEventListener` which aren't bound by such constraint.
The syntax to add a handler:
@@ -261,7 +261,7 @@ input.removeEventListener("click", handler);
Please note -- if we don't store the function in a variable, then we can't remove it. There's no way to "read back" handlers assigned by `addEventListener`.
````
-Multiple calls to `addEventListener` allow to add multiple handlers, like this:
+Multiple calls to `addEventListener` allow it to add multiple handlers, like this:
```html run no-beautify
@@ -288,7 +288,7 @@ As we can see in the example above, we can set handlers *both* using a DOM-prope
````warn header="For some events, handlers only work with `addEventListener`"
There exist events that can't be assigned via a DOM-property. Only with `addEventListener`.
-For instance, the `DOMContentLoaded` event, that triggers when the document is loaded and DOM is built.
+For instance, the `DOMContentLoaded` event, that triggers when the document is loaded and the DOM has been built.
```js
// will never run
@@ -334,10 +334,10 @@ Some properties of `event` object:
`event.currentTarget`
: Element that handled the event. That's exactly the same as `this`, unless the handler is an arrow function, or its `this` is bound to something else, then we can get the element from `event.currentTarget`.
-`event.clientX / event.clientY`
+`event.clientX` / `event.clientY`
: Window-relative coordinates of the cursor, for pointer events.
-There are more properties. Many of them depend on the event type: keyboard events have one set of properties, pointer events - another one, we'll study them later when we come to different events in details.
+There are more properties. Many of them depend on the event type: keyboard events have one set of properties, pointer events - another one, we'll study them later when as we move on to the details of different events.
````smart header="The event object is also available in HTML handlers"
If we assign a handler in HTML, we can also use the `event` object, like this:
@@ -373,7 +373,7 @@ For instance:
As we can see, when `addEventListener` receives an object as the handler, it calls `obj.handleEvent(event)` in case of an event.
-We could also use a class for that:
+We could also use objects of a custom class, like this:
```html run
@@ -395,6 +395,7 @@ We could also use a class for that:
*!*
let menu = new Menu();
+
elem.addEventListener('mousedown', menu);
elem.addEventListener('mouseup', menu);
*/!*
diff --git a/2-ui/2-events/02-bubbling-and-capturing/article.md b/2-ui/2-events/02-bubbling-and-capturing/article.md
index 3d55b7d8c..1c85bdb15 100644
--- a/2-ui/2-events/02-bubbling-and-capturing/article.md
+++ b/2-ui/2-events/02-bubbling-and-capturing/article.md
@@ -192,7 +192,7 @@ There's a property `event.eventPhase` that tells us the number of the phase on w
If we `addEventListener(..., true)`, then we should mention the same phase in `removeEventListener(..., true)` to correctly remove the handler.
```
-````smart header="Listeners on same element and same phase run in their set order"
+````smart header="Listeners on the same element and same phase run in their set order"
If we have multiple event handlers on the same phase, assigned to the same element with `addEventListener`, they run in the same order as they are created:
```js
diff --git a/5-network/06-fetch-api/article.md b/5-network/06-fetch-api/article.md
index c3f75175f..16e2705f0 100644
--- a/5-network/06-fetch-api/article.md
+++ b/5-network/06-fetch-api/article.md
@@ -21,7 +21,7 @@ let promise = fetch(url, {
// depending on the request body
"Content-Type": "text/plain;charset=UTF-8"
},
- body: undefined // string, FormData, Blob, BufferSource, or URLSearchParams
+ body: undefined, // string, FormData, Blob, BufferSource, or URLSearchParams
referrer: "about:client", // or "" to send no Referer header,
// or an url from the current origin
referrerPolicy: "no-referrer-when-downgrade", // no-referrer, origin, same-origin...
diff --git a/6-data-storage/01-cookie/article.md b/6-data-storage/01-cookie/article.md
index b88849457..01c0e1fee 100644
--- a/6-data-storage/01-cookie/article.md
+++ b/6-data-storage/01-cookie/article.md
@@ -156,7 +156,7 @@ If we set `expires` to a date in the past, the cookie is deleted.
- **`max-age=3600`**
-Is an alternative to `expires` and specifies the cookie's expiration in seconds from the current moment.
+It's an alternative to `expires` and specifies the cookie's expiration in seconds from the current moment.
If set to zero or a negative value, the cookie is deleted:
diff --git a/8-web-components/5-slots-composition/article.md b/8-web-components/5-slots-composition/article.md
index c35824a9e..c41e26e05 100644
--- a/8-web-components/5-slots-composition/article.md
+++ b/8-web-components/5-slots-composition/article.md
@@ -381,7 +381,7 @@ If we'd like to track internal modifications of light DOM from JavaScript, that'
Finally, let's mention the slot-related JavaScript methods.
-As we've seen before, JavaScript looks at the "real" DOM, without flattening. But, if the shadow tree has `{mode: 'open'}`, then we can figure out which elements assigned to a slot and, vise-versa, the slot by the element inside it:
+As we've seen before, JavaScript looks at the "real" DOM, without flattening. But, if the shadow tree has `{mode: 'open'}`, then we can figure out which elements assigned to a slot and, vice-versa, the slot by the element inside it:
- `node.assignedSlot` -- returns the `` element that the `node` is assigned to.
- `slot.assignedNodes({flatten: true/false})` -- DOM nodes, assigned to the slot. The `flatten` option is `false` by default. If explicitly set to `true`, then it looks more deeply into the flattened DOM, returning nested slots in case of nested components and the fallback content if no node assigned.