This commit is contained in:
Ilya Kantor 2016-07-02 18:11:36 +03:00
parent 0ef7054ccf
commit 3dc3018fd1
98 changed files with 1182 additions and 198 deletions

View file

@ -233,6 +233,37 @@ Most of time, the dot is used to access object properties, but when we need a co
Objects in JavaScript are very powerful. Here we've just scratched the surface of the topic that is really huge. We'll be closely working with objects and learning more about them in further parts of the tutorial. Objects in JavaScript are very powerful. Here we've just scratched the surface of the topic that is really huge. We'll be closely working with objects and learning more about them in further parts of the tutorial.
## Arrays
As weve just seen, objects in Javascript store arbitrary keyed values.
But quite often we find that we need an *ordered collection*, where we have a 1st, a 2nd, a 3rd element and so on. For example, we need that to store a list of something: users, goods, HTML elements etc. It not convenient to use an object here, because it provides no methods to manage the order of elements. We cant easily access the n-th element in an object. Also we cant insert a new property “before” the existing ones, and so on. Objects are just not meant for such use.
There exists a special data structure named "an array", to store ordered collections.
An array is created using square brackets:
```js
let empty = []; // empty array
let fruits = ["Apple", "Orange", "Plum"]; // array with 3 values
```
Individual items are accessed using brackets `[]`, the first item has zero index:
```js run
let fruits = ["Apple", "Orange", "Plum"]; // array with 3 values
alert( fruits[0] ); // Apple
alert( fruits[1] ); // Orange
alert( fruits[2] ); // Plum
// how many elements (last index + 1)
alert( fruits.length ); // 3
```
Please note that arrays do not form a separate language type. They are based on objects, but have many features of their own including methods to add, remove, extract elements from the array, to sort arrays and more. We'll cover them in the chapter <info:array>.
## Symbol type ## Symbol type
The `symbol` type is used in conjunction with objects. Probably we won't need them any time soon, but it's the 7th and the last type of the language, so we must mention it here for the sake of completeness. The `symbol` type is used in conjunction with objects. Probably we won't need them any time soon, but it's the 7th and the last type of the language, so we must mention it here for the sake of completeness.
@ -319,140 +350,24 @@ typeof Symbol("id") // "symbol"
typeof {} // "object" typeof {} // "object"
*!* *!*
typeof null // "object" (1) typeof [] // "object" (1)
*/!* */!*
*!* *!*
typeof alert // "function" (2) typeof null // "object" (2)
*/!* */!*
```
Please note the last two lines, because `typeof` behaves a little bit strange there.
1. The result of `typeof null` equals to `"object"`. That is an officially recognized error in `typeof` implementation, kept for compatibility. Of course, `null` is not an object. It is a special value with a separate type of its own. So `typeof` just says it wrong here.
2. The result of `typeof alert` is `"function"`, because `alert` is a function of the language. We'll study functions in the near future and see that actually functions belong to the object type. But `typeof` treats them differently. That's very convenient in practice.
## Type conversions
A variable in JavaScript can contain any data. A variable can at one moment be a string and later recieve a numeric value:
```js
// no error
let message = "hello";
message = 123456;
```
...But sometimes we need to convert a value from one type to another. For example, `alert` automatically converts any value to a string, to show it. Or, so to say, an `if(value)` test converts the `value` to boolean type to see if it's `true` or `false`.
There are also cases when we need to explicitly convert between types to ensure that we store the right data the right way or to use special features of a certain type.
There are many type conversions in JavaScript, fully listed in [the specification](http://www.ecma-international.org/ecma-262/6.0/index.html#sec-type-conversion).
Three conversions that happen the most often:
1. String conversion.
2. Numeric conversion.
3. Boolean conversion.
Let's see how they work and when they happen.
### String conversion
The string conversion happens when we need a string form of a value.
For example, `alert` does it:
```js run
let a = true;
alert( a ); // "true"
```
We can also use a call `String(value)` function for that:
```js run
let a = true;
alert(typeof a); // boolean
*!* *!*
a = String(a); // now: a = "true" typeof alert // "function" (3)
alert(typeof a); // string
*/!* */!*
``` ```
The string conversion is obvious. A `false` becomes `"false"`, `null` becomes `"null"` etc. Please note the last lines.
### Numeric conversion 1. The array is not a type of its own, but a subtype of object, that's why `typeof []` is `"object"`.
2. The result of `typeof null` equals to `"object"`. That's wrong. It is an officially recognized error in `typeof` implementation, kept for compatibility. Of course, `null` is not an object. It is a special value with a separate type of its own.
3. The result of `typeof alert` is `"function"`, because `alert` is a function of the language. We'll study functions in the near future and see that actually functions belong to the object type. But `typeof` treats them differently. That's very convenient in practice.
Numeric conversion happens in mathematical functions and expressions automatically.
For example, a mathematical operation like division '/' can be applied to non-numbers:
```js run
alert( "6" / "2" ); // 3, strings become numbers
```
We can use a `Number(value)` function to convert any `value` to a number:
```js run
let str = "123";
alert(typeof str); // string
let n = Number(str); // becomes a number 123
alert(typeof n); // number
```
The conversion is usually applied when we have a numeric value coming from a text form field or another string-based source.
If the string is not a number, the result of such conversion is `NaN`, for instance:
```js run
let age = Number("an arbitrary string instead of a number");
alert(age); // NaN, conversion failed
```
The numeric conversion rules:
| Value | Becomes... |
|-------|-------------|
|`undefined`|`NaN`|
|`null`|`0`|
|<code>true&nbsp;/&nbsp;false</code> | `1 / 0` |
| `string` | Whitespaces from the start and the end are removed. Then, if the remaining string is empty, the result is `0`, otherwise - the number is "read" from the string. An error gives `NaN`. |
Examples:
```js run
alert( Number(" 123 ") ); // 123
alert( Number("123z") ); // NaN (error reading a number at "z")
alert( Number(true) ); // 1
alert( Number(false) ); // 0
```
Please note that `null` and `undefined` behave differently here: `null` becomes a zero, while `undefined` becomes `NaN`.
### Boolean conversion
Boolean conversion is the simplest one.
It happens in logical operations (later we'll meet `if` tests and other kinds), but also can be performed manually with the call of `Boolean(value)`.
The conversion rule:
- Values that are intuitively "empty", like `0`, an empty string, `null`, `undefined` and `NaN` become `false`.
- Other values become `true`.
````warn header="Please note: a string `\"0\"` is `true`"
Some languages (namely PHP) treat `"0"` as `false`. But in JavaScript a non-empty string is always `false`, no matter what is in it.
```js run
alert( Boolean("0") ); // true
alert( Boolean(" ") ); // any non-empty string, even whitespaces are true
```
````
## Summary ## Summary
@ -464,9 +379,8 @@ There are 7 basic types in JavaScript.
- `boolean` for `true`/`false`, can convert into it using `Boolean(value)`. - `boolean` for `true`/`false`, can convert into it using `Boolean(value)`.
- `null` for unknown values. - `null` for unknown values.
- `undefined` for unassigned values. - `undefined` for unassigned values.
- `object` for complex data structures.
- `symbol` for unique identifiers. - `symbol` for unique identifiers.
- `object` for more complex data structures (there exist many, we saw arrays).
The `typeof` operator allows to see which type is stored in the variable, but note that it mistakingly returns `"object"` for `null`. The `typeof` operator allows to see which type is stored in the variable.
Now let's study operators and other language constructs that actually form our code.

View file

@ -0,0 +1,151 @@
# Type conversions
A variable in JavaScript can contain any data. A variable can at one moment be a string and later recieve a numeric value:
```js
// no error
let message = "hello";
message = 123456;
```
...But sometimes we need to convert a value from one type to another. For example, `alert` automatically converts any value to a string, to show it. Or, so to say, an `if(value)` test converts the `value` to boolean type to see if it's `true` or `false`.
There are also cases when we need to explicitly convert between types to ensure that we store the right data the right way or to use special features of a certain type.
There are many type conversions in JavaScript, fully listed in [the specification](http://www.ecma-international.org/ecma-262/6.0/index.html#sec-type-conversion).
Three conversions that happen the most often:
1. String conversion.
2. Numeric conversion.
3. Boolean conversion.
Let's see how they work and when they happen.
## String conversion
The string conversion happens when we need a string form of a value.
For example, `alert` does it:
```js run
let a = true;
alert( a ); // "true"
```
We can also use a call `String(value)` function for that:
```js run
let a = true;
alert(typeof a); // boolean
*!*
a = String(a); // now: a = "true"
alert(typeof a); // string
*/!*
```
The string conversion is obvious. A `false` becomes `"false"`, `null` becomes `"null"` etc.
## Numeric conversion
Numeric conversion happens in mathematical functions and expressions automatically.
For example, a mathematical operation like division '/' can be applied to non-numbers:
```js run
alert( "6" / "2" ); // 3, strings become numbers
```
We can use a `Number(value)` function to convert any `value` to a number:
```js run
let str = "123";
alert(typeof str); // string
let n = Number(str); // becomes a number 123
alert(typeof n); // number
```
The conversion is usually applied when we have a numeric value coming from a text form field or another string-based source.
If the string is not a number, the result of such conversion is `NaN`, for instance:
```js run
let age = Number("an arbitrary string instead of a number");
alert(age); // NaN, conversion failed
```
The numeric conversion rules:
| Value | Becomes... |
|-------|-------------|
|`undefined`|`NaN`|
|`null`|`0`|
|<code>true&nbsp;and&nbsp;false</code> | `1` and `0` |
| `string` | Whitespaces from the start and the end are removed. Then, if the remaining string is empty, the result is `0`, otherwise -- the number is "read" from the string. An error gives `NaN`. |
Examples:
```js run
alert( Number(" 123 ") ); // 123
alert( Number("123z") ); // NaN (error reading a number at "z")
alert( Number(true) ); // 1
alert( Number(false) ); // 0
```
Please note that `null` and `undefined` behave differently here: `null` becomes a zero, while `undefined` becomes `NaN`.
## Boolean conversion
Boolean conversion is the simplest one.
It happens in logical operations (later we'll meet `if` tests and other kinds), but also can be performed manually with the call of `Boolean(value)`.
The conversion rule:
- Values that are intuitively "empty", like `0`, an empty string, `null`, `undefined` and `NaN` become `false`.
- Other values become `true`.
````warn header="Please note: the string with zero `\"0\"` is `true`"
Some languages (namely PHP) treat `"0"` as `false`. But in JavaScript a non-empty string is always `true`.
```js run
alert( Boolean("0") ); // true
alert( Boolean(" ") ); // any non-empty string, even whitespaces are true
```
````
## Summary
There exist three most widely used type conversions: to string, to number and to boolean.
The conversion to string is usully obvious.
To number follows the rules:
| Value | Becomes... |
|-------|-------------|
|`undefined`|`NaN`|
|`null`|`0`|
|<code>true&nbsp;/&nbsp;false</code> | `1 / 0` |
| `string` | The string is read "as is", whitespaces from both sides are ignored. An empty string is `0`. An error gives `NaN`. |
To boolean:
| Value | Becomes... |
|-------|-------------|
|`0`, `null`, `undefined`, `NaN`, `""` |`false`|
|any other value| `true` |
Objects provide advanced means to specify how they are converted to string and number, we'll see them later, when study objects in-depth.
Most of these rules are easy to understand and memorize. The notable exceptions where people usually make mistakes are:
- `undefined` is `NaN` as a number.
- `"0"` is true as a boolean.
In the next chapter we'll study operators. You will find enough tasks for type conversion there.

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

View file

@ -0,0 +1,13 @@
```js run
let user = {
name: "John",
years: 30
};
let {name, years: age, isAdmin = false} = user;
alert( name ); // John
alert( age ); // 30
alert( isAdmin ); // false
```

View file

@ -0,0 +1,33 @@
importance: 5
---
# Destructuring assignment
We have an object:
```js
let user = {
name: "John",
years: 30
};
```
Write the destructuring assignment that reads:
- `name` property into the variable `name`.
- `years` property into the variable `age`.
- `isAdmin` property into the variable `isAdmin` (false if absent)
The values after the assignment should be:
```js
let user = { name: "John", years: 30 };
// your code to the left side:
// ... = user
alert( name ); // John
alert( age ); // 30
alert( isAdmin ); // false
```

View file

@ -0,0 +1,273 @@
# Destructuring
Destructuring assignment is a special syntax for assignments that allows to immediately split an array or a complex object into variables.
[cut]
## Array destructuring
An example of how the array is destructured into variables:
```js
// we have an array with the name and surname
let arr = ["Ilya", "Kantor"]
*!*
// destructuring assignment
let [firstName, surname] = arr;
*/!*
alert(firstName); // Ilya
alert(surname); // Kantor
```
The destructuring assignment puts the first value of the array into the variable `firstName` and the second one into `surname`. Any other array elements (if exist) are ignored.
Note that the array itself is not modified in the process.
````smart header="Ignore first elements"
Unwanted elements of the array can also be thrown away via an extra comma:
```js run
*!*
// first and second elements are not needed
let [, , title] = ["Julius", "Caesar", "Consul", "of the Roman Republic"];
*/!*
alert( title ); // Imperator
```
In the code above, the first and second elements of the array are skipped, the third one is assigned to `title`, and the rest is also skipped.
````
### The rest operator
If we want to get all following values of the array, but are not sure of their number -- we can add one more parameter that gets "the rest" using the rest operator `"..."` (three dots):
```js run
*!*
let [name1, name2, ...rest] = ["Julius", "Caesar", "Consul", "of the Roman Republic"];
*/!*
alert(name1); // Julius
alert(name2); // Caesar
alert(rest[0]); // Consul
alert(rest[1]); // of the Roman Republic
```
The value of `rest` is the array of the remaining array elements. We can use any other variable name in place of `rest`, the operator is three dots. It must be the last element of the destructuring assignment.
### The default values
The there are less values in the array than variables in the assignment -- there will be no error, absent values are considered undefined:
```js run
*!*
let [firstName, surname] = [];
*/!*
alert(firstName); // undefined
```
If we want a "default" value to take place of the absent one, we can provide it using `=`:
```js run
*!*
// default values
let [name="Guest", surname="Anonymous"] = [];
*/!*
alert(name); // Guest
alert(surname); // Anonymous
```
Note that default values can be more complex expressions. They are evaluated only if the value is not provided.
## Object destructuring
The destructuring assignment also works with objects.
The basic syntax is:
```js
let {var1, var2} = {var1:…, var2…}
```
We have an existing object at the right side, that we want to split into variables. To the left side is the list of variables for corresponding properties.
For instance:
```js run
let options = {
title: "Menu",
width: 100,
height: 200
};
*!*
let {title, width, height} = options;
*/!*
alert(title); // Menu
alert(width); // 100
alert(height); // 200
```
Properties `options.title`, `options.width` and `options.height` are automcatically assigned to the corresponding variables. The order of propertiies does not matter, that works too:
```js
// let {title, width, height}
let {height, width, title} = { title: "Menu", height: 200, width: 100 }
```
If we want to assign a property to a variable with another name, for instance, `options.width` to go into the variable named `w`, then it should be set after a colon:
```js run
let options = {
title: "Menu",
width: 100,
height: 200
};
*!*
// { source property: target variable }
let {width: w, height: h, title} = options;
*/!*
alert(title); // Menu
alert(w); // 100
alert(h); // 200
```
The colon shows "what : goes where". In the example above the property `width` goes to `w`, property `height` goes to `h`, and `title` is assigned to the same name.
If some properties are absent, we can set default values using `=`, like this:
```js run
let options = {
title: "Menu"
};
*!*
let {width=100, height=200, title} = options;
*/!*
alert(title); // Меню
alert(width); // 100
alert(height); // 200
```
We also can combine both the colon and equality:
```js run
let options = {
title: "Menu"
};
*!*
let {width:w=100, height:h=200, title} = options;
*/!*
alert(title); // Menu
alert(w); // 100
alert(h); // 200
```
What if the object has more properties than we have variables? Can we assign the "rest" somewhere, like we do with arrays?
Unfortunately, the current specification does not support that feature. There is a proposal, but it's not in the standard yet.
````smart header="Destructuring without `let`"
In the examples above variables were declared right before the assignment: `let {…} = {…}`. Of course, we could use the existing variables too. But there's a catch.
This won't work:
```js run
let title, width, height;
// error in this line
{title, width, height} = {title: "Menu", width: 200, height: 100};
```
The problem is that Javascript treats `{...}` in the main code flow (not inside another expression) as a code block. Such standalone code blocks are rarely used, but in theory can group statements, like this:
```js run
{
// a code block
let message = "Hello";
// ...
alert( message );
}
```
To show Javascript that it's not a code block, we can wrap the whole assignment in brackets `(...)`:
```js run
let title, width, height;
// okay now
*!*(*/!*{title, width, height} = {title: "Menu", width: 200, height: 100}*!*)*/!*;
alert( title ); // Menu
```
````
## Nested destructuring
If an object or an array contain other objects and arrays, we can go deeper in the destructuring assignment.
In the code below `options` has another object in the property `size` and an array in the property `items`. The destructuring assignment has the same structure:
```js run
let options = {
size: {
width: 100,
height: 200
},
items: ["Cake", "Donut"]
}
// destructuring assignment on multiple lines for clarity
let {
size: { // put size here
width,
height
},
items: [item1, item2], // assign items here
title = "Menu" // an extra property (default value is used)
} = options;
alert(title); // Menu
alert(width); // 100
alert(height); // 200
alert(item1); // Cake
alert(item2); // Donut
```
As we can see, the whole `options` object is correctly assigned to variables.
The left part of the destructuring assignment can combine and nest things as needed.
## Summary
- Destructuring assignment allows to instantly map an object or array into many variables.
- The object syntax:
```js
let {prop : varName = default, ...} = object
```
This means that property `prop` should go into the variable `varName` and, if no such property exists, then `default` value should be used.
- The array syntax:
```js
let [item1 = default, item2, ...rest] = array
```
The first item goes to `item1`, the second goes into `item2`, all the rest makes the array `rest`.
- For more complex cases, the left side must have the same structure as the right one.

View file

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 92 KiB

After

Width:  |  Height:  |  Size: 92 KiB

Before After
Before After

View file

@ -139,7 +139,7 @@ for (*!*let*/!* i = 0; i < 3; i++) {
The variable will be visible only inside the loop. The variable will be visible only inside the loop.
```` ````
## Skipping parts ### Skipping parts
Any part of the `for` can be skipped. Any part of the `for` can be skipped.
@ -178,15 +178,9 @@ for (;;) {
Please note that the semicolons `;` must present, otherwise it would be a syntax error. Please note that the semicolons `;` must present, otherwise it would be a syntax error.
```smart header="`for..in` and `for..of`"
There are special constructs: `for..in` and `for..of` for more advanced iterations over objects.
We'll get to them later, in chapters about objects.
```
## Breaking the loop ## Breaking the loop
Normally the loop exists when the condition becomes falsy. Normally the loop exits when the condition becomes falsy.
But we can force the exit at any moment. There's a special `break` directive for that. But we can force the exit at any moment. There's a special `break` directive for that.
@ -211,7 +205,7 @@ alert( 'Sum: ' + sum );
The `break` directive is activated in the line `(*)` if the user enters an empty line or cancels the input. It stops the loop immediately, passing the control to the first line after it's loop. Namely, `alert`. The `break` directive is activated in the line `(*)` if the user enters an empty line or cancels the input. It stops the loop immediately, passing the control to the first line after it's loop. Namely, `alert`.
The composition: "infinite loop + break as needed" is a great thing for situations when the condition must be checked not in beginning/end of the loop, but in the middle. Or even in several places of the body. The combination: "infinite loop + `break` as needed" is great for situations when the condition must be checked not in beginning/end of the loop, but in the middle. Or even in several places of the body.
## Continue to the next iteration [#continue] ## Continue to the next iteration [#continue]
@ -246,7 +240,7 @@ for (let i = 0; i < 10; i++) {
} }
``` ```
From the technical point of view it's identical. Surely, we can just wrap the code in the `if` block instead of `continue`. From the technical point of view it's identical to the example above. Surely, we can just wrap the code in the `if` block instead of `continue`.
But as a side-effect we got one more figure brackets nesting level. If the code inside `if` is longer than a few lines, that may decrease the overall readability. But as a side-effect we got one more figure brackets nesting level. If the code inside `if` is longer than a few lines, that may decrease the overall readability.
```` ````
@ -254,7 +248,7 @@ But as a side-effect we got one more figure brackets nesting level. If the code
````warn header="No `break/continue` to the right side of '?'" ````warn header="No `break/continue` to the right side of '?'"
Please note that syntax constructs that are not expressions cannot be used in `'?'`. In particular, directives `break/continue` are disallowed there. Please note that syntax constructs that are not expressions cannot be used in `'?'`. In particular, directives `break/continue` are disallowed there.
For example, if one we took this code: For example, if one we take this code:
```js ```js
if (i > 5) { if (i > 5) {
@ -264,14 +258,14 @@ if (i > 5) {
} }
``` ```
...And rewrote it using a question mark: ...And rewrite it using a question mark:
```js no-beautify ```js no-beautify
(i > 5) ? alert(i) : *!*continue*/!*; // continue not allowed here (i > 5) ? alert(i) : *!*continue*/!*; // continue not allowed here
``` ```
...Then it won't work. The code like this will give a syntax error: ...Then it stops working. The code like this will give a syntax error:
That's just another reason not to use a question mark operator `'?'` instead of `if`. That's just another reason not to use a question mark operator `'?'` instead of `if`.
@ -290,7 +284,7 @@ for (let i = 0; i < 3; i++) {
let input = prompt(`Value at coords (${i},${j})`, ''); let input = prompt(`Value at coords (${i},${j})`, '');
// what if I want to exit from here? // what if I want to exit from here to Done (below)?
} }
} }
@ -355,15 +349,97 @@ label: for(...)
The call to a `break/continue` is only possible from inside the loop, and the label must be somewhere upwards from the directive. The call to a `break/continue` is only possible from inside the loop, and the label must be somewhere upwards from the directive.
```` ````
## The "for..in" loop
To walk over all keys of an object, there exists a special form of the loop: `for..in`. This is a completely different thing from the `for(;;)` construct that we've studied before.
The syntax:
```js
for(key in object) {
// executes the body for each key among object properties
}
```
For instance, let's output all properties of `user`:
```js run
let user = {
name: "John",
age: 30,
isAdmin: true
};
for(let key in user) {
// keys
alert( key ); // name, age, 30
// values for the keys
alert( user[key] ); // John, 30, true
}
```
Note that all "for" constructs allow to declare the looping variable inside the loop, like `let key` here. The name "key" for the variable is not mandatory, we could use any variable name here, usually "key" or "prop" names are used for such iterations.
## The "for..of" loop
And the third (the last one) kind of the `for` loop. Again it has a totally different meaning from what we've seen before.
This form iterates over arrays.
Actually, we can do it with the `for(;;)` loop:
```js run
let fruits = ["Apple", "Orange", "Plum"];
// iterates over all elements:
// i is the number of the current element
// fruits[i] is the value of the current element
for(let i = 0; i < fruits.length; i++) {
alert( fruits[i] ); // Apple, then Orange, then Plum
}
```
The "generic" `for(;;)` loop works well even in most outdated browsers.
The `for..of` syntax is shorter:
```js run
let fruits = ["Apple", "Orange", "Plum"];
// iterates over all elements:
// fruit is the value of the current element
for(let fruit of fruits) {
alert( fruit );
}
```
The `for..of` doesn't give access to the number of the current element, just its value, but in most cases that's enough.
````smart header="Iterables"
Later we'll learn the concept of *iterable* objects in Javascript. An iterable object must implement special functionality that allows to use `for..of` on it.
There are many iterable objects. For instance, a string is iterable, `for..of` will list characters in the example:
```js run
for(let char of "test") {
alert( char ); t, then e, then s, then t
}
```
````
## Summary ## Summary
There are 3 types of loops in JavaScript: There are 5 types of loops in JavaScript:
- `while` -- the condition is checked before each iteration. - `while` -- the condition is checked before each iteration.
- `do..while` -- the condition is checked after each iteration. - `do..while` -- the condition is checked after each iteration.
- `for` -- the condition is checked before each iteration, additional settings available. - `for(;;)` -- the condition is checked before each iteration, additional settings available.
- `for(key in obj)` -- to iterate over object properties.
- `for(item of array)` -- to iterate over array items.
To make in "infinite" loop, usually the `while(true)` construct is used. Such a loop, just like any other, can be stopped with the `break` directive. To make an "infinite" loop, usually the `while(true)` construct is used. Such a loop, just like any other, can be stopped with the `break` directive.
If we don't want to do anything more on this iteration and would like to forward on to the next one -- the `continue` directive does it. If we don't want to do anything more on this iteration and would like to forward on to the next one -- the `continue` directive does it.

View file

@ -176,61 +176,6 @@ showMessage(from, "Hello"); // *Ann*: Hello
alert( from ); // Ann alert( from ); // Ann
``` ```
### Default values
A function can be called with any number of arguments. If a parameter is not provided, but listed in the declaration, then its value becomes `undefined`.
For instance, the aforementioned function `showMessage(from, text)` can be called with a single argument:
```js
showMessage("Ann");
```
That's not an error. Such call would output `"Ann: undefined"`, because `text === undefined`.
If we want to track when the function is called with a single argument and use a "default" value in this case, then we can check if `text` is defined, like here:
```js run
function showMessage(from, text) {
*!*
if (text === undefined) {
text = 'no text given';
}
*/!*
alert( from + ": " + text );
}
showMessage("Ann", "Hello!"); // Ann: Hello!
*!*
showMessage("Ann"); // Ann: no text given
*/!*
```
There are also other ways to supply "default values" for missing arguments:
- Use operator `||`:
```js
function showMessage(from, text) {
text = text || 'no text given';
...
}
```
This way is shorter, but the argument is considered missing also if it's falsy, like an empty line, `0` or `null`.
- ES-2015 introduced a neater syntax for default values:
```js run
function showMessage(from, *!*text = 'no text given'*/!*) {
alert( from + ": " + text );
}
showMessage("Ann"); // Ann: no text given
```
Here `'no text given'` is a string, but it can be any other value or expression, which is only evaluated and assigned if the parameter is missing.
## Returning a value ## Returning a value
A function can return a value back into the calling code as the result. A function can return a value back into the calling code as the result.

View file

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Before After
Before After

View file

@ -0,0 +1,555 @@
# Function parameters
The syntax of function parameters is very versatile.
It allows:
- To specify values if the parameter if missing.
- To gather parameters into an array and deal with it instead of variables.
- To destructurize the object into parameters.
- And more.
All these features aim to help us in writing good-looking and concise code.
## Default values
A function can be called with any number of arguments. If a parameter is not provided, but listed in the declaration, then its value becomes `undefined`.
For instance, the aforementioned function `showMessage(from, text)` can be called with a single argument:
```js
showMessage("Ann");
```
That's not an error. Such call would output `"Ann: undefined"`, because `text === undefined`.
If we want to track when the function is called with a single argument and use a "default" value in this case, then we can check if `text` is defined, like here:
```js run
function showMessage(from, text) {
*!*
if (text === undefined) {
text = 'no text given';
}
*/!*
alert( from + ": " + text );
}
showMessage("Ann", "Hello!"); // Ann: Hello!
*!*
showMessage("Ann"); // Ann: no text given
*/!*
```
There are also other ways to supply "default values" for missing arguments:
- Use operator `||`:
```js
function showMessage(from, text) {
text = text || 'no text given';
...
}
```
This way is shorter, but the argument is considered missing even if it exists, but is falsy, like an empty line, `0` or `null`.
- Specify the default value after `=`:
```js run
function showMessage(from, *!*text = 'no text given'*/!*) {
alert( from + ": " + text );
}
showMessage("Ann"); // Ann: no text given
```
Here `'no text given'` is a string, but it can be a more complex expression, which is only evaluated and assigned if the parameter is missing. So, this is also possible:
```js run
function showMessage(from, text = anotherFunction()) {
// anotherFunction() is executed if no text given
}
```
## Arbitrary number of arguments
To support any number of arguments, we can use the rest operator `...`, similar to [destructuring](info:destructuring):
```js run
function sumAll(...args) {
let sum = 0;
for(let arg of args) sum += arg;
return sum;
}
alert( sumAll(1) ); // 1
alert( sumAll(1, 2) ); // 3
alert( sumAll(1, 2, 3) ); // 6
```
We also can put few first arguments into variables and gather only the rest:
```js run
function showName(firstName, lastName, ...rest) {
alert( firstName + ' ' + lastName ); // Julius Caesar
// the rest = ["Consul", "of the Roman Republic"]
alert( rest[0] ); // Consul
alert( rest[1] ); // of the Roman Republic
}
showName("Julius", "Caesar", "Consul", "of the Roman Republic");
```
````warn header="The rest operator … must be at the end"
The rest operator `…` gathers all remaining arguments, so the following has no sense:
```js
function f(arg1, ...rest, arg2) { // arg2 after ...rest ?!
// error
}
```
The `...rest` must always be the last.
````
````smart header="The `arguments` variable"
In old times, there were no rest operator. But there was a special variable named `arguments` that contained all arguments by their index. It is still supported and can be used like this:
```js run
function showName() {
alert( arguments[0] );
alert( arguments[1] );
alert( arguments.length );
}
// shows: Julius, Caesar, 2
showName("Julius", "Caesar");
// shows: Ilya, undefined, 1
showName("Ilya");
```
The downside is that `arguments` looks like an array, but it's not. It does not support many useful array features. It only exists for backwards compatibility. The rest operator is better.
````
## Destructuring in parameters
There are times when a function may have many parameters. Imagine a function that creates a menu. It may have a width, a height, a title, items list and so on.
Here's a bad way to write such function:
```js
function showMenu(title = "Untitled", width = 200, height = 100, items = []) {
// ...
}
```
The real-life problem is how to remember the order of arguments. Usually IDEs try to help us, especially if the code is well-documented, but still... Another problem is how to call a function when most parameters are ok by default.
Like this?
```js
showMenu("My Menu", undefined, undefined, ["Item1", "Item2"])
```
That's ugly. And becomes unreadable if we have not 4 but 10 parameters.
Destructuring comes to the rescue!
We can pass parameters as an object, and the function immediately destructurizes them into variables:
```js run
let options = {
title: "My menu",
items: ["Item1", "Item2"]
};
*!*
function showMenu({title = "Untitled", width = 200, height = 100, items = []}) {
*/!*
alert( title + ' ' + width + ' ' + height ); // My Menu 100 200
alert( items ); // Item1, Item2
}
showMenu(options);
```
We can also use the more complex destructuring with nestings and colon mappings:
```js run
let options = {
title: "My menu",
items: ["Item1", "Item2"]
};
*!*
function showMenu({
title = "Untitled",
width:w = 100, // width goes to w
height:h = 200, // height goes to h
items: [item1, item2] // items first element goes to item1, second to item2
}) {
*/!*
alert( title + ' ' + w + ' ' + h ); // My Menu 100 200
alert( item1 ); // Item1
alert( item2 ); // Item2
}
showMenu(options);
```
The syntax is the same as for a destructuring assignment:
```js
function({
incoming property: parameterName = defaultValue
...
})
```
Please note that such destructuring assumes that `showMenu()` does have an argument. If we want all values by default, then we should specify an empty object:
```js
showMenu({});
// that would give an error
showMenu();
```
We can fix this of course, by making an empty object a value by default for the whole destructuring thing:
```js run
// simplified parameters a bit for clarity
function showMenu(*!*{ title="Menu", width=100, height=200 } = {}*/!*) {
alert( title + ' ' + width + ' ' + height );
}
showMenu(); // Menu 100 200
```
In the code above, the whole arguments object is `{}` by default, so there's always something to destructurize.
## The spread operator
// TODO!!!
Выше мы увидели использование `...` для чтения параметров в объявлении функции. Но этот же оператор можно использовать и при вызове функции, для передачи массива параметров как списка, например:
```js run
'use strict';
let numbers = [2, 3, 15];
// Оператор ... в вызове передаст массив как список аргументов
// Этот вызов аналогичен Math.max(2, 3, 15)
let max = Math.max(*!*...numbers*/!*);
alert( max ); // 15
```
Формально говоря, эти два вызова делают одно и то же:
```js
Math.max(...numbers);
Math.max.apply(Math, numbers);
```
Похоже, что первый -- короче и красивее.
## Деструктуризация в параметрах
## Имя "name"
В свойстве `name` у функции находится её имя.
Например:
```js run
'use strict';
function f() {} // f.name == "f"
let g = function g() {}; // g.name == "g"
alert(f.name + ' ' + g.name) // f g
```
В примере выше показаны Function Declaration и Named Function Expression. В синтаксисе выше довольно очевидно, что у этих функций есть имя `name`. В конце концов, оно указано в объявлении.
Но современный JavaScript идёт дальше, он старается даже анонимным функциям дать разумные имена.
Например, при создании анонимной функции с одновременной записью в переменную или свойство -- её имя равно названию переменной (или свойства).
Например:
```js
'use strict';
// свойство g.name = "g"
let g = function() {};
let user = {
// свойство user.sayHi.name == "sayHi"
sayHi: function() {}
};
```
## Функции в блоке
Объявление функции Function Declaration, сделанное в блоке, видно только в этом блоке.
Например:
```js run
'use strict';
if (true) {
sayHi(); // работает
function sayHi() {
alert("Привет!");
}
}
sayHi(); // ошибка, функции не существует
```
То есть, иными словами, такое объявление -- ведёт себя в точности как если бы `let sayHi = function() {…}` было сделано в начале блока.
## Функции через =>
Появился новый синтаксис для задания функций через "стрелку" `=>`.
Его простейший вариант выглядит так:
```js run
'use strict';
*!*
let inc = x => x+1;
*/!*
alert( inc(1) ); // 2
```
Эти две записи -- примерно аналогичны:
```js
let inc = x => x+1;
let inc = function(x) { return x + 1; };
```
Как видно, `"x => x+1"` -- это уже готовая функция. Слева от `=>` находится аргумент, а справа -- выражение, которое нужно вернуть.
Если аргументов несколько, то нужно обернуть их в скобки, вот так:
```js run
'use strict';
*!*
let sum = (a,b) => a + b;
*/!*
// аналог с function
// let inc = function(a, b) { return a + b; };
alert( sum(1, 2) ); // 3
```
Если нужно задать функцию без аргументов, то также используются скобки, в этом случае -- пустые:
```js run
'use strict';
*!*
// вызов getTime() будет возвращать текущее время
let getTime = () => new Date().getHours() + ':' + new Date().getMinutes();
*/!*
alert( getTime() ); // текущее время
```
Когда тело функции достаточно большое, то можно его обернуть в фигурные скобки `{…}`:
```js run
'use strict';
*!*
let getTime = () => {
let date = new Date();
let hours = date.getHours();
let minutes = date.getMinutes();
return hourse + ':' + minutes;
};
*/!*
alert( getTime() ); // текущее время
```
Заметим, что как только тело функции оборачивается в `{…}`, то её результат уже не возвращается автоматически. Такая функция должна делать явный `return`, как в примере выше, если конечно хочет что-либо возвратить.
Функции-стрелки очень удобны в качестве коллбеков, например:
```js run
`use strict`;
let arr = [5, 8, 3];
*!*
let sorted = arr.sort( (a,b) => a - b );
*/!*
alert(sorted); // 3, 5, 8
```
Такая запись -- коротка и понятна. Далее мы познакомимся с дополнительными преимуществами использования функций-стрелок для этой цели.
## Функции-стрелки не имеют своего this
Внутри функций-стрелок -- тот же `this`, что и снаружи.
Это очень удобно в обработчиках событий и коллбэках, например:
```js run
'use strict';
let group = {
title: "Наш курс",
students: ["Вася", "Петя", "Даша"],
showList: function() {
*!*
this.students.forEach(
student => alert(this.title + ': ' + student)
)
*/!*
}
}
group.showList();
// Наш курс: Вася
// Наш курс: Петя
// Наш курс: Даша
```
Здесь в `forEach` была использована функция-стрелка, поэтому `this.title` в коллбэке -- тот же, что и во внешней функции `showList`. То есть, в данном случае -- `group.title`.
Если бы в `forEach` вместо функции-стрелки была обычная функция, то была бы ошибка:
```js run
'use strict';
let group = {
title: "Наш курс",
students: ["Вася", "Петя", "Даша"],
showList: function() {
*!*
this.students.forEach(function(student) {
alert(this.title + ': ' + student); // будет ошибка
})
*/!*
}
}
group.showList();
```
При запуске будет "попытка прочитать свойство `title` у `undefined`", так как `.forEach(f)` при запуске `f` не ставит `this`. То есть, `this` внутри `forEach` будет `undefined`.
```warn header="Функции стрелки нельзя запускать с `new`"
Отсутствие у функции-стрелки "своего `this`" влечёт за собой естественное ограничение: такие функции нельзя использовать в качестве конструктора, то есть нельзя вызывать через `new`.
```
```smart header="=> это не то же самое, что `.bind(this)`"
Есть тонкое различие между функцией стрелкой `=>` и обычной функцией, у которой вызван `.bind(this)`:
- Вызовом `.bind(this)` мы передаём текущий `this`, привязывая его к функции.
- При `=>` привязки не происходит, так как функция стрелка вообще не имеет контекста `this`. Поиск `this` в ней осуществляется так же, как и поиск обычной переменной, то есть, выше в замыкании. До появления стандарта ES-2015 такое было невозможно.
```
## Функции-стрелки не имеют своего arguments
В качестве `arguments` используются аргументы внешней "обычной" функции.
Например:
```js run
'use strict';
function f() {
let showArg = () => alert(arguments[0]);
showArg();
}
f(1); // 1
```
Вызов `showArg()` выведет `1`, получив его из аргументов функции `f`. Функция-стрелка здесь вызвана без параметров, но это не важно: `arguments` всегда берутся из внешней "обычной" функции.
Сохранение внешнего `this` и `arguments` удобно использовать для форвардинга вызовов и создания декораторов.
Например, декоратор `defer(f, ms)` ниже получает функцию `f` и возвращает обёртку вокруг неё, откладывающую вызов на `ms` миллисекунд:
```js run
'use strict';
*!*
function defer(f, ms) {
return function() {
setTimeout(() => f.apply(this, arguments), ms)
}
}
*/!*
function sayHi(who) {
alert('Привет, ' + who);
}
let sayHiDeferred = defer(sayHi, 2000);
sayHiDeferred("Вася"); // Привет, Вася через 2 секунды
```
Аналогичная реализация без функции-стрелки выглядела бы так:
```js
function defer(f, ms) {
return function() {
*!*
let args = arguments;
let ctx = this;
*/!*
setTimeout(function() {
return f.apply(ctx, args);
}, ms);
}
}
```
В этом коде пришлось создавать дополнительные переменные `args` и `ctx` для передачи внешних аргументов и контекста через замыкание.
## Итого
Основные улучшения в функциях:
- Можно задавать параметры по умолчанию, а также использовать деструктуризацию для чтения приходящего объекта.
- Оператор spread (троеточие) в объявлении позволяет функции получать оставшиеся аргументы в массив: `function f(arg1, arg2, ...rest)`.
- Тот же оператор spread в вызове функции позволяет передать её массив как список аргументов (вместо `apply`).
- У функции есть свойство `name`, оно содержит имя, указанное при объявлении функции, либо, если его нет, то имя свойства или переменную, в которую она записана. Есть и некоторые другие ситуации, в которых интерпретатор подставляет "самое подходящее" имя.
- Объявление Function Declaration в блоке `{...}` видно только в этом блоке.
- Появились функции-стрелки:
- Без фигурных скобок возвращают выражение `expr`: `(args) => expr`.
- С фигурными скобками требуют явного `return`.
- Не имеют своих `this` и `arguments`, при обращении получают их из окружающего контекста.
- Не могут быть использованы как конструкторы, с `new`.

View file

@ -49,6 +49,30 @@ let guestList = "Guests: // Error: Unexpected token ILLEGAL
Single and double quotes come from ancient times of language creation, and the need for multiline strings was not taken into account. Backticks appeared much later and thus are more versatile. Single and double quotes come from ancient times of language creation, and the need for multiline strings was not taken into account. Backticks appeared much later and thus are more versatile.
Backticks also allow to specify a "template function" at the beginning.
Its name is put before the first backtick. Then it receives the string and embedded expressions can can process them.
The syntax is:
```js run
function love(string, value1, value2) {
alert( string[0] ); // Hello
alert( string[1] ); // and
alert( value1 ); // Ilya
alert( value2 ); // Julia
return value1 + ' ♥ ' + value2;
}
let mom = "Julia";
let dad = "Ilya";
let str = love`Hello ${mom} and ${dad}`;
alert(str); // 'Julia ♥ Ilya'
```
In the example above, `love` is the name for the function. It is called with an array
## Special characters ## Special characters
It is still possible to create multiline strings with single quotes, using a so-called "newline character" written as `\n`, that denotes a line break: It is still possible to create multiline strings with single quotes, using a so-called "newline character" written as `\n`, that denotes a line break: