diff --git a/1-js/2-first-steps/07-types/article.md b/1-js/2-first-steps/07-types/article.md index 40b82e45..177d9510 100644 --- a/1-js/2-first-steps/07-types/article.md +++ b/1-js/2-first-steps/07-types/article.md @@ -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. +## Arrays + +As we’ve 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 can’t easily access the n-th element in an object. Also we can’t 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 . + ## 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. @@ -319,140 +350,24 @@ typeof Symbol("id") // "symbol" 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" -alert(typeof a); // string +typeof alert // "function" (3) */!* ``` -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`| -|true / false | `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 @@ -464,9 +379,8 @@ There are 7 basic types in JavaScript. - `boolean` for `true`/`false`, can convert into it using `Boolean(value)`. - `null` for unknown values. - `undefined` for unassigned values. -- `object` for complex data structures. - `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. diff --git a/1-js/2-first-steps/08-type-conversions/article.md b/1-js/2-first-steps/08-type-conversions/article.md new file mode 100644 index 00000000..57525e33 --- /dev/null +++ b/1-js/2-first-steps/08-type-conversions/article.md @@ -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`| +|true and false | `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`| +|true / false | `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. \ No newline at end of file diff --git a/1-js/2-first-steps/08-type-conversions/object-user-delete.png b/1-js/2-first-steps/08-type-conversions/object-user-delete.png new file mode 100644 index 00000000..3022fef2 Binary files /dev/null and b/1-js/2-first-steps/08-type-conversions/object-user-delete.png differ diff --git a/1-js/2-first-steps/08-type-conversions/object-user-delete@2x.png b/1-js/2-first-steps/08-type-conversions/object-user-delete@2x.png new file mode 100644 index 00000000..3a17a1e4 Binary files /dev/null and b/1-js/2-first-steps/08-type-conversions/object-user-delete@2x.png differ diff --git a/1-js/2-first-steps/08-type-conversions/object-user-isadmin.png b/1-js/2-first-steps/08-type-conversions/object-user-isadmin.png new file mode 100644 index 00000000..e0f3bcb6 Binary files /dev/null and b/1-js/2-first-steps/08-type-conversions/object-user-isadmin.png differ diff --git a/1-js/2-first-steps/08-type-conversions/object-user-isadmin@2x.png b/1-js/2-first-steps/08-type-conversions/object-user-isadmin@2x.png new file mode 100644 index 00000000..79dc9a5b Binary files /dev/null and b/1-js/2-first-steps/08-type-conversions/object-user-isadmin@2x.png differ diff --git a/1-js/2-first-steps/08-type-conversions/object-user.png b/1-js/2-first-steps/08-type-conversions/object-user.png new file mode 100644 index 00000000..2d4023e1 Binary files /dev/null and b/1-js/2-first-steps/08-type-conversions/object-user.png differ diff --git a/1-js/2-first-steps/08-type-conversions/object-user@2x.png b/1-js/2-first-steps/08-type-conversions/object-user@2x.png new file mode 100644 index 00000000..a3ce20ed Binary files /dev/null and b/1-js/2-first-steps/08-type-conversions/object-user@2x.png differ diff --git a/1-js/2-first-steps/08-operators/1-increment-order/solution.md b/1-js/2-first-steps/09-operators/1-increment-order/solution.md similarity index 100% rename from 1-js/2-first-steps/08-operators/1-increment-order/solution.md rename to 1-js/2-first-steps/09-operators/1-increment-order/solution.md diff --git a/1-js/2-first-steps/08-operators/1-increment-order/task.md b/1-js/2-first-steps/09-operators/1-increment-order/task.md similarity index 100% rename from 1-js/2-first-steps/08-operators/1-increment-order/task.md rename to 1-js/2-first-steps/09-operators/1-increment-order/task.md diff --git a/1-js/2-first-steps/08-operators/2-assignment-result/solution.md b/1-js/2-first-steps/09-operators/2-assignment-result/solution.md similarity index 100% rename from 1-js/2-first-steps/08-operators/2-assignment-result/solution.md rename to 1-js/2-first-steps/09-operators/2-assignment-result/solution.md diff --git a/1-js/2-first-steps/08-operators/2-assignment-result/task.md b/1-js/2-first-steps/09-operators/2-assignment-result/task.md similarity index 100% rename from 1-js/2-first-steps/08-operators/2-assignment-result/task.md rename to 1-js/2-first-steps/09-operators/2-assignment-result/task.md diff --git a/1-js/2-first-steps/08-operators/3-primitive-conversions-questions/solution.md b/1-js/2-first-steps/09-operators/3-primitive-conversions-questions/solution.md similarity index 100% rename from 1-js/2-first-steps/08-operators/3-primitive-conversions-questions/solution.md rename to 1-js/2-first-steps/09-operators/3-primitive-conversions-questions/solution.md diff --git a/1-js/2-first-steps/08-operators/3-primitive-conversions-questions/task.md b/1-js/2-first-steps/09-operators/3-primitive-conversions-questions/task.md similarity index 100% rename from 1-js/2-first-steps/08-operators/3-primitive-conversions-questions/task.md rename to 1-js/2-first-steps/09-operators/3-primitive-conversions-questions/task.md diff --git a/1-js/2-first-steps/08-operators/article.md b/1-js/2-first-steps/09-operators/article.md similarity index 100% rename from 1-js/2-first-steps/08-operators/article.md rename to 1-js/2-first-steps/09-operators/article.md diff --git a/1-js/2-first-steps/09-comparison/1-comparison-questions/solution.md b/1-js/2-first-steps/10-comparison/1-comparison-questions/solution.md similarity index 100% rename from 1-js/2-first-steps/09-comparison/1-comparison-questions/solution.md rename to 1-js/2-first-steps/10-comparison/1-comparison-questions/solution.md diff --git a/1-js/2-first-steps/09-comparison/1-comparison-questions/task.md b/1-js/2-first-steps/10-comparison/1-comparison-questions/task.md similarity index 100% rename from 1-js/2-first-steps/09-comparison/1-comparison-questions/task.md rename to 1-js/2-first-steps/10-comparison/1-comparison-questions/task.md diff --git a/1-js/2-first-steps/09-comparison/article.md b/1-js/2-first-steps/10-comparison/article.md similarity index 100% rename from 1-js/2-first-steps/09-comparison/article.md rename to 1-js/2-first-steps/10-comparison/article.md diff --git a/1-js/2-first-steps/11-destructuring/1-destructuring-assignment/solution.md b/1-js/2-first-steps/11-destructuring/1-destructuring-assignment/solution.md new file mode 100644 index 00000000..cc226e7c --- /dev/null +++ b/1-js/2-first-steps/11-destructuring/1-destructuring-assignment/solution.md @@ -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 +``` \ No newline at end of file diff --git a/1-js/2-first-steps/11-destructuring/1-destructuring-assignment/task.md b/1-js/2-first-steps/11-destructuring/1-destructuring-assignment/task.md new file mode 100644 index 00000000..b2213323 --- /dev/null +++ b/1-js/2-first-steps/11-destructuring/1-destructuring-assignment/task.md @@ -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 +``` diff --git a/1-js/2-first-steps/11-destructuring/article.md b/1-js/2-first-steps/11-destructuring/article.md new file mode 100644 index 00000000..f58af9b9 --- /dev/null +++ b/1-js/2-first-steps/11-destructuring/article.md @@ -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. + + + + diff --git a/1-js/2-first-steps/10-uibasic/1-simple-page/solution.md b/1-js/2-first-steps/12-uibasic/1-simple-page/solution.md similarity index 100% rename from 1-js/2-first-steps/10-uibasic/1-simple-page/solution.md rename to 1-js/2-first-steps/12-uibasic/1-simple-page/solution.md diff --git a/1-js/2-first-steps/10-uibasic/1-simple-page/task.md b/1-js/2-first-steps/12-uibasic/1-simple-page/task.md similarity index 100% rename from 1-js/2-first-steps/10-uibasic/1-simple-page/task.md rename to 1-js/2-first-steps/12-uibasic/1-simple-page/task.md diff --git a/1-js/2-first-steps/10-uibasic/article.md b/1-js/2-first-steps/12-uibasic/article.md similarity index 100% rename from 1-js/2-first-steps/10-uibasic/article.md rename to 1-js/2-first-steps/12-uibasic/article.md diff --git a/1-js/2-first-steps/11-ifelse/1-if-zero-string/solution.md b/1-js/2-first-steps/13-ifelse/1-if-zero-string/solution.md similarity index 100% rename from 1-js/2-first-steps/11-ifelse/1-if-zero-string/solution.md rename to 1-js/2-first-steps/13-ifelse/1-if-zero-string/solution.md diff --git a/1-js/2-first-steps/11-ifelse/1-if-zero-string/task.md b/1-js/2-first-steps/13-ifelse/1-if-zero-string/task.md similarity index 100% rename from 1-js/2-first-steps/11-ifelse/1-if-zero-string/task.md rename to 1-js/2-first-steps/13-ifelse/1-if-zero-string/task.md diff --git a/1-js/2-first-steps/11-ifelse/2-check-standard/ifelse_task2.png b/1-js/2-first-steps/13-ifelse/2-check-standard/ifelse_task2.png similarity index 100% rename from 1-js/2-first-steps/11-ifelse/2-check-standard/ifelse_task2.png rename to 1-js/2-first-steps/13-ifelse/2-check-standard/ifelse_task2.png diff --git a/1-js/2-first-steps/11-ifelse/2-check-standard/ifelse_task2/index.html b/1-js/2-first-steps/13-ifelse/2-check-standard/ifelse_task2/index.html similarity index 100% rename from 1-js/2-first-steps/11-ifelse/2-check-standard/ifelse_task2/index.html rename to 1-js/2-first-steps/13-ifelse/2-check-standard/ifelse_task2/index.html diff --git a/1-js/2-first-steps/11-ifelse/2-check-standard/ifelse_task2@2x.png b/1-js/2-first-steps/13-ifelse/2-check-standard/ifelse_task2@2x.png similarity index 100% rename from 1-js/2-first-steps/11-ifelse/2-check-standard/ifelse_task2@2x.png rename to 1-js/2-first-steps/13-ifelse/2-check-standard/ifelse_task2@2x.png diff --git a/1-js/2-first-steps/11-ifelse/2-check-standard/solution.md b/1-js/2-first-steps/13-ifelse/2-check-standard/solution.md similarity index 100% rename from 1-js/2-first-steps/11-ifelse/2-check-standard/solution.md rename to 1-js/2-first-steps/13-ifelse/2-check-standard/solution.md diff --git a/1-js/2-first-steps/11-ifelse/2-check-standard/task.md b/1-js/2-first-steps/13-ifelse/2-check-standard/task.md similarity index 100% rename from 1-js/2-first-steps/11-ifelse/2-check-standard/task.md rename to 1-js/2-first-steps/13-ifelse/2-check-standard/task.md diff --git a/1-js/2-first-steps/11-ifelse/3-sign/if_sign/index.html b/1-js/2-first-steps/13-ifelse/3-sign/if_sign/index.html similarity index 100% rename from 1-js/2-first-steps/11-ifelse/3-sign/if_sign/index.html rename to 1-js/2-first-steps/13-ifelse/3-sign/if_sign/index.html diff --git a/1-js/2-first-steps/11-ifelse/3-sign/solution.md b/1-js/2-first-steps/13-ifelse/3-sign/solution.md similarity index 100% rename from 1-js/2-first-steps/11-ifelse/3-sign/solution.md rename to 1-js/2-first-steps/13-ifelse/3-sign/solution.md diff --git a/1-js/2-first-steps/11-ifelse/3-sign/task.md b/1-js/2-first-steps/13-ifelse/3-sign/task.md similarity index 100% rename from 1-js/2-first-steps/11-ifelse/3-sign/task.md rename to 1-js/2-first-steps/13-ifelse/3-sign/task.md diff --git a/1-js/2-first-steps/11-ifelse/4-check-login/ifelse_task.png b/1-js/2-first-steps/13-ifelse/4-check-login/ifelse_task.png similarity index 100% rename from 1-js/2-first-steps/11-ifelse/4-check-login/ifelse_task.png rename to 1-js/2-first-steps/13-ifelse/4-check-login/ifelse_task.png diff --git a/1-js/2-first-steps/11-ifelse/4-check-login/ifelse_task@2x.png b/1-js/2-first-steps/13-ifelse/4-check-login/ifelse_task@2x.png similarity index 100% rename from 1-js/2-first-steps/11-ifelse/4-check-login/ifelse_task@2x.png rename to 1-js/2-first-steps/13-ifelse/4-check-login/ifelse_task@2x.png diff --git a/1-js/2-first-steps/11-ifelse/4-check-login/solution.md b/1-js/2-first-steps/13-ifelse/4-check-login/solution.md similarity index 100% rename from 1-js/2-first-steps/11-ifelse/4-check-login/solution.md rename to 1-js/2-first-steps/13-ifelse/4-check-login/solution.md diff --git a/1-js/2-first-steps/11-ifelse/4-check-login/task.md b/1-js/2-first-steps/13-ifelse/4-check-login/task.md similarity index 100% rename from 1-js/2-first-steps/11-ifelse/4-check-login/task.md rename to 1-js/2-first-steps/13-ifelse/4-check-login/task.md diff --git a/1-js/2-first-steps/11-ifelse/5-rewrite-if-question/solution.md b/1-js/2-first-steps/13-ifelse/5-rewrite-if-question/solution.md similarity index 100% rename from 1-js/2-first-steps/11-ifelse/5-rewrite-if-question/solution.md rename to 1-js/2-first-steps/13-ifelse/5-rewrite-if-question/solution.md diff --git a/1-js/2-first-steps/11-ifelse/5-rewrite-if-question/task.md b/1-js/2-first-steps/13-ifelse/5-rewrite-if-question/task.md similarity index 100% rename from 1-js/2-first-steps/11-ifelse/5-rewrite-if-question/task.md rename to 1-js/2-first-steps/13-ifelse/5-rewrite-if-question/task.md diff --git a/1-js/2-first-steps/11-ifelse/6-rewrite-if-else-question/solution.md b/1-js/2-first-steps/13-ifelse/6-rewrite-if-else-question/solution.md similarity index 100% rename from 1-js/2-first-steps/11-ifelse/6-rewrite-if-else-question/solution.md rename to 1-js/2-first-steps/13-ifelse/6-rewrite-if-else-question/solution.md diff --git a/1-js/2-first-steps/11-ifelse/6-rewrite-if-else-question/task.md b/1-js/2-first-steps/13-ifelse/6-rewrite-if-else-question/task.md similarity index 100% rename from 1-js/2-first-steps/11-ifelse/6-rewrite-if-else-question/task.md rename to 1-js/2-first-steps/13-ifelse/6-rewrite-if-else-question/task.md diff --git a/1-js/2-first-steps/11-ifelse/article.md b/1-js/2-first-steps/13-ifelse/article.md similarity index 100% rename from 1-js/2-first-steps/11-ifelse/article.md rename to 1-js/2-first-steps/13-ifelse/article.md diff --git a/1-js/2-first-steps/12-logical-ops/1-alert-null-2-undefined/solution.md b/1-js/2-first-steps/14-logical-ops/1-alert-null-2-undefined/solution.md similarity index 100% rename from 1-js/2-first-steps/12-logical-ops/1-alert-null-2-undefined/solution.md rename to 1-js/2-first-steps/14-logical-ops/1-alert-null-2-undefined/solution.md diff --git a/1-js/2-first-steps/12-logical-ops/1-alert-null-2-undefined/task.md b/1-js/2-first-steps/14-logical-ops/1-alert-null-2-undefined/task.md similarity index 100% rename from 1-js/2-first-steps/12-logical-ops/1-alert-null-2-undefined/task.md rename to 1-js/2-first-steps/14-logical-ops/1-alert-null-2-undefined/task.md diff --git a/1-js/2-first-steps/12-logical-ops/2-alert-or/solution.md b/1-js/2-first-steps/14-logical-ops/2-alert-or/solution.md similarity index 100% rename from 1-js/2-first-steps/12-logical-ops/2-alert-or/solution.md rename to 1-js/2-first-steps/14-logical-ops/2-alert-or/solution.md diff --git a/1-js/2-first-steps/12-logical-ops/2-alert-or/task.md b/1-js/2-first-steps/14-logical-ops/2-alert-or/task.md similarity index 100% rename from 1-js/2-first-steps/12-logical-ops/2-alert-or/task.md rename to 1-js/2-first-steps/14-logical-ops/2-alert-or/task.md diff --git a/1-js/2-first-steps/12-logical-ops/3-alert-1-null-2/solution.md b/1-js/2-first-steps/14-logical-ops/3-alert-1-null-2/solution.md similarity index 100% rename from 1-js/2-first-steps/12-logical-ops/3-alert-1-null-2/solution.md rename to 1-js/2-first-steps/14-logical-ops/3-alert-1-null-2/solution.md diff --git a/1-js/2-first-steps/12-logical-ops/3-alert-1-null-2/task.md b/1-js/2-first-steps/14-logical-ops/3-alert-1-null-2/task.md similarity index 100% rename from 1-js/2-first-steps/12-logical-ops/3-alert-1-null-2/task.md rename to 1-js/2-first-steps/14-logical-ops/3-alert-1-null-2/task.md diff --git a/1-js/2-first-steps/12-logical-ops/4-alert-and/solution.md b/1-js/2-first-steps/14-logical-ops/4-alert-and/solution.md similarity index 100% rename from 1-js/2-first-steps/12-logical-ops/4-alert-and/solution.md rename to 1-js/2-first-steps/14-logical-ops/4-alert-and/solution.md diff --git a/1-js/2-first-steps/12-logical-ops/4-alert-and/task.md b/1-js/2-first-steps/14-logical-ops/4-alert-and/task.md similarity index 100% rename from 1-js/2-first-steps/12-logical-ops/4-alert-and/task.md rename to 1-js/2-first-steps/14-logical-ops/4-alert-and/task.md diff --git a/1-js/2-first-steps/12-logical-ops/5-alert-and-or/solution.md b/1-js/2-first-steps/14-logical-ops/5-alert-and-or/solution.md similarity index 100% rename from 1-js/2-first-steps/12-logical-ops/5-alert-and-or/solution.md rename to 1-js/2-first-steps/14-logical-ops/5-alert-and-or/solution.md diff --git a/1-js/2-first-steps/12-logical-ops/5-alert-and-or/task.md b/1-js/2-first-steps/14-logical-ops/5-alert-and-or/task.md similarity index 100% rename from 1-js/2-first-steps/12-logical-ops/5-alert-and-or/task.md rename to 1-js/2-first-steps/14-logical-ops/5-alert-and-or/task.md diff --git a/1-js/2-first-steps/12-logical-ops/6-check-if-in-range/solution.md b/1-js/2-first-steps/14-logical-ops/6-check-if-in-range/solution.md similarity index 100% rename from 1-js/2-first-steps/12-logical-ops/6-check-if-in-range/solution.md rename to 1-js/2-first-steps/14-logical-ops/6-check-if-in-range/solution.md diff --git a/1-js/2-first-steps/12-logical-ops/6-check-if-in-range/task.md b/1-js/2-first-steps/14-logical-ops/6-check-if-in-range/task.md similarity index 100% rename from 1-js/2-first-steps/12-logical-ops/6-check-if-in-range/task.md rename to 1-js/2-first-steps/14-logical-ops/6-check-if-in-range/task.md diff --git a/1-js/2-first-steps/12-logical-ops/7-check-if-out-range/solution.md b/1-js/2-first-steps/14-logical-ops/7-check-if-out-range/solution.md similarity index 100% rename from 1-js/2-first-steps/12-logical-ops/7-check-if-out-range/solution.md rename to 1-js/2-first-steps/14-logical-ops/7-check-if-out-range/solution.md diff --git a/1-js/2-first-steps/12-logical-ops/7-check-if-out-range/task.md b/1-js/2-first-steps/14-logical-ops/7-check-if-out-range/task.md similarity index 100% rename from 1-js/2-first-steps/12-logical-ops/7-check-if-out-range/task.md rename to 1-js/2-first-steps/14-logical-ops/7-check-if-out-range/task.md diff --git a/1-js/2-first-steps/12-logical-ops/8-if-question/solution.md b/1-js/2-first-steps/14-logical-ops/8-if-question/solution.md similarity index 100% rename from 1-js/2-first-steps/12-logical-ops/8-if-question/solution.md rename to 1-js/2-first-steps/14-logical-ops/8-if-question/solution.md diff --git a/1-js/2-first-steps/12-logical-ops/8-if-question/task.md b/1-js/2-first-steps/14-logical-ops/8-if-question/task.md similarity index 100% rename from 1-js/2-first-steps/12-logical-ops/8-if-question/task.md rename to 1-js/2-first-steps/14-logical-ops/8-if-question/task.md diff --git a/1-js/2-first-steps/12-logical-ops/article.md b/1-js/2-first-steps/14-logical-ops/article.md similarity index 100% rename from 1-js/2-first-steps/12-logical-ops/article.md rename to 1-js/2-first-steps/14-logical-ops/article.md diff --git a/1-js/2-first-steps/13-while-for/1-loop-last-value/solution.md b/1-js/2-first-steps/15-while-for/1-loop-last-value/solution.md similarity index 100% rename from 1-js/2-first-steps/13-while-for/1-loop-last-value/solution.md rename to 1-js/2-first-steps/15-while-for/1-loop-last-value/solution.md diff --git a/1-js/2-first-steps/13-while-for/1-loop-last-value/task.md b/1-js/2-first-steps/15-while-for/1-loop-last-value/task.md similarity index 100% rename from 1-js/2-first-steps/13-while-for/1-loop-last-value/task.md rename to 1-js/2-first-steps/15-while-for/1-loop-last-value/task.md diff --git a/1-js/2-first-steps/13-while-for/2-which-value-while/solution.md b/1-js/2-first-steps/15-while-for/2-which-value-while/solution.md similarity index 100% rename from 1-js/2-first-steps/13-while-for/2-which-value-while/solution.md rename to 1-js/2-first-steps/15-while-for/2-which-value-while/solution.md diff --git a/1-js/2-first-steps/13-while-for/2-which-value-while/task.md b/1-js/2-first-steps/15-while-for/2-which-value-while/task.md similarity index 100% rename from 1-js/2-first-steps/13-while-for/2-which-value-while/task.md rename to 1-js/2-first-steps/15-while-for/2-which-value-while/task.md diff --git a/1-js/2-first-steps/13-while-for/3-which-value-for/solution.md b/1-js/2-first-steps/15-while-for/3-which-value-for/solution.md similarity index 100% rename from 1-js/2-first-steps/13-while-for/3-which-value-for/solution.md rename to 1-js/2-first-steps/15-while-for/3-which-value-for/solution.md diff --git a/1-js/2-first-steps/13-while-for/3-which-value-for/task.md b/1-js/2-first-steps/15-while-for/3-which-value-for/task.md similarity index 100% rename from 1-js/2-first-steps/13-while-for/3-which-value-for/task.md rename to 1-js/2-first-steps/15-while-for/3-which-value-for/task.md diff --git a/1-js/2-first-steps/13-while-for/4-for-even/solution.md b/1-js/2-first-steps/15-while-for/4-for-even/solution.md similarity index 100% rename from 1-js/2-first-steps/13-while-for/4-for-even/solution.md rename to 1-js/2-first-steps/15-while-for/4-for-even/solution.md diff --git a/1-js/2-first-steps/13-while-for/4-for-even/task.md b/1-js/2-first-steps/15-while-for/4-for-even/task.md similarity index 100% rename from 1-js/2-first-steps/13-while-for/4-for-even/task.md rename to 1-js/2-first-steps/15-while-for/4-for-even/task.md diff --git a/1-js/2-first-steps/13-while-for/5-replace-for-while/solution.md b/1-js/2-first-steps/15-while-for/5-replace-for-while/solution.md similarity index 100% rename from 1-js/2-first-steps/13-while-for/5-replace-for-while/solution.md rename to 1-js/2-first-steps/15-while-for/5-replace-for-while/solution.md diff --git a/1-js/2-first-steps/13-while-for/5-replace-for-while/task.md b/1-js/2-first-steps/15-while-for/5-replace-for-while/task.md similarity index 100% rename from 1-js/2-first-steps/13-while-for/5-replace-for-while/task.md rename to 1-js/2-first-steps/15-while-for/5-replace-for-while/task.md diff --git a/1-js/2-first-steps/13-while-for/6-repeat-until-correct/solution.md b/1-js/2-first-steps/15-while-for/6-repeat-until-correct/solution.md similarity index 100% rename from 1-js/2-first-steps/13-while-for/6-repeat-until-correct/solution.md rename to 1-js/2-first-steps/15-while-for/6-repeat-until-correct/solution.md diff --git a/1-js/2-first-steps/13-while-for/6-repeat-until-correct/task.md b/1-js/2-first-steps/15-while-for/6-repeat-until-correct/task.md similarity index 100% rename from 1-js/2-first-steps/13-while-for/6-repeat-until-correct/task.md rename to 1-js/2-first-steps/15-while-for/6-repeat-until-correct/task.md diff --git a/1-js/2-first-steps/13-while-for/7-list-primes/solution.md b/1-js/2-first-steps/15-while-for/7-list-primes/solution.md similarity index 100% rename from 1-js/2-first-steps/13-while-for/7-list-primes/solution.md rename to 1-js/2-first-steps/15-while-for/7-list-primes/solution.md diff --git a/1-js/2-first-steps/13-while-for/7-list-primes/task.md b/1-js/2-first-steps/15-while-for/7-list-primes/task.md similarity index 100% rename from 1-js/2-first-steps/13-while-for/7-list-primes/task.md rename to 1-js/2-first-steps/15-while-for/7-list-primes/task.md diff --git a/1-js/2-first-steps/13-while-for/article.md b/1-js/2-first-steps/15-while-for/article.md similarity index 72% rename from 1-js/2-first-steps/13-while-for/article.md rename to 1-js/2-first-steps/15-while-for/article.md index 77e44b17..e2544c2f 100644 --- a/1-js/2-first-steps/13-while-for/article.md +++ b/1-js/2-first-steps/15-while-for/article.md @@ -139,7 +139,7 @@ for (*!*let*/!* i = 0; i < 3; i++) { The variable will be visible only inside the loop. ```` -## Skipping parts +### Skipping parts 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. -```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 -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. @@ -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 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] @@ -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. ```` @@ -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 '?'" 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 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 (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`. @@ -290,7 +284,7 @@ for (let i = 0; i < 3; i++) { 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 "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 -There are 3 types of loops in JavaScript: +There are 5 types of loops in JavaScript: - `while` -- the condition is checked before 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. diff --git a/1-js/2-first-steps/14-switch/1-rewrite-switch-if-else/solution.md b/1-js/2-first-steps/16-switch/1-rewrite-switch-if-else/solution.md similarity index 100% rename from 1-js/2-first-steps/14-switch/1-rewrite-switch-if-else/solution.md rename to 1-js/2-first-steps/16-switch/1-rewrite-switch-if-else/solution.md diff --git a/1-js/2-first-steps/14-switch/1-rewrite-switch-if-else/task.md b/1-js/2-first-steps/16-switch/1-rewrite-switch-if-else/task.md similarity index 100% rename from 1-js/2-first-steps/14-switch/1-rewrite-switch-if-else/task.md rename to 1-js/2-first-steps/16-switch/1-rewrite-switch-if-else/task.md diff --git a/1-js/2-first-steps/14-switch/2-rewrite-if-switch/solution.md b/1-js/2-first-steps/16-switch/2-rewrite-if-switch/solution.md similarity index 100% rename from 1-js/2-first-steps/14-switch/2-rewrite-if-switch/solution.md rename to 1-js/2-first-steps/16-switch/2-rewrite-if-switch/solution.md diff --git a/1-js/2-first-steps/14-switch/2-rewrite-if-switch/task.md b/1-js/2-first-steps/16-switch/2-rewrite-if-switch/task.md similarity index 100% rename from 1-js/2-first-steps/14-switch/2-rewrite-if-switch/task.md rename to 1-js/2-first-steps/16-switch/2-rewrite-if-switch/task.md diff --git a/1-js/2-first-steps/14-switch/article.md b/1-js/2-first-steps/16-switch/article.md similarity index 100% rename from 1-js/2-first-steps/14-switch/article.md rename to 1-js/2-first-steps/16-switch/article.md diff --git a/1-js/2-first-steps/18-function-basics/1-if-else-required/solution.md b/1-js/2-first-steps/19-function-basics/1-if-else-required/solution.md similarity index 100% rename from 1-js/2-first-steps/18-function-basics/1-if-else-required/solution.md rename to 1-js/2-first-steps/19-function-basics/1-if-else-required/solution.md diff --git a/1-js/2-first-steps/18-function-basics/1-if-else-required/task.md b/1-js/2-first-steps/19-function-basics/1-if-else-required/task.md similarity index 100% rename from 1-js/2-first-steps/18-function-basics/1-if-else-required/task.md rename to 1-js/2-first-steps/19-function-basics/1-if-else-required/task.md diff --git a/1-js/2-first-steps/18-function-basics/2-rewrite-function-question-or/solution.md b/1-js/2-first-steps/19-function-basics/2-rewrite-function-question-or/solution.md similarity index 100% rename from 1-js/2-first-steps/18-function-basics/2-rewrite-function-question-or/solution.md rename to 1-js/2-first-steps/19-function-basics/2-rewrite-function-question-or/solution.md diff --git a/1-js/2-first-steps/18-function-basics/2-rewrite-function-question-or/task.md b/1-js/2-first-steps/19-function-basics/2-rewrite-function-question-or/task.md similarity index 100% rename from 1-js/2-first-steps/18-function-basics/2-rewrite-function-question-or/task.md rename to 1-js/2-first-steps/19-function-basics/2-rewrite-function-question-or/task.md diff --git a/1-js/2-first-steps/18-function-basics/3-min/solution.md b/1-js/2-first-steps/19-function-basics/3-min/solution.md similarity index 100% rename from 1-js/2-first-steps/18-function-basics/3-min/solution.md rename to 1-js/2-first-steps/19-function-basics/3-min/solution.md diff --git a/1-js/2-first-steps/18-function-basics/3-min/task.md b/1-js/2-first-steps/19-function-basics/3-min/task.md similarity index 100% rename from 1-js/2-first-steps/18-function-basics/3-min/task.md rename to 1-js/2-first-steps/19-function-basics/3-min/task.md diff --git a/1-js/2-first-steps/18-function-basics/4-pow/solution.md b/1-js/2-first-steps/19-function-basics/4-pow/solution.md similarity index 100% rename from 1-js/2-first-steps/18-function-basics/4-pow/solution.md rename to 1-js/2-first-steps/19-function-basics/4-pow/solution.md diff --git a/1-js/2-first-steps/18-function-basics/4-pow/task.md b/1-js/2-first-steps/19-function-basics/4-pow/task.md similarity index 100% rename from 1-js/2-first-steps/18-function-basics/4-pow/task.md rename to 1-js/2-first-steps/19-function-basics/4-pow/task.md diff --git a/1-js/2-first-steps/18-function-basics/article.md b/1-js/2-first-steps/19-function-basics/article.md similarity index 87% rename from 1-js/2-first-steps/18-function-basics/article.md rename to 1-js/2-first-steps/19-function-basics/article.md index 2b0884b1..9c8e51ab 100644 --- a/1-js/2-first-steps/18-function-basics/article.md +++ b/1-js/2-first-steps/19-function-basics/article.md @@ -176,61 +176,6 @@ showMessage(from, "Hello"); // *Ann*: Hello 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 A function can return a value back into the calling code as the result. diff --git a/1-js/2-first-steps/18-function-basics/function_basics.png b/1-js/2-first-steps/19-function-basics/function_basics.png similarity index 100% rename from 1-js/2-first-steps/18-function-basics/function_basics.png rename to 1-js/2-first-steps/19-function-basics/function_basics.png diff --git a/1-js/2-first-steps/18-function-basics/function_basics@2x.png b/1-js/2-first-steps/19-function-basics/function_basics@2x.png similarity index 100% rename from 1-js/2-first-steps/18-function-basics/function_basics@2x.png rename to 1-js/2-first-steps/19-function-basics/function_basics@2x.png diff --git a/1-js/2-first-steps/20-function-parameters/article.md b/1-js/2-first-steps/20-function-parameters/article.md new file mode 100644 index 00000000..db0969fb --- /dev/null +++ b/1-js/2-first-steps/20-function-parameters/article.md @@ -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`. + + + diff --git a/1-js/2-first-steps/19-function-expression/article.md b/1-js/2-first-steps/21-function-expression/article.md similarity index 100% rename from 1-js/2-first-steps/19-function-expression/article.md rename to 1-js/2-first-steps/21-function-expression/article.md diff --git a/1-js/2-first-steps/20-primitives-methods/1-string-new-property/solution.md b/1-js/2-first-steps/22-primitives-methods/1-string-new-property/solution.md similarity index 100% rename from 1-js/2-first-steps/20-primitives-methods/1-string-new-property/solution.md rename to 1-js/2-first-steps/22-primitives-methods/1-string-new-property/solution.md diff --git a/1-js/2-first-steps/20-primitives-methods/1-string-new-property/task.md b/1-js/2-first-steps/22-primitives-methods/1-string-new-property/task.md similarity index 100% rename from 1-js/2-first-steps/20-primitives-methods/1-string-new-property/task.md rename to 1-js/2-first-steps/22-primitives-methods/1-string-new-property/task.md diff --git a/1-js/2-first-steps/20-primitives-methods/article.md b/1-js/2-first-steps/22-primitives-methods/article.md similarity index 100% rename from 1-js/2-first-steps/20-primitives-methods/article.md rename to 1-js/2-first-steps/22-primitives-methods/article.md diff --git a/1-js/2-first-steps/22-javascript-specials/article.md b/1-js/2-first-steps/23-javascript-specials/article.md similarity index 100% rename from 1-js/2-first-steps/22-javascript-specials/article.md rename to 1-js/2-first-steps/23-javascript-specials/article.md diff --git a/1-js/4-data-structures/3-string/article.md b/1-js/4-data-structures/3-string/article.md index 4b382158..d812af1a 100644 --- a/1-js/4-data-structures/3-string/article.md +++ b/1-js/4-data-structures/3-string/article.md @@ -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. +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 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: