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
2 changes: 1 addition & 1 deletion 1-js/01-getting-started/1-intro/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ Examples of such restrictions include:
Modern browsers allow it to work with files, but the access is limited and only provided if the user does certain actions, like "dropping" a file into a browser window or selecting it via an `<input>` tag.

There are ways to interact with camera/microphone and other devices, but they require a user's explicit permission. So a JavaScript-enabled page may not sneakily enable a web-camera, observe the surroundings and send the information to the [NSA](https://en.wikipedia.org/wiki/National_Security_Agency).
- Different tabs/windows generally do not know about each other. Sometimes they do; for example when one window uses JavaScript to open the other one. But even in this case, JavaScript from one page may not access the other if they come from different sites (from a different domain, protocol or port).
- Different tabs/windows generally do not know about each other. Sometimes they do, for example when one window uses JavaScript to open the other one. But even in this case, JavaScript from one page may not access the other if they come from different sites (from a different domain, protocol or port).

This is called the "Same Origin Policy". To work around that, *both pages* must agree for data exchange and contain a special JavaScript code that handles it. We'll cover that in the tutorial.

Expand Down
2 changes: 1 addition & 1 deletion 1-js/01-getting-started/4-devtools/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ To see errors and get a lot of other useful information about scripts, "develope

Most developers lean towards Chrome or Firefox for development because those browsers have the best developer tools. Other browsers also provide developer tools, sometimes with special features, but are usually playing "catch-up" to Chrome or Firefox. So most developers have a "favorite" browser and switch to others if a problem is browser-specific.

Developer tools are potent; they have many features. To start, we'll learn how to open them, look at errors, and run JavaScript commands.
Developer tools are potent, they have many features. To start, we'll learn how to open them, look at errors, and run JavaScript commands.

## Google Chrome

Expand Down
2 changes: 1 addition & 1 deletion 1-js/03-code-quality/02-coding-style/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ Here's an example of an `.eslintrc` file:
},
"rules": {
"no-console": 0,
"indent": ["warning", 2]
"indent": 2
}
}
```
Expand Down
2 changes: 1 addition & 1 deletion 1-js/03-code-quality/06-polyfills/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Actually, there are two parts in Babel:

2. Second, the polyfill.

New language features may include new built-in functions and syntax constructs.
New language features may include not only syntax constructs, but also built-in functions.
The transpiler rewrites the code, transforming syntax constructs into older ones. But as for new built-in functions, we need to implement them. JavaScript is a highly dynamic language, scripts may add/modify any functions, so that they behave according to the modern standard.

A script that updates/adds new functions is called "polyfill". It "fills in" the gap and adds missing implementations.
Expand Down
2 changes: 1 addition & 1 deletion 1-js/04-object-basics/03-garbage-collection/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Simply put, "reachable" values are those that are accessible or usable somehow.

2. Any other value is considered reachable if it's reachable from a root by a reference or by a chain of references.

For instance, if there's an object in a global variable, and that object has a property referencing another object, that object is considered reachable. And those that it references are also reachable. Detailed examples to follow.
For instance, if there's an object in a global variable, and that object has a property referencing another object, *that* object is considered reachable. And those that it references are also reachable. Detailed examples to follow.

There's a background process in the JavaScript engine that is called [garbage collector](https://en.wikipedia.org/wiki/Garbage_collection_(computer_science)). It monitors all objects and removes those that have become unreachable.

Expand Down
85 changes: 62 additions & 23 deletions 1-js/04-object-basics/07-optional-chaining/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,51 +9,89 @@ The optional chaining `?.` is a safe way to access nested object properties, eve

If you've just started to read the tutorial and learn JavaScript, maybe the problem hasn't touched you yet, but it's quite common.

As an example, let's consider objects for user data. Most of our users have addresses in `user.address` property, with the street `user.address.street`, but some did not provide them.
As an example, let's say we have `user` objects that hold the information about our users.

In such case, when we attempt to get `user.address.street`, we'll get an error:
Most of our users have addresses in `user.address` property, with the street `user.address.street`, but some did not provide them.

In such case, when we attempt to get `user.address.street`, and the user happens to be without an address, we get an error:

```js run
let user = {}; // the user without "address" property
let user = {}; // a user without "address" property

