Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
5189d51
minor change
haond10adp Sep 26, 2020
02e82ad
Update article.md
haond10adp Sep 26, 2020
73550e5
Update 'properties' to 'property values'
Mr0cket Sep 27, 2020
2c65914
correct minor typo
ZYinMD Sep 27, 2020
af95ad1
Fix wrong variables names.
Ledorub Sep 28, 2020
c970f42
Fix link in 1.2.18
vsemozhetbyt Sep 28, 2020
f11dd6d
Update README.md
CrypterSr Oct 1, 2020
af9b339
Update README.md
Anurag-Chauhan-289 Oct 1, 2020
1d13d11
Update article.md
Anurag-Chauhan-289 Oct 1, 2020
6fca8a4
minor fixes
iliakan Oct 5, 2020
4320617
Fix syntax enumeration in 1.4.7
vsemozhetbyt Oct 5, 2020
ae06ca6
Merge pull request #2168 from vsemozhetbyt/patch-3
lex111 Oct 5, 2020
f409905
minor fixes
iliakan Oct 5, 2020
545f514
Remove extra trailing style tag
lex111 Oct 5, 2020
f4cf580
minor fixes
iliakan Oct 6, 2020
6f24128
Merge pull request #2151 from haond10adp/patch-1
iliakan Oct 7, 2020
1200946
Merge pull request #2155 from Mr0cket/patch-2
iliakan Oct 7, 2020
77e2dee
Merge pull request #2157 from ZYinMD/patch-3
iliakan Oct 7, 2020
d77dbf8
Merge pull request #2158 from Ledorub/indexeddb-example-fix
iliakan Oct 7, 2020
fa0bcd3
Merge pull request #2159 from vsemozhetbyt/patch-2
iliakan Oct 7, 2020
ea8fc1a
Merge pull request #2162 from CrypterSr/patch-1
iliakan Oct 7, 2020
6c6a7b5
Merge pull request #2163 from Anurag-Chauhan-289/master
iliakan Oct 7, 2020
176e5f8
Fix typos in 1.5.3
vsemozhetbyt Oct 8, 2020
bdb3def
Update article.md
paroche Oct 9, 2020
f880f1f
Make code example more realistic and safe
vsemozhetbyt Oct 9, 2020
b2b83b7
Correct outdated info
vsemozhetbyt Oct 9, 2020
54c8247
Fix link in 1.5.3
vsemozhetbyt Oct 7, 2020
edef0b4
minor fixes
iliakan Oct 9, 2020
858ad69
Fix some possible typos and omissions in 1.5.5
vsemozhetbyt Oct 9, 2020
dea1a2d
Add a note on some()/every() short circuit
vsemozhetbyt Oct 9, 2020
7b70f79
Mention arr.flat()/arr.flatMap() in 1.5.5
vsemozhetbyt Oct 9, 2020
2336288
typo
Taruna06 Oct 10, 2020
4ee44dc
Fix a typo in a 1.5.5 task solution
vsemozhetbyt Oct 10, 2020
3188466
Make a solution of 1.5.5 task more correct
vsemozhetbyt Oct 10, 2020
1273ae4
minor fixes
iliakan Oct 11, 2020
e7953a4
minor fixes
iliakan Oct 11, 2020
3fbbcd1
minor fixes
iliakan Oct 11, 2020
d3b38c2
minor fixes
iliakan Oct 11, 2020
35f47b9
Merge pull request #2172 from vsemozhetbyt/patch-1
iliakan Oct 11, 2020
8e613bc
Merge pull request #2185 from vsemozhetbyt/patch-9
iliakan Oct 11, 2020
3ba22f7
Merge pull request #2184 from vsemozhetbyt/patch-8
iliakan Oct 11, 2020
95016bc
Merge pull request #2183 from Taruna06/master
iliakan Oct 11, 2020
37f67e6
Merge pull request #2181 from vsemozhetbyt/patch-7
iliakan Oct 11, 2020
80efcda
Merge pull request #2180 from vsemozhetbyt/patch-6
iliakan Oct 11, 2020
60ec589
Merge pull request #2179 from vsemozhetbyt/patch-5
iliakan Oct 11, 2020
1a4849a
Merge pull request #2178 from vsemozhetbyt/patch-4
iliakan Oct 11, 2020
b6f3f5c
Merge pull request #2175 from vsemozhetbyt/patch-2
iliakan Oct 11, 2020
0599d07
Merge pull request #2177 from vsemozhetbyt/patch-3
iliakan Oct 11, 2020
75aed55
assign "it"
joaquinelio Oct 12, 2020
1431a25
Merge pull request #2188 from joaquinelio/patch-1
iliakan Oct 12, 2020
bb70310
Update 2-ui/4-forms-controls/1-form-elements/article.md
dorelljames Oct 12, 2020
040efef
Update task.md
dorelljames Oct 12, 2020
ac28995
Merge pull request #2194 from dorelljames/patch-4
iliakan Oct 12, 2020
552c0b7
Update article.md
dorelljames Oct 12, 2020
54df94d
Winnie Pooh case... : )
joaquinelio Oct 12, 2020
8ccc7f6
Merge pull request #2190 from dorelljames/patch-2
iliakan Oct 13, 2020
872cc6a
Merge pull request #2195 from joaquinelio/patch-1
iliakan Oct 13, 2020
6e61e8a
Fix: change ; => ,
leviding Oct 14, 2020
15767fc
Merge pull request #2198 from leviding/patch-23
lex111 Oct 14, 2020
9d49866
redundant "use case"
joaquinelio Oct 15, 2020
1fbc508
Merge pull request #2207 from joaquinelio/patch-2
iliakan Oct 16, 2020
10d1b1f
minor fixes
iliakan Oct 16, 2020
d6e8864
minor fixes
iliakan Oct 17, 2020
755148f
merging all conflicts
iliakan Oct 19, 2020
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
6 changes: 3 additions & 3 deletions 1-js/01-getting-started/1-intro/article.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# An Introduction to JavaScript

