Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 1 addition & 2 deletions 1-js/02-first-steps/12-while-for/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -308,8 +308,7 @@ for (let i = 0; i < 3; i++) {

let input = prompt(`Value at coords (${i},${j})`, '');

// what if I want to exit from here to Done (below)?

// what if we want to exit from here to Done (below)?
}
}

Expand Down
14 changes: 14 additions & 0 deletions 1-js/04-object-basics/01-object/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,20 @@ let key = prompt("What do you want to know about the user?", "name");
alert( user[key] ); // John (if enter "name")
```

<<<<<<< HEAD
=======
The dot notation cannot be used in a similar way:

```js run
let user = {
name: "John",
age: 30
};

let key = "name";
alert( user.key ) // undefined
```
>>>>>>> 852ee189170d9022f67ab6d387aeae76810b5923

### Computed properties

Expand Down
32 changes: 21 additions & 11 deletions 1-js/04-object-basics/03-symbol/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@

By specification, object property keys may be either of string type, or of symbol type. Not numbers, not booleans, only strings or symbols, these two types.

Till now we've only seen strings. Now let's see the advantages that symbols can give us.
Till now we've been using only strings. Now let's see the benefits that symbols can give us.

## Symbols

"Symbol" value represents a unique identifier.
A "symbol" represents a unique identifier.

A value of this type can be created using `Symbol()`:

Expand Down Expand Up @@ -52,15 +52,15 @@ alert(id); // TypeError: Cannot convert a Symbol value to a string

That's a "language guard" against messing up, because strings and symbols are fundamentally different and should not occasionally convert one into another.

If we really want to show a symbol, we need to call `.toString()` on it, like here:
If we really want to show a symbol, we need to explicitly call `.toString()` on it, like here:
```js run
let id = Symbol("id");
*!*
alert(id.toString()); // Symbol(id), now it works
*/!*
```

Or get `symbol.description` property to get the description only:
Or get `symbol.description` property to show the description only:
```js run
let id = Symbol("id");
*!*
Expand All @@ -74,13 +74,23 @@ alert(id.description); // id

Symbols allow us to create "hidden" properties of an object, that no other part of code can occasionally access or overwrite.

<<<<<<< HEAD
For instance, if we want to store an "identifier" for the object `user`, we can use a symbol as a key for it:
=======
For instance, if we're working with `user` objects, that belong to a third-party code. We'd like to add identifiers to them.

Let's use a symbol key for it:
>>>>>>> 852ee189170d9022f67ab6d387aeae76810b5923

```js run
let user = { name: "John" };
let user = { // belongs to another code
name: "John"
};

let id = Symbol("id");

user[id] = "ID Value";
user[id] = 1;

alert( user[id] ); // we can access the data using the symbol as the key
```

Expand All @@ -106,13 +116,13 @@ Now note that if we used a string `"id"` instead of a symbol for the same purpos
```js run
let user = { name: "John" };

// our script uses "id" property
user.id = "ID Value";
// Our script uses "id" property
user.id = "Our id value";

// ...if later another script the uses "id" for its purposes...
// ...Another script also wants "id" for its purposes...

user.id = "Their id value"
// boom! overwritten! it did not mean to harm the colleague, but did it!
// Boom! overwritten by another script!
```

### Symbols in a literal
Expand All @@ -127,7 +137,7 @@ let id = Symbol("id");
let user = {
name: "John",
*!*
[id]: 123 // not just "id: 123"
[id]: 123 // not "id: 123"
*/!*
};
```
Expand Down
4 changes: 2 additions & 2 deletions 1-js/05-data-types/03-string/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -355,8 +355,8 @@ alert( "Hello".includes("Bye") ); // false
The optional second argument of `str.includes` is the position to start searching from:

```js run
alert( "Midget".includes("id") ); // true
alert( "Midget".includes("id", 3) ); // false, from position 3 there is no "id"
alert( "Widget".includes("id") ); // true
alert( "Widget".includes("id", 3) ); // false, from position 3 there is no "id"
```

The methods [str.startsWith](mdn:js/String/startsWith) and [str.endsWith](mdn:js/String/endsWith) do exactly what they say:
Expand Down
8 changes: 7 additions & 1 deletion 1-js/05-data-types/05-array-methods/9-shuffle/solution.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,13 @@ There are other good ways to do the task. For instance, there's a great algorith
function shuffle(array) {
for (let i = array.length - 1; i > 0; i--) {
let j = Math.floor(Math.random() * (i + 1)); // random index from 0 to i
[array[i], array[j]] = [array[j], array[i]]; // swap elements

// swap elements array[i] and array[j]
// we use "destructuring assignment" syntax to achieve that
// you'll find more details about that syntax in later chapters
// same can be written as:
// let t = array[i]; array[i] = array[j]; array[j] = t
[array[i], array[j]] = [array[j], array[i]];
}
}
```
Expand Down
6 changes: 5 additions & 1 deletion 1-js/09-classes/07-mixins/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@

In JavaScript we can only inherit from a single object. There can be only one `[[Prototype]]` for an object. And a class may extend only one other class.

<<<<<<< HEAD
But sometimes that feels limiting. For instance, I have a class `StreetSweeper` and a class `Bicycle`, and want to make a `StreetSweepingBicycle`.
=======
But sometimes that feels limiting. For instance, we have a class `StreetSweeper` and a class `Bicycle`, and want to make their mix: a `StreetSweepingBicycle`.
>>>>>>> 852ee189170d9022f67ab6d387aeae76810b5923

Or, talking about programming, we have a class `Renderer` that implements templating and a class `EventEmitter` that implements event handling, and want to merge these functionalities together with a class `Page`, to make a page that can use templates and emit events.