alert(user.address.street); // Error!
```

That's the expected result, JavaScript works like this, but many practical cases we'd prefer to get `undefined` instead of an error (meaning "no street").
That's the expected result. JavaScript works like this. As `user.address` is `undefined`, an attempt to get `user.address.street` fails with an error.

In many practical cases we'd prefer to get `undefined` instead of an error here (meaning "no street").

...And another example. In the web development, we may need to get an information about an element on the page, that sometimes doesn't exist:
...And another example. In the web development, we can get an object that corresponds to a web page element using a special method call, such as `document.querySelector('.elem')`, and it returns `null` when there's no such element.

```js run
// Error if the result of querySelector(...) is null
let html = document.querySelector('.my-element').innerHTML;
// document.querySelector('.elem') is null if there's no element
let html = document.querySelector('.elem').innerHTML; // error if it's null
```

Before `?.` appeared in the language, the `&&` operator was used to work around that.
Once again, if the element doesn't exist, we'll get an error accessing `.innerHTML` of `null`. And in some cases, when the absence of the element is normal, we'd like to avoid the error and just accept `html = null` as the result.

For example:
How can we do this?

The obvious solution would be to check the value using `if` or the conditional operator `?`, before accessing its property, like this:

```js
let user = {};

alert(user.address ? user.address.street : undefined);
```

It works, there's no error... But it's quite inelegant. As you can see, the `"user.address"` appears twice in the code. For more deeply nested properties, that becomes a problem as more repetitions are required.

E.g. let's try getting `user.address.street.name`.

We need to check both `user.address` and `user.address.street`:

```js
let user = {}; // user has no address

alert(user.address ? user.address.street ? user.address.street.name : null : null);
```

That's just awful, one may even have problems understanding such code.

Don't even care to, as there's a better way to write it, using the `&&` operator:

```js run
let user = {}; // user has no address

alert( user && user.address && user.address.street ); // undefined (no error)
alert( user.address && user.address.street && user.address.street.name ); // undefined (no error)
```

AND'ing the whole path to the property ensures that all components exist (if not, the evaluation stops), but is cumbersome to write.
AND'ing the whole path to the property ensures that all components exist (if not, the evaluation stops), but also isn't ideal.

As you can see, property names are still duplicated in the code. E.g. in the code above, `user.address` appears three times.

That's why the optional chaining `?.` was added to the language. To solve this problem once and for all!

## Optional chaining

The optional chaining `?.` stops the evaluation and returns `undefined` if the part before `?.` is `undefined` or `null`.
The optional chaining `?.` stops the evaluation if the part before `?.` is `undefined` or `null` and returns that part.

**Further in this article, for brevity, we'll be saying that something "exists" if it's not `null` and not `undefined`.**

Here's the safe way to access `user.address.street`:
In other words, `value?.prop`:
- is the same as `value.prop` if `value` exists,
- otherwise (when `value` is `undefined/null`) it returns `undefined`.

Here's the safe way to access `user.address.street` using `?.`:

```js run
let user = {}; // user has no address

alert( user?.address?.street ); // undefined (no error)
```

The code is short and clean, there's no duplication at all.

Reading the address with `user?.address` works even if `user` object doesn't exist:

```js run
Expand All @@ -65,14 +103,12 @@ alert( user?.address.street ); // undefined

Please note: the `?.` syntax makes optional the value before it, but not any further.

In the example above, `user?.` allows only `user` to be `null/undefined`.

On the other hand, if `user` does exist, then it must have `user.address` property, otherwise `user?.address.street` gives an error at the second dot.
E.g. in `user?.address.street.name` the `?.` allows `user` to be `null/undefined`, but it's all it does. Further properties are accessed in a regular way. If we want some of them to be optional, then we'll need to replace more `.` with `?.`.

