Skip to content

Commit 2c57f11

Browse files
committed
fixes
1 parent fb03c7d commit 2c57f11

File tree

6 files changed

+79
-44
lines changed

6 files changed

+79
-44
lines changed

1-js/05-data-types/04-array/10-maximal-subarray/solution.md

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
# The slow solution
1+
# The slow solution
22

3-
We can calculate all possible subsums.
3+
We can calculate all possible subsums.
44

55
The simplest way is to take every element and calculate sums of all subarrays starting from it.
66

@@ -61,7 +61,7 @@ The solution has a time complexety of [O(n<sup>2</sup>)](https://en.wikipedia.or
6161

6262
For big arrays (1000, 10000 or more items) such algorithms can lead to a seroius sluggishness.
6363

64-
# Fast solution
64+
# Fast solution
6565

6666
Let's walk the array and keep the current partial sum of elements in the variable `s`. If `s` becomes negative at some point, then assign `s=0`. The maximum of all such `s` will be the answer.
6767

@@ -72,7 +72,7 @@ function getMaxSubSum(arr) {
7272
let maxSum = 0;
7373
let partialSum = 0;
7474

75-
for (let item of arr; i++) { // for each item of arr
75+
for (let item of arr) { // for each item of arr
7676
partialSum += item; // add it to partialSum
7777
maxSum = Math.max(maxSum, partialSum); // remember the maximum
7878
if (partialSum < 0) partialSum = 0; // zero if negative
@@ -91,5 +91,4 @@ alert( getMaxSubSum([-1, -2, -3]) ); // 0
9191

9292
The algorithm requires exactly 1 array pass, so the time complexity is O(n).
9393

94-
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.
95-
94+
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.

1-js/05-data-types/05-array-methods/article.md

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -519,7 +519,7 @@ let arr = [1, 2, 3, 4, 5]
519519

520520
let result = arr.reduce((sum, current) => sum + current), 0);
521521

522-
alert( result ); // 15
522+
alert(result); // 15
523523
```
524524

525525
Here we used the most common variant of `reduce` which uses only 2 arguments.
@@ -562,10 +562,22 @@ The result is the same. That's because if there's no initial, then `reduce` take
562562

563563
The calculation table is the same as above, minus the first row.
564564

565-
But such use requires an extreme care. If the array is empty, then `reduce` call without initial value gives an error. So it's generally advised to specify the initial value.
565+
But such use requires an extreme care. If the array is empty, then `reduce` call without initial value gives an error.
566566

567-
The method [arr.reduceRight](mdn:js/Array/reduceRight) does the same, but goes from right to left.
567+
Here's an example:
568568

569+
```js run
570+
let arr = [];
571+
572+
// Error: Reduce of empty array with no initial value
573+
// if the initial value existed, reduce would return it for the empty arr.
574+
arr.reduce((sum, current) => sum + current);
575+
```
576+
577+
578+
So it's advised to always specify the initial value.
579+
580+
The method [arr.reduceRight](mdn:js/Array/reduceRight) does the same, but goes from right to left.
569581

570582

571583
## Iterate: forEach
@@ -615,13 +627,13 @@ alert(Array.isArray({})); // false
615627
alert(Array.isArray([])); // true
616628
```
617629

618-
## Methods: "thisArg"
630+
## Most methods support "thisArg"
619631

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

622-
In the sections above that parameter is not explained, because it's rarely used.
634+
That parameter is not explained in the sections above, because it's rarely used. But for completeness we have to cover it.
623635

624-
But for completeness here's the full syntax:
636+
Here's the full syntax of these methods:
625637

626638
```js
627639
arr.find(func, thisArg);
@@ -633,7 +645,7 @@ arr.map(func, thisArg);
633645

634646
The value of `thisArg` parameter becomes `this` for `func`.
635647

636-
For instance, here we use an object method as a filter:
648+
For instance, here we use an object method as a filter and `thisArg` comes in handy:
637649

638650
```js run
639651
let user = {

1-js/08-error-handling/2-custom-errors/article.md

Lines changed: 48 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22

33
When we develop something, we often need our own error classes to reflect specific things that may go wrong in our tasks. For errors in network operations we may need `HttpError`, for database operations `DbError`, for searching operations `NotFoundError` and so on.
44

5-
Our errors should inherit from basic `Error` class and support basic error properties like `message`, `name` and, preferably, `stack`. But they also may have other properties of their own, e.g. `HttpError` objects may have `statusCode` property with a value like `404` or `403` or `500`.
5+
Our errors should support basic error properties like `message`, `name` and, preferably, `stack`. But they also may have other properties of their own, e.g. `HttpError` objects may have `statusCode` property with a value like `404` or `403` or `500`.
66

7-
Technically, we can use standalone classes for our errors, because JavaScript allows to use `throw` with any argument. But if we inherit from `Error`, then it becomes possible to use `obj instanceof Error` check to identify error objects. So it's better to inherit from it.
7+
JavaScript allows to use `throw` with any argument, so technically our custom error classes don't need to inherit from `Error`. But if we inherit, then it becomes possible to use `obj instanceof Error` to identify error objects. So it's better to inherit from it.
88

9-
As we build our application, our own errors naturally form a hierarchy, for instance `HttpTimeoutError` may inherit from `HttpError`. Examples will follow soon.
9+
As we build our application, our own errors naturally form a hierarchy, for instance `HttpTimeoutError` may inherit from `HttpError`, and so on.
1010

1111
## Extending Error
1212

@@ -17,14 +17,20 @@ Here's an example of how a valid `json` may look:
1717
let json = `{ "name": "John", "age": 30 }`;
1818
```
1919

20-
If `JSON.parse` receives malformed `json`, then it throws `SyntaxError`. But even if `json` is syntactically correct, it may don't have the necessary data. For instance, if may not have `name` and `age` properties that are essential for our users.
20+
Internally, we'll use `JSON.parse`. If it receives malformed `json`, then it throws `SyntaxError`.
2121

22-
That's called "data validation" -- we need to ensure that the data has all the necessary fields. And if the validation fails, then it not really a `SyntaxError`, because the data is syntactically correct. Let's create `ValidationError` -- the error object of our own with additional information about the offending field.
22+
But even if `json` is syntactically correct, that doesn't mean that it's a valid user, right? It may miss the necessary data. For instance, if may not have `name` and `age` properties that are essential for our users.
2323

24-
Our `ValidationError` should inherit from the built-in `Error` class. To better understand what we're extending -- here's the approximate code for built-in [Error class](https://tc39.github.io/ecma262/#sec-error-message):
24+
Our function `readUser(json)` will not only read JSON, but check ("validate") the data. If there are no required fields, or the format is wrong, then that's an error. And that's not a `SyntaxError`, because the data is syntactically correct, but another kind of error. We'll call it `ValidationError` and create a class for it. An error of that kind should also carry the information about the offending field.
25+
26+
Our `ValidationError` class should inherit from the built-in `Error` class.
27+
28+
That class is built-in, but we should have its approximate code before our eyes, to understand what we're extending.
29+
30+
So here you are:
2531

2632
```js
27-
// "pseudocode" for the built-in Error class defined by JavaScript itself
33+
// The "pseudocode" for the built-in Error class defined by JavaScript itself
2834
class Error {
2935
constructor(message) {
3036
this.message = message;
@@ -34,7 +40,7 @@ class Error {
3440
}
3541
```
3642

37-
Now let's inherit from it:
43+
Now let's go on and inherit `ValidationError` from it:
3844

3945
```js run untrusted
4046
*!*
@@ -59,10 +65,10 @@ try {
5965
}
6066
```
6167

62-
Please note:
68+
Please take a look at the constructor:
6369

64-
1. In the line `(1)` we call the parent constructor to set the message. JavaScript requires us to call `super` in the child constructor.
65-
2. The parent constructor sets the `name` property to `"Error"`, so here we reset it to the right value.
70+
1. In the line `(1)` we call the parent constructor. JavaScript requires us to call `super` in the child constructor, so that's obligatory. The parent constructor sets the `message` property.
71+
2. The parent constructor also sets the `name` property to `"Error"`, so in the line `(2)` we reset it to the right value.
6672

6773
Let's try to use it in `readUser(json)`:
6874

@@ -97,23 +103,34 @@ try {
97103
*!*
98104
alert("Invalid data: " + err.message); // Invalid data: No field: name
99105
*/!*
100-
} else if (err instanceof SyntaxError) {
106+
} else if (err instanceof SyntaxError) { // (*)
101107
alert("JSON Syntax Error: " + err.message);
102108
} else {
103-
throw err; // unknown error, rethrow it
109+
throw err; // unknown error, rethrow it (**)
104110
}
105111
}
106112
```
107113

108-
Everything works -- both our `ValidationError` and the built-in `SyntaxError` from `JSON.parse` can be generated and handled.
114+
The `try..catch` block in the code above handles both our `ValidationError` and the built-in `SyntaxError` from `JSON.parse`.
115+
116+
Please take a look at how we use `instanceof` to check for the specific error type in the line `(*)`.
109117

110-
Please take a look at how the code checks for the error type in `catch (err) { ... }`. We could use `if (err.name == "ValidationError")`, but `if (err instanceof ValidationError)` is much better, because in the future we are going to extend `ValidationError`, make new subtypes of it, namely `PropertyRequiredError`. And `instanceof` check will continue to work. So that's future-proof.
118+
We could also look at `err.name`, like this:
111119

112-
Also it's important that if `catch` meets an unknown error, then it rethrows it. The `catch` only knows how to handle validation and syntax errors, other kinds (due to a typo in the code or such) should fall through.
120+
```js
121+
// ...
122+
// instead of (err instanceof SyntaxError)
123+
} else if (err.name == "SyntaxError") { // (*)
124+
// ...
125+
```
126+
127+
The `instanceof` version is much better, because in the future we are going to extend `ValidationError`, make subtypes of it, like `PropertyRequiredError`. And `instanceof` check will continue to work for new inheriting classes. So that's future-proof.
128+
129+
Also it's important that if `catch` meets an unknown error, then it rethrows it in the line `(**)`. The `catch` only knows how to handle validation and syntax errors, other kinds (due to a typo in the code or such) should fall through.
113130
114131
## Further inheritance
115132
116-
The `ValidationError` class is very generic. Many things may be wrong. The property may be absent or it may be in a wrong format (like a string value for `age`). Let's make a more concrete class `PropertyRequiredError`, exactly for absent properties. It will carry additional information about the property that's missing.
133+
The `ValidationError` class is very generic. Many things may go wrong. The property may be absent or it may be in a wrong format (like a string value for `age`). Let's make a more concrete class `PropertyRequiredError`, exactly for absent properties. It will carry additional information about the property that's missing.
117134
118135
```js run
119136
class ValidationError extends Error {
@@ -168,9 +185,11 @@ try {
168185
169186
The new class `PropertyRequiredError` is easy to use: we only need to pass the property name: `new PropertyRequiredError(property)`. The human-readable `message` is generated by the constructor.
170187
171-
Plese note that `this.name` in `PropertyRequiredError` once again assigned manually. We could make our own "basic error" class, name it `MyError` that removes this burden from our shoulders by using `this.constructor.name` for `this.name` in the constructor. And then inherit from it.
188+
Please note that `this.name` in `PropertyRequiredError` constructor is again assigned manually. That may become a bit tedius -- to assign `this.name = <class name>` when creating each custom error. But there's a way out. We can make our own "basic error" class that removes this burden from our shoulders by using `this.constructor.name` for `this.name` in the constructor. And then inherit from it.
172189
173-
Here we go:
190+
Let's call it `MyError`.
191+
192+
Here's the code with `MyError` and other custom error classes, simplified:
174193
175194
```js run
176195
class MyError extends Error {
@@ -195,17 +214,19 @@ class PropertyRequiredError extends ValidationError {
195214
alert( new PropertyRequiredError("field").name ); // PropertyRequiredError
196215
```
197216
198-
Now the inheritance became simpler, as we got rid of the `"this.name = ..."` line in the constructor.
217+
Now custom errors are much shorter, especially `ValidationError`, as we got rid of the `"this.name = ..."` line in the constructor.
199218
200219
## Wrapping exceptions
201220
202-
The purpose of the function `readUser` in the code above is "to read the user data", right? There may occur different kinds of errors in the process. Right now we have `SyntaxError` and `ValidationError`, but there may appear more if we put more stuff into it.
221+
The purpose of the function `readUser` in the code above is "to read the user data", right? There may occur different kinds of errors in the process. Right now we have `SyntaxError` and `ValidationError`, but in the future `readUser` function may grow: the new code will probably generate other kinds of errors.
222+
223+
The code which calls `readUser` should handle these errors. Right now it uses multiple `if` in the `catch` block to check for different error types and rethrow the unknown ones. But if `readUser` function generates several kinds of errors -- then we should ask ourselves: do we really want to check for all error types one-by-one in every code that calls `readUser`?
203224
204-
Right now the code which calls `readUser` uses multiple `if` in `catch` to check for different error types. The important questions is: do we really want to check for all error types one-by-one every time we call `readUser`?
225+
Often the answer is "No": the outer code wants to be "one level above all that". It wants to have some kind of "data reading error". Why exactly it happened -- is often irrelevant (the error message describes it). Or, even better if there is a way to get error details, but only if we need to.
205226
206-
Often the answer is: "No". The outer code wants to be "one level above all that". It wants to have some kind of "data reading error". Why exactly it happened -- is usually irrelevant (the message has the info). Or, even better if there is a way to get more details, but only if we need to.
227+
So let's make a new class `ReadError` to represent such errors. If an error occurs inside `readUser`, we'll catch it there and generate `ReadError`. We'll also keep the reference to the original error in the `cause` property. Then the outer code will only have to check for `ReadError`.
207228
208-
So let's make a new class `ReadError` to represent such errors. If an error occurs inside `readUser`, we'll catch it there and generate `ReadError`. We'll also keep the reference to the original error in the `cause` property.
229+
Here's the code that defines `ReadError` and demonstrates its use in `readUser` and `try..catch`:
209230
210231
```js run
211232
class ReadError extends Error {
@@ -273,7 +294,9 @@ try {
273294
}
274295
```
275296
276-
In the code above, `readUser` does exactly as described -- catches syntax and validation errors and throws `ReadError` errors instead (unknown errors are rethrown as usual).
297+
In the code above, `readUser` works exactly as described -- catches syntax and validation errors and throws `ReadError` errors instead (unknown errors are rethrown as usual).
298+
299+
So the outer code checks `instanceof ReadError` and that's it. No need to list possible all error types.
277300
278301
The approach is called "wrapping exceptions", because we take "low level exceptions" and "wrap" them into `ReadError` that is more abstract and more convenient to use for the calling code. It is widely used in object-oriented programming.
279302

6-async/05-async-await/01-rewrite-async-2/task.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11

22
# Rewrite "rethrow" async/await
33

4-
Rewrite the "rethrow" example from the chapter <info:promise-chaining> using `async/await` instead of `.then/catch`.
4+
Below you can find the "rethrow" example from the chapter <info:promise-chaining>. Rewrite it using `async/await` instead of `.then/catch`.
55

6-
And get rid of recursion in favour of a loop in `demoGithubUser`: with `async/await` that becomes possible and is easier to develop later on.
6+
And get rid of the recursion in favour of a loop in `demoGithubUser`: with `async/await` that becomes easy to do.
77

88
```js run
99
class HttpError extends Error {

6-async/05-async-await/article.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -119,18 +119,19 @@ async function showAvatar() {
119119
showAvatar();
120120
```
121121
122-
Pretty clean and easy to read, right?
122+
Pretty clean and easy to read, right? Much better than before.
123123
124-
Please note that we can't write `await` in the top-level code. That wouldn't work:
124+
````smart header="`await` won't work in the top-level code"
125+
People who are just starting to use `await` tend to forget that, but we can't write `await` in the top-level code. That wouldn't work:
125126
126127
```js run
127128
// syntax error in top-level code
128129
let response = await fetch('/article/promise-chaining/user.json');
129130
let user = await response.json();
130131
```
131132
132-
So we need to have a wrapping async function for the code that awaits.
133-
133+
So we need to have a wrapping async function for the code that awaits. Just as in the example above.
134+
````
134135
````smart header="`await` accepts thenables"
135136
Like `promise.then`, `await` allows to use thenable objects (those with a callable `then` method). Again, the idea is that a 3rd-party object may be not a promise, but promise-compatible: if it supports `.then`, that's enough to use with `await`.
136137

figures.sketch

-19 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)