Skip to content

Sync with upstream @ 540d753e #233

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 67 commits into from
Closed
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
023c0ec
Fixing a minor grammatical typo in the document.
rahulrao0209 Jul 16, 2023
d51037a
Fix grammar and add an example
nakhodkin Dec 27, 2023
c66bace
Fix grammar and typos
nakhodkin Dec 31, 2023
bbac8a5
Fix grammar and JavaScript syntax
nakhodkin Jan 2, 2024
b6c604a
Merge branch 'master' into patch-5
smith558 Jan 27, 2024
ea05aa9
Updated result visualization
Filin3 Mar 31, 2024
04b73bf
Merge pull request #3681 from Filin3/patch-1
smith558 May 5, 2024
acf339c
Merge pull request #3632 from nakhodkin/patch-2
smith558 May 5, 2024
541b7f9
Merge pull request #3636 from nakhodkin/patch-5
smith558 May 5, 2024
0b9bc2f
Merge pull request #3634 from nakhodkin/patch-4
smith558 May 5, 2024
85da6f1
Update article.md
ellie-heidari May 10, 2024
475899e
Update article.md
smith558 May 17, 2024
7e524ba
Add link
smith558 May 17, 2024
42851f4
Update task.md
pvepamb1 May 18, 2024
2092da7
Merge pull request #3694 from pvepamb1/patch-1
smith558 May 18, 2024
f684d39
change example element of multidimensional array
sneeed Jun 8, 2024
c151e11
minor fixes
iliakan Jun 13, 2024
3fd3f98
- `run`
Alexandre887 Jun 23, 2024
d1ffe5d
docs: remove eval polyfill.io
kricsleo Jul 5, 2024
262f91a
Merge pull request #3712 from kricsleo/patch-1
smith558 Jul 8, 2024
815fafa
Merge pull request #3692 from ellie-heidari/patch-1
smith558 Jul 8, 2024
d6e0376
Remove BigInt IE incompatibility part (#3709)
FloffyGarlic Jul 9, 2024
5a0df77
Update article.md
shallow-beach Jul 10, 2024
62827d8
Merge pull request #3715 from shallow-beach/master
smith558 Jul 10, 2024
4104eba
Merge pull request #3704 from Alexandre887/patch-10
smith558 Jul 10, 2024
ca72abb
Merge pull request #3700 from sneeed/patch-2
smith558 Jul 10, 2024
b258d7d
Merge pull request #3495 from rahulrao0209/patch-1
smith558 Jul 10, 2024
6f08958
minor fix to function name written in explanation
tonybishnoi Oct 9, 2024
eedc262
Grammatical fix
nikoandpiko Oct 22, 2024
34a80e7
Merge pull request #3768 from tonybishnoi/patch-1
smith558 Oct 24, 2024
67833c9
Update article.md
zakingslayerv22 Dec 11, 2024
b36823a
better wording
pj-szdm Dec 18, 2024
dc14378
Update article.md
mhi1627 Jan 22, 2025
8b2a2f2
Improve readability
AdityaGirdhar Jan 30, 2025
a2b97b5
Merge pull request #3810 from AdityaGirdhar/patch-1
smith558 Feb 4, 2025
793ad4b
Merge branch 'master' into patch-1
smith558 Feb 4, 2025
e466826
Merge pull request #3772 from nikoandpiko/patch-1
smith558 Feb 4, 2025
a087279
Merge pull request #3804 from mhi1627/patch-1
smith558 Feb 8, 2025
26daef2
Merge pull request #3794 from zakingslayerv22/patch-1
smith558 Feb 8, 2025
6236eb8
Merge pull request #3797 from pj-szdm/patch-1
smith558 Feb 8, 2025
011dd4f
Update article.md
Gleb-Pastushenko Feb 10, 2025
1dce5b7
Merge pull request #3815 from Gleb-Pastushenko/patch-6
smith558 Feb 10, 2025
4b3474b
Fixed missing closing parenthesis in 2-ui/4-forms-control/1-form-elem…
vincent-clipet Mar 10, 2025
3d7abb9
Merge pull request #3822 from vincent-clipet/fix/missing_parenthesis
smith558 Mar 10, 2025
3de63df
promise.all task
iliakan Mar 24, 2025
ef31066
minor fixes
iliakan Mar 24, 2025
de4247b
minor fixes
iliakan Mar 24, 2025
0af25bc
minor fixes
iliakan Mar 24, 2025
d932e52
minor fixes
iliakan Mar 24, 2025
f0d8abb
minor fixes
iliakan Mar 24, 2025
f775835
minor fixes
iliakan Mar 24, 2025
0760c90
minor fixes
iliakan Mar 24, 2025
5dea441
minor fixes
iliakan Mar 24, 2025
035c526
minor fixes
iliakan Mar 24, 2025
4c4598b
Fix for #3826 - Removed errorception.com from the respective md file.
Paramesh-T-S Mar 29, 2025
cde189d
Update Safari settings screenshots
dangerman Apr 2, 2025
78c6c44
Update Safari devtools instructions
dangerman Apr 2, 2025
e88c212
Update Function object, NFE article
rahulrao0209 Apr 6, 2025
a711a1f
Merge pull request #3830 from Paramesh-T-S/Issue_fix_Outdate_-link_in…
iliakan Apr 8, 2025
efaa9aa
sentry.io added as per suggestion - https://github.com/javascript-tut…
Paramesh-T-S Apr 8, 2025
6cc5077
Merge pull request #3835 from Paramesh-T-S/Issue_fix_Outdate_-link_in…
iliakan Apr 9, 2025
81cfee9
Update article.md
rahulrao0209 Apr 13, 2025
e2d8ebe
Merge pull request #3836 from rahulrao0209/patch-3
smith558 Apr 13, 2025
6bbdd0c
Merge pull request #3834 from rahulrao0209/patch-2
smith558 Apr 13, 2025
d3c395c
Merge pull request #3832 from dangerman/update-safari-devtools-screen…
smith558 Apr 13, 2025
540d753
Replace with up to date screenshots
smith558 Apr 13, 2025
53eaa18
merging all conflicts
iliakan Jun 2, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
github: iliakan
6 changes: 5 additions & 1 deletion 1-js/01-getting-started/4-devtools/article.md
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@

Դա ունի այսպիսի տեսք։

![chrome](chrome.png)
![chrome](chrome.webp)

Ծրագրավորման գործիքների տեսքը կախված է Chrome-ի տարբերակից: Այն ժամանակ առ ժամանակ փոխվում է, բայց պետք է հիմնականում նման լինի:

@@ -49,7 +49,11 @@

Safari (Mac զննիչ, հասանելի չէ Windows և Linux ՕՀ-երում) այն մի փոքր առանձնահատուկ է: Մենք նախ պետք է միացնենք «ծրագրավորման գործիքները»:

<<<<<<< HEAD
Բացեք Preferences էջը և գնացեք "Advanced" պատուհան։ Ներքևում checkbox կա ծրագրավորման գործիքների համար։
=======
Open Settings and go to the "Advanced" pane. There's a checkbox at the bottom:
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b
![safari](safari.png)

Binary file removed 1-js/01-getting-started/4-devtools/chrome.png
Binary file not shown.
Binary file added 1-js/01-getting-started/4-devtools/chrome.webp
Binary file not shown.
Binary file added 1-js/01-getting-started/4-devtools/chrome@2.webp
Binary file not shown.
Binary file removed 1-js/01-getting-started/4-devtools/chrome@2x.png
Binary file not shown.
Binary file modified 1-js/01-getting-started/4-devtools/safari.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified 1-js/01-getting-started/4-devtools/safari@2x.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions 1-js/02-first-steps/05-types/article.md
Original file line number Diff line number Diff line change
@@ -94,6 +94,7 @@ const bigInt = 1234567890123456789012345678901234567890n;

Քանի որ `BigInt` թվերը հազվադեպ են օգտագործվում, դրանց այստեղ չենք անդրադառնա, այլ կդիտարկենք առանձին՝ <info:bigint> գլխում։ Կարդացե՛ք այն, եթե նման մեծ թվերի օգտագոծման կարիք կունենաք։

<<<<<<< HEAD

```smart header="Համատեղելիության խնդիրներ"
Այս պահին `BigInt`-ը համատեղելի է Firefox/Chrome/Edge/Safari զննիչների հետ, բայց ոչ IE-ի։
@@ -102,6 +103,9 @@ const bigInt = 1234567890123456789012345678901234567890n;
Կարող եք ստուգել [*MDN* BigInt compatibility table](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#Browser_compatibility) հոդվածը, հասկանալու համար, թե զննիչների որ տարբերակների հետ է այն համատեղելի։

## Տող (String)
=======
## String
>>>>>>> 540d753e90789205fc6e75c502f68382c87dea9b

JavaScript-ում տողը պետք է շրջապատված լինի չակերտներով։

2 changes: 1 addition & 1 deletion 1-js/02-first-steps/16-function-expressions/article.md
Original file line number Diff line number Diff line change
@@ -82,7 +82,7 @@ let sayHi = function() { // (1) ստեղծել
alert( "Ողջույն" );
};

let func = sayHi;
let func = sayHi; //(2)
// ...
```

5 changes: 1 addition & 4 deletions 1-js/03-code-quality/06-polyfills/article.md
Original file line number Diff line number Diff line change
@@ -71,10 +71,7 @@ if (!Math.trunc) { // if no such function
JavaScript is a highly dynamic language. Scripts may add/modify any function, even built-in ones.
Two interesting polyfill libraries are:
- [core js](https://github.com/zloirock/core-js) that supports a lot, allows to include only needed features.
- [polyfill.io](https://polyfill.io/) service that provides a script with polyfills, depending on the features and user's browser.
One interesting polyfill library is [core-js](https://github.com/zloirock/core-js), which supports a wide range of features and allows you to include only the ones you need.
## Summary
8 changes: 4 additions & 4 deletions 1-js/04-object-basics/04-object-methods/8-chain-calls/task.md
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@ importance: 2

# Chaining

There's a `ladder` object that allows to go up and down:
There's a `ladder` object that allows you to go up and down:

```js
let ladder = {
@@ -21,7 +21,7 @@ let ladder = {
};
```

Now, if we need to make several calls in sequence, can do it like this:
Now, if we need to make several calls in sequence, we can do it like this:

```js
ladder.up();
@@ -32,10 +32,10 @@ ladder.down();
ladder.showStep(); // 0
```

Modify the code of `up`, `down` and `showStep` to make the calls chainable, like this:
Modify the code of `up`, `down`, and `showStep` to make the calls chainable, like this:

```js
ladder.up().up().down().showStep().down().showStep(); // shows 1 then 0
```

Such approach is widely used across JavaScript libraries.
Such an approach is widely used across JavaScript libraries.
2 changes: 1 addition & 1 deletion 1-js/04-object-basics/09-object-toprimitive/article.md
Original file line number Diff line number Diff line change
@@ -253,7 +253,7 @@ let obj = {
}
};

alert(obj + 2); // 22 ("2" + 2), conversion to primitive returned a string => concatenation
alert(obj + 2); // "22" ("2" + 2), conversion to primitive returned a string => concatenation
```

## Summary
40 changes: 24 additions & 16 deletions 1-js/05-data-types/02-number/article.md
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@ In modern JavaScript, there are two types of numbers:

1. Regular numbers in JavaScript are stored in 64-bit format [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754), also known as "double precision floating point numbers". These are numbers that we're using most of the time, and we'll talk about them in this chapter.

2. BigInt numbers represent integers of arbitrary length. They are sometimes needed because a regular integer number can't safely exceed <code>(2<sup>53</sup>-1)</code> or be less than <code>-(2<sup>53</sup>-1)</code>, as we mentioned earlier in the chapter <info:types>. As bigints are used in few special areas, we devote them a special chapter <info:bigint>.
2. BigInt numbers represent integers of arbitrary length. They are sometimes needed because a regular integer number can't safely exceed <code>(2<sup>53</sup>-1)</code> or be less than <code>-(2<sup>53</sup>-1)</code>, as we mentioned earlier in the chapter <info:types>. As bigints are used in a few special areas, we devote them to a special chapter <info:bigint>.

So here we'll talk about regular numbers. Let's expand our knowledge of them.

@@ -41,7 +41,7 @@ In other words, `e` multiplies the number by `1` with the given zeroes count.
1.23e6 === 1.23 * 1000000; // e6 means *1000000
```

Now let's write something very small. Say, 1 microsecond (one millionth of a second):
Now let's write something very small. Say, 1 microsecond (one-millionth of a second):

```js
let mсs = 0.000001;
@@ -103,13 +103,13 @@ alert( num.toString(16) ); // ff
alert( num.toString(2) ); // 11111111
```

The `base` can vary from `2` to `36`. By default it's `10`.
The `base` can vary from `2` to `36`. By default, it's `10`.

Common use cases for this are:

- **base=16** is used for hex colors, character encodings etc, digits can be `0..9` or `A..F`.
- **base=2** is mostly for debugging bitwise operations, digits can be `0` or `1`.
- **base=36** is the maximum, digits can be `0..9` or `A..Z`. The whole latin alphabet is used to represent a number. A funny, but useful case for `36` is when we need to turn a long numeric identifier into something shorter, for example to make a short url. Can simply represent it in the numeral system with base `36`:
- **base=36** is the maximum, digits can be `0..9` or `A..Z`. The whole Latin alphabet is used to represent a number. A funny, but useful case for `36` is when we need to turn a long numeric identifier into something shorter, for example, to make a short url. Can simply represent it in the numeral system with base `36`:

```js run
alert( 123456..toString(36) ); // 2n9c
@@ -118,7 +118,7 @@ Common use cases for this are:
```warn header="Two dots to call a method"
Please note that two dots in `123456..toString(36)` is not a typo. If we want to call a method directly on a number, like `toString` in the example above, then we need to place two dots `..` after it.
If we placed a single dot: `123456.toString(36)`, then there would be an error, because JavaScript syntax implies the decimal part after the first dot. And if we place one more dot, then JavaScript knows that the decimal part is empty and now goes the method.
If we placed a single dot: `123456.toString(36)`, then there would be an error, because JavaScript syntax implies the decimal part after the first dot. And if we place one more dot, then JavaScript knows that the decimal part is empty and now uses the method.
Also could write `(123456).toString(36)`.
@@ -137,7 +137,7 @@ There are several built-in functions for rounding:
: Rounds up: `3.1` becomes `4`, and `-1.1` becomes `-1`.

`Math.round`
: Rounds to the nearest integer: `3.1` becomes `3`, `3.6` becomes `4`, the middle case: `3.5` rounds up to `4` too.
: Rounds to the nearest integer: `3.1` becomes `3`, `3.6` becomes `4`. In the middle cases `3.5` rounds up to `4`, and `-3.5` rounds up to `-3`.

`Math.trunc` (not supported by Internet Explorer)
: Removes anything after the decimal point without rounding: `3.1` becomes `3`, `-1.1` becomes `-1`.
@@ -147,8 +147,10 @@ Here's the table to summarize the differences between them:
| | `Math.floor` | `Math.ceil` | `Math.round` | `Math.trunc` |
|---|---------|--------|---------|---------|
|`3.1`| `3` | `4` | `3` | `3` |
|`3.5`| `3` | `4` | `4` | `3` |
|`3.6`| `3` | `4` | `4` | `3` |
|`-1.1`| `-2` | `-1` | `-1` | `-1` |
|`-1.5`| `-2` | `-1` | `-1` | `-1` |
|`-1.6`| `-2` | `-1` | `-2` | `-1` |
@@ -188,7 +190,7 @@ There are two ways to do so:
alert( num.toFixed(5) ); // "12.34000", added zeroes to make exactly 5 digits
```

We can convert it to a number using the unary plus or a `Number()` call, e.g write `+num.toFixed(5)`.
We can convert it to a number using the unary plus or a `Number()` call, e.g. write `+num.toFixed(5)`.

## Imprecise calculations

@@ -222,7 +224,13 @@ But why does this happen?

A number is stored in memory in its binary form, a sequence of bits - ones and zeroes. But fractions like `0.1`, `0.2` that look simple in the decimal numeric system are actually unending fractions in their binary form.

What is `0.1`? It is one divided by ten `1/10`, one-tenth. In decimal numeral system such numbers are easily representable. Compare it to one-third: `1/3`. It becomes an endless fraction `0.33333(3)`.
```js run
alert(0.1.toString(2)); // 0.0001100110011001100110011001100110011001100110011001101
alert(0.2.toString(2)); // 0.001100110011001100110011001100110011001100110011001101
alert((0.1 + 0.2).toString(2)); // 0.0100110011001100110011001100110011001100110011001101
```

What is `0.1`? It is one divided by ten `1/10`, one-tenth. In the decimal numeral system, such numbers are easily representable. Compare it to one-third: `1/3`. It becomes an endless fraction `0.33333(3)`.

So, division by powers `10` is guaranteed to work well in the decimal system, but division by `3` is not. For the same reason, in the binary numeral system, the division by powers of `2` is guaranteed to work, but `1/10` becomes an endless binary fraction.

@@ -242,7 +250,7 @@ That's why `0.1 + 0.2` is not exactly `0.3`.
```smart header="Not only JavaScript"
The same issue exists in many other programming languages.

PHP, Java, C, Perl, Ruby give exactly the same result, because they are based on the same numeric format.
PHP, Java, C, Perl, and Ruby give exactly the same result, because they are based on the same numeric format.
```

Can we work around the problem? Sure, the most reliable method is to round the result with the help of a method [toFixed(n)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed):
@@ -266,7 +274,7 @@ alert( (0.1 * 10 + 0.2 * 10) / 10 ); // 0.3
alert( (0.28 * 100 + 0.14 * 100) / 100); // 0.4200000000000001
```

So, multiply/divide approach reduces the error, but doesn't remove it totally.
So, the multiply/divide approach reduces the error, but doesn't remove it totally.

Sometimes we could try to evade fractions at all. Like if we're dealing with a shop, then we can store prices in cents instead of dollars. But what if we apply a discount of 30%? In practice, totally evading fractions is rarely possible. Just round them to cut "tails" when needed.

@@ -288,7 +296,7 @@ Another funny consequence of the internal representation of numbers is the exist

That's because a sign is represented by a single bit, so it can be set or not set for any number including a zero.

In most cases the distinction is unnoticeable, because operators are suited to treat them as the same.
In most cases, the distinction is unnoticeable, because operators are suited to treat them as the same.
```

## Tests: isFinite and isNaN
@@ -337,7 +345,7 @@ Please note that an empty or a space-only string is treated as `0` in all numeri
````smart header="`Number.isNaN` and `Number.isFinite`"
[Number.isNaN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isNaN) and [Number.isFinite](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isFinite) methods are the more "strict" versions of `isNaN` and `isFinite` functions. They do not autoconvert their argument into a number, but check if it belongs to the `number` type instead.

- `Number.isNaN(value)` returns `true` if the argument belongs to the `number` type and it is `NaN`. In any other case it returns `false`.
- `Number.isNaN(value)` returns `true` if the argument belongs to the `number` type and it is `NaN`. In any other case, it returns `false`.

```js run
alert( Number.isNaN(NaN) ); // true
@@ -348,7 +356,7 @@ Please note that an empty or a space-only string is treated as `0` in all numeri
alert( isNaN("str") ); // true, because isNaN converts string "str" into a number and gets NaN as a result of this conversion
```

- `Number.isFinite(value)` returns `true` if the argument belongs to the `number` type and it is not `NaN/Infinity/-Infinity`. In any other case it returns `false`.
- `Number.isFinite(value)` returns `true` if the argument belongs to the `number` type and it is not `NaN/Infinity/-Infinity`. In any other case, it returns `false`.

```js run
alert( Number.isFinite(123) ); // true
@@ -367,7 +375,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 correct, 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`.

@@ -385,7 +393,7 @@ alert( +"100px" ); // NaN

The sole exception is spaces at the beginning or at the end of the string, as they are ignored.

But in real life we often have values in units, like `"100px"` or `"12pt"` in CSS. Also in many countries the currency symbol goes after the amount, so we have `"19€"` and would like to extract a numeric value out of that.
But in real life, we often have values in units, like `"100px"` or `"12pt"` in CSS. Also in many countries, the currency symbol goes after the amount, so we have `"19€"` and would like to extract a numeric value out of that.

That's what `parseInt` and `parseFloat` are for.

@@ -479,4 +487,4 @@ For fractions:

More mathematical functions:

- See the [Math](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Math) object when you need them. The library is very small, but can cover basic needs.
- See the [Math](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Math) object when you need them. The library is very small but can cover basic needs.
2 changes: 1 addition & 1 deletion 1-js/05-data-types/04-array/article.md
Original file line number Diff line number Diff line change
@@ -426,7 +426,7 @@ let matrix = [
[7, 8, 9]
];

alert( matrix[1][1] ); // 5, the central element
alert( matrix[0][1] ); // 2, the second value of the first inner array
```

## toString
48 changes: 24 additions & 24 deletions 1-js/05-data-types/05-array-methods/article.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Array methods

Arrays provide a lot of methods. To make things easier, in this chapter they are split into groups.
Arrays provide a lot of methods. To make things easier, in this chapter, they are split into groups.

## Add/remove items

@@ -32,11 +32,11 @@ alert( arr.length ); // 3

The element was removed, but the array still has 3 elements, we can see that `arr.length == 3`.

That's natural, because `delete obj.key` removes a value by the `key`. It's all it does. Fine for objects. But for arrays we usually want the rest of elements to shift and occupy the freed place. We expect to have a shorter array now.
That's natural, because `delete obj.key` removes a value by the `key`. It's all it does. Fine for objects. But for arrays we usually want the rest of the elements to shift and occupy the freed place. We expect to have a shorter array now.

So, special methods should be used.

The [arr.splice](mdn:js/Array/splice) method is a swiss army knife for arrays. It can do everything: insert, remove and replace elements.
The [arr.splice](mdn:js/Array/splice) method is a Swiss army knife for arrays. It can do everything: insert, remove and replace elements.

The syntax is:

@@ -62,7 +62,7 @@ alert( arr ); // ["I", "JavaScript"]

Easy, right? Starting from the index `1` it removed `1` element.

In the next example we remove 3 elements and replace them with the other two:
In the next example, we remove 3 elements and replace them with the other two:

```js run
let arr = [*!*"I", "study", "JavaScript",*/!* "right", "now"];
@@ -84,7 +84,7 @@ let removed = arr.splice(0, 2);
alert( removed ); // "I", "study" <-- array of removed elements
```

The `splice` method is also able to insert the elements without any removals. For that we need to set `deleteCount` to `0`:
The `splice` method is also able to insert the elements without any removals. For that, we need to set `deleteCount` to `0`:

```js run
let arr = ["I", "study", "JavaScript"];
@@ -114,7 +114,7 @@ alert( arr ); // 1,2,3,4,5

### slice

The method [arr.slice](mdn:js/Array/slice) is much simpler than similar-looking `arr.splice`.
The method [arr.slice](mdn:js/Array/slice) is much simpler than the similar-looking `arr.splice`.

The syntax is:

@@ -124,7 +124,7 @@ arr.slice([start], [end])

It returns a new array copying to it all items from index `start` to `end` (not including `end`). Both `start` and `end` can be negative, in that case position from array end is assumed.

It's similar to a string method `str.slice`, but instead of substrings it makes subarrays.
It's similar to a string method `str.slice`, but instead of substrings, it makes subarrays.

For instance:

@@ -206,7 +206,7 @@ The [arr.forEach](mdn:js/Array/forEach) method allows to run a function for ever
The syntax:
```js
arr.forEach(function(item, index, array) {
// ... do something with item
// ... do something with an item
});
```

@@ -239,7 +239,7 @@ The methods [arr.indexOf](mdn:js/Array/indexOf) and [arr.includes](mdn:js/Array/
- `arr.indexOf(item, from)` -- looks for `item` starting from index `from`, and returns the index where it was found, otherwise `-1`.
- `arr.includes(item, from)` -- looks for `item` starting from index `from`, returns `true` if found.

Usually these methods are used with only one argument: the `item` to search. By default, the search is from the beginning.
Usually, these methods are used with only one argument: the `item` to search. By default, the search is from the beginning.

For instance:

@@ -255,7 +255,7 @@ alert( arr.includes(1) ); // true

Please note that `indexOf` uses the strict equality `===` for comparison. So, if we look for `false`, it finds exactly `false` and not the zero.

If we want to check if `item` exists in the array, and don't need the index, then `arr.includes` is preferred.
If we want to check if `item` exists in the array and don't need the index, then `arr.includes` is preferred.

The method [arr.lastIndexOf](mdn:js/Array/lastIndexOf) is the same as `indexOf`, but looks for from right to left.

@@ -274,12 +274,12 @@ const arr = [NaN];
alert( arr.indexOf(NaN) ); // -1 (wrong, should be 0)
alert( arr.includes(NaN) );// true (correct)
```
That's because `includes` was added to JavaScript much later and uses the more up to date comparison algorithm internally.
That's because `includes` was added to JavaScript much later and uses the more up-to-date comparison algorithm internally.
````
### find and findIndex/findLastIndex
Imagine we have an array of objects. How do we find an object with the specific condition?
Imagine we have an array of objects. How do we find an object with a specific condition?
Here the [arr.find(fn)](mdn:js/Array/find) method comes in handy.
@@ -297,7 +297,7 @@ The function is called for elements of the array, one after another:
- `index` is its index.
- `array` is the array itself.
If it returns `true`, the search is stopped, the `item` is returned. If nothing found, `undefined` is returned.
If it returns `true`, the search is stopped, the `item` is returned. If nothing is found, `undefined` is returned.
For example, we have an array of users, each with the fields `id` and `name`. Let's find the one with `id == 1`:
@@ -313,11 +313,11 @@ let user = users.find(item => item.id == 1);
alert(user.name); // John
```
In real life arrays of objects is a common thing, so the `find` method is very useful.
In real life, arrays of objects are a common thing, so the `find` method is very useful.
Note that in the example we provide to `find` the function `item => item.id == 1` with one argument. That's typical, other arguments of this function are rarely used.
The [arr.findIndex](mdn:js/Array/findIndex) method has the same syntax, but returns the index where the element was found instead of the element itself. The value of `-1` is returned if nothing is found.
The [arr.findIndex](mdn:js/Array/findIndex) method has the same syntax but returns the index where the element was found instead of the element itself. The value of `-1` is returned if nothing is found.
The [arr.findLastIndex](mdn:js/Array/findLastIndex) method is like `findIndex`, but searches from right to left, similar to `lastIndexOf`.
@@ -450,11 +450,11 @@ alert(arr); // *!*1, 2, 15*/!*
Now it works as intended.
Let's step aside and think what's happening. The `arr` can be array of anything, right? It may contain numbers or strings or objects or whatever. We have a set of *some items*. To sort it, we need an *ordering function* that knows how to compare its elements. The default is a string order.
Let's step aside and think about what's happening. The `arr` can be an array of anything, right? It may contain numbers or strings or objects or whatever. We have a set of *some items*. To sort it, we need an *ordering function* that knows how to compare its elements. The default is a string order.
The `arr.sort(fn)` method implements a generic sorting algorithm. We don't need to care how it internally works (an optimized [quicksort](https://en.wikipedia.org/wiki/Quicksort) or [Timsort](https://en.wikipedia.org/wiki/Timsort) most of the time). It will walk the array, compare its elements using the provided function and reorder them, all we need is to provide the `fn` which does the comparison.
By the way, if we ever want to know which elements are compared -- nothing prevents from alerting them:
By the way, if we ever want to know which elements are compared -- nothing prevents us from alerting them:
```js run
[1, -2, 15, 2, 0, 8].sort(function(a, b) {
@@ -526,7 +526,7 @@ Here's the situation from real life. We are writing a messaging app, and the per
The [str.split(delim)](mdn:js/String/split) method does exactly that. It splits the string into an array by the given delimiter `delim`.
In the example below, we split by a comma followed by space:
In the example below, we split by a comma followed by a space:
```js run
let names = 'Bilbo, Gandalf, Nazgul';
@@ -593,9 +593,9 @@ Arguments:
- `index` -- is its position.
- `array` -- is the array.

As function is applied, the result of the previous function call is passed to the next one as the first argument.
As the function is applied, the result of the previous function call is passed to the next one as the first argument.

So, the first argument is essentially the accumulator that stores the combined result of all previous executions. And at the end it becomes the result of `reduce`.
So, the first argument is essentially the accumulator that stores the combined result of all previous executions. And at the end, it becomes the result of `reduce`.

Sounds complicated?

@@ -664,7 +664,7 @@ arr.reduce((sum, current) => sum + current);

So it's advised to always specify the initial value.

The method [arr.reduceRight](mdn:js/Array/reduceRight) does the same, but goes from right to left.
The method [arr.reduceRight](mdn:js/Array/reduceRight) does the same but goes from right to left.

## Array.isArray

@@ -689,7 +689,7 @@ alert(Array.isArray([])); // true

Almost all array methods that call functions -- like `find`, `filter`, `map`, with a notable exception of `sort`, accept an optional additional parameter `thisArg`.

That parameter is not explained in the sections above, because it's rarely used. But for completeness we have to cover it.
That parameter is not explained in the sections above, because it's rarely used. But for completeness, we have to cover it.

Here's the full syntax of these methods:

@@ -749,7 +749,7 @@ A cheat sheet of array methods:
- `concat(...items)` -- returns a new array: copies all members of the current one and adds `items` to it. If any of `items` is an array, then its elements are taken.

- To search among elements:
- `indexOf/lastIndexOf(item, pos)` -- look for `item` starting from position `pos`, return the index or `-1` if not found.
- `indexOf/lastIndexOf(item, pos)` -- look for `item` starting from position `pos`, and return the index or `-1` if not found.
- `includes(value)` -- returns `true` if the array has `value`, otherwise `false`.
- `find/filter(func)` -- filter elements through the function, return first/all values that make it return `true`.
- `findIndex` is like `find`, but returns the index instead of a value.
@@ -795,7 +795,7 @@ These methods are the most used ones, they cover 99% of use cases. But there are

For the full list, see the [manual](mdn:js/Array).

From the first sight it may seem that there are so many methods, quite difficult to remember. But actually that's much easier.
At first sight, it may seem that there are so many methods, quite difficult to remember. But actually, that's much easier.

Look through the cheat sheet just to be aware of them. Then solve the tasks of this chapter to practice, so that you have experience with array methods.

2 changes: 1 addition & 1 deletion 1-js/05-data-types/06-iterable/article.md
Original file line number Diff line number Diff line change
@@ -174,7 +174,7 @@ When we use JavaScript for practical tasks in a browser or any other environment

For instance, strings are both iterable (`for..of` works on them) and array-like (they have numeric indexes and `length`).

But an iterable may be not array-like. And vice versa an array-like may be not iterable.
But an iterable may not be array-like. And vice versa an array-like may not be iterable.

For example, the `range` in the example above is iterable, but not array-like, because it does not have indexed properties and `length`.

14 changes: 7 additions & 7 deletions 1-js/05-data-types/10-destructuring-assignment/article.md
Original file line number Diff line number Diff line change
@@ -449,7 +449,7 @@ alert(item1); // Cake
alert(item2); // Donut
```
All properties of `options` object except `extra` that is absent in the left part, are assigned to corresponding variables:
All properties of `options` object except `extra` which is absent in the left part, are assigned to corresponding variables:
![](destructuring-complex.svg)
@@ -459,7 +459,7 @@ Note that there are no variables for `size` and `items`, as we take their conten
## Smart function parameters
There are times when a function has many parameters, most of which are optional. That's especially true for user interfaces. Imagine a function that creates a menu. It may have a width, a height, a title, items list and so on.
There are times when a function has many parameters, most of which are optional. That's especially true for user interfaces. Imagine a function that creates a menu. It may have a width, a height, a title, an item list and so on.
Here's a bad way to write such a function:
@@ -469,7 +469,7 @@ function showMenu(title = "Untitled", width = 200, height = 100, items = []) {
}
```
In real-life, the problem is how to remember the order of arguments. Usually IDEs try to help us, especially if the code is well-documented, but still... Another problem is how to call a function when most parameters are ok by default.
In real-life, the problem is how to remember the order of arguments. Usually, IDEs try to help us, especially if the code is well-documented, but still... Another problem is how to call a function when most parameters are ok by default.
Like this?
@@ -534,7 +534,7 @@ function({
})
```

Then, for an object of parameters, there will be a variable `varName` for property `incomingProperty`, with `defaultValue` by default.
Then, for an object of parameters, there will be a variable `varName` for the property `incomingProperty`, with `defaultValue` by default.

Please note that such destructuring assumes that `showMenu()` does have an argument. If we want all values by default, then we should specify an empty object:

@@ -561,7 +561,7 @@ In the code above, the whole arguments object is `{}` by default, so there's alw
- Destructuring assignment allows for instantly mapping an object or array onto many variables.
- The full object syntax:
```js
let {prop : varName = default, ...rest} = object
let {prop : varName = defaultValue, ...rest} = object
```

This means that property `prop` should go into the variable `varName` and, if no such property exists, then the `default` value should be used.
@@ -571,9 +571,9 @@ In the code above, the whole arguments object is `{}` by default, so there's alw
- The full array syntax:

```js
let [item1 = default, item2, ...rest] = array
let [item1 = defaultValue, item2, ...rest] = array
```

The first item goes to `item1`; the second goes into `item2`, all the rest makes the array `rest`.
The first item goes to `item1`; the second goes into `item2`, and all the rest makes the array `rest`.

- It's possible to extract data from nested arrays/objects, for that the left side must have the same structure as the right one.
2 changes: 1 addition & 1 deletion 1-js/06-advanced-functions/06-function-object/article.md
Original file line number Diff line number Diff line change
@@ -326,7 +326,7 @@ welcome(); // Hello, Guest (nested call works)

Now it works, because the name `"func"` is function-local. It is not taken from outside (and not visible there). The specification guarantees that it will always reference the current function.

The outer code still has its variable `sayHi` or `welcome`. And `func` is an "internal function name", the way for the function to can call itself reliably.
The outer code still has its variable `sayHi` or `welcome`. And `func` is an "internal function name", the way for the function to call itself reliably.

```smart header="There's no such thing for Function Declaration"
The "internal name" feature described here is only available for Function Expressions, not for Function Declarations. For Function Declarations, there is no syntax for adding an "internal" name.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

The error occurs because `ask` gets functions `loginOk/loginFail` without the object.
The error occurs because `askPassword` gets functions `loginOk/loginFail` without the object.

When it calls them, they naturally assume `this=undefined`.

2 changes: 1 addition & 1 deletion 1-js/06-advanced-functions/10-bind/article.md
Original file line number Diff line number Diff line change
@@ -125,7 +125,7 @@ funcUser(); // John
*/!*
```

Here `func.bind(user)` as a "bound variant" of `func`, with fixed `this=user`.
Here `func.bind(user)` is a "bound variant" of `func`, with fixed `this=user`.

All arguments are passed to the original `func` "as is", for instance:

2 changes: 1 addition & 1 deletion 1-js/08-prototypes/04-prototype-methods/article.md
Original file line number Diff line number Diff line change
@@ -116,7 +116,7 @@ alert(obj[key]); // [object Object], not "some value"!

Here, if the user types in `__proto__`, the assignment in line 4 is ignored!

That could surely be surprising for a non-developer, but pretty understandable for us. The `__proto__` property is special: it must be either an object or `null`. A string can not become a prototype. That's why an assignment a string to `__proto__` is ignored.
That could surely be surprising for a non-developer, but pretty understandable for us. The `__proto__` property is special: it must be either an object or `null`. A string can not become a prototype. That's why assigning a string to `__proto__` is ignored.

But we didn't *intend* to implement such behavior, right? We want to store key/value pairs, and the key named `"__proto__"` was not properly saved. So that's a bug!

2 changes: 1 addition & 1 deletion 1-js/10-error-handling/1-try-catch/article.md
Original file line number Diff line number Diff line change
@@ -632,7 +632,7 @@ For instance:
The role of the global handler `window.onerror` is usually not to recover the script execution -- that's probably impossible in case of programming errors, but to send the error message to developers.
There are also web-services that provide error-logging for such cases, like <https://errorception.com> or <https://www.muscula.com>.
There are also web-services that provide error-logging for such cases, like <https://muscula.com> or <https://www.sentry.io>.
They work like this:
4 changes: 2 additions & 2 deletions 1-js/11-async/02-promise-basics/article.md
Original file line number Diff line number Diff line change
@@ -46,7 +46,7 @@ Later we'll see how "fans" can subscribe to these changes.

Here's an example of a promise constructor and a simple executor function with "producing code" that takes time (via `setTimeout`):

```js run
```js
let promise = new Promise(function(resolve, reject) {
// the function is executed automatically when the promise is constructed

@@ -222,7 +222,7 @@ The idea of `finally` is to set up a handler for performing cleanup/finalizing a
E.g. stopping loading indicators, closing no longer needed connections, etc.
Think of it as a party finisher. No matter was a party good or bad, how many friends were in it, we still need (or at least should) do a cleanup after it.
Think of it as a party finisher. Irresepective of whether a party was good or bad, how many friends were in it, we still need (or at least should) do a cleanup after it.
The code may look like this:
113 changes: 113 additions & 0 deletions 1-js/11-async/08-async-await/04-promise-all-failure/solution.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@

The root of the problem is that `Promise.all` immediately rejects when one of its promises rejects, but it do nothing to cancel the other promises.

In our case, the second query fails, so `Promise.all` rejects, and the `try...catch` block catches this error.Meanwhile, other promises are *not affected* - they independently continue their execution. In our case, the third query throws an error of its own after a bit of time. And that error is never caught, we can see it in the console.

The problem is especially dangerous in server-side environments, such as Node.js, when an uncaught error may cause the process to crash.

How to fix it?

An ideal solution would be to cancel all unfinished queries when one of them fails. This way we avoid any potential errors.

However, the bad news is that service calls (such as `database.query`) are often implemented by a 3rd-party library which doesn't support cancellation. Then there's no way to cancel a call.

As an alternative, we can write our own wrapper function around `Promise.all` which adds a custom `then/catch` handler to each promise to track them: results are gathered and, if an error occurs, all subsequent promises are ignored.

```js
function customPromiseAll(promises) {
return new Promise((resolve, reject) => {
const results = [];
let resultsCount = 0;
let hasError = false; // we'll set it to true upon first error

promises.forEach((promise, index) => {
promise
.then(result => {
if (hasError) return; // ignore the promise if already errored
results[index] = result;
resultsCount++;
if (resultsCount === promises.length) {
resolve(results); // when all results are ready - successs
}
})
.catch(error => {
if (hasError) return; // ignore the promise if already errored
hasError = true; // wops, error!
reject(error); // fail with rejection
});
});
});
}
```

This approach has an issue of its own - it's often undesirable to `disconnect()` when queries are still in the process.

It may be important that all queries complete, especially if some of them make important updates.

So we should wait until all promises are settled before going further with the execution and eventually disconnecting.

Here's another implementation. It behaves similar to `Promise.all` - also resolves with the first error, but waits until all promises are settled.

```js
function customPromiseAllWait(promises) {
return new Promise((resolve, reject) => {
const results = new Array(promises.length);
let settledCount = 0;
let firstError = null;

promises.forEach((promise, index) => {
Promise.resolve(promise)
.then(result => {
results[index] = result;
})
.catch(error => {
if (firstError === null) {
firstError = error;
}
})
.finally(() => {
settledCount++;
if (settledCount === promises.length) {
if (firstError !== null) {
reject(firstError);
} else {
resolve(results);
}
}
});
});
});
}
```

Now `await customPromiseAllWait(...)` will stall the execution until all queries are processed.

This is a more reliable approach, as it guarantees a predictable execution flow.

Lastly, if we'd like to process all errors, we can use either use `Promise.allSettled` or write a wrapper around it to gathers all errors in a single [AggregateError](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AggregateError) object and rejects with it.

```js
// wait for all promises to settle
// return results if no errors
// throw AggregateError with all errors if any
function allOrAggregateError(promises) {
return Promise.allSettled(promises).then(results => {
const errors = [];
const values = [];

results.forEach((res, i) => {
if (res.status === 'fulfilled') {
values[i] = res.value;
} else {
errors.push(res.reason);
}
});

if (errors.length > 0) {
throw new AggregateError(errors, 'One or more promises failed');
}

return values;
});
}
```
79 changes: 79 additions & 0 deletions 1-js/11-async/08-async-await/04-promise-all-failure/task.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@

# Dangerous Promise.all

`Promise.all` is a great way to parallelize multiple operations. It's especially useful when we need to make parallel requests to multiple services.

However, there's a hidden danger. We'll see an example in this task and explore how to avoid it.

Let's say we have a connection to a remote service, such as a database.

There're two functions: `connect()` and `disconnect()`.

When connected, we can send requests using `database.query(...)` - an async function which usually returns the result but also may throw an error.

Here's a simple implementation:

```js
let database;

function connect() {
database = {
async query(isOk) {
if (!isOk) throw new Error('Query failed');
}
};
}

function disconnect() {
database = null;
}

// intended usage:
// connect()
// ...
// database.query(true) to emulate a successful call
// database.query(false) to emulate a failed call
// ...
// disconnect()
```

Now here's the problem.

We wrote the code to connect and send 3 queries in parallel (all of them take different time, e.g. 100, 200 and 300ms), then disconnect:

```js
// Helper function to call async function `fn` after `ms` milliseconds
function delay(fn, ms) {
return new Promise((resolve, reject) => {
setTimeout(() => fn().then(resolve, reject), ms);
});
}

async function run() {
connect();

try {
await Promise.all([
// these 3 parallel jobs take different time: 100, 200 and 300 ms
// we use the `delay` helper to achieve this effect
*!*
delay(() => database.query(true), 100),
delay(() => database.query(false), 200),
delay(() => database.query(false), 300)
*/!*
]);
} catch(error) {
console.log('Error handled (or was it?)');
}

disconnect();
}

run();
```

Two of these queries happen to be unsuccessful, but we're smart enough to wrap the `Promise.all` call into a `try..catch` block.

However, this doesn't help! This script actually leads to an uncaught error in console!

Why? How to avoid it?
2 changes: 1 addition & 1 deletion 2-ui/4-forms-controls/1-form-elements/article.md
Original file line number Diff line number Diff line change
@@ -244,7 +244,7 @@ This syntax is optional. We can use `document.createElement('option')` and set a
- `defaultSelected` -- if `true`, then `selected` HTML-attribute is created,
- `selected` -- if `true`, then the option is selected.
The difference between `defaultSelected` and `selected` is that `defaultSelected` sets the HTML-attribute (that we can get using `option.getAttribute('selected')`, while `selected` sets whether the option is selected or not.
The difference between `defaultSelected` and `selected` is that `defaultSelected` sets the HTML-attribute (that we can get using `option.getAttribute('selected')`), while `selected` sets whether the option is selected or not.
In practice, one should usually set _both_ values to `true` or `false`. (Or, simply omit them; both default to `false`.)
2 changes: 1 addition & 1 deletion 2-ui/99-ui-misc/02-selection-range/article.md
Original file line number Diff line number Diff line change
@@ -354,7 +354,7 @@ The main selection properties are:

```smart header="Selection end/start vs Range"
There's an important differences of a selection anchor/focus compared with a `Range` start/end.
There's an important difference between a selection anchor/focus compared with a `Range` start/end.
As we know, `Range` objects always have their start before the end.
12 changes: 6 additions & 6 deletions 2-ui/99-ui-misc/03-event-loop/article.md
Original file line number Diff line number Diff line change
@@ -17,7 +17,7 @@ The general algorithm of the engine:
- execute them, starting with the oldest task.
2. Sleep until a task appears, then go to 1.

That's a formalization for what we see when browsing a page. The JavaScript engine does nothing most of the time, it only runs if a script/handler/event activates.
That's a formalization of what we see when browsing a page. The JavaScript engine does nothing most of the time, it only runs if a script/handler/event activates.

Examples of tasks:

@@ -30,19 +30,19 @@ Tasks are set -- the engine handles them -- then waits for more tasks (while sle

It may happen that a task comes while the engine is busy, then it's enqueued.

The tasks form a queue, so-called "macrotask queue" (v8 term):
The tasks form a queue, the so-called "macrotask queue" ([v8](https://v8.dev/) term):

![](eventLoop.svg)

For instance, while the engine is busy executing a `script`, a user may move their mouse causing `mousemove`, and `setTimeout` may be due and so on, these tasks form a queue, as illustrated on the picture above.
For instance, while the engine is busy executing a `script`, a user may move their mouse causing `mousemove`, and `setTimeout` may be due and so on, these tasks form a queue, as illustrated in the picture above.

Tasks from the queue are processed on "first come – first served" basis. When the engine browser is done with the `script`, it handles `mousemove` event, then `setTimeout` handler, and so on.
Tasks from the queue are processed on a "first come – first served" basis. When the engine browser is done with the `script`, it handles `mousemove` event, then `setTimeout` handler, and so on.

So far, quite simple, right?

Two more details:
1. Rendering never happens while the engine executes a task. It doesn't matter if the task takes a long time. Changes to the DOM are painted only after the task is complete.
2. If a task takes too long, the browser can't do other tasks, such as processing user events. So after a time, it raises an alert like "Page Unresponsive", suggesting killing the task with the whole page. That happens when there are a lot of complex calculations or a programming error leading to an infinite loop.
2. If a task takes too long, the browser can't do other tasks, such as processing user events. So after some time, it raises an alert like "Page Unresponsive", suggesting killing the task with the whole page. That happens when there are a lot of complex calculations or a programming error leading to an infinite loop.

That was the theory. Now let's see how we can apply that knowledge.

@@ -54,7 +54,7 @@ For example, syntax-highlighting (used to colorize code examples on this page) i

While the engine is busy with syntax highlighting, it can't do other DOM-related stuff, process user events, etc. It may even cause the browser to "hiccup" or even "hang" for a bit, which is unacceptable.

We can avoid problems by splitting the big task into pieces. Highlight first 100 lines, then schedule `setTimeout` (with zero-delay) for the next 100 lines, and so on.
We can avoid problems by splitting the big task into pieces. Highlight the first 100 lines, then schedule `setTimeout` (with zero-delay) for the next 100 lines, and so on.

To demonstrate this approach, for the sake of simplicity, instead of text-highlighting, let's take a function that counts from `1` to `1000000000`.

6 changes: 6 additions & 0 deletions BACKERS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

# Sponsors and Supporters

## Supporters

- Ilya Zelenko