```warn header="Don't overuse the optional chaining"
We should use `?.` only where it's ok that something doesn't exist.

For example, if according to our coding logic `user` object must be there, but `address` is optional, then `user.address?.street` would be better.
For example, if according to our coding logic `user` object must exist, but `address` is optional, then we should write `user.address?.street`, but not `user?.address?.street`.

So, if `user` happens to be undefined due to a mistake, we'll see a programming error about it and fix it. Otherwise, coding errors can be silenced where not appropriate, and become more difficult to debug.
```
Expand All @@ -84,7 +120,7 @@ If there's no variable `user` at all, then `user?.anything` triggers an error:
// ReferenceError: user is not defined
user?.address;
```
There must be a declaration (e.g. `let/const/var user`). The optional chaining works only for declared variables.
The variable must be declared (e.g. `let/const/var user` or as a function parameter). The optional chaining works only for declared variables.
````

## Short-circuiting
Expand Down Expand Up @@ -113,17 +149,20 @@ For example, `?.()` is used to call a function that may not exist.
In the code below, some of our users have `admin` method, and some don't:

```js run
let user1 = {
let userAdmin = {
admin() {
alert("I am admin");
}
}
};

let user2 = {};
let userGuest = {};

*!*
userAdmin.admin?.(); // I am admin
*/!*

*!*
user1.admin?.(); // I am admin
user2.admin?.();
userGuest.admin?.(); // nothing (no such method)
*/!*
```

Expand Down
2 changes: 1 addition & 1 deletion 1-js/05-data-types/02-number/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,7 @@ A few examples:
alert( Math.pow(2, 10) ); // 2 in power 10 = 1024
```

There are more functions and constants in `Math` object, including trigonometry, which you can find in the [docs for the Math](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Math) object.
There are more functions and constants in `Math` object, including trigonometry, which you can find in the [docs for the Math object](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Math).

## Summary

Expand Down
2 changes: 1 addition & 1 deletion 1-js/05-data-types/05-array-methods/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -743,7 +743,7 @@ These methods are the most used ones, they cover 99% of use cases. But there are

The function `fn` is called on each element of the array similar to `map`. If any/all results are `true`, returns `true`, otherwise `false`.

These methods behave sort of like `||` and `&&` operators: if `fn` returns a truthy value, `arr.some()` immediately returns `true` and stops iterating over the rest items; if `fn` returns a falsy value, `arr.every()` immediately returns `false` and stops iterating over the rest items as well.
These methods behave sort of like `||` and `&&` operators: if `fn` returns a truthy value, `arr.some()` immediately returns `true` and stops iterating over the rest items; if `fn` returns a falsy value, `arr.every()` immediately returns `false` and stops iterating over the rest items as well.

We can use `every` to compare arrays:
```js run
Expand Down
3 changes: 2 additions & 1 deletion 1-js/05-data-types/08-weakmap-weakset/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ let array = [ john ];
john = null; // overwrite the reference

*!*
// john is stored inside the array, so it won't be garbage-collected
// the object previously referenced by john is stored inside the array
// therefore it won't be garbage-collected
// we can get it as array[0]
*/!*
```
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
By definition, a factorial is `n!` can be written as `n * (n-1)!`.
By definition, a factorial `n!` can be written as `n * (n-1)!`.

In other words, the result of `factorial(n)` can be calculated as `n` multiplied by the result of `factorial(n-1)`. And the call for `n-1` can recursively descend lower, and lower, till `1`.

Expand Down
4 changes: 2 additions & 2 deletions 1-js/06-advanced-functions/01-recursion/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ We can sketch it as:
</li>
</ul>

That's when the function starts to execute. The condition `n == 1` is false, so the flow continues into the second branch of `if`:
That's when the function starts to execute. The condition `n == 1` is falsy, so the flow continues into the second branch of `if`:

```js run
function pow(x, n) {
Expand Down Expand Up @@ -188,7 +188,7 @@ The new current execution context is on top (and bold), and previous remembered
When we finish the subcall -- it is easy to resume the previous context, because it keeps both variables and the exact place of the code where it stopped.

```smart
Here in the picture we use the word "line", as our example there's only one subcall in line, but generally a single line of code may contain multiple subcalls, like `pow(…) + pow(…) + somethingElse(…)`.
Here in the picture we use the word "line", as in our example there's only one subcall in line, but generally a single line of code may contain multiple subcalls, like `pow(…) + pow(…) + somethingElse(…)`.