Let's see what's so special about JavaScript, what we can achieve with it, and which other technologies play well with it.
Let's see what's so special about JavaScript, what we can achieve with it, and what other technologies play well with it.

## What is JavaScript?

Expand Down Expand Up @@ -116,6 +116,6 @@ There are more. Of course, even if we use one of transpiled languages, we should

## Summary

- JavaScript was initially created as a browser-only language, but is now used in many other environments as well.
- Today, JavaScript has a unique position as the most widely-adopted browser language with full integration with HTML/CSS.
- JavaScript was initially created as a browser-only language, but it is now used in many other environments as well.
- Today, JavaScript has a unique position as the most widely-adopted browser language with full integration in HTML/CSS.
- There are many languages that get "transpiled" to JavaScript and provide certain features. It is recommended to take a look at them, at least briefly, after mastering JavaScript.
2 changes: 1 addition & 1 deletion 1-js/02-first-steps/18-javascript-specials/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ Assignments
: There is a simple assignment: `a = b` and combined ones like `a *= 2`.

Bitwise
: Bitwise operators work with 32-bit integers at the lowest, bit-level: see the [docs](mdn:/JavaScript/Reference/Operators/Bitwise_Operators) when they are needed.
: Bitwise operators work with 32-bit integers at the lowest, bit-level: see the [docs](mdn:/JavaScript/Guide/Expressions_and_Operators#Bitwise) when they are needed.

Conditional
: The only operator with three parameters: `cond ? resultA : resultB`. If `cond` is truthy, returns `resultA`, otherwise `resultB`.
Expand Down
4 changes: 2 additions & 2 deletions 1-js/04-object-basics/01-object/8-multiply-numeric/task.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ importance: 3

---

# Multiply numeric properties by 2
# Multiply numeric property values by 2

Create a function `multiplyNumeric(obj)` that multiplies all numeric properties of `obj` by `2`.
Create a function `multiplyNumeric(obj)` that multiplies all numeric property values of `obj` by `2`.

For instance:

Expand Down
41 changes: 27 additions & 14 deletions 1-js/04-object-basics/02-object-copy/article.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
# Object copying, references
# Object references and copying

One of the fundamental differences of objects vs primitives is that they are stored and copied "by reference".
One of the fundamental differences of objects versus primitives is that objects are stored and copied "by reference", as opposed to primitive values: strings, numbers, booleans, etc -- that are always copied "as a whole value".

Primitive values: strings, numbers, booleans -- are assigned/copied "as a whole value".
That's easy to understand if we look a bit "under a cover" of what happens when we copy a value.

For instance:
Let's start with a primitive, such as a string.

Here we put a copy of `message` into `phrase`:

```js
let message = "Hello!";
Expand All @@ -15,21 +17,31 @@ As a result we have two independent variables, each one is storing the string `"

![](variable-copy-value.svg)

Quite an obvious result, right?

Objects are not like that.

**A variable stores not the object itself, but its "address in memory", in other words "a reference" to it.**
**A variable assigned to an object stores not the object itself, but its "address in memory", in other words "a reference" to it.**

Here's the picture for the object:
Let's look at an example of such variable:

```js
let user = {
name: "John"
};
```

And here's how it's actually stored in memory:

![](variable-contains-reference.svg)

Here, the object is stored somewhere in memory. And the variable `user` has a "reference" to it.
The object is stored somewhere in memory (at the right of the picture), while the `user` variable (at the left) has a "reference" to it.

We may think of an object variable, such as `user`, as of a sheet of paper with the address.

When we perform actions with the object, e.g. take a property `user.name`, JavaScript engine looks into that address and performs the operation on the actual object.

Now here's why it's important.

**When an object variable is copied -- the reference is copied, the object is not duplicated.**

Expand All @@ -45,6 +57,8 @@ Now we have two variables, each one with the reference to the same object:

![](variable-copy-reference.svg)

As you can see, there's still one object, now with two variables that reference it.

We can use any variable to access the object and modify its contents:

```js run
Expand All @@ -59,15 +73,14 @@ admin.name = 'Pete'; // changed by the "admin" reference
alert(*!*user.name*/!*); // 'Pete', changes are seen from the "user" reference
```

The example above demonstrates that there is only one object. As if we had a cabinet with two keys and used one of them (`admin`) to get into it. Then, if we later use another key (`user`) we can see changes.

## Comparison by reference
It's just as if we had a cabinet with two keys and used one of them (`admin`) to get into it. Then, if we later use another key (`user`) we can see changes.

The equality `==` and strict equality `===` operators for objects work exactly the same.
## Comparison by reference

**Two objects are equal only if they are the same object.**
Two objects are equal only if they are the same object.

Here two variables reference the same object, thus they are equal:
For instance, here `a` and `b` reference the same object, thus they are equal:

```js run
let a = {};
Expand All @@ -77,7 +90,7 @@ alert( a == b ); // true, both variables reference the same object
alert( a === b ); // true
```

And here two independent objects are not equal, even though both are empty:
And here two independent objects are not equal, even though they look alike (both are empty):

```js run
let a = {};
Expand All @@ -86,7 +99,7 @@ let b = {}; // two independent objects
alert( a == b ); // false
```

For comparisons like `obj1 > obj2` or for a comparison against a primitive `obj == 5`, objects are converted to primitives. We'll study how object conversions work very soon, but to tell the truth, such comparisons occur very rarely, usually as a result of a coding mistake.
For comparisons like `obj1 > obj2` or for a comparison against a primitive `obj == 5`, objects are converted to primitives. We'll study how object conversions work very soon, but to tell the truth, such comparisons are needed very rarely, usually they appear as a result of a programming mistake.

## Cloning and merging, Object.assign

Expand Down
12 changes: 7 additions & 5 deletions 1-js/04-object-basics/04-object-methods/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ user.sayHi = function() {
user.sayHi(); // Hello!
```

Here we've just used a Function Expression to create the function and assign it to the property `user.sayHi` of the object.
Here we've just used a Function Expression to create a function and assign it to the property `user.sayHi` of the object.

Then we can call it. The user can now speak!
Then we can call it as `user.sayHi()`. The user can now speak!

A function that is the property of an object is called its *method*.
A function that is a property of an object is called its *method*.

So, here we've got a method `sayHi` of the object `user`.

Expand Down Expand Up @@ -160,14 +160,16 @@ let user = {
let admin = user;
user = null; // overwrite to make things obvious

admin.sayHi(); // Whoops! inside sayHi(), the old name is used! error!
*!*
admin.sayHi(); // TypeError: Cannot read property 'name' of null
*/!*
```

If we used `this.name` instead of `user.name` inside the `alert`, then the code would work.

## "this" is not bound

In JavaScript, keyword `this` behaves unlike most other programming languages. It can be used in any function.
In JavaScript, keyword `this` behaves unlike most other programming languages. It can be used in any function, even if it's not a method of an object.

There's no syntax error in the following example:

Expand Down
96 changes: 67 additions & 29 deletions 1-js/04-object-basics/07-optional-chaining/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,53 +3,87 @@

[recent browser="new"]

The optional chaining `?.` is an error-proof way to access nested object properties, even if an intermediate property doesn't exist.
The optional chaining `?.` is a safe way to access nested object properties, even if an intermediate property doesn't exist.

## The problem
## The "non-existing property" problem

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.

For example, some of our users have addresses, but few did not provide them. Then we can't safely read `user.address.street`:
As an example, 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.

In such case, when we attempt to get `user.address.street`, we may get an error:

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

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

Or, in the web development, we'd like to get an information about an element on the page, but it may not exist:
That's the expected result, JavaScript works like this. As `user.address` is `undefined`, the attempt to get `user.address.street` fails with an error. Although, 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 the information about an element on the page. The element is returned by `document.querySelector('.elem')`, and the catch is again - that it sometimes doesn't exist:

```js run
// Error if the result of querySelector(...) is null
let html = document.querySelector('.my-element').innerHTML;
// the result of the call document.querySelector('.elem') may be an object or null
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, we may want to avoid the error in such case.

For example:
How can we do this?

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

```js
let user = {};

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

...But that's quite inelegant. As you can see, the `user.address` is duplicated in the code. For more deeply nested properties, that becomes a problem.

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 looks awful.

Before the optional chaining `?.` was added to the language, people used the `&&` operator for such cases:

```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, 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, the property names are still duplicated in the code. E.g. in the code above, `user.address` appears three times.

And now, finally, the optional chaining comes to the rescue!

## Optional chaining

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

**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`:
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 @@ -61,16 +95,16 @@ 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`.
In the example above, `user?.address.street` 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.

```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 know about it and fix it. Otherwise, coding errors can be silenced where not appropriate, and become more difficult to debug.
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.
```

````warn header="The variable before `?.` must be declared"
Expand All @@ -80,25 +114,27 @@ If there's no variable `user` at all, then `user?.anything` triggers an error:
// ReferenceError: user is not defined
user?.address;
```
There must be `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

As it was said before, the `?.` immediately stops ("short-circuits") the evaluation if the left part doesn't exist.

So, if there are any further function calls or side effects, they don't occur:
So, if there are any further function calls or side effects, they don't occur.

For instance:

```js run
let user = null;
let x = 0;

user?.sayHi(x++); // nothing happens
user?.sayHi(x++); // no "sayHi", so the execution doesn't reach x++

alert(x); // 0, value not incremented
```

## Other cases: ?.(), ?.[]
## Other variants: ?.(), ?.[]

The optional chaining `?.` is not an operator, but a special syntax construct, that also works with functions and square brackets.

Expand All @@ -121,9 +157,9 @@ user2.admin?.();
*/!*
```

Here, in both lines we first use the dot `.` to get `admin` property, because the user object must exist, so it's safe read from it.
Here, in both lines we first use the dot (`user1.admin`) to get `admin` property, because the user object must exist, so it's safe read from it.

Then `?.()` checks the left part: if the admin function exists, then it runs (for `user1`). Otherwise (for `user2`) the evaluation stops without errors.
Then `?.()` checks the left part: if the admin function exists, then it runs (that's so for `user1`). Otherwise (for `user2`) the evaluation stops without errors.

The `?.[]` syntax also works, if we'd like to use brackets `[]` to access properties instead of dot `.`. Similar to previous cases, it allows to safely read a property from an object that may not exist.

Expand All @@ -148,28 +184,30 @@ Also we can use `?.` with `delete`:
delete user?.name; // delete user.name if user exists
```

```warn header="We can use `?.` for safe reading and deleting, but not writing"
The optional chaining `?.` has no use at the left side of an assignment:
````warn header="We can use `?.` for safe reading and deleting, but not writing"
The optional chaining `?.` has no use at the left side of an assignment.

For example:
```js run
// the idea of the code below is to write user.name, if user exists
let user = null;

user?.name = "John"; // Error, doesn't work
// because it evaluates to undefined = "John"
```

It's just not that smart.
````

## Summary

The `?.` syntax has three forms:
The optional chaining `?.` syntax has three forms:

1. `obj?.prop` -- returns `obj.prop` if `obj` exists, otherwise `undefined`.
2. `obj?.[prop]` -- returns `obj[prop]` if `obj` exists, otherwise `undefined`.
3. `obj?.method()` -- calls `obj.method()` if `obj` exists, otherwise returns `undefined`.
3. `obj.method?.()` -- calls `obj.method()` if `obj.method` exists, otherwise returns `undefined`.

As we can see, all of them are straightforward and simple to use. The `?.` checks the left part for `null/undefined` and allows the evaluation to proceed if it's not so.

A chain of `?.` allows to safely access nested properties.

Still, we should apply `?.` carefully, only where it's ok that the left part doesn't to exist.

So that it won't hide programming errors from us, if they occur.
Still, we should apply `?.` carefully, only where it's acceptable that the left part doesn't to exist. So that it won't hide programming errors from us, if they occur.
Loading