diff --git a/1-js/01-getting-started/1-intro/article.md b/1-js/01-getting-started/1-intro/article.md index 74521076ca..10bd54fedb 100644 --- a/1-js/01-getting-started/1-intro/article.md +++ b/1-js/01-getting-started/1-intro/article.md @@ -25,7 +25,7 @@ The browser has an embedded engine, sometimes it's also called a "JavaScript vir Different engines have different "codenames", for example: - [V8](https://en.wikipedia.org/wiki/V8_(JavaScript_engine)) -- in Chrome and Opera. -- [Gecko](https://en.wikipedia.org/wiki/Gecko_(software)) -- in Firefox. +- [SpiderMonkey](https://en.wikipedia.org/wiki/SpiderMonkey) -- in Firefox. - ...There are other codenames like "Trident", "Chakra" for different versions of IE, "ChakraCore" for Microsoft Edge, "Nitro" and "SquirrelFish" for Safari etc. The terms above are good to remember, because they are used in developer articles on the internet. We'll use them too. For instance, if "a feature X is supported by V8", then it probably works in Chrome and Opera. @@ -108,7 +108,7 @@ Modern tools make the transpilation very fast and transparent, actually allowing Examples of such languages: -- [CoffeeScript](http://coffeescript.org/) is a "syntax sugar" for JavaScript, it introduces shorter syntax, allowing to write more precise and clear code. Usually Ruby devs like it. +- [CoffeeScript](http://coffeescript.org/) is a "syntactic sugar" for JavaScript, it introduces shorter syntax, allowing to write more precise and clear code. Usually Ruby devs like it. - [TypeScript](http://www.typescriptlang.org/) is concentrated on adding "strict data typing", to simplify development and support of complex systems. It is developed by Microsoft. - [Dart](https://www.dartlang.org/) is a standalone language that has its own engine that runs in non-browser environments (like mobile apps). It was initially offered by Google as a replacement for JavaScript, but as of now, browsers require it to be transpiled to JavaScript just like the ones above. diff --git a/1-js/02-first-steps/01-hello-world/article.md b/1-js/02-first-steps/01-hello-world/article.md index e69de29bb2..e69a25f0c6 100644 --- a/1-js/02-first-steps/01-hello-world/article.md +++ b/1-js/02-first-steps/01-hello-world/article.md @@ -0,0 +1,137 @@ +# Hello, world! + +The tutorial that you're reading is about core JavaScript, which is platform-independent. Further on, you will learn Node.JS and other platforms that use it. + +But, we need a working environment to run our scripts, and, just because this book is online, the browser is a good choice. We'll keep the amount of browser-specific commands (like `alert`) to a minimum, so that you don't spend time on them if you plan to concentrate on another environment like Node.JS. On the other hand, browser details are explained in detail in the [next part](/ui) of the tutorial. + +So first, let's see how to attach a script to a webpage. For server-side environments, you can just execute it with a command like `"node my.js"` for Node.JS. + + +[cut] + +## The "script" tag + +JavaScript programs can be inserted in any part of an HTML document with the help of the ` +*/!* + +

...After the script.

+ + + + +``` + +```online +You can run the example by clicking on the "Play" button in its right-top corner. +``` + +The ` + ``` + + These comments were supposed to hide the code from an old browser that didn't know about a ` +``` + +Here `/path/to/script.js` is an absolute path to the file with the script (from the site root). + +It is also possible to provide a path relative to the current page. For instance, `src="script.js"` would mean a file `"script.js"` in the current folder. + +We can give a full URL as well, for instance: + +```html + +``` + +To attach several scripts, use multiple tags: + +```html + + +… +``` + +```smart +As a rule, only the simplest scripts are put into HTML. More complex ones reside in separate files. + +The benefit of a separate file is that the browser will download it and then store in its [cache](https://en.wikipedia.org/wiki/Web_cache). + +After this, other pages that want the same script will take it from the cache instead of downloading it. So the file is actually downloaded only once. + +That saves traffic and makes pages faster. +``` + +````warn header="If `src` is set, the script content is ignored." +A single ` +``` + +We must choose: either it's an external ` + +``` +```` + +## Summary + +- We can use a ``. + + +There is much more to learn about browser scripts and their interaction with the web-page. But let's keep in mind that this part of the tutorial is devoted to the JavaScript language, so we shouldn't distract ourselves from it. We'll be using a browser as a way to run JavaScript, which is very convenient for online reading, but yet one of many. diff --git a/1-js/02-first-steps/04-variables/article.md b/1-js/02-first-steps/04-variables/article.md index 9c4c964222..fe50cc52ae 100644 --- a/1-js/02-first-steps/04-variables/article.md +++ b/1-js/02-first-steps/04-variables/article.md @@ -1,8 +1,10 @@ # Variables -Most of the time, a script needs to work with information. If it's an online-shop -- that's going to be the goods and a shopping cart. If it's a chat -- users, messages and so on. +Most of the time, a JavaScript application needs to work with information. Here are 2 examples: +1. An online-shop -- the information might include goods being sold and a shopping cart. +2. A chat application -- the information might include users, messages, and much more. -Variables are used to store the information. +Variables are used to store this information. [cut] @@ -296,7 +298,7 @@ Please name the variables sensibly. Take time to think if needed. Variable naming is one of the most important and complex skills in programming. A quick glance at variable names can reveal which code is written by a beginner and which by an experienced developer. -In a real project, most of the time is spent on modifying and extending the existing code base, rather than writing something completely separate from the scratch. And when we return to the code after some time of doing something else, it's much easier to find information that is well-labelled. Or, in other words, when the variables have good names. +In a real project, most of the time is spent on modifying and extending the existing code base, rather than writing something completely separate from scratch. And when we return to the code after some time of doing something else, it's much easier to find information that is well-labelled. Or, in other words, when the variables have good names. Please spend some time thinking about the right name for a variable before declaring it. That will repay you a lot. @@ -305,14 +307,14 @@ Some good-to-follow rules are: - Use human-readable names like `userName` or `shoppingCart`. - Stay away from abbreviations or short names like `a`, `b`, `c`, unless you really know what you're doing. - Make the name maximally descriptive and concise. Examples of bad names are `data` and `value`. Such a name says nothing. It is only ok to use them if it's exceptionally obvious from the context which data or value is meant. -- Agree on terms within the team and in your own mind. If a site visitor is called a "user" then we should name related variables like `currentUser` or `newUser`, but not `currentVisitor` or a `newManInTown`. +- Agree on terms within your team and in your own mind. If a site visitor is called a "user" then we should name related variables like `currentUser` or `newUser`, but not `currentVisitor` or a `newManInTown`. Sounds simple? Indeed it is, but creating good descriptive-and-concise names in practice is not. Go for it. ```smart header="Reuse or create?" And the last note. There are some lazy programmers who, instead of declaring a new variable, tend to reuse the existing ones. -As the result, the variable is like a box where people throw different things without changing the sticker. What is inside it now? Who knows... We need to come closer and check. +As a result, the variable is like a box where people throw different things without changing the sticker. What is inside it now? Who knows... We need to come closer and check. Such a programmer saves a little bit on variable declaration, but loses ten times more on debugging the code. diff --git a/1-js/02-first-steps/05-types/article.md b/1-js/02-first-steps/05-types/article.md index 3e2de60ae2..3225221318 100644 --- a/1-js/02-first-steps/05-types/article.md +++ b/1-js/02-first-steps/05-types/article.md @@ -191,7 +191,7 @@ It supports two forms of syntax: 1. As an operator: `typeof x`. 2. Function style: `typeof(x)`. -In other words, it works both with the brackets or without them. The result is the same. +In other words, it works both with parentheses or without them. The result is the same. The call to `typeof x` returns a string with the type name: diff --git a/1-js/02-first-steps/06-type-conversions/1-primitive-conversions-questions/solution.md b/1-js/02-first-steps/06-type-conversions/1-primitive-conversions-questions/solution.md index 0e1be244bd..a4f3fec7ae 100644 --- a/1-js/02-first-steps/06-type-conversions/1-primitive-conversions-questions/solution.md +++ b/1-js/02-first-steps/06-type-conversions/1-primitive-conversions-questions/solution.md @@ -17,6 +17,6 @@ undefined + 1 = NaN // (4) ``` 1. The addition with a string `"" + 1` converts `1` to a string: `"" + 1 = "1"`, and then we have `"1" + 0`, the same rule is applied. -2. The substruction `"-"` (like most math operations) only works with numbers, it converts an empty string `""` to `0`. +2. The subtraction `"-"` (like most math operations) only works with numbers, it converts an empty string `""` to `0`. 3. `null` becomes `0` after the numeric conversion. 4. `undefined` becomes `NaN` after the numeric conversion. diff --git a/1-js/02-first-steps/07-operators/article.md b/1-js/02-first-steps/07-operators/article.md index 61f119b390..df24311c63 100644 --- a/1-js/02-first-steps/07-operators/article.md +++ b/1-js/02-first-steps/07-operators/article.md @@ -173,7 +173,7 @@ alert( b ); // 4 alert( c ); // 4 ``` -Chained assignments evaluate from right to left. First the rightmost expression `2+2` is evaluated then assigned to the variables on the left: `c`, `b` and `a`. At the end, all variables share a single value. +Chained assignments evaluate from right to left. First the rightmost expression `2 + 2` is evaluated then assigned to the variables on the left: `c`, `b` and `a`. At the end, all variables share a single value. ````smart header="The assignment operator `\"=\"` returns a value" An operator always returns a value. That's obvious for most of them like an addition `+` or a multiplication `*`. But the assignment operator follows that rule too. @@ -281,7 +281,7 @@ let a = ++counter; // (*) alert(a); // *!*2*/!* ``` -Here in the line `(*)` the prefix call `++counter` increments `i` and returns the new value that is `2`. So the `alert` shows `2`. +Here in the line `(*)` the prefix call `++counter` increments `counter` and returns the new value that is `2`. So the `alert` shows `2`. Now let's use the postfix form: @@ -292,7 +292,7 @@ let a = counter++; // (*) changed ++counter to counter++ alert(a); // *!*1*/!* ``` -In the line `(*)` the *postfix* form `counter++` also increments `i`, but returns the *old* value (prior to increment). So the `alert` shows `1`. +In the line `(*)` the *postfix* form `counter++` also increments `counter`, but returns the *old* value (prior to increment). So the `alert` shows `1`. To summarize: @@ -381,8 +381,8 @@ This notation can be shortened using operators `+=` and `*=`: ```js run let n = 2; -n += 5; // now n=7 (same as n = n + 5) -n *= 2; // now n=14 (same as n = n * 2) +n += 5; // now n = 7 (same as n = n + 5) +n *= 2; // now n = 14 (same as n = n * 2) alert( n ); // 14 ``` @@ -409,18 +409,18 @@ For example: ```js run *!* -let a = (1+2, 3+4); +let a = (1 + 2, 3 + 4); */!* -alert( a ); // 7 (the result of 3+4) +alert( a ); // 7 (the result of 3 + 4) ``` -Here, the first expression `1+2` is evaluated, and its result is thrown away, then `3+4` is evaluated and returned as the result. +Here, the first expression `1 + 2` is evaluated, and its result is thrown away, then `3 + 4` is evaluated and returned as the result. ```smart header="Comma has a very low precedence" Please note that the comma operator has very low precedence, lower than `=`, so parentheses are important in the example above. -Without them: `a=1+2,3+4` evaluates `+` first, summing the numbers into `a=3,7`, then the assignment operator `=` assigns `a=3`, and then the number after the comma `7` is not processed anyhow, so it's ignored. +Without them: `a = 1 + 2, 3 + 4` evaluates `+` first, summing the numbers into `a = 3, 7`, then the assignment operator `=` assigns `a = 3`, and then the number after the comma `7` is not processed anyhow, so it's ignored. ``` Why do we need such an operator which throws away everything except the last part? diff --git a/1-js/02-first-steps/11-logical-operators/article.md b/1-js/02-first-steps/11-logical-operators/article.md index b3aecc3119..48715b169d 100644 --- a/1-js/02-first-steps/11-logical-operators/article.md +++ b/1-js/02-first-steps/11-logical-operators/article.md @@ -68,7 +68,7 @@ if (hour < 10 || hour > 18 || isWeekend) { ## OR seeks the first truthy value -The logic described above is somewhat classical. Now let's bring in the "extra" features of JavaScipt. +The logic described above is somewhat classical. Now let's bring in the "extra" features of JavaScript. The extended algorithm works as follows. diff --git a/1-js/02-first-steps/12-while-for/3-which-value-for/solution.md b/1-js/02-first-steps/12-while-for/3-which-value-for/solution.md index 766278fd06..e2e28e75b4 100644 --- a/1-js/02-first-steps/12-while-for/3-which-value-for/solution.md +++ b/1-js/02-first-steps/12-while-for/3-which-value-for/solution.md @@ -8,8 +8,8 @@ for (let i = 0; i < 5; i++) alert( i ); That can be easily deducted from the algorithm of `for`: -1. Execute once `i=0` before everything (begin). -2. Check the condition `i<5` +1. Execute once `i = 0` before everything (begin). +2. Check the condition `i < 5` 3. If `true` -- execute the loop body `alert(i)`, and then `i++` The increment `i++` is separated from the condition check (2). That's just another statement. diff --git a/1-js/02-first-steps/12-while-for/7-list-primes/task.md b/1-js/02-first-steps/12-while-for/7-list-primes/task.md index 0f92fecf1e..6344b9f6f8 100644 --- a/1-js/02-first-steps/12-while-for/7-list-primes/task.md +++ b/1-js/02-first-steps/12-while-for/7-list-primes/task.md @@ -6,12 +6,12 @@ importance: 3 An integer number greater than `1` is called a [prime](https://en.wikipedia.org/wiki/Prime_number) if it cannot be divided without a remainder by anything except `1` and itself. -In other words, `n>1` is a prime if it can't be evenly divided by anything except `1` and `n`. +In other words, `n > 1` is a prime if it can't be evenly divided by anything except `1` and `n`. For example, `5` is a prime, because it cannot be divided without a remainder by `2`, `3` and `4`. **Write the code which outputs prime numbers in the interval from `2` to `n`.** -For `n=10` the result will be `2,3,5,7`. +For `n = 10` the result will be `2,3,5,7`. P.S. The code should work for any `n`, not be hard-tuned for any fixed value. diff --git a/1-js/02-first-steps/12-while-for/article.md b/1-js/02-first-steps/12-while-for/article.md index 55ea63029b..95f0167845 100644 --- a/1-js/02-first-steps/12-while-for/article.md +++ b/1-js/02-first-steps/12-while-for/article.md @@ -21,7 +21,7 @@ while (condition) { While the `condition` is `true`, the `code` from the loop body is executed. -For instance, the loop below outputs `i` while `i<3`: +For instance, the loop below outputs `i` while `i < 3`: ```js run let i = 0; @@ -37,7 +37,7 @@ If there were no `i++` in the example above, the loop would repeat (in theory) f Any expression or a variable can be a loop condition, not just a comparison. They are evaluated and converted to boolean by `while`. -For instance, the shorter way to write `while (i!=0)` could be `while (i)`: +For instance, the shorter way to write `while (i != 0)` could be `while (i)`: ```js run let i = 3; @@ -108,8 +108,8 @@ Let's examine the `for` statement part by part: | part | | | |-------|----------|----------------------------------------------------------------------------| -| begin | `i=0` | Executes once upon entering the loop. | -| condition | `i<3`| Checked before every loop iteration, if fails the loop stops. | +| begin | `i = 0` | Executes once upon entering the loop. | +| condition | `i < 3`| Checked before every loop iteration, if fails the loop stops. | | step| `i++` | Executes after the body on each iteration, but before the condition check. | | body | `alert(i)`| Runs again and again while the condition is truthy | @@ -188,11 +188,11 @@ We can also remove the `step` part: let i = 0; for (; i < 3;) { - alert( i ); + alert( i++ ); } ``` -The loop became identical to `while (i<3)`. +The loop became identical to `while (i < 3)`. We can actually remove everything, thus creating an infinite loop: @@ -239,7 +239,7 @@ The `continue` directive is a "lighter version" of `break`. It doesn't stop the We can use it if we're done on the current iteration and would like to move on to the next. -The loop above uses `continue` to output only odd values: +The loop below uses `continue` to output only odd values: ```js run no-beautify for (let i = 0; i < 10; i++) { @@ -324,7 +324,7 @@ The ordinary `break` after `input` would only break the inner loop. That's not s A *label* is an identifier with a colon before a loop: ```js -labelName: for(...) { +labelName: for (...) { ... } ``` @@ -369,7 +369,7 @@ For example, it is impossible to do this: ```js break label; // jumps to label? No. -label: for(...) +label: for (...) ``` The call to a `break/continue` is only possible from inside the loop, and the label must be somewhere upwards from the directive. @@ -381,7 +381,7 @@ We covered 3 types of loops: - `while` -- The condition is checked before each iteration. - `do..while` -- The condition is checked after each iteration. -- `for(;;)` -- The condition is checked before each iteration, additional settings available. +- `for (;;)` -- The condition is checked before each iteration, additional settings available. To make an "infinite" loop, usually the `while(true)` construct is used. Such a loop, just like any other, can be stopped with the `break` directive. diff --git a/1-js/02-first-steps/14-function-basics/1-if-else-required/task.md b/1-js/02-first-steps/14-function-basics/1-if-else-required/task.md index 743ba78a9f..4f69a5c8c3 100644 --- a/1-js/02-first-steps/14-function-basics/1-if-else-required/task.md +++ b/1-js/02-first-steps/14-function-basics/1-if-else-required/task.md @@ -35,4 +35,4 @@ function checkAge(age) { } ``` -Is there any difference in the bahavior of these two variants? +Is there any difference in the behavior of these two variants? diff --git a/1-js/02-first-steps/15-function-expressions-arrows/article.md b/1-js/02-first-steps/15-function-expressions-arrows/article.md index eca7019bb7..a7a840d02e 100644 --- a/1-js/02-first-steps/15-function-expressions-arrows/article.md +++ b/1-js/02-first-steps/15-function-expressions-arrows/article.md @@ -135,13 +135,11 @@ function showCancel() { ask("Do you agree?", showOk, showCancel); ``` -Before we explore how we can write it in a much shorter way, let's note that in the browser (and on the server-side in some cases) such functions are quite popular. +Before we explore how we can write it in a much shorter way, let's note that in the browser (and on the server-side in some cases) such functions are quite popular. The major difference between a real-life implementation and the example above is that real-life functions use more complex ways to interact with the user than a simple `confirm`. In the browser, such a function usually draws a nice-looking question window. But that's another story. -The major difference between a real-life implementation and the example above is that real-life functions use more complex ways to interact with the user than a simple `confirm`. In the browser, such a function usually draws a nice-looking question window. But that's another story. +**The arguments of `ask` are called *callback functions* or just *callbacks*.** -**The arguments of `ask` are called *callback functions* or just *callbacks*. The idea is that we pass a function and expect it to be "called back" in certain circumstances.** - -So, `showOk` becomes the callback for the "yes" answer, and `showCancel` for the "no" answer. +The idea is that we pass a function and expect it to be "called back" later if necessary. In our case, `showOk` becomes the callback for the "yes" answer, and `showCancel` for the "no" answer. We can use Function Expressions to write the same function much shorter: @@ -160,6 +158,7 @@ ask( */!* ``` + Here, functions are declared right inside the `ask(...)` call. They have no name, and so are called *anonymous*. Such functions are not accessible outside of `ask` (because they are not assigned to variables), but that's just what we want here. Such code appears in our scripts very naturally, it's in the spirit of JavaScript. @@ -242,10 +241,7 @@ let sayHi = function(name) { // (*) no magic any more Function Expressions are created when the execution reaches them. That would happen only in the line `(*)`. Too late. - -### Function Declaration in a block - -When a Function Declaration is made within a code block, it is visible everywhere inside that block. But not outside of it. +**When a Function Declaration is made within a code block, it is visible everywhere inside that block. But not outside of it.** Sometimes that's handy to declare a local function only needed in that block alone. But that feature may also cause problems. @@ -256,6 +252,7 @@ The code below doesn't work: ```js run let age = prompt("What is your age?", 18); +// conditionally declare a function if (age < 18) { function welcome() { @@ -270,14 +267,15 @@ if (age < 18) { } +// ...use it later *!* welcome(); // Error: welcome is not defined */!* ``` -A Function Declaration is only visible inside the code block in which it resides. +That's because a Function Declaration is only visible inside the code block in which it resides. -We can call it from within the block, but not from outside: +Here's another example: ```js run let age = 16; // take 16 as an example @@ -296,11 +294,10 @@ if (age < 18) { */!* } else { - // \ - function welcome() { // | - alert("Greetings!"); // | if age=16, the the execution does not go here, - } // | so this "welcome" is never created - // / + + function welcome() { // for age = 16, this "welcome" is never created + alert("Greetings!"); + } } // Here we're out of figure brackets, @@ -313,7 +310,9 @@ welcome(); // Error: welcome is not defined What can we do to make `welcome` visible outside of `if`? -The correct approach would be to use a Function Expression and assign `welcome` to the variable that is declared outside of `if` and has the proper visibility: +The correct approach would be to use a Function Expression and assign `welcome` to the variable that is declared outside of `if` and has the proper visibility. + +Now it works as intended: ```js run let age = prompt("What is your age?", 18); @@ -354,7 +353,7 @@ welcome(); // ok now ``` -```smart header="What to choose: Function Declaration or Function Expression?" +```smart header="When to choose Function Declaration versus Function Expression?" As a rule of thumb, when we need to declare a function, the first to consider is Function Declaration syntax, the one we used before. It gives more freedom in how to organize our code, because we can call such functions before they are declared. It's also a little bit easier to look up `function f(…) {…}` in the code than `let f = function(…) {…}`. Function Declarations are more "eye-catching". @@ -365,7 +364,7 @@ It's also a little bit easier to look up `function f(…) {…}` in the code tha ## Arrow functions [#arrow-functions] -There's one more very simple and concise syntax for creating functions. It's called "arrow functions", because it looks like this: +There's one more very simple and concise syntax for creating functions, that's often better than Function Expressions. It's called "arrow functions", because it looks like this: ```js @@ -382,37 +381,37 @@ let func = function(arg1, arg2, ...argN) { } ``` -...But much shorter. +...But much more concise. Let's see an example: ```js run let sum = (a, b) => a + b; -alert( sum(1, 2) ); // 3 -``` - -Here the arrow function is a shorter form of: +/* The arrow function is a shorter form of: -```js let sum = function(a, b) { return a + b; }; +*/ + +alert( sum(1, 2) ); // 3 + ``` If we have only one argument, then parentheses can be omitted, making that even shorter: ```js run // same as -// let double = function(n) { return n*2 } +// let double = function(n) { return n * 2 } *!* -let double = n => n*2; +let double = n => n * 2; */!* alert( double(3) ); // 6 ``` -If there are no arguments, we can insert empty parentheses: +If there are no arguments, parentheses should be empty (but they should be present): ```js run let sayHi = () => alert("Hello!"); @@ -434,9 +433,9 @@ let welcome = (age < 18) ? welcome(); // ok now ``` -The syntax may appear unfamiliar and not very readable at first, but that quickly changes as the eyes get used to the structure. +Arrow functions may appear unfamiliar and not very readable at first, but that quickly changes as the eyes get used to the structure. -Arrow functions are very convenient for simple one-line actions, when we're just too lazy to write many words. +They are very convenient for simple one-line actions, when we're just too lazy to write many words. ```smart header="Multiline arrow functions" diff --git a/1-js/02-first-steps/16-javascript-specials/article.md b/1-js/02-first-steps/16-javascript-specials/article.md index 39e7537082..5e64592dd1 100644 --- a/1-js/02-first-steps/16-javascript-specials/article.md +++ b/1-js/02-first-steps/16-javascript-specials/article.md @@ -216,7 +216,7 @@ switch (age) { alert("Won't work"); // the result of prompt is a string, not a number case "18": - alert("This works!""); + alert("This works!"); break; default: @@ -273,7 +273,7 @@ We covered three ways to create a function in JavaScript: - Functions may have local variables: those declared inside its body. Such variables are only visible inside the function. -- Parameters can have default values: `function sum(a=1, b=2) {...}`. +- Parameters can have default values: `function sum(a = 1, b = 2) {...}`. - Functions always return something. If there's no `return` statement, then the result is `undefined`. diff --git a/1-js/03-code-quality/01-debugging-chrome/article.md b/1-js/03-code-quality/01-debugging-chrome/article.md index 14efb9a556..9c81d2dc79 100644 --- a/1-js/03-code-quality/01-debugging-chrome/article.md +++ b/1-js/03-code-quality/01-debugging-chrome/article.md @@ -101,7 +101,7 @@ Please open the informational dropdowns to the right (labeled with arrows). They 1. **`Watch` -- shows current values for any expressions.** - You can click the plus `+` and input an expression. The debugger will show its value at any moment, automatically recalculating it in in the process of execution. + You can click the plus `+` and input an expression. The debugger will show its value at any moment, automatically recalculating it in the process of execution. 2. **`Call Stack` -- shows the nested calls chain.** diff --git a/1-js/03-code-quality/03-comments/article.md b/1-js/03-code-quality/03-comments/article.md index fbc496286d..4f1871297e 100644 --- a/1-js/03-code-quality/03-comments/article.md +++ b/1-js/03-code-quality/03-comments/article.md @@ -58,8 +58,9 @@ function showPrimes(n) { function isPrime(n) { for (let i = 2; i < n; i++) { - if ( n % i == 0) return false; + if (n % i == 0) return false; } + return true; } ``` diff --git a/1-js/03-code-quality/04-ninja-code/article.md b/1-js/03-code-quality/04-ninja-code/article.md index 0b97b3ff76..58bd9c2ce5 100644 --- a/1-js/03-code-quality/04-ninja-code/article.md +++ b/1-js/03-code-quality/04-ninja-code/article.md @@ -70,7 +70,7 @@ While choosing a name try to use the most abstract word. Like `obj`, `data`, `va ...But what to do if `data` is already taken? Try `value`, it's also universal. After all, a variable eventually gets a *value*. -- **Name the variable by its type: `str`, `num`...** +- **Name a variable by its type: `str`, `num`...** Give them a try. A young ninja may wonder -- do such names make the code worse? Actually, yes! @@ -78,7 +78,7 @@ While choosing a name try to use the most abstract word. Like `obj`, `data`, `va Indeed, the value type is easy to find out by debugging. But what's the meaning of the variable? Which string/number does it store? There's just no way to figure out without a good meditation! -- **...But what if there are no more such names?** Just add a letter: `item1, item2, elem5, data1`... +- **...But what if there are no more such names?** Just add a number: `data1, item2, elem5`... ## Attention test diff --git a/1-js/03-code-quality/06-polyfills/article.md b/1-js/03-code-quality/06-polyfills/article.md index 37ea9f8978..caf0826c7d 100644 --- a/1-js/03-code-quality/06-polyfills/article.md +++ b/1-js/03-code-quality/06-polyfills/article.md @@ -46,7 +46,7 @@ alert('Press the "Play" button in the upper-right corner to run'); ``` Examples that use modern JS will work only if your browser supports it. -``` +```` ```offline As you're reading the offline version, examples are not runnable. But they usually work :) diff --git a/1-js/04-object-basics/01-object/3-is-empty/solution.md b/1-js/04-object-basics/01-object/3-is-empty/solution.md index cd50543eb7..b3f40e3d22 100644 --- a/1-js/04-object-basics/01-object/3-is-empty/solution.md +++ b/1-js/04-object-basics/01-object/3-is-empty/solution.md @@ -2,7 +2,7 @@ Just loop over the object and `return false` immediately if there's at least one ```js function isEmpty(obj) { - for(let key in obj) { + for (let key in obj) { return false; } return true; diff --git a/1-js/04-object-basics/01-object/5-sum-object/solution.md b/1-js/04-object-basics/01-object/5-sum-object/solution.md index eb5b176a29..63df878493 100644 --- a/1-js/04-object-basics/01-object/5-sum-object/solution.md +++ b/1-js/04-object-basics/01-object/5-sum-object/solution.md @@ -4,10 +4,10 @@ let salaries = { John: 100, Ann: 160, Pete: 130 -} +}; let sum = 0; -for(let key in salaries) { +for (let key in salaries) { sum += salaries[key]; } diff --git a/1-js/04-object-basics/01-object/article.md b/1-js/04-object-basics/01-object/article.md index ce86f7fba4..31e164d985 100644 --- a/1-js/04-object-basics/01-object/article.md +++ b/1-js/04-object-basics/01-object/article.md @@ -189,7 +189,7 @@ We can use more complex expressions inside square brackets: ```js let fruit = 'apple'; let bag = { - ['apple' + 'Computers']: 5 // bag.appleComputers = 5 + [fruit + 'Computers']: 5 // bag.appleComputers = 5 }; ``` @@ -222,7 +222,14 @@ obj.__proto__ = 5; alert(obj.__proto__); // [object Object], didn't work as intended ``` -As we see from the code, the assignment to a primitive `5` is ignored. If we want to store *arbitrary* (user-provided) keys, then such behavior can be the source of bugs and even vulnerabilities, because it's unexpected. There's another data structure [Map](info:map-set-weakmap-weakset), that we'll learn in the chapter , which supports arbitrary keys. +As we see from the code, the assignment to a primitive `5` is ignored. + +That can become a source of bugs and even vulnerabilies if we intent to store arbitrary key-value pairs in an object, and allow a visitor to specify the keys. + +In that case the visitor may choose "__proto__" as the key, and the assignment logic will be ruined (as shown above). + +There exist a way to make objects treat `__proto__` as a regular property, we'll cover it later, but first we need to know more about objects to understand it. +There's another data structure [Map](info:map-set-weakmap-weakset), that we'll learn in the chapter , which supports arbitrary keys. Also ```` diff --git a/1-js/04-object-basics/03-symbol/article.md b/1-js/04-object-basics/03-symbol/article.md index 47fdc28b0a..3983ae5620 100644 --- a/1-js/04-object-basics/03-symbol/article.md +++ b/1-js/04-object-basics/03-symbol/article.md @@ -9,18 +9,25 @@ Till now we've only seen strings. Now let's see the advantages that symbols can ## Symbols -"Symbol" value represents an unique identifier with a given name. +"Symbol" value represents a unique identifier. -A value of this type can be created using `Symbol(name)`: +A value of this type can be created using `Symbol()`: ```js -// id is a symbol with the name "id" +// id is a new symbol +let id = Symbol(); +``` + +We can also give symbol a description (also called a symbol name), mostly useful for debugging purposes: + +```js +// id is a symbol with the description "id" let id = Symbol("id"); ``` -Symbols are guaranteed to be unique. Even if we create many symbols with the same name, they are different values. +Symbols are guaranteed to be unique. Even if we create many symbols with the same description, they are different values. The description is just a label that doesn't affect anything. -For instance, here are two symbols with the same name -- they are not equal: +For instance, here are two symbols with the same description -- they are not equal: ```js run let id1 = Symbol("id"); @@ -56,13 +63,11 @@ alert(id.toString()); // Symbol(id), now it works That's a "language guard" against messing up, because strings and symbols are fundamentally different and should not occasionally convert one into another. ```` - - ## "Hidden" properties Symbols allow us to create "hidden" properties of an object, that no other part of code can occasionally access or overwrite. -For instance, if we want to store an "identifier" for the object `user`, we can create a symbol with the name `id` for it: +For instance, if we want to store an "identifier" for the object `user`, we can use a symbol as a key for it: ```js run let user = { name: "John" }; @@ -72,11 +77,13 @@ user[id] = "ID Value"; alert( user[id] ); // we can access the data using the symbol as the key ``` -Now let's imagine that another script wants to have its own "id" property inside `user`, for its own purposes. That may be another JavaScript library, so the scripts are completely unaware of each other. +What's the benefit over using `Symbol("id")` over a string `"id"`? + +Let's make the example a bit deeper to see that. -No problem. It can create its own `Symbol("id")`. +Imagine that another script wants to have its own "id" property inside `user`, for its own purposes. That may be another JavaScript library, so the scripts are completely unaware of each other. -The script: +Then that script can create its own `Symbol("id")`, like this: ```js // ... @@ -87,7 +94,7 @@ user[id] = "Their id value"; There will be no conflict, because symbols are always different, even if they have the same name. -Please note that if we used a string `"id"` instead of a symbol for the same purpose, then there *would* be a conflict: +Now note that if we used a string `"id"` instead of a symbol for the same purpose, then there *would* be a conflict: ```js run let user = { name: "John" }; @@ -134,7 +141,7 @@ let user = { }; *!* -for(let key in user) alert(key); // name, age (no symbols) +for (let key in user) alert(key); // name, age (no symbols) */!* // the direct access by the symbol works @@ -176,25 +183,27 @@ alert( obj[0] ); // test (same property) ## Global symbols -As we've seen, usually all symbols are different, even if they have the same name. But sometimes we want same-named symbols to be the same entities. +As we've seen, usually all symbols are different, even if they have the same names. But sometimes we want same-named symbols to be same entities. For instance, different parts of our application want to access symbol `"id"` meaning exactly the same property. To achieve that, there exists a *global symbol registry*. We can create symbols in it and access them later, and it guarantees that repeated accesses by the same name return exactly the same symbol. -In order to create or read a symbol in the registry, use `Symbol.for(name)`. +In order to create or read a symbol in the registry, use `Symbol.for(key)`. + +That call checks the global registry, and if there's a symbol described as `key`, then returns it, otherwise creates a new symbol `Symbol(key)` and stores it in the registry by the given `key`. For instance: ```js run // read from the global registry -let name = Symbol.for("name"); // if the symbol did not exist, it is created +let id = Symbol.for("id"); // if the symbol did not exist, it is created // read it again -let nameAgain = Symbol.for("name"); +let idAgain = Symbol.for("id"); // the same symbol -alert( name === nameAgain ); // true +alert( id === idAgain ); // true ``` Symbols inside the registry are called *global symbols*. If we want an application-wide symbol, accessible everywhere in the code -- that's what they are for. @@ -207,7 +216,7 @@ In JavaScript, as we can see, that's right for global symbols. ### Symbol.keyFor -For global symbols, not only `Symbol.for(name)` returns a symbol by name, but there's a reverse call: `Symbol.keyFor(sym)`, that does the reverse: returns a name by a global symbol. +For global symbols, not only `Symbol.for(key)` returns a symbol by name, but there's a reverse call: `Symbol.keyFor(sym)`, that does the reverse: returns a name by a global symbol. For instance: @@ -220,20 +229,16 @@ alert( Symbol.keyFor(sym) ); // name alert( Symbol.keyFor(sym2) ); // id ``` -The `Symbol.keyFor` internally uses the global symbol registry to look up the name for the symbol. So it doesn't work for non-global symbols. If the symbol is not global, it won't be able to find it and return `undefined`. +The `Symbol.keyFor` internally uses the global symbol registry to look up the key for the symbol. So it doesn't work for non-global symbols. If the symbol is not global, it won't be able to find it and return `undefined`. For instance: ```js run alert( Symbol.keyFor(Symbol.for("name")) ); // name, global symbol -alert( Symbol.keyFor(Symbol("name2")) ); // undefined, non-global symbol +alert( Symbol.keyFor(Symbol("name2")) ); // undefined, the argument isn't a global symbol ``` -So, for global symbols the name may be indeed helpful, as we can get a symbol by id. - -And for non-global symbols the name is only used for debugging purposes, like printing out a symbol. - ## System symbols There exist many "system" symbols that JavaScript uses internally, and we can use them to fine-tune various aspects of our objects. @@ -254,9 +259,9 @@ Other symbols will also become familiar when we study the corresponding language `Symbol` is a primitive type for unique identifiers. -Symbols are created with `Symbol(name)` call. +Symbols are created with `Symbol()` call with an optional description. -Symbols are always different values, even if they have the same name. If we want same-named symbols to be equal, then we should use the global registry: `Symbol.for(name)` returns (creates if needed) a global symbol with the given name. Multiple calls of `Symbol.for` return exactly the same symbol. +Symbols are always different values, even if they have the same name. If we want same-named symbols to be equal, then we should use the global registry: `Symbol.for(key)` returns (creates if needed) a global symbol with `key` as the name. Multiple calls of `Symbol.for` return exactly the same symbol. Symbols have two main use cases: diff --git a/1-js/04-object-basics/05-object-toprimitive/article.md b/1-js/04-object-basics/05-object-toprimitive/article.md index 943fd0224a..2bb3270019 100644 --- a/1-js/04-object-basics/05-object-toprimitive/article.md +++ b/1-js/04-object-basics/05-object-toprimitive/article.md @@ -213,7 +213,7 @@ For instance: ```smart header="Historical notes" For historical reasons, methods `toString` or `valueOf` *should* return a primitive: if any of them returns an object, then there's no error, but that object is ignored (like if the method didn't exist). -In contrast, `Symbol.toPrimitive` *must* return an primitive, otherwise there will be an error. +In contrast, `Symbol.toPrimitive` *must* return a primitive, otherwise, there will be an error. ``` ## Summary diff --git a/1-js/05-data-types/01-primitives-methods/article.md b/1-js/05-data-types/01-primitives-methods/article.md index c583ecedf6..e822190750 100644 --- a/1-js/05-data-types/01-primitives-methods/article.md +++ b/1-js/05-data-types/01-primitives-methods/article.md @@ -70,7 +70,7 @@ So primitives can provide methods, but they still remain lightweight. The JavaScript engine highly optimizes this process. It may even skip the creation of the extra object at all. But it must still adhere to the specification and behave as if it creates one. -A number has methods of it's own, for instance, [toFixed(n)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed) rounds the number to the given precision: +A number has methods of its own, for instance, [toFixed(n)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed) rounds the number to the given precision: ```js run let n = 1.23456; diff --git a/1-js/05-data-types/02-number/article.md b/1-js/05-data-types/02-number/article.md index 0a9feb1c83..16e4275e60 100644 --- a/1-js/05-data-types/02-number/article.md +++ b/1-js/05-data-types/02-number/article.md @@ -249,10 +249,10 @@ Can we work around the problem? Sure, there're a number of ways: 2. We can temporarily turn numbers into integers for the maths and then revert it back. It works like this: ```js run - alert( (0.1*10 + 0.2*10) / 10 ); // 0.3 + alert( (0.1 * 10 + 0.2 * 10) / 10 ); // 0.3 ``` - This works because when we do `0.1*10 = 1` and `0.2 * 10 = 2` then both numbers become integers, and there's no precision loss. + This works because when we do `0.1 * 10 = 1` and `0.2 * 10 = 2` then both numbers become integers, and there's no precision loss. 3. If we were dealing with a shop, then the most radical solution would be to store all prices in cents and use no fractions at all. But what if we apply a discount of 30%? In practice, totally evading fractions is rarely feasible, so the solutions above help avoid this pitfall. @@ -270,7 +270,7 @@ JavaScript doesn't trigger an error in such events. It does its best to fit the ```` ```smart header="Two zeroes" -Another funny consequence of the internal representation of numbers is the existance of two zeroes: `0` and `-0`. +Another funny consequence of the internal representation of numbers is the existence of two zeroes: `0` and `-0`. That's because a sign is represented by a single bit, so every number can be positive or negative, including a zero. diff --git a/1-js/05-data-types/03-string/article.md b/1-js/05-data-types/03-string/article.md index e002ae478a..b74399a94f 100644 --- a/1-js/05-data-types/03-string/article.md +++ b/1-js/05-data-types/03-string/article.md @@ -19,7 +19,7 @@ let double = "double-quoted"; let backticks = `backticks`; ``` -Single and double quotes are essentially the same. Backticks however allow us to embed any expression into the string, including function calls: +Single and double quotes are essentially the same. Backticks, however, allow us to embed any expression into the string, including function calls: ```js run function sum(a, b) { @@ -49,7 +49,7 @@ let guestList = "Guests: // Error: Unexpected token ILLEGAL Single and double quotes come from ancient times of language creation when the need for multiline strings was not taken into account. Backticks appeared much later and thus are more versatile. -Backticks also allow us to specify a "template function" before the first backtick. The syntax is: func`string`. The function `func` is called automatically, receives the string and embedded expressions and can process them. You can read more about it in the [docs](mdn:JavaScript/Reference/Template_literals#Tagged_template_literals). This is called "tagged templates". This feature makes it easier to wrap strings into custom templating or other functionality, but it is rarely used. +Backticks also allow us to specify a "template function" before the first backtick. The syntax is: func`string`. The function `func` is called automatically, receives the string and embedded expressions and can process them. You can read more about it in the [docs](mdn:/JavaScript/Reference/Template_literals#Tagged_template_literals). This is called "tagged templates". This feature makes it easier to wrap strings into custom templating or other functionality, but it is rarely used. ## Special characters @@ -88,8 +88,8 @@ Examples with unicode: ```js run alert( "\u00A9" ); // © -alert( "\u{20331}" ); // 𠌱, a rare chinese hieroglyph (long unicode) -alert( "\u{1F60D}"); // a smiling face sumbol (another long unicode) +alert( "\u{20331}" ); // 佫, a rare chinese hieroglyph (long unicode) +alert( "\u{1F60D}" ); // 😍, a smiling face symbol (another long unicode) ``` All special characters start with a backslash character `\`. It is also called an "escape character". @@ -166,7 +166,7 @@ alert( str.charAt(1000) ); // '' (an empty string) We can also iterate over characters using `for..of`: ```js run -for(let char of "Hello") { +for (let char of "Hello") { alert(char); // H,e,l,l,o (char becomes "H", then "e", then "l" etc) } ``` @@ -379,8 +379,8 @@ There are 3 methods in JavaScript to get a substring: `substring`, `substr` and ```js run let str = "stringify"; - alert( str.slice(0,5) ); // 'strin', the substring from 0 to 5 (not including 5) - alert( str.slice(0,1) ); // 's', from 0 to 1, but not including 1, so only character at 0 + alert( str.slice(0, 5) ); // 'strin', the substring from 0 to 5 (not including 5) + alert( str.slice(0, 1) ); // 's', from 0 to 1, but not including 1, so only character at 0 ``` If there is no second argument, then `slice` goes till the end of the string: @@ -548,7 +548,7 @@ For instance: alert( 'Österreich'.localeCompare('Zealand') ); // -1 ``` -This method actually has two additional arguments specified in [the documentation](mdn:js/String/localeCompare), which allow it 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. +This method actually has two additional arguments specified in [the documentation](mdn:js/String/localeCompare), which allows it to specify the language (by default taken from the environment) and setup additional rules like case sensitivity or should `"a"` and `"á"` be treated as the same etc. ## Internals, Unicode diff --git a/1-js/05-data-types/04-array/2-create-array/solution.md b/1-js/05-data-types/04-array/2-create-array/solution.md index 2435143469..eec9055e7c 100644 --- a/1-js/05-data-types/04-array/2-create-array/solution.md +++ b/1-js/05-data-types/04-array/2-create-array/solution.md @@ -3,7 +3,7 @@ ```js run let styles = ["Jazz", "Blues"]; styles.push("Rock-n-Roll"); -styles[(styles.length + 1) / 2] = "Classics"; +styles[Math.floor((styles.length - 1) / 2)] = "Classics"; alert( styles.shift() ); styles.unshift("Rap", "Reggie"); ``` diff --git a/1-js/05-data-types/04-array/2-create-array/task.md b/1-js/05-data-types/04-array/2-create-array/task.md index e6dab6a965..3e93007939 100644 --- a/1-js/05-data-types/04-array/2-create-array/task.md +++ b/1-js/05-data-types/04-array/2-create-array/task.md @@ -10,7 +10,7 @@ Let's try 5 array operations. 2. Append "Rock-n-Roll" to the end. 3. Replace the value in the middle by "Classics". Your code for finding the middle value should work for any arrays with odd length. 4. Strip off the first value of the array and show it. -5. Prepend `Rap` and `Reggie` to the array. +5. Prepend `Rap` and `Reggae` to the array. The array in the process: @@ -19,6 +19,6 @@ Jazz, Blues Jazz, Bues, Rock-n-Roll Jazz, Classics, Rock-n-Roll Classics, Rock-n-Roll -Rap, Reggie, Classics, Rock-n-Roll +Rap, Reggae, Classics, Rock-n-Roll ``` diff --git a/1-js/05-data-types/04-array/article.md b/1-js/05-data-types/04-array/article.md index 781699f7d1..825cfbedf3 100644 --- a/1-js/05-data-types/04-array/article.md +++ b/1-js/05-data-types/04-array/article.md @@ -113,7 +113,7 @@ There's another use case for arrays -- the data structure named [stack](https:// It supports two operations: - `push` adds an element to the end. -- `pop` takes an element to the end. +- `pop` takes an element from the end. So new elements are added or taken always from the "end". @@ -297,7 +297,7 @@ But for arrays there is another form of loop, `for..of`: let fruits = ["Apple", "Orange", "Plum"]; // iterates over array elements -for(let fruit of fruits) { +for (let fruit of fruits) { alert( fruit ); } ``` @@ -356,7 +356,7 @@ arr.length = 5; // return length back alert( arr[3] ); // undefined: the values do not return ``` -So, the simplest way to clear the array is: `arr.length=0`. +So, the simplest way to clear the array is: `arr.length = 0;`. ## new Array() [#new-array] @@ -385,9 +385,9 @@ In the code above, `new Array(number)` has all elements `undefined`. To evade such surprises, we usually use square brackets, unless we really know what we're doing. -## Multidimentional arrays +## Multidimensional arrays -Arrays can have items that are also arrays. We can use it for multidimentional arrays, to store matrices: +Arrays can have items that are also arrays. We can use it for multidimensional arrays, to store matrices: ```js run let matrix = [ @@ -458,9 +458,9 @@ We can use an array as a deque with the following operations: - `unshift(...items)` adds items to the beginning. To loop over the elements of the array: - - `for(let i=0; i. diff --git a/1-js/05-data-types/05-array-methods/10-average-age/solution.md b/1-js/05-data-types/05-array-methods/10-average-age/solution.md index 5e19fb5ac5..f5d4df9318 100644 --- a/1-js/05-data-types/05-array-methods/10-average-age/solution.md +++ b/1-js/05-data-types/05-array-methods/10-average-age/solution.md @@ -3,9 +3,9 @@ function getAverageAge(users) { return users.reduce((prev, user) => prev + user.age, 0) / users.length; } -let john = { name: "John", age: 25 } -let pete = { name: "Pete", age: 30 } -let mary = { name: "Mary", age: 29 } +let john = { name: "John", age: 25 }; +let pete = { name: "Pete", age: 30 }; +let mary = { name: "Mary", age: 29 }; let arr = [ john, pete, mary ]; diff --git a/1-js/05-data-types/05-array-methods/10-average-age/task.md b/1-js/05-data-types/05-array-methods/10-average-age/task.md index ccb2142dbe..a991c156b9 100644 --- a/1-js/05-data-types/05-array-methods/10-average-age/task.md +++ b/1-js/05-data-types/05-array-methods/10-average-age/task.md @@ -11,12 +11,12 @@ The formula for the average is `(age1 + age2 + ... + ageN) / N`. For instance: ```js no-beautify -let john = { name: "John", age: 25 } -let pete = { name: "Pete", age: 30 } -let mary = { name: "Mary", age: 29 } +let john = { name: "John", age: 25 }; +let pete = { name: "Pete", age: 30 }; +let mary = { name: "Mary", age: 29 }; let arr = [ john, pete, mary ]; -alert( getAverageAge(arr) ); // (25+30+29)/3 = 28 +alert( getAverageAge(arr) ); // (25 + 30 + 29) / 3 = 28 ``` diff --git a/1-js/05-data-types/05-array-methods/11-array-unique/solution.md b/1-js/05-data-types/05-array-methods/11-array-unique/solution.md index 5b565d4230..8f7fd9af44 100644 --- a/1-js/05-data-types/05-array-methods/11-array-unique/solution.md +++ b/1-js/05-data-types/05-array-methods/11-array-unique/solution.md @@ -7,7 +7,7 @@ function unique(arr) { let result = []; for (let str of arr) { - if (!result.includes(str) { + if (!result.includes(str)) { result.push(str); } } diff --git a/1-js/05-data-types/05-array-methods/2-filter-range/task.md b/1-js/05-data-types/05-array-methods/2-filter-range/task.md index fb6f06eefb..18b2c1d9b5 100644 --- a/1-js/05-data-types/05-array-methods/2-filter-range/task.md +++ b/1-js/05-data-types/05-array-methods/2-filter-range/task.md @@ -2,7 +2,7 @@ importance: 4 --- -# Filter "in place" +# Filter range Write a function `filterRange(arr, a, b)` that gets an array `arr`, looks for elements between `a` and `b` in it and returns an array of them. diff --git a/1-js/05-data-types/05-array-methods/4-sort-back/solution.md b/1-js/05-data-types/05-array-methods/4-sort-back/solution.md index 5a9f076bca..cdf1335116 100644 --- a/1-js/05-data-types/05-array-methods/4-sort-back/solution.md +++ b/1-js/05-data-types/05-array-methods/4-sort-back/solution.md @@ -3,7 +3,7 @@ ```js run let arr = [5, 2, 1, -10, 8]; -arr.sort((a,b) => b - a); +arr.sort((a, b) => b - a); alert( arr ); ``` diff --git a/1-js/05-data-types/05-array-methods/7-map-objects/solution.md b/1-js/05-data-types/05-array-methods/7-map-objects/solution.md index 5e9b815f1b..5d8bf4a13d 100644 --- a/1-js/05-data-types/05-array-methods/7-map-objects/solution.md +++ b/1-js/05-data-types/05-array-methods/7-map-objects/solution.md @@ -21,8 +21,8 @@ usersMapped = [ ] */ -alert( usersMapped[0].id ) // 1 -alert( usersMapped[0].fullName ) // John Smith +alert( usersMapped[0].id ); // 1 +alert( usersMapped[0].fullName ); // John Smith ``` Please note that in for the arrow functions we need to use additional brackets. @@ -43,7 +43,7 @@ Here JavaScript would treat `{` as the start of function body, not the start of let usersMapped = users.map(user => *!*({*/!* fullName: `${user.name} ${user.surname}`, id: user.id -}); +})); ``` Now fine. diff --git a/1-js/05-data-types/05-array-methods/8-sort-objects/solution.md b/1-js/05-data-types/05-array-methods/8-sort-objects/solution.md index 2c7c518162..8d56db9d66 100644 --- a/1-js/05-data-types/05-array-methods/8-sort-objects/solution.md +++ b/1-js/05-data-types/05-array-methods/8-sort-objects/solution.md @@ -12,6 +12,6 @@ let arr = [ john, pete, mary ]; sortByName(arr); // now sorted is: [john, mary, pete] -alert(arr[1].name) // Mary +alert(arr[1].name); // Mary ``` diff --git a/1-js/05-data-types/05-array-methods/8-sort-objects/task.md b/1-js/05-data-types/05-array-methods/8-sort-objects/task.md index 935c27fc66..8a3f5a97a3 100644 --- a/1-js/05-data-types/05-array-methods/8-sort-objects/task.md +++ b/1-js/05-data-types/05-array-methods/8-sort-objects/task.md @@ -9,15 +9,15 @@ Write the function `sortByName(users)` that gets an array of objects with proper For instance: ```js no-beautify -let john = { name: "John", age: 25 } -let pete = { name: "Pete", age: 30 } -let mary = { name: "Mary", age: 28 } +let john = { name: "John", age: 25 }; +let pete = { name: "Pete", age: 30 }; +let mary = { name: "Mary", age: 28 }; let arr = [ john, pete, mary ]; -lsortByName(arr); +sortByName(arr); // now: [john, mary, pete] -alert(arr[1].name) // Mary +alert(arr[1].name); // Mary ``` diff --git a/1-js/05-data-types/05-array-methods/9-shuffle/solution.md b/1-js/05-data-types/05-array-methods/9-shuffle/solution.md index 12cf75515d..a43715db87 100644 --- a/1-js/05-data-types/05-array-methods/9-shuffle/solution.md +++ b/1-js/05-data-types/05-array-methods/9-shuffle/solution.md @@ -12,7 +12,7 @@ shuffle(arr); alert(arr); ``` -That somewhat works, because `Math.random()-0.5` is a random number that may be positive or negative, so the sorting function reorders elements randomly. +That somewhat works, because `Math.random() - 0.5` is a random number that may be positive or negative, so the sorting function reorders elements randomly. But because the sorting function is not meant to be used this way, not all permutations have the same probability. @@ -33,14 +33,14 @@ let count = { '312': 0 }; -for(let i = 0; i < 1000000; i++) { +for (let i = 0; i < 1000000; i++) { let array = [1, 2, 3]; shuffle(array); count[array.join('')]++; } // show counts of all possible permutations -for(let key in count) { +for (let key in count) { alert(`${key}: ${count[key]}`); } ``` @@ -66,8 +66,8 @@ There are other good ways to do the task. For instance, there's a great algorith ```js function shuffle(array) { - for(let i = array.length - 1; i > 0; i--) { - let j = Math.floor(Math.random() * (i+1)); // random index from 0 to i + for (let i = array.length - 1; i > 0; i--) { + let j = Math.floor(Math.random() * (i + 1)); // random index from 0 to i [array[i], array[j]] = [array[j], array[i]]; // swap elements } } @@ -77,8 +77,8 @@ Let's test it the same way: ```js run function shuffle(array) { - for(let i = array.length - 1; i > 0; i--) { - let j = Math.floor(Math.random() * (i+1)); + for (let i = array.length - 1; i > 0; i--) { + let j = Math.floor(Math.random() * (i + 1)); [array[i], array[j]] = [array[j], array[i]]; } } @@ -93,14 +93,14 @@ let count = { '312': 0 }; -for(let i = 0; i < 1000000; i++) { +for (let i = 0; i < 1000000; i++) { let array = [1, 2, 3]; shuffle(array); count[array.join('')]++; } // show counts of all possible permutations -for(let key in count) { +for (let key in count) { alert(`${key}: ${count[key]}`); } ``` diff --git a/1-js/05-data-types/05-array-methods/article.md b/1-js/05-data-types/05-array-methods/article.md index 261634579a..a9f9720d89 100644 --- a/1-js/05-data-types/05-array-methods/article.md +++ b/1-js/05-data-types/05-array-methods/article.md @@ -64,13 +64,13 @@ alert( arr ); // ["I", "JavaScript"] Easy, right? Starting from the index `1` it removed `1` element. -In the next example we remove 3 elements and replace them by the other two: +In the next example we remove 3 elements and replace them with the other two: ```js run let arr = [*!*"I", "study", "JavaScript",*/!* "right", "now"]; -// remove 3 first elements and replace them by another -arr.splice(0, 3, "Let's", "dance") +// remove 3 first elements and replace them with another +arr.splice(0, 3, "Let's", "dance"); alert( arr ) // now [*!*"Let's", "dance"*/!*, "right", "now"] ``` @@ -103,7 +103,7 @@ alert( arr ); // "I", "study", "complex", "language", "JavaScript" Here and in other array methods, negative indexes are allowed. They specify the position from the end of the array, like here: ```js run -let arr = [1, 2, 5] +let arr = [1, 2, 5]; // from index -1 (one step from the end) // delete 0 elements, @@ -155,7 +155,7 @@ It accepts any number of arguments -- either arrays or values. The result is a new array containing items from `arr`, then `arg1`, `arg2` etc. -If an argument is an array or has `Symbol.isConcatSpreadable` property, then all its elements are copied. Otherwise the argument itself is copied. +If an argument is an array or has `Symbol.isConcatSpreadable` property, then all its elements are copied. Otherwise, the argument itself is copied. For instance: @@ -172,7 +172,7 @@ alert( arr.concat([3, 4], [5, 6])); // 1,2,3,4,5,6 alert( arr.concat([3, 4], 5, 6)); // 1,2,3,4,5,6 ``` -Normally, it only copies elements from arrays ("spreads" them), other objects even if they look like arrays and added as a whole: +Normally, it only copies elements from arrays ("spreads" them). Other objects, even if they look like arrays, added as a whole: ```js run let arr = [1, 2]; @@ -211,7 +211,7 @@ These are methods to search for something in an array. The methods [arr.indexOf](mdn:js/Array/indexOf), [arr.lastIndexOf](mdn:js/Array/lastIndexOf) and [arr.includes](mdn:js/Array/includes) have the same syntax and do essentially the same as their string counterparts, but operate on items instead of characters: -- `arr.indexOf(item, from)` looks for `item` starting from index `from`, and returns the index where it was found, otheriwse `-1`. +- `arr.indexOf(item, from)` looks for `item` starting from index `from`, and returns the index where it was found, otherwise `-1`. - `arr.lastIndexOf(item, from)` -- same, but looks from right to left. - `arr.includes(item, from)` -- looks for `item` starting from index `from`, returns `true` if found. @@ -231,6 +231,13 @@ Note that the methods use `===` comparison. So, if we look for `false`, it finds If we want to check for inclusion, and don't want to know the exact index, then `arr.includes` is preferred. +Also, a very minor difference of `include` is that it correctly handles `NaN`, unlike `indexOf/lastIndexOf`: + +```js run +const arr = [NaN]; +alert( arr.indexOf(NaN) ); // -1 (should be 0, but === equality doesn't work for NaN) +alert( arr.includes(NaN) );// true (correct) +``` ### find and findIndex @@ -316,7 +323,7 @@ The syntax is: ```js let result = arr.map(function(item, index, array) { // returns the new value instead of item -} +}) ``` It calls the function for each element of the array and returns the array of results. @@ -472,7 +479,7 @@ alert( str.split('') ); // t,e,s,t ``` ```` -The call [arr.join(str)](mdn:js/Array/join) does the reverse to `split`. It creates a string of `arr` items glued by `str` beween them. +The call [arr.join(str)](mdn:js/Array/join) does the reverse to `split`. It creates a string of `arr` items glued by `str` between them. For instance: @@ -515,7 +522,7 @@ The easiest way to grasp that is by example. Here we get a sum of array in one line: ```js run -let arr = [1, 2, 3, 4, 5] +let arr = [1, 2, 3, 4, 5]; let result = arr.reduce((sum, current) => sum + current, 0); @@ -528,7 +535,7 @@ Let's see the details of what's going on. 1. On the first run, `sum` is the initial value (the last argument of `reduce`), equals `0`, and `current` is the first array element, equals `1`. So the result is `1`. 2. On the second run, `sum = 1`, we add the second array element (`2`) to it and return. -3. On the 3rd run, `sum = 3` and we add one more element ot it, and so on... +3. On the 3rd run, `sum = 3` and we add one more element to it, and so on... The calculation flow: @@ -687,7 +694,7 @@ A cheatsheet of array methods: - To search among elements: - `indexOf/lastIndexOf(item, pos)` -- look for `item` starting from position `pos`, return the index or `-1` if not found. - `includes(value)` -- returns `true` if the array has `value`, otherwise `false`. - - `find/filter(func)` -- filter elements of through the function, return first/all values that make it return `true`. + - `find/filter(func)` -- filter elements through the function, return first/all values that make it return `true`. - `findIndex` is like `find`, but returns the index instead of a value. - To transform the array: @@ -703,7 +710,7 @@ A cheatsheet of array methods: - Additionally: - `Array.isArray(arr)` checks `arr` for being an array. -Of all these methods only `sort`, `reverse` and `splice` modify the array itself, the other ones only return a value. +Please note that methods `sort`, `reverse` and `splice` modify the array itself. These methods are the most used ones, they cover 99% of use cases. But there are few others: diff --git a/1-js/05-data-types/06-iterable/article.md b/1-js/05-data-types/06-iterable/article.md index cd6c074ba2..1ccbaea056 100644 --- a/1-js/05-data-types/06-iterable/article.md +++ b/1-js/05-data-types/06-iterable/article.md @@ -121,7 +121,7 @@ Arrays and strings are most widely used built-in iterables. For a string, `for..of` loops over its characters: ```js run -for(let char of "test") { +for (let char of "test") { alert( char ); // t, then e, then s, then t } ``` @@ -130,8 +130,8 @@ And it works right with surrogate pairs! ```js run let str = '𝒳😂'; -for(let char of str) { - alert(char); // 𝒳, and then 😂 +for (let char of str) { + alert( char ); // 𝒳, and then 😂 } ``` @@ -139,7 +139,7 @@ for(let char of str) { 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. +But to understand things a little bit deeper let's see how to create an iterator explicitly. We'll iterate over a string the same way as `for..of`, but with direct calls. This code gets a string iterator and calls it "manually": @@ -151,7 +151,7 @@ let str = "Hello"; let iterator = str[Symbol.iterator](); -while(true) { +while (true) { let result = iterator.next(); if (result.done) break; alert(result.value); // outputs characters one by one @@ -184,7 +184,7 @@ let arrayLike = { // has indexes and length => array-like *!* // Error (no Symbol.iterator) -for(let item of arrayLike) {} +for (let item of arrayLike) {} */!* ``` @@ -258,7 +258,7 @@ Technically here it does the same as: let str = '𝒳😂'; let chars = []; // Array.from internally does the same loop -for(let char of str) { +for (let char of str) { chars.push(char); } diff --git a/1-js/05-data-types/07-map-set-weakmap-weakset/02-filter-anagrams/solution.md b/1-js/05-data-types/07-map-set-weakmap-weakset/02-filter-anagrams/solution.md index f95b41aa20..dacb42856c 100644 --- a/1-js/05-data-types/07-map-set-weakmap-weakset/02-filter-anagrams/solution.md +++ b/1-js/05-data-types/07-map-set-weakmap-weakset/02-filter-anagrams/solution.md @@ -15,7 +15,7 @@ We'll use the letter-sorted variants as map keys to store only one value per eac function aclean(arr) { let map = new Map(); - for(let word of arr) { + for (let word of arr) { // split the word by letters, sort them and join back *!* let sorted = word.toLowerCase().split('').sort().join(''); // (*) diff --git a/1-js/05-data-types/07-map-set-weakmap-weakset/article.md b/1-js/05-data-types/07-map-set-weakmap-weakset/article.md index 40ff18b5fc..e3e28f4fd6 100644 --- a/1-js/05-data-types/07-map-set-weakmap-weakset/article.md +++ b/1-js/05-data-types/07-map-set-weakmap-weakset/article.md @@ -16,7 +16,7 @@ The main methods are: - `new Map()` -- creates the map. - `map.set(key, value)` -- stores the value by the key. -- `map.get(key)` -- returns the value by the key. +- `map.get(key)` -- returns the value by the key, `undefined` if `key` doesn't exist in map. - `map.has(key)` -- returns `true` if the `key` exists, `false` otherwise. - `map.delete(key)` -- removes the value by the key. - `map.clear()` -- clears the map @@ -137,18 +137,18 @@ let recipeMap = new Map([ ]); // iterate over keys (vegetables) -for(let vegetable of recipeMap.keys()) { +for (let vegetable of recipeMap.keys()) { alert(vegetable); // cucumber, tomateos, onion } // iterate over values (amounts) -for(let amount of recipeMap.values()) { +for (let amount of recipeMap.values()) { alert(amount); // 500, 350, 50 } // iterate over [key, value] entries -for(let entry of recipeMap) { // the same as of recipeMap.entries() - alert(entry); // cucumber,50 (and so on) +for (let entry of recipeMap) { // the same as of recipeMap.entries() + alert(entry); // cucumber,500 (and so on) } ``` @@ -160,7 +160,7 @@ Besides that, `Map` has a built-in `forEach` method, similar to `Array`: ```js recipeMap.forEach( (value, key, map) => { - alert(`${key}: ${value}`); // cucumber: 50 etc + alert(`${key}: ${value}`); // cucumber: 500 etc }); ``` @@ -199,7 +199,7 @@ set.add(mary); // set keeps only unique values alert( set.size ); // 3 -for(let user of set) { +for (let user of set) { alert(user.name); // John (then Pete and Mary) } ``` @@ -213,7 +213,7 @@ We can loop over a set either with `for..of` or using `forEach`: ```js run let set = new Set(["oranges", "apples", "bananas"]); -for(let value of set) alert(value); +for (let value of set) alert(value); // the same with forEach: set.forEach((value, valueAgain, set) => { @@ -332,7 +332,7 @@ That's useful for situations when we have a main storage for the objects somewhe 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. +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 anymore. One way would be to keep track of leaving users and clean up the storage manually: @@ -345,7 +345,7 @@ let visitsCountMap = new Map(); // john is the key for the map visitsCountMap.set(john, 123); -// now john leaves us, we don't need him any more +// now john leaves us, we don't need him anymore john = null; *!* @@ -364,7 +364,7 @@ let visitsCountMap = new WeakMap(); visitsCountMap.set(john, 123); -// now john leaves us, we don't need him any more +// now john leaves us, we don't need him anymore john = null; // there are no references except WeakMap, @@ -408,7 +408,7 @@ The most notable limitation of `WeakMap` and `WeakSet` is the absence of iterati ## Summary -- `Map` -- is a a collection of keyed values. +- `Map` -- is a collection of keyed values. The differences from a regular `Object`: @@ -421,12 +421,12 @@ The most notable limitation of `WeakMap` and `WeakSet` is the absence of iterati - 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 inaccessible by other means. - It does not support operations on the structure as a whole: no `size`, no `clear()`, no iterations. -- `WeakSet` -- is a variant of `Set` that only stores objects and removes them once they become unaccessible by other means. +- `WeakSet` -- is a variant of `Set` that only stores objects and removes them once they become inaccessible by other means. - 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. +`WeakMap` and `WeakSet` are used as "secondary" data structures in addition to the "main" object storage. Once the object is removed from the main storage, so it only stays in `WeakMap/WeakSet`, they clean up automatically. diff --git a/1-js/05-data-types/08-keys-values-entries/article.md b/1-js/05-data-types/08-keys-values-entries/article.md index 083ed9a391..50ba91cef5 100644 --- a/1-js/05-data-types/08-keys-values-entries/article.md +++ b/1-js/05-data-types/08-keys-values-entries/article.md @@ -32,7 +32,7 @@ For plain objects, the following methods are available: The first difference is that we have to call `Object.keys(obj)`, and not `obj.keys()`. -Why so? There main reason is flexibility. Remember, objects are a base of all complex structures in JavaScript. So we may 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. +Why so? The main reason is flexibility. Remember, objects are a base of all complex structures in JavaScript. So we may 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. The second difference is that `Object.*` methods return "real" array objects, not just an iterable. That's mainly for historical reasons. @@ -58,7 +58,7 @@ let user = { }; // loop over values -for(let value of Object.values(user)) { +for (let value of Object.values(user)) { alert(value); // John, then 30 } ``` diff --git a/1-js/05-data-types/09-destructuring-assignment/article.md b/1-js/05-data-types/09-destructuring-assignment/article.md index df553aeb2b..a1fb7d54c1 100644 --- a/1-js/05-data-types/09-destructuring-assignment/article.md +++ b/1-js/05-data-types/09-destructuring-assignment/article.md @@ -2,9 +2,9 @@ The two most used data structures in JavaScript are `Object` and `Array`. -Objects allow to pack many pieces of information into a single entity and arrays allow to store ordered collections. So we can make an object or an array and handle it as a single thing, maybe pass to a function call. +Objects allow us to pack many pieces of information into a single entity and arrays allow us to store ordered collections. So we can make an object or an array and handle it as a single entity, or maybe pass it to a function call. -*Destructuring assignment* is a special syntax that allows to "unpack" arrays or objects into a bunch of variables, as sometimes they are more convenient. Destructuring also works great with complex functions that have a lot of parameters, default values, and soon we'll see how these are handled too. +*Destructuring assignment* is a special syntax that allows us to "unpack" arrays or objects into a bunch of variables, as sometimes they are more convenient. Destructuring also works great with complex functions that have a lot of parameters, default values, and soon we'll see how these are handled too. [cut] @@ -34,9 +34,9 @@ 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. +It'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: +It's just a shorter way to write: ```js // let [firstName, surname] = arr; let firstName = arr[0]; @@ -56,12 +56,12 @@ let [, , title] = ["Julius", "Caesar", "Consul", "of the Roman Republic"]; alert( title ); // Consul ``` -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. +In the code above, although the first and second elements of the array are skipped, the third one is assigned to `title`, and the rest are also skipped. ```` ````smart header="Works with any iterable on the right-side" -...Actually, we can use it with any iterable, not only an array: +...Actually, we can use it with any iterable, not only arrays: ```js let [a, b, c] = "abc"; // ["a", "b", "c"] @@ -87,7 +87,7 @@ alert(user.name); // Ilya ````smart header="Looping with .entries()" -In the previous chapter we saw [Object.entries(obj)](mdn:js/Object/entries) method. +In the previous chapter we saw the [Object.entries(obj)](mdn:js/Object/entries) method. We can use it with destructuring to loop over keys-and-values of an object: @@ -99,7 +99,7 @@ let user = { // loop over keys-and-values *!* -for(let [key, value] of Object.entries(user)) { +for (let [key, value] of Object.entries(user)) { */!* alert(`${key}:${value}`); // name:John, then age:30 } @@ -113,7 +113,7 @@ user.set("name", "John"); user.set("age", "30"); *!* -for(let [key, value] of user.entries()) { +for (let [key, value] of user.entries()) { */!* alert(`${key}:${value}`); // name:John, then age:30 } @@ -121,7 +121,7 @@ for(let [key, value] of user.entries()) { ```` ### 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 `"..."`: +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 three dots `"..."`: ```js run let [name1, name2, *!*...rest*/!*] = ["Julius", "Caesar", *!*"Consul", "of the Roman Republic"*/!*]; @@ -136,11 +136,11 @@ 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`, just make sure it has three dots before it and goes the last. +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 last in the destructuring assignment. ### Default values -If there are fewer values in the array than variables in the assignment -- there will be no error, absent values are considered undefined: +If there are fewer values in the array than variables in the assignment, there will be no error. Absent values are considered undefined: ```js run *!* @@ -155,7 +155,7 @@ If we want a "default" value to replace the missing one, we can provide it using ```js run *!* // default values -let [name="Guest", surname="Anonymous"] = ["Julius"]; +let [name = "Guest", surname = "Anonymous"] = ["Julius"]; */!* alert(name); // Julius (from array) @@ -164,11 +164,11 @@ 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 two defaults. But it will only run only for the missing one: +For instance, here we use the `prompt` function for two defaults. But it will run only for the missing one: ```js run // runs only prompt for surname -let [name=prompt('name?'), surname=prompt('surname?')] = ["Julius"]; +let [name = prompt('name?'), surname = prompt('surname?')] = ["Julius"]; alert(name); // Julius (from array) alert(surname); // whatever prompt gets @@ -186,7 +186,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 contains a "pattern" for corresponding properties. In the simple case that's a list of variable names in `{...}`. +We have an existing object at the right side, that we want to split into variables. The left side contains a "pattern" for corresponding properties. In the simple case, that's a list of variable names in `{...}`. For instance: @@ -206,7 +206,7 @@ alert(width); // 100 alert(height); // 200 ``` -Properties `options.title`, `options.width` and `options.height` are assigned to the corresponding variables. The order does not matter, that works too: +Properties `options.title`, `options.width` and `options.height` are assigned to the corresponding variables. The order does not matter. This works too: ```js // changed the order of properties in let {...} @@ -248,7 +248,7 @@ let options = { }; *!* -let {width=100, height=200, title} = options; +let {width = 100, height = 200, title} = options; */!* alert(title); // Menu @@ -258,7 +258,7 @@ 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. +The code below asks for width, but not the title. ```js run let options = { @@ -266,7 +266,7 @@ let options = { }; *!* -let {width=prompt("width?"), title=prompt("title?")} = options; +let {width = prompt("width?"), title = prompt("title?")} = options; */!* alert(title); // Menu @@ -281,7 +281,7 @@ let options = { }; *!* -let {width:w=100, height:h=200, title} = options; +let {width: w = 100, height: h = 200, title} = options; */!* alert(title); // Menu @@ -391,7 +391,7 @@ Finally, we have `width`, `height`, `item1`, `item2` and `title` from the defaul That often happens with destructuring assignments. We have a complex object with many properties and want to extract only what we need. -Even like this: +Even here it happens: ```js // take size as a whole into a variable, ignore the rest let { size } = options; @@ -409,7 +409,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. +In real-life the 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? @@ -434,7 +434,7 @@ let options = { function showMenu(*!*{title = "Untitled", width = 200, height = 100, items = []}*/!*) { // title, items – taken from options, // width, height – defaults used - alert( title + ' ' + width + ' ' + height ); // My Menu 100 200 + alert( `${title} ${width} ${height}` ); // My Menu 200 100 alert( items ); // Item1, Item2 } @@ -452,12 +452,12 @@ let options = { *!* function showMenu({ title = "Untitled", - width:w = 100, // width goes to w - height:h = 200, // height goes to h + width: w = 100, // width goes to w + height: h = 200, // height goes to h items: [item1, item2] // items first element goes to item1, second to item2 }) { */!* - alert( title + ' ' + w + ' ' + h ); // My Menu 100 200 + alert( `${title} ${w} ${h}` ); // My Menu 100 200 alert( item1 ); // Item1 alert( item2 ); // Item2 } @@ -487,8 +487,8 @@ 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 ); +function showMenu(*!*{ title = "Menu", width = 100, height = 200 } = {}*/!*) { + alert( `${title} ${width} ${height}` ); } showMenu(); // Menu 100 200 @@ -498,7 +498,7 @@ In the code above, the whole arguments object is `{}` by default, so there's alw ## Summary -- Destructuring assignment allows to instantly map an object or array into many variables. +- Destructuring assignment allows for instantly mapping an object or array onto many variables. - The object syntax: ```js let {prop : varName = default, ...} = object diff --git a/1-js/05-data-types/10-date/article.md b/1-js/05-data-types/10-date/article.md index c025aaa447..becd9ac1fe 100644 --- a/1-js/05-data-types/10-date/article.md +++ b/1-js/05-data-types/10-date/article.md @@ -100,7 +100,7 @@ There are also their UTC-counterparts, that return day, month, year and so on fo If your local time zone is shifted relative to UTC, then the code below shows different hours: ```js run -// currend date +// current date let date = new Date(); // the hour in your current time zone @@ -208,7 +208,7 @@ let date = new Date(); alert(+date); // the number of milliseconds, same as date.getTime() ``` -The important side effect: dates can be substracted, the result is their difference in ms. +The important side effect: dates can be subtracted, the result is their difference in ms. That can be used for time measurements: @@ -251,7 +251,7 @@ for (let i = 0; i < 100000; i++) { let end = Date.now(); // done */!* -alert( `The loop took ${end - start} ms` ); // substract numbers, not dates +alert( `The loop took ${end - start} ms` ); // subtract numbers, not dates ``` ## Benchmarking @@ -262,7 +262,7 @@ For instance, let's measure two functions that calculate the difference between ```js // we have date1 and date2, which function faster returns their difference in ms? -function diffSubstract(date1, date2) { +function diffSubtract(date1, date2) { return date2 - date1; } @@ -281,7 +281,7 @@ The first idea may be to run them many times in a row and measure the time diffe Let's measure: ```js run -function diffSubstract(date1, date2) { +function diffSubtract(date1, date2) { return date2 - date1; } @@ -298,7 +298,7 @@ function bench(f) { return Date.now() - start; } -alert( 'Time of diffSubstract: ' + bench(diffSubstract) + 'ms' ); +alert( 'Time of diffSubtract: ' + bench(diffSubtract) + 'ms' ); alert( 'Time of diffGetTime: ' + bench(diffGetTime) + 'ms' ); ``` @@ -306,7 +306,7 @@ Wow! Using `getTime()` is so much faster! That's because there's no type convers Okay, we have something. But that's not a good benchmark yet. -Imagine that at the time of running `bench(diffSubstract)` CPU was doing something in parallel, and it was taking resources. And by the time of running `bench(diffGetTime)` the work has finished. +Imagine that at the time of running `bench(diffSubtract)` CPU was doing something in parallel, and it was taking resources. And by the time of running `bench(diffGetTime)` the work has finished. A pretty real scenario for a modern multi-process OS. @@ -317,7 +317,7 @@ As a result, the first benchmark will have less CPU resources than the second. T Here's the code example: ```js run -function diffSubstract(date1, date2) { +function diffSubtract(date1, date2) { return date2 - date1; } @@ -340,12 +340,12 @@ let time2 = 0; *!* // run bench(upperSlice) and bench(upperLoop) each 10 times alternating for (let i = 0; i < 10; i++) { - time1 += bench(diffSubstract); + time1 += bench(diffSubtract); time2 += bench(diffGetTime); } */!* -alert( 'Total time for diffSubstract: ' + time1 ); +alert( 'Total time for diffSubtract: ' + time1 ); alert( 'Total time for diffGetTime: ' + time2 ); ``` @@ -353,12 +353,12 @@ Modern JavaScript engines start applying advanced optimizations only to "hot cod ```js // added for "heating up" prior to the main loop -bench(diffSubstract); +bench(diffSubtract); bench(diffGetTime); // now benchmark for (let i = 0; i < 10; i++) { - time1 += bench(diffSubstract); + time1 += bench(diffSubtract); time2 += bench(diffGetTime); } ``` @@ -405,8 +405,8 @@ alert(date); - Date and time in JavaScript are represented with the [Date](mdn:js/Date) object. We can't create "only date" or "only time": `Date` objects always carry both. - Months are counted from zero (yes, January is a zero month). - Days of week in `getDay()` are also counted from zero (that's Sunday). -- `Date` auto-corrects itself when out-of-range components are set. Good for adding/substracting days/months/hours. -- Dates can be substracted, giving their difference in milliseconds. That's because a `Date` becomes the timestamp if converted to a number. +- `Date` auto-corrects itself when out-of-range components are set. Good for adding/subtracting days/months/hours. +- Dates can be subtracted, giving their difference in milliseconds. That's because a `Date` becomes the timestamp when converted to a number. - Use `Date.now()` to get the current timestamp fast. Note that unlike many other systems, timestamps in JavaScript are in milliseconds, not in seconds. @@ -417,7 +417,7 @@ Also, sometimes we need more precise time measurements. JavaScript itself does n alert(`Loading started ${performance.now()}ms ago`); // Something like: "Loading started 34731.26000000001ms ago" // .26 is microseconds (260 microseconds) -// more than 3 digits after the decimal point are precision errors, but only 3 first are correct +// more than 3 digits after the decimal point are precision errors, but only the first 3 are correct ``` Node.JS has `microtime` module and other ways. Technically, any device and environment allows to get more precision, it's just not in `Date`. diff --git a/1-js/06-advanced-functions/01-recursion/article.md b/1-js/06-advanced-functions/01-recursion/article.md index 6995cdc4dc..466ce26bd9 100644 --- a/1-js/06-advanced-functions/01-recursion/article.md +++ b/1-js/06-advanced-functions/01-recursion/article.md @@ -31,7 +31,7 @@ There are two ways to implement it. let result = 1; // multiply result by x n times in the loop - for(let i = 0; i < n; i++) { + for (let i = 0; i < n; i++) { result *= x; } @@ -67,8 +67,8 @@ pow(x, n) = else = x * pow(x, n - 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`. +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 until `n` reaches `1`. We can also say that `pow` *recursively calls itself* till `n == 1`. @@ -91,7 +91,7 @@ Here we can rewrite the same using the ternary `?` operator instead of `if` to m ```js run function pow(x, n) { - return (n == 1) ? x : (x * pow(x, n-1)); + return (n == 1) ? x : (x * pow(x, n - 1)); } ``` ```` @@ -134,7 +134,7 @@ We can sketch it as: -That's when the function starts to execute. The condition `n == 1` is falsy, so the flow continues into the second branch of `if`: +That's when the function starts to execute. The condition `n == 1` is false, so the flow continues into the second branch of `if`: ```js run function pow(x, n) { @@ -160,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 `pow(2, 2)`. +To calculate `x * pow(x, n - 1)`, we need to make a subcall of `pow` with new arguments `pow(2, 2)`. ### pow(2, 2) @@ -230,7 +230,7 @@ function pow(x, n) { 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 the top of the stack: +As the function finishes, its execution context is not needed anymore, so it's removed from the memory. The previous one is restored off the top of the stack:
    @@ -244,7 +244,7 @@ As the function finishes, its execution context is not needed any more, so it's
-The execution of `pow(2, 2)` is resumed. It has the result of the subcall `pow(2, 1)`, so it also can finish the evaluation of `x * pow(x, n-1)`, returning `4`. +The execution of `pow(2, 2)` is resumed. It has the result of the subcall `pow(2, 1)`, so it also can finish the evaluation of `x * pow(x, n - 1)`, returning `4`. Then the previous context is restored: @@ -269,7 +269,7 @@ A loop-based algorithm is more memory-saving: function pow(x, n) { let result = 1; - for(let i = 0; i < n; i++) { + for (let i = 0; i < n; i++) { result *= x; } @@ -289,7 +289,7 @@ Recursion can give a shorter code, easier to understand and support. Optimizatio Another great application of the recursion is a recursive traversal. -Imagine, we have an company. The staff structure can be presented as an object: +Imagine, we have a company. The staff structure can be presented as an object: ```js let company = { @@ -360,7 +360,7 @@ function sumSalaries(department) { return department.reduce((prev, current) => prev + current.salary, 0); // sum the array } else { // case (2) let sum = 0; - for(let subdep of Object.values(department)) { + for (let subdep of Object.values(department)) { sum += sumSalaries(subdep); // recursively call for subdepartments, sum the results } return sum; @@ -395,7 +395,7 @@ A company *department* is: - Either an array of people. - Or an object with *departments*. -For web-developers there are much better known examples: HTML and XML documents. +For web-developers there are much better-known examples: HTML and XML documents. In the HTML document, an *HTML-tag* may contain a list of: - Text pieces. diff --git a/1-js/06-advanced-functions/02-rest-parameters-spread-operator/article.md b/1-js/06-advanced-functions/02-rest-parameters-spread-operator/article.md index eaa42ab9f3..0788860e40 100644 --- a/1-js/06-advanced-functions/02-rest-parameters-spread-operator/article.md +++ b/1-js/06-advanced-functions/02-rest-parameters-spread-operator/article.md @@ -1,6 +1,6 @@ # Rest parameters and spread operator -Many JavaScript built-in functions support on arbitrary number of arguments. +Many JavaScript built-in functions support an arbitrary number of arguments. For instance: @@ -35,7 +35,7 @@ For instance, to gather all arguments into array `args`: function sumAll(...args) { // args is the name for the array let sum = 0; - for(let arg of args) sum += arg; + for (let arg of args) sum += arg; return sum; } @@ -207,7 +207,7 @@ 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]`. +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: diff --git a/1-js/06-advanced-functions/03-closure/article.md b/1-js/06-advanced-functions/03-closure/article.md index 015f303230..cdb2942bef 100644 --- a/1-js/06-advanced-functions/03-closure/article.md +++ b/1-js/06-advanced-functions/03-closure/article.md @@ -296,7 +296,7 @@ let counter2 = makeCounter(); alert( counter1() ); // 0 alert( counter1() ); // 1 -alert( counter2() ); // 0 (independant) +alert( counter2() ); // 0 (independent) ``` @@ -358,7 +358,7 @@ Here's what's going on in the `makeCounter` example step-by-step, follow it to m Please note how memory management works here. When `makeCounter()` call finished some time ago, its Lexical Environment was retained in memory, because there's a nested function with `[[Environment]]` referencing it. - Generally, a Lexical Environment object lives until there is a function which may use it. And when there are none, it is cleared. + Generally, a Lexical Environment object lives as long as there is a function which may use it. And when there are none, it is cleared. 6. The call to `counter()` not only returns the value of `count`, but also increases it. Note that the modification is done "in place". The value of `count` is modified exactly in the environment where it was found. @@ -423,7 +423,7 @@ For instance, after `if` finishes, the `alert` below won't see the `user`, hence For a loop, every run has a separate Lexical Environment. If the variable is declared in `for`, then it's also local to that Lexical Environment: ```js run -for(let i = 0; i < 10; i++) { +for (let i = 0; i < 10; i++) { // Each loop has its own Lexical Environment // {i: value} } @@ -567,7 +567,7 @@ Lexical Environment objects that we've been talking about are subjects to same m return function() { alert(value); }; } - // 3 functions in array, every of them links to Lexical Environment + // 3 functions in array, every one of them links to Lexical Environment // from the corresponding f() run // LE LE LE let arr = [f(), f(), f()]; @@ -641,6 +641,6 @@ g(); ```warn header="See ya!" This feature of V8 is good to know. If you are debugging with Chrome/Opera, sooner or later you will meet it. -That is not a bug of debugger, but a special feature of V8. Maybe it will be changed some time. +That is not a bug of debugger, but a special feature of V8. Maybe it will be changed sometime. You always can check for it by running examples on this page. ``` diff --git a/1-js/06-advanced-functions/04-var/article.md b/1-js/06-advanced-functions/04-var/article.md index 9a089b5f54..e36d2d2d08 100644 --- a/1-js/06-advanced-functions/04-var/article.md +++ b/1-js/06-advanced-functions/04-var/article.md @@ -52,7 +52,7 @@ If we used `let test` on the 2nd line, then it wouldn't be visible to `alert`. B The same thing for loops: `var` cannot be block- or loop-local: ```js -for(var i = 0; i < 10; i++) { +for (var i = 0; i < 10; i++) { // ... } @@ -61,7 +61,7 @@ alert(i); // 10, "i" is visible after loop, it's a global variable */!* ``` -If a code block in inside a function, then `var` becomes a function-level variable: +If a code block is inside a function, then `var` becomes a function-level variable: ```js function sayHi() { @@ -76,7 +76,7 @@ sayHi(); alert(phrase); // Error: phrase is not defined ``` -As we can see, `var` pierces through `if`, `for` or other code blocks. That's because long time ago in JavaScript blocks had no Lexical Environments. And `var` is a reminiscence of that. +As we can see, `var` pierces through `if`, `for` or other code blocks. That's because a long time ago in JavaScript blocks had no Lexical Environments. And `var` is a reminiscence of that. ## "var" are processed at the function start @@ -184,4 +184,4 @@ There are two main differences of `var`: There's one more minor difference related to the global object, we'll cover that in the next chapter. -These differences are actually a bad thing most of time. First, we can't create block-local variables. And hoisting just creates more space for errors. So, for new scripts `var` is used exceptionally rarely. +These differences are actually a bad thing most of the time. First, we can't create block-local variables. And hoisting just creates more space for errors. So, for new scripts `var` is used exceptionally rarely. diff --git a/1-js/06-advanced-functions/07-new-function/article.md b/1-js/06-advanced-functions/07-new-function/article.md index b44da63428..738057459a 100644 --- a/1-js/06-advanced-functions/07-new-function/article.md +++ b/1-js/06-advanced-functions/07-new-function/article.md @@ -10,20 +10,22 @@ There's one more way to create a function. It's rarely used, but sometimes there The syntax for creating a function: ```js -let func = new Function('a', 'b', 'return a + b'); +let func = new Function ([arg1[, arg2[, ...argN]],] functionBody) ``` -All arguments of `new Function` are strings. Parameters go first, and the body is the last. +In other words, function parameters (or, more precise, names for them) go first, and the body is the last. All arguments are strings. -For instance: +That's easy to understand by example. + +For instance, here's a function with two arguments: ```js run -let sum = new Function('arg1', 'arg2', 'return arg1 + arg2'); +let sum = new Function('a', 'b', 'return a + b'); alert( sum(1, 2) ); // 3 ``` -If there are no arguments, then there will be only body: +If there are no arguments, then there's only one single argument, function body: ```js run let sayHi = new Function('alert("Hello")'); @@ -136,4 +138,4 @@ new Function('a,b', ' return a + b; '); // comma-separated new Function('a , b', ' return a + b; '); // comma-separated with spaces ``` -Functions created with `new Function`, have `[[Environment]]` referencing the global Lexical Environment, not the outer one. Hence, they can not use outer variables. But that's actually good, because it saves us from errors. Explicit parameters passing is a much better thing architecturally and has no problems with minifiers. \ No newline at end of file +Functions created with `new Function`, have `[[Environment]]` referencing the global Lexical Environment, not the outer one. Hence, they can not use outer variables. But that's actually good, because it saves us from errors. Explicit parameters passing is a much better thing architecturally and has no problems with minifiers. diff --git a/1-js/06-advanced-functions/08-settimeout-setinterval/article.md b/1-js/06-advanced-functions/08-settimeout-setinterval/article.md index ec6459130e..cc347aae6f 100644 --- a/1-js/06-advanced-functions/08-settimeout-setinterval/article.md +++ b/1-js/06-advanced-functions/08-settimeout-setinterval/article.md @@ -158,7 +158,7 @@ The `setTimeout` above schedules next call right at the end of the current one ` Recursive `setTimeout` is more flexible method than `setInterval`. This way the next call may be scheduled differently, depending on the results of the current one. -For instance, we need to write a service that each 5 seconds sends a request to server asking for data, but in case the server is overloaded, it should increase the interval to 10, 20, 40 seconds... +For instance, we need to write a service that every 5 seconds sends a request to server asking for data, but in case the server is overloaded, it should increase the interval to 10, 20, 40 seconds... Here's the pseudocode: ```js @@ -178,7 +178,7 @@ let timerId = setTimeout(function request() { ``` -And if we regulary have CPU-hungry tasks, then we can measure the time taken by the execution and plan the next call sooner or later. +And if we regularly have CPU-hungry tasks, then we can measure the time taken by the execution and plan the next call sooner or later. **Recursive `setTimeout` guarantees a delay between the executions, `setInterval` -- does not.** @@ -209,7 +209,7 @@ Did you notice?... **The real delay between `func` calls for `setInterval` is less than in the code!** -That's natural, because the time taken by `func` execution "consumes" a part of the interval. +That's natural, because the time is taken by `func` execution "consumes" a part of the interval. It is possible that `func` execution turns out to be longer than we expected and takes more than 100ms. @@ -235,7 +235,7 @@ setTimeout(function() {...}, 100); For `setInterval` the function stays in memory until `cancelInterval` is called. -There's a side-effect. A function references the outer lexical environment, so, while it lives, outer variables live too. They may take much more memory than the function itself. So when we don't need the scheduled function any more, it's better to cancel it, even if it's very small. +There's a side-effect. A function references the outer lexical environment, so, while it lives, outer variables live too. They may take much more memory than the function itself. So when we don't need the scheduled function anymore, it's better to cancel it, even if it's very small. ```` ## setTimeout(...,0) @@ -260,7 +260,7 @@ The first line "puts the call into calendar after 0ms". But the scheduler will o There's a trick to split CPU-hungry task using `setTimeout`. -For instance, syntax highlighting script (used to colorize code examples on this page) is quite CPU-heavy. To hightlight the code, it performs the analysis, creates many colored elements, adds them to the document -- for a big text that takes a lot. It may even cause the browser to "hang", that's unacceptable. +For instance, syntax highlighting script (used to colorize code examples on this page) is quite CPU-heavy. To highlight the code, it performs the analysis, creates many colored elements, adds them to the document -- for a big text that takes a lot. It may even cause the browser to "hang", that's unacceptable. So we can split the long text to pieces. First 100 lines, then plan another 100 lines using `setTimeout(...,0)`, and so on. @@ -276,7 +276,7 @@ let start = Date.now(); function count() { // do a heavy job - for(let j = 0; j < 1e9; j++) { + for (let j = 0; j < 1e9; j++) { i++; } @@ -325,7 +325,7 @@ Then the next call is scheduled in `(*)` if we're not done yet. Pauses between `count` executions provide just enough "breath" for the JavaScript engine to do something else, to react on other user actions. -The notable thing is that both variants: with and without splitting the job by `setInterval` -- are comparable in speed. There's no much difference in the overall counting time. +The notable thing is that both variants: with and without splitting the job by `setTimeout` -- are comparable in speed. There's no much difference in the overall counting time. To make them closer let's make an improvement. @@ -401,7 +401,7 @@ Here's the demo: let i = 0; function count() { - for(let j = 0; j < 1e6; j++) { + for (let j = 0; j < 1e6; j++) { i++; // put the current i into the
// (we'll talk more about innerHTML in the specific chapter, should be obvious here) @@ -461,4 +461,4 @@ For example, the in-browser timer may slow down for a lot of reasons: - The browser tab is in the background mode. - The laptop is on battery. -All that may decrease the minimal timer resolution (the minimal delay) to 300ms or even 1000ms depending on the browser and settings. +All that may increase the minimal timer resolution (the minimal delay) to 300ms or even 1000ms depending on the browser and settings. diff --git a/1-js/06-advanced-functions/09-call-apply-decorators/01-spy-decorator/_js.view/test.js b/1-js/06-advanced-functions/09-call-apply-decorators/01-spy-decorator/_js.view/test.js index 7e84f3c99d..5adfcb9787 100644 --- a/1-js/06-advanced-functions/09-call-apply-decorators/01-spy-decorator/_js.view/test.js +++ b/1-js/06-advanced-functions/09-call-apply-decorators/01-spy-decorator/_js.view/test.js @@ -24,7 +24,7 @@ describe("spy", function() { let wrappedSum = spy(sum); assert.equal(wrappedSum(1, 2), 3); - assert(spy.calledWith(1, 2)); + assert(sum.calledWith(1, 2)); }); @@ -36,9 +36,9 @@ describe("spy", function() { calc.wrappedSum = spy(calc.sum); - assert.equal(calculator.wrappedSum(1, 2), 3); - assert(spy.calledWith(1, 2)); - assert(spy.calledOn(calculator)); + assert.equal(calc.wrappedSum(1, 2), 3); + assert(calc.sum.calledWith(1, 2)); + assert(calc.sum.calledOn(calc)); }); -}); \ No newline at end of file +}); diff --git a/1-js/06-advanced-functions/09-call-apply-decorators/01-spy-decorator/solution.md b/1-js/06-advanced-functions/09-call-apply-decorators/01-spy-decorator/solution.md index 4a79d3e118..19a072014b 100644 --- a/1-js/06-advanced-functions/09-call-apply-decorators/01-spy-decorator/solution.md +++ b/1-js/06-advanced-functions/09-call-apply-decorators/01-spy-decorator/solution.md @@ -1 +1 @@ -Here we can use `log.push(args)` to store all arguments in the log and `f.apply(this, args)` to forward the call. +Here we can use `calls.push(args)` to store all arguments in the log and `f.apply(this, args)` to forward the call. diff --git a/1-js/06-advanced-functions/09-call-apply-decorators/01-spy-decorator/task.md b/1-js/06-advanced-functions/09-call-apply-decorators/01-spy-decorator/task.md index e4a30cb83d..9ebb7abfca 100644 --- a/1-js/06-advanced-functions/09-call-apply-decorators/01-spy-decorator/task.md +++ b/1-js/06-advanced-functions/09-call-apply-decorators/01-spy-decorator/task.md @@ -22,7 +22,7 @@ work = spy(work); work(1, 2); // 3 work(4, 5); // 9 -for(let args of work.calls) { +for (let args of work.calls) { alert( 'call:' + args.join() ); // "call:1,2", "call:4,5" } ``` diff --git a/1-js/06-advanced-functions/09-call-apply-decorators/article.md b/1-js/06-advanced-functions/09-call-apply-decorators/article.md index 707bbf5890..01845e484d 100644 --- a/1-js/06-advanced-functions/09-call-apply-decorators/article.md +++ b/1-js/06-advanced-functions/09-call-apply-decorators/article.md @@ -135,7 +135,7 @@ func.call(context, arg1, arg2, ...) It runs `func` providing the first argument as `this`, and the next as the arguments. -To put it simple, these two calls do almost the same: +To put it simply, these two calls do almost the same: ```js func(1, 2, 3); func.call(obj, 1, 2, 3) @@ -154,8 +154,8 @@ let user = { name: "John" }; let admin = { name: "Admin" }; // use call to pass different objects as "this" -sayHi.call( user ); // John -sayHi.call( admin ); // Admin +sayHi.call( user ); // this = John +sayHi.call( admin ); // this = Admin ``` And here we use `call` to call `say` with the given context and phrase: @@ -241,7 +241,7 @@ There are many solutions possible: 1. Implement a new (or use a third-party) map-like data structure that is more versatile and allows multi-keys. 2. Use nested maps: `cache.set(min)` will be a `Map` that stores the pair `(max, result)`. So we can get `result` as `cache.get(min).get(max)`. -3. Join two values into one. In our particular case we can just use a string `"min,max"` as the `Map` key. For flexibility, we can allow to provide a *hashing function* for the decorator, that knows how to make a one value from many. +3. Join two values into one. In our particular case we can just use a string `"min,max"` as the `Map` key. For flexibility, we can allow to provide a *hashing function* for the decorator, that knows how to make one value from many. For many practical applications, the 3rd variant is good enough, so we'll stick to it. @@ -319,7 +319,7 @@ let wrapper = function() { That's called *call forwarding*. The `wrapper` passes everything it gets: the context `this` and arguments to `anotherFunction` and returns back its result. -When an external code calls such `wrapper`, it is undistinguishable from the call of the original function. +When an external code calls such `wrapper`, it is indistinguishable from the call of the original function. Now let's bake it all into the more powerful `cachingDecorator`: @@ -438,7 +438,7 @@ So, technically it takes `this` and joins `this[0]`, `this[1]` ...etc together. *Decorator* is a wrapper around a function that alters its behavior. The main job is still carried out by the function. -It is generally safe to replace a function or a method with a decorated one, except for one little thing. If the original function had properties on it, like `func.calledCount` or whatever, then the decorated one will not provide them. Because that is a wrapper. So one need to be careful if one uses them. Some decorators provide their own properties. +It is generally safe to replace a function or a method with a decorated one, except for one little thing. If the original function had properties on it, like `func.calledCount` or whatever, then the decorated one will not provide them. Because that is a wrapper. So one needs to be careful if one uses them. Some decorators provide their own properties. Decorators can be seen as "features" or "aspects" that can be added to a function. We can add one or add many. And all this without changing its code! diff --git a/1-js/06-advanced-functions/10-bind/article.md b/1-js/06-advanced-functions/10-bind/article.md index 87637929fd..1971819938 100644 --- a/1-js/06-advanced-functions/10-bind/article.md +++ b/1-js/06-advanced-functions/10-bind/article.md @@ -72,7 +72,7 @@ setTimeout(() => user.sayHi(), 1000); // Hello, John! Looks fine, but a slight vulnerability appears in our code structure. -What if before `setTimeout` triggers (there's one second delay!) `user` changes value? Then, suddenly, the it will call the wrong object! +What if before `setTimeout` triggers (there's one second delay!) `user` changes value? Then, suddenly, it will call the wrong object! ```js run diff --git a/1-js/06-advanced-functions/12-arrow-functions/article.md b/1-js/06-advanced-functions/12-arrow-functions/article.md index dcae22a4e6..92a884263a 100644 --- a/1-js/06-advanced-functions/12-arrow-functions/article.md +++ b/1-js/06-advanced-functions/12-arrow-functions/article.md @@ -125,4 +125,4 @@ Arrow functions: - Can't be called with `new`. - (They also don't have `super`, but we didn't study it. Will be in the chapter ). -That's because they are meant for short pieces of code that does not have their own "context", but rather works in the current one. And they really shine in that use case. +That's because they are meant for short pieces of code that do not have their own "context", but rather works in the current one. And they really shine in that use case. diff --git a/1-js/07-object-oriented-programming/01-property-descriptors/article.md b/1-js/07-object-oriented-programming/01-property-descriptors/article.md index e927d5d708..5eadd6f4ce 100644 --- a/1-js/07-object-oriented-programming/01-property-descriptors/article.md +++ b/1-js/07-object-oriented-programming/01-property-descriptors/article.md @@ -15,7 +15,7 @@ Object properties, besides a **`value`**, have three special attributes (so-call - **`enumerable`** -- if `true`, then listed in loops, otherwise not listed. - **`configurable`** -- if `true`, the property can be deleted and these attributes can be modified, otherwise not. -We didn't see them yet, because generally they do not show up. When we create a property "the usual way", all of them are `true`. But we also can change them any time. +We didn't see them yet, because generally they do not show up. When we create a property "the usual way", all of them are `true`. But we also can change them anytime. First, let's see how to get those flags. @@ -156,7 +156,7 @@ let user = { }; // By default, both our properties are listed: -for(let key in user) alert(key); // name, toString +for (let key in user) alert(key); // name, toString ``` If we don't like it, then we can set `enumerable:false`. Then it won't appear in `for..in` loop, just like the built-in one: @@ -178,7 +178,7 @@ Object.defineProperty(user, "toString", { *!* // Now our toString disappears: */!* -for(let key in user) alert(key); // name +for (let key in user) alert(key); // name ``` Non-enumerable properties are also excluded from `Object.keys`: @@ -282,7 +282,7 @@ let clone = Object.defineProperties({}, Object.getOwnPropertyDescriptors(obj)); Normally when we clone an object, we use an assignment to copy properties, like this: ```js -for(let key in user) { +for (let key in user) { clone[key] = user[key] } ``` diff --git a/1-js/07-object-oriented-programming/04-function-prototype/article.md b/1-js/07-object-oriented-programming/04-function-prototype/article.md index e002014fdd..9fc9262e20 100644 --- a/1-js/07-object-oriented-programming/04-function-prototype/article.md +++ b/1-js/07-object-oriented-programming/04-function-prototype/article.md @@ -1,6 +1,6 @@ # F.prototype -In modern JavaScript we can set a prototype using `__proto__`. But it wasn't like that all the time. +In modern JavaScript we can set a prototype using `__proto__`, as described in the previous article. But it wasn't like that all the time. [cut] @@ -10,11 +10,11 @@ But in the old times, there was another (and the only) way to set it: to use a ` ## The "prototype" property -As we know already, `new F()` creates a new object. But what we didn't use yet `F.prototype` property. +As we know already, `new F()` creates a new object. -That property is used by the JavaScript itself to set `[[Prototype]]` for new objects. +When a new object is created with `new F()`, the object's `[[Prototype]]` is set to `F.prototype`. -**When a new object is created with `new F()`, the object's `[[Prototype]]` is set to `F.prototype`.** +In other words, if `F` has a `prototype` property with a value of the object type, then `new` operator uses it to set `[[Prototype]]` for the new object. Please note that `F.prototype` here means a regular property named `"prototype"` on `F`. It sounds something similar to the term "prototype", but here we really mean a regular property with this name. diff --git a/1-js/07-object-oriented-programming/08-class-patterns/article.md b/1-js/07-object-oriented-programming/08-class-patterns/article.md index d548e20dac..b27203d0cb 100644 --- a/1-js/07-object-oriented-programming/08-class-patterns/article.md +++ b/1-js/07-object-oriented-programming/08-class-patterns/article.md @@ -5,7 +5,7 @@ In object-oriented programming, a *class* is an extensible program-code-template for creating objects, providing initial values for state (member variables) and implementations of behavior (member functions or methods). ``` -There's a special syntax construct and a keyword `class` in JavaScript. But before studying it, we should consider that the term "class" comes the theory of object-oriented programming. The definition is cited above, and it's language-independant. +There's a special syntax construct and a keyword `class` in JavaScript. But before studying it, we should consider that the term "class" comes from the theory of object-oriented programming. The definition is cited above, and it's language-independant. In JavaScript there are several well-known programming patterns to make classes even without using the `class` keyword. And here we'll talk about them first. diff --git a/1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-1.png b/1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-1.png index 8df10f5d9a..e63d7d7844 100644 Binary files a/1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-1.png and b/1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-1.png differ diff --git a/1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-1@2x.png b/1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-1@2x.png index b61ddfbd0b..3d1be9ccef 100644 Binary files a/1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-1@2x.png and b/1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-1@2x.png differ diff --git a/1-js/07-object-oriented-programming/09-class/article.md b/1-js/07-object-oriented-programming/09-class/article.md index 717376e87d..549a40e172 100644 --- a/1-js/07-object-oriented-programming/09-class/article.md +++ b/1-js/07-object-oriented-programming/09-class/article.md @@ -43,7 +43,7 @@ let user = new User("John"); user.sayHi(); ``` -It's easy to see that the two examples are alike. Just please note that methods in a class do not have a comma between them. Notice developers sometimes forget it and put a comma between class methods, and things don't work. That's not a literal object, but a class syntax. +It's easy to see that the two examples are alike. Just please note that methods in a class do not have a comma between them. Novice developers sometimes forget it and put a comma between class methods, and things don't work. That's not a literal object, but a class syntax. So, what exactly does `class` do? We may think that it defines a new language-level entity, but that would be wrong. diff --git a/1-js/08-error-handling/2-custom-errors/article.md b/1-js/08-error-handling/2-custom-errors/article.md index d7a780f642..d7d61166dd 100644 --- a/1-js/08-error-handling/2-custom-errors/article.md +++ b/1-js/08-error-handling/2-custom-errors/article.md @@ -224,7 +224,7 @@ The code which calls `readUser` should handle these errors. Right now it uses mu Often the answer is "No": the outer code wants to be "one level above all that". It wants to have some kind of "data reading error". Why exactly it happened -- is often irrelevant (the error message describes it). Or, even better if there is a way to get error details, but only if we need to. -So let's make a new class `ReadError` to represent such errors. If an error occurs inside `readUser`, we'll catch it there and generate `ReadError`. We'll also keep the reference to the original error in the `cause` property. Then the outer code will only have to check for `ReadError`. +So let's make a new class `ReadError` to represent such errors. If an error occurs inside `readUser`, we'll catch it there and generate `ReadError`. We'll also keep the reference to the original error in its `cause` property. Then the outer code will only have to check for `ReadError`. Here's the code that defines `ReadError` and demonstrates its use in `readUser` and `try..catch`: diff --git a/2-ui/1-document/02-dom-nodes/article.md b/2-ui/1-document/02-dom-nodes/article.md index 635986d104..db47859e32 100644 --- a/2-ui/1-document/02-dom-nodes/article.md +++ b/2-ui/1-document/02-dom-nodes/article.md @@ -41,7 +41,7 @@ drawHtmlTree(node1, 'div.domtree', 690, 320); ```online -On the picture above element nodes you can click on element nodes. Their children will open/collapse. +On the picture above, you can click on element nodes and their children will open/collapse. ``` Tags are called *element nodes* (or just elements). Nested tags become children of the enclosing ones. As a result we have a tree of elements: `` is at the root, then `` and `` are its children etc. diff --git a/2-ui/1-document/03-dom-navigation/article.md b/2-ui/1-document/03-dom-navigation/article.md index ab96e5f7ba..b46467c3fa 100644 --- a/2-ui/1-document/03-dom-navigation/article.md +++ b/2-ui/1-document/03-dom-navigation/article.md @@ -157,7 +157,7 @@ The first thing is nice. The second is tolerable, because we can use `Array.from ```warn header="DOM collections are read-only" DOM collections, and even more -- *all* navigation properties listed in this chapter are read-only. -We can't replace an child by something else assigning `childNodes[i] = ...`. +We can't replace a child by something else assigning `childNodes[i] = ...`. Changing DOM needs other methods, we'll see them in the next chapter. ``` diff --git a/2-ui/1-document/09-size-and-scroll/metric-offset-parent.png b/2-ui/1-document/09-size-and-scroll/metric-offset-parent.png index 5f1e58f994..01d2d00087 100644 Binary files a/2-ui/1-document/09-size-and-scroll/metric-offset-parent.png and b/2-ui/1-document/09-size-and-scroll/metric-offset-parent.png differ diff --git a/2-ui/1-document/09-size-and-scroll/metric-offset-parent@2x.png b/2-ui/1-document/09-size-and-scroll/metric-offset-parent@2x.png index af62ebdc78..125a91e153 100644 Binary files a/2-ui/1-document/09-size-and-scroll/metric-offset-parent@2x.png and b/2-ui/1-document/09-size-and-scroll/metric-offset-parent@2x.png differ diff --git a/2-ui/1-document/10-size-and-scroll-window/article.md b/2-ui/1-document/10-size-and-scroll-window/article.md index 0825b75297..5fce2c4bd2 100644 --- a/2-ui/1-document/10-size-and-scroll-window/article.md +++ b/2-ui/1-document/10-size-and-scroll-window/article.md @@ -55,7 +55,7 @@ let scrollHeight = Math.max( document.body.clientHeight, document.documentElement.clientHeight ); -alert('Full document width, with scrolled out part: ' + scrollHeight); +alert('Full document height, with scrolled out part: ' + scrollHeight); ``` Why so? Better don't ask. These inconsistencies come from ancient times, not a "smart" logic. diff --git a/2-ui/2-events/02-bubbling-and-capturing/article.md b/2-ui/2-events/02-bubbling-and-capturing/article.md index 05da72fc42..bc5d70dc1b 100644 --- a/2-ui/2-events/02-bubbling-and-capturing/article.md +++ b/2-ui/2-events/02-bubbling-and-capturing/article.md @@ -5,7 +5,7 @@ Let's start with an example. This handler is assigned to `
`, but also runs if you click any nested tag like `` or ``: ```html autorun height=60 -
+
If you click on EM, the handler on DIV runs.
``` @@ -50,7 +50,7 @@ The process is called "bubbling", because events "bubble" from the inner element ```warn header="*Almost* all events bubble." The key word in this phrase is "almost". -For instance, a `focus` event does not bubble. There are other examples too, we'll meet them. But still it's an exception, rather then a rule, most events do bubble. +For instance, a `focus` event does not bubble. There are other examples too, we'll meet them. But still it's an exception, rather than a rule, most events do bubble. ``` ## event.target diff --git a/2-ui/2-events/02-bubbling-and-capturing/bubble-target.view/script.js b/2-ui/2-events/02-bubbling-and-capturing/bubble-target.view/script.js index d3959a6816..b1353712fa 100644 --- a/2-ui/2-events/02-bubbling-and-capturing/bubble-target.view/script.js +++ b/2-ui/2-events/02-bubbling-and-capturing/bubble-target.view/script.js @@ -2,7 +2,9 @@ form.onclick = function(event) { event.target.style.backgroundColor = 'yellow'; - alert("target = " + event.target.tagName + ", this=" + this.tagName); - - event.target.style.backgroundColor = ''; + // chrome needs some time to paint yellow + setTimeout(() => { + alert("target = " + event.target.tagName + ", this=" + this.tagName); + event.target.style.backgroundColor = '' + }, 0); }; diff --git a/2-ui/3-event-details/8-onscroll/1-endless-page/solution.md b/2-ui/3-event-details/8-onscroll/1-endless-page/solution.md index a3f21b576a..f7412bd4e7 100644 --- a/2-ui/3-event-details/8-onscroll/1-endless-page/solution.md +++ b/2-ui/3-event-details/8-onscroll/1-endless-page/solution.md @@ -18,7 +18,7 @@ For instance, if the height of the whole HTML document is 2000px, then: document.documentElement.getBoundingClientRect().top = 0 // window-relative bottom = 2000 -// the document is long, so that is probably far beyound the window bottom +// the document is long, so that is probably far beyond the window bottom document.documentElement.getBoundingClientRect().bottom = 2000 ``` diff --git a/2-ui/3-event-details/8-onscroll/1-endless-page/task.md b/2-ui/3-event-details/8-onscroll/1-endless-page/task.md index 4b49070b4a..7c8d14fca6 100644 --- a/2-ui/3-event-details/8-onscroll/1-endless-page/task.md +++ b/2-ui/3-event-details/8-onscroll/1-endless-page/task.md @@ -12,7 +12,7 @@ Like this: Please note two important features of the scroll: -1. **The scroll is "elastic".** We can scroll a little beyound the document start or end in some browsers/devices (empty space below is shown, and then the document will automatically "bounces back" to normal). +1. **The scroll is "elastic".** We can scroll a little beyond the document start or end in some browsers/devices (empty space below is shown, and then the document will automatically "bounces back" to normal). 2. **The scroll is imprecise.** When we scroll to page end, then we may be in fact like 0-50px away from the real document bottom. So, "scrolling to the end" should mean that the visitor is no more than 100px away from the document end. diff --git a/2-ui/4-forms-controls/2-focus-blur/article.md b/2-ui/4-forms-controls/2-focus-blur/article.md index d829fb36d1..09478a297a 100644 --- a/2-ui/4-forms-controls/2-focus-blur/article.md +++ b/2-ui/4-forms-controls/2-focus-blur/article.md @@ -4,9 +4,9 @@ An element receives a focus when the user either clicks on it or uses the `key:T Focusing generally means: "prepare to accept the data here", so that's the moment when we can run the code to initialize or load something. -The moment of loosing the focus ("blur") can be even more important. That's when a user clicks somewhere else or presses `key:Tab` to go to the next form field, or there are other means as well. +The moment of losing the focus ("blur") can be even more important. That's when a user clicks somewhere else or presses `key:Tab` to go to the next form field, or there are other means as well. -Loosing the focus generally means: "the data has been entered", so we can run the code to check it or even to save it to the server and so on. +Losing the focus generally means: "the data has been entered", so we can run the code to check it or even to save it to the server and so on. There are important peculiarities when working with focus events. We'll do the best to cover them here. @@ -14,7 +14,7 @@ There are important peculiarities when working with focus events. We'll do the b ## Events focus/blur -The `focus` event is called on focusing, and `blur` -- when the element looses the focus. +The `focus` event is called on focusing, and `blur` -- when the element loses the focus. Let's use them for validation of an input field. @@ -68,6 +68,7 @@ For instance, let's make the visitor unable to leave the input if the value is i Your email please: +