There's a concept that can help here, called "mixins".

As defined in Wikipedia, a [mixin](https://en.wikipedia.org/wiki/Mixin) is a class that contains methods for use by other classes without having to be the parent class of those other classes.
As defined in Wikipedia, a [mixin](https://en.wikipedia.org/wiki/Mixin) is a class containing methods that can be used by other classes without a need to inherit from it.

In other words, a *mixin* provides methods that implement a certain behavior, but we do not use it alone, we use it to add the behavior to other classes.

Expand Down
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
Expand Up @@ -302,7 +302,7 @@ try {
*!*
alert(e.name); // SyntaxError
*/!*
alert(e.message); // Unexpected token o in JSON at position 0
alert(e.message); // Unexpected token o in JSON at position 2
}
```

Expand Down
24 changes: 24 additions & 0 deletions 1-js/11-async/01-callbacks/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,30 @@ As calls become more nested, the code becomes deeper and increasingly more diffi

That's sometimes called "callback hell" or "pyramid of doom."

<!--
loadScript('1.js', function(error, script) {
if (error) {
handleError(error);
} else {
// ...
loadScript('2.js', function(error, script) {
if (error) {
handleError(error);
} else {
// ...
loadScript('3.js', function(error, script) {
if (error) {
handleError(error);
} else {
// ...
}
});
}
})
}
});
-->

![](callback-hell.svg)

The "pyramid" of nested calls grows to the right with every asynchronous action. Soon it spirals out of control.
Expand Down
6 changes: 5 additions & 1 deletion 1-js/11-async/01-callbacks/callback-hell.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions 1-js/11-async/04-promise-error-handling/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,11 @@ new Promise((resolve, reject) => {
}

}).then(function() {
<<<<<<< HEAD
/* never runs here */
=======
/* doesn't run here */
>>>>>>> 852ee189170d9022f67ab6d387aeae76810b5923
}).catch(error => { // (**)

alert(`The unknown error has occurred: ${error}`);
Expand Down Expand Up @@ -266,7 +270,11 @@ new Promise(function() {

In case of an error, the promise state becomes "rejected", and the execution should jump to the closest rejection handler. But there is no such handler in the examples above. So the error gets "stuck".

<<<<<<< HEAD
In practice, just like with a regular unhandled errors, it means that something has terribly gone wrong, the script probably died.
=======
In practice, just like with regular unhandled errors in code, it means that something has terribly gone wrong.
>>>>>>> 852ee189170d9022f67ab6d387aeae76810b5923

Most JavaScript engines track such situations and generate a global error in that case. We can see it in the console.

Expand Down
15 changes: 15 additions & 0 deletions 1-js/12-generators-iterators/1-generators/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -243,10 +243,25 @@ That surely would require a `break` in `for..of`, otherwise the loop would repea

Generator composition is a special feature of generators that allows to transparently "embed" generators in each other.

<<<<<<< HEAD
For instance, we'd like to generate a sequence of:
- digits `0..9` (character codes 48..57),
- followed by alphabet letters `a..z` (character codes 65..90)
- followed by uppercased letters `A..Z` (character codes 97..122)
=======
For instance, we have a function that generates a sequence of numbers:

```js
function* generateSequence(start, end) {
for (let i = start; i <= end; i++) yield i;
}
```

Now we'd like to reuse it for generation of a more complex sequence:
- first, digits `0..9` (with character codes 48..57),
- followed by alphabet letters `A..Z` (character codes 65..90)
- followed by uppercased letters `a..z` (character codes 97..122)
>>>>>>> 852ee189170d9022f67ab6d387aeae76810b5923

Then we plan to create passwords by selecting characters from it (could add syntax characters as well), but need to generate the sequence first.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,11 @@ for await (let commit of fetchCommits(repo)) {
}
```

<<<<<<< HEAD
We'd like `fetchCommits` to get commits for us, making requests whenever needed. And let it care about all pagination stuff, for us it'll be a simple `for await..of`.
=======
We'd like to make a function `fetchCommits(repo)` that gets commits for us, making requests whenever needed. And let it care about all pagination stuff, for us it'll be a simple `for await..of`.
>>>>>>> 852ee189170d9022f67ab6d387aeae76810b5923

With async generators that's pretty easy to implement:

Expand Down Expand Up @@ -358,4 +362,8 @@ In web-development we often meet streams of data, when it flows chunk-by-chunk.

We could use async generators to process such data, but there's also another API called Streams, that may be more convenient, as it provides special interfaces to transform the data and to pass it from one stream to another (e.g. download from one place and immediately send elsewhere). But they are also more complex.

<<<<<<< HEAD
Streams API not a part of JavaScript language standard. Streams and async generators complement each other, both are great ways to handle async data flows.
=======
Streams API is not a part of JavaScript language standard.
>>>>>>> 852ee189170d9022f67ab6d387aeae76810b5923
27 changes: 27 additions & 0 deletions 1-js/99-js-misc/01-proxy/03-observable/task.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@

# Observable

Create a function `makeObservable(target)` that "makes the object observable" by returning a proxy.

Here's how it should work:

```js run
function makeObservable(target) {
/* your code */
}

let user = {};
user = makeObservable(user);

user.observe((key, value) => {
alert(`SET ${key}=${value}`);
});

user.name = "John"; // alerts: SET name=John
```

In other words, an object returned by `makeObservable` is just like the original one, but also has the method `observe(handler)` that sets `handler` function to be called on any property change.

Whenever a property changes, `handler(key, value)` is called with the name and value of the property.

P.S. In this task, please only take care about writing to a property. Other operations can be implemented in a similar way.
Loading