diff --git a/1-js/4-object-basics/01-object/article.md b/1-js/4-object-basics/01-object/article.md index af69005a..ad99bec2 100644 --- a/1-js/4-object-basics/01-object/article.md +++ b/1-js/4-object-basics/01-object/article.md @@ -226,7 +226,7 @@ As we see from the code, the assignment to a primitive `5` is ignored. If we wan ```` -## Property name shorthand +## Property value shorthand In real code we often use existing variables as values for property names. @@ -270,7 +270,6 @@ let user = { }; ``` - ## Existance check A notable objects feature is that it's possible to access any property. There will be no error if the property doesn't exist! Accessing a non-existing property just returns `undefined`. It provides a very common way to test whether the property exists -- to get it and compare vs undefined: diff --git a/1-js/4-object-basics/06-symbol/article.md b/1-js/4-object-basics/03-symbol/article.md similarity index 100% rename from 1-js/4-object-basics/06-symbol/article.md rename to 1-js/4-object-basics/03-symbol/article.md diff --git a/1-js/4-object-basics/03-object-methods/2-check-syntax/solution.md b/1-js/4-object-basics/04-object-methods/2-check-syntax/solution.md similarity index 100% rename from 1-js/4-object-basics/03-object-methods/2-check-syntax/solution.md rename to 1-js/4-object-basics/04-object-methods/2-check-syntax/solution.md diff --git a/1-js/4-object-basics/03-object-methods/2-check-syntax/task.md b/1-js/4-object-basics/04-object-methods/2-check-syntax/task.md similarity index 100% rename from 1-js/4-object-basics/03-object-methods/2-check-syntax/task.md rename to 1-js/4-object-basics/04-object-methods/2-check-syntax/task.md diff --git a/1-js/4-object-basics/03-object-methods/3-why-this/solution.md b/1-js/4-object-basics/04-object-methods/3-why-this/solution.md similarity index 100% rename from 1-js/4-object-basics/03-object-methods/3-why-this/solution.md rename to 1-js/4-object-basics/04-object-methods/3-why-this/solution.md diff --git a/1-js/4-object-basics/03-object-methods/3-why-this/task.md b/1-js/4-object-basics/04-object-methods/3-why-this/task.md similarity index 100% rename from 1-js/4-object-basics/03-object-methods/3-why-this/task.md rename to 1-js/4-object-basics/04-object-methods/3-why-this/task.md diff --git a/1-js/4-object-basics/03-object-methods/4-object-property-this/solution.md b/1-js/4-object-basics/04-object-methods/4-object-property-this/solution.md similarity index 100% rename from 1-js/4-object-basics/03-object-methods/4-object-property-this/solution.md rename to 1-js/4-object-basics/04-object-methods/4-object-property-this/solution.md diff --git a/1-js/4-object-basics/03-object-methods/4-object-property-this/task.md b/1-js/4-object-basics/04-object-methods/4-object-property-this/task.md similarity index 100% rename from 1-js/4-object-basics/03-object-methods/4-object-property-this/task.md rename to 1-js/4-object-basics/04-object-methods/4-object-property-this/task.md diff --git a/1-js/4-object-basics/03-object-methods/7-calculator/_js.view/solution.js b/1-js/4-object-basics/04-object-methods/7-calculator/_js.view/solution.js similarity index 100% rename from 1-js/4-object-basics/03-object-methods/7-calculator/_js.view/solution.js rename to 1-js/4-object-basics/04-object-methods/7-calculator/_js.view/solution.js diff --git a/1-js/4-object-basics/03-object-methods/7-calculator/_js.view/test.js b/1-js/4-object-basics/04-object-methods/7-calculator/_js.view/test.js similarity index 100% rename from 1-js/4-object-basics/03-object-methods/7-calculator/_js.view/test.js rename to 1-js/4-object-basics/04-object-methods/7-calculator/_js.view/test.js diff --git a/1-js/4-object-basics/03-object-methods/7-calculator/solution.md b/1-js/4-object-basics/04-object-methods/7-calculator/solution.md similarity index 100% rename from 1-js/4-object-basics/03-object-methods/7-calculator/solution.md rename to 1-js/4-object-basics/04-object-methods/7-calculator/solution.md diff --git a/1-js/4-object-basics/03-object-methods/7-calculator/task.md b/1-js/4-object-basics/04-object-methods/7-calculator/task.md similarity index 100% rename from 1-js/4-object-basics/03-object-methods/7-calculator/task.md rename to 1-js/4-object-basics/04-object-methods/7-calculator/task.md diff --git a/1-js/4-object-basics/03-object-methods/8-chain-calls/_js.view/solution.js b/1-js/4-object-basics/04-object-methods/8-chain-calls/_js.view/solution.js similarity index 100% rename from 1-js/4-object-basics/03-object-methods/8-chain-calls/_js.view/solution.js rename to 1-js/4-object-basics/04-object-methods/8-chain-calls/_js.view/solution.js diff --git a/1-js/4-object-basics/03-object-methods/8-chain-calls/_js.view/test.js b/1-js/4-object-basics/04-object-methods/8-chain-calls/_js.view/test.js similarity index 100% rename from 1-js/4-object-basics/03-object-methods/8-chain-calls/_js.view/test.js rename to 1-js/4-object-basics/04-object-methods/8-chain-calls/_js.view/test.js diff --git a/1-js/4-object-basics/03-object-methods/8-chain-calls/solution.md b/1-js/4-object-basics/04-object-methods/8-chain-calls/solution.md similarity index 100% rename from 1-js/4-object-basics/03-object-methods/8-chain-calls/solution.md rename to 1-js/4-object-basics/04-object-methods/8-chain-calls/solution.md diff --git a/1-js/4-object-basics/03-object-methods/8-chain-calls/task.md b/1-js/4-object-basics/04-object-methods/8-chain-calls/task.md similarity index 100% rename from 1-js/4-object-basics/03-object-methods/8-chain-calls/task.md rename to 1-js/4-object-basics/04-object-methods/8-chain-calls/task.md diff --git a/1-js/4-object-basics/03-object-methods/article.md b/1-js/4-object-basics/04-object-methods/article.md similarity index 94% rename from 1-js/4-object-basics/03-object-methods/article.md rename to 1-js/4-object-basics/04-object-methods/article.md index f6b666f6..eb7bc097 100644 --- a/1-js/4-object-basics/03-object-methods/article.md +++ b/1-js/4-object-basics/04-object-methods/article.md @@ -9,7 +9,7 @@ let user = { }; ``` -And, in the real world, a user can `act`: to select something from the shopping cart, to login, to logout etc. +And, in the real world, a user can `act`: to select something from the shopping cart, to login, to logout etc. Let's implement the same in Javascript using functions in properties. @@ -65,22 +65,22 @@ user.sayHi(); // Hello! That would also work, but is longer. Also we get an "extra" function `sayHi` outside of the `user` object. Usually we don't want that. ```smart header="Object-oriented programming" -When we write our code using objects to represent entities, that's called an [object-oriented programming](https://en.wikipedia.org/wiki/Object-oriented_programming), in short: "OOP". +When we write our code using objects to represent entities, that's called an [object-oriented programming](https://en.wikipedia.org/wiki/Object-oriented_programming), in short: "OOP". -OOP is a big thing, an interesting science of its own. How to choose the right entities? How to organize the interaction between them? That's architecture. +OOP is a big thing, an interesting science of its own. How to choose the right entities? How to organize the interaction between them? That's architecture. ``` ### Method shorthand There exists a shorter syntax for methods in an object literal: -```js +```js // these objects do the same let user = { - sayHi: function() { + sayHi: function() { alert("Hello"); } -}; +}; // method shorthand looks better, right? let user = { @@ -89,10 +89,10 @@ let user = { */!* alert("Hello"); } -}; +}; ``` -As demonstrated, we can omit `"function"` and just write `sayHi()`. +As demonstrated, we can omit `"function"` and just write `sayHi()`. To say the truth, the notations are not fully identical. There are subtle differences related to object inheritance (to be covered later), but for now they do not matter. In almost all cases the shorter syntax is preferred. @@ -128,10 +128,10 @@ Here during the execution of `user.sayHi()`, the value of `this` will be `user`. Technically, it's also possible to access the object without `this`: -```js +```js ... sayHi() { - alert( *!*user.name*/!* ); + alert( *!*user.name*/!* ); } ... ``` @@ -164,14 +164,14 @@ If we used `this.name` instead of `user.name` inside the `alert`, then the code ## "this" is not bound -In Javascript, "this" keyword behaves unlike most other programming languages. First, it can be used in any function. +In Javascript, "this" keyword behaves unlike most other programming languages. First, it can be used in any function. There's no syntax error in the code like that: ```js function sayHi() { alert( *!*this*/!*.name ); -} +} ``` The value of `this` is evaluated during the run-time. And it can be anything. @@ -210,16 +210,16 @@ function sayHi() { sayHi(); ``` -In this case `this` is `undefined` in strict mode. If we try to access `this.name`, there will be an error. +In this case `this` is `undefined` in strict mode. If we try to access `this.name`, there will be an error. -In non-strict mode (if you forgot `use strict`) the value of `this` in such case will be the *global object* (`"window"` for browser, we'll study it later). This is just a historical thing that `"use strict"` fixes. +In non-strict mode (if you forgot `use strict`) the value of `this` in such case will be the *global object* (`"window"` for browser, we'll study it later). This is just a historical thing that `"use strict"` fixes. Please note that usually a call of a function using `this` without an object is not normal, but rather a programming mistake. If a function has `this`, then it is usually meant to be called in the context of an object. ```smart header="The consequences of unbound `this`" If you come from another programming languages, then you are probably used to an idea of a "bound `this`", where methods defined in an object always have `this` referencing that object. -The idea of unbound, run-time evaluated `this` has both pluses and minuses. From one side, a function can be reused for different objects. From the other side, it's possible to occasionally loose `this` by making an improper call. +The idea of unbound, run-time evaluated `this` has both pluses and minuses. From one side, a function can be reused for different objects. From the other side, greater flexibility opens a place for mistakes. Here we are not to judge whether this language design decision is good or bad. We will understand how to work with it, how to get benefits and evade problems. ``` diff --git a/1-js/4-object-basics/07-object-toprimitive/article.md b/1-js/4-object-basics/05-object-toprimitive/article.md similarity index 98% rename from 1-js/4-object-basics/07-object-toprimitive/article.md rename to 1-js/4-object-basics/05-object-toprimitive/article.md index ff36a530..0741cc26 100644 --- a/1-js/4-object-basics/07-object-toprimitive/article.md +++ b/1-js/4-object-basics/05-object-toprimitive/article.md @@ -3,7 +3,7 @@ In the chapter we've seen the rules for numeric, string and boolean conversions of primitives. -But we left a gap for objects. Now let's fill it. +But we left a gap for objects. Now let's close it. And, in the process, we'll see some built-in methods and the example of a built-in symbol. [cut] diff --git a/1-js/4-object-basics/05-constructor-new/1-two-functions-one-object/solution.md b/1-js/4-object-basics/07-constructor-new/1-two-functions-one-object/solution.md similarity index 100% rename from 1-js/4-object-basics/05-constructor-new/1-two-functions-one-object/solution.md rename to 1-js/4-object-basics/07-constructor-new/1-two-functions-one-object/solution.md diff --git a/1-js/4-object-basics/05-constructor-new/1-two-functions-one-object/task.md b/1-js/4-object-basics/07-constructor-new/1-two-functions-one-object/task.md similarity index 100% rename from 1-js/4-object-basics/05-constructor-new/1-two-functions-one-object/task.md rename to 1-js/4-object-basics/07-constructor-new/1-two-functions-one-object/task.md diff --git a/1-js/4-object-basics/05-constructor-new/2-calculator-constructor/_js.view/solution.js b/1-js/4-object-basics/07-constructor-new/2-calculator-constructor/_js.view/solution.js similarity index 100% rename from 1-js/4-object-basics/05-constructor-new/2-calculator-constructor/_js.view/solution.js rename to 1-js/4-object-basics/07-constructor-new/2-calculator-constructor/_js.view/solution.js diff --git a/1-js/4-object-basics/05-constructor-new/2-calculator-constructor/_js.view/test.js b/1-js/4-object-basics/07-constructor-new/2-calculator-constructor/_js.view/test.js similarity index 100% rename from 1-js/4-object-basics/05-constructor-new/2-calculator-constructor/_js.view/test.js rename to 1-js/4-object-basics/07-constructor-new/2-calculator-constructor/_js.view/test.js diff --git a/1-js/4-object-basics/05-constructor-new/2-calculator-constructor/solution.md b/1-js/4-object-basics/07-constructor-new/2-calculator-constructor/solution.md similarity index 100% rename from 1-js/4-object-basics/05-constructor-new/2-calculator-constructor/solution.md rename to 1-js/4-object-basics/07-constructor-new/2-calculator-constructor/solution.md diff --git a/1-js/4-object-basics/05-constructor-new/2-calculator-constructor/task.md b/1-js/4-object-basics/07-constructor-new/2-calculator-constructor/task.md similarity index 100% rename from 1-js/4-object-basics/05-constructor-new/2-calculator-constructor/task.md rename to 1-js/4-object-basics/07-constructor-new/2-calculator-constructor/task.md diff --git a/1-js/4-object-basics/05-constructor-new/3-accumulator/_js.view/solution.js b/1-js/4-object-basics/07-constructor-new/3-accumulator/_js.view/solution.js similarity index 100% rename from 1-js/4-object-basics/05-constructor-new/3-accumulator/_js.view/solution.js rename to 1-js/4-object-basics/07-constructor-new/3-accumulator/_js.view/solution.js diff --git a/1-js/4-object-basics/05-constructor-new/3-accumulator/_js.view/test.js b/1-js/4-object-basics/07-constructor-new/3-accumulator/_js.view/test.js similarity index 100% rename from 1-js/4-object-basics/05-constructor-new/3-accumulator/_js.view/test.js rename to 1-js/4-object-basics/07-constructor-new/3-accumulator/_js.view/test.js diff --git a/1-js/4-object-basics/05-constructor-new/3-accumulator/solution.md b/1-js/4-object-basics/07-constructor-new/3-accumulator/solution.md similarity index 100% rename from 1-js/4-object-basics/05-constructor-new/3-accumulator/solution.md rename to 1-js/4-object-basics/07-constructor-new/3-accumulator/solution.md diff --git a/1-js/4-object-basics/05-constructor-new/3-accumulator/task.md b/1-js/4-object-basics/07-constructor-new/3-accumulator/task.md similarity index 100% rename from 1-js/4-object-basics/05-constructor-new/3-accumulator/task.md rename to 1-js/4-object-basics/07-constructor-new/3-accumulator/task.md diff --git a/1-js/4-object-basics/05-constructor-new/4-calculator-extendable/_js.view/solution.js b/1-js/4-object-basics/07-constructor-new/4-calculator-extendable/_js.view/solution.js similarity index 100% rename from 1-js/4-object-basics/05-constructor-new/4-calculator-extendable/_js.view/solution.js rename to 1-js/4-object-basics/07-constructor-new/4-calculator-extendable/_js.view/solution.js diff --git a/1-js/4-object-basics/05-constructor-new/4-calculator-extendable/_js.view/test.js b/1-js/4-object-basics/07-constructor-new/4-calculator-extendable/_js.view/test.js similarity index 100% rename from 1-js/4-object-basics/05-constructor-new/4-calculator-extendable/_js.view/test.js rename to 1-js/4-object-basics/07-constructor-new/4-calculator-extendable/_js.view/test.js diff --git a/1-js/4-object-basics/05-constructor-new/4-calculator-extendable/solution.md b/1-js/4-object-basics/07-constructor-new/4-calculator-extendable/solution.md similarity index 100% rename from 1-js/4-object-basics/05-constructor-new/4-calculator-extendable/solution.md rename to 1-js/4-object-basics/07-constructor-new/4-calculator-extendable/solution.md diff --git a/1-js/4-object-basics/05-constructor-new/4-calculator-extendable/task.md b/1-js/4-object-basics/07-constructor-new/4-calculator-extendable/task.md similarity index 100% rename from 1-js/4-object-basics/05-constructor-new/4-calculator-extendable/task.md rename to 1-js/4-object-basics/07-constructor-new/4-calculator-extendable/task.md diff --git a/1-js/4-object-basics/05-constructor-new/article.md b/1-js/4-object-basics/07-constructor-new/article.md similarity index 100% rename from 1-js/4-object-basics/05-constructor-new/article.md rename to 1-js/4-object-basics/07-constructor-new/article.md diff --git a/1-js/5-data-types/01-primitives-methods/article.md b/1-js/5-data-types/01-primitives-methods/article.md index a38e14c1..844732ad 100644 --- a/1-js/5-data-types/01-primitives-methods/article.md +++ b/1-js/5-data-types/01-primitives-methods/article.md @@ -1,6 +1,8 @@ # Methods of primitives -JavaScript allows to work with primitives (strings, numbers etc) as if they were objects. They also have methods and such. Of course, primitives are not objects (and here we plan to make it even more clear), but can be used like them. +JavaScript allows to work with primitives (strings, numbers etc) as if they were objects. + +They also provide methods to call and such. We are going to study them soon, but first let's see how it works, because, of course, primitives are not objects (and here we plan to make it even more clear). [cut] @@ -10,13 +12,13 @@ A primitive : Is a value of a primitive type. There are 6 primitive types: `string`, `number`, `boolean`, `symbol`, `null` and `undefined`. An object -: Is capable of storing multiple values as properties. +: Is capable of storing multiple values as properties. Can be created with `{}`, for instance: `{name: "John", age: 30}`. There are other kinds of objects in JavaScript, e.g. functions are objects. One of the best thing about objects is that we can store a function as one of properties: ```js run -let john = { +let john = { name: "John", sayHi: function() { alert("Hi buddy!"); @@ -45,7 +47,7 @@ The solution looks a little bit awkward, but here it is. 1. Primitives are still primitive. A single value, as desired. 2. The language allows to access methods and properties of strings, numbers, booleans and symbols. -3. When it happens, a special "object wrapper" is created that provides the functionality and then is destroyed. +3. When it happens, a special "object wrapper" is created that provides the functionality and then is destroyed. The "object wrappers" are different for each primitive type and are named specifically: `String`, `Number`, `Boolean` and `Symbol`. Thus they provide different sets of methods. @@ -77,7 +79,7 @@ let n = 1.23456; alert( n.toFixed(2) ); // 1.23 ``` -We'll see more specific methods in chapters and . +We'll see more specific methods in chapters and . ````warn header="null/undefined have no methods" Special primitives `null` and `undefined` are exceptions. They have no corresponding "wrapper objects" and provide no methods. In a sense, they are "the most primitive". @@ -92,4 +94,3 @@ alert(null.test); // error - Primitives except `null` and `undefined` provide many helpful methods. We plan to study those in the next chapters. - Formally, these methods work via temporary objects, but JavaScript engines are very well tuned to optimize that internally, so they are not expensive to call. - diff --git a/1-js/5-data-types/03-string/article.md b/1-js/5-data-types/03-string/article.md index d5e579f5..68c22805 100644 --- a/1-js/5-data-types/03-string/article.md +++ b/1-js/5-data-types/03-string/article.md @@ -1,4 +1,4 @@ -# Strings +# Strings In JavaScript, the textual data is stored as strings. There is no separate type for a single character. @@ -62,7 +62,7 @@ let guestList = "Guests:\n * John\n * Pete\n * Mary"; alert(guestList); // a multiline list of guests ``` -So to speak, these two lines describe the same: +So to speak, these two lines describe the same: ```js run alert( "Hello\nWorld" ); // two lines using a "newline symbol" @@ -92,7 +92,7 @@ alert( "\u{20331}" ); // 𠌱, a rare chinese hieroglyph (long unicode) alert( "\u{1F60D}"); // a smiling face sumbol (another long unicode) ``` -All special characters start with a backslash character `\`. It is also called an "escaping character". +All special characters start with a backslash character `\`. It is also called an "escaping character". We should also use it if we want to insert the quote into the string. @@ -107,7 +107,7 @@ See, we have to prepend the inner quote by the backslash `\'`, because otherwise Of course, that refers only for the quotes that are same as the enclosing ones. So, as a more elegant solution, we could switch to double quotes or backticks instead: ```js run -alert( `I'm the Walrus!` ); // I'm the Walrus! +alert( `I'm the Walrus!` ); // I'm the Walrus! ``` Note that the backslash `\` serves for the correct reading of the string by Javascript, then disappears. The in-memory string has no `\`. You can clearly see that in `alert` from the examples above. @@ -123,7 +123,7 @@ alert( `The backslash: \\` ); // The backslash: \ ## String length -The `length` property has the string length: +The `length` property has the string length: ```js run alert( `My\n`.length ); // 3 @@ -134,7 +134,7 @@ Note that `\n` is a single "special" character, so the length is indeed `3`. ```warn header="`length` is a property" People with background in some other languages sometimes mistype by calling `str.length()` instead of just `str.length`. That doesn't work. -Please note that `str.length` is a numeric property, not a function. There is no need to add brackets after it. +Please note that `str.length` is a numeric property, not a function. There is no need to add brackets after it. ``` ## Accessing characters @@ -173,7 +173,7 @@ for(let char of "Hello") { ## Strings are immutable -Strings can't be changed in JavaScript. It is impossible to change a character. +Strings can't be changed in JavaScript. It is impossible to change a character. Let's try to see that it doesn't work: @@ -219,7 +219,7 @@ There are multiple ways to look for a substring in a string. ### str.indexOf -The first method is [str.indexOf(substr, pos)](mdn:js/String/indexOf). +The first method is [str.indexOf(substr, pos)](mdn:js/String/indexOf). It looks for the `substr` in `str`, starting from the given position `pos`, and returns the position where the match was found or `-1` if nothing found. @@ -249,7 +249,7 @@ If we're interested in all occurences, we can run `indexOf` in a loop. Every new ```js run -let str = 'As sly as a fox, as strong as an ox'; +let str = 'As sly as a fox, as strong as an ox'; let target = 'as'; // let's look for it @@ -258,7 +258,7 @@ while (true) { let foundPos = str.indexOf(target, pos); if (foundPos == -1) break; - alert( `Found at ${foundPos}` ); + alert( `Found at ${foundPos}` ); pos = foundPos + 1; // continue the search from the next position } ``` @@ -302,7 +302,7 @@ let str = "Widget with id"; *!* if (str.indexOf("Widget") != -1) { -*/!* +*/!* alert("We found it"); // works now! } ``` @@ -310,7 +310,7 @@ if (str.indexOf("Widget") != -1) { ````smart header="The bitwise NOT trick" One of the old tricks used here is the [bitwise NOT](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Bitwise_NOT) `~` operator. It converts the number to 32-bit integer (removes the decimal part if exists) and then reverses all bits in its binary representation. -For 32-bit integers the call `~n` means exactly the same as `-(n+1)` (due to IEEE-754 format). +For 32-bit integers the call `~n` means exactly the same as `-(n+1)` (due to IEEE-754 format). For instance: @@ -344,7 +344,7 @@ Just remember: `if (~str.indexOf(...))` reads as "if found". ### includes, startsWith, endsWith -The more modern method [str.includes(substr, pos)](mdn:js/String/includes) returns `true/false` depending on whether `str` has `substr` as its part. +The more modern method [str.includes(substr, pos)](mdn:js/String/includes) returns `true/false` depending on whether `str` has `substr` as its part. It's the right choice if we need to test for the match, but don't need its position: @@ -373,7 +373,7 @@ alert( "Widget".endsWith("get") ); // true, "Widget" ends with "get" There are 3 methods in JavaScript to get a substring: `substring`, `substr` and `slice`. `str.slice(start [, end])` -: Returns the part of the string from `start` to (but not including) `end`. +: Returns the part of the string from `start` to (but not including) `end`. For instance: @@ -398,12 +398,12 @@ There are 3 methods in JavaScript to get a substring: `substring`, `substr` and // start at the 4th position from the right, end at the 1st from the right alert( str.slice(-4, -1) ); // gif ``` - + `str.substring(start [, end])` : Returns the part of the string *between* `start` and `end`. - Almost the same as `slice`, but allows `start` to be greater than `end`. + Almost the same as `slice`, but allows `start` to be greater than `end`. For instance: @@ -421,7 +421,7 @@ There are 3 methods in JavaScript to get a substring: `substring`, `substr` and ``` - Negative arguments are (unlike slice) not supported, they are treated as `0`. + Negative arguments are (unlike slice) not supported, they are treated as `0`. `str.substr(start [, length])` @@ -511,14 +511,14 @@ let str = ''; for (let i = 65; i <= 220; i++) { str += String.fromCodePoint(i); } -alert( str ); +alert( str ); // ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„ // ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜ ``` See? Capital character go first, then few special ones, then lowercase characters. -Now it becomes obvious why `a > Z`. +Now it becomes obvious why `a > Z`. The characters are compared by their numeric code. The greater code means that the character is greater. The code for `a` (97) is greater than the code for `Z` (90). @@ -548,21 +548,21 @@ For instance: alert( 'Österreich'.localeCompare('Zealand') ); // -1 ``` -The method actually has two additional arguments specified in [the documentation](mdn:js/String/localeCompare), that allow to specify the language (by default taken from the environment) and setup additional rules like case sensivity or should `"a"` and `"á"` be treated as the same etc. +The method actually has two additional arguments specified in [the documentation](mdn:js/String/localeCompare), that allow to specify the language (by default taken from the environment) and setup additional rules like case sensivity or should `"a"` and `"á"` be treated as the same etc. ## Internals, Unicode ```warn header="Advanced knowledge" -The section goes deeper into string internals. The knowledge will be useful for you if you plan to deal with emoji, rare mathematical of hieroglyphs characters or other rare symbols. +The section goes deeper into string internals. The knowledge will be useful for you if you plan to deal with emoji, rare mathematical of hieroglyphs characters or other rare symbols. You can skip the section if you don't plan to support them. ``` ### Surrogate pairs -Most symbols have a 2-byte code. Letters of most european languages, numbers, even most hieroglyphs have a 2-byte representation. +Most symbols have a 2-byte code. Letters of most european languages, numbers, even most hieroglyphs have a 2-byte representation. -But 2 bytes only allow 65536 combinations that's not enough for every possible symbol. So rare symbols are encoded with a pair of 2-byte characters called "a surrogate pair". +But 2 bytes only allow 65536 combinations that's not enough for every possible symbol. So rare symbols are encoded with a pair of 2-byte characters called "a surrogate pair". The length of such symbols is `2`: @@ -574,7 +574,7 @@ alert( '𩷶'.length ); // 2, a rare chinese hieroglyph Note that surrogate pairs did not exist at the time when Javascript was created, and thus are not correctly processed by the language! -We actually have a single symbol in each of the strings above, but the `length` shows the length of `2`. +We actually have a single symbol in each of the strings above, but the `length` shows the length of `2`. `String.fromCodePoint` and `str.codePointAt` are few rare methods that deal with surrogate pairs right. They recently appeared in the language. Before them, there were only [String.fromCharCode](mdn:js/String/fromCharCode) and [str.charCodeAt](mdn:js/String/charCodeAt). These methods are actually the same as `fromCodePoint/codePointAt`, but don't work with surrogate pairs. @@ -651,7 +651,7 @@ alert( "S\u0307\u0323".normalize().length ); // 1 alert( "S\u0307\u0323".normalize() == "\u1e68" ); // true ``` -In real, that is not always so. The reason is that symbol `Ṩ` is "common enough", so UTF-16 creators included it into the main table and gave it the code. +In real, that is not always so. The reason is that symbol `Ṩ` is "common enough", so UTF-16 creators included it into the main table and gave it the code. If you want to learn more about normalization rules and variants -- they are described in the appendix to the Unicode standard: [Unicode Normalization Forms](http://www.unicode.org/reports/tr15/), but for most practical reasons the information from this section is enough. @@ -659,7 +659,7 @@ If you want to learn more about normalization rules and variants -- they are des ## Summary - There are 3 types of quotes. Backticks allow a string to span multiple lines and embed expressions. -- Strings in JavaScript are encoded using UTF-16. +- Strings in JavaScript are encoded using UTF-16. - We can use special characters like `\n` and insert letters by their unicode using `\u...`. - To get a character: use `[]`. - To get a substring: use `slice` or `substring`. @@ -669,9 +669,8 @@ If you want to learn more about normalization rules and variants -- they are des There are several other helpful methods in strings: -- [str.trim()]` -- removes ("trims") spaces from the beginning and end of the string. -- [str.repeat(n)]` -- repeats the string `n` times. +- `str.trim()` -- removes ("trims") spaces from the beginning and end of the string. +- `str.repeat(n)` -- repeats the string `n` times. - ...and others, see the [manual](mdn:js/String) for details. Also strings have methods for doing search/replace with regular expressions. But that topic deserves a separate chapter, so we'll return to that later. - diff --git a/1-js/5-data-types/05-array/1-item-value/solution.md b/1-js/5-data-types/04-array/1-item-value/solution.md similarity index 100% rename from 1-js/5-data-types/05-array/1-item-value/solution.md rename to 1-js/5-data-types/04-array/1-item-value/solution.md diff --git a/1-js/5-data-types/05-array/1-item-value/task.md b/1-js/5-data-types/04-array/1-item-value/task.md similarity index 100% rename from 1-js/5-data-types/05-array/1-item-value/task.md rename to 1-js/5-data-types/04-array/1-item-value/task.md diff --git a/1-js/5-data-types/05-array/10-maximal-subarray/_js.view/solution.js b/1-js/5-data-types/04-array/10-maximal-subarray/_js.view/solution.js similarity index 100% rename from 1-js/5-data-types/05-array/10-maximal-subarray/_js.view/solution.js rename to 1-js/5-data-types/04-array/10-maximal-subarray/_js.view/solution.js diff --git a/1-js/5-data-types/05-array/10-maximal-subarray/_js.view/test.js b/1-js/5-data-types/04-array/10-maximal-subarray/_js.view/test.js similarity index 100% rename from 1-js/5-data-types/05-array/10-maximal-subarray/_js.view/test.js rename to 1-js/5-data-types/04-array/10-maximal-subarray/_js.view/test.js diff --git a/1-js/5-data-types/05-array/10-maximal-subarray/solution.md b/1-js/5-data-types/04-array/10-maximal-subarray/solution.md similarity index 100% rename from 1-js/5-data-types/05-array/10-maximal-subarray/solution.md rename to 1-js/5-data-types/04-array/10-maximal-subarray/solution.md diff --git a/1-js/5-data-types/05-array/10-maximal-subarray/task.md b/1-js/5-data-types/04-array/10-maximal-subarray/task.md similarity index 100% rename from 1-js/5-data-types/05-array/10-maximal-subarray/task.md rename to 1-js/5-data-types/04-array/10-maximal-subarray/task.md diff --git a/1-js/5-data-types/05-array/2-create-array/solution.md b/1-js/5-data-types/04-array/2-create-array/solution.md similarity index 100% rename from 1-js/5-data-types/05-array/2-create-array/solution.md rename to 1-js/5-data-types/04-array/2-create-array/solution.md diff --git a/1-js/5-data-types/05-array/2-create-array/task.md b/1-js/5-data-types/04-array/2-create-array/task.md similarity index 100% rename from 1-js/5-data-types/05-array/2-create-array/task.md rename to 1-js/5-data-types/04-array/2-create-array/task.md diff --git a/1-js/5-data-types/05-array/3-call-array-this/solution.md b/1-js/5-data-types/04-array/3-call-array-this/solution.md similarity index 100% rename from 1-js/5-data-types/05-array/3-call-array-this/solution.md rename to 1-js/5-data-types/04-array/3-call-array-this/solution.md diff --git a/1-js/5-data-types/05-array/3-call-array-this/task.md b/1-js/5-data-types/04-array/3-call-array-this/task.md similarity index 100% rename from 1-js/5-data-types/05-array/3-call-array-this/task.md rename to 1-js/5-data-types/04-array/3-call-array-this/task.md diff --git a/1-js/5-data-types/05-array/5-array-input-sum/solution.md b/1-js/5-data-types/04-array/5-array-input-sum/solution.md similarity index 100% rename from 1-js/5-data-types/05-array/5-array-input-sum/solution.md rename to 1-js/5-data-types/04-array/5-array-input-sum/solution.md diff --git a/1-js/5-data-types/05-array/5-array-input-sum/task.md b/1-js/5-data-types/04-array/5-array-input-sum/task.md similarity index 100% rename from 1-js/5-data-types/05-array/5-array-input-sum/task.md rename to 1-js/5-data-types/04-array/5-array-input-sum/task.md diff --git a/1-js/5-data-types/05-array/array-pop.png b/1-js/5-data-types/04-array/array-pop.png similarity index 100% rename from 1-js/5-data-types/05-array/array-pop.png rename to 1-js/5-data-types/04-array/array-pop.png diff --git a/1-js/5-data-types/05-array/array-pop@2x.png b/1-js/5-data-types/04-array/array-pop@2x.png similarity index 100% rename from 1-js/5-data-types/05-array/array-pop@2x.png rename to 1-js/5-data-types/04-array/array-pop@2x.png diff --git a/1-js/5-data-types/05-array/array-shift.png b/1-js/5-data-types/04-array/array-shift.png similarity index 100% rename from 1-js/5-data-types/05-array/array-shift.png rename to 1-js/5-data-types/04-array/array-shift.png diff --git a/1-js/5-data-types/05-array/array-shift@2x.png b/1-js/5-data-types/04-array/array-shift@2x.png similarity index 100% rename from 1-js/5-data-types/05-array/array-shift@2x.png rename to 1-js/5-data-types/04-array/array-shift@2x.png diff --git a/1-js/5-data-types/05-array/array-speed.png b/1-js/5-data-types/04-array/array-speed.png similarity index 100% rename from 1-js/5-data-types/05-array/array-speed.png rename to 1-js/5-data-types/04-array/array-speed.png diff --git a/1-js/5-data-types/05-array/array-speed@2x.png b/1-js/5-data-types/04-array/array-speed@2x.png similarity index 100% rename from 1-js/5-data-types/05-array/array-speed@2x.png rename to 1-js/5-data-types/04-array/array-speed@2x.png diff --git a/1-js/5-data-types/05-array/article.md b/1-js/5-data-types/04-array/article.md similarity index 97% rename from 1-js/5-data-types/05-array/article.md rename to 1-js/5-data-types/04-array/article.md index dd7a911e..aaf99be8 100644 --- a/1-js/5-data-types/05-array/article.md +++ b/1-js/5-data-types/04-array/article.md @@ -320,9 +320,9 @@ But that's actually a bad idea. There are potential problems with it: 1. The loop `for..in` iterates over *all properties*, not only the numeric ones. - There are so-called "array-like" objects in the browser and in other environments, that *look like arrays*. That is, they have `length` and indexes properties, but they have *other non-numeric properties and methods*, which we usually don't need in the loop. The `for..in` will list them. If we need to work with array-like objects, then these "extra" properties can become a problem. + There are so-called "array-like" objects in the browser and in other environments, that *look like arrays*. That is, they have `length` and indexes properties, but they may also have other non-numeric properties and methods, which we usually don't need. The `for..in` loop will list them though. So if we need to work with array-like objects, then these "extra" properties can become a problem. -2. The `for..in` loop is optimized for generic objects, not arrays, and thus is 10-100 times slower. Of course, it's still very fast. The speedup maybe important in few places bottlenecks or just irrelevant. But still we should be aware of the difference. +2. The `for..in` loop is optimized for generic objects, not arrays, and thus is 10-100 times slower. Of course, it's still very fast. The speedup may matter only in bottlenecks or just irrelevant. But still we should be aware of the difference. Generally, we shouldn't use `for..in` for arrays. diff --git a/1-js/5-data-types/05-array/queue.png b/1-js/5-data-types/04-array/queue.png similarity index 100% rename from 1-js/5-data-types/05-array/queue.png rename to 1-js/5-data-types/04-array/queue.png diff --git a/1-js/5-data-types/05-array/queue@2x.png b/1-js/5-data-types/04-array/queue@2x.png similarity index 100% rename from 1-js/5-data-types/05-array/queue@2x.png rename to 1-js/5-data-types/04-array/queue@2x.png diff --git a/1-js/5-data-types/05-array/stack.png b/1-js/5-data-types/04-array/stack.png similarity index 100% rename from 1-js/5-data-types/05-array/stack.png rename to 1-js/5-data-types/04-array/stack.png diff --git a/1-js/5-data-types/05-array/stack@2x.png b/1-js/5-data-types/04-array/stack@2x.png similarity index 100% rename from 1-js/5-data-types/05-array/stack@2x.png rename to 1-js/5-data-types/04-array/stack@2x.png diff --git a/1-js/5-data-types/04-date/6-get-seconds-today/task.md b/1-js/5-data-types/04-date/6-get-seconds-today/task.md deleted file mode 100644 index c78ff30b..00000000 --- a/1-js/5-data-types/04-date/6-get-seconds-today/task.md +++ /dev/null @@ -1,15 +0,0 @@ -importance: 5 - ---- - -# Сколько секунд уже прошло сегодня? - -Напишите функцию `getSecondsToday()` которая возвращает, сколько секунд прошло с начала сегодняшнего дня. - -Например, если сейчас `10:00` и не было перехода на зимнее/летнее время, то: - -```js -getSecondsToday() == 36000 // (3600 * 10) -``` - -Функция должна работать в любой день, т.е. в ней не должно быть конкретного значения сегодняшней даты. \ No newline at end of file diff --git a/1-js/5-data-types/04-date/8-format-date-relative/_js.view/solution.js b/1-js/5-data-types/04-date/8-format-date-relative/_js.view/solution.js deleted file mode 100644 index 4db4972e..00000000 --- a/1-js/5-data-types/04-date/8-format-date-relative/_js.view/solution.js +++ /dev/null @@ -1,34 +0,0 @@ -function formatDate(date) { - var diff = new Date() - date; // number of ms till now - - if (diff < 1000) { // less than a second - return 'right now'; - } - - var sec = Math.floor(diff / 1000); // get seconds - - if (sec < 60) { - return sec + ' sec. ago'; - } - - var min = Math.floor(diff / 60000); // get minutes - if (min < 60) { - return min + ' min. ago'; - } - - // format the date, take into account that months start from zero - var d = date; - d = [ - '0' + d.getDate(), - '0' + (d.getMonth() + 1), - '' + d.getFullYear(), - '0' + d.getHours(), - '0' + d.getMinutes() - ]; - - for (var i = 0; i < d.length; i++) { - d[i] = d[i].slice(-2); // remove extra zeroes - } - - return d.slice(0, 3).join('.') + ' ' + d.slice(3).join(':'); -} diff --git a/1-js/5-data-types/04-date/8-format-date-relative/solution.md b/1-js/5-data-types/04-date/8-format-date-relative/solution.md deleted file mode 100644 index ae9e8a14..00000000 --- a/1-js/5-data-types/04-date/8-format-date-relative/solution.md +++ /dev/null @@ -1,47 +0,0 @@ -Для того, чтобы узнать время от `date` до текущего момента - используем вычитание дат. - -```js run -function formatDate(date) { - var diff = new Date() - date; // разница в миллисекундах - - if (diff < 1000) { // прошло менее 1 секунды - return 'только что'; - } - - var sec = Math.floor(diff / 1000); // округлить diff до секунд - - if (sec < 60) { - return sec + ' сек. назад'; - } - - var min = Math.floor(diff / 60000); // округлить diff до минут - if (min < 60) { - return min + ' мин. назад'; - } - - // форматировать дату, с учетом того, что месяцы начинаются с 0 - var d = date; - d = [ - '0' + d.getDate(), - '0' + (d.getMonth() + 1), - '' + d.getFullYear(), - '0' + d.getHours(), - '0' + d.getMinutes() - ]; - - for (var i = 0; i < d.length; i++) { - d[i] = d[i].slice(-2); - } - - return d.slice(0, 3).join('.') + ' ' + d.slice(3).join(':'); -} - -alert( formatDate(new Date(new Date - 1)) ); // только что - -alert( formatDate(new Date(new Date - 30 * 1000)) ); // 30 сек. назад - -alert( formatDate(new Date(new Date - 5 * 60 * 1000)) ); // 5 мин. назад - -alert( formatDate(new Date(new Date - 86400 * 1000)) ); // вчерашняя дата в формате "дд.мм.гг чч:мм" -``` - diff --git a/1-js/5-data-types/06-array-methods/1-camelcase/_js.view/solution.js b/1-js/5-data-types/05-array-methods/1-camelcase/_js.view/solution.js similarity index 100% rename from 1-js/5-data-types/06-array-methods/1-camelcase/_js.view/solution.js rename to 1-js/5-data-types/05-array-methods/1-camelcase/_js.view/solution.js diff --git a/1-js/5-data-types/06-array-methods/1-camelcase/_js.view/test.js b/1-js/5-data-types/05-array-methods/1-camelcase/_js.view/test.js similarity index 100% rename from 1-js/5-data-types/06-array-methods/1-camelcase/_js.view/test.js rename to 1-js/5-data-types/05-array-methods/1-camelcase/_js.view/test.js diff --git a/1-js/5-data-types/06-array-methods/1-camelcase/solution.md b/1-js/5-data-types/05-array-methods/1-camelcase/solution.md similarity index 100% rename from 1-js/5-data-types/06-array-methods/1-camelcase/solution.md rename to 1-js/5-data-types/05-array-methods/1-camelcase/solution.md diff --git a/1-js/5-data-types/06-array-methods/1-camelcase/task.md b/1-js/5-data-types/05-array-methods/1-camelcase/task.md similarity index 100% rename from 1-js/5-data-types/06-array-methods/1-camelcase/task.md rename to 1-js/5-data-types/05-array-methods/1-camelcase/task.md diff --git a/1-js/5-data-types/06-array-methods/11-array-unique/_js.view/solution.js b/1-js/5-data-types/05-array-methods/11-array-unique/_js.view/solution.js similarity index 100% rename from 1-js/5-data-types/06-array-methods/11-array-unique/_js.view/solution.js rename to 1-js/5-data-types/05-array-methods/11-array-unique/_js.view/solution.js diff --git a/1-js/5-data-types/06-array-methods/11-array-unique/_js.view/test.js b/1-js/5-data-types/05-array-methods/11-array-unique/_js.view/test.js similarity index 100% rename from 1-js/5-data-types/06-array-methods/11-array-unique/_js.view/test.js rename to 1-js/5-data-types/05-array-methods/11-array-unique/_js.view/test.js diff --git a/1-js/5-data-types/06-array-methods/11-array-unique/solution.md b/1-js/5-data-types/05-array-methods/11-array-unique/solution.md similarity index 100% rename from 1-js/5-data-types/06-array-methods/11-array-unique/solution.md rename to 1-js/5-data-types/05-array-methods/11-array-unique/solution.md diff --git a/1-js/5-data-types/06-array-methods/11-array-unique/task.md b/1-js/5-data-types/05-array-methods/11-array-unique/task.md similarity index 100% rename from 1-js/5-data-types/06-array-methods/11-array-unique/task.md rename to 1-js/5-data-types/05-array-methods/11-array-unique/task.md diff --git a/1-js/5-data-types/06-array-methods/2-filter-range/_js.view/solution.js b/1-js/5-data-types/05-array-methods/2-filter-range/_js.view/solution.js similarity index 100% rename from 1-js/5-data-types/06-array-methods/2-filter-range/_js.view/solution.js rename to 1-js/5-data-types/05-array-methods/2-filter-range/_js.view/solution.js diff --git a/1-js/5-data-types/06-array-methods/2-filter-range/_js.view/test.js b/1-js/5-data-types/05-array-methods/2-filter-range/_js.view/test.js similarity index 100% rename from 1-js/5-data-types/06-array-methods/2-filter-range/_js.view/test.js rename to 1-js/5-data-types/05-array-methods/2-filter-range/_js.view/test.js diff --git a/1-js/5-data-types/06-array-methods/2-filter-range/solution.md b/1-js/5-data-types/05-array-methods/2-filter-range/solution.md similarity index 100% rename from 1-js/5-data-types/06-array-methods/2-filter-range/solution.md rename to 1-js/5-data-types/05-array-methods/2-filter-range/solution.md diff --git a/1-js/5-data-types/06-array-methods/2-filter-range/task.md b/1-js/5-data-types/05-array-methods/2-filter-range/task.md similarity index 100% rename from 1-js/5-data-types/06-array-methods/2-filter-range/task.md rename to 1-js/5-data-types/05-array-methods/2-filter-range/task.md diff --git a/1-js/5-data-types/06-array-methods/3-filter-range-in-place/_js.view/solution.js b/1-js/5-data-types/05-array-methods/3-filter-range-in-place/_js.view/solution.js similarity index 100% rename from 1-js/5-data-types/06-array-methods/3-filter-range-in-place/_js.view/solution.js rename to 1-js/5-data-types/05-array-methods/3-filter-range-in-place/_js.view/solution.js diff --git a/1-js/5-data-types/06-array-methods/3-filter-range-in-place/_js.view/test.js b/1-js/5-data-types/05-array-methods/3-filter-range-in-place/_js.view/test.js similarity index 100% rename from 1-js/5-data-types/06-array-methods/3-filter-range-in-place/_js.view/test.js rename to 1-js/5-data-types/05-array-methods/3-filter-range-in-place/_js.view/test.js diff --git a/1-js/5-data-types/06-array-methods/3-filter-range-in-place/solution.md b/1-js/5-data-types/05-array-methods/3-filter-range-in-place/solution.md similarity index 100% rename from 1-js/5-data-types/06-array-methods/3-filter-range-in-place/solution.md rename to 1-js/5-data-types/05-array-methods/3-filter-range-in-place/solution.md diff --git a/1-js/5-data-types/06-array-methods/3-filter-range-in-place/task.md b/1-js/5-data-types/05-array-methods/3-filter-range-in-place/task.md similarity index 100% rename from 1-js/5-data-types/06-array-methods/3-filter-range-in-place/task.md rename to 1-js/5-data-types/05-array-methods/3-filter-range-in-place/task.md diff --git a/1-js/5-data-types/06-array-methods/4-sort-back/solution.md b/1-js/5-data-types/05-array-methods/4-sort-back/solution.md similarity index 100% rename from 1-js/5-data-types/06-array-methods/4-sort-back/solution.md rename to 1-js/5-data-types/05-array-methods/4-sort-back/solution.md diff --git a/1-js/5-data-types/06-array-methods/4-sort-back/task.md b/1-js/5-data-types/05-array-methods/4-sort-back/task.md similarity index 100% rename from 1-js/5-data-types/06-array-methods/4-sort-back/task.md rename to 1-js/5-data-types/05-array-methods/4-sort-back/task.md diff --git a/1-js/5-data-types/06-array-methods/5-copy-sort-array/solution.md b/1-js/5-data-types/05-array-methods/5-copy-sort-array/solution.md similarity index 100% rename from 1-js/5-data-types/06-array-methods/5-copy-sort-array/solution.md rename to 1-js/5-data-types/05-array-methods/5-copy-sort-array/solution.md diff --git a/1-js/5-data-types/06-array-methods/5-copy-sort-array/task.md b/1-js/5-data-types/05-array-methods/5-copy-sort-array/task.md similarity index 100% rename from 1-js/5-data-types/06-array-methods/5-copy-sort-array/task.md rename to 1-js/5-data-types/05-array-methods/5-copy-sort-array/task.md diff --git a/1-js/5-data-types/06-array-methods/6-array-get-names/solution.md b/1-js/5-data-types/05-array-methods/6-array-get-names/solution.md similarity index 100% rename from 1-js/5-data-types/06-array-methods/6-array-get-names/solution.md rename to 1-js/5-data-types/05-array-methods/6-array-get-names/solution.md diff --git a/1-js/5-data-types/06-array-methods/6-array-get-names/task.md b/1-js/5-data-types/05-array-methods/6-array-get-names/task.md similarity index 100% rename from 1-js/5-data-types/06-array-methods/6-array-get-names/task.md rename to 1-js/5-data-types/05-array-methods/6-array-get-names/task.md diff --git a/1-js/5-data-types/06-array-methods/7-map-objects/solution.md b/1-js/5-data-types/05-array-methods/7-map-objects/solution.md similarity index 100% rename from 1-js/5-data-types/06-array-methods/7-map-objects/solution.md rename to 1-js/5-data-types/05-array-methods/7-map-objects/solution.md diff --git a/1-js/5-data-types/06-array-methods/7-map-objects/task.md b/1-js/5-data-types/05-array-methods/7-map-objects/task.md similarity index 100% rename from 1-js/5-data-types/06-array-methods/7-map-objects/task.md rename to 1-js/5-data-types/05-array-methods/7-map-objects/task.md diff --git a/1-js/5-data-types/06-array-methods/8-sort-objects/solution.md b/1-js/5-data-types/05-array-methods/8-sort-objects/solution.md similarity index 100% rename from 1-js/5-data-types/06-array-methods/8-sort-objects/solution.md rename to 1-js/5-data-types/05-array-methods/8-sort-objects/solution.md diff --git a/1-js/5-data-types/06-array-methods/8-sort-objects/task.md b/1-js/5-data-types/05-array-methods/8-sort-objects/task.md similarity index 100% rename from 1-js/5-data-types/06-array-methods/8-sort-objects/task.md rename to 1-js/5-data-types/05-array-methods/8-sort-objects/task.md diff --git a/1-js/5-data-types/06-array-methods/9-average-age/solution.md b/1-js/5-data-types/05-array-methods/9-average-age/solution.md similarity index 100% rename from 1-js/5-data-types/06-array-methods/9-average-age/solution.md rename to 1-js/5-data-types/05-array-methods/9-average-age/solution.md diff --git a/1-js/5-data-types/06-array-methods/9-average-age/task.md b/1-js/5-data-types/05-array-methods/9-average-age/task.md similarity index 100% rename from 1-js/5-data-types/06-array-methods/9-average-age/task.md rename to 1-js/5-data-types/05-array-methods/9-average-age/task.md diff --git a/1-js/5-data-types/06-array-methods/article.md b/1-js/5-data-types/05-array-methods/article.md similarity index 97% rename from 1-js/5-data-types/06-array-methods/article.md rename to 1-js/5-data-types/05-array-methods/article.md index 7f1fa371..d47c9f3d 100644 --- a/1-js/5-data-types/06-array-methods/article.md +++ b/1-js/5-data-types/05-array-methods/article.md @@ -8,10 +8,10 @@ Arrays provide a lot of methods. To make things easier, in this chapter they are We already know methods that add and remove items from the beginning or the end: -- `arr.push(...items)` -- `arr.pop()` -- `arr.shift(...items)` -- `arr.unshift()` +- `arr.push(...items)` -- adds items to the end, +- `arr.pop()` -- extracts an item from the end, +- `arr.shift(...items)` -- adds items to the beginning, +- `arr.unshift()` -- extracts an item from the beginning. Here are few others. @@ -32,7 +32,7 @@ alert( arr[1] ); // undefined alert( arr.length ); // 3 ``` -The element was removed, but the array still has 3 elements, we can see that `arr.length == 3`. +The element was removed, but the array still has 3 elements, we can see that `arr.length == 3`. That's natural, because `delete obj.key` removes a value by the `key`. It's all it does. Fine for objects. But for arrays we usually want the rest of elements to shift and occupy the freed place. We expect to have a shorter array now. @@ -330,7 +330,7 @@ alert(lengths); // 5,7,6 ### sort(fn) -The method [arr.sort](mdn:js/Array/sort) sorts the array *at place*. +The method [arr.sort](mdn:js/Array/sort) sorts the array *at place*. For instance: @@ -351,7 +351,7 @@ The order became `1, 15, 2`. Incorrect. But why? Literally, all elements are converted to strings and then compared. So, the lexicographic ordering is applied and indeed `"2" > "15"`. -To use our own sorting order, we need to supply a function of two arguments as the argument of `arr.sort()`. +To use our own sorting order, we need to supply a function of two arguments as the argument of `arr.sort()`. The function should work like this: ```js @@ -398,7 +398,7 @@ The algorithm may compare an element multiple times in the process, but it tries ````smart header="A comparison function may return any number" -Actually, a comparison function is only required to return a positive number to say "greater" and a negative number to say "less". +Actually, a comparison function is only required to return a positive number to say "greater" and a negative number to say "less". That allows to write shorter functions: @@ -414,7 +414,7 @@ alert(arr); // *!*1, 2, 15*/!* ````smart header="Arrow functions for the best" Remember [arrow functions](info:function-expression#arrow-functions)? We can use them here for neater sorting: -```js +```js arr.sort( (a, b) => a - b ); ``` @@ -489,7 +489,7 @@ When we need to iterate over an array -- we can use `forEach`. When we need to iterate and return the data for each element -- we can use `map`. -The methods [arr.reduce](mdn:js/Array/reduce) and [arr.reduceRight](mdn:js/Array/reduceRight) also belong to that breed, but are a little bit more intricate. They are used to calculate a single value based on the array. +The methods [arr.reduce](mdn:js/Array/reduce) and [arr.reduceRight](mdn:js/Array/reduceRight) also belong to that breed, but are a little bit more intricate. They are used to calculate a single value based on the array. The syntax is: @@ -552,14 +552,14 @@ We also can omit the initial value: let arr = [1, 2, 3, 4, 5]; // removed initial value from reduce (no 0) -let result = arr.reduce((sum, current) => sum + current); +let result = arr.reduce((sum, current) => sum + current); alert( result ); // 15 ``` The result is the same. That's because if there's no initial, then `reduce` takes the first element of the array as the initial value and starts the iteration from the 2nd element. -The calculation table is the same as above, minus the first row. +The calculation table is the same as above, minus the first row. But such use requires an extreme care. If the array is empty, then `reduce` call without initial value gives an error. So it's generally advised to specify the initial value. @@ -567,7 +567,7 @@ The method [arr.reduceRight](mdn:js/Array/reduceRight) does the same, but goes f -## Iterate: forEach +## Iterate: forEach The [arr.forEach](mdn:js/Array/forEach) method allows to run a function for every element of the array. @@ -594,10 +594,10 @@ And this code is more elaborate about their positions in the target array: ``` The result of the function (if it returns any) is thrown away and ignored. - + ## Array.isArray -Arrays do not form a separate language type. They are based on objects. +Arrays do not form a separate language type. They are based on objects. So `typeof` does not help to distinguish a plain object from an array: @@ -614,6 +614,9 @@ alert(Array.isArray({})); // false alert(Array.isArray([])); // true ``` +```smart header="`Array.isArray` vs other type-checks" +You remembeare other ways to check for + ## Methods: "thisArg" Almost all array methods that call functions -- like `find`, `filter`, `map`, with a notable exception of `sort`, accept an optional additional parameter `thisArg`. diff --git a/1-js/5-data-types/06-array-methods/reduce.png b/1-js/5-data-types/05-array-methods/reduce.png similarity index 100% rename from 1-js/5-data-types/06-array-methods/reduce.png rename to 1-js/5-data-types/05-array-methods/reduce.png diff --git a/1-js/5-data-types/06-array-methods/reduce@2x.png b/1-js/5-data-types/05-array-methods/reduce@2x.png similarity index 100% rename from 1-js/5-data-types/06-array-methods/reduce@2x.png rename to 1-js/5-data-types/05-array-methods/reduce@2x.png diff --git a/1-js/5-data-types/07-iterable/article.md b/1-js/5-data-types/06-iterable/article.md similarity index 68% rename from 1-js/5-data-types/07-iterable/article.md rename to 1-js/5-data-types/06-iterable/article.md index de259815..a29e29fb 100644 --- a/1-js/5-data-types/07-iterable/article.md +++ b/1-js/5-data-types/06-iterable/article.md @@ -1,11 +1,11 @@ # Iterables -*Iterable* objects is a general concept that allows to make any object useable in a `for..of` loop. +*Iterable* objects is a generalization of arrays. That's a concept that allows to make any object useable in a `for..of` loop. -Many built-ins are partial cases of this concept. For instance, arrays are iterable. But not only arrays. Strings are iterable too. +Arrays by themselves are iterable. But not only arrays. Strings are iterable too, and many other built-in objects as well. -Iterables come from the very core of Javascript and are widely used both in built-in methods and those provided by the environment. +Iterables are widely used by the core Javascript, as we'll see many operators and built-in methods rely on them. [cut] @@ -29,7 +29,7 @@ let range = { To make the `range` iterable (and thus let `for..of` work) we need to add a method to the object named `Symbol.iterator` (a special built-in symbol just for that). -- When `for..of` starts, it calls that method (or errors if none found). +- When `for..of` starts, it calls that method (or errors if none found). - The method must return an *iterator* -- an object with the method `next`. - When `for..of` wants the next value, it calls `next()` on that object. - The result of `next()` must have the form `{done: Boolean, value: any}`, where `done=true` means that the iteration is finished, otherwise `value` must be the new value. @@ -47,13 +47,13 @@ range[Symbol.iterator] = function() { // 2. ...it returns the iterator: return { - current: this.from, // remember "from" and "to" of the range - last: this.to, // in object properties + current: this.from, // start at "range.from", + last: this.to, // end at "range.to" - // 3. next() is called on each iteration of the loop + // 3. next() is called on each iteration by for..of next() { if (this.current <= this.last) { - // 4. iterator returns the value as an object {done:.., value :...} + // 4. it should return the value as an object {done:.., value :...} return { done: false, value: this.current++ }; } else { return { done: true }; @@ -67,37 +67,51 @@ for (let num of range) { } ``` -There is an important separation of concerns in this code. +There is an important separation of concerns in this code: - The `range` itself does not have the `next()` method. -- Instead, another object, a so-called "iterator" is created by the call to `range[Symbol.iterator]()`. -- It keeps the iteration state in its `current` property. That's good, because the original object is not modified by iterations. Also multiple `for..of` loops over the same object can run simultaneously, because they create separate iterators. +- Instead, another object, a so-called "iterator" is created by the call to `range[Symbol.iterator]()`, and it handles the iteration. -The fact that the object itself does not do the iteration also adds flexibility, because `range[Symbol.iterator]` can create iterators the smart way, depending on other object properties or external conditions. It's a full-fledged function that may be more complex than just a `return {...}`. +So, the iterator is separate from the object. -Please note that the internal mechanics is not seen from outside. Here `for..of` calls `range[Symbol.iterator]()` and then the `next()` until `done: false`, but the external code doesn't see that. It only gets values. +Technically, we may merge them and use `range` itself as the iterator, to make the code simpler. + +Like this: + +```js run +let range = { + from: 1, + to: 5, + + [Symbol.iterator]() { + this.current = this.from; + return this; + }, + + next() { + if (this.current <= this.to) { + return { done: false, value: this.current++ }; + } else { + return { done: true }; + } + } +}; + +for (let num of range) { + alert(num); // 1, then 2, 3, 4, 5 +} +``` + +Now `range[Symbol.iterator]()` returns the `range` object itself, and it has the necessary `next()` method. Sometimes that's fine too. The downside is that now it's impossible to have two `for..of` loops running over the object simultaneously: they'll share the iteration state, because there's only one iterator -- the object itself. ```smart header="Infinite iterators" Infinite iterators are also doable. For instance, the `range` becomes infinite for `range.to = Infinity`. Or we can make an iterable object that generates an infinite sequence of pseudorandom numbers. Also can be useful. There are no limitations on `next`, it can return more and more values, that's normal. -Of course, the `for..of` loop over such an iterable would be endless, we'll need to stop if, for instance, using `break`. +Of course, the `for..of` loop over such an iterable would be endless. But we can always stop it using `break`. ``` -````smart header="`Symbol.iterator` in a literal" -We could also write `Symbol.iterator` directly in the object literal, via computed properties syntax: - -```js -let range = { - from: 1, - to: 5, - [Symbol.iterator]() { - return {...}; - } -}; -``` -```` ## String is iterable @@ -115,13 +129,16 @@ And it works right with surrogate pairs! ```js run let str = '𝒳😂'; -for(let char of str) { +for(let char of str) { alert(char); // 𝒳, and then 😂 } ``` -````smart header="Calling an iterator manually" -Iterators can also be created explicitly, without `for..of`, with a direct call of `Symbol.iterator`. For built-in objects too. +## Calling an iterator explicitly + +Normally, internals of iterables are hidden from the external code. There's a `for..of` loop, that works, that's all it needs to know. + +But to understand things a little bit more deeper let's see how to create an iterator explicitly. We'll do that same as `for..of`, but with direct calls. For instance, this code gets a string iterator and calls it "manually": @@ -140,17 +157,16 @@ while(true) { } ``` -That is a little bit more flexible than `for..of`, because we can split the iteration process: iterate a bit, then stop, do something else, and then continue later. -```` +That is rarely needed, but gives us more control than `for..of`. For instance, we can split the iteration process: iterate a bit, then stop, do something else, and then resume later. ## Iterables and array-likes [#array-like] -There are two official terms that are similar, but actually very different. Please be careful to avoid the confusion. +There are two official terms that look similar, but are very different. Please be careful to avoid the confusion. -- Iterables are objects that implement the `Symbol.iterator` method, as described above. -- Array-likes are objects that have indexes and `length`, so they look like arrays. +- *Iterables* are objects that implement the `Symbol.iterator` method, as described above. +- *Array-likes* are objects that have indexes and `length`, so they look like arrays. -Sometimes they can both be applied. For instance, strings are both iterable and array-like. +Naturally, they can combine. For instance, strings are both iterable and array-like. But an iterable may be not array-like and vise versa. @@ -171,7 +187,7 @@ for(let item of arrayLike) {} */!* ``` -But what they share in common -- both iterables and array-likes are usually *not arrays*, they don't have `push`, `pop` etc. That's rather inconvenient if we received such object and want to work with it as with an array. +What they share in common -- both iterables and array-likes are usually *not arrays*, they don't have `push`, `pop` etc. That's rather inconvenient if we have such object and want to work with it as with an array. ## Array.from @@ -186,10 +202,16 @@ let arrayLike = { length: 2 }; -let arr = Array.from(arrayLike); +*!* +let arr = Array.from(arrayLike); // (*) +*/!* alert(arr.pop()); // World (method works) ``` +`Array.from` at the line `(*)` takes the object, examines it for being an iterable or array-like, then makes a new array and copies there all items. + +The same happens for an iterable: + ```js // assuming that range is taken from the example above let arr = Array.from(range); @@ -209,9 +231,9 @@ For instance: // assuming that range is taken from the example above // square each number -let arr = Array.from(range, num => num * num); +let arr = Array.from(range, num => num * num); -alert(arr); // 1,4,9,16,25 +alert(arr); // 1,4,9,16,25 ``` We can also use `Array.from` to turn a string into array of characters: @@ -236,7 +258,7 @@ let str = '𝒳😂'; let chars = []; // Array.from internally does the same loop for(let char of str) { - chars.push(char); + chars.push(char); } alert(chars); @@ -277,5 +299,3 @@ Objects that have indexed properties and `length` are called *array-like*. Such If we look inside the specification -- we'll see that most built-in methods assume that they work with iterables or array-likes instead of "real" arrays, because that's more abstract. `Array.from(obj[, mapFn, thisArg])` makes a real `Array` of an iterable or array-like `obj`, and then we can use array methods on it. The optional arguments `mapFn` and `thisArg` allow to apply a function to each item. - - diff --git a/1-js/5-data-types/08-map-set-weakmap-weakset/1-filter-anagrams/_js.view/solution.js b/1-js/5-data-types/07-map-set-weakmap-weakset/01-filter-anagrams/_js.view/solution.js similarity index 100% rename from 1-js/5-data-types/08-map-set-weakmap-weakset/1-filter-anagrams/_js.view/solution.js rename to 1-js/5-data-types/07-map-set-weakmap-weakset/01-filter-anagrams/_js.view/solution.js diff --git a/1-js/5-data-types/08-map-set-weakmap-weakset/1-filter-anagrams/_js.view/test.js b/1-js/5-data-types/07-map-set-weakmap-weakset/01-filter-anagrams/_js.view/test.js similarity index 100% rename from 1-js/5-data-types/08-map-set-weakmap-weakset/1-filter-anagrams/_js.view/test.js rename to 1-js/5-data-types/07-map-set-weakmap-weakset/01-filter-anagrams/_js.view/test.js diff --git a/1-js/5-data-types/08-map-set-weakmap-weakset/1-filter-anagrams/solution.md b/1-js/5-data-types/07-map-set-weakmap-weakset/01-filter-anagrams/solution.md similarity index 100% rename from 1-js/5-data-types/08-map-set-weakmap-weakset/1-filter-anagrams/solution.md rename to 1-js/5-data-types/07-map-set-weakmap-weakset/01-filter-anagrams/solution.md diff --git a/1-js/5-data-types/08-map-set-weakmap-weakset/1-filter-anagrams/task.md b/1-js/5-data-types/07-map-set-weakmap-weakset/01-filter-anagrams/task.md similarity index 100% rename from 1-js/5-data-types/08-map-set-weakmap-weakset/1-filter-anagrams/task.md rename to 1-js/5-data-types/07-map-set-weakmap-weakset/01-filter-anagrams/task.md diff --git a/1-js/5-data-types/07-map-set-weakmap-weakset/02-iterable-keys/solution.md b/1-js/5-data-types/07-map-set-weakmap-weakset/02-iterable-keys/solution.md new file mode 100644 index 00000000..7310d1d3 --- /dev/null +++ b/1-js/5-data-types/07-map-set-weakmap-weakset/02-iterable-keys/solution.md @@ -0,0 +1,19 @@ + +That's because `map.keys()` returns an iterable, but not an array. + +We can convert it into an array using `Array.from`: + + +```js run +let map = new Map(); + +map.set("name", "John"); + +*!* +let keys = Array.from(map.keys()); +*/!* + +keys.push("more"); + +alert(keys); // name, more +``` diff --git a/1-js/5-data-types/07-map-set-weakmap-weakset/02-iterable-keys/task.md b/1-js/5-data-types/07-map-set-weakmap-weakset/02-iterable-keys/task.md new file mode 100644 index 00000000..9a3e5d25 --- /dev/null +++ b/1-js/5-data-types/07-map-set-weakmap-weakset/02-iterable-keys/task.md @@ -0,0 +1,24 @@ +importance: 5 + +--- + +# Iterable keys + +We want to get an array of `map.keys()` and go on working with it (apart from the map itself). + +But there's a problem: + +```js run +let map = new Map(); + +map.set("name", "John"); + +let keys = map.keys(); + +*!* +// Error: numbers.push is not a function +keys.push("more"); +*/!* +``` + +Why? How can we fix the code to make `keys.push` work? diff --git a/1-js/5-data-types/07-map-set-weakmap-weakset/03-recipients-read/solution.md b/1-js/5-data-types/07-map-set-weakmap-weakset/03-recipients-read/solution.md new file mode 100644 index 00000000..ce56f593 --- /dev/null +++ b/1-js/5-data-types/07-map-set-weakmap-weakset/03-recipients-read/solution.md @@ -0,0 +1,41 @@ +The sane choice here is a `WeakSet`: + +```js +let messages = [ + {text: "Hello", from: "John"}, + {text: "How goes?", from: "John"}, + {text: "See you soon", from: "Alice"} +]; + +let readMessages = new WeakSet(); + +// two messages have been read +readMessages.add(messages[0]); +readMessages.add(messages[1]); +// readMessages has 2 elements + +// ...let's read the first message again! +readMessages.add(messages[0]); +// readMessages still has 2 unique elements + +// answer: was the message[0] read? +alert("Read message 0: " + readMessages.has(messages[0])); // true + +messages.shift(); +// now readMessages has 1 element (technically memory may be cleaned later) +``` + +The `WeakSet` allows to store a set of messages and easily check for the existance of a message in it. + +It cleans up itself automatically. The tradeoff is that we can't iterate over it. We can't get "all read messages" directly. But we can do it by iterating over all messages and filtering those that are in the set. + +P.S. Adding a property of our own to each message may be dangerous if messages are managed by someone else's code, but we can make it a symbol to evade conflicts. + +Like this: +```js +// the symbolic property is only known to our code +let isRead = Symbol("isRead"); +messages[0][isRead] = true; +``` + +Now even if someone else's code uses `for..in` loop for message properties, our secret flag won't appear. diff --git a/1-js/5-data-types/07-map-set-weakmap-weakset/03-recipients-read/task.md b/1-js/5-data-types/07-map-set-weakmap-weakset/03-recipients-read/task.md new file mode 100644 index 00000000..7ec1faf1 --- /dev/null +++ b/1-js/5-data-types/07-map-set-weakmap-weakset/03-recipients-read/task.md @@ -0,0 +1,23 @@ +importance: 5 + +--- + +# Store "unread" flags + +There's an array of messages: + +```js +let messages = [ + {text: "Hello", from: "John"}, + {text: "How goes?", from: "John"}, + {text: "See you soon", from: "Alice"} +]; +``` + +Your code can access it, but the messages are managed by someone else's code. New messages are added, old ones are removed regularly by that code, and you don't know the exact moments when it happens. + +Now, which data structure you could use to store information whether the message "have been read"? The structure must be well-suited to give the answer "was it read?" for the given message object. + +P.S. When a message is removed from `messages`, it should disappear from your structure as well. + +P.P.S. We shouldn't modify message objects directly. If they are managed by someone else's code, then adding extra properties to them may have bad consequences. diff --git a/1-js/5-data-types/07-map-set-weakmap-weakset/04-recipients-when-read/solution.md b/1-js/5-data-types/07-map-set-weakmap-weakset/04-recipients-when-read/solution.md new file mode 100644 index 00000000..7f387b4d --- /dev/null +++ b/1-js/5-data-types/07-map-set-weakmap-weakset/04-recipients-when-read/solution.md @@ -0,0 +1,15 @@ + +To store a date, we can use `WeakMap`: + +```js +let messages = [ + {text: "Hello", from: "John"}, + {text: "How goes?", from: "John"}, + {text: "See you soon", from: "Alice"} +]; + +let readMap = new WeakMap(); + +readMap.set(messages[0], new Date(2017, 1, 1)); +// Date object we'll study later +``` diff --git a/1-js/5-data-types/07-map-set-weakmap-weakset/04-recipients-when-read/task.md b/1-js/5-data-types/07-map-set-weakmap-weakset/04-recipients-when-read/task.md new file mode 100644 index 00000000..22b51a38 --- /dev/null +++ b/1-js/5-data-types/07-map-set-weakmap-weakset/04-recipients-when-read/task.md @@ -0,0 +1,19 @@ +importance: 5 + +--- + +# Store read dates + +There's an array of messages as in the [previous task](info:task/recipients-read). The situation is similar. + +```js +let messages = [ + {text: "Hello", from: "John"}, + {text: "How goes?", from: "John"}, + {text: "See you soon", from: "Alice"} +]; +``` + +The question now is: which data structure you'd suggest to store the information: "when the message was read?". + +In the previous task we only needed to store the "yes/no" fact. Now we need to store the date and it, once again, should disappear if the message is gone. diff --git a/1-js/5-data-types/08-map-set-weakmap-weakset/article.md b/1-js/5-data-types/07-map-set-weakmap-weakset/article.md similarity index 66% rename from 1-js/5-data-types/08-map-set-weakmap-weakset/article.md rename to 1-js/5-data-types/07-map-set-weakmap-weakset/article.md index 5219a86c..e70994d2 100644 --- a/1-js/5-data-types/08-map-set-weakmap-weakset/article.md +++ b/1-js/5-data-types/07-map-set-weakmap-weakset/article.md @@ -106,7 +106,9 @@ let map = new Map([ ]); ``` -Note that this is exactly the format of [Object.entries](mdn:js/Object/entries), so we can initialize a map from an object: +There is a built-in method [Object.entries(obj)](mdn:js/Object/entries) that returns the array of key/value pairs for an object exactly in that format. + +So we can initialize a map from an object like this: ```js let map = new Map(Object.entries({ @@ -115,7 +117,7 @@ let map = new Map(Object.entries({ })); ``` -Here, `Object.entries` returns the array of key/value pairs: `[ ["name","John"], ["age", 30] ]`. That's what `Map` needs. +Here, `Object.entries` returns the array of key/value pairs: `[ ["name","John"], ["age", 30] ]`. That's what `Map` needs. ## Iteration over Map @@ -169,7 +171,7 @@ recipeMap.forEach( (value, key, map) => { The main methods are: -- `new Set(iterable)` -- creates the set, optionally from an array of values (any iterable will do). +- `new Set(iterable)` -- creates the set, optionally from an array of values (any iterable will do). - `set.add(value)` -- adds a value, returns the set itself. - `set.delete(value)` -- removes the value, returns `true` if `value` existed at the moment of the call, otherwise `false`. - `set.has(value)` -- returns `true` if the value exists in the set, otherwise `false`. @@ -215,7 +217,7 @@ for(let value of set) alert(value); // the same with forEach: set.forEach((value, valueAgain, set) => { - alert(value); + alert(value); }); ``` @@ -231,9 +233,9 @@ The same methods as `Map` has for iterators are also supported: ## WeakMap and WeakSet -`WeakSet` is a special kind of `Set` that does not prevent JavaScript from memory cleaning. `WeakMap` is the same thing for `Map`. +`WeakSet` is a special kind of `Set` that does not prevent JavaScript from removing its items from memory. `WeakMap` is the same thing for `Map`. -That is: usually the JavaScript engine stores a value in memory while it can potentially be accessed/used. +As we know from the chapter , JavaScript engine stores a value in memory while it is reachable (and can potentially be used). For instance: ```js @@ -244,17 +246,38 @@ let john = { name: "John" }; // overwrite the reference john = null; -// the object will be removed from memory +*!* +// the object will be removed from memory +*/!* ``` -We'll go into more details later, but the example here is somewhat obvious, right? If nothing references the object, it can be safely removed. +Usually, properties of an object or elements of an array or another data structure are considered reachable and kept in memory while that data structure is in memory. -Usually, if an object is in a set or an array or another data structure, and the data structure is in memory, then the object remains in memory too. With the exception of `WeakMap/WeakSet`. +In a regular `Map`, if we store an object as the key or as a value -- does not matter, then it is stored there even if there are no more references to it. + +For instance: +```js +let john = { name: "John" }; + +let map = new Map(); +map.set(john, "..."); + +john = null; // overwrite the reference + +*!* +// john is stored inside the map +// we can get it by using map.keys() +*/!* +``` + + +With the exception of `WeakMap/WeakSet`. **`WeakMap/WeakSet` does not prevent the object removal from the memory.** -For instance, let's start with `WeakMap`. The first difference from `Map` is that its keys can only be objects, not primitive values: +Let's start with `WeakMap`. +The first difference from `Map` is that its keys must be objects, not primitive values: ```js run let weakMap = new WeakMap(); @@ -268,9 +291,46 @@ weakMap.set("test", "wops"); // Error, because "test" is a primitive */!* ``` -So it stores data for objects. And the data only exists while the object exists. That's handy for situations when we have a main storage for the objects somewhere and need to keep additional information that is only relevant while the object lives. +Now, if we use an object as the key in it, and there are no other references to that object -- it will be removed from memory (and from the map) automatically. -Let's see an example. +```js +let john = { name: "John" }; + +let weakMap = new WeakMap(); +weakMap.set(john, "..."); + +john = null; // overwrite the reference + +// john is removed from memory! +``` + +Compare it with the regular `Map` example above. Now if `john` only exists as the key of `WeakMap` -- it is to be automatically deleted. + +...And `WeakMap` does not support methods `keys()`, `values()`, `entries()`, we can not iterate over it. So there's really no way to receive all keys or values from it. + +`WeakMap` has only the following methods: + +- `weakMap.get(key)` +- `weakMap.set(key, value)` +- `weakMap.delete(key, value)` +- `weakMap.has(key)` + +Why such a limitation? That's for technical reasons. If the object has lost all other references (like `john` in the code above), then it is to be deleted automatically. But technically it's not exactly specified *when the cleanup happens*. + +The JavaScript engine decides that. It may choose to perform the memory cleanup immediately or to wait and do the cleaning later when more deletions happen. So, technically the current element count of the `WeakMap` is not known. The engine may have cleaned it up or not, or did it partially. For that reason, methods that access `WeakMap` as a whole are not supported. + +Now where do we need such thing? + +The idea of `WeakMap` is that we can store something for an object that exists only while the object exists. But we do not force the object to live by the mere fact that we store something for it. + +```js +weakMap.put(john, "secret documents"); +// if john dies, secret documents will be destroyed +``` + +That's useful for situations when we have a main storage for the objects somewhere and need to keep additional information that is only relevant while the object lives. + +Let's see an example. For instance, we have a code that keeps a visit count for each user. The information is stored in a map: a user is the key and the visit count is the value. When a user leaves, we don't want to store his visit count any more. @@ -297,7 +357,7 @@ alert( visitsCountMap.size ); // 1 Another way would be to use `WeakMap`: -```js +```js let john = { name: "John" }; let visitsCountMap = new WeakMap(); @@ -307,33 +367,44 @@ visitsCountMap.set(john, 123); // now john leaves us, we don't need him any more john = null; -// there are no references except WeakMap, +// there are no references except WeakMap, // so the object is removed both from the memory and from visitsCountMap automatically ``` -With a regular `Map`, the user deletion becomes a tedious task: we not only need to remove the user from it's main storage (be it a variable or an array), but also need to clean up the additional stores like `visitsCountMap`. And it can become cumbersome in more complex cases when users are managed in one place of the code and the additional structure is at another place and is getting no information about removals. +With a regular `Map`, cleaning up after a user left becomes a tedious task: we not only need to remove the user from its main storage (be it a variable or an array), but also need to clean up the additional stores like `visitsCountMap`. And it can become cumbersome in more complex cases when users are managed in one place of the code and the additional structure is at another place and is getting no information about removals. -`WeakMap` can make things simpler, because it is cleaned up automatically. +`WeakMap` can make things simpler, because it is cleaned up automatically. The information in it like visits count in the example above lives only while the key object exists. -`WeakMap` has only the following methods: +`WeakSet` behaves similarly: -- `weakMap.get(key)` -- `weakMap.set(key, value)` -- `weakMap.delete(key, value)` -- `weakMap.has(key)` +- It is analogous to `Set`, but we may only add objects to `WeakSet` (not primitives). +- An object exists in the set while it has reachable from somewhere else. +- Like `Set`, it supports `add`, `has` and `delete`, but not `size`, `keys()` and no iterations. -Please note `WeakMap` does not support methods `clear()` and has no `size` property. Also we can not iterate over it. +For instance, we can use it to keep track of whether an item is checked: -That's for technical reasons. If the object is to be removed (like `john` in the code above), then it `WeakMap` will be cleaned up automatically. But technically it's not exactly specified *when the cleanup happens*. +```js +let messages = [ + {text: "Hello", from: "John"}, + {text: "How goes?", from: "John"}, + {text: "See you soon", from: "Alice"} +]; -The JavaScript engine decides that. It may choose to perform the memory cleanup immediately or to wait and do the cleaning later when more deletions happen. So, technically the current element count of the `WeakMap` is not known. The engine may have cleaned it up or not, or did it partially. For that reason, the methods that access the `WeakMap` as a whole are not supported. +// fill it with array elements (3 items) +let unreadSet = new WeakSet(messages); -The same refers to `WeakSet`. +// we can use unreadSet to see whether a message is unread +alert(unreadSet.has(messages[1])); // true +// remove it from the set after reading +unreadSet.delete(messages[1]); // true -- It keeps a set of objects, an object exists while it is referenced from anywhere else. -- Like `Set`, it supports `add`, `has` and `delete`, but not `size` and no iterations. +// and when we shift our messages history, the set is cleaned up automatically +messages.shift(); +// no need to clean unreadSet, it now has 2 items +// unfortunately, there's no method to get the exact count of items, so can't show it +``` -The limitations may appear inconvenient, but they actually do not prevent `WeakMap/WeakSet` from doing their main job -- be an "additional" storage of data for objects which are stored/managed at another place. +The most notable limitation of `WeakMap` and `WeakSet` is the absence of iterations, and inability to get all current content. That may appear inconvenient, but actually does not prevent `WeakMap/WeakSet` from doing their main job -- be an "additional" storage of data for objects which are stored/managed at another place. ## Summary @@ -350,7 +421,7 @@ The limitations may appear inconvenient, but they actually do not prevent `WeakM - Unlike an array, does not allow to reorder elements. - Keeps the insertion order. -- `WeakMap` -- a variant of `Map` that allows only objects as keys and removes them once they become unaccessible by other means. +- `WeakMap` -- a variant of `Map` that allows only objects as keys and removes them once they become unaccessible by other means. - It does not support operations on the structure as a whole: no `size`, no `clear()`, no iterations. @@ -359,4 +430,3 @@ The limitations may appear inconvenient, but they actually do not prevent `WeakM - Also does not support `size/clear()` and iterations. `WeakMap` and `WeakSet` are used as "secondary" data structures in additional to the "main" object storage. Once the object is removed from the main storage, so it only stays in `WeakMap/WeakSet`, they clean up aumatically. - diff --git a/1-js/6-more-syntax/3-advanced-loops/5-sum-salaries/_js.view/solution.js b/1-js/5-data-types/08-keys-values-entries/01-sum-salaries/_js.view/solution.js similarity index 100% rename from 1-js/6-more-syntax/3-advanced-loops/5-sum-salaries/_js.view/solution.js rename to 1-js/5-data-types/08-keys-values-entries/01-sum-salaries/_js.view/solution.js diff --git a/1-js/6-more-syntax/3-advanced-loops/5-sum-salaries/_js.view/test.js b/1-js/5-data-types/08-keys-values-entries/01-sum-salaries/_js.view/test.js similarity index 100% rename from 1-js/6-more-syntax/3-advanced-loops/5-sum-salaries/_js.view/test.js rename to 1-js/5-data-types/08-keys-values-entries/01-sum-salaries/_js.view/test.js diff --git a/1-js/6-more-syntax/3-advanced-loops/5-sum-salaries/solution.md b/1-js/5-data-types/08-keys-values-entries/01-sum-salaries/solution.md similarity index 100% rename from 1-js/6-more-syntax/3-advanced-loops/5-sum-salaries/solution.md rename to 1-js/5-data-types/08-keys-values-entries/01-sum-salaries/solution.md diff --git a/1-js/6-more-syntax/3-advanced-loops/5-sum-salaries/task.md b/1-js/5-data-types/08-keys-values-entries/01-sum-salaries/task.md similarity index 100% rename from 1-js/6-more-syntax/3-advanced-loops/5-sum-salaries/task.md rename to 1-js/5-data-types/08-keys-values-entries/01-sum-salaries/task.md diff --git a/1-js/6-more-syntax/3-advanced-loops/7-count-properties/_js.view/solution.js b/1-js/5-data-types/08-keys-values-entries/02-count-properties/_js.view/solution.js similarity index 100% rename from 1-js/6-more-syntax/3-advanced-loops/7-count-properties/_js.view/solution.js rename to 1-js/5-data-types/08-keys-values-entries/02-count-properties/_js.view/solution.js diff --git a/1-js/6-more-syntax/3-advanced-loops/7-count-properties/_js.view/test.js b/1-js/5-data-types/08-keys-values-entries/02-count-properties/_js.view/test.js similarity index 100% rename from 1-js/6-more-syntax/3-advanced-loops/7-count-properties/_js.view/test.js rename to 1-js/5-data-types/08-keys-values-entries/02-count-properties/_js.view/test.js diff --git a/1-js/6-more-syntax/3-advanced-loops/6-max-salary/solution.md b/1-js/5-data-types/08-keys-values-entries/02-count-properties/solution.md similarity index 100% rename from 1-js/6-more-syntax/3-advanced-loops/6-max-salary/solution.md rename to 1-js/5-data-types/08-keys-values-entries/02-count-properties/solution.md diff --git a/1-js/6-more-syntax/3-advanced-loops/7-count-properties/task.md b/1-js/5-data-types/08-keys-values-entries/02-count-properties/task.md similarity index 100% rename from 1-js/6-more-syntax/3-advanced-loops/7-count-properties/task.md rename to 1-js/5-data-types/08-keys-values-entries/02-count-properties/task.md diff --git a/1-js/5-data-types/08-keys-values-entries/article.md b/1-js/5-data-types/08-keys-values-entries/article.md new file mode 100644 index 00000000..a6f20a03 --- /dev/null +++ b/1-js/5-data-types/08-keys-values-entries/article.md @@ -0,0 +1,67 @@ + +# Object.keys, values, entries + +In the previous chapter we saw methods `map.keys()`, `map.values()`, `map.entries()`. + +These methods are generic, there is a common agreement to use them for data structures. + +They are supported for: + +- `Map` +- `Set` +- `Array` (without `arr.values()`, because that would be repeating itself) + +If we ever create a data structure of our own, we should implement them too. + +## Object.keys, values, entries + +For plain objects, situation is a little bit different. + +There are similar methods: + +- [Object.keys(obj)](mdn:js/Object/keys) -- returns an array of keys. +- [Object.values(obj)](mdn:js/Object/values) -- returns an array of values. +- [Object.entries(obj)](mdn:js/Object/entries) -- returns an array of `[key, value]` pairs. + +...But please note the distinctions (compared to map for example): + +| | Map | Object | +|-------------|------------------|--------------| +| Call syntax | `map.keys()` | `Object.keys(obj)`, but not `obj.keys()` | +| Returns | iterable | "real" Array | + +1. The reason for call syntax `Object.keys(obj)` is flexibility. We can have an object of our own like `order` that implements its own `order.values()` method. And we still can call `Object.values(order)` on it. +2. ...And the returned value is not just an iterable, but an Array for historical reasons. + +For instance: + +```js +let user = { + name: "John", + age: 30 +}; +``` + +- `Object.keys(user) = [name, age]` +- `Object.values(user) = ["John", 30]` +- `Object.entries(user) = [ ["name","John"], ["age",30] ]` + +We can also `Object.values` for a loop over property values: + +```js run +let user = { + name: "John", + age: 30 +}; + +// loop over values +for(let value of Object.values(user)) { + alert(value); // John, then 30 +} +``` + +```smart header="`Object.keys/values/entries` ignore symbolic properties" +Just like `for..in` loop, these methods ignore properties that use `Symbol(...)` as keys. + +Usually that's convenient. There is a separate method named [Object.getOwnPropertySymbols](mdn:js/Object/getOwnPropertySymbols) that returns an array of only symbolic keys (if we really know what we're doing). Also, the method [Reflect.ownKeys(obj)](mdn:js/Reflect/ownKeys) returns *all* keys. +``` diff --git a/1-js/5-data-types/08-map-set-weakmap-weakset/2-recipients-set/solution.md b/1-js/5-data-types/08-map-set-weakmap-weakset/2-recipients-set/solution.md deleted file mode 100644 index 7b546873..00000000 --- a/1-js/5-data-types/08-map-set-weakmap-weakset/2-recipients-set/solution.md +++ /dev/null @@ -1,33 +0,0 @@ -The sane choice here is a `WeakSet`: - -```js -let messages = [ - {title: "Hello"}, - {title: "I'm Mary"} - {title: "Bye!"} -]; - -let readMessages = new WeakSet(); - -// two messages have been read -readMessages.add(messages[0]); -readMessages.add(messages[1]); -// readMessages has 2 elements - -// ...let's read the first message again! -readMessages.add(messages[0]); -// readMessages still has 2 unique elements - -// answer: was the message[0] read? -alert("Read message 0: " + readMessages.has(messages[0])); - -messages.shift(); -// now readMessages has 1 element (technically memory may be cleaned later) -``` - -The `WeakSet` allows to store a set of messages and easily check for the existance of a message in it. - -It cleans up itself automatically. The tradeoff is that we can't iterate over it. That's not needed here though. - -P.S. `WeakSet` stores one bit of information: "was it read" (yes/no - in set/out of set). To store a date, we can use `WeakMap`. - diff --git a/1-js/5-data-types/08-map-set-weakmap-weakset/2-recipients-set/task.md b/1-js/5-data-types/08-map-set-weakmap-weakset/2-recipients-set/task.md deleted file mode 100644 index 8f343517..00000000 --- a/1-js/5-data-types/08-map-set-weakmap-weakset/2-recipients-set/task.md +++ /dev/null @@ -1,23 +0,0 @@ -importance: 5 - ---- - -# A set of messages - -There's an array of messages: - -```js -let messages = [ - {title: "Hello"}, - {title: "I'm Mary"} - {title: "Bye!"} -]; -``` - -Your code can access it, but the messages are managed by someone else's code. New messages are added, old ones are removed regularly by that code, and you don't know the exact moments when it happens. - -Now, which data structure you could use to store only messages that are "have been read"? The structure must be well-suited to give the answer "was it read?" for the given message object. - -P.S. When a message is removed from the collection, it should disappear from your structure as well. - -P.P.S. What you'd use to store the information about "when it was read?" \ No newline at end of file diff --git a/1-js/6-more-syntax/2-destructuring-assignment/1-destructuring-assignment/solution.md b/1-js/5-data-types/09-destructuring-assignment/1-destructuring-assignment/solution.md similarity index 100% rename from 1-js/6-more-syntax/2-destructuring-assignment/1-destructuring-assignment/solution.md rename to 1-js/5-data-types/09-destructuring-assignment/1-destructuring-assignment/solution.md diff --git a/1-js/6-more-syntax/2-destructuring-assignment/1-destructuring-assignment/task.md b/1-js/5-data-types/09-destructuring-assignment/1-destructuring-assignment/task.md similarity index 100% rename from 1-js/6-more-syntax/2-destructuring-assignment/1-destructuring-assignment/task.md rename to 1-js/5-data-types/09-destructuring-assignment/1-destructuring-assignment/task.md diff --git a/1-js/6-more-syntax/3-advanced-loops/6-max-salary/_js.view/solution.js b/1-js/5-data-types/09-destructuring-assignment/6-max-salary/_js.view/solution.js similarity index 100% rename from 1-js/6-more-syntax/3-advanced-loops/6-max-salary/_js.view/solution.js rename to 1-js/5-data-types/09-destructuring-assignment/6-max-salary/_js.view/solution.js diff --git a/1-js/6-more-syntax/3-advanced-loops/6-max-salary/_js.view/test.js b/1-js/5-data-types/09-destructuring-assignment/6-max-salary/_js.view/test.js similarity index 100% rename from 1-js/6-more-syntax/3-advanced-loops/6-max-salary/_js.view/test.js rename to 1-js/5-data-types/09-destructuring-assignment/6-max-salary/_js.view/test.js diff --git a/1-js/6-more-syntax/3-advanced-loops/7-count-properties/solution.md b/1-js/5-data-types/09-destructuring-assignment/6-max-salary/solution.md similarity index 100% rename from 1-js/6-more-syntax/3-advanced-loops/7-count-properties/solution.md rename to 1-js/5-data-types/09-destructuring-assignment/6-max-salary/solution.md diff --git a/1-js/6-more-syntax/3-advanced-loops/6-max-salary/task.md b/1-js/5-data-types/09-destructuring-assignment/6-max-salary/task.md similarity index 100% rename from 1-js/6-more-syntax/3-advanced-loops/6-max-salary/task.md rename to 1-js/5-data-types/09-destructuring-assignment/6-max-salary/task.md diff --git a/1-js/6-more-syntax/2-destructuring-assignment/article.md b/1-js/5-data-types/09-destructuring-assignment/article.md similarity index 63% rename from 1-js/6-more-syntax/2-destructuring-assignment/article.md rename to 1-js/5-data-types/09-destructuring-assignment/article.md index 8bd7d039..11c8cd19 100644 --- a/1-js/6-more-syntax/2-destructuring-assignment/article.md +++ b/1-js/5-data-types/09-destructuring-assignment/article.md @@ -1,6 +1,6 @@ # Destructuring assignment -*Destructuring assignment* is a special syntax for assignments that allows to immediately split an array or a complex object into variables. +*Destructuring assignment* is a special syntax that allows to "unpack" arrays or objects into a bunch of variables, that are sometimes more convenient to work. Destructuring also works great with complex functions that have a lot of parameters, default values etc. [cut] @@ -21,9 +21,24 @@ 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. +Now we can work with variables instead of array members. -Note that the word "destructuring" may be a bit too fearful. It doesn't destroy anything. The array `arr` is not modified in the process. It just "unpacks" the array into variables. +It looks great when combined with `split` or other array-returning methods: + +```js +let [firstName, surname] = "Ilya Kantor".split(' '); +``` + +````smart header="\"Destructuring\" does not mean \"destructive\"" +That's called "destructuring assignment", because it "destructurizes" by copying items into variables. But the array itself is not modified. + +That's just a shorter way to write: +```js +// let [firstName, surname] = arr; +let firstName = arr[0]; +let surname = arr[1]; +``` +```` ````smart header="Ignore first elements" Unwanted elements of the array can also be thrown away via an extra comma: @@ -40,6 +55,62 @@ 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. ```` +````smart header="Works with any iterable on the right-side" + +...Actually, we can use it with any iterable, not only an array: + +```js +let [a, b, c] = "abc"; // ["a", "b", "c"] +let [one, two, three] = new Set([1, 2, 3]); +``` + +```` + + +````smart header="Assign to anything at the left-side" + +We can use any "assignables" at the left side. + +For instance, an object property: +```js run +let user = {}; +[user.name, user.surname] = "Ilya Kantor".split(' '); + +alert(user.name); // Ilya +``` + +```` + +````smart header="Looping with .entries()" + +In the previous chapter we saw [Object.entries(obj)](mdn:js/Object/entries) method. + +We can use it with destructuring to loop over keys-and-values of an object: + +```js run +let user = { + name: "John", + age: 30 +}; + +// loop over keys-and-values +for(let [key, value] of Object.entries(user)) { + alert(`${key}:${value}`); // name:John, then age:30 +} +``` + +...And the same for a map: + +```js run +let user = new Map(); +user.set("name", "John"); +user.set("age", "30"); + +for(let [key, value] of user.entries()) { + alert(`${key}:${value}`); // name:John, then age:30 +} +``` +```` ### The rest '...' If we want not just to get first values, but also to gather all that follows -- we can add one more parameter that gets "the rest" using the three dots `"..."`: @@ -52,12 +123,12 @@ alert(name2); // Caesar *!* alert(rest[0]); // Consul -alert(rest[1]); // of the Roman Republic +alert(rest[1]); // of the Roman Republic alert(rest.length); // 2 */!* ``` -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. The rest operator must be the last in the destructuring assignment. +The value of `rest` is the array of the remaining array elements. We can use any other variable name in place of `rest`, just make sure it has three dots before it and goes the last. ### Default values @@ -85,9 +156,10 @@ alert(surname); // Anonymous (default used) Default values can be more complex expressions or even function calls. They are evaluated only if the value is not provided. -For instance, here we use `prompt` function for defaults. But it will run only for the second one, as it's not provided: +For instance, here we use `prompt` function for two defaults. But it will only run only for the missing one: ```js run +// runs only prompt for surname let [name=prompt('name?'), surname=prompt('surname?')] = ["Julius"]; alert(name); // Julius (from array) @@ -98,7 +170,7 @@ alert(surname); // whatever prompt gets ## Object destructuring -The destructuring assignment also works with objects. +The destructuring assignment also works with objects. The basic syntax is: @@ -106,7 +178,7 @@ The basic syntax is: 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. +We have an existing object at the right side, that we want to split into variables. To the left side contains a "pattern" for corresponding properties. In the simple case that's a list of variable names in `{...}`. For instance: @@ -133,7 +205,9 @@ Properties `options.title`, `options.width` and `options.height` are assigned to 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: +The pattern on the left side may be more complex and specify the mapping between properties and variables. + +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 we can set it using a colon: ```js run let options = { @@ -158,7 +232,7 @@ 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: +For potentially missing properties we can set default values using `"="`, like this: ```js run let options = { @@ -174,6 +248,23 @@ alert(width); // 100 alert(height); // 200 ``` +Just like with arrays or function parameters, default values can be any expressions or even function calls. They will be evaluated if the value is not provided. + +The code below asks for width, but not about the title. + +```js run +let options = { + title: "Menu" +}; + +*!* +let {width=prompt("width?"), title=prompt("title?")} = options; +*/!* + +alert(title); // Menu +alert(width); // (whatever you the result of prompt is) +``` + We also can combine both the colon and equality: ```js run @@ -190,12 +281,34 @@ 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? +### The rest operator -Unfortunately, the current specification does not support that feature. There is a proposal, but it's not in the standard yet. +What if the object has more properties than we have variables? Can we take some and then assign the "rest" somewhere? -````smart header="Destructuring without `let`" -In the examples above variables were declared right before the assignment: `let {…} = {…}`. Of course, we could use existing variables as well. But there's a catch. +The specification for using the rest operator (three dots) here is almost in the standard, but most browsers do not support it yet. + +It looks like this: + +```js run +let options = { + title: "Menu", + height: 200, + width: 100 +}; + +*!* +let {title, ...rest} = options; +*/!* + +// now title="Menu", rest={height: 200, widht: 100} +alert(rest.height); // 200 +alert(rest.width); // 100 +``` + + + +````smart header="Gotcha without `let`" +In the examples above variables were declared right before the assignment: `let {…} = {…}`. Of course, we could use existing variables too. But there's a catch. This won't work: ```js run @@ -205,37 +318,35 @@ let title, width, height; {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 can group statements, like this: +The problem is that Javascript treats `{...}` in the main code flow (not inside another expression) as a code block. Such code blocks can be used to group statements, like this: ```js run { // a code block let message = "Hello"; // ... - alert( message ); + alert( message ); } ``` -Of course, we want destructuring assignment, but by syntax rules of Javascript blocks are checked first. To show Javascript that it's not a code block, we can wrap the whole assignment in brackets `(...)`: - +To show Javascript that it's not a code block, we can wrap the whole assignment in brackets `(...)`: ```js run let title, width, height; -// starts with a bracket, so it's not a code block -// now Javascript will see the destructuring assignment +// okay now *!*(*/!*{title, width, height} = {title: "Menu", width: 200, height: 100}*!*)*/!*; alert( title ); // Menu ``` -Brackets do not affect the result, but now the `{...}` thing is not in the main code flow, so code block syntax does not apply, and the engine finally understands it right. + ```` ## Nested destructuring -If an object or an array contain other objects and arrays, we can go deeper in the destructuring assignment. +If an object or an array contain other objects and arrays, we can use more complex left-side patterns to extract deeper portions. -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: +In the code below `options` has another object in the property `size` and an array in the property `items`. The pattern at the left side of the assignment has the same structure: ```js run let options = { @@ -245,14 +356,14 @@ let options = { }, items: ["Cake", "Donut"], extra: true // something extra that we will not destruct -} +}; // destructuring assignment on multiple lines for clarity -let { +let { size: { // put size here - width, + width, height - }, + }, items: [item1, item2], // assign items here title = "Menu" // not present in the object (default value is used) } = options; @@ -268,7 +379,15 @@ The whole `options` object except `extra` that was not mentioned, is assigned to ![](destructuring-complex.png) -As we can see, the left part of the destructuring assignment can combine and nest things as needed. +Finally, we have `width`, `height`, `item1`, `item2` and `title` from the default value. + +That often happens with destructuring assignments. We have a complex object with may properties and want extract only what we need. + +Even like this: +```js +// take size as a whole into a variable, ignore the rest +let { size } = options; +``` ## Smart function parameters @@ -282,7 +401,7 @@ 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. +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? @@ -303,9 +422,9 @@ let options = { items: ["Item1", "Item2"] }; -// ...and it immediately expands it to variables +// ...and it immediately expands it to variables function showMenu(*!*{title = "Untitled", width = 200, height = 100, items = []}*/!*) { - // title, items – taken from options, + // title, items – taken from options, // width, height – defaults used alert( title + ' ' + width + ' ' + height ); // My Menu 100 200 alert( items ); // Item1, Item2 @@ -324,7 +443,7 @@ let options = { *!* function showMenu({ - title = "Untitled", + 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 @@ -335,7 +454,7 @@ function showMenu({ alert( item2 ); // Item2 } -showMenu(options); +showMenu(options); ``` The syntax is the same as for a destructuring assignment: @@ -361,7 +480,7 @@ We can fix this by making `{}` the default value for the whole destructuring thi ```js run // simplified parameters a bit for clarity function showMenu(*!*{ title="Menu", width=100, height=200 } = {}*/!*) { - alert( title + ' ' + width + ' ' + height ); + alert( title + ' ' + width + ' ' + height ); } showMenu(); // Menu 100 200 @@ -377,7 +496,7 @@ In the code above, the whole arguments object is `{}` by default, so there's alw 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. + 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: @@ -388,7 +507,3 @@ In the code above, the whole arguments object is `{}` by default, so there's alw 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/5-data-types/09-destructuring-assignment/destructuring-complex.png b/1-js/5-data-types/09-destructuring-assignment/destructuring-complex.png new file mode 100644 index 00000000..50c4ffc9 Binary files /dev/null and b/1-js/5-data-types/09-destructuring-assignment/destructuring-complex.png differ diff --git a/1-js/5-data-types/09-destructuring-assignment/destructuring-complex@2x.png b/1-js/5-data-types/09-destructuring-assignment/destructuring-complex@2x.png new file mode 100644 index 00000000..bb908281 Binary files /dev/null and b/1-js/5-data-types/09-destructuring-assignment/destructuring-complex@2x.png differ diff --git a/1-js/5-data-types/04-date/1-new-date/solution.md b/1-js/5-data-types/10-date/1-new-date/solution.md similarity index 100% rename from 1-js/5-data-types/04-date/1-new-date/solution.md rename to 1-js/5-data-types/10-date/1-new-date/solution.md diff --git a/1-js/5-data-types/04-date/1-new-date/task.md b/1-js/5-data-types/10-date/1-new-date/task.md similarity index 100% rename from 1-js/5-data-types/04-date/1-new-date/task.md rename to 1-js/5-data-types/10-date/1-new-date/task.md diff --git a/1-js/5-data-types/04-date/2-get-week-day/_js.view/solution.js b/1-js/5-data-types/10-date/2-get-week-day/_js.view/solution.js similarity index 100% rename from 1-js/5-data-types/04-date/2-get-week-day/_js.view/solution.js rename to 1-js/5-data-types/10-date/2-get-week-day/_js.view/solution.js diff --git a/1-js/5-data-types/04-date/2-get-week-day/_js.view/test.js b/1-js/5-data-types/10-date/2-get-week-day/_js.view/test.js similarity index 100% rename from 1-js/5-data-types/04-date/2-get-week-day/_js.view/test.js rename to 1-js/5-data-types/10-date/2-get-week-day/_js.view/test.js diff --git a/1-js/5-data-types/04-date/2-get-week-day/solution.md b/1-js/5-data-types/10-date/2-get-week-day/solution.md similarity index 100% rename from 1-js/5-data-types/04-date/2-get-week-day/solution.md rename to 1-js/5-data-types/10-date/2-get-week-day/solution.md diff --git a/1-js/5-data-types/04-date/2-get-week-day/task.md b/1-js/5-data-types/10-date/2-get-week-day/task.md similarity index 100% rename from 1-js/5-data-types/04-date/2-get-week-day/task.md rename to 1-js/5-data-types/10-date/2-get-week-day/task.md diff --git a/1-js/5-data-types/04-date/3-weekday/_js.view/solution.js b/1-js/5-data-types/10-date/3-weekday/_js.view/solution.js similarity index 100% rename from 1-js/5-data-types/04-date/3-weekday/_js.view/solution.js rename to 1-js/5-data-types/10-date/3-weekday/_js.view/solution.js diff --git a/1-js/5-data-types/04-date/3-weekday/_js.view/test.js b/1-js/5-data-types/10-date/3-weekday/_js.view/test.js similarity index 100% rename from 1-js/5-data-types/04-date/3-weekday/_js.view/test.js rename to 1-js/5-data-types/10-date/3-weekday/_js.view/test.js diff --git a/1-js/5-data-types/04-date/3-weekday/solution.md b/1-js/5-data-types/10-date/3-weekday/solution.md similarity index 100% rename from 1-js/5-data-types/04-date/3-weekday/solution.md rename to 1-js/5-data-types/10-date/3-weekday/solution.md diff --git a/1-js/5-data-types/04-date/3-weekday/task.md b/1-js/5-data-types/10-date/3-weekday/task.md similarity index 100% rename from 1-js/5-data-types/04-date/3-weekday/task.md rename to 1-js/5-data-types/10-date/3-weekday/task.md diff --git a/1-js/5-data-types/04-date/4-get-date-ago/_js.view/solution.js b/1-js/5-data-types/10-date/4-get-date-ago/_js.view/solution.js similarity index 100% rename from 1-js/5-data-types/04-date/4-get-date-ago/_js.view/solution.js rename to 1-js/5-data-types/10-date/4-get-date-ago/_js.view/solution.js diff --git a/1-js/5-data-types/04-date/4-get-date-ago/_js.view/test.js b/1-js/5-data-types/10-date/4-get-date-ago/_js.view/test.js similarity index 100% rename from 1-js/5-data-types/04-date/4-get-date-ago/_js.view/test.js rename to 1-js/5-data-types/10-date/4-get-date-ago/_js.view/test.js diff --git a/1-js/5-data-types/04-date/4-get-date-ago/solution.md b/1-js/5-data-types/10-date/4-get-date-ago/solution.md similarity index 100% rename from 1-js/5-data-types/04-date/4-get-date-ago/solution.md rename to 1-js/5-data-types/10-date/4-get-date-ago/solution.md diff --git a/1-js/5-data-types/04-date/4-get-date-ago/task.md b/1-js/5-data-types/10-date/4-get-date-ago/task.md similarity index 100% rename from 1-js/5-data-types/04-date/4-get-date-ago/task.md rename to 1-js/5-data-types/10-date/4-get-date-ago/task.md diff --git a/1-js/5-data-types/04-date/5-last-day-of-month/_js.view/solution.js b/1-js/5-data-types/10-date/5-last-day-of-month/_js.view/solution.js similarity index 100% rename from 1-js/5-data-types/04-date/5-last-day-of-month/_js.view/solution.js rename to 1-js/5-data-types/10-date/5-last-day-of-month/_js.view/solution.js diff --git a/1-js/5-data-types/04-date/5-last-day-of-month/_js.view/test.js b/1-js/5-data-types/10-date/5-last-day-of-month/_js.view/test.js similarity index 100% rename from 1-js/5-data-types/04-date/5-last-day-of-month/_js.view/test.js rename to 1-js/5-data-types/10-date/5-last-day-of-month/_js.view/test.js diff --git a/1-js/5-data-types/04-date/5-last-day-of-month/solution.md b/1-js/5-data-types/10-date/5-last-day-of-month/solution.md similarity index 100% rename from 1-js/5-data-types/04-date/5-last-day-of-month/solution.md rename to 1-js/5-data-types/10-date/5-last-day-of-month/solution.md diff --git a/1-js/5-data-types/04-date/5-last-day-of-month/task.md b/1-js/5-data-types/10-date/5-last-day-of-month/task.md similarity index 100% rename from 1-js/5-data-types/04-date/5-last-day-of-month/task.md rename to 1-js/5-data-types/10-date/5-last-day-of-month/task.md diff --git a/1-js/5-data-types/04-date/6-get-seconds-today/solution.md b/1-js/5-data-types/10-date/6-get-seconds-today/solution.md similarity index 100% rename from 1-js/5-data-types/04-date/6-get-seconds-today/solution.md rename to 1-js/5-data-types/10-date/6-get-seconds-today/solution.md diff --git a/1-js/5-data-types/10-date/6-get-seconds-today/task.md b/1-js/5-data-types/10-date/6-get-seconds-today/task.md new file mode 100644 index 00000000..3fbe1328 --- /dev/null +++ b/1-js/5-data-types/10-date/6-get-seconds-today/task.md @@ -0,0 +1,15 @@ +importance: 5 + +--- + +# How many seconds has passed today? + +Write a function `getSecondsToday()` that returns the number of seconds from the beginning of today. + +For instance, if now `10:00 am`, and there was no daylight savings shift, then: + +```js +getSecondsToday() == 36000 // (3600 * 10) +``` + +The function should work in any day. That is, it should not have a hard-coded value of "today". diff --git a/1-js/5-data-types/04-date/7-get-seconds-to-tomorrow/solution.md b/1-js/5-data-types/10-date/7-get-seconds-to-tomorrow/solution.md similarity index 100% rename from 1-js/5-data-types/04-date/7-get-seconds-to-tomorrow/solution.md rename to 1-js/5-data-types/10-date/7-get-seconds-to-tomorrow/solution.md diff --git a/1-js/5-data-types/04-date/7-get-seconds-to-tomorrow/task.md b/1-js/5-data-types/10-date/7-get-seconds-to-tomorrow/task.md similarity index 100% rename from 1-js/5-data-types/04-date/7-get-seconds-to-tomorrow/task.md rename to 1-js/5-data-types/10-date/7-get-seconds-to-tomorrow/task.md diff --git a/1-js/5-data-types/10-date/8-format-date-relative/_js.view/solution.js b/1-js/5-data-types/10-date/8-format-date-relative/_js.view/solution.js new file mode 100644 index 00000000..80b1ef49 --- /dev/null +++ b/1-js/5-data-types/10-date/8-format-date-relative/_js.view/solution.js @@ -0,0 +1,33 @@ + +function formatDate(date) { + let diff = new Date() - date; // the difference in milliseconds + + if (diff < 1000) { // less than 1 second + return 'right now'; + } + + let sec = Math.floor(diff / 1000); // convert diff to seconds + + if (sec < 60) { + return sec + ' sec. ago'; + } + + var min = Math.floor(diff / 60000); // convert diff to minutes + if (min < 60) { + return min + ' min. ago'; + } + + // format the date + // add leading zeroes to single-digit day/month/hours/minutes + let d = date; + d = [ + '0' + d.getDate(), + '0' + (d.getMonth() + 1), + '' + d.getFullYear(), + '0' + d.getHours(), + '0' + d.getMinutes() + ].map(component => component.slice(-2)); // take last 2 digits of every component + + // join the components into date + return d.slice(0, 3).join('.') + ' ' + d.slice(3).join(':'); +} diff --git a/1-js/5-data-types/04-date/8-format-date-relative/_js.view/test.js b/1-js/5-data-types/10-date/8-format-date-relative/_js.view/test.js similarity index 82% rename from 1-js/5-data-types/04-date/8-format-date-relative/_js.view/test.js rename to 1-js/5-data-types/10-date/8-format-date-relative/_js.view/test.js index c345e9e4..9b4cb2f5 100644 --- a/1-js/5-data-types/04-date/8-format-date-relative/_js.view/test.js +++ b/1-js/5-data-types/10-date/8-format-date-relative/_js.view/test.js @@ -4,14 +4,14 @@ describe("formatDate", function() { }); it('"30 seconds ago"', function() { - assert.equal(formatDate(new Date(new Date - 30 * 1000)), "30 сек. назад"); + assert.equal(formatDate(new Date(new Date - 30 * 1000)), "30 sec. ago"); }); it('"5 minutes ago"', function() { - assert.equal(formatDate(new Date(new Date - 5 * 60 * 1000)), "5 мин. назад"); + assert.equal(formatDate(new Date(new Date - 5 * 60 * 1000)), "5 min. ago"); }); - it("older dates as dd.mm.yyyy hh:mm", function() { + it("older dates as DD.MM.YY HH:mm", function() { assert.equal(formatDate(new Date(2014, 2, 1, 11, 22, 33)), "01.03.14 11:22"); }); diff --git a/1-js/5-data-types/10-date/8-format-date-relative/solution.md b/1-js/5-data-types/10-date/8-format-date-relative/solution.md new file mode 100644 index 00000000..4505a002 --- /dev/null +++ b/1-js/5-data-types/10-date/8-format-date-relative/solution.md @@ -0,0 +1,45 @@ +To get the time from `date` till now -- let's substract the dates. + +```js run +function formatDate(date) { + let diff = new Date() - date; // the difference in milliseconds + + if (diff < 1000) { // less than 1 second + return 'right now'; + } + + let sec = Math.floor(diff / 1000); // convert diff to seconds + + if (sec < 60) { + return sec + ' sec. ago'; + } + + var min = Math.floor(diff / 60000); // convert diff to minutes + if (min < 60) { + return min + ' min. ago'; + } + + // format the date + // add leading zeroes to single-digit day/month/hours/minutes + let d = date; + d = [ + '0' + d.getDate(), + '0' + (d.getMonth() + 1), + '' + d.getFullYear(), + '0' + d.getHours(), + '0' + d.getMinutes() + ].map(component => component.slice(-2)); // take last 2 digits of every component + + // join the components into date + return d.slice(0, 3).join('.') + ' ' + d.slice(3).join(':'); +} + +alert( formatDate(new Date(new Date - 1)) ); // "right now" + +alert( formatDate(new Date(new Date - 30 * 1000)) ); // "30 sec. ago" + +alert( formatDate(new Date(new Date - 5 * 60 * 1000)) ); // "5 min. ago" + +// yesterday's date like 31.12.2016, 20:00 +alert( formatDate(new Date(new Date - 86400 * 1000)) ); +``` diff --git a/1-js/5-data-types/04-date/8-format-date-relative/task.md b/1-js/5-data-types/10-date/8-format-date-relative/task.md similarity index 68% rename from 1-js/5-data-types/04-date/8-format-date-relative/task.md rename to 1-js/5-data-types/10-date/8-format-date-relative/task.md index af765ee8..7b341ca2 100644 --- a/1-js/5-data-types/04-date/8-format-date-relative/task.md +++ b/1-js/5-data-types/10-date/8-format-date-relative/task.md @@ -9,7 +9,7 @@ Write a function `formatDate(date)` that should format `date` as follows: - If since `date` passed less than 1 second, then `"right now"`. - Otherwise, if since `date` passed less than 1 minute, then `"n sec. ago"`. - Otherwise, if less than an hour, then `"m min. ago"`. -- Otherwise, the full date in the format `"day.month.year hours:minutes"`. +- Otherwise, the full date in the format `"DD.MM.YY HH:mm"`. That is: `"day.month.year hours:minutes"`, all in 2-digit format, e.g. `31.12.16 10:00`. For instance: @@ -20,5 +20,6 @@ alert( formatDate(new Date(new Date - 30 * 1000)) ); // "30 sec. ago" alert( formatDate(new Date(new Date - 5 * 60 * 1000)) ); // "5 min. ago" -alert( formatDate(new Date(new Date - 86400 * 1000)) ); // yesterday's date like 31.12.2016, 20:00 +// yesterday's date like 31.12.2016, 20:00 +alert( formatDate(new Date(new Date - 86400 * 1000)) ); ``` diff --git a/1-js/5-data-types/04-date/article.md b/1-js/5-data-types/10-date/article.md similarity index 96% rename from 1-js/5-data-types/04-date/article.md rename to 1-js/5-data-types/10-date/article.md index 29f560f2..d229e137 100644 --- a/1-js/5-data-types/04-date/article.md +++ b/1-js/5-data-types/10-date/article.md @@ -1,8 +1,8 @@ -# Date and time +# Date and time -JavaScript has a built-in object [Date](mdn:js/Date) for date/time management. +Let's meet a new built-in object: [Date](mdn:js/Date). It stores the date and provides methods for date/time management. -It contains the date, time, timezone -- everything. +For instance, we can use it to store creation/modification times, or to measure time, or just to print out the current date. [cut] @@ -10,7 +10,6 @@ It contains the date, time, timezone -- everything. To create a new `Date` object call `new Date()` with one of the following arguments: - `new Date()` : Without arguments -- create a `Date` object for the current date and time: @@ -32,7 +31,7 @@ To create a new `Date` object call `new Date()` with one of the following argume alert( Jan02_1970 ); ``` - The number of milliseconds that has passed since the beginning of 1970 is called a *timestamp*. + The number of milliseconds that has passed since the beginning of 1970 is called a *timestamp*. It is a lightweight numeric representation of a date. We can always create a date from a timestamp using `new Date(timestamp)` and convert the existing `Date` object to a timestamp, there's a `date.getTime()` method (see below). @@ -46,7 +45,7 @@ To create a new `Date` object call `new Date()` with one of the following argume ``` `new Date(year, month, date, hours, minutes, seconds, ms)` -: Create the date with the given components in the local time zone. Only two first arguments are obligatory. +: Create the date with the given components in the local time zone. Only two first arguments are obligatory. Note: @@ -115,7 +114,7 @@ alert( date.getUTCHours() ); Besides the given methods, there are two special ones, that do not have a UTC-variant: `getTime()` -: Returns the timestamp for the date -- a number of milliseconds passed from the January 1st of 1970 UTC+0. +: Returns the timestamp for the date -- a number of milliseconds passed from the January 1st of 1970 UTC+0. `getTimezoneOffset()` : Returns the difference between the local time zene and UTC, in minutes: @@ -228,7 +227,7 @@ alert( `The loop took ${end - start} ms` ); If we only want to measure the difference, we don't need `Date` object. -There's a special method `Date.now()` that returns the current timestamp. +There's a special method `Date.now()` that returns the current timestamp. It is semantically equivalent to `new Date().getTime()`, but it does not create an intermediate `Date` object. So it's faster and does not put pressure on garbage collection. @@ -297,7 +296,7 @@ alert( 'Time of diffSubstract: ' + bench(diffSubstract) + 'ms' ); alert( 'Time of diffGetTime: ' + bench(diffGetTime) + 'ms' ); ``` -Wow! Using `getTime()` is so much faster! That's because there's no type conversion, it is much easier for engines to optimize. +Wow! Using `getTime()` is so much faster! That's because there's no type conversion, it is much easier for engines to optimize. Okay, we have something. But that's not a good benchmark yet. @@ -400,7 +399,7 @@ alert( date ); ## Summary -- Date and time in JavaScript are represented with the [Date](mdn:js/Date) object. We can't create "only date" or "only time". +- Date and time in JavaScript are represented with the [Date](mdn:js/Date) object. We can't create "only date" or "only time". - Months are counted from the zero (yes, January is a zero month). - Days of week (for `getDay()`) are also counted from the zero (Sunday). - `Date` auto-corrects itself when out-of-range components are set. Good for adding/substracting days/months/hours. @@ -420,10 +419,3 @@ alert(`Loading started ${performance.now()}ms ago`); ``` Node.JS has `microtime` module and other ways. Technically, any device and environment allows to get that, it's just not in `Date`. - - - - - - - diff --git a/1-js/5-data-types/09-json/1-serialize-object/solution.md b/1-js/5-data-types/11-json/1-serialize-object/solution.md similarity index 100% rename from 1-js/5-data-types/09-json/1-serialize-object/solution.md rename to 1-js/5-data-types/11-json/1-serialize-object/solution.md diff --git a/1-js/5-data-types/09-json/1-serialize-object/task.md b/1-js/5-data-types/11-json/1-serialize-object/task.md similarity index 100% rename from 1-js/5-data-types/09-json/1-serialize-object/task.md rename to 1-js/5-data-types/11-json/1-serialize-object/task.md diff --git a/1-js/5-data-types/09-json/2-serialize-event-circular/solution.md b/1-js/5-data-types/11-json/2-serialize-event-circular/solution.md similarity index 100% rename from 1-js/5-data-types/09-json/2-serialize-event-circular/solution.md rename to 1-js/5-data-types/11-json/2-serialize-event-circular/solution.md diff --git a/1-js/5-data-types/09-json/2-serialize-event-circular/task.md b/1-js/5-data-types/11-json/2-serialize-event-circular/task.md similarity index 100% rename from 1-js/5-data-types/09-json/2-serialize-event-circular/task.md rename to 1-js/5-data-types/11-json/2-serialize-event-circular/task.md diff --git a/1-js/5-data-types/09-json/article.md b/1-js/5-data-types/11-json/article.md similarity index 66% rename from 1-js/5-data-types/09-json/article.md rename to 1-js/5-data-types/11-json/article.md index 95d4c2df..7d540ff0 100644 --- a/1-js/5-data-types/09-json/article.md +++ b/1-js/5-data-types/11-json/article.md @@ -1,20 +1,183 @@ -# JSON methods, toJSON +# JSON methods, toJSON -The [JSON](http://en.wikipedia.org/wiki/JSON) (JavaScript Object Notation) format is used to represent an object as a string. +Let's say we have a complex object, and we'd like to convert it into a string, to send it over a network, or just to output it for logging purposes. -When we need to send an object over a network -- from the client to server or in the reverse direction, this format is the most widespread. +Naturally, such a string should include all important properties. -Javascript provides built-in methods to convert objects into JSON and back, which also allow a few tricks that make them even more useful. +We could implement the conversion like this: + +```js run +let user = { + name: "John", + age: 30, + +*!* + toString() { + return `{name: "${this.name}", age: ${this.age}}`; + } +*/!* +}; + +alert(user); // {name: "John", age: 30} +``` + +...But in the process of development, new properties are added, old properties are renamed and removed. Updating such `toString` every time can become a pain. We could try to loop over properties in it, but what is the object is complex and has nested objects in properties? We'd need to implement their conversion as well. And, if we're sending the object over a network, then we also need to supply the code to "read" our object on the receiving side. + +Luckily, there's no need to write the code to handle all this. The task has been solved already. [cut] ## JSON.stringify -JSON, despite its name (JavaScript Object Notation) is an independent standard. It is described as [RFC 4627](http://tools.ietf.org/html/rfc4627). Most programming languages have libraries to encode objects in it. So it's easy to use JSON for data exchange when the client uses Javascript and the server is written on Ruby/PHP/Java/Whatever. +The [JSON](http://en.wikipedia.org/wiki/JSON) (JavaScript Object Notation) is a general format to represent values and objects. It is described as in [RFC 4627](http://tools.ietf.org/html/rfc4627) standard. Initially it was made for Javascript, but many other languages have libraries to handle it as well. So it's easy to use JSON for data exchange when the client uses Javascript and the server is written on Ruby/PHP/Java/Whatever. -In Javascript, the native method [JSON.stringify(value)](mdn:js/JSON/stringify) accepts a value and returns it's representation as a string in JSON format. +Javascript provides methods: -The syntax: +- `JSON.stringify` to convert objects into JSON. +- `JSON.parse` to convert JSON back into an object. + +For instance, here's we `JSON.stringify` a student: +```js run +let student = { + name: 'John', + age: 30, + isAdmin: false, + courses: ['html', 'css', 'js'], + wife: null +}; + +*!* +let json = JSON.stringify(student); +*/!* + +alert(typeof json); // we've got a string! + +alert(json); +*!* +/* JSON-encoded object: +{ + "name": "John", + "age": 30, + "isAdmin": false, + "courses": ["html", "css", "js"], + "wife": null +} +*/ +*/!* +``` + +The method `JSON.stringify(student)` takes the object and converts it into a string. + +The resulting `json` string is a called *JSON-encoded* or *serialized* or *stringified* object. We are ready to send it over the wire or put into plain data store. + + +Please note that JSON-encoded object has several important differences from the object literal: + +- Strings use double quotes. No single quotes or backticks in JSON. So `'John'` becomes `"John"`. +- Object property names are double-quoted also. That's obligatory. So `age:30` becomes `"age":30`. + +`JSON.stringify` can be applied to primitives as well. + +Natively supported JSON types are: + +- Objects `{ ... }` +- Arrays `[ ... ]` +- Primitives: + - strings, + - numbers, + - boolean values `true/false`, + - `null`. + +For instance: + +```js run +// a number in JSON is just a number +alert( JSON.stringify(1) ) // 1 + +// a string in JSON is still a string, but double-quoted +alert( JSON.stringify('test') ) // "test" + +alert( JSON.stringify(true) ); // true + +alert( JSON.stringify([1, 2, 3]) ); // [1,2,3] +``` + +JSON is data-only cross-language specification, so some Javascript-specific object properties are skipped by `JSON.stringify`. + +Namely: + +- Function properties (methods). +- Symbolic properties. +- Properties that store `undefined`. + +```js run +let user = { + sayHi() { // ignored + alert("Hello"); + }, + [Symbol("id")]: 123, // ignored + something: undefined // ignored +}; + +alert( JSON.stringify(user) ); // {} (empty object) +``` + +Usually that's fine. If that's not what we want, then soon we'll see how to customize the process. + +The great thing is that nested objects are supported and converted automatically. + +For instance: + +```js run +let meetup = { + title: "Conference", +*!* + room: { + number: 123, + participants: ["john", "ann"] + } +*/!* +}; + +alert( JSON.stringify(meetup) ); +/* The whole structure is stringified: +{ + "title":"Conference", + "room":{"number":23,"participants":["john","ann"]}, +} +*/ +``` + +The important limitation: there must be no circular references. + +For instance: + +```js run +let room = { + number: 23 +}; + +let meetup = { + title: "Conference", + participants: ["john", "ann"] +}; + +meetup.room = room; // meetup references room +room.occupiedBy = meetup; // room references meetup + +*!* +JSON.stringify(meetup); // Error: Converting circular structure to JSON +*/!* +``` + +Here, the conversion fails, because of circular reference: `room.occupiedBy` references `meetup`, and `meetup.place` references `room`: + +![](json-meetup.png) + + +## Excluding and transforming: replacer + +The full syntax of `JSON.stringify` is: ```js let json = JSON.stringify(value[, replacer, space]) @@ -29,146 +192,7 @@ replacer space : Amount of space to use for formatting -Most of time, `JSON.stringify` is used with first argument only. - -For instance: -```js run -let student = { - name: 'John', - age: 30, - isAdmin: false, - courses: ['html', 'css', 'js'], - wife: null -}; - -let json = JSON.stringify(student); - -alert(typeof json); // we've got a string! - -alert(json); -*!* -/* JSON-encoded object: -{ - "name": "John", - "age": 30, - "isAdmin": false, - "courses": ["html", "css", "js"], - "wife": null -} -*/ -*/!* -``` - -The resulting `json` string is a called *JSON-encoded* or *serialized* or *stringified* object. We are ready to send it over the wire or put into plain data store. - -JSON-encoded object has several important differences from the original variant: - -- Strings use double quotes. No single quotes or backticks in JSON. So `'John'` becomes `"John"`. -- Object property names are double-quoted also. Also obligatory. So `age:30` becomes `"age":30`. - -`JSON.stringify` can be applied not only to objects, but to other values: - -```js run -// a number in JSON is just a number -alert( JSON.stringify(1) ) // 1 - -// a string in JSON is still a string, but double-quoted -alert( JSON.stringify('test') ) // "test" - -// double quotes are escaped -alert( JSON.stringify('"quoted"') ) // "\"quoted\"" (inner quotes are escaped with \") - -// array of objects -alert( JSON.stringify([{name: "John"},{name: "Ann"}]) ); // [{"name":"John"},{"name":"Ann"}] -``` - -The supported JSON types are: - -- Objects `{ ... }` -- Arrays `[ ... ]` -- Primitives: - - strings, - - numbers, - - boolean values `true/false`, - - `null`. - -In the examples above we can see them all. - -JSON format does not support any other types. It is data-only cross-language specification, so some Javascript-specific things like function properties, `undefined` values and symbolic properties are just skipped by `JSON.stringify`: - -```js run -let user = { - sayHi() { // ignored - alert("Hello"); - }, - [Symbol("id")]: 123, // ignored - something: undefined // ignored -}; - -alert( JSON.stringify(user) ); // {} (empty object) -``` - -From the other hand, nested objects that reference other objects and arrays are supported. - -For instance: - -```js run -let room = { - number: 23 -}; - -let meetup = { - title: "Conference", - room, - participants: [{ - name: "John" - }, { - name: "Alice" - }] -}; - -alert( JSON.stringify(meetup) ); -/* The whole structure is stringified: -{ - "title":"Conference", - "room":{"number":23}, - "participants":[ - {"name":"John"},{"name":"Alice"} - ] -} -*/ -``` - -The only limitation for `JSON.stringify`: there must be no circular references. - -For instance: - -```js run -let room = { - number: 23 -}; - -let meetup = { - title: "Conference", - participants: [{name: "John"}, {name: "Alice"}], - place: room // meetup references room -}; - -room.occupiedBy = meetup; // room references meetup - -*!* -JSON.stringify(meetup); // Error: Converting circular structure to JSON -*/!* -``` - -Here, the conversion fails, because of circular reference: `room.occupiedBy` references `meetup`, and `meetup.place` references `room`: - -![](json-meetup.png) - - -## Excluding and transforming: replacer - -One of the ways to deal with circular references is to use the second, optional argument of `JSON.stringify`. +Most of time, `JSON.stringify` is used with first argument only. But if we need to fine-tune the replacement process, like to filter out circular references, we can use the second argument of `JSON.stringify`. If we pass an array of properties to it, only these properties will be encoded. @@ -187,14 +211,13 @@ let meetup = { room.occupiedBy = meetup; // room references meetup -alert( JSON.stringify(meetup, *!*['title', 'participants']*/!*) ); +alert( JSON.stringify(meetup, *!*['title', 'participants']*/!*) ); // {"title":"Conference","participants":[{},{}]} ``` Here we are probably too strict. The property list is applied to the whole object structure. So participants are empty, because `name` is not in the list. -We are safe to include every property except `room.occupiedBy` that would cause the circular reference: - +Let's include every property except `room.occupiedBy` that would cause the circular reference: ```js run let room = { @@ -209,7 +232,7 @@ let meetup = { room.occupiedBy = meetup; // room references meetup -alert( JSON.stringify(meetup, *!*['title', 'participants', 'place', 'name', 'number']*/!*) ); +alert( JSON.stringify(meetup, *!*['title', 'participants', 'place', 'name', 'number']*/!*) ); /* { "title":"Conference", @@ -219,9 +242,9 @@ alert( JSON.stringify(meetup, *!*['title', 'participants', 'place', 'name', 'num */ ``` -Now everything excepts `occupiedBy` is serialized. +Now everything excepts `occupiedBy` is serialized. But the list of properties is quite long. -Another way would be to provide a function instead the array as `replacer`. +Fortunately, we can use a function instead the array as `replacer`. The function will be called for every `(key,value)` pair and should return the "replaced" value, which will be used instead of the original one. @@ -258,15 +281,18 @@ number: 23 Please note that `replacer` function gets every key/value pair including nested objects and array items. It is applied recursively. The value of `this` inside `replacer` is the object that contains the current property. -The first call is made using a special "wrapper object": `{"": meetup}`, the first `(key,value)` pair is an empty key and the target object as a whole. That's why the first line is `":[object Object]"`. +The first call is special. It is made using a special "wrapper object": `{"": meetup}`. In other words, the first `(key,value)` pair has an empty key, and the value is the target object as a whole. That's why the first line is `":[object Object]"` in the example above. + +The idea is to provide as much power for `replacer` as possible: it has a chance to analyze and replace/skip the whole object if necessary. + ## Formatting: spacer -The third argument of `JSON.stringify(value, replacer, spaces) is the number of spaces to use for pretty formatting. +The third argument of `JSON.stringify(value, replacer, spaces)` is the number of spaces to use for pretty formatting. -Previously, all objects had no indents and extra spaces. +Previously, all stringified objects had no indents and extra spaces. That's fine if we want to send an object over a network. The `spacer` argument is used exclusively for a nice output. -Here they will have them: +Here `spacer = 2` tells Javascript to show nested objects on multiple lines, with indentation of 2 spaces inside an object: ```js run let user = { @@ -290,8 +316,7 @@ alert(JSON.stringify(user, null, 2)); } */ -alert(JSON.stringify(user, null, 4)); -/* four-space indents: +/* for JSON.stringify(user, null, 4) the result would be more indented: { "name": "John", "age": 25, @@ -307,9 +332,7 @@ The `spaces` parameter is used solely for logging and nice-output purposes. ## Custom "toJSON" -Previously, we used `replacer` to serialize objects "the smart way" and ignore circular links. - -But if object is not satisfied with the default behavior of `JSON.stringify`, it can provide its own by implementing method `toJSON`. +Like `toString` for a string conversion, an object may provide method `toJSON` for to-JSON conversion. `JSON.stringify` automatically calls it if available. For instance: @@ -336,7 +359,7 @@ alert( JSON.stringify(meetup) ); */ ``` -Here we can see that `date` `(1)` became a string. That's because all dates have a built-in `toJSON` method which returns such kind of string. +Here we can see that `date` `(1)` became a string. That's because all dates have a built-in `toJSON` method which returns such kind of string. Now let's add a custom `toJSON` for our object `room`: @@ -423,7 +446,7 @@ let json = `{ }`; ``` -Besides, JSON does not support comments. Adding a comment to JSON makes it invalid. +Besides, JSON does not support comments. Adding a comment to JSON makes it invalid. There's another format named [JSON5](http://json5.org/), which allows unquoted keys, comments etc. But this is a standalone library, not in the specification of the language. @@ -476,10 +499,10 @@ alert( meetup.date.getDate() ); // now works! By the way, that works for nested objects as well: ```js run -let schedule = `{ - "meetups": [ - {"title":"Conference","date":"2017-11-30T12:00:00.000Z"}, - {"title":"Birthday","date":"2017-04-18T12:00:00.000Z"} +let schedule = `{ + "meetups": [ + {"title":"Conference","date":"2017-11-30T12:00:00.000Z"}, + {"title":"Birthday","date":"2017-04-18T12:00:00.000Z"} ] }`; @@ -502,4 +525,3 @@ alert( schedule.meetups[1].date.getDate() ); // works! - Javascript provides methods [JSON.stringify](mdn:js/JSON/stringify) to serialize into JSON and [JSON.parse](mdn:js/JSON/parse) to read from JSON. - Both methods support transformer functions for smart reading/writing. - If an object has `toJSON`, then it is called by `JSON.stringify`. - diff --git a/1-js/5-data-types/09-json/json-meetup.png b/1-js/5-data-types/11-json/json-meetup.png similarity index 100% rename from 1-js/5-data-types/09-json/json-meetup.png rename to 1-js/5-data-types/11-json/json-meetup.png diff --git a/1-js/5-data-types/09-json/json-meetup@2x.png b/1-js/5-data-types/11-json/json-meetup@2x.png similarity index 100% rename from 1-js/5-data-types/09-json/json-meetup@2x.png rename to 1-js/5-data-types/11-json/json-meetup@2x.png diff --git a/1-js/5-data-types/10-property-flags-descriptors/article.md b/1-js/5-data-types/12-property-flags-descriptors/article.md similarity index 100% rename from 1-js/5-data-types/10-property-flags-descriptors/article.md rename to 1-js/5-data-types/12-property-flags-descriptors/article.md diff --git a/1-js/5-data-types/11-property-accessors/article.md b/1-js/5-data-types/13-property-accessors/article.md similarity index 100% rename from 1-js/5-data-types/11-property-accessors/article.md rename to 1-js/5-data-types/13-property-accessors/article.md diff --git a/1-js/6-more-syntax/1-function-arguments-rest-spread/article.md b/1-js/6-more-syntax/1-function-arguments-rest-spread/article.md deleted file mode 100644 index 06fa7bc8..00000000 --- a/1-js/6-more-syntax/1-function-arguments-rest-spread/article.md +++ /dev/null @@ -1,204 +0,0 @@ -# Function arguments: rest and spread - -In Javascript, there is an internal type named [List](https://tc39.github.io/ecma262/#sec-list-and-record-specification-type). - -When a function is called, it is said to have "a list of parameters". That's not a figure of speech. The specification officially says that in the call `f(a, b, c)` we have the function `f` the list of `(a, b, c)`. - -[cut] - -Both List and Array represent ordered collections of values. - -The difference is: - -- `Array` is the open data type that we can use, it provides special methods like `push/pop`. -- `List` is the internal type, to represend lists of arguments and such, it has no special methods. - -There are two use cases when we want to convert between them: - -1. To work with an arbitrary number of function arguments, it's useful to have them in the form of array. -2. We have an array of values and would like to call a function that requires them to be listed. - -Both variants are possible. - -## Rest parameters `...` - -The rest parameters are denoted with three dots `...`. They literally mean: "gather the list into an array". - -For instance, here we gather all arguments into array `args`: - -```js run -function sumAll(...args) { // args is the name for the array - 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 -``` - -That also works partially. - -For instance, here we put few first arguments into variables and gather only the rest of them: - -```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 - alert( rest.length ); // 2 -} - -showName("Julius", "Caesar", "Consul", "of the Roman Republic"); -``` - -````warn header="The rest parameters must be at the end" -The rest parameters gather 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. -```` - -## The "arguments" variable - -But there is also a special array-like object named `arguments` that contains all arguments by their index. - -For instance: - -```js run -function showName() { - alert( arguments[0] ); - alert( arguments[1] ); - alert( arguments.length ); - - // iterable too - // for(let arg of arguments) alert(arg); -} - -// shows: Julius, Caesar, 2 -showName("Julius", "Caesar"); - -// shows: Ilya, undefined, 1 -showName("Ilya"); -``` - -In old times, rest parameters did not exist in the language, and `arguments` was the only way to get *all arguments* of the function no matter of their total number. - -And it still works. - -But the downside is that though `arguments` is both array-like and iterable, it's not an array. It does not support array methods. Also, it always has everything in it, we can't get first parameters in the variable and keep only the rest in arguments. - -So when we need these features, then rest parameters are preferred. - -## Spread operator [#spread-operator] - -We've just seen how to get an array from the list of parameters. - -Now let's do the reverse. - -For instance, there's a built-in function [Math.max](mdn:js/Math/max) that returns the greatest number from the list: - -```js run -alert( Math.max(3, 5, 1) ); // 5 -``` - -Now let's say we have an array `[3, 5, 1]`. How to call `Math.max` with it? - -Passing it "as it" won't work, because `Math.max` expects a list of numeric arguments, not a single array: - -```js run -let arr = [3, 5, 1]; - -*!* -alert( Math.max(arr) ); // NaN -*/!* -``` - -Most of the time we also can't manually list items in the code `Math.max(arg[0], arg[1], arg[2])`, because their exact number is not known. As our script executes, there might be many, or there might be none. - -*Spread operator* to the rescue. It looks similar to rest parameters, also using `...`, but does quite the opposite. - -When `...iter` is used in the function call, it "expands" an iterable object `iter` into the list. - -For `Math.max`: - -```js run -let arr = [3, 5, 1]; - -alert( Math.max(...arr) ); // 5 (spread turns array into a list of arguments) -``` - -Unlike rest parameters, spread operators can appear as many times as needed within a single call. - -Here's an example: - -```js run -let arr = [3, 5, 1]; -let arr2 = [8, 9, 15]; - -*!* -alert( Math.max(0, ...arr, 2, ...arr2) ); // 15 -*/!* -``` - -```smart header="Spread or rest?" -When you see `"..."`, there's an easy way to differ spread operator from rest parameters: - -- Rest parameters, if it's in the function definition (gathers into array). -- Spread operator, if in the function call (expands an array). -``` - -Also we can use spread operator to merge arrays: - -```js run -let arr = [3, 5, 1]; -let arr2 = [8, 9, 15]; - -*!* -let merged = [0, ...arr, 2, ...arr2]; -*/!* - -alert(merged); // 0,3,5,1,2,8,9,15 (0, then arr, then 2, then arr2) -``` - -In the examples above we used an array to demonstrate the spread operator, but any iterable will do. - -For instance, here we use spread operator to turn the string into array of characters: - -```js run -let str = "Hello"; - -alert( [...str] ); // H,e,l,l,o -``` - -The spread operator `...str` uses the same iterator mechanism as `for..of` to iterate and gather elements. For a string, `for..of` returns characters, so `...str` becomes `"h","e","l","l","o"`. The list of characters is passed to array initializer `[]`. - -Here we could also use `Array.from` that converts an iterable (a string) into an array: - -```js run -let str = "Hello"; - -// Array.from converts an iterable into an array -alert( Array.from(str) ); // H,e,l,l,o -``` - -The result is the same as `[...str]`. - -## Summary - -- When `...` is at the end of function parameters, it's "rest parameters" and gathers the rest of the list into the array. -- When `...` occurs in a function call, it's called a "spread operator" and expands an array into the list. - -Together they help to travel between a list and an array of parameters with ease. - -All arguments of a function call are also available in the `arguments` array-like iterable object. diff --git a/1-js/6-more-syntax/2-destructuring-assignment/destructuring-complex.png b/1-js/6-more-syntax/2-destructuring-assignment/destructuring-complex.png deleted file mode 100644 index 94b675e6..00000000 Binary files a/1-js/6-more-syntax/2-destructuring-assignment/destructuring-complex.png and /dev/null differ diff --git a/1-js/6-more-syntax/2-destructuring-assignment/destructuring-complex@2x.png b/1-js/6-more-syntax/2-destructuring-assignment/destructuring-complex@2x.png deleted file mode 100644 index 85b1bc7b..00000000 Binary files a/1-js/6-more-syntax/2-destructuring-assignment/destructuring-complex@2x.png and /dev/null differ diff --git a/1-js/6-more-syntax/3-advanced-loops/1-iterable-keys/solution.md b/1-js/6-more-syntax/3-advanced-loops/1-iterable-keys/solution.md deleted file mode 100644 index 36f90e4c..00000000 --- a/1-js/6-more-syntax/3-advanced-loops/1-iterable-keys/solution.md +++ /dev/null @@ -1,16 +0,0 @@ - -That's because `arr.keys()` returns an iterable, but not an array. - -We can convert it into an array using `Array.from`: - - -```js run -let arr = []; -arr[5] = true; - -*!* -let numbers = Array.from(arr.keys()); -*/!* -numbers.push(6); -alert(numbers); // 0,1,2,3,4,5,6 -``` diff --git a/1-js/6-more-syntax/3-advanced-loops/1-iterable-keys/task.md b/1-js/6-more-syntax/3-advanced-loops/1-iterable-keys/task.md deleted file mode 100644 index de246abe..00000000 --- a/1-js/6-more-syntax/3-advanced-loops/1-iterable-keys/task.md +++ /dev/null @@ -1,28 +0,0 @@ -importance: 5 - ---- - -# Iterable keys - -We want to get an array of numbers `0,1,2,3,4,5`. - -The call to `arr.keys()` can be used, but there's a problem: - -```js run -let arr = []; -arr[5] = true; - -// loop works -for(let number of arr.keys()) { - alert(number); // 0,1,2,3,4,5 -} - - -let numbers = arr.keys(); -*!* -// Error: numbers.push is not a function -numbers.push(6); -*/!* -``` - -Why? How can we fix the code to let `numbers.push` work on the last one? \ No newline at end of file diff --git a/1-js/6-more-syntax/3-advanced-loops/article.md b/1-js/6-more-syntax/3-advanced-loops/article.md deleted file mode 100644 index fdc00d5b..00000000 --- a/1-js/6-more-syntax/3-advanced-loops/article.md +++ /dev/null @@ -1,163 +0,0 @@ - -# Advanced loops - -Now as we know arrays, iterables and destructuring, we're ready to cover more forms of loops. - -## Over objects - -We've already seen one of the most popular loops: `for..in` - -```js -for(let key in obj) { - // key iterates over object keys -} -``` - -But there are also ways to get keys, values or or key/value pairs as arrays: - -- [Object.keys(obj)](mdn:js/Object/keys) -- returns the array of keys. -- [Object.values(obj)](mdn:js/Object/values) -- returns the array of values. -- [Object.entries(obj)](mdn:js/Object/entries) -- returns the array of `[key, value]` pairs. - -For instance: - -```js -let user = { - name: "John", - age: 30 -}; -``` - -- `Object.keys(user) = [name, age]` -- `Object.values(user) = ["John", 30]` -- `Object.entries(user) = [ ["name","John"], ["age",30] ]` - - -We can use `Object.values` to iterate over object values: - -```js -for(let value of Object.values(obj)) { - // value iterates over object values -} -``` - -Here `Object.values(obj)` returns the array of properties, and `for..of` iterates over the array. - -Also we can combine destructuring with `Object.entries` to iterate over key/value pairs: - -```js -for(let [key, value] of Object.entries(obj)) { - // key,value iterate over properties -} -``` - -The example of all 3 loops: - -```js run -let user = { - name: "John", - age: 30 -}; - -// over keys -for(let key in user) { - alert(key); // name, then age - // can get the values with user[key] -} - -// over values -for(let value of Object.values(user)) { - alert(value); // John, then 30 -} - -// over key/value pairs -for(let [key, value] of Object.entries(user)) { - alert(key + ':' + value); // name:John, then age:30 -} -``` - - -```smart header="The loops ignore symbolic properties" -All 3 forms of loops (and the given `Object` methods) ignore properties that use `Symbol(...)` as keys. - -That's an expected behavior, because symbols are often created to make sure that the property can not be accessed accidentaly. There is a separate method named [Object.getOwnPropertySymbols](mdn:js/Object/getOwnPropertySymbols) that returns an array of only symbolic keys (if we really know what we're doing). Also, the method [Reflect.ownKeys(obj)](mdn:js/Reflect/ownKeys) returns all keys. -``` - -## Over arrays - -There are many data structures that keep keys and/or values. `Object` is one of them, `Array` is the other one, and we'll see more in the future. - -The method names `keys()`, `values()` and `entries()` are generic, there is a common agreement to use them for common iterations. - -For arrays: - -- [arr.keys()](mdn:js/Array/keys) -- returns the iterable for keys (indexes). -- [arr.entries()](mdn:js/Array/entries) -- returns the iterable for `[index, value]` pairs. - -There is no `arr.values()`, because `for..of` iterates over values already. - -Note that for arrays and further data structures the methods are callable directly, and they return iterables, not arrays. - -The loops: - -```js run -let users = ["John", "Pete", "Ann"]; - -// over keys (indexes) -for(let key of users.keys()) { - alert(key); // 0, 1, 2 -} - -// over values, basic for..of (no special method) -for(let value of users) { - alert(value); // John, then 30 -} - -// over key/value pairs -for(let [key, value] of users.entries()) { - alert(key + ':' + value); // 0:John, then 1:Pete, 2:Ann -} -``` - -````smart header="Arrays are treated as contiguous, without holes" -All these methods treat arrays as contiguous. "Holes" are considered `undefined` items. - -```js run -let arr = []; -arr[4] = "test"; // items till index 4 are assumed to be undefined - -*!* -// arr.keys() lists keys contigously from 0 to 4 -*/!* -for(let i of arr.keys()) alert(i); // 0,1,2,3,4 - -alert(`Length: ${arr.length}`); // 5, remember, length is the last index + 1 -``` - -```` - -## Over sets, maps... - -As we've seen in the chapter , `Map` and `Set` also implement methods `keys()`, `values()` and `entries()`. - -So we can iterate over them the same way. - -For instance, key/value iteration over a map: - -```js run -let recipeMap = new Map([ - ['cucumber', 500], - ['tomatoes', 350], - ['onion', 50] -]); - -for(let [key, value] of recipeMap) { // the same as of recipeMap.entries() - alert(key); // cucumber - alert(value); // 500 - // .. and so on -} -``` - -## Summary [todo] - -keys/values/entries are generic for collections \ No newline at end of file diff --git a/1-js/6-more-syntax/4-try-catch/1-finally-or-code-after/solution.md b/1-js/7-advanced-syntax/4-try-catch/1-finally-or-code-after/solution.md similarity index 100% rename from 1-js/6-more-syntax/4-try-catch/1-finally-or-code-after/solution.md rename to 1-js/7-advanced-syntax/4-try-catch/1-finally-or-code-after/solution.md diff --git a/1-js/6-more-syntax/4-try-catch/1-finally-or-code-after/task.md b/1-js/7-advanced-syntax/4-try-catch/1-finally-or-code-after/task.md similarity index 100% rename from 1-js/6-more-syntax/4-try-catch/1-finally-or-code-after/task.md rename to 1-js/7-advanced-syntax/4-try-catch/1-finally-or-code-after/task.md diff --git a/1-js/6-more-syntax/4-try-catch/2-eval-calculator-errors/solution.md b/1-js/7-advanced-syntax/4-try-catch/2-eval-calculator-errors/solution.md similarity index 100% rename from 1-js/6-more-syntax/4-try-catch/2-eval-calculator-errors/solution.md rename to 1-js/7-advanced-syntax/4-try-catch/2-eval-calculator-errors/solution.md diff --git a/1-js/6-more-syntax/4-try-catch/2-eval-calculator-errors/task.md b/1-js/7-advanced-syntax/4-try-catch/2-eval-calculator-errors/task.md similarity index 100% rename from 1-js/6-more-syntax/4-try-catch/2-eval-calculator-errors/task.md rename to 1-js/7-advanced-syntax/4-try-catch/2-eval-calculator-errors/task.md diff --git a/1-js/6-more-syntax/4-try-catch/article.md b/1-js/7-advanced-syntax/4-try-catch/article.md similarity index 100% rename from 1-js/6-more-syntax/4-try-catch/article.md rename to 1-js/7-advanced-syntax/4-try-catch/article.md diff --git a/1-js/6-more-syntax/index.md b/1-js/7-advanced-syntax/index.md similarity index 100% rename from 1-js/6-more-syntax/index.md rename to 1-js/7-advanced-syntax/index.md diff --git a/1-js/8-more-functions/01-recursion/01-sum-to/solution.md b/1-js/8-more-functions/01-recursion/01-sum-to/solution.md new file mode 100644 index 00000000..b8f753d5 --- /dev/null +++ b/1-js/8-more-functions/01-recursion/01-sum-to/solution.md @@ -0,0 +1,40 @@ +The solution using a loop: + +```js run +function sumTo(n) { + let sum = 0; + for (let i = 1; i <= n; i++) { + sum += i; + } + return sum; +} + +alert( sumTo(100) ); +``` + +The solution using recursion: + +```js run +function sumTo(n) { + if (n == 1) return 1; + return n + sumTo(n - 1); +} + +alert( sumTo(100) ); +``` + +The solution using the formula: `sumTo(n) = n*(n+1)/2`: + +```js run +function sumTo(n) { + return n * (n + 1) / 2; +} + +alert( sumTo(100) ); +``` + +P.S. Naturally, the formula is the fastest solution. It uses only 3 operations for any number `n`. The math helps! + +The loop variant is the second in terms of speed. In both the recursive and the loop variant we sum the same numbers. But the recursion involves nested calls and execution stack management. That also takes resources, so it's slower. + +P.P.S. The standard describes a "tail call" optimization: if the recursive call is the very last one in the function (like in `sumTo` above), then the outer function will not need to resume the execution and we don't need to remember its execution context. In that case `sumTo(100000)` is countable. But if your Javascript engine does not support it, there will be an error: maximum stack size exceeded, because there's usually a limitation on the total stack size. diff --git a/1-js/8-more-functions/01-recursion/01-sum-to/task.md b/1-js/8-more-functions/01-recursion/01-sum-to/task.md new file mode 100644 index 00000000..cabc1329 --- /dev/null +++ b/1-js/8-more-functions/01-recursion/01-sum-to/task.md @@ -0,0 +1,36 @@ +importance: 5 + +--- + +# Sum all numbers till the given one + +Write a function `sumTo(n)` that calculates the sum of numbers `1 + 2 + ... + n`. + +For instance: + +```js no-beautify +sumTo(1) = 1 +sumTo(2) = 2 + 1 = 3 +sumTo(3) = 3 + 2 + 1 = 6 +sumTo(4) = 4 + 3 + 2 + 1 = 10 +... +sumTo(100) = 100 + 99 + ... + 2 + 1 = 5050 +``` + +Make 3 solution variants: + +1. Using a for loop. +2. Using a recursion, cause `sumTo(n) = n + sumTo(n-1)` for `n > 1`. +3. Using the [arithmetic progression](https://en.wikipedia.org/wiki/Arithmetic_progression) formula. + +An example of the result: + +```js +function sumTo(n) { /*... your code ... */ } + +alert( sumTo(100) ); // 5050 +``` + +P.S. Which solution variant is the fastest? The slowest? Why? + +P.P.S. Can we use recursion to count `sumTo(100000)`? diff --git a/1-js/8-more-functions/01-recursion/02-factorial/solution.md b/1-js/8-more-functions/01-recursion/02-factorial/solution.md new file mode 100644 index 00000000..59040a2b --- /dev/null +++ b/1-js/8-more-functions/01-recursion/02-factorial/solution.md @@ -0,0 +1,21 @@ +By definition, a factorial is `n!` can be written as `n * (n-1)!`. + +In other words, the result of `factorial(n)` can be calculated as `n` multiplied by the result of `factorial(n-1)`. And the call for `n-1` can recursively descend lower, and lower, till `1`. + +```js run +function factorial(n) { + return (n != 1) ? n * factorial(n - 1) : 1; +} + +alert( factorial(5) ); // 120 +``` + +The basis of recursion is the value `1`. We can also make `0` the basis here, doesn't matter much, but gives one more recursive step: + +```js run +function factorial(n) { + return n ? n * factorial(n - 1) : 1; +} + +alert( factorial(5) ); // 120 +``` diff --git a/1-js/8-more-functions/01-recursion/02-factorial/task.md b/1-js/8-more-functions/01-recursion/02-factorial/task.md new file mode 100644 index 00000000..d2aef2d9 --- /dev/null +++ b/1-js/8-more-functions/01-recursion/02-factorial/task.md @@ -0,0 +1,31 @@ +importance: 4 + +--- + +# Calculate factorial + +The [factorial](https://en.wikipedia.org/wiki/Factorial) of a natural number is a number multiplied by `"number minus one"`, then by `"number minus two"`, and so on till `1`. The factorial of `n` is denoted as `n!` + +We can write a definition of factorial like this: + +```js +n! = n * (n - 1) * (n - 2) * ...*1 +``` + +Values of factorials for different `n`: + +```js +1! = 1 +2! = 2 * 1 = 2 +3! = 3 * 2 * 1 = 6 +4! = 4 * 3 * 2 * 1 = 24 +5! = 5 * 4 * 3 * 2 * 1 = 120 +``` + +The task is to write a function `factorial(n)` that calculates `n!` using recursive calls. + +```js +alert( factorial(5) ); // 120 +``` + +P.S. Hint: `n!` can be written as `n * (n-1)!` For instance: `3! = 3*2! = 3*2*1! = 6` diff --git a/1-js/8-more-functions/01-recursion/03-fibonacci-numbers/fibonacci-recursion-tree.png b/1-js/8-more-functions/01-recursion/03-fibonacci-numbers/fibonacci-recursion-tree.png new file mode 100644 index 00000000..c45418ff Binary files /dev/null and b/1-js/8-more-functions/01-recursion/03-fibonacci-numbers/fibonacci-recursion-tree.png differ diff --git a/1-js/8-more-functions/01-recursion/03-fibonacci-numbers/fibonacci-recursion-tree@2x.png b/1-js/8-more-functions/01-recursion/03-fibonacci-numbers/fibonacci-recursion-tree@2x.png new file mode 100644 index 00000000..6fc39ae1 Binary files /dev/null and b/1-js/8-more-functions/01-recursion/03-fibonacci-numbers/fibonacci-recursion-tree@2x.png differ diff --git a/1-js/8-more-functions/01-recursion/03-fibonacci-numbers/solution.md b/1-js/8-more-functions/01-recursion/03-fibonacci-numbers/solution.md new file mode 100644 index 00000000..91bcecc0 --- /dev/null +++ b/1-js/8-more-functions/01-recursion/03-fibonacci-numbers/solution.md @@ -0,0 +1,110 @@ +The first solution we could try here is the recursive one. + +Fibonacci numbers are recursive by definition: + +```js run +function fib(n) { + return n <= 1 ? n : fib(n - 1) + fib(n - 2); +} + +alert( fib(3) ); // 2 +alert( fib(7) ); // 13 +// fib(77); // will be extremely slow! +``` + +...But for big values of `n` it's very slow. For instance, `fib(77)` may hang up the engine for some time eating all CPU resources. + +That's because the function makes too many subcalls. The same values are re-evaluated again and again. + +For instance, let's see a piece of calculations for `fib(5)`: + +```js no-beautify +... +fib(5) = fib(4) + fib(3) +fib(4) = fib(3) + fib(2) +... +``` + +Here we can see that the value of `fib(3)` is needed for both `fib(5)` and `fib(4)`. So `fib(3)` will be called and evaluated two times completely independently. + +Here's the full recursion tree: + +![fibonacci recursion tree](fibonacci-recursion-tree.png) + +We can clearly notice that `fib(3)` is evaluated two times and `fib(2)` is evaluated three times. The total amount of computations grows much faster than `n`, making it enormous even for `n=77`. + +We can optimize that by remembering already-evaluated values: if a value of say `fib(3)` is calculated once, then we can just reuse it in future computations. + +Another variant would be to give up recursion and use a totally different loop-based algorithm. + +Instead of going from `n` down to lower values, we can make a loop that starts from `1` and `2`, then gets `fib(3)` as their sum, then `fib(4)` as the sum of two previous values, then `fib(5)` and goes up and up, till it gets to the needed value. On each step we only need to remember two previous values. + +Here are the steps of the new algorithm in details. + +The start: + +```js +// a = fib(1), b = fib(2), these values are by definition 1 +let a = 1, b = 1; + +// get c = fib(3) as their sum +let c = a + b; + +/* we now have fib(1), fib(2), fib(3) +a b c +1, 1, 2 +*/ +``` + +Now we want to get `fib(4) = fib(2) + fib(3)`. + +Let's shift the variables: `a,b` will get `fib(2),fib(3)`, and `c` will get their sum: + +```js no-beautify +a = b; // now a = fib(2) +b = c; // now b = fib(3) +c = a + b; // c = fib(4) + +/* now we have the sequence: + a b c +1, 1, 2, 3 +*/ +``` + +The next step gives another sequence number: + +```js no-beautify +a = b; // now a = fib(3) +b = c; // now b = fib(4) +c = a + b; // c = fib(5) + +/* now the sequence is (one more number): + a b c +1, 1, 2, 3, 5 +*/ +``` + +...And so on until we get the needed value. That's much faster than recursion and involves no duplicate computations. + +The full code: + +```js run +function fib(n) { + let a = 1; + let b = 1; + for (let i = 3; i <= n; i++) { + let c = a + b; + a = b; + b = c; + } + return b; +} + +alert( fib(3) ); // 2 +alert( fib(7) ); // 13 +alert( fib(77) ); // 5527939700884757 +``` + +The loop starts with `i=3`, because the first and the second sequence values are hard-coded into variables `a=1`, `b=1`. + +The approach is called [dynamic programming bottom-up](https://en.wikipedia.org/wiki/Dynamic_programming). diff --git a/1-js/8-more-functions/01-recursion/03-fibonacci-numbers/task.md b/1-js/8-more-functions/01-recursion/03-fibonacci-numbers/task.md new file mode 100644 index 00000000..3cdadd21 --- /dev/null +++ b/1-js/8-more-functions/01-recursion/03-fibonacci-numbers/task.md @@ -0,0 +1,25 @@ +importance: 5 + +--- + +# Fibonacci numbers + +The sequence of [Fibonacci numbers](https://en.wikipedia.org/wiki/Fibonacci_number) has the formula Fn = Fn-1 + Fn-2. In other words, the next number is a sum of the two preceding ones. + +First two numbers are `1`, then `2(1+1)`, then `3(1+2)`, `5(2+3)` and so on: `1, 1, 2, 3, 5, 8, 13, 21...`. + +Fibonacci numbers are related to the [Golden ratio](https://en.wikipedia.org/wiki/Golden_ratio) and many natural phenomena around us. + +Write a function `fib(n)` that returns the `n-th` Fibonacci number. + +An example of work: + +```js +function fib(n) { /* your code */ } + +alert(fib(3)); // 2 +alert(fib(7)); // 13 +alert(fib(77)); // 5527939700884757 +``` + +P.S. The function should be fast. The call to `fib(77)` should take no more than a fraction of a second. diff --git a/1-js/8-more-functions/01-recursion/04-output-single-linked-list/solution.md b/1-js/8-more-functions/01-recursion/04-output-single-linked-list/solution.md new file mode 100644 index 00000000..4e9de146 --- /dev/null +++ b/1-js/8-more-functions/01-recursion/04-output-single-linked-list/solution.md @@ -0,0 +1,88 @@ +# Loop-based solution + +The loop-based variant of the solution: + +```js run +let list = { + value: 1, + next: { + value: 2, + next: { + value: 3, + next: { + value: 4, + next: null + } + } + } +}; + +function printList(list) { + let tmp = list; + + while (tmp) { + alert(tmp.value); + tmp = tmp.next; + } + +} + +printList(list); +``` + +Please note that we use a temporary variable `tmp` to walk over the list. Technically, we could use a function parameter `list` instead: + +```js +function printList(list) { + + while(*!*list*/!*) { + alert(list.value); + list = list.next; + } + +} +``` + +...But that would be unwise. In the future we may need to extend a function, do something else with the list. If we change `list`, then we loose such ability. + +Talking about good variable names, `list` here is the list itself. The first element of it. And it should remain like that. That's clear and reliable. + +From the other side, the role of `tmp` is exclusively a list traversal, like `i` in the `for` loop. + +# Recursive solution + +The recursive variant of `printList(list)` follows a simple logic: to output a list we should output the current element `list`, then do the same for `list.next`: + +```js run +let list = { + value: 1, + next: { + value: 2, + next: { + value: 3, + next: { + value: 4, + next: null + } + } + } +}; + +function printList(list) { + + alert(list.value); // output the current item + + if (list.next) { + printList(list.next); // do the same for the rest of the list + } + +} + +printList(list); +``` + +Now what's better? + +Technically, the loop is more effective. These two variants do the same, but the loop does not spend resources for nested function calls. + +From the other side, the recursive variant is shorter and sometimes easier to understand. diff --git a/1-js/8-more-functions/01-recursion/04-output-single-linked-list/task.md b/1-js/8-more-functions/01-recursion/04-output-single-linked-list/task.md new file mode 100644 index 00000000..1076b952 --- /dev/null +++ b/1-js/8-more-functions/01-recursion/04-output-single-linked-list/task.md @@ -0,0 +1,29 @@ +importance: 5 + +--- + +# Output a single-linked list + +Let's say we have a single-linked list (as described in the chapter ): + +```js +let list = { + value: 1, + next: { + value: 2, + next: { + value: 3, + next: { + value: 4, + next: null + } + } + } +}; +``` + +Write a function `printList(list)` that outputs list items one-by-one. + +Make two variants of the solution: using a loop and using recursion. + +What's better: with recursion or without it? diff --git a/1-js/8-more-functions/01-recursion/05-output-single-linked-list-reverse/solution.md b/1-js/8-more-functions/01-recursion/05-output-single-linked-list-reverse/solution.md new file mode 100644 index 00000000..a9ba0baf --- /dev/null +++ b/1-js/8-more-functions/01-recursion/05-output-single-linked-list-reverse/solution.md @@ -0,0 +1,74 @@ +# Using a recursion + +The recursive logic is a little bit tricky here. + +We need to first output the rest of the list and *then* output the current one: + +```js run +let list = { + value: 1, + next: { + value: 2, + next: { + value: 3, + next: { + value: 4, + next: null + } + } + } +}; + +function printReverseList(list) { + + if (list.next) { + printReverseList(list.next); + } + + alert(list.value); +} + +printReverseList(list); +``` + +# Using a loop + +The loop variant is also a little bit more complicated then the direct output. + +There is no way to get the last value in our `list`. We also can't "go back". + +So what we can do is to first go through the items in the direct order and rememeber them in an array, and then output what we remembered in the reverse order: + +```js run +let list = { + value: 1, + next: { + value: 2, + next: { + value: 3, + next: { + value: 4, + next: null + } + } + } +}; + +function printReverseList(list) { + let arr = []; + let tmp = list; + + while (tmp) { + arr.push(tmp.value); + tmp = tmp.next; + } + + for (let i = arr.length - 1; i >= 0; i--) { + alert( arr[i] ); + } +} + +printReverseList(list); +``` + +Please note that the recursive solution actually does exactly the same: it follows the list, remembers the items in the chain of nested calls (in the execution context stack), and then outputs them. diff --git a/1-js/8-more-functions/01-recursion/05-output-single-linked-list-reverse/task.md b/1-js/8-more-functions/01-recursion/05-output-single-linked-list-reverse/task.md new file mode 100644 index 00000000..81b1f3e3 --- /dev/null +++ b/1-js/8-more-functions/01-recursion/05-output-single-linked-list-reverse/task.md @@ -0,0 +1,9 @@ +importance: 5 + +--- + +# Output a single-linked list in the reverse order + +Output a single-linked list from the previous task in the reverse order. + +Make two solutions: using a loop and using a recursion. diff --git a/1-js/8-more-functions/1-recursion/article.md b/1-js/8-more-functions/01-recursion/article.md similarity index 79% rename from 1-js/8-more-functions/1-recursion/article.md rename to 1-js/8-more-functions/01-recursion/article.md index 74a1a58c..78f89e55 100644 --- a/1-js/8-more-functions/1-recursion/article.md +++ b/1-js/8-more-functions/01-recursion/article.md @@ -1,24 +1,30 @@ # Recursion and stack -A function solves a task. In the process it can call many other functions. A partial case of this is when a function calls *itself*. +Let's return to functions and study them more in-depth. -That's called *recursion*. +Our first topic will be *recursion*. -Recursion is useful in situations when a task can be naturally split into several tasks of the same kind, but simpler. Or when a task can be simplified into an easy action plus a simpler variant of the same task. Or, as we'll see soon, to deal with certain data structures. +If you are not new to programming, then it is probably familiar and you could skip this chapter. -Recursion is a common programming topic, not specific to Javascript. If you developed in other languages or studied algorithms, then you probably already know what's this. +Recursion is a programming pattern that is useful in situations when a task can be naturally split into several tasks of the same kind, but simpler. Or when a task can be simplified into an easy action plus a simpler variant of the same task. Or, as we'll see soon, to deal with certain data structures. -The chapter is mostly meant for those who are unfamiliar with the topic and want to study better how functions work. +When a function solves a task, in the process it can call many other functions. A partial case of this is when a function calls *itself*. That's called *recursion*. [cut] ## Two ways of thinking -For something simple to start with -- let's consider a task of raising `x` to a natural power of `n`. In other words, we need `x` multiplied by itself `n` times. +For something simple to start with -- let's write a function `pow(x, n)` that raises `x` to a natural power of `n`. In other words, multiplies `x` by itself `n` times. -There are two ways to solve it. +```js +pow(2, 2) = 4 +pow(2, 3) = 8 +pow(2, 4) = 16 +``` -1. Iterative thinking -- the `for` loop: +There are two ways to implement it. + +1. Iterative thinking: the `for` loop: ```js run function pow(x, n) { @@ -35,7 +41,7 @@ There are two ways to solve it. alert( pow(2, 3) ); // 8 ``` -2. Recursive thinking -- simplify the task and call self: +2. Recursive thinking: simplify the task and call self: ```js run function pow(x, n) { @@ -49,18 +55,20 @@ There are two ways to solve it. alert( pow(2, 3) ); // 8 ``` -The recursive variant has two branches: +Please note how the recursive variant is fundamentally different. + +When `pow(x, n)` is called, the execution splits into two branches: ```js - if n==1 = x + if n==1 = x / -pow(x, n) = - \ = x * pow(x, n - 1) - else +pow(x, n) = + \ + else = x * pow(x, n - 1) ``` -1. The `n==1` is the trivial variant. It is called *the base* of recursion, because it immediately produces the obvious result: `pow(x, 1)` equals `x`. -2. Otherwise, we can represent `pow(x, n)` as `x * pow(x, n-1)`. In maths, one would write xn = x * xn-1. This is called *a recursive step*, because we transform the task into a simpler action (multiplication by `x`) and a simpler call of the same task (`pow` with lower `n`). Next steps simplify it further and further untill `n` reaches `1`. +1. If `n==1`, then everything is trivial. It is called *the base* of recursion, because it immediately produces the obvious result: `pow(x, 1)` equals `x`. +2. Otherwise, we can represent `pow(x, n)` as `x * pow(x, n-1)`. In maths, one would write xn = x * xn-1. This is called *a recursive step*: we transform the task into a simpler action (multiplication by `x`) and a simpler call of the same task (`pow` with lower `n`). Next steps simplify it further and further untill `n` reaches `1`. We can also say that `pow` *recursively calls itself* till `n == 1`. @@ -69,17 +77,17 @@ We can also say that `pow` *recursively calls itself* till `n == 1`. For example, to calculate `pow(2, 4)` the recursive variant does these steps: -1. `pow(2, 4) = 2 * pow(2, 3)` +1. `pow(2, 4) = 2 * pow(2, 3)` 2. `pow(2, 3) = 2 * pow(2, 2)` 3. `pow(2, 2) = 2 * pow(2, 1)` 4. `pow(2, 1) = 2` -So, the recursion can be used to reduce a function call to a simpler one, and then -- to even more simpler, and so on, until the result becomes obvious. +So, the recursion reduces a function call to a simpler one, and then -- to even more simpler, and so on, until the result becomes obvious. ````smart header="Recursion is usually shorter" -A recursive solution is usually shorter than iterative. +A recursive solution is usually shorter than an iterative one. -Here we can write the same using the ternary `?` operator instead of `if` to make it `pow(x, n)` more terse and still very readable: +Here we can rewrite the same using the ternary `?` operator instead of `if` to make `pow(x, n)` more terse and still very readable: ```js run function pow(x, n) { @@ -96,19 +104,26 @@ That limits the application of recursion, but it still remains very wide. There ## The execution stack -Now let's examine how recursive calls work. For that we'll look under the hood of functions, how they work. +Now let's examine how recursive calls work. For that we'll look under the hood of functions. The information about a function run is stored in its *execution context*. -The [execution context](https://tc39.github.io/ecma262/#sec-execution-contexts) is an internal data structure that contains details about the execution: where the control flow is now, current variables, the value of `this` (we don't use it here) and few other internal details. +The [execution context](https://tc39.github.io/ecma262/#sec-execution-contexts) is an internal data structure that contains details about the execution of a function: where the control flow is now, the current variables, the value of `this` (we don't use it here) and few other internal details. -Execution contexts together form an *execution stack*. When a subcall happens, current execution context is pushed on top of the stack, and when it finishes -- popped of it and resumed. +One function call has exactly one execution context associated with it. + +When a function makes a nested call, the following happens: + +- The current function is paused. +- The execution context associated with it is remembered in a special data structure called *execution context stack*. +- The nested call executes. +- After it ends, the old execution context is retrieved from the stack, and the outer function is resumed from where it stopped. Let's see what happens during the `pow(2, 3)` call. ### pow(2, 3) -In the beginning of the call `pow(2, 3)` the execution context will store variables: `x = 2, n = 3`, the execution flow is at line `1` of the function. +In the beginning of the call `pow(2, 3)` the execution context will store variables: `x = 2, n = 3`, the execution flow is at line `1` of the function. We can sketch it as: @@ -123,13 +138,13 @@ That's when the function starts to execute. The condition `n == 1` is falsy, so ```js run function pow(x, n) { - if (n == 1) { + if (n == 1) { return x; } else { *!* return x * pow(x, n - 1); */!* - } + } } alert( pow(2, 3) ); @@ -145,7 +160,7 @@ The variables are same, but the line changes, so the context is now: -To calculate `x*pow(x, n-1)`, we need to make a subcall of `pow` with new arguments. +To calculate `x*pow(x, n-1)`, we need to make a subcall of `pow` with new arguments `pow(2, 2)`. ### pow(2, 2) @@ -153,7 +168,7 @@ To do a nested call, Javascript remembers the current execution context in the * Here we call the same function `pow`, but it absolutely doesn't matter. The process is the same for all functions: -1. The current context is "pushed away" on top of the stack. +1. The current context is "remembered" on top of the stack. 2. The new context is created for the subcall. 3. When the subcall is finished -- the previous context is popped from the stack, and its execution continues. @@ -203,17 +218,17 @@ During the execution of `pow(2, 1)`, unlike before, the condition `n == 1` is tr ```js function pow(x, n) { - if (n == 1) { + if (n == 1) { *!* return x; */!* } else { return x * pow(x, n - 1); - } + } } ``` -There are no more nested calls, so the function finishes, returning `2`. +There are no more nested calls, so the function finishes, returning `2`. As the function finishes, its execution context is not needed any more, so it's removed from the memory. The previous one is restored off-top of the stack: @@ -266,7 +281,7 @@ The iterative `pow` uses a single context changing `i` and `result` in the proce **Any recursion can be rewritten as a loop. The loop variant usually can be made more effective.** -...But sometimes the rewrite is non-trivial, especially when function uses different recursive subcalls depending on conditions and merges their results or when the branching is more intricate. And the optimization may be unneeded and totally not worth the efforts. +...But sometimes the rewrite is non-trivial, especially when function uses different recursive subcalls depending on conditions and merges their results or when the branching is more intricate. And the optimization may be unneeded and totally not worth the efforts. Recursion can give a shorter code, easier to understand and support. Optimizations are not required in every place, mostly we need a good code, that's why it's used. @@ -303,7 +318,7 @@ let company = { }; ``` -In other words, a company has departments. +In other words, a company has departments. - A department may have an array of staff. For instance, `sales` department has 2 employees: John and Alice. - Or a department may split into subdepartments, like `development` has two branches: `sites` and `internals`. Each of them has the own staff. @@ -315,7 +330,7 @@ Now let's say we want a function to get the sum of all salaries. How can we do t An iterative approach is not easy, because the structure is not simple. The first idea may be to make a `for` loop over `company` with nested subloop over 1st level departments. But then we need more nested subloops to iterate over the staff in 2nd level departments like `sites`. ...And then another subloop inside those for 3rd level departments that might appear in the future? Should we stop on level 3 or make 4 levels of loops? If we put 3-4 nested subloops in the code to traverse a single object, it becomes rather ugly. -Let's try recursion. +Let's try recursion. As we can see, when our function gets a department to sum, there are two possible cases: @@ -356,7 +371,7 @@ function sumSalaries(department) { alert(sumSalaries(company)); // 6700 ``` -The code is short and easy to understand (hopefully?). That's the power of recursion. It also works for any level of subdepartment nesting. +The code is short and easy to understand (hopefully?). That's the power of recursion. It also works for any level of subdepartment nesting. Here's the diagram of calls: @@ -393,7 +408,7 @@ For better understanding, we'll cover one more recursive structure named "Linked ### Linked list -Imagine, we want to store an ordered list of objects. +Imagine, we want to store an ordered list of objects. The natural choice would be an array: @@ -487,13 +502,15 @@ list.next = list.next.next; We made `list.next` jump over `1` to value `2`. The value `1` is now excluded from the chain. If it's not stored anywhere else, it will be automatically removed from the memory. -Unlike arrays, there's no mass-renumbering, we can easily rearrange elements. +Unlike arrays, there's no mass-renumbering, we can easily rearrange elements. -But it's a trade-off, because: -- We can't easily access an element by its number in the list. We need to start from the first one `list` and go `next` `N` times to get the Nth element. -- To insert or remove an element from the structure, we must first get the reference to its previous neighbour, and then alter its `next`. +Naturally, lists are not always better than arrays. Otherwise everyone would use only lists. -Although, sometimes these drawbacks do not matter. For instance, when we need a queue or even a [deque](https://en.wikipedia.org/wiki/Double-ended_queue) -- the ordered structure that must allow very fast adding/removing elements from both ends. For that we can add another variable named `tail` to track the last element of the list (and update it when adding/removing elements from the end). For large sets of elements the speed difference versus arrays will be huge. +The main drawback is that we can't easily access an element by its number. In an array that's easy: `arr[n]` is a direct reference. But in the list we need to start from the first item and go `next` `N` times to get the Nth element. + +...But we don't always need such operations. For instance, when we need a queue or even a [deque](https://en.wikipedia.org/wiki/Double-ended_queue) -- the ordered structure that must allow very fast adding/removing elements from both ends. + +Sometimes it's worth to add another variable named `tail` to track the last element of the list (and update it when adding/removing elements from the end). For large sets of elements the speed difference versus arrays is huge. ## Summary @@ -507,5 +524,3 @@ There are many applications of recursive calls: - Dealing with [recursively-defined](https://en.wikipedia.org/wiki/Recursive_data_type) data structures, like HTML documents or trees. - For "deep" cloning or inspection of complex objects. - ...etc - - diff --git a/1-js/8-more-functions/1-recursion/head.html b/1-js/8-more-functions/01-recursion/head.html similarity index 100% rename from 1-js/8-more-functions/1-recursion/head.html rename to 1-js/8-more-functions/01-recursion/head.html diff --git a/1-js/8-more-functions/1-recursion/linked-list-0.png b/1-js/8-more-functions/01-recursion/linked-list-0.png similarity index 100% rename from 1-js/8-more-functions/1-recursion/linked-list-0.png rename to 1-js/8-more-functions/01-recursion/linked-list-0.png diff --git a/1-js/8-more-functions/1-recursion/linked-list-0@2x.png b/1-js/8-more-functions/01-recursion/linked-list-0@2x.png similarity index 100% rename from 1-js/8-more-functions/1-recursion/linked-list-0@2x.png rename to 1-js/8-more-functions/01-recursion/linked-list-0@2x.png diff --git a/1-js/8-more-functions/1-recursion/linked-list-remove-1.png b/1-js/8-more-functions/01-recursion/linked-list-remove-1.png similarity index 100% rename from 1-js/8-more-functions/1-recursion/linked-list-remove-1.png rename to 1-js/8-more-functions/01-recursion/linked-list-remove-1.png diff --git a/1-js/8-more-functions/1-recursion/linked-list-remove-1@2x.png b/1-js/8-more-functions/01-recursion/linked-list-remove-1@2x.png similarity index 100% rename from 1-js/8-more-functions/1-recursion/linked-list-remove-1@2x.png rename to 1-js/8-more-functions/01-recursion/linked-list-remove-1@2x.png diff --git a/1-js/8-more-functions/1-recursion/linked-list-split.png b/1-js/8-more-functions/01-recursion/linked-list-split.png similarity index 100% rename from 1-js/8-more-functions/1-recursion/linked-list-split.png rename to 1-js/8-more-functions/01-recursion/linked-list-split.png diff --git a/1-js/8-more-functions/1-recursion/linked-list-split@2x.png b/1-js/8-more-functions/01-recursion/linked-list-split@2x.png similarity index 100% rename from 1-js/8-more-functions/1-recursion/linked-list-split@2x.png rename to 1-js/8-more-functions/01-recursion/linked-list-split@2x.png diff --git a/1-js/8-more-functions/1-recursion/linked-list.png b/1-js/8-more-functions/01-recursion/linked-list.png similarity index 100% rename from 1-js/8-more-functions/1-recursion/linked-list.png rename to 1-js/8-more-functions/01-recursion/linked-list.png diff --git a/1-js/8-more-functions/1-recursion/linked-list@2x.png b/1-js/8-more-functions/01-recursion/linked-list@2x.png similarity index 100% rename from 1-js/8-more-functions/1-recursion/linked-list@2x.png rename to 1-js/8-more-functions/01-recursion/linked-list@2x.png diff --git a/1-js/8-more-functions/1-recursion/recursion-pow.png b/1-js/8-more-functions/01-recursion/recursion-pow.png similarity index 100% rename from 1-js/8-more-functions/1-recursion/recursion-pow.png rename to 1-js/8-more-functions/01-recursion/recursion-pow.png diff --git a/1-js/8-more-functions/1-recursion/recursion-pow@2x.png b/1-js/8-more-functions/01-recursion/recursion-pow@2x.png similarity index 100% rename from 1-js/8-more-functions/1-recursion/recursion-pow@2x.png rename to 1-js/8-more-functions/01-recursion/recursion-pow@2x.png diff --git a/1-js/8-more-functions/1-recursion/recursive-salaries.png b/1-js/8-more-functions/01-recursion/recursive-salaries.png similarity index 100% rename from 1-js/8-more-functions/1-recursion/recursive-salaries.png rename to 1-js/8-more-functions/01-recursion/recursive-salaries.png diff --git a/1-js/8-more-functions/1-recursion/recursive-salaries@2x.png b/1-js/8-more-functions/01-recursion/recursive-salaries@2x.png similarity index 100% rename from 1-js/8-more-functions/1-recursion/recursive-salaries@2x.png rename to 1-js/8-more-functions/01-recursion/recursive-salaries@2x.png diff --git a/1-js/8-more-functions/02-rest-parameters-spread-operator/article.md b/1-js/8-more-functions/02-rest-parameters-spread-operator/article.md new file mode 100644 index 00000000..68e57f4a --- /dev/null +++ b/1-js/8-more-functions/02-rest-parameters-spread-operator/article.md @@ -0,0 +1,229 @@ +# Rest parameters and spread operator + +Many Javascript built-in functions support on arbitrary number of arguments. + +For instance: + +- `Math.max(arg1, arg2, ..., argN)` -- returns the greatest of the arguments. +- `Object.assign(dest, src1, ..., srcN)` -- copies properties from `src1..N` into `dest`. +- ...and so on. + +In this chapter we'll see how to do the same. And, more important, how to feel comfortable working with such functions and arrays. + +[cut] + +## Rest parameters `...` + +A function can be called with any number of arguments, no matter how it is defined. + +Like here: +```js run +function sum(a, b) { + return a + b; +} + +alert( sum(1, 2, 3, 4, 5) ); +``` + +There will be no error because of "excessive" arguments. But of course in the result only the first two will be counted. + +The rest parameters can be mentioned in a function definition with three dots `...`. They literally mean: "gather the remaining parameters into an array". + +For instance, to gather all arguments into array `args`: + +```js run +function sumAll(...args) { // args is the name for the array + 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 can choose to get first parameters as variables, and gather only the rest. + +Here the first two arguments go into variables and the rest goes to `titles` array: + +```js run +function showName(firstName, lastName, ...titles) { + alert( firstName + ' ' + lastName ); // Julius Caesar + + // the rest = ["Consul", "Imperator"] + alert( rest[0] ); // Consul + alert( rest[1] ); // Imperator + alert( rest.length ); // 2 +} + +showName("Julius", "Caesar", "Consul", "Imperator"); +``` + +````warn header="The rest parameters must be at the end" +The rest parameters gather 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. +```` + +## The "arguments" variable + +There is also a special array-like object named `arguments` that contains all arguments by their index. + +For instance: + +```js run +function showName() { + alert( arguments.length ); + alert( arguments[0] ); + alert( arguments[1] ); + + // it's iterable + // for(let arg of arguments) alert(arg); +} + +// shows: 2, Julius, Caesar +showName("Julius", "Caesar"); + +// shows: 1, Ilya, undefined (no second argument) +showName("Ilya"); +``` + +In old times, rest parameters did not exist in the language, and `arguments` was the only way to get all arguments of the function no matter of their total number. + +And it still works, we can use it. + +But the downside is that though `arguments` is both array-like and iterable, it's not an array. It does not support array methods, so we can't say call `arguments.map(...)`. + +Also, it always has all arguments in it, we can't capture them partially, like we did with rest parameters. + +So when we need these features, then rest parameters are preferred. + +## Spread operator [#spread-operator] + +We've just seen how to get an array from the list of parameters. + +But sometimes we need to do exactly the reverse. + +For instance, there's a built-in function [Math.max](mdn:js/Math/max) that returns the greatest number from the list: + +```js run +alert( Math.max(3, 5, 1) ); // 5 +``` + +Now let's say we have an array `[3, 5, 1]`. How to call `Math.max` with it? + +Passing it "as it" won't work, because `Math.max` expects a list of numeric arguments, not a single array: + +```js run +let arr = [3, 5, 1]; + +*!* +alert( Math.max(arr) ); // NaN +*/!* +``` + +...And surely we can't manually list items in the code `Math.max(arg[0], arg[1], arg[2])`, because we may be unsure how much are there. As our script executes, there might be many, or there might be none. Also that would be ugly. + +*Spread operator* to the rescue. It looks similar to rest parameters, also using `...`, but does quite the opposite. + +When `...arr` is used in the function call, it "expands" an iterable object `arr` into the list of arguments. + +For `Math.max`: + +```js run +let arr = [3, 5, 1]; + +alert( Math.max(...arr) ); // 5 (spread turns array into a list of arguments) +``` + +We also can pass multiple iterables this way: + +```js run +let arr1 = [1, -2, 3, 4]; +let arr2 = [8, 3, -8, 1]; + +alert( Math.max(...arr1, ...arr2) ); // 8 +``` + +...And even combine the spread operator with normal values: + + +```js run +let arr1 = [1, -2, 3, 4]; +let arr2 = [8, 3, -8, 1]; + +alert( Math.max(1, ...arr1, 2, ...arr2, 25) ); // 25 +``` + +Also spread operator can be used to merge arrays: + +```js run +let arr = [3, 5, 1]; +let arr2 = [8, 9, 15]; + +*!* +let merged = [0, ...arr, 2, ...arr2]; +*/!* + +alert(merged); // 0,3,5,1,2,8,9,15 (0, then arr, then 2, then arr2) +``` + +In the examples above we used an array to demonstrate the spread operator, but any iterable will do. + +For instance, here we use spread operator to turn the string into array of characters: + +```js run +let str = "Hello"; + +alert( [...str] ); // H,e,l,l,o +``` + +The spread operator internally uses iterators to gather elements, the same way as `for..of` does. + +So, for a string, `for..of` returns characters and `...str` becomes `"h","e","l","l","o"`. The list of characters is passed to array initializer `[...str]`. + +For this particular task we could also use `Array.from`, because it converts an iterable (like a string) into an array: + +```js run +let str = "Hello"; + +// Array.from converts an iterable into an array +alert( Array.from(str) ); // H,e,l,l,o +``` + +The result is the same as `[...str]`. + +But there's a subtle difference between `Array.from(obj)` and `[...obj]`: + +- `Array.from` operates on both array-likes and iterables. +- The spread operator operates only on iterables. + +So, for the task of turning something into an array, `Array.from` appears more universal. + + +## Summary + +When we see `"..."` in the code, it is either rest parameters or the spread operator. + +There's an easy way to distinguish between them: + +- When `...` is at the end of function parameters, it's "rest parameters" and gathers the rest of the list into the array. +- When `...` occurs in a function call or alike, it's called a "spread operator" and expands an array into the list. + +Use patterns: + +- Rest parameters are used to create functions that accept any number of arguments. +- The spread operator is used to pass an array to functions that normally require a list of many arguments. + +Together they help to travel between a list and an array of parameters with ease. + +All arguments of a function call are also available in "old-style" `arguments`: array-like iterable object. diff --git a/1-js/8-more-functions/2-closure/1-counter-independent/solution.md b/1-js/8-more-functions/03-closure/1-counter-independent/solution.md similarity index 100% rename from 1-js/8-more-functions/2-closure/1-counter-independent/solution.md rename to 1-js/8-more-functions/03-closure/1-counter-independent/solution.md diff --git a/1-js/8-more-functions/2-closure/1-counter-independent/task.md b/1-js/8-more-functions/03-closure/1-counter-independent/task.md similarity index 100% rename from 1-js/8-more-functions/2-closure/1-counter-independent/task.md rename to 1-js/8-more-functions/03-closure/1-counter-independent/task.md diff --git a/1-js/8-more-functions/2-closure/2-counter-object-independent/solution.md b/1-js/8-more-functions/03-closure/2-counter-object-independent/solution.md similarity index 100% rename from 1-js/8-more-functions/2-closure/2-counter-object-independent/solution.md rename to 1-js/8-more-functions/03-closure/2-counter-object-independent/solution.md diff --git a/1-js/8-more-functions/2-closure/2-counter-object-independent/task.md b/1-js/8-more-functions/03-closure/2-counter-object-independent/task.md similarity index 100% rename from 1-js/8-more-functions/2-closure/2-counter-object-independent/task.md rename to 1-js/8-more-functions/03-closure/2-counter-object-independent/task.md diff --git a/1-js/8-more-functions/2-closure/3-function-in-if/solution.md b/1-js/8-more-functions/03-closure/3-function-in-if/solution.md similarity index 100% rename from 1-js/8-more-functions/2-closure/3-function-in-if/solution.md rename to 1-js/8-more-functions/03-closure/3-function-in-if/solution.md diff --git a/1-js/8-more-functions/2-closure/3-function-in-if/task.md b/1-js/8-more-functions/03-closure/3-function-in-if/task.md similarity index 100% rename from 1-js/8-more-functions/2-closure/3-function-in-if/task.md rename to 1-js/8-more-functions/03-closure/3-function-in-if/task.md diff --git a/1-js/8-more-functions/2-closure/4-closure-sum/solution.md b/1-js/8-more-functions/03-closure/4-closure-sum/solution.md similarity index 100% rename from 1-js/8-more-functions/2-closure/4-closure-sum/solution.md rename to 1-js/8-more-functions/03-closure/4-closure-sum/solution.md diff --git a/1-js/8-more-functions/2-closure/4-closure-sum/task.md b/1-js/8-more-functions/03-closure/4-closure-sum/task.md similarity index 100% rename from 1-js/8-more-functions/2-closure/4-closure-sum/task.md rename to 1-js/8-more-functions/03-closure/4-closure-sum/task.md diff --git a/1-js/8-more-functions/2-closure/6-filter-through-function/_js.view/solution.js b/1-js/8-more-functions/03-closure/6-filter-through-function/_js.view/solution.js similarity index 100% rename from 1-js/8-more-functions/2-closure/6-filter-through-function/_js.view/solution.js rename to 1-js/8-more-functions/03-closure/6-filter-through-function/_js.view/solution.js diff --git a/1-js/8-more-functions/2-closure/6-filter-through-function/_js.view/source.js b/1-js/8-more-functions/03-closure/6-filter-through-function/_js.view/source.js similarity index 100% rename from 1-js/8-more-functions/2-closure/6-filter-through-function/_js.view/source.js rename to 1-js/8-more-functions/03-closure/6-filter-through-function/_js.view/source.js diff --git a/1-js/8-more-functions/2-closure/6-filter-through-function/_js.view/test.js b/1-js/8-more-functions/03-closure/6-filter-through-function/_js.view/test.js similarity index 100% rename from 1-js/8-more-functions/2-closure/6-filter-through-function/_js.view/test.js rename to 1-js/8-more-functions/03-closure/6-filter-through-function/_js.view/test.js diff --git a/1-js/8-more-functions/2-closure/6-filter-through-function/solution.md b/1-js/8-more-functions/03-closure/6-filter-through-function/solution.md similarity index 100% rename from 1-js/8-more-functions/2-closure/6-filter-through-function/solution.md rename to 1-js/8-more-functions/03-closure/6-filter-through-function/solution.md diff --git a/1-js/8-more-functions/2-closure/6-filter-through-function/task.md b/1-js/8-more-functions/03-closure/6-filter-through-function/task.md similarity index 100% rename from 1-js/8-more-functions/2-closure/6-filter-through-function/task.md rename to 1-js/8-more-functions/03-closure/6-filter-through-function/task.md diff --git a/1-js/8-more-functions/2-closure/7-sort-by-field/solution.md b/1-js/8-more-functions/03-closure/7-sort-by-field/solution.md similarity index 100% rename from 1-js/8-more-functions/2-closure/7-sort-by-field/solution.md rename to 1-js/8-more-functions/03-closure/7-sort-by-field/solution.md diff --git a/1-js/8-more-functions/2-closure/7-sort-by-field/task.md b/1-js/8-more-functions/03-closure/7-sort-by-field/task.md similarity index 100% rename from 1-js/8-more-functions/2-closure/7-sort-by-field/task.md rename to 1-js/8-more-functions/03-closure/7-sort-by-field/task.md diff --git a/1-js/8-more-functions/2-closure/8-make-army/_js.view/solution.js b/1-js/8-more-functions/03-closure/8-make-army/_js.view/solution.js similarity index 100% rename from 1-js/8-more-functions/2-closure/8-make-army/_js.view/solution.js rename to 1-js/8-more-functions/03-closure/8-make-army/_js.view/solution.js diff --git a/1-js/8-more-functions/2-closure/8-make-army/_js.view/source.js b/1-js/8-more-functions/03-closure/8-make-army/_js.view/source.js similarity index 100% rename from 1-js/8-more-functions/2-closure/8-make-army/_js.view/source.js rename to 1-js/8-more-functions/03-closure/8-make-army/_js.view/source.js diff --git a/1-js/8-more-functions/2-closure/8-make-army/_js.view/test.js b/1-js/8-more-functions/03-closure/8-make-army/_js.view/test.js similarity index 100% rename from 1-js/8-more-functions/2-closure/8-make-army/_js.view/test.js rename to 1-js/8-more-functions/03-closure/8-make-army/_js.view/test.js diff --git a/1-js/8-more-functions/2-closure/8-make-army/lexenv-makearmy.png b/1-js/8-more-functions/03-closure/8-make-army/lexenv-makearmy.png similarity index 100% rename from 1-js/8-more-functions/2-closure/8-make-army/lexenv-makearmy.png rename to 1-js/8-more-functions/03-closure/8-make-army/lexenv-makearmy.png diff --git a/1-js/8-more-functions/2-closure/8-make-army/lexenv-makearmy@2x.png b/1-js/8-more-functions/03-closure/8-make-army/lexenv-makearmy@2x.png similarity index 100% rename from 1-js/8-more-functions/2-closure/8-make-army/lexenv-makearmy@2x.png rename to 1-js/8-more-functions/03-closure/8-make-army/lexenv-makearmy@2x.png diff --git a/1-js/8-more-functions/2-closure/8-make-army/solution.md b/1-js/8-more-functions/03-closure/8-make-army/solution.md similarity index 100% rename from 1-js/8-more-functions/2-closure/8-make-army/solution.md rename to 1-js/8-more-functions/03-closure/8-make-army/solution.md diff --git a/1-js/8-more-functions/2-closure/8-make-army/task.md b/1-js/8-more-functions/03-closure/8-make-army/task.md similarity index 100% rename from 1-js/8-more-functions/2-closure/8-make-army/task.md rename to 1-js/8-more-functions/03-closure/8-make-army/task.md diff --git a/1-js/8-more-functions/2-closure/article.md b/1-js/8-more-functions/03-closure/article.md similarity index 63% rename from 1-js/8-more-functions/2-closure/article.md rename to 1-js/8-more-functions/03-closure/article.md index a4d5533b..b7806d73 100644 --- a/1-js/8-more-functions/2-closure/article.md +++ b/1-js/8-more-functions/03-closure/article.md @@ -1,25 +1,25 @@ # Closure -Javascript is a very function-oriented language, it gives a lot of freedom. A function can be created at one moment, then passed as a value to another variable or function and called from a totally different place much later. +Javascript is a very function-oriented language, it gives a lot of freedom. A function can be created at one moment, then passed as a value to another variable or function and called from a totally different place much later. -We know that a function can access variables outside of it. And this feature is used quite often. +We know that a function can access variables outside of it. And this feature is used quite often. -But what happens when outer variables have change? Does a function get a new value or the old one? +But what happens when outer variables change? Does a function get a new value or the old one? -Also, what happens when a function travels to another place of the code -- will it get access to variables in the new place? +Also, what happens when a variable with the function travels to another place of the code and is called from there -- will it get access to outer variables in the new place? -We realy should understand what's going on before doing complex things with functions. There is no general programming answer for that. Different languages behave differently. Here we'll cover Javascript. +There is no general programming answer for these questions. Different languages behave differently. Here we'll cover Javascript. [cut] ## A couple of questions -Let's formulate two questions for the seed, and then study internal mechanics piece-by-piece, so that you will be able to answer them and have no problems with similar ones in the future. +Let's formulate two questions for the seed, and then study internal mechanics piece-by-piece, so that you will be able to answer them and have no problems with more complex ones in the future. -1. A function uses an external variable `name`. The variable changes before function runs. Will it pick up a new variant? +1. The function `sayHi` uses an external variable `name`. The variable changes before function runs. Will it pick up a new variant? - ```js + ```js let name = "John"; function sayHi() { @@ -28,22 +28,24 @@ Let's formulate two questions for the seed, and then study internal mechanics pi name = "Pete"; + *!* sayHi(); // what will it show: "John" or "Pete"? + */!* ``` - Such situations are common in both browser and server-side development. A function may be assigned to execute on user action or network request etc. + Such situations are common in both browser and server-side development. A function may be scheduled to execute later than it is created: on user action or after a network request etc. So, the the question is: does it pick up latest changes? -2. A function can make another function and return it. That new function can be called from somewhere else. Will it have access to outer variables from its creation place or the invocation place or maybe both? +2. The function `makeWorker` makes another function and returns it. That new function can be called from somewhere else. Will it have access to outer variables from its creation place or the invocation place or maybe both? - ```js + ```js function makeWorker() { let name = "Pete"; return function() { - alert(name); + alert(name); }; } @@ -53,7 +55,9 @@ Let's formulate two questions for the seed, and then study internal mechanics pi let work = makeWorker(); // call it + *!* work(); // what will it show? "Pete" (name where created) or "John" (name where called)? + */!* ``` @@ -66,15 +70,15 @@ In Javascript, every running function, code block and the script as a whole have The Lexical Environment object consists of two parts: 1. *Environment Record* -- an object that has all local variables as its properties (and some other information like the value of `this`). -2. An reference to the *outer lexical environment*, the one associated with the structure right outside of it. +2. An reference to the *outer lexical environment*, usually the one associated with the code lexically right outside of it (outside of the current figure brackets). -So, a "variable" is just a property of the special internal object, Environment Record. "To get or change a variable" means "to get or change the property". +So, a "variable" is just a property of the special internal object, Environment Record. "To get or change a variable" means "to get or change the property of that object". For instance, in this simple code, there is only one Lexical Environment: ![lexical environment](lexical-environment-global.png) -This is a so-called global Lexical Environment, associated with the whole script. For browsers, all ` - ``` - - Here, first two alerts use the current window, and the latter two take variables from `iframe` window. Can be any variables if `iframe` originates from the same protocol/host/port. - -### "this" and global object - -There are two special cases that link `this` and global object. - -1. In the browser, the value of `this` in the global area is `window`: - - ```js run - // outside of functions - alert( this === window ); // true - ``` - - Other, non-browser environments, may use another value for `this` in such cases. - -2. When a function with `this` is called in not-strict mode, it gets the global object as `this`: - ```js run no-strict - // not in strict mode (!) - function f() { - alert(this); // [object Window] - } - - f(); // called without an object - ``` - - By specification, `this` in this case must be the global object, even in non-browser environments like Node.JS. That's for compatibility with old scripts, in strict mode `this` would be `undefined`. - diff --git a/1-js/8-more-functions/2-closure/lexenv-if.png b/1-js/8-more-functions/03-closure/lexenv-if.png similarity index 100% rename from 1-js/8-more-functions/2-closure/lexenv-if.png rename to 1-js/8-more-functions/03-closure/lexenv-if.png diff --git a/1-js/8-more-functions/2-closure/lexenv-if@2x.png b/1-js/8-more-functions/03-closure/lexenv-if@2x.png similarity index 100% rename from 1-js/8-more-functions/2-closure/lexenv-if@2x.png rename to 1-js/8-more-functions/03-closure/lexenv-if@2x.png diff --git a/1-js/8-more-functions/2-closure/lexenv-nested-makecounter-1.png b/1-js/8-more-functions/03-closure/lexenv-nested-makecounter-1.png similarity index 100% rename from 1-js/8-more-functions/2-closure/lexenv-nested-makecounter-1.png rename to 1-js/8-more-functions/03-closure/lexenv-nested-makecounter-1.png diff --git a/1-js/8-more-functions/2-closure/lexenv-nested-makecounter-1@2x.png b/1-js/8-more-functions/03-closure/lexenv-nested-makecounter-1@2x.png similarity index 100% rename from 1-js/8-more-functions/2-closure/lexenv-nested-makecounter-1@2x.png rename to 1-js/8-more-functions/03-closure/lexenv-nested-makecounter-1@2x.png diff --git a/1-js/8-more-functions/2-closure/lexenv-nested-makecounter-2.png b/1-js/8-more-functions/03-closure/lexenv-nested-makecounter-2.png similarity index 100% rename from 1-js/8-more-functions/2-closure/lexenv-nested-makecounter-2.png rename to 1-js/8-more-functions/03-closure/lexenv-nested-makecounter-2.png diff --git a/1-js/8-more-functions/2-closure/lexenv-nested-makecounter-2@2x.png b/1-js/8-more-functions/03-closure/lexenv-nested-makecounter-2@2x.png similarity index 100% rename from 1-js/8-more-functions/2-closure/lexenv-nested-makecounter-2@2x.png rename to 1-js/8-more-functions/03-closure/lexenv-nested-makecounter-2@2x.png diff --git a/1-js/8-more-functions/2-closure/lexenv-nested-makecounter-3.png b/1-js/8-more-functions/03-closure/lexenv-nested-makecounter-3.png similarity index 100% rename from 1-js/8-more-functions/2-closure/lexenv-nested-makecounter-3.png rename to 1-js/8-more-functions/03-closure/lexenv-nested-makecounter-3.png diff --git a/1-js/8-more-functions/2-closure/lexenv-nested-makecounter-3@2x.png b/1-js/8-more-functions/03-closure/lexenv-nested-makecounter-3@2x.png similarity index 100% rename from 1-js/8-more-functions/2-closure/lexenv-nested-makecounter-3@2x.png rename to 1-js/8-more-functions/03-closure/lexenv-nested-makecounter-3@2x.png diff --git a/1-js/8-more-functions/2-closure/lexenv-nested-makecounter-4.png b/1-js/8-more-functions/03-closure/lexenv-nested-makecounter-4.png similarity index 100% rename from 1-js/8-more-functions/2-closure/lexenv-nested-makecounter-4.png rename to 1-js/8-more-functions/03-closure/lexenv-nested-makecounter-4.png diff --git a/1-js/8-more-functions/2-closure/lexenv-nested-makecounter-4@2x.png b/1-js/8-more-functions/03-closure/lexenv-nested-makecounter-4@2x.png similarity index 100% rename from 1-js/8-more-functions/2-closure/lexenv-nested-makecounter-4@2x.png rename to 1-js/8-more-functions/03-closure/lexenv-nested-makecounter-4@2x.png diff --git a/1-js/8-more-functions/03-closure/lexenv-nested-makecounter-5.png b/1-js/8-more-functions/03-closure/lexenv-nested-makecounter-5.png new file mode 100644 index 00000000..527c4157 Binary files /dev/null and b/1-js/8-more-functions/03-closure/lexenv-nested-makecounter-5.png differ diff --git a/1-js/8-more-functions/03-closure/lexenv-nested-makecounter-5@2x.png b/1-js/8-more-functions/03-closure/lexenv-nested-makecounter-5@2x.png new file mode 100644 index 00000000..035dfd31 Binary files /dev/null and b/1-js/8-more-functions/03-closure/lexenv-nested-makecounter-5@2x.png differ diff --git a/1-js/8-more-functions/2-closure/lexenv-nested-makecounter-6.png b/1-js/8-more-functions/03-closure/lexenv-nested-makecounter-6.png similarity index 100% rename from 1-js/8-more-functions/2-closure/lexenv-nested-makecounter-6.png rename to 1-js/8-more-functions/03-closure/lexenv-nested-makecounter-6.png diff --git a/1-js/8-more-functions/2-closure/lexenv-nested-makecounter-6@2x.png b/1-js/8-more-functions/03-closure/lexenv-nested-makecounter-6@2x.png similarity index 100% rename from 1-js/8-more-functions/2-closure/lexenv-nested-makecounter-6@2x.png rename to 1-js/8-more-functions/03-closure/lexenv-nested-makecounter-6@2x.png diff --git a/1-js/8-more-functions/2-closure/lexenv-nested-work.png b/1-js/8-more-functions/03-closure/lexenv-nested-work.png similarity index 100% rename from 1-js/8-more-functions/2-closure/lexenv-nested-work.png rename to 1-js/8-more-functions/03-closure/lexenv-nested-work.png diff --git a/1-js/8-more-functions/2-closure/lexenv-nested-work@2x.png b/1-js/8-more-functions/03-closure/lexenv-nested-work@2x.png similarity index 100% rename from 1-js/8-more-functions/2-closure/lexenv-nested-work@2x.png rename to 1-js/8-more-functions/03-closure/lexenv-nested-work@2x.png diff --git a/1-js/8-more-functions/2-closure/lexical-environment-global-2.png b/1-js/8-more-functions/03-closure/lexical-environment-global-2.png similarity index 100% rename from 1-js/8-more-functions/2-closure/lexical-environment-global-2.png rename to 1-js/8-more-functions/03-closure/lexical-environment-global-2.png diff --git a/1-js/8-more-functions/2-closure/lexical-environment-global-2@2x.png b/1-js/8-more-functions/03-closure/lexical-environment-global-2@2x.png similarity index 100% rename from 1-js/8-more-functions/2-closure/lexical-environment-global-2@2x.png rename to 1-js/8-more-functions/03-closure/lexical-environment-global-2@2x.png diff --git a/1-js/8-more-functions/2-closure/lexical-environment-global-3.png b/1-js/8-more-functions/03-closure/lexical-environment-global-3.png similarity index 100% rename from 1-js/8-more-functions/2-closure/lexical-environment-global-3.png rename to 1-js/8-more-functions/03-closure/lexical-environment-global-3.png diff --git a/1-js/8-more-functions/2-closure/lexical-environment-global-3@2x.png b/1-js/8-more-functions/03-closure/lexical-environment-global-3@2x.png similarity index 100% rename from 1-js/8-more-functions/2-closure/lexical-environment-global-3@2x.png rename to 1-js/8-more-functions/03-closure/lexical-environment-global-3@2x.png diff --git a/1-js/8-more-functions/2-closure/lexical-environment-global.png b/1-js/8-more-functions/03-closure/lexical-environment-global.png similarity index 100% rename from 1-js/8-more-functions/2-closure/lexical-environment-global.png rename to 1-js/8-more-functions/03-closure/lexical-environment-global.png diff --git a/1-js/8-more-functions/2-closure/lexical-environment-global@2x.png b/1-js/8-more-functions/03-closure/lexical-environment-global@2x.png similarity index 100% rename from 1-js/8-more-functions/2-closure/lexical-environment-global@2x.png rename to 1-js/8-more-functions/03-closure/lexical-environment-global@2x.png diff --git a/1-js/8-more-functions/2-closure/lexical-environment-simple-lookup.png b/1-js/8-more-functions/03-closure/lexical-environment-simple-lookup.png similarity index 100% rename from 1-js/8-more-functions/2-closure/lexical-environment-simple-lookup.png rename to 1-js/8-more-functions/03-closure/lexical-environment-simple-lookup.png diff --git a/1-js/8-more-functions/2-closure/lexical-environment-simple-lookup@2x.png b/1-js/8-more-functions/03-closure/lexical-environment-simple-lookup@2x.png similarity index 100% rename from 1-js/8-more-functions/2-closure/lexical-environment-simple-lookup@2x.png rename to 1-js/8-more-functions/03-closure/lexical-environment-simple-lookup@2x.png diff --git a/1-js/8-more-functions/03-closure/lexical-environment-simple.png b/1-js/8-more-functions/03-closure/lexical-environment-simple.png new file mode 100644 index 00000000..e6fd8b6e Binary files /dev/null and b/1-js/8-more-functions/03-closure/lexical-environment-simple.png differ diff --git a/1-js/8-more-functions/03-closure/lexical-environment-simple@2x.png b/1-js/8-more-functions/03-closure/lexical-environment-simple@2x.png new file mode 100644 index 00000000..28e971b3 Binary files /dev/null and b/1-js/8-more-functions/03-closure/lexical-environment-simple@2x.png differ diff --git a/1-js/8-more-functions/2-closure/lexical-search-order.png b/1-js/8-more-functions/03-closure/lexical-search-order.png similarity index 100% rename from 1-js/8-more-functions/2-closure/lexical-search-order.png rename to 1-js/8-more-functions/03-closure/lexical-search-order.png diff --git a/1-js/8-more-functions/2-closure/lexical-search-order@2x.png b/1-js/8-more-functions/03-closure/lexical-search-order@2x.png similarity index 100% rename from 1-js/8-more-functions/2-closure/lexical-search-order@2x.png rename to 1-js/8-more-functions/03-closure/lexical-search-order@2x.png diff --git a/1-js/8-more-functions/3-function-object/2-counter-inc-dec/_js.view/solution.js b/1-js/8-more-functions/04-function-object/2-counter-inc-dec/_js.view/solution.js similarity index 100% rename from 1-js/8-more-functions/3-function-object/2-counter-inc-dec/_js.view/solution.js rename to 1-js/8-more-functions/04-function-object/2-counter-inc-dec/_js.view/solution.js diff --git a/1-js/8-more-functions/3-function-object/2-counter-inc-dec/_js.view/source.js b/1-js/8-more-functions/04-function-object/2-counter-inc-dec/_js.view/source.js similarity index 100% rename from 1-js/8-more-functions/3-function-object/2-counter-inc-dec/_js.view/source.js rename to 1-js/8-more-functions/04-function-object/2-counter-inc-dec/_js.view/source.js diff --git a/1-js/8-more-functions/3-function-object/2-counter-inc-dec/_js.view/test.js b/1-js/8-more-functions/04-function-object/2-counter-inc-dec/_js.view/test.js similarity index 100% rename from 1-js/8-more-functions/3-function-object/2-counter-inc-dec/_js.view/test.js rename to 1-js/8-more-functions/04-function-object/2-counter-inc-dec/_js.view/test.js diff --git a/1-js/8-more-functions/3-function-object/2-counter-inc-dec/solution.md b/1-js/8-more-functions/04-function-object/2-counter-inc-dec/solution.md similarity index 100% rename from 1-js/8-more-functions/3-function-object/2-counter-inc-dec/solution.md rename to 1-js/8-more-functions/04-function-object/2-counter-inc-dec/solution.md diff --git a/1-js/8-more-functions/3-function-object/2-counter-inc-dec/task.md b/1-js/8-more-functions/04-function-object/2-counter-inc-dec/task.md similarity index 100% rename from 1-js/8-more-functions/3-function-object/2-counter-inc-dec/task.md rename to 1-js/8-more-functions/04-function-object/2-counter-inc-dec/task.md diff --git a/1-js/8-more-functions/3-function-object/5-sum-many-brackets/solution.md b/1-js/8-more-functions/04-function-object/5-sum-many-brackets/solution.md similarity index 100% rename from 1-js/8-more-functions/3-function-object/5-sum-many-brackets/solution.md rename to 1-js/8-more-functions/04-function-object/5-sum-many-brackets/solution.md diff --git a/1-js/8-more-functions/3-function-object/5-sum-many-brackets/task.md b/1-js/8-more-functions/04-function-object/5-sum-many-brackets/task.md similarity index 100% rename from 1-js/8-more-functions/3-function-object/5-sum-many-brackets/task.md rename to 1-js/8-more-functions/04-function-object/5-sum-many-brackets/task.md diff --git a/1-js/8-more-functions/3-function-object/article.md b/1-js/8-more-functions/04-function-object/article.md similarity index 100% rename from 1-js/8-more-functions/3-function-object/article.md rename to 1-js/8-more-functions/04-function-object/article.md diff --git a/1-js/8-more-functions/4-new-function/article.md b/1-js/8-more-functions/05-new-function/article.md similarity index 100% rename from 1-js/8-more-functions/4-new-function/article.md rename to 1-js/8-more-functions/05-new-function/article.md diff --git a/1-js/8-more-functions/5-settimeout-setinterval/1-output-numbers-100ms/solution.md b/1-js/8-more-functions/06-settimeout-setinterval/1-output-numbers-100ms/solution.md similarity index 100% rename from 1-js/8-more-functions/5-settimeout-setinterval/1-output-numbers-100ms/solution.md rename to 1-js/8-more-functions/06-settimeout-setinterval/1-output-numbers-100ms/solution.md diff --git a/1-js/8-more-functions/5-settimeout-setinterval/1-output-numbers-100ms/task.md b/1-js/8-more-functions/06-settimeout-setinterval/1-output-numbers-100ms/task.md similarity index 100% rename from 1-js/8-more-functions/5-settimeout-setinterval/1-output-numbers-100ms/task.md rename to 1-js/8-more-functions/06-settimeout-setinterval/1-output-numbers-100ms/task.md diff --git a/1-js/8-more-functions/5-settimeout-setinterval/3-rewrite-settimeout-0/solution.md b/1-js/8-more-functions/06-settimeout-setinterval/3-rewrite-settimeout-0/solution.md similarity index 100% rename from 1-js/8-more-functions/5-settimeout-setinterval/3-rewrite-settimeout-0/solution.md rename to 1-js/8-more-functions/06-settimeout-setinterval/3-rewrite-settimeout-0/solution.md diff --git a/1-js/8-more-functions/5-settimeout-setinterval/3-rewrite-settimeout-0/task.md b/1-js/8-more-functions/06-settimeout-setinterval/3-rewrite-settimeout-0/task.md similarity index 100% rename from 1-js/8-more-functions/5-settimeout-setinterval/3-rewrite-settimeout-0/task.md rename to 1-js/8-more-functions/06-settimeout-setinterval/3-rewrite-settimeout-0/task.md diff --git a/1-js/8-more-functions/5-settimeout-setinterval/4-settimeout-result/solution.md b/1-js/8-more-functions/06-settimeout-setinterval/4-settimeout-result/solution.md similarity index 100% rename from 1-js/8-more-functions/5-settimeout-setinterval/4-settimeout-result/solution.md rename to 1-js/8-more-functions/06-settimeout-setinterval/4-settimeout-result/solution.md diff --git a/1-js/8-more-functions/5-settimeout-setinterval/4-settimeout-result/task.md b/1-js/8-more-functions/06-settimeout-setinterval/4-settimeout-result/task.md similarity index 100% rename from 1-js/8-more-functions/5-settimeout-setinterval/4-settimeout-result/task.md rename to 1-js/8-more-functions/06-settimeout-setinterval/4-settimeout-result/task.md diff --git a/1-js/8-more-functions/5-settimeout-setinterval/article.md b/1-js/8-more-functions/06-settimeout-setinterval/article.md similarity index 100% rename from 1-js/8-more-functions/5-settimeout-setinterval/article.md rename to 1-js/8-more-functions/06-settimeout-setinterval/article.md diff --git a/1-js/8-more-functions/5-settimeout-setinterval/setinterval-interval.png b/1-js/8-more-functions/06-settimeout-setinterval/setinterval-interval.png similarity index 100% rename from 1-js/8-more-functions/5-settimeout-setinterval/setinterval-interval.png rename to 1-js/8-more-functions/06-settimeout-setinterval/setinterval-interval.png diff --git a/1-js/8-more-functions/5-settimeout-setinterval/setinterval-interval@2x.png b/1-js/8-more-functions/06-settimeout-setinterval/setinterval-interval@2x.png similarity index 100% rename from 1-js/8-more-functions/5-settimeout-setinterval/setinterval-interval@2x.png rename to 1-js/8-more-functions/06-settimeout-setinterval/setinterval-interval@2x.png diff --git a/1-js/8-more-functions/5-settimeout-setinterval/settimeout-interval.png b/1-js/8-more-functions/06-settimeout-setinterval/settimeout-interval.png similarity index 100% rename from 1-js/8-more-functions/5-settimeout-setinterval/settimeout-interval.png rename to 1-js/8-more-functions/06-settimeout-setinterval/settimeout-interval.png diff --git a/1-js/8-more-functions/5-settimeout-setinterval/settimeout-interval@2x.png b/1-js/8-more-functions/06-settimeout-setinterval/settimeout-interval@2x.png similarity index 100% rename from 1-js/8-more-functions/5-settimeout-setinterval/settimeout-interval@2x.png rename to 1-js/8-more-functions/06-settimeout-setinterval/settimeout-interval@2x.png diff --git a/1-js/8-more-functions/6-call-apply-decorators/2-spy-decorator/_js.view/solution.js b/1-js/8-more-functions/07-call-apply-decorators/2-spy-decorator/_js.view/solution.js similarity index 100% rename from 1-js/8-more-functions/6-call-apply-decorators/2-spy-decorator/_js.view/solution.js rename to 1-js/8-more-functions/07-call-apply-decorators/2-spy-decorator/_js.view/solution.js diff --git a/1-js/8-more-functions/6-call-apply-decorators/2-spy-decorator/_js.view/source.js b/1-js/8-more-functions/07-call-apply-decorators/2-spy-decorator/_js.view/source.js similarity index 100% rename from 1-js/8-more-functions/6-call-apply-decorators/2-spy-decorator/_js.view/source.js rename to 1-js/8-more-functions/07-call-apply-decorators/2-spy-decorator/_js.view/source.js diff --git a/1-js/8-more-functions/6-call-apply-decorators/2-spy-decorator/_js.view/test.js b/1-js/8-more-functions/07-call-apply-decorators/2-spy-decorator/_js.view/test.js similarity index 100% rename from 1-js/8-more-functions/6-call-apply-decorators/2-spy-decorator/_js.view/test.js rename to 1-js/8-more-functions/07-call-apply-decorators/2-spy-decorator/_js.view/test.js diff --git a/1-js/8-more-functions/6-call-apply-decorators/2-spy-decorator/solution.md b/1-js/8-more-functions/07-call-apply-decorators/2-spy-decorator/solution.md similarity index 100% rename from 1-js/8-more-functions/6-call-apply-decorators/2-spy-decorator/solution.md rename to 1-js/8-more-functions/07-call-apply-decorators/2-spy-decorator/solution.md diff --git a/1-js/8-more-functions/6-call-apply-decorators/2-spy-decorator/task.md b/1-js/8-more-functions/07-call-apply-decorators/2-spy-decorator/task.md similarity index 100% rename from 1-js/8-more-functions/6-call-apply-decorators/2-spy-decorator/task.md rename to 1-js/8-more-functions/07-call-apply-decorators/2-spy-decorator/task.md diff --git a/1-js/8-more-functions/6-call-apply-decorators/8-debounce/_js.view/solution.js b/1-js/8-more-functions/07-call-apply-decorators/8-debounce/_js.view/solution.js similarity index 100% rename from 1-js/8-more-functions/6-call-apply-decorators/8-debounce/_js.view/solution.js rename to 1-js/8-more-functions/07-call-apply-decorators/8-debounce/_js.view/solution.js diff --git a/1-js/8-more-functions/6-call-apply-decorators/8-debounce/_js.view/test.js b/1-js/8-more-functions/07-call-apply-decorators/8-debounce/_js.view/test.js similarity index 100% rename from 1-js/8-more-functions/6-call-apply-decorators/8-debounce/_js.view/test.js rename to 1-js/8-more-functions/07-call-apply-decorators/8-debounce/_js.view/test.js diff --git a/1-js/8-more-functions/6-call-apply-decorators/8-debounce/solution.md b/1-js/8-more-functions/07-call-apply-decorators/8-debounce/solution.md similarity index 100% rename from 1-js/8-more-functions/6-call-apply-decorators/8-debounce/solution.md rename to 1-js/8-more-functions/07-call-apply-decorators/8-debounce/solution.md diff --git a/1-js/8-more-functions/6-call-apply-decorators/8-debounce/task.md b/1-js/8-more-functions/07-call-apply-decorators/8-debounce/task.md similarity index 100% rename from 1-js/8-more-functions/6-call-apply-decorators/8-debounce/task.md rename to 1-js/8-more-functions/07-call-apply-decorators/8-debounce/task.md diff --git a/1-js/8-more-functions/6-call-apply-decorators/9-throttle/_js.view/solution.js b/1-js/8-more-functions/07-call-apply-decorators/9-throttle/_js.view/solution.js similarity index 100% rename from 1-js/8-more-functions/6-call-apply-decorators/9-throttle/_js.view/solution.js rename to 1-js/8-more-functions/07-call-apply-decorators/9-throttle/_js.view/solution.js diff --git a/1-js/8-more-functions/6-call-apply-decorators/9-throttle/_js.view/test.js b/1-js/8-more-functions/07-call-apply-decorators/9-throttle/_js.view/test.js similarity index 100% rename from 1-js/8-more-functions/6-call-apply-decorators/9-throttle/_js.view/test.js rename to 1-js/8-more-functions/07-call-apply-decorators/9-throttle/_js.view/test.js diff --git a/1-js/8-more-functions/6-call-apply-decorators/9-throttle/solution.md b/1-js/8-more-functions/07-call-apply-decorators/9-throttle/solution.md similarity index 100% rename from 1-js/8-more-functions/6-call-apply-decorators/9-throttle/solution.md rename to 1-js/8-more-functions/07-call-apply-decorators/9-throttle/solution.md diff --git a/1-js/8-more-functions/6-call-apply-decorators/9-throttle/task.md b/1-js/8-more-functions/07-call-apply-decorators/9-throttle/task.md similarity index 100% rename from 1-js/8-more-functions/6-call-apply-decorators/9-throttle/task.md rename to 1-js/8-more-functions/07-call-apply-decorators/9-throttle/task.md diff --git a/1-js/8-more-functions/6-call-apply-decorators/article.md b/1-js/8-more-functions/07-call-apply-decorators/article.md similarity index 100% rename from 1-js/8-more-functions/6-call-apply-decorators/article.md rename to 1-js/8-more-functions/07-call-apply-decorators/article.md diff --git a/1-js/8-more-functions/6-call-apply-decorators/decorator-makecaching-wrapper.png b/1-js/8-more-functions/07-call-apply-decorators/decorator-makecaching-wrapper.png similarity index 100% rename from 1-js/8-more-functions/6-call-apply-decorators/decorator-makecaching-wrapper.png rename to 1-js/8-more-functions/07-call-apply-decorators/decorator-makecaching-wrapper.png diff --git a/1-js/8-more-functions/6-call-apply-decorators/decorator-makecaching-wrapper@2x.png b/1-js/8-more-functions/07-call-apply-decorators/decorator-makecaching-wrapper@2x.png similarity index 100% rename from 1-js/8-more-functions/6-call-apply-decorators/decorator-makecaching-wrapper@2x.png rename to 1-js/8-more-functions/07-call-apply-decorators/decorator-makecaching-wrapper@2x.png diff --git a/1-js/8-more-functions/7-bind/2-write-to-object-after-bind/solution.md b/1-js/8-more-functions/08-bind/2-write-to-object-after-bind/solution.md similarity index 100% rename from 1-js/8-more-functions/7-bind/2-write-to-object-after-bind/solution.md rename to 1-js/8-more-functions/08-bind/2-write-to-object-after-bind/solution.md diff --git a/1-js/8-more-functions/7-bind/2-write-to-object-after-bind/task.md b/1-js/8-more-functions/08-bind/2-write-to-object-after-bind/task.md similarity index 100% rename from 1-js/8-more-functions/7-bind/2-write-to-object-after-bind/task.md rename to 1-js/8-more-functions/08-bind/2-write-to-object-after-bind/task.md diff --git a/1-js/8-more-functions/7-bind/3-second-bind/solution.md b/1-js/8-more-functions/08-bind/3-second-bind/solution.md similarity index 100% rename from 1-js/8-more-functions/7-bind/3-second-bind/solution.md rename to 1-js/8-more-functions/08-bind/3-second-bind/solution.md diff --git a/1-js/8-more-functions/7-bind/3-second-bind/task.md b/1-js/8-more-functions/08-bind/3-second-bind/task.md similarity index 100% rename from 1-js/8-more-functions/7-bind/3-second-bind/task.md rename to 1-js/8-more-functions/08-bind/3-second-bind/task.md diff --git a/1-js/8-more-functions/7-bind/4-function-property-after-bind/solution.md b/1-js/8-more-functions/08-bind/4-function-property-after-bind/solution.md similarity index 100% rename from 1-js/8-more-functions/7-bind/4-function-property-after-bind/solution.md rename to 1-js/8-more-functions/08-bind/4-function-property-after-bind/solution.md diff --git a/1-js/8-more-functions/7-bind/4-function-property-after-bind/task.md b/1-js/8-more-functions/08-bind/4-function-property-after-bind/task.md similarity index 100% rename from 1-js/8-more-functions/7-bind/4-function-property-after-bind/task.md rename to 1-js/8-more-functions/08-bind/4-function-property-after-bind/task.md diff --git a/1-js/8-more-functions/7-bind/5-question-use-bind/solution.md b/1-js/8-more-functions/08-bind/5-question-use-bind/solution.md similarity index 100% rename from 1-js/8-more-functions/7-bind/5-question-use-bind/solution.md rename to 1-js/8-more-functions/08-bind/5-question-use-bind/solution.md diff --git a/1-js/8-more-functions/7-bind/5-question-use-bind/task.md b/1-js/8-more-functions/08-bind/5-question-use-bind/task.md similarity index 100% rename from 1-js/8-more-functions/7-bind/5-question-use-bind/task.md rename to 1-js/8-more-functions/08-bind/5-question-use-bind/task.md diff --git a/1-js/8-more-functions/7-bind/6-ask-currying/solution.md b/1-js/8-more-functions/08-bind/6-ask-currying/solution.md similarity index 100% rename from 1-js/8-more-functions/7-bind/6-ask-currying/solution.md rename to 1-js/8-more-functions/08-bind/6-ask-currying/solution.md diff --git a/1-js/8-more-functions/7-bind/6-ask-currying/task.md b/1-js/8-more-functions/08-bind/6-ask-currying/task.md similarity index 100% rename from 1-js/8-more-functions/7-bind/6-ask-currying/task.md rename to 1-js/8-more-functions/08-bind/6-ask-currying/task.md diff --git a/1-js/8-more-functions/7-bind/article.md b/1-js/8-more-functions/08-bind/article.md similarity index 100% rename from 1-js/8-more-functions/7-bind/article.md rename to 1-js/8-more-functions/08-bind/article.md diff --git a/1-js/8-more-functions/7-bind/head.html b/1-js/8-more-functions/08-bind/head.html similarity index 100% rename from 1-js/8-more-functions/7-bind/head.html rename to 1-js/8-more-functions/08-bind/head.html diff --git a/1-js/8-more-functions/1-recursion/1-sum-to/solution.md b/1-js/8-more-functions/1-recursion/1-sum-to/solution.md deleted file mode 100644 index 0d9e1e39..00000000 --- a/1-js/8-more-functions/1-recursion/1-sum-to/solution.md +++ /dev/null @@ -1,42 +0,0 @@ -Решение **с использованием цикла**: - -```js run -function sumTo(n) { - var sum = 0; - for (var i = 1; i <= n; i++) { - sum += i; - } - return sum; -} - -alert( sumTo(100) ); -``` - -Решение через **рекурсию**: - -```js run -function sumTo(n) { - if (n == 1) return 1; - return n + sumTo(n - 1); -} - -alert( sumTo(100) ); -``` - -Решение **по формуле**: `sumTo(n) = n*(n+1)/2`: - -```js run -function sumTo(n) { - return n * (n + 1) / 2; -} - -alert( sumTo(100) ); -``` - -P.S. Надо ли говорить, что решение по формуле работает быстрее всех? Это очевидно. Оно использует всего три операции для любого `n`, а цикл и рекурсия требуют как минимум `n` операций сложения. - -Вариант с циклом -- второй по скорости. Он быстрее рекурсии, так как операций сложения столько же, но нет дополнительных вычислительных затрат на организацию вложенных вызовов. - -Рекурсия в данном случае работает медленнее всех. - -P.P.S. Существует ограничение глубины вложенных вызовов, поэтому рекурсивный вызов `sumTo(100000)` выдаст ошибку. \ No newline at end of file diff --git a/1-js/8-more-functions/1-recursion/1-sum-to/task.md b/1-js/8-more-functions/1-recursion/1-sum-to/task.md deleted file mode 100644 index 91b9d08c..00000000 --- a/1-js/8-more-functions/1-recursion/1-sum-to/task.md +++ /dev/null @@ -1,34 +0,0 @@ -importance: 5 - ---- - -# Вычислить сумму чисел до данного - -Напишите функцию `sumTo(n)`, которая для данного `n` вычисляет сумму чисел от 1 до `n`, например: - -```js no-beautify -sumTo(1) = 1 -sumTo(2) = 2 + 1 = 3 -sumTo(3) = 3 + 2 + 1 = 6 -sumTo(4) = 4 + 3 + 2 + 1 = 10 -... -sumTo(100) = 100 + 99 + ... + 2 + 1 = 5050 -``` - -Сделайте три варианта решения: - -1. С использованием цикла. -2. Через рекурсию, т.к. `sumTo(n) = n + sumTo(n-1)` для `n > 1`. -3. С использованием формулы для суммы [арифметической прогрессии](http://ru.wikipedia.org/wiki/%D0%90%D1%80%D0%B8%D1%84%D0%BC%D0%B5%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B0%D1%8F_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B5%D1%81%D1%81%D0%B8%D1%8F). - -Пример работы вашей функции: - -```js -function sumTo(n) { /*... ваш код ... */ } - -alert( sumTo(100) ); // 5050 -``` - -**Какой вариант решения самый быстрый? Самый медленный? Почему?** - -**Можно ли при помощи рекурсии посчитать `sumTo(100000)`? Если нет, то почему?** diff --git a/1-js/8-more-functions/1-recursion/2-factorial/solution.md b/1-js/8-more-functions/1-recursion/2-factorial/solution.md deleted file mode 100644 index 1cbd981f..00000000 --- a/1-js/8-more-functions/1-recursion/2-factorial/solution.md +++ /dev/null @@ -1,23 +0,0 @@ -По свойствам факториала, как описано в условии, `n!` можно записать как `n * (n-1)!`. - -То есть, результат функции для `n` можно получить как `n`, умноженное на результат функции для `n-1`, и так далее до `1!`: - -```js run -function factorial(n) { - return (n != 1) ? n * factorial(n - 1) : 1; -} - -alert( factorial(5) ); // 120 -``` - -Базисом рекурсии является значение `1`. А можно было бы сделать базисом и `0`. Тогда код станет чуть короче: - -```js run -function factorial(n) { - return n ? n * factorial(n - 1) : 1; -} - -alert( factorial(5) ); // 120 -``` - -В этом случае вызов `factorial(1)` сведётся к `1*factorial(0)`, будет дополнительный шаг рекурсии. diff --git a/1-js/8-more-functions/1-recursion/2-factorial/task.md b/1-js/8-more-functions/1-recursion/2-factorial/task.md deleted file mode 100644 index 78049ec4..00000000 --- a/1-js/8-more-functions/1-recursion/2-factorial/task.md +++ /dev/null @@ -1,31 +0,0 @@ -importance: 4 - ---- - -# Вычислить факториал - -*Факториа́л числа* -- это число, умноженное на "себя минус один", затем на "себя минус два" и так далее, до единицы. Обозначается `n!` - -Определение факториала можно записать как: - -```js -n! = n * (n - 1) * (n - 2) * ...*1 -``` - -Примеры значений для разных `n`: - -```js -1! = 1 -2! = 2 * 1 = 2 -3! = 3 * 2 * 1 = 6 -4! = 4 * 3 * 2 * 1 = 24 -5! = 5 * 4 * 3 * 2 * 1 = 120 -``` - -Задача -- написать функцию `factorial(n)`, которая возвращает факториал числа `n!`, используя рекурсивный вызов. - -```js -alert( factorial(5) ); // 120 -``` - -Подсказка: обратите внимание, что `n!` можно записать как `n * (n-1)!`. Например: `3! = 3*2! = 3*2*1! = 6` diff --git a/1-js/8-more-functions/1-recursion/3-fibonacci-numbers/solution.md b/1-js/8-more-functions/1-recursion/3-fibonacci-numbers/solution.md deleted file mode 100644 index 0f20f250..00000000 --- a/1-js/8-more-functions/1-recursion/3-fibonacci-numbers/solution.md +++ /dev/null @@ -1,93 +0,0 @@ -# Вычисление рекурсией (медленное) - -Решение по формуле, используя рекурсию: - -```js run -function fib(n) { - return n <= 1 ? n : fib(n - 1) + fib(n - 2); -} - -alert( fib(3) ); // 2 -alert( fib(7) ); // 13 -// fib(77); // не запускаем, подвесит браузер -``` - -При больших значениях `n` оно будет работать очень медленно. Например, `fib(77)` уже будет вычисляться очень долго. - -Это потому, что функция порождает обширное дерево вложенных вызовов. При этом ряд значений вычисляется много раз. Например, посмотрим на отрывок вычислений: - -```js no-beautify -... -fib(5) = fib(4) + fib(3) -fib(4) = fib(3) + fib(2) -... -``` - -Здесь видно, что значение `fib(3)` нужно одновременно и для `fib(5)` и для `fib(4)`. В коде оно будет вычислено два раза, совершенно независимо. - -Можно это оптимизировать, запоминая уже вычисленные значения, получится гораздо быстрее. Альтернативный вариант -- вообще отказаться от рекурсии, а вместо этого в цикле начать с первых значений `1`, `2`, затем из них получить `fib(3)`, далее `fib(4)`, затем `fib(5)` и так далее, до нужного значения. - -Это решение будет наиболее эффективным. Попробуйте его написать. - -# Алгоритм вычисления в цикле - -Будем идти по формуле слева-направо: - -```js no-beautify -var a = 1, b = 1; // начальные значения -var c = a + b; // 2 - -/* переменные на начальном шаге: -a b c -1, 1, 2 -*/ -``` - -Теперь следующий шаг, присвоим `a` и `b` текущие 2 числа и получим новое следующее в `c`: - -```js no-beautify -a = b, b = c; -c = a + b; - -/* стало так (ещё число): - a b c -1, 1, 2, 3 -*/ -``` - -Следующий шаг даст нам ещё одно число последовательности: - -```js no-beautify -a = b, b = c; -c = a + b; - -/* стало так (ещё число): - a b c -1, 1, 2, 3, 5 -*/ -``` - -Повторять в цикле до тех пор, пока не получим нужное значение. Это гораздо быстрее, чем рекурсия, хотя бы потому что ни одно из чисел не вычисляется дважды. - -P.S. Этот подход к вычислению называется [динамическое программирование снизу-вверх](http://ru.wikipedia.org/wiki/%D0%94%D0%B8%D0%BD%D0%B0%D0%BC%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5). - -# Код для вычисления в цикле - -```js run -function fib(n) { - var a = 1, - b = 1; - for (var i = 3; i <= n; i++) { - var c = a + b; - a = b; - b = c; - } - return b; -} - -alert( fib(3) ); // 2 -alert( fib(7) ); // 13 -alert( fib(77) ); // 5527939700884757 -``` - -Цикл здесь начинается с `i=3`, так как первое и второе числа Фибоначчи заранее записаны в переменные `a=1`, `b=1`. diff --git a/1-js/8-more-functions/1-recursion/3-fibonacci-numbers/task.md b/1-js/8-more-functions/1-recursion/3-fibonacci-numbers/task.md deleted file mode 100644 index 4ec36b41..00000000 --- a/1-js/8-more-functions/1-recursion/3-fibonacci-numbers/task.md +++ /dev/null @@ -1,24 +0,0 @@ -importance: 5 - ---- - -# Числа Фибоначчи - -Последовательность [чисел Фибоначчи](http://ru.wikipedia.org/wiki/%D0%A7%D0%B8%D1%81%D0%BB%D0%B0_%D0%A4%D0%B8%D0%B1%D0%BE%D0%BD%D0%B0%D1%87%D1%87%D0%B8) имеет формулу Fn = Fn-1 + Fn-2. То есть, следующее число получается как сумма двух предыдущих. - -Первые два числа равны `1`, затем `2(1+1)`, затем `3(1+2)`, `5(2+3)` и так далее: `1, 1, 2, 3, 5, 8, 13, 21...`. - -Числа Фибоначчи тесно связаны с [золотым сечением](http://ru.wikipedia.org/wiki/%D0%97%D0%BE%D0%BB%D0%BE%D1%82%D0%BE%D0%B5_%D1%81%D0%B5%D1%87%D0%B5%D0%BD%D0%B8%D0%B5) и множеством природных явлений вокруг нас. - -Напишите функцию `fib(n)`, которая возвращает `n-е` число Фибоначчи. Пример работы: - -```js no-beautify -function fib(n) { /* ваш код */ } - -alert( fib(3) ); // 2 -alert( fib(7) ); // 13 -alert( fib(77)); // 5527939700884757 -``` - -**Все запуски функций из примера выше должны срабатывать быстро.** - diff --git a/1-js/8-more-functions/1-recursion/9-output-single-linked-list/solution.md b/1-js/8-more-functions/1-recursion/9-output-single-linked-list/solution.md deleted file mode 100644 index 2552debe..00000000 --- a/1-js/8-more-functions/1-recursion/9-output-single-linked-list/solution.md +++ /dev/null @@ -1,153 +0,0 @@ -# Вывод списка в цикле - -```js run -var list = { - value: 1, - next: { - value: 2, - next: { - value: 3, - next: { - value: 4, - next: null - } - } - } -}; - -function printList(list) { - var tmp = list; - - while (tmp) { - alert( tmp.value ); - tmp = tmp.next; - } - -} - -printList(list); -``` - -Обратите внимание, что для прохода по списку используется временная переменная `tmp`, а не `list`. Можно было бы и бегать по списку, используя входной параметр функции: - -```js -function printList(list) { - - while(*!*list*/!*) { - alert( list.value ); - list = list.next; - } - -} -``` - -...Но при этом мы в будущем не сможем расширить функцию и сделать со списком что-то ещё, ведь после окончания цикла начало списка уже нигде не хранится. - -Поэтому и используется временная переменная -- чтобы сделать код расширяемым, и, кстати, более понятным, ведь роль `tmp` -- исключительно обход списка, как `i` в цикле `for`. - -# Вывод списка с рекурсией - -Рекурсивный вариант `printList(list)` следует простой логике: вывести текущее значение `(1)`, а затем пропустить через себя следующее `(2)`: - -```js run -var list = { - value: 1, - next: { - value: 2, - next: { - value: 3, - next: { - value: 4, - next: null - } - } - } -}; - -function printList(list) { - - alert( list.value ); // (1) - - if (list.next) { - printList(list.next); // (2) - } - -} - -printList(list); -``` - -# Обратный вывод с рекурсией - -Обратный вывод -- почти то же самое, что прямой, просто сначала мы обрабатываем следующее значение, а потом -- текущее: - -```js run -var list = { - value: 1, - next: { - value: 2, - next: { - value: 3, - next: { - value: 4, - next: null - } - } - } -}; - -function printReverseList(list) { - - if (list.next) { - printReverseList(list.next); - } - - alert( list.value ); -} - -printReverseList(list); -``` - -# Обратный вывод без рекурсии - -```js run -var list = { - value: 1, - next: { - value: 2, - next: { - value: 3, - next: { - value: 4, - next: null - } - } - } -}; - -function printReverseList(list) { - var arr = []; - var tmp = list; - - while (tmp) { - arr.push(tmp.value); - tmp = tmp.next; - } - - for (var i = arr.length - 1; i >= 0; i--) { - alert( arr[i] ); - } -} - -printReverseList(list); -``` - -**Обратный вывод без рекурсии быстрее.** - -По сути, рекурсивный вариант и нерекурсивный работают одинаково: они проходят список и запоминают его элементы, а потом выводят в обратном порядке. - -В случае с массивом это очевидно, а для рекурсии запоминание происходит в стеке (внутренней специальной структуре данных): когда вызывается вложенная функция, то интерпретатор сохраняет в стек текущие параметры. Вложенные вызовы заполняют стек, а потом он выводится в обратном порядке. - -При этом, при рекурсии в стеке сохраняется не только элемент списка, а другая вспомогательная информация, необходимая для возвращения из вложенного вызова. Поэтому тратится больше памяти. Все эти расходы отсутствуют в варианте без рекурсии, так как в массиве хранится именно то, что нужно. - -Преимущество рекурсии, с другой стороны -- более короткий и, зачастую, более простой код. diff --git a/1-js/8-more-functions/1-recursion/9-output-single-linked-list/task.md b/1-js/8-more-functions/1-recursion/9-output-single-linked-list/task.md deleted file mode 100644 index e87d2cfa..00000000 --- a/1-js/8-more-functions/1-recursion/9-output-single-linked-list/task.md +++ /dev/null @@ -1,52 +0,0 @@ -importance: 5 - ---- - -# Вывести односвязный список - -TODO: определение списка есть в статье, убрать отсюда. -TODO: разбить на две задачи - прямой и обратный вывод. - -[Односвязный список](http://ru.wikipedia.org/wiki/Связный_список) -- это структура данных, которая состоит из *элементов*, каждый из которых хранит ссылку на следующий. Последний элемент может не иметь ссылки, либо она равна `null`. - -Например, объект ниже задаёт односвязный список, в `next` хранится ссылка на следующий элемент: - -```js -var list = { - value: 1, - next: { - value: 2, - next: { - value: 3, - next: { - value: 4, - next: null - } - } - } -}; -``` - -Графическое представление этого списка: -![](linked-list.png) - -Альтернативный способ создания: - -```js no-beautify -var list = { value: 1 }; -list.next = { value: 2 }; -list.next.next = { value: 3 }; -list.next.next.next = { value: 4 }; -``` - -Такая структура данных интересна тем, что можно очень быстро разбить список на части, объединить списки, удалить или добавить элемент в любое место, включая начало. При использовании массива такие действия требуют обширных перенумерований. - -Задачи: - -1. Напишите функцию `printList(list)`, которая выводит элементы списка по очереди, при помощи цикла. -2. Напишите функцию `printList(list)` при помощи рекурсии. -3. Напишите функцию `printReverseList(list)`, которая выводит элементы списка в обратном порядке, при помощи рекурсии. -Для списка выше она должна выводить `4`,`3`,`2`,`1` -4. Сделайте вариант `printReverseList(list)`, использующий не рекурсию, а цикл. - -Как лучше -- с рекурсией или без? \ No newline at end of file diff --git a/1-js/8-more-functions/2-closure/lexenv-nested-makecounter-5.png b/1-js/8-more-functions/2-closure/lexenv-nested-makecounter-5.png deleted file mode 100644 index 045b0516..00000000 Binary files a/1-js/8-more-functions/2-closure/lexenv-nested-makecounter-5.png and /dev/null differ diff --git a/1-js/8-more-functions/2-closure/lexenv-nested-makecounter-5@2x.png b/1-js/8-more-functions/2-closure/lexenv-nested-makecounter-5@2x.png deleted file mode 100644 index 3a5ae908..00000000 Binary files a/1-js/8-more-functions/2-closure/lexenv-nested-makecounter-5@2x.png and /dev/null differ diff --git a/1-js/8-more-functions/2-closure/lexical-environment-simple.png b/1-js/8-more-functions/2-closure/lexical-environment-simple.png deleted file mode 100644 index d800fc92..00000000 Binary files a/1-js/8-more-functions/2-closure/lexical-environment-simple.png and /dev/null differ diff --git a/1-js/8-more-functions/2-closure/lexical-environment-simple@2x.png b/1-js/8-more-functions/2-closure/lexical-environment-simple@2x.png deleted file mode 100644 index 576556b0..00000000 Binary files a/1-js/8-more-functions/2-closure/lexical-environment-simple@2x.png and /dev/null differ diff --git a/1-js/9-object-inheritance/05-class/this-super-loop.png b/1-js/9-object-inheritance/05-class/this-super-loop.png index b51fcf22..bb4c1fc4 100644 Binary files a/1-js/9-object-inheritance/05-class/this-super-loop.png and b/1-js/9-object-inheritance/05-class/this-super-loop.png differ diff --git a/1-js/9-object-inheritance/05-class/this-super-loop@2x.png b/1-js/9-object-inheritance/05-class/this-super-loop@2x.png index 3bb5f625..91d2e03c 100644 Binary files a/1-js/9-object-inheritance/05-class/this-super-loop@2x.png and b/1-js/9-object-inheritance/05-class/this-super-loop@2x.png differ diff --git a/1-js/plan3.txt b/1-js/plan3.txt index 24ca55c6..66363cab 100644 --- a/1-js/plan3.txt +++ b/1-js/plan3.txt @@ -15,8 +15,8 @@ first-steps logical-ops while-for (labels, no objects) switch - function-basics (decl, shadowing, naming, default params) - function-expressions-arrows (function expr, arrow, todo: move new Function out of this?) + function-basics (decl, shadowing, naming, default params) + function-expressions-arrows (function expr, arrow) javascript-specials (TODO, remove it? migrate all function* to separate chapter?) code-quality debugging-chrome (TODO) @@ -27,12 +27,12 @@ code-quality object-basics object(props, for-in, refs, assign) garbage-collection - object-methods (this, method syntax, no call/apply) - primitives-methods - constructor-new symbol + object-methods (this, method syntax, no call/apply) object-toprimitive + constructor-new data-types + primitives-methods number (rounding, precision, isFinite, isNaN, parse*, Math.*) string (quotes, search, substring, tagged template notice) date (TODO: tasks) @@ -41,14 +41,13 @@ data-types iterable map-set-weakmap-weakset json - << intl more-syntax function-arguments-rest-spread destructuring-assignment (also func params destructuring) advanced-loops (iterators over objects, arrays) try..catch (own errors, rethrowing, window.onerror) deeper - recursion + recursion running execution context = where + lexical environment = envrec + outer context stack pow task @@ -65,7 +64,7 @@ deeper length property custom properties new function - scheduling: settimeout, setinterval + scheduling: settimeout, setinterval recursive settimeout call-apply-decorators bind @@ -73,7 +72,7 @@ deeper ------- - +<< intl <<< descriptors (TODO: LATER, need JSON to output, better after inheritance to explain getOwnProps) <<< getter setter @@ -82,14 +81,14 @@ constructors classes instanceof -после 4-object сделать +после 4-object сделать descriptors proxy? error-handling - try..catch + try..catch object-oriented error handling (inheritance, rethrow etc) more-features diff --git a/figures.sketch b/figures.sketch index 69749f77..6bcfe34d 100644 Binary files a/figures.sketch and b/figures.sketch differ