Skip to content

Sync with upstream @ 02793353 #2

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 42 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
f5f7550
Update article.md
tonchique Jun 6, 2019
3a5647a
Update article.md
tonchique Jun 7, 2019
d8e04e4
update description regarding forEach method on Set
Violet-Bora-Lee Jun 8, 2019
fec4742
update description regarding forEach method on Set
Violet-Bora-Lee Jun 8, 2019
f752403
Merge remote-tracking branch 'origin/patch-6' into patch-6
Violet-Bora-Lee Jun 8, 2019
7c6cfeb
Update article.md (#1040)
lex111 Jun 10, 2019
728998b
regexp
iliakan Jun 10, 2019
dbcbd45
fix
iliakan Jun 10, 2019
1913b45
Merge branch 'master' into patch-7
tonchique Jun 10, 2019
1b963bd
Update article.md (#1043)
lex111 Jun 10, 2019
e1c0d62
Merge pull request #1044 from Violet-Bora-Lee/patch-6
iliakan Jun 11, 2019
bfd62b9
Update article.md
developerdavo Jun 11, 2019
88b8604
Merge pull request #1 from javascript-tutorial/master
DouglasMV Jun 11, 2019
ace757d
missing backticks
DouglasMV Jun 11, 2019
1856fe3
missing backticks
DouglasMV Jun 11, 2019
cce0816
typo
DouglasMV Jun 11, 2019
3e2e8c7
Spelling and grammar fixes to generators and modules
jasmaa Jun 12, 2019
f6e2c89
Merge pull request #1048 from DeveloperDavo/master
iliakan Jun 12, 2019
5498450
fixes
iliakan Jun 12, 2019
60585f2
fix
iliakan Jun 12, 2019
726f08a
Merge pull request #1049 from DouglasMV/master
iliakan Jun 12, 2019
852b9bf
proxy
iliakan Jun 12, 2019
c6620f6
Fix typo
lex111 Jun 13, 2019
011eb86
css
iliakan Jun 14, 2019
afdd505
Update README.md
iliakan Jun 14, 2019
611cef3
Update README.md
iliakan Jun 14, 2019
fea7593
Update README.md
iliakan Jun 14, 2019
8be8d6c
Update README.md
iliakan Jun 14, 2019
dfb4f00
Merge branch 'master' of https://github.com/javascript-tutorial/en.ja…
jasmaa Jun 14, 2019
76d599f
rewrite global object
iliakan Jun 14, 2019
f418ab3
fix
iliakan Jun 14, 2019
ed85606
update example scripts
Violet-Bora-Lee Jun 15, 2019
661cb16
Merge pull request #1059 from Violet-Bora-Lee/patch-8
iliakan Jun 15, 2019
e35bcf2
Merge pull request #1057 from jasmaa/minor-fixes
iliakan Jun 15, 2019
358c140
fixes
iliakan Jun 15, 2019
1dc1e63
Fix typo
Violet-Bora-Lee Jun 15, 2019
0e81fb4
Fix typo (#1061)
lex111 Jun 15, 2019
42a1108
minor
iliakan Jun 15, 2019
329a53c
eval
iliakan Jun 15, 2019
70cf2b2
minor
iliakan Jun 16, 2019
0279335
fixes, eventSource
iliakan Jun 16, 2019
a866ae4
merging all conflicts
Jun 17, 2019
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
20 changes: 4 additions & 16 deletions 1-js/01-getting-started/2-code-editors/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,12 @@ An IDE loads the project (which can be many files), allows navigation between fi

If you haven't selected an IDE yet, consider the following options:

- [WebStorm](http://www.jetbrains.com/webstorm/) for frontend development. The same company offers other editors for other languages (paid).
- [Netbeans](http://netbeans.org/) (free).
- [Visual Studio Code](https://code.visualstudio.com/) (cross-platform, free).
- [WebStorm](http://www.jetbrains.com/webstorm/) (cross-platform, paid).

All of these IDEs are cross-platform.
For Windows, there's also "Visual Studio", not to be confused with "Visual Studio Code". "Visual Studio" is a paid and mighty Windows-only editor, well-suited for the .NET platform. It's also good at JavaScript. There's also a free version [Visual Studio Community](https://www.visualstudio.com/vs/community/).

For Windows, there's also "Visual Studio", not to be confused with "Visual Studio Code." "Visual Studio" is a paid and mighty Windows-only editor, well-suited for the .NET platform. A free version of it is called [Visual Studio Community](https://www.visualstudio.com/vs/community/).

Many IDEs are paid but have a trial period. Their cost is usually negligible compared to a qualified developer's salary, so just choose the best one for you.
Many IDEs are paid, but have a trial period. Their cost is usually negligible compared to a qualified developer's salary, so just choose the best one for you.

## Lightweight editors

Expand All @@ -33,21 +31,11 @@ In practice, lightweight editors may have a lot of plugins including directory-l

The following options deserve your attention:

- [Visual Studio Code](https://code.visualstudio.com/) (cross-platform, free) also has many IDE-like features.
- [Atom](https://atom.io/) (cross-platform, free).
- [Sublime Text](http://www.sublimetext.com) (cross-platform, shareware).
- [Notepad++](https://notepad-plus-plus.org/) (Windows, free).
- [Vim](http://www.vim.org/) and [Emacs](https://www.gnu.org/software/emacs/) are also cool if you know how to use them.

## My favorites

The personal preference of the author is to have both an IDE for projects and a lightweight editor for quick and easy file editing.

I'm using:

- As an IDE for JS -- [WebStorm](http://www.jetbrains.com/webstorm/) (I switch to one of the other JetBrains offerings when using other languages)
- As a lightweight editor -- [Sublime Text](http://www.sublimetext.com) or [Atom](https://atom.io/).

## Let's not argue

The editors in the lists above are those that either I or my friends whom I consider good developers have been using for a long time and are happy with.
Expand Down
26 changes: 13 additions & 13 deletions 1-js/04-object-basics/03-symbol/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ A value of this type can be created using `Symbol()`:
let id = Symbol();
```

We can also give symbol a description (also called a symbol name), mostly useful for debugging purposes:
Upon creation, we can give symbol a description (also called a symbol name), mostly useful for debugging purposes:

```js run
// id is a symbol with the description "id"
Expand Down Expand Up @@ -74,7 +74,7 @@ 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.

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'd like to add an "identifier" to the object `user`, we can use a symbol as a key for it:

```js run
let user = { name: "John" };
Expand All @@ -88,7 +88,7 @@ What's the benefit of using `Symbol("id")` over a string `"id"`?

Let's make the example a bit deeper to see that.

Imagine that another script wants to have its own "id" property inside `user`, for its own purposes. That may be another JavaScript library, so the scripts are completely unaware of each other.
Imagine that another script wants to have its own identifier inside `user`, for its own purposes. That may be another JavaScript library, so thes scripts are completely unaware of each other.

Then that script can create its own `Symbol("id")`, like this:

Expand All @@ -99,9 +99,9 @@ let id = Symbol("id");
user[id] = "Their id value";
```

There will be no conflict, because symbols are always different, even if they have the same name.
There will be no conflict between our and their identifiers, because symbols are always different, even if they have the same name.

Now note that if we used a string `"id"` instead of a symbol for the same purpose, then there *would* be a conflict:
...But if we used a string `"id"` instead of a symbol for the same purpose, then there *would* be a conflict:

```js run
let user = { name: "John" };
Expand All @@ -117,7 +117,7 @@ user.id = "Their id value"

### Symbols in a literal

If we want to use a symbol in an object literal, we need square brackets.
If we want to use a symbol in an object literal `{...}`, we need square brackets around it.

Like this:

Expand Down Expand Up @@ -155,7 +155,7 @@ for (let key in user) alert(key); // name, age (no symbols)
alert( "Direct: " + user[id] );
```

That's a part of the general "hiding" concept. If another script or a library loops over our object, it won't unexpectedly access a symbolic property.
`Object.keys(user)` also ignores them. That's a part of the general "hiding symbolic properties" principle. If another script or a library loops over our object, it won't unexpectedly access a symbolic property.

In contrast, [Object.assign](mdn:js/Object/assign) copies both string and symbol properties:

Expand Down Expand Up @@ -190,13 +190,13 @@ alert( obj[0] ); // test (same property)

## Global symbols

As we've seen, usually all symbols are different, even if they have the same names. But sometimes we want same-named symbols to be same entities.
As we've seen, usually all symbols are different, even if they have the same name. But sometimes we want same-named symbols to be same entities.

For instance, different parts of our application want to access symbol `"id"` meaning exactly the same property.

To achieve that, there exists a *global symbol registry*. We can create symbols in it and access them later, and it guarantees that repeated accesses by the same name return exactly the same symbol.

In order to create or read a symbol in the registry, use `Symbol.for(key)`.
In order to read (create if absent) a symbol from the registry, use `Symbol.for(key)`.

That call checks the global registry, and if there's a symbol described as `key`, then returns it, otherwise creates a new symbol `Symbol(key)` and stores it in the registry by the given `key`.

Expand All @@ -206,7 +206,7 @@ For instance:
// read from the global registry
let id = Symbol.for("id"); // if the symbol did not exist, it is created

// read it again
// read it again (maybe from another part of the code)
let idAgain = Symbol.for("id");

// the same symbol
Expand Down Expand Up @@ -266,14 +266,14 @@ Other symbols will also become familiar when we study the corresponding language

`Symbol` is a primitive type for unique identifiers.

Symbols are created with `Symbol()` call with an optional description.
Symbols are created with `Symbol()` call with an optional description (name).

Symbols are always different values, even if they have the same name. If we want same-named symbols to be equal, then we should use the global registry: `Symbol.for(key)` returns (creates if needed) a global symbol with `key` as the name. Multiple calls of `Symbol.for` return exactly the same symbol.
Symbols are always different values, even if they have the same name. If we want same-named symbols to be equal, then we should use the global registry: `Symbol.for(key)` returns (creates if needed) a global symbol with `key` as the name. Multiple calls of `Symbol.for` with the same `key` return exactly the same symbol.

Symbols have two main use cases:

1. "Hidden" object properties.
If we want to add a property into an object that "belongs" to another script or a library, we can create a symbol and use it as a property key. A symbolic property does not appear in `for..in`, so it won't be occasionally listed. Also it won't be accessed directly, because another script does not have our symbol, so it will not occasionally intervene into its actions.
If we want to add a property into an object that "belongs" to another script or a library, we can create a symbol and use it as a property key. A symbolic property does not appear in `for..in`, so it won't be occasionally processed together with other properties. Also it won't be accessed directly, because another script does not have our symbol. So the property will be protected from occasional use or overwrite.

So we can "covertly" hide something into objects that we need, but others should not see, using symbolic properties.

Expand Down
2 changes: 1 addition & 1 deletion 1-js/05-data-types/03-string/2-check-spam/task.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ importance: 5

# Check for spam

Write a function `checkSpam(str)` that returns `true` if `str` contains 'viagra' or 'XXX', otherwise false.
Write a function `checkSpam(str)` that returns `true` if `str` contains 'viagra' or 'XXX', otherwise `false`.

The function must be case-insensitive:

Expand Down
2 changes: 1 addition & 1 deletion 1-js/05-data-types/04-array/2-create-array/task.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ The array in the process:

```js no-beautify
Jazz, Blues
Jazz, Bues, Rock-n-Roll
Jazz, Blues, Rock-n-Roll
Jazz, Classics, Rock-n-Roll
Classics, Rock-n-Roll
Rap, Reggae, Classics, Rock-n-Roll
Expand Down
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
Expand Up @@ -453,7 +453,7 @@ We can use an array as a deque with the following operations:
- `push(...items)` adds `items` to the end.
- `pop()` removes the element from the end and returns it.
- `shift()` removes the element from the beginning and returns it.
- `unshift(...items)` adds items to the beginning.
- `unshift(...items)` adds `items` to the beginning.

To loop over the elements of the array:
- `for (let i=0; i<arr.length; i++)` -- works fastest, old-browser-compatible.
Expand Down
27 changes: 22 additions & 5 deletions 1-js/05-data-types/07-map-set-weakmap-weakset/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,24 @@ alert( visitsCountMap.get(john) ); // 123

Using objects as keys is one of most notable and important `Map` features. For string keys, `Object` can be fine, but it would be difficult to replace the `Map` with a regular `Object` in the example above.

In the old times, before `Map` existed, people added unique identifiers to objects for that:
Let's try:

```js run
let john = { name: "John" };

let visitsCountObj = {}; // try to use an object

visitsCountObj[john] = 123; // try to use john object as the key

*!*
// That's what got written!
alert( visitsCountObj["[object Object]"] ); // 123
*/!*
```

As `john` is an object, it got converted to the key string `"[object Object]"`. All objects without a special conversion handling are converted to such string, so they'll all mess up.

In the old times, before `Map` existed, people used to add unique identifiers to objects for that:

```js run
// we add the id field
Expand Down Expand Up @@ -159,7 +176,7 @@ The iteration goes in the same order as the values were inserted. `Map` preserve
Besides that, `Map` has a built-in `forEach` method, similar to `Array`:

```js
// runs the function for each (key, value) pair
// runs the function for each (key, value) pair
recipeMap.forEach( (value, key, map) => {
alert(`${key}: ${value}`); // cucumber: 500 etc
});
Expand All @@ -172,7 +189,7 @@ A `Set` is a collection of values, where each value may occur only once.

Its main methods are:

- `new Set(iterable)` -- creates the set, optionally from an array of values (any iterable will do).
- `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`.
Expand Down Expand Up @@ -222,9 +239,9 @@ set.forEach((value, valueAgain, set) => {
});
```

Note the funny thing. The `forEach` function in the `Set` has 3 arguments: a value, then *again a value*, and then the target object. Indeed, the same value appears in the arguments twice.
Note the funny thing. The callback function passed in `forEach` has 3 arguments: a value, then *again a value*, and then the target object. Indeed, the same value appears in the arguments twice.

That's for compatibility with `Map` where `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 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:

Expand Down
95 changes: 90 additions & 5 deletions 1-js/05-data-types/08-keys-values-entries/article.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@

# Object.keys, values, entries

Let's step away from the individual data structures and talk about the iterations over them.
Let's step away from the individual data structures and talk about the iterations over them.

In the previous chapter we saw methods `map.keys()`, `map.values()`, `map.entries()`.

These methods are generic, there is a common agreement to use them for data structures. If we ever create a data structure of our own, we should implement them too.
These methods are generic, there is a common agreement to use them for data structures. If we ever create a data structure of our own, we should implement them too.

They are supported for:

Expand Down Expand Up @@ -63,8 +63,93 @@ for (let value of Object.values(user)) {
}
```

## Object.keys/values/entries ignore symbolic properties

```warn header="Object.keys/values/entries ignore symbolic properties"
Just like a `for..in` loop, these methods ignore properties that use `Symbol(...)` as keys.

Usually that's convenient. But if we want symbolic keys too, then there's a separate method [Object.getOwnPropertySymbols](mdn:js/Object/getOwnPropertySymbols) that returns an array of only symbolic keys. Also, the method [Reflect.ownKeys(obj)](mdn:js/Reflect/ownKeys) returns *all* keys.
Usually that's convenient. But if we want symbolic keys too, then there's a separate method [Object.getOwnPropertySymbols](mdn:js/Object/getOwnPropertySymbols) that returns an array of only symbolic keys. Also, there exist a method [Reflect.ownKeys(obj)](mdn:js/Reflect/ownKeys) that returns *all* keys.
```

## Object.fromEntries to transform objects

Sometimes we need to perform a transformation of an object to `Map` and back.

We already have `new Map(Object.entries(obj))` to make a `Map` from `obj`.

The syntax of `Object.fromEntries` does the reverse. Given an array of `[key, value]` pairs, it creates an object:

```js run
let prices = Object.fromEntries([
['banana', 1],
['orange', 2],
['meat', 4]
]);

// now prices = { banana: 1, orange: 2, meat: 4 }

alert(prices.orange); // 2
```

Let's see practical applications.

For example, we'd like to create a new object with double prices from the existing one.

For arrays, we have `.map` method that allows to transform an array, but nothing like that for objects.

So we can use a loop:

```js run
let prices = {
banana: 1,
orange: 2,
meat: 4,
};

let doublePrices = {};
for(let [product, price] of Object.entries(prices)) {
doublePrices[product] = price * 2;
}

alert(doublePrices.meat); // 8
```

...Or we can represent the object as an `Array` using `Object.entries`, then perform the operations with `map` (and potentially other array methods), and then go back using `Object.fromEntries`.

Let's do it for our object:

```js run
let prices = {
banana: 1,
orange: 2,
meat: 4,
};

*!*
let doublePrices = Object.fromEntries(
// convert to array, map, and then fromEntries gives back the object
Object.entries(prices).map(([key, value]) => [key, value * 2])
);
*/!*

alert(doublePrices.meat); // 8
```

It may look difficult from the first sight, but becomes easy to understand after you use it once or twice.

We also can use `fromEntries` to get an object from `Map`.

E.g. we have a `Map` of prices, but we need to pass it to a 3rd-party code that expects an object.

Here we go:

```js run
let map = new Map();
map.set('banana', 1);
map.set('orange', 2);
map.set('meat', 4);

let obj = Object.fromEntries(map);

// now obj = { banana: 1, orange: 2, meat: 4 }

alert(obj.orange); // 2
```
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,4 @@ P.S. Naturally, the formula is the fastest solution. It uses only 3 operations f

The loop variant is the second in terms of speed. In both the recursive and the loop variant we sum the same numbers. But the recursion involves nested calls and execution stack management. That also takes resources, so it's slower.

P.P.S. The standard describes a "tail call" optimization: if the recursive call is the very last one in the function (like in `sumTo` above), then the outer function will not need to resume the execution and we don't need to remember its execution context. In that case `sumTo(100000)` is countable. But if your JavaScript engine does not support it, there will be an error: maximum stack size exceeded, because there's usually a limitation on the total stack size.
P.P.S. Some engines support the "tail call" optimization: if a recursive call is the very last one in the function (like in `sumTo` above), then the outer function will not need to resume the execution, so the engine doesn't need to remember its execution context. That removes the burden on memory, so counting `sumTo(100000)` becomes possible. But if the JavaScript engine does not support tail call optimization (most of them don't), there will be an error: maximum stack size exceeded, because there's usually a limitation on the total stack size.
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ The loop variant is also a little bit more complicated then the direct output.

There is no way to get the last value in our `list`. We also can't "go back".

So what we can do is to first go through the items in the direct order and rememeber them in an array, and then output what we remembered in the reverse order:
So what we can do is to first go through the items in the direct order and remember them in an array, and then output what we remembered in the reverse order:

```js run
let list = {
Expand Down
Loading