So it would be more precise to say that the execution resumes "immediately after the subcall".
```
Expand Down
4 changes: 2 additions & 2 deletions 1-js/06-advanced-functions/04-var/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ function sayHi() {
}

sayHi();
alert(phrase); // Error: phrase is not defined (Check the Developer Console)
alert(phrase); // Error: phrase is not defined
```

As we can see, `var` pierces through `if`, `for` or other code blocks. That's because a long time ago in JavaScript, blocks had no Lexical Environments, and `var` is a remnant of that.
Expand Down Expand Up @@ -279,7 +279,7 @@ In all the above cases we declare a Function Expression and run it immediately.

There are two main differences of `var` compared to `let/const`:

1. `var` variables have no block scope; their visibility is scoped to current function, or global, if declared outside function.
1. `var` variables have no block scope, their visibility is scoped to current function, or global, if declared outside function.
2. `var` declarations are processed at function start (script start for globals).

There's one more very minor difference related to the global object, that we'll cover in the next chapter.
Expand Down
4 changes: 3 additions & 1 deletion 1-js/06-advanced-functions/05-global-object/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ The global object provides variables and functions that are available anywhere.

In a browser it is named `window`, for Node.js it is `global`, for other environments it may have another name.

Recently, `globalThis` was added to the language, as a standardized name for a global object, that should be supported across all environments. It's supported in all major browsers.
Recently, `globalThis` was added to the language, as a standardized name for a global object, that should be supported across all environments. It's supported in all major browsers.

We'll use `window` here, assuming that our environment is a browser. If your script may run in other environments, it's better to use `globalThis` instead.

Expand All @@ -25,6 +25,8 @@ var gVar = 5;
alert(window.gVar); // 5 (became a property of the global object)
```

The same effect have function declarations (statements with `function` keyword in the main code flow, not function expressions).

Please don't rely on that! This behavior exists for compatibility reasons. Modern scripts use [JavaScript modules](info:modules) where such thing doesn't happen.

If we used `let` instead, such thing wouldn't happen:
Expand Down
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
Expand Up @@ -347,7 +347,7 @@ If the function is declared as a Function Expression (not in the main code flow)

Also, functions may carry additional properties. Many well-known JavaScript libraries make great use of this feature.

They create a "main" function and attach many other "helper" functions to it. For instance, the [jQuery](https://jquery.com) library creates a function named `$`. The [lodash](https://lodash.com) library creates a function `_`, and then adds `_.clone`, `_.keyBy` and other properties to it (see the [docs](https://lodash.com/docs) when you want learn more about them). Actually, they do it to lessen their pollution of the global space, so that a single library gives only one global variable. That reduces the possibility of naming conflicts.
They create a "main" function and attach many other "helper" functions to it. For instance, the [jQuery](https://jquery.com) library creates a function named `$`. The [lodash](https://lodash.com) library creates a function `_`, and then adds `_.clone`, `_.keyBy` and other properties to it (see the [docs](https://lodash.com/docs) when you want to learn more about them). Actually, they do it to lessen their pollution of the global space, so that a single library gives only one global variable. That reduces the possibility of naming conflicts.


So, a function can do a useful job by itself and also carry a bunch of other functionality in properties.
2 changes: 1 addition & 1 deletion 1-js/06-advanced-functions/07-new-function/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ What if it could access the outer variables?

The problem is that before JavaScript is published to production, it's compressed using a *minifier* -- a special program that shrinks code by removing extra comments, spaces and -- what's important, renames local variables into shorter ones.

For instance, if a function has `let userName`, minifier replaces it `let a` (or another letter if this one is occupied), and does it everywhere. That's usually a safe thing to do, because the variable is local, nothing outside the function can access it. And inside the function, minifier replaces every mention of it. Minifiers are smart, they analyze the code structure, so they don't break anything. They're not just a dumb find-and-replace.
For instance, if a function has `let userName`, minifier replaces it with `let a` (or another letter if this one is occupied), and does it everywhere. That's usually a safe thing to do, because the variable is local, nothing outside the function can access it. And inside the function, minifier replaces every mention of it. Minifiers are smart, they analyze the code structure, so they don't break anything. They're not just a dumb find-and-replace.

So if `new Function` had access to outer variables, it would be unable to find renamed `userName`.

Expand Down
Loading