diff --git a/1-js/01-getting-started/4-devtools/article.md b/1-js/01-getting-started/4-devtools/article.md index b83fbcb94..90405d7ee 100644 --- a/1-js/01-getting-started/4-devtools/article.md +++ b/1-js/01-getting-started/4-devtools/article.md @@ -29,10 +29,19 @@ Konkretus vaizdas priklauso nuo Chrome versijos, kurią naudoji. Kartais atsiran - Čia mes galim pamatyt raudoną error message. Šiuo atveju skriptas turi nežinomą "lalala" komandą. - Dešinėje yra clickable link'as į `bug.html:12` su skaičiumi eilutės, kurioje yra error'as. +<<<<<<< HEAD Žemiau error message'o yra mėlynas `>` simbolis. Jis parodo "command line", kuriame mes galime rašyti JavaScript komandas. Spausk `key:Enter` kad jas paleisti (`key:Shift+Enter` kad rašyti komandas per daugiau nei vieną eilutę). +======= +Below the error message, there is a blue `>` symbol. It marks a "command line" where we can type JavaScript commands. Press `key:Enter` to run them. +>>>>>>> a4a84083a7656f2b25de8b766b2457d3aae17874 Dabar mes galime matyti error'us. Kaip pradžiai, to pakanka. Vėliau mes grįšim į developer tools'us ir kalbėsim apie debugginimą chapteryje . +```smart header="Multi-line input" +Usually, when we put a line of code into the console, and then press `key:Enter`, it executes. + +To insert multiple lines, press `key:Shift+Enter`. This way one can enter long fragments of JavaScript code. +``` ## Firefox, Edge ir kiti @@ -50,6 +59,7 @@ Atidaryk Preferences ir eik į "Advanced". Apačioj bus checkbox'as: Dabar `key:Cmd+Opt+C` įjungs konsolę. Taip pat turėk omeny, kad naujas item'as "Develop" atsirado viršutiniam meniu. Jame yra daug komandų ir nustatymų. +<<<<<<< HEAD ```smart header="Kelių eilučių komandos" Dažniausiai, jeigu konsolėje parašome vieną eilutę kodo ir paspaudžiame `key:Enter`, ji suveikia. @@ -57,6 +67,9 @@ Tam, kad parašyti kelias eilutes, spausk `key:Shift+Enter`. Tokiu būdu mes gal ``` ## Reziumė +======= +## Summary +>>>>>>> a4a84083a7656f2b25de8b766b2457d3aae17874 - Developer tools'ai leidžia mum pamatyti errorus, paleisti komandas, analizuoti kintamuosiuos ir daugiau. - Juos paleisti galime su `key:F12` dauguma naršklių Windows'uose. Chrome, jeigu naudojame Mac, reikaluaja `key:Cmd+Opt+J`, Safari: `key:Cmd+Opt+C` (iš pradžių reikia aktyvuoti). diff --git a/1-js/02-first-steps/03-strict-mode/article.md b/1-js/02-first-steps/03-strict-mode/article.md index 573d76bc5..a96432002 100644 --- a/1-js/02-first-steps/03-strict-mode/article.md +++ b/1-js/02-first-steps/03-strict-mode/article.md @@ -42,7 +42,7 @@ Only comments may appear above `"use strict"`. ```warn header="There's no way to cancel `use strict`" There is no directive like `"no use strict"` that reverts the engine to old behavior. -Once we enter strict mode, there's no return. +Once we enter strict mode, there's no going back. ``` ## Browser console diff --git a/1-js/02-first-steps/05-types/article.md b/1-js/02-first-steps/05-types/article.md index 0da617373..cce9267db 100644 --- a/1-js/02-first-steps/05-types/article.md +++ b/1-js/02-first-steps/05-types/article.md @@ -10,9 +10,9 @@ message = 123456; Programming languages that allow such things are called "dynamically typed", meaning that there are data types, but variables are not bound to any of them. -There are seven basic data types in JavaScript. Here, we'll cover them in general and in the next chapters we'll talk about each of them in detail. +There are eight basic data types in JavaScript. Here, we'll cover them in general and in the next chapters we'll talk about each of them in detail. -## A number +## Number ```js let n = 123; @@ -62,14 +62,33 @@ Special numeric values formally belong to the "number" type. Of course they are We'll see more about working with numbers in the chapter . -## A string +## BigInt + +In JavaScript, the "number" type cannot represent integer values larger than 253 (or less than -253 for negatives), that's a technical limitation caused by their internal representation. That's about 16 decimal digits, so for most purposes the limitation isn't a problem, but sometimes we need really big numbers, e.g. for cryptography or microsecond-precision timestamps. + +`BigInt` type was recently added to the language to represent integers of arbitrary length. + +A `BigInt` is created by appending `n` to the end of an integer literal: + +```js +// the "n" at the end means it's a BigInt +const bigInt = 1234567890123456789012345678901234567890n; +``` + +As `BigInt` numbers are rarely needed, we devoted them a separate chapter . + +```smart header="Compatability issues" +Right now `BigInt` is supported in Firefox and Chrome, but not in Safari/IE/Edge. +``` + +## String A string in JavaScript must be surrounded by quotes. ```js let str = "Hello"; let str2 = 'Single quotes are ok too'; -let phrase = `can embed ${str}`; +let phrase = `can embed another ${str}`; ``` In JavaScript, there are 3 types of quotes. @@ -78,7 +97,7 @@ In JavaScript, there are 3 types of quotes. 2. Single quotes: `'Hello'`. 3. Backticks: `Hello`. -Double and single quotes are "simple" quotes. There's no difference between them in JavaScript. +Double and single quotes are "simple" quotes. There's practically no difference between them in JavaScript. Backticks are "extended functionality" quotes. They allow us to embed variables and expressions into a string by wrapping them in `${…}`, for example: @@ -102,12 +121,12 @@ alert( "the result is ${1 + 2}" ); // the result is ${1 + 2} (double quotes do n We'll cover strings more thoroughly in the chapter . ```smart header="There is no *character* type." -In some languages, there is a special "character" type for a single character. For example, in the C language and in Java it is `char`. +In some languages, there is a special "character" type for a single character. For example, in the C language and in Java it is called "char". In JavaScript, there is no such type. There's only one type: `string`. A string may consist of only one character or many of them. ``` -## A boolean (logical type) +## Boolean (logical type) The boolean type has only two values: `true` and `false`. @@ -198,6 +217,8 @@ typeof undefined // "undefined" typeof 0 // "number" +typeof 10n // "bigint" + typeof true // "boolean" typeof "foo" // "string" @@ -223,12 +244,12 @@ The last three lines may need additional explanation: 2. The result of `typeof null` is `"object"`. That's wrong. It is an officially recognized error in `typeof`, kept for compatibility. Of course, `null` is not an object. It is a special value with a separate type of its own. So, again, this is an error in the language. 3. The result of `typeof alert` is `"function"`, because `alert` is a function. We'll study functions in the next chapters where we'll also see that there's no special "function" type in JavaScript. Functions belong to the object type. But `typeof` treats them differently, returning `"function"`. That's not quite correct, but very convenient in practice. - ## Summary -There are 7 basic data types in JavaScript. +There are 8 basic data types in JavaScript. -- `number` for numbers of any kind: integer or floating-point. +- `number` for numbers of any kind: integer or floating-point, integers are limited by ±253. +- `bigint` is for integer numbers of arbitrary length. - `string` for strings. A string may have one or more characters, there's no separate single-character type. - `boolean` for `true`/`false`. - `null` for unknown values -- a standalone type that has a single value `null`. diff --git a/1-js/02-first-steps/06-type-conversions/article.md b/1-js/02-first-steps/06-type-conversions/article.md index 20d093ea4..e7c381252 100644 --- a/1-js/02-first-steps/06-type-conversions/article.md +++ b/1-js/02-first-steps/06-type-conversions/article.md @@ -1,6 +1,6 @@ # Type Conversions -Most of the time, operators and functions automatically convert the values given to them to the right type. +Most of the time, operators and functions automatically convert the values given to them to the right type. For example, `alert` automatically converts any value to a string to show it. Mathematical operations convert values to numbers. @@ -81,18 +81,7 @@ alert( Number(false) ); // 0 Please note that `null` and `undefined` behave differently here: `null` becomes zero while `undefined` becomes `NaN`. -````smart header="Addition '+' concatenates strings" -Almost all mathematical operations convert values to numbers. A notable exception is addition `+`. If one of the added values is a string, the other one is also converted to a string. - -Then, it concatenates (joins) them: - -```js run -alert( 1 + '2' ); // '12' (string to the right) -alert( '1' + 2 ); // '12' (string to the left) -``` - -This only happens when at least one of the arguments is a string. Otherwise, values are converted to numbers. -```` +Most mathematical operators also perform such conversion, we'll see that in the next chapter. ## Boolean Conversion diff --git a/1-js/02-first-steps/06-type-conversions/1-primitive-conversions-questions/solution.md b/1-js/02-first-steps/07-operators/3-primitive-conversions-questions/solution.md similarity index 100% rename from 1-js/02-first-steps/06-type-conversions/1-primitive-conversions-questions/solution.md rename to 1-js/02-first-steps/07-operators/3-primitive-conversions-questions/solution.md diff --git a/1-js/02-first-steps/06-type-conversions/1-primitive-conversions-questions/task.md b/1-js/02-first-steps/07-operators/3-primitive-conversions-questions/task.md similarity index 100% rename from 1-js/02-first-steps/06-type-conversions/1-primitive-conversions-questions/task.md rename to 1-js/02-first-steps/07-operators/3-primitive-conversions-questions/task.md diff --git a/1-js/02-first-steps/08-comparison/1-comparison-questions/solution.md b/1-js/02-first-steps/08-comparison/1-comparison-questions/solution.md index 6437b512e..a86a9f73e 100644 --- a/1-js/02-first-steps/08-comparison/1-comparison-questions/solution.md +++ b/1-js/02-first-steps/08-comparison/1-comparison-questions/solution.md @@ -13,7 +13,7 @@ null === +"\n0\n" → false Some of the reasons: 1. Obviously, true. -2. Dictionary comparison, hence false. +2. Dictionary comparison, hence false. `"a"` is smaller than `"p"`. 3. Again, dictionary comparison, first char of `"2"` is greater than the first char of `"1"`. 4. Values `null` and `undefined` equal each other only. 5. Strict equality is strict. Different types from both sides lead to false. diff --git a/1-js/02-first-steps/11-logical-operators/9-check-login/ifelse_task.svg b/1-js/02-first-steps/11-logical-operators/9-check-login/ifelse_task.svg index cbc8c7840..ca3e0aead 100644 --- a/1-js/02-first-steps/11-logical-operators/9-check-login/ifelse_task.svg +++ b/1-js/02-first-steps/11-logical-operators/9-check-login/ifelse_task.svg @@ -1 +1 @@ -BeginCanceledCanceledWelcome!I don't know youWrong passwordWho's there?Password?CancelCancelAdminTheMasterOtherOther \ No newline at end of file +BeginCanceledCanceledWelcome!I don't know youWrong passwordWho's there?Password?CancelCancelAdminTheMasterOtherOther \ No newline at end of file diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-open-sources.svg b/1-js/03-code-quality/01-debugging-chrome/chrome-open-sources.svg index 6f729fd3a..1f7d21288 100644 --- a/1-js/03-code-quality/01-debugging-chrome/chrome-open-sources.svg +++ b/1-js/03-code-quality/01-debugging-chrome/chrome-open-sources.svg @@ -1 +1 @@ -open sources \ No newline at end of file +open sources \ No newline at end of file diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-breakpoint.svg b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-breakpoint.svg index e9b28655f..6fb4332f1 100644 --- a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-breakpoint.svg +++ b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-breakpoint.svg @@ -1 +1 @@ -here's the listbreakpoints \ No newline at end of file +here's the listbreakpoints \ No newline at end of file diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-trace-1.svg b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-trace-1.svg index 61f08d25c..0d5bde9c4 100644 --- a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-trace-1.svg +++ b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-trace-1.svg @@ -1 +1 @@ -nested calls \ No newline at end of file +nested calls \ No newline at end of file diff --git a/1-js/03-code-quality/02-coding-style/code-style.svg b/1-js/03-code-quality/02-coding-style/code-style.svg index e8e374048..bd62691c6 100644 --- a/1-js/03-code-quality/02-coding-style/code-style.svg +++ b/1-js/03-code-quality/02-coding-style/code-style.svg @@ -1 +1 @@ -2No space between the function name and parentheses between the parentheses and the parameterIndentation 2 spacesA space after for/if/while…} else { without a line breakSpaces around a nested callAn empty line between logical blocksLines are not very longA semicolon ; is mandatorySpaces around operatorsCurly brace { on the same line, after a spaceA space between parametersA space between parameters \ No newline at end of file +2No space between the function name and parentheses between the parentheses and the parameterIndentation 2 spacesA space after for/if/while…} else { without a line breakSpaces around a nested callAn empty line between logical blocksLines are not very longA semicolon ; is mandatorySpaces around operatorsCurly brace { on the same line, after a spaceA space between parametersA space between parameters \ No newline at end of file diff --git a/1-js/03-code-quality/05-testing-mocha/article.md b/1-js/03-code-quality/05-testing-mocha/article.md index e9b5e96c1..68ffcae4d 100644 --- a/1-js/03-code-quality/05-testing-mocha/article.md +++ b/1-js/03-code-quality/05-testing-mocha/article.md @@ -159,8 +159,8 @@ We can select one of two ways to organize the test here: assert.equal(pow(2, 3), 8); }); - it("3 raised to power 3 is 27", function() { - assert.equal(pow(3, 3), 27); + it("3 raised to power 4 is 81", function() { + assert.equal(pow(3, 4), 81); }); }); @@ -182,7 +182,7 @@ The result: [iframe height=250 src="pow-2" edit border="1"] -As we could expect, the second test failed. Sure, our function always returns `8`, while the `assert` expects `27`. +As we could expect, the second test failed. Sure, our function always returns `8`, while the `assert` expects `81`. ## Improving the implementation diff --git a/1-js/03-code-quality/05-testing-mocha/pow-2.view/test.js b/1-js/03-code-quality/05-testing-mocha/pow-2.view/test.js index 9a2f8fde7..c803f0e61 100644 --- a/1-js/03-code-quality/05-testing-mocha/pow-2.view/test.js +++ b/1-js/03-code-quality/05-testing-mocha/pow-2.view/test.js @@ -4,8 +4,8 @@ describe("pow", function() { assert.equal(pow(2, 3), 8); }); - it("3 raised to power 3 is 27", function() { - assert.equal(pow(3, 3), 27); + it("3 raised to power 4 is 81", function() { + assert.equal(pow(3, 4), 81); }); }); diff --git a/1-js/03-code-quality/06-polyfills/article.md b/1-js/03-code-quality/06-polyfills/article.md index b399fd428..75db49d2f 100644 --- a/1-js/03-code-quality/06-polyfills/article.md +++ b/1-js/03-code-quality/06-polyfills/article.md @@ -19,7 +19,7 @@ Here Babel comes to the rescue. Actually, there are two parts in Babel: -1. First, the transpiler program, which rewrites the code. The developer runs it on their own computer. It rewrites the code into the older standard. And then the code is delivered to the website for users. Modern project build systems like [webpack](http://webpack.github.io/) provide means to run transpiler automatically on every code change, so that very easy to integrate into development process. +1. First, the transpiler program, which rewrites the code. The developer runs it on their own computer. It rewrites the code into the older standard. And then the code is delivered to the website for users. Modern project build systems like [webpack](http://webpack.github.io/) provide means to run transpiler automatically on every code change, so that it's very easy to integrate into development process. 2. Second, the polyfill. diff --git a/1-js/04-object-basics/02-garbage-collection/family-delete-refs.svg b/1-js/04-object-basics/02-garbage-collection/family-delete-refs.svg index 133b3908d..2ae1f664c 100644 --- a/1-js/04-object-basics/02-garbage-collection/family-delete-refs.svg +++ b/1-js/04-object-basics/02-garbage-collection/family-delete-refs.svg @@ -1 +1 @@ -<global variable>ObjectObjectwifefamilyname: "John"name: "Ann"motherObjectfatherhusband \ No newline at end of file +<global variable>ObjectObjectwifefamilyname: "John"name: "Ann"motherObjectfatherhusband \ No newline at end of file diff --git a/1-js/04-object-basics/02-garbage-collection/family-no-family.svg b/1-js/04-object-basics/02-garbage-collection/family-no-family.svg index 8ba3b9f9f..655d1982e 100644 --- a/1-js/04-object-basics/02-garbage-collection/family-no-family.svg +++ b/1-js/04-object-basics/02-garbage-collection/family-no-family.svg @@ -1 +1 @@ -<global>ObjectObjectfatherwifename: "John"name: "Ann"motherObjecthusbandfamily: null \ No newline at end of file +<global>ObjectObjectfatherwifename: "John"name: "Ann"motherObjecthusbandfamily: null \ No newline at end of file diff --git a/1-js/04-object-basics/02-garbage-collection/family-no-father.svg b/1-js/04-object-basics/02-garbage-collection/family-no-father.svg index 59f0b2afc..b76c868e0 100644 --- a/1-js/04-object-basics/02-garbage-collection/family-no-father.svg +++ b/1-js/04-object-basics/02-garbage-collection/family-no-father.svg @@ -1 +1 @@ -ObjectObjectwifefamilyname: "John"name: "Ann"motherObject<global> \ No newline at end of file +ObjectObjectwifefamilyname: "John"name: "Ann"motherObject<global> \ No newline at end of file diff --git a/1-js/04-object-basics/02-garbage-collection/family.svg b/1-js/04-object-basics/02-garbage-collection/family.svg index 8fbbc35ea..bec2f4ddc 100644 --- a/1-js/04-object-basics/02-garbage-collection/family.svg +++ b/1-js/04-object-basics/02-garbage-collection/family.svg @@ -1 +1 @@ -ObjectObjectfatherwifefamilyname: "John"name: "Ann"motherObjecthusband<global variable> \ No newline at end of file +ObjectObjectfatherwifefamilyname: "John"name: "Ann"motherObjecthusband<global variable> \ No newline at end of file diff --git a/1-js/04-object-basics/02-garbage-collection/garbage-collection-1.svg b/1-js/04-object-basics/02-garbage-collection/garbage-collection-1.svg index 125031ee4..2563c8185 100644 --- a/1-js/04-object-basics/02-garbage-collection/garbage-collection-1.svg +++ b/1-js/04-object-basics/02-garbage-collection/garbage-collection-1.svg @@ -1 +1 @@ -<global> \ No newline at end of file +<global> \ No newline at end of file diff --git a/1-js/04-object-basics/02-garbage-collection/garbage-collection-2.svg b/1-js/04-object-basics/02-garbage-collection/garbage-collection-2.svg index b4f1a0870..acd5025e9 100644 --- a/1-js/04-object-basics/02-garbage-collection/garbage-collection-2.svg +++ b/1-js/04-object-basics/02-garbage-collection/garbage-collection-2.svg @@ -1 +1 @@ -<global> \ No newline at end of file +<global> \ No newline at end of file diff --git a/1-js/04-object-basics/02-garbage-collection/garbage-collection-3.svg b/1-js/04-object-basics/02-garbage-collection/garbage-collection-3.svg index 7477c4ba7..4421ec784 100644 --- a/1-js/04-object-basics/02-garbage-collection/garbage-collection-3.svg +++ b/1-js/04-object-basics/02-garbage-collection/garbage-collection-3.svg @@ -1 +1 @@ -<global> \ No newline at end of file +<global> \ No newline at end of file diff --git a/1-js/04-object-basics/02-garbage-collection/garbage-collection-4.svg b/1-js/04-object-basics/02-garbage-collection/garbage-collection-4.svg index 6b13c1dc0..74adc8135 100644 --- a/1-js/04-object-basics/02-garbage-collection/garbage-collection-4.svg +++ b/1-js/04-object-basics/02-garbage-collection/garbage-collection-4.svg @@ -1 +1 @@ -<global> \ No newline at end of file +<global> \ No newline at end of file diff --git a/1-js/04-object-basics/02-garbage-collection/garbage-collection-5.svg b/1-js/04-object-basics/02-garbage-collection/garbage-collection-5.svg index 5cb54dc28..abb127ab2 100644 --- a/1-js/04-object-basics/02-garbage-collection/garbage-collection-5.svg +++ b/1-js/04-object-basics/02-garbage-collection/garbage-collection-5.svg @@ -1 +1 @@ -<global>unreachables \ No newline at end of file +<global>unreachables \ No newline at end of file diff --git a/1-js/04-object-basics/04-object-methods/4-object-property-this/solution.md b/1-js/04-object-basics/04-object-methods/4-object-property-this/solution.md index ea00c970d..c1aaf4f97 100644 --- a/1-js/04-object-basics/04-object-methods/4-object-property-this/solution.md +++ b/1-js/04-object-basics/04-object-methods/4-object-property-this/solution.md @@ -22,6 +22,17 @@ The value of `this` is one for the whole function, code blocks and object litera So `ref: this` actually takes current `this` of the function. +We can rewrite the function and return the same `this` with `undefined` value: + +```js run +function makeUser(){ + return this; // this time there's no object literal +} + +alert( makeUser().name ); // Error: Cannot read property 'name' of undefined +``` +As you can see the result of `alert( makeUser().name )` is the same as the result of `alert( user.ref.name )` from the previous example. + Here's the opposite case: ```js run 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 06138a004..6c13acda6 100644 --- a/1-js/05-data-types/01-primitives-methods/article.md +++ b/1-js/05-data-types/01-primitives-methods/article.md @@ -7,7 +7,7 @@ Let's look at the key distinctions between primitives and objects. A primitive - Is a value of a primitive type. -- There are 6 primitive types: `string`, `number`, `boolean`, `symbol`, `null` and `undefined`. +- There are 7 primitive types: `string`, `number`, `bigint`, `boolean`, `symbol`, `null` and `undefined`. An object diff --git a/1-js/05-data-types/02-number/article.md b/1-js/05-data-types/02-number/article.md index f9ffdd81b..d167b836e 100644 --- a/1-js/05-data-types/02-number/article.md +++ b/1-js/05-data-types/02-number/article.md @@ -1,8 +1,12 @@ # Numbers -All numbers in JavaScript are stored in 64-bit format [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754-2008_revision), also known as "double precision floating point numbers". +In modern JavaScript, there are two types of numbers: -Let's expand upon what we currently know about them. +1. Regular numbers in JavaScript are stored in 64-bit format [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754-2008_revision), also known as "double precision floating point numbers". These are numbers that we're using most of the time, and we'll talk about them in this chapter. + +2. BigInt numbers, to represent integers of arbitrary length. They are sometimes needed, because a regular number can't exceed 253 or be less than -253. As bigints are used in few special areas, we devote them a special chapter . + +So here we'll talk about regular numbers. Let's expand our knowledge of them. ## More ways to write a number @@ -29,14 +33,13 @@ In other words, `"e"` multiplies the number by `1` with the given zeroes count. 1.23e6 = 1.23 * 1000000 ``` - Now let's write something very small. Say, 1 microsecond (one millionth of a second): ```js let ms = 0.000001; ``` -Just like before, using `"e"` can help. If we'd like to avoid writing the zeroes explicitly, we could say: +Just like before, using `"e"` can help. If we'd like to avoid writing the zeroes explicitly, we could say the same as: ```js let ms = 1e-6; // six zeroes to the left from 1 @@ -271,13 +274,11 @@ 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 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. +That's because a sign is represented by a single bit, so it can be set or not set for any number including a zero. In most cases the distinction is unnoticeable, because operators are suited to treat them as the same. ``` - - ## Tests: isFinite and isNaN Remember these two special numeric values? @@ -409,10 +410,10 @@ There are more functions and constants in `Math` object, including trigonometry, ## Summary -To write big numbers: +To write numbers with many zeroes: -- Append `"e"` with the zeroes count to the number. Like: `123e6` is `123` with 6 zeroes. -- A negative number after `"e"` causes the number to be divided by 1 with given zeroes. That's for one-millionth or such. +- Append `"e"` with the zeroes count to the number. Like: `123e6` is the same as `123` with 6 zeroes `123000000`. +- A negative number after `"e"` causes the number to be divided by 1 with given zeroes. E.g. `123e-6` means `0.000123` (`123` millionths). For different numeral systems: diff --git a/1-js/05-data-types/05-array-methods/12-reduce-object/_js.view/solution.js b/1-js/05-data-types/05-array-methods/12-reduce-object/_js.view/solution.js new file mode 100644 index 000000000..8dea23a06 --- /dev/null +++ b/1-js/05-data-types/05-array-methods/12-reduce-object/_js.view/solution.js @@ -0,0 +1,6 @@ +function groupById(array) { + return array.reduce((obj, value) => { + obj[value.id] = value; + return obj; + }, {}) +} diff --git a/1-js/05-data-types/05-array-methods/12-reduce-object/_js.view/test.js b/1-js/05-data-types/05-array-methods/12-reduce-object/_js.view/test.js new file mode 100644 index 000000000..02299e307 --- /dev/null +++ b/1-js/05-data-types/05-array-methods/12-reduce-object/_js.view/test.js @@ -0,0 +1,20 @@ +describe("groupById", function() { + + it("creates an object grouped by id", function() { + let users = [ + {id: 'john', name: "John Smith", age: 20}, + {id: 'ann', name: "Ann Smith", age: 24}, + {id: 'pete', name: "Pete Peterson", age: 31}, + ]; + + assert.deepEqual(groupById(users), { + john: {id: 'john', name: "John Smith", age: 20} + ann: {id: 'ann', name: "Ann Smith", age: 24}, + pete: {id: 'pete', name: "Pete Peterson", age: 31}, + }); + }); + + it("works with an empty array", function() { + assert.deepEqual(groupById(users), {}); + }); +}); diff --git a/1-js/05-data-types/05-array-methods/12-reduce-object/solution.md b/1-js/05-data-types/05-array-methods/12-reduce-object/solution.md new file mode 100644 index 000000000..e69de29bb diff --git a/1-js/05-data-types/05-array-methods/12-reduce-object/task.md b/1-js/05-data-types/05-array-methods/12-reduce-object/task.md new file mode 100644 index 000000000..421f8cb8b --- /dev/null +++ b/1-js/05-data-types/05-array-methods/12-reduce-object/task.md @@ -0,0 +1,37 @@ +importance: 4 + +--- + +# Create keyed object from array + +Let's say we received an array of users in the form `{id:..., name:..., age... }`. + +Create a function `groupById(arr)` that creates an object from it, with `id` as the key, and array items as values. + +For example: + +```js +let users = [ + {id: 'john', name: "John Smith", age: 20}, + {id: 'ann', name: "Ann Smith", age: 24}, + {id: 'pete', name: "Pete Peterson", age: 31}, +]; + +let usersById = groupById(users); + +/* +// after the call we have: + +usersById = { + john: {id: 'john', name: "John Smith", age: 20} + ann: {id: 'ann', name: "Ann Smith", age: 24}, + pete: {id: 'pete', name: "Pete Peterson", age: 31}, +} +*/ +``` + +Such function is really handy when working with server data. + +In this task we assume that `id` is unique. There may be no two array items with the same `id`. + +Please use array `.reduce` method in the solution. 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 5a1a7762a..301696440 100644 --- a/1-js/05-data-types/05-array-methods/article.md +++ b/1-js/05-data-types/05-array-methods/article.md @@ -384,7 +384,7 @@ The order became `1, 15, 2`. Incorrect. But why? **The items are sorted as strings by default.** -Literally, all elements are converted to strings for comparisons. For strings, lexicographic ordering is applied and indeed `"2" > "15"`. +Literally, all elements are converted to strings for comparisons. For strings, lexicographic ordering is applied and indeed `"2" > "15"`. To use our own sorting order, we need to supply a function as the argument of `arr.sort()`. @@ -431,7 +431,6 @@ By the way, if we ever want to know which elements are compared -- nothing preve The algorithm may compare an element with multiple others in the process, but it tries to make as few comparisons as possible. - ````smart header="A comparison function may return any number" Actually, a comparison function is only required to return a positive number to say "greater" and a negative number to say "less". @@ -456,6 +455,22 @@ arr.sort( (a, b) => a - b ); This works exactly the same as the longer version above. ```` +````smart header="Use `localeCompare` for strings" +Remember [strings](info:string#correct-comparisons) comparison algorithm? It compares letters by their codes by default. + +For many alphabets, it's better to use `str.localeCompare` method to correctly sort letters, such as `Ö`. + +For example, let's sort a few countries in German: + +```js run +let countries = ['Österreich', 'Andorra', 'Vietnam']; + +alert( countries.sort( (a, b) => a > b ? 1 : -1) ); // Andorra, Vietnam, Österreich (wrong) + +alert( countries.sort( (a, b) => a.localeCompare(b) ) ); // Andorra,Österreich,Vietnam (correct!) +``` +```` + ### reverse The method [arr.reverse](mdn:js/Array/reverse) reverses the order of elements in `arr`. @@ -530,7 +545,7 @@ The methods [arr.reduce](mdn:js/Array/reduce) and [arr.reduceRight](mdn:js/Array The syntax is: ```js -let value = arr.reduce(function(previousValue, item, index, array) { +let value = arr.reduce(function(accumulator, item, index, array) { // ... }, [initial]); ``` @@ -539,14 +554,16 @@ The function is applied to all array elements one after another and "carries on" Arguments: -- `previousValue` -- is the result of the previous function call, equals `initial` the first time (if `initial` is provided). +- `accumulator` -- is the result of the previous function call, equals `initial` the first time (if `initial` is provided). - `item` -- is the current array item. - `index` -- is its position. - `array` -- is the array. As function is applied, the result of the previous function call is passed to the next one as the first argument. -Sounds complicated, but it's not if you think about the first argument as the "accumulator" that stores the combined result of all previous execution. And at the end it becomes the result of `reduce`. +So, the first argument is essentially the accumulator that stores the combined result of all previous executions. And at the end it becomes the result of `reduce`. + +Sounds complicated? The easiest way to grasp that is by example. @@ -611,7 +628,6 @@ let arr = []; arr.reduce((sum, current) => sum + current); ``` - So it's advised to always specify the initial value. The method [arr.reduceRight](mdn:js/Array/reduceRight) does the same, but goes from right to left. diff --git a/1-js/05-data-types/07-map-set/02-filter-anagrams/solution.md b/1-js/05-data-types/07-map-set/02-filter-anagrams/solution.md index 4c8af1f24..160675185 100644 --- a/1-js/05-data-types/07-map-set/02-filter-anagrams/solution.md +++ b/1-js/05-data-types/07-map-set/02-filter-anagrams/solution.md @@ -36,7 +36,7 @@ Letter-sorting is done by the chain of calls in the line `(*)`. For convenience let's split it into multiple lines: ```js -let sorted = arr[i] // PAN +let sorted = word // PAN .toLowerCase() // pan .split('') // ['p','a','n'] .sort() // ['a','n','p'] diff --git a/1-js/05-data-types/11-date/8-format-date-relative/solution.md b/1-js/05-data-types/11-date/8-format-date-relative/solution.md index 2507c840c..718618528 100644 --- a/1-js/05-data-types/11-date/8-format-date-relative/solution.md +++ b/1-js/05-data-types/11-date/8-format-date-relative/solution.md @@ -62,6 +62,8 @@ function formatDate(date) { year = year.toString().slice(-2); month = month < 10 ? '0' + month : month; dayOfMonth = dayOfMonth < 10 ? '0' + dayOfMonth : dayOfMonth; + hour = hour < 10 ? '0' + hour : hour; + minutes = minutes < 10 ? '0' + minutes : minutes; if (diffSec < 1) { return 'right now'; diff --git a/1-js/06-advanced-functions/01-recursion/03-fibonacci-numbers/fibonacci-recursion-tree.svg b/1-js/06-advanced-functions/01-recursion/03-fibonacci-numbers/fibonacci-recursion-tree.svg index 28f0c9b13..59e6a52c4 100644 --- a/1-js/06-advanced-functions/01-recursion/03-fibonacci-numbers/fibonacci-recursion-tree.svg +++ b/1-js/06-advanced-functions/01-recursion/03-fibonacci-numbers/fibonacci-recursion-tree.svg @@ -1 +1 @@ -fib ( 5 )fib(4)fib(3)fib(3)fib(2)fib(0)fib(1)fib(1)fib(2)fib(0)fib(1)fib(1)fib(2)fib(0)fib(1) \ No newline at end of file +fib ( 5 )fib(4)fib(3)fib(3)fib(2)fib(0)fib(1)fib(1)fib(2)fib(0)fib(1)fib(1)fib(2)fib(0)fib(1) \ No newline at end of file diff --git a/1-js/06-advanced-functions/01-recursion/linked-list-remove-1.svg b/1-js/06-advanced-functions/01-recursion/linked-list-remove-1.svg index 346c436a3..edec23912 100644 --- a/1-js/06-advanced-functions/01-recursion/linked-list-remove-1.svg +++ b/1-js/06-advanced-functions/01-recursion/linked-list-remove-1.svg @@ -1 +1 @@ -value"new item"nextvalue1nextvalue2nextvalue3nextvalue4nextnulllist \ No newline at end of file +value"new item"nextvalue1nextvalue2nextvalue3nextvalue4nextnulllist \ No newline at end of file diff --git a/1-js/06-advanced-functions/01-recursion/recursion-pow.svg b/1-js/06-advanced-functions/01-recursion/recursion-pow.svg index 910b2aa50..8bd4a43fe 100644 --- a/1-js/06-advanced-functions/01-recursion/recursion-pow.svg +++ b/1-js/06-advanced-functions/01-recursion/recursion-pow.svg @@ -1 +1 @@ -pow(x,n)xx * pow(x, n-1)n == 1 ?YesNorecursive call until n==1 \ No newline at end of file +pow(x,n)xx * pow(x, n-1)n == 1 ?YesNorecursive call until n==1 \ No newline at end of file diff --git a/1-js/06-advanced-functions/01-recursion/recursive-salaries.svg b/1-js/06-advanced-functions/01-recursion/recursive-salaries.svg index 9a3296655..f47f0668b 100644 --- a/1-js/06-advanced-functions/01-recursion/recursive-salaries.svg +++ b/1-js/06-advanced-functions/01-recursion/recursive-salaries.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file 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/article.md similarity index 89% rename from 1-js/06-advanced-functions/02-rest-parameters-spread-operator/article.md rename to 1-js/06-advanced-functions/02-rest-parameters-spread/article.md index a14f0fb73..74c85d4f4 100644 --- a/1-js/06-advanced-functions/02-rest-parameters-spread-operator/article.md +++ b/1-js/06-advanced-functions/02-rest-parameters-spread/article.md @@ -1,4 +1,4 @@ -# Rest parameters and spread operator +# Rest parameters and spread syntax Many JavaScript built-in functions support an arbitrary number of arguments. @@ -122,7 +122,7 @@ As we remember, arrow functions don't have their own `this`. Now we know they do ```` -## Spread operator [#spread-operator] +## Spread syntax [#spread-syntax] We've just seen how to get an array from the list of parameters. @@ -148,7 +148,7 @@ alert( Math.max(arr) ); // NaN And surely we can't manually list items in the code `Math.max(arr[0], arr[1], arr[2])`, because we may be unsure how many there are. As our script executes, there could be a lot, or there could be none. And that would get ugly. -*Spread operator* to the rescue! It looks similar to rest parameters, also using `...`, but does quite the opposite. +*Spread syntax* to the rescue! It looks similar to rest parameters, also using `...`, but does quite the opposite. When `...arr` is used in the function call, it "expands" an iterable object `arr` into the list of arguments. @@ -169,7 +169,7 @@ let arr2 = [8, 3, -8, 1]; alert( Math.max(...arr1, ...arr2) ); // 8 ``` -We can even combine the spread operator with normal values: +We can even combine the spread syntax with normal values: ```js run @@ -179,7 +179,7 @@ let arr2 = [8, 3, -8, 1]; alert( Math.max(1, ...arr1, 2, ...arr2, 25) ); // 25 ``` -Also, the spread operator can be used to merge arrays: +Also, the spread syntax can be used to merge arrays: ```js run let arr = [3, 5, 1]; @@ -192,9 +192,9 @@ let merged = [0, ...arr, 2, ...arr2]; alert(merged); // 0,3,5,1,2,8,9,15 (0, then arr, then 2, then arr2) ``` -In the examples above we used an array to demonstrate the spread operator, but any iterable will do. +In the examples above we used an array to demonstrate the spread syntax, but any iterable will do. -For instance, here we use the spread operator to turn the string into array of characters: +For instance, here we use the spread syntax to turn the string into array of characters: ```js run let str = "Hello"; @@ -202,7 +202,7 @@ let str = "Hello"; alert( [...str] ); // H,e,l,l,o ``` -The spread operator internally uses iterators to gather elements, the same way as `for..of` does. +The spread syntax 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]`. @@ -220,24 +220,24 @@ The result is the same as `[...str]`. But there's a subtle difference between `Array.from(obj)` and `[...obj]`: - `Array.from` operates on both array-likes and iterables. -- The spread operator operates only on iterables. +- The spread syntax works only with iterables. So, for the task of turning something into an array, `Array.from` tends to be more universal. ## Summary -When we see `"..."` in the code, it is either rest parameters or the spread operator. +When we see `"..."` in the code, it is either rest parameters or the spread syntax. There's an easy way to distinguish between them: - When `...` is at the end of function parameters, it's "rest parameters" and gathers the rest of the list of arguments into an array. -- When `...` occurs in a function call or alike, it's called a "spread operator" and expands an array into a list. +- When `...` occurs in a function call or alike, it's called a "spread syntax" and expands an array into a list. Use patterns: - Rest parameters are used to create functions that accept any number of arguments. -- The spread operator is used to pass an array to functions that normally require a list of many arguments. +- The spread syntax is used to pass an array to functions that normally require a list of many arguments. Together they help to travel between a list and an array of parameters with ease. diff --git a/1-js/06-advanced-functions/03-closure/1-closure-latest-changes/solution.md b/1-js/06-advanced-functions/03-closure/1-closure-latest-changes/solution.md new file mode 100644 index 000000000..7cbd85ab7 --- /dev/null +++ b/1-js/06-advanced-functions/03-closure/1-closure-latest-changes/solution.md @@ -0,0 +1,5 @@ +The answer is: **Pete**. + +A function gets outer variables as they are now, it uses the most recent values. + +Old variable values are not saved anywhere. When a function wants a variable, it takes the current value from its own Lexical Environment or the outer one. diff --git a/1-js/06-advanced-functions/03-closure/1-closure-latest-changes/task.md b/1-js/06-advanced-functions/03-closure/1-closure-latest-changes/task.md new file mode 100644 index 000000000..819189773 --- /dev/null +++ b/1-js/06-advanced-functions/03-closure/1-closure-latest-changes/task.md @@ -0,0 +1,23 @@ +importance: 5 + +--- + +# Does a function pickup latest changes? + +The function sayHi uses an external variable name. When the function runs, which value is it going to use? + +```js +let name = "John"; + +function sayHi() { + alert("Hi, " + name); +} + +name = "Pete"; + +sayHi(); // what will it show: "John" or "Pete"? +``` + +Such situations are common both in browser and server-side development. A function may be scheduled to execute later than it is created, for instance after a user action or a network request. + +So, the question is: does it pick up the latest changes? diff --git a/1-js/06-advanced-functions/03-closure/8-make-army/_js.view/solution.js b/1-js/06-advanced-functions/03-closure/10-make-army/_js.view/solution.js similarity index 100% rename from 1-js/06-advanced-functions/03-closure/8-make-army/_js.view/solution.js rename to 1-js/06-advanced-functions/03-closure/10-make-army/_js.view/solution.js diff --git a/1-js/06-advanced-functions/03-closure/8-make-army/_js.view/source.js b/1-js/06-advanced-functions/03-closure/10-make-army/_js.view/source.js similarity index 100% rename from 1-js/06-advanced-functions/03-closure/8-make-army/_js.view/source.js rename to 1-js/06-advanced-functions/03-closure/10-make-army/_js.view/source.js diff --git a/1-js/06-advanced-functions/03-closure/8-make-army/_js.view/test.js b/1-js/06-advanced-functions/03-closure/10-make-army/_js.view/test.js similarity index 100% rename from 1-js/06-advanced-functions/03-closure/8-make-army/_js.view/test.js rename to 1-js/06-advanced-functions/03-closure/10-make-army/_js.view/test.js diff --git a/1-js/06-advanced-functions/03-closure/8-make-army/lexenv-makearmy.svg b/1-js/06-advanced-functions/03-closure/10-make-army/lexenv-makearmy.svg similarity index 100% rename from 1-js/06-advanced-functions/03-closure/8-make-army/lexenv-makearmy.svg rename to 1-js/06-advanced-functions/03-closure/10-make-army/lexenv-makearmy.svg diff --git a/1-js/06-advanced-functions/03-closure/8-make-army/solution.md b/1-js/06-advanced-functions/03-closure/10-make-army/solution.md similarity index 100% rename from 1-js/06-advanced-functions/03-closure/8-make-army/solution.md rename to 1-js/06-advanced-functions/03-closure/10-make-army/solution.md diff --git a/1-js/06-advanced-functions/03-closure/8-make-army/task.md b/1-js/06-advanced-functions/03-closure/10-make-army/task.md similarity index 86% rename from 1-js/06-advanced-functions/03-closure/8-make-army/task.md rename to 1-js/06-advanced-functions/03-closure/10-make-army/task.md index ede8fd045..93e64f2d0 100644 --- a/1-js/06-advanced-functions/03-closure/8-make-army/task.md +++ b/1-js/06-advanced-functions/03-closure/10-make-army/task.md @@ -31,5 +31,5 @@ army[5](); // and number 5 also outputs 10... // ... all shooters show 10 instead of their 0, 1, 2, 3... ``` -Why all shooters show the same? Fix the code so that they work as intended. +Why do all of the shooters show the same value? Fix the code so that they work as intended. diff --git a/1-js/06-advanced-functions/03-closure/lexenv-nested-work.svg b/1-js/06-advanced-functions/03-closure/2-closure-variable-access/lexenv-nested-work.svg similarity index 99% rename from 1-js/06-advanced-functions/03-closure/lexenv-nested-work.svg rename to 1-js/06-advanced-functions/03-closure/2-closure-variable-access/lexenv-nested-work.svg index e5b7f83e7..5cdf7f1a4 100644 --- a/1-js/06-advanced-functions/03-closure/lexenv-nested-work.svg +++ b/1-js/06-advanced-functions/03-closure/2-closure-variable-access/lexenv-nested-work.svg @@ -1 +1 @@ -makeWorker: function name: "John"<empty>outerouterouternullname: "Pete" \ No newline at end of file +makeWorker: function name: "John"<empty>outerouterouternullname: "Pete" \ No newline at end of file diff --git a/1-js/06-advanced-functions/03-closure/2-closure-variable-access/solution.md b/1-js/06-advanced-functions/03-closure/2-closure-variable-access/solution.md new file mode 100644 index 000000000..0a522132f --- /dev/null +++ b/1-js/06-advanced-functions/03-closure/2-closure-variable-access/solution.md @@ -0,0 +1,9 @@ +The answer is: **Pete**. + +The `work()` function in the code below gets `name` from the place of its origin through the outer lexical environment reference: + +![](lexenv-nested-work.svg) + +So, the result is `"Pete"` here. + +But if there were no `let name` in `makeWorker()`, then the search would go outside and take the global variable as we can see from the chain above. In that case the result would be `"John"`. diff --git a/1-js/06-advanced-functions/03-closure/2-closure-variable-access/task.md b/1-js/06-advanced-functions/03-closure/2-closure-variable-access/task.md new file mode 100644 index 000000000..d12a385c8 --- /dev/null +++ b/1-js/06-advanced-functions/03-closure/2-closure-variable-access/task.md @@ -0,0 +1,29 @@ +importance: 5 + +--- + +# Which variables are available? + +The function `makeWorker` below makes another function and returns it. That new function can be called from somewhere else. + +Will it have access to the outer variables from its creation place, or the invocation place, or both? + +```js +function makeWorker() { + let name = "Pete"; + + return function() { + alert(name); + }; +} + +let name = "John"; + +// create a function +let work = makeWorker(); + +// call it +work(); // what will it show? +``` + +Which value it will show? "Pete" or "John"? diff --git a/1-js/06-advanced-functions/03-closure/1-counter-independent/solution.md b/1-js/06-advanced-functions/03-closure/3-counter-independent/solution.md similarity index 100% rename from 1-js/06-advanced-functions/03-closure/1-counter-independent/solution.md rename to 1-js/06-advanced-functions/03-closure/3-counter-independent/solution.md diff --git a/1-js/06-advanced-functions/03-closure/1-counter-independent/task.md b/1-js/06-advanced-functions/03-closure/3-counter-independent/task.md similarity index 100% rename from 1-js/06-advanced-functions/03-closure/1-counter-independent/task.md rename to 1-js/06-advanced-functions/03-closure/3-counter-independent/task.md diff --git a/1-js/06-advanced-functions/03-closure/2-counter-object-independent/solution.md b/1-js/06-advanced-functions/03-closure/4-counter-object-independent/solution.md similarity index 100% rename from 1-js/06-advanced-functions/03-closure/2-counter-object-independent/solution.md rename to 1-js/06-advanced-functions/03-closure/4-counter-object-independent/solution.md diff --git a/1-js/06-advanced-functions/03-closure/2-counter-object-independent/task.md b/1-js/06-advanced-functions/03-closure/4-counter-object-independent/task.md similarity index 100% rename from 1-js/06-advanced-functions/03-closure/2-counter-object-independent/task.md rename to 1-js/06-advanced-functions/03-closure/4-counter-object-independent/task.md diff --git a/1-js/06-advanced-functions/03-closure/3-function-in-if/solution.md b/1-js/06-advanced-functions/03-closure/5-function-in-if/solution.md similarity index 100% rename from 1-js/06-advanced-functions/03-closure/3-function-in-if/solution.md rename to 1-js/06-advanced-functions/03-closure/5-function-in-if/solution.md diff --git a/1-js/06-advanced-functions/03-closure/3-function-in-if/task.md b/1-js/06-advanced-functions/03-closure/5-function-in-if/task.md similarity index 100% rename from 1-js/06-advanced-functions/03-closure/3-function-in-if/task.md rename to 1-js/06-advanced-functions/03-closure/5-function-in-if/task.md diff --git a/1-js/06-advanced-functions/03-closure/4-closure-sum/solution.md b/1-js/06-advanced-functions/03-closure/6-closure-sum/solution.md similarity index 100% rename from 1-js/06-advanced-functions/03-closure/4-closure-sum/solution.md rename to 1-js/06-advanced-functions/03-closure/6-closure-sum/solution.md diff --git a/1-js/06-advanced-functions/03-closure/4-closure-sum/task.md b/1-js/06-advanced-functions/03-closure/6-closure-sum/task.md similarity index 100% rename from 1-js/06-advanced-functions/03-closure/4-closure-sum/task.md rename to 1-js/06-advanced-functions/03-closure/6-closure-sum/task.md diff --git a/1-js/06-advanced-functions/03-closure/7-let-scope/solution.md b/1-js/06-advanced-functions/03-closure/7-let-scope/solution.md new file mode 100644 index 000000000..f20ac9e5d --- /dev/null +++ b/1-js/06-advanced-functions/03-closure/7-let-scope/solution.md @@ -0,0 +1,40 @@ +The result is: **error**. + +Try running it: + +```js run +let x = 1; + +function func() { +*!* + console.log(x); // ReferenceError: Cannot access 'x' before initialization +*/!* + let x = 2; +} + +func(); +``` + +In this example we can observe the peculiar difference between a "non-existing" and "unitialized" variable. + +As you may have read in the article [](info:closure), a variable starts in the "uninitialized" state from the moment when the execution enters a code block (or a function). And it stays uninitalized until the corresponding `let` statement. + +In other words, a variable technically exists, but can't be used before `let`. + +The code above demonstrates it. + +```js +function func() { +*!* + // the local variable x is known to the engine from the beginning of the function, + // but "unitialized" (unusable) until let ("dead zone") + // hence the error +*/!* + + console.log(x); // ReferenceError: Cannot access 'vx before initialization + + let x = 2; +} +``` + +This zone of temporary unusability of a variable (from the beginning of the code block till `let`) is sometimes called the "dead zone". diff --git a/1-js/06-advanced-functions/03-closure/7-let-scope/task.md b/1-js/06-advanced-functions/03-closure/7-let-scope/task.md new file mode 100644 index 000000000..fb7445e66 --- /dev/null +++ b/1-js/06-advanced-functions/03-closure/7-let-scope/task.md @@ -0,0 +1,21 @@ +importance: 4 + +--- + +# Is variable visible? + +What will be the result of this code? + +```js +let x = 1; + +function func() { + console.log(x); // ? + + let x = 2; +} + +func(); +``` + +P.S. There's a pitfall in this task. The solution is not obvious. diff --git a/1-js/06-advanced-functions/03-closure/6-filter-through-function/_js.view/solution.js b/1-js/06-advanced-functions/03-closure/8-filter-through-function/_js.view/solution.js similarity index 100% rename from 1-js/06-advanced-functions/03-closure/6-filter-through-function/_js.view/solution.js rename to 1-js/06-advanced-functions/03-closure/8-filter-through-function/_js.view/solution.js diff --git a/1-js/06-advanced-functions/03-closure/6-filter-through-function/_js.view/source.js b/1-js/06-advanced-functions/03-closure/8-filter-through-function/_js.view/source.js similarity index 100% rename from 1-js/06-advanced-functions/03-closure/6-filter-through-function/_js.view/source.js rename to 1-js/06-advanced-functions/03-closure/8-filter-through-function/_js.view/source.js diff --git a/1-js/06-advanced-functions/03-closure/6-filter-through-function/_js.view/test.js b/1-js/06-advanced-functions/03-closure/8-filter-through-function/_js.view/test.js similarity index 100% rename from 1-js/06-advanced-functions/03-closure/6-filter-through-function/_js.view/test.js rename to 1-js/06-advanced-functions/03-closure/8-filter-through-function/_js.view/test.js diff --git a/1-js/06-advanced-functions/03-closure/6-filter-through-function/solution.md b/1-js/06-advanced-functions/03-closure/8-filter-through-function/solution.md similarity index 100% rename from 1-js/06-advanced-functions/03-closure/6-filter-through-function/solution.md rename to 1-js/06-advanced-functions/03-closure/8-filter-through-function/solution.md diff --git a/1-js/06-advanced-functions/03-closure/6-filter-through-function/task.md b/1-js/06-advanced-functions/03-closure/8-filter-through-function/task.md similarity index 100% rename from 1-js/06-advanced-functions/03-closure/6-filter-through-function/task.md rename to 1-js/06-advanced-functions/03-closure/8-filter-through-function/task.md diff --git a/1-js/06-advanced-functions/03-closure/7-sort-by-field/solution.md b/1-js/06-advanced-functions/03-closure/9-sort-by-field/solution.md similarity index 100% rename from 1-js/06-advanced-functions/03-closure/7-sort-by-field/solution.md rename to 1-js/06-advanced-functions/03-closure/9-sort-by-field/solution.md diff --git a/1-js/06-advanced-functions/03-closure/7-sort-by-field/task.md b/1-js/06-advanced-functions/03-closure/9-sort-by-field/task.md similarity index 100% rename from 1-js/06-advanced-functions/03-closure/7-sort-by-field/task.md rename to 1-js/06-advanced-functions/03-closure/9-sort-by-field/task.md diff --git a/1-js/06-advanced-functions/03-closure/article.md b/1-js/06-advanced-functions/03-closure/article.md index 64104541c..0f3bc8cc8 100644 --- a/1-js/06-advanced-functions/03-closure/article.md +++ b/1-js/06-advanced-functions/03-closure/article.md @@ -1,203 +1,98 @@ -# Closure +# Variable scope -JavaScript is a very function-oriented language. It gives us a lot of freedom. A function can be created dynamically, copied to another variable or passed as an argument to another function and called from a totally different place later. +JavaScript is a very function-oriented language. It gives us a lot of freedom. A function can be created dynamically, passed as an argument to another function and called from a totally different place of code later. -We know that a function can access variables outside of it, this feature is used quite often. +We already know that a function can access variables outside of it. -But what happens when an outer variable changes? Does a function get the most recent value or the one that existed when the function was created? +Now let's expand our knowledge to include more complex scenarios. -Also, what happens when a function travels to another place in the code and is called from there -- does it get access to the outer variables of the new place? +```smart header="We'll talk about `let/const` variables here" +In JavaScript, there are 3 ways to declare a variable: `let`, `const` (the modern ones), and `var` (the remnant of the past). -Different languages behave differently here, and in this chapter we cover the behaviour of JavaScript. - -## A couple of questions - -Let's consider two situations to begin with, and then study the internal mechanics piece-by-piece, so that you'll be able to answer the following questions and more complex ones in the future. - -1. The function `sayHi` uses an external variable `name`. When the function runs, which value is it going to use? - - ```js - let name = "John"; - - function sayHi() { - alert("Hi, " + name); - } - - name = "Pete"; - - *!* - sayHi(); // what will it show: "John" or "Pete"? - */!* - ``` - - Such situations are common both in browser and server-side development. A function may be scheduled to execute later than it is created, for instance after a user action or a network request. - - So, the question is: does it pick up the latest changes? - - -2. The function `makeWorker` makes another function and returns it. That new function can be called from somewhere else. Will it have access to the outer variables from its creation place, or the invocation place, or both? - - ```js - function makeWorker() { - let name = "Pete"; - - return function() { - alert(name); - }; - } - - let name = "John"; - - // create a function - let work = makeWorker(); - - // call it - *!* - work(); // what will it show? "Pete" (name where created) or "John" (name where called)? - */!* - ``` - - -## Lexical Environment - -To understand what's going on, let's first discuss what a "variable" actually is. - -In JavaScript, every running function, code block `{...}`, and the script as a whole have an internal (hidden) associated object known as the *Lexical Environment*. - -The Lexical Environment object consists of two parts: - -1. *Environment Record* -- an object that stores all local variables as its properties (and some other information like the value of `this`). -2. A reference to the *outer lexical environment*, the one associated with the outer code. - -**A "variable" is just a property of the special internal object, `Environment Record`. "To get or change a variable" means "to get or change a property of that object".** - -For instance, in this simple code, there is only one Lexical Environment: - -![lexical environment](lexical-environment-global.svg) - -This is a so-called global Lexical Environment, associated with the whole script. - -On the picture above, the rectangle means Environment Record (variable store) and the arrow means the outer reference. The global Lexical Environment has no outer reference, so it points to `null`. - -And that's how it changes when a variable is defined and assigned: - -![lexical environment](lexical-environment-global-2.svg) - -Rectangles on the right-hand side demonstrate how the global Lexical Environment changes during the execution: - -1. When the script starts, the Lexical Environment is empty. -2. The `let phrase` definition appears. It has been assigned no value, so `undefined` is stored. -3. `phrase` is assigned a value. -4. `phrase` changes value. - -Everything looks simple for now, right? - -To summarize: - -- A variable is a property of a special internal object, associated with the currently executing block/function/script. -- Working with variables is actually working with the properties of that object. - -### Function Declaration - -Until now, we only observed variables. Now enter Function Declarations. - -**Unlike `let` variables, they are fully initialized not when the execution reaches them, but earlier, when a Lexical Environment is created.** - -For top-level functions, it means the moment when the script is started. - -That is why we can call a function declaration before it is defined. - -The code below demonstrates that the Lexical Environment is non-empty from the beginning. It has `say`, because that's a Function Declaration. And later it gets `phrase`, declared with `let`: - -![lexical environment](lexical-environment-global-3.svg) - - -### Inner and outer Lexical Environment - -Now let's go on and explore what happens when a function accesses an outer variable. - -During the call, `say()` uses the outer variable `phrase`. Let's look at the details of what's going on. - -When a function runs, a new Lexical Environment is created automatically to store local variables and parameters of the call. - -For instance, for `say("John")`, it looks like this (the execution is at the line, labelled with an arrow): - - - -![lexical environment](lexical-environment-simple.svg) +- In this article we'll use `let` variables in examples. +- Variables, declared with `const`, behave the same, so this article is about `const` too. +- The old `var` has some notable differences, they will be covered in the article . +``` -So, during the function call we have two Lexical Environments: the inner one (for the function call) and the outer one (global): +## Code blocks -- The inner Lexical Environment corresponds to the current execution of `say`. +If a variable is declared inside a code block `{...}`, it's only visible inside that block. - It has a single property: `name`, the function argument. We called `say("John")`, so the value of `name` is `"John"`. -- The outer Lexical Environment is the global Lexical Environment. +For example: - It has `phrase` variable and the function itself. +```js run +{ + // do some job with local variables that should not be seen outside -The inner Lexical Environment has a reference to the `outer` one. + let message = "Hello"; // only visible in this block -**When the code wants to access a variable -- the inner Lexical Environment is searched first, then the outer one, then the more outer one and so on until the global one.** + alert(message); // Hello +} -If a variable is not found anywhere, that's an error in strict mode. Without `use strict`, an assignment to a non-existing variable like `user = "John"` creates a new global variable `user`. That's for backwards compatibility. +alert(message); // Error: message is not defined +``` -Let's see how the search proceeds in our example: +We can use this to isolate a piece of code that does its own task, with variables that only belong to it: -- When the `alert` inside `say` wants to access `name`, it finds it immediately in the function Lexical Environment. -- When it wants to access `phrase`, then there is no `phrase` locally, so it follows the reference to the enclosing Lexical Environment and finds it there. +```js run +{ + // show message + let message = "Hello"; + alert(message); +} -![lexical environment lookup](lexical-environment-simple-lookup.svg) +{ + // show another message + let message = "Goodbye"; + alert(message); +} +``` -Now we can give the answer to the first question from the beginning of the chapter. +````smart header="There'd be an error without blocks" +Please note, without separate blocks there would be an error, if we use `let` with the existing variable name: -**A function gets outer variables as they are now, it uses the most recent values.** +```js run +// show message +let message = "Hello"; +alert(message); -Old variable values are not saved anywhere. When a function wants a variable, it takes the current value from its own Lexical Environment or the outer one. +// show another message +*!* +let message = "Goodbye"; // Error: variable already declared +*/!* +alert(message); +``` +```` -So the answer to the first question is `Pete`: +For `if`, `for`, `while` and so on, variables declared in `{...}` are also only visible inside: ```js run -let name = "John"; +if (true) { + let phrase = "Hello!"; -function sayHi() { - alert("Hi, " + name); + alert(phrase); // Hello! } -name = "Pete"; // (*) - -*!* -sayHi(); // Pete -*/!* +alert(phrase); // Error, no such variable! ``` +Here, after `if` finishes, the `alert` below won't see the `phrase`, hence the error. -The execution flow of the code above: - -1. The global Lexical Environment has `name: "John"`. -2. At the line `(*)` the global variable is changed. Now it has `name: "Pete"`. -3. When the function `sayHi()` is executed it takes `name` from outside, the global Lexical Environment, where its value is already `"Pete"`. +That's great, as it allows us to create block-local variables, specific to an `if` branch. +The similar thing holds true for `for` and `while` loops: -```smart header="One call -- one Lexical Environment" -Please note that a new function Lexical Environment is created each time a function runs. - -And if a function is called multiple times, then each invocation will have its own Lexical Environment, with local variables and parameters specific for that very run. -``` +```js run +for (let i = 0; i < 3; i++) { + // the variable i is only visible inside this for + alert(i); // 0, then 1, then 2 +} -```smart header="Lexical Environment is a specification object" -"Lexical Environment" is a specification object: it only exists "theoretically" in the [language specification](https://tc39.es/ecma262/#sec-lexical-environments) to describe how things work. We can't get this object in our code and manipulate it directly. JavaScript engines also may optimize it, discard variables that are unused to save memory and perform other internal tricks, as long as the visible behavior remains as described. +alert(i); // Error, no such variable ``` +Visually, `let i` is outside of `{...}`. But the `for` construct is special here: the variable, declared inside it, is considered a part of the block. ## Nested functions @@ -223,32 +118,16 @@ function sayHiBye(firstName, lastName) { Here the *nested* function `getFullName()` is made for convenience. It can access the outer variables and so can return the full name. Nested functions are quite common in JavaScript. -What's much more interesting, a nested function can be returned: either as a property of a new object (if the outer function creates an object with methods) or as a result by itself. It can then be used somewhere else. No matter where, it still has access to the same outer variables. - -For instance, here the nested function is assigned to the new object by the [constructor function](info:constructor-new): - -```js run -// constructor function returns a new object -function User(name) { - - // the object method is created as a nested function - this.sayHi = function() { - alert(name); - }; -} - -let user = new User("John"); -user.sayHi(); // the method "sayHi" code has access to the outer "name" -``` +What's much more interesting, a nested function can be returned: either as a property of a new object or as a result by itself. It can then be used somewhere else. No matter where, it still has access to the same outer variables. -And here we just create and return a "counting" function: +Below, `makeCounter` creates the "counter" function that returns the next number on each invocation: ```js run function makeCounter() { let count = 0; return function() { - return count++; // has access to the outer "count" + return count++; }; } @@ -259,314 +138,195 @@ alert( counter() ); // 1 alert( counter() ); // 2 ``` -Let's go on with the `makeCounter` example. It creates the "counter" function that returns the next number on each invocation. Despite being simple, slightly modified variants of that code have practical uses, for instance, as a [pseudorandom number generator](https://en.wikipedia.org/wiki/Pseudorandom_number_generator), and more. - -How does the counter work internally? - -When the inner function runs, the variable in `count++` is searched from inside out. For the example above, the order will be: +Despite being simple, slightly modified variants of that code have practical uses, for instance, as a [random number generator](https://en.wikipedia.org/wiki/Pseudorandom_number_generator) to generate random values for automated tests. -![](lexical-search-order.svg) +How does this work? If we create multiple counters, will they be independent? What's going on with the variables here? -1. The locals of the nested function... -2. The variables of the outer function... -3. And so on until it reaches global variables. +Undestanding such things is great for the overall knowledge of JavaScript and beneficial for more complex scenarios. So let's go a bit in-depth. -In this example `count` is found on step `2`. When an outer variable is modified, it's changed where it's found. So `count++` finds the outer variable and increases it in the Lexical Environment where it belongs. Like if we had `let count = 1`. - -Here are two questions to consider: - -1. Can we somehow reset the counter `count` from the code that doesn't belong to `makeCounter`? E.g. after `alert` calls in the example above. -2. If we call `makeCounter()` multiple times -- it returns many `counter` functions. Are they independent or do they share the same `count`? - -Try to answer them before you continue reading. - -... - -All done? - -Okay, let's go over the answers. - -1. There is no way: `count` is a local function variable, we can't access it from the outside. -2. For every call to `makeCounter()` a new function Lexical Environment is created, with its own `count`. So the resulting `counter` functions are independent. - -Here's the demo: - -```js run -function makeCounter() { - let count = 0; - return function() { - return count++; - }; -} - -let counter1 = makeCounter(); -let counter2 = makeCounter(); +## Lexical Environment -alert( counter1() ); // 0 -alert( counter1() ); // 1 +```warn header="Here be dragons!" +The in-depth technical explanation lies ahead. -alert( counter2() ); // 0 (independent) +As far as I'd like to avoid low-level language details, any understanding without them would be lacking and incomplete, so get ready. ``` +For clarity, the explanation is split into multiple steps. -Hopefully, the situation with outer variables is clear now. For most situations such understanding is enough. There are few details in the specification that we omitted for brevity. So in the next section we cover even more details. - -## Environments in detail - -Here's what's going on in the `makeCounter` example step-by-step. Follow it to make sure that you understand how it works in detail. - -Please note the additional `[[Environment]]` property is covered here. We didn't mention it before for simplicity. - -1. When the script has just started, there is only the global Lexical Environment: - - ![](lexenv-nested-makecounter-1.svg) - - At that starting moment there is only the `makeCounter` function, because it's a Function Declaration. It did not run yet. - - **All functions "on birth" receive a hidden property `[[Environment]]` with a reference to the Lexical Environment of their creation.** - - We didn't talk about it before. That's how the function knows where it was made. - - Here, `makeCounter` is created in the global Lexical Environment, so `[[Environment]]` keeps a reference to it. - - In other words, a function is "imprinted" with a reference to the Lexical Environment where it was born. And `[[Environment]]` is the hidden function property that has that reference. - -2. The code runs on, the new global variable `counter` is declared and gets the result of the `makeCounter()` call. Here's a snapshot of the moment when the execution is on the first line inside `makeCounter()`: - - ![](lexenv-nested-makecounter-2.svg) +### Step 1. Variables - At the moment of the call of `makeCounter()`, the Lexical Environment is created, to hold its variables and arguments. - - As all Lexical Environments, it stores two things: - 1. An Environment Record with local variables. In our case `count` is the only local variable (appearing when the line with `let count` is executed). - 2. The outer lexical reference, which is set to the value of `[[Environment]]` of the function. Here `[[Environment]]` of `makeCounter` references the global Lexical Environment. - - So, now we have two Lexical Environments: the first one is global, the second one is for the current `makeCounter` call, with the outer reference to global. - -3. During the execution of `makeCounter()`, a tiny nested function is created. - - It doesn't matter whether the function is created using Function Declaration or Function Expression. All functions get the `[[Environment]]` property that references the Lexical Environment in which they were made. So our new tiny nested function gets it as well. - - For our new nested function the value of `[[Environment]]` is the current Lexical Environment of `makeCounter()` (where it was born): +In JavaScript, every running function, code block `{...}`, and the script as a whole have an internal (hidden) associated object known as the *Lexical Environment*. - ![](lexenv-nested-makecounter-3.svg) +The Lexical Environment object consists of two parts: - Please note that on this step the inner function was created, but not yet called. The code inside `return count++;` is not running. +1. *Environment Record* -- an object that stores all local variables as its properties (and some other information like the value of `this`). +2. A reference to the *outer lexical environment*, the one associated with the outer code. -4. As the execution goes on, the call to `makeCounter()` finishes, and the result (the tiny nested function) is assigned to the global variable `counter`: +**A "variable" is just a property of the special internal object, `Environment Record`. "To get or change a variable" means "to get or change a property of that object".** - ![](lexenv-nested-makecounter-4.svg) +In this simple code without functions, there is only one Lexical Environment: - That function has only one line: `return count++`, that will be executed when we run it. +![lexical environment](lexical-environment-global.svg) -5. When `counter()` is called, a new Lexical Environment is created for the call. It's empty, as `counter` has no local variables by itself. But the `[[Environment]]` of `counter` is used as the `outer` reference for it, that provides access to the variables of the former `makeCounter()` call where it was created: +This is the so-called *global* Lexical Environment, associated with the whole script. - ![](lexenv-nested-makecounter-5.svg) +On the picture above, the rectangle means Environment Record (variable store) and the arrow means the outer reference. The global Lexical Environment has no outer reference, that's why the arrow points to `null`. - Now when the call looks for `count` variable, it first searches its own Lexical Environment (empty), then the Lexical Environment of the outer `makeCounter()` call, where it finds it. +As the code starts executing and goes on, the Lexical Environment changes. - Please note how memory management works here. Although `makeCounter()` call finished some time ago, its Lexical Environment was retained in memory, because there's a nested function with `[[Environment]]` referencing it. +Here's a little bit longer code: - Generally, a Lexical Environment object lives as long as there is a function which may use it. And only when there are none remaining, it is cleared. +![lexical environment](closure-variable-phrase.svg) -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. +Rectangles on the right-hand side demonstrate how the global Lexical Environment changes during the execution: - ![](lexenv-nested-makecounter-6.svg) +1. When the script starts, the Lexical Environment is pre-populated with all declared variables. + - Initially, they are in the "Uninitialized" state. That's a special internal state, it means that the engine knows about the variable, but won't allow to use it before `let`. It's almost the same as if the variable didn't exist. +2. Then `let phrase` definition appears. There's no assignment yet, so its value is `undefined`. We can use the variable since this moment. +3. `phrase` is assigned a value. +4. `phrase` changes the value. -7. Next `counter()` invocations do the same. +Everything looks simple for now, right? -The answer to the second question from the beginning of the chapter should now be obvious. +- A variable is a property of a special internal object, associated with the currently executing block/function/script. +- Working with variables is actually working with the properties of that object. -The `work()` function in the code below gets `name` from the place of its origin through the outer lexical environment reference: +```smart header="Lexical Environment is a specification object" +"Lexical Environment" is a specification object: it only exists "theoretically" in the [language specification](https://tc39.es/ecma262/#sec-lexical-environments) to describe how things work. We can't get this object in our code and manipulate it directly. -![](lexenv-nested-work.svg) +JavaScript engines also may optimize it, discard variables that are unused to save memory and perform other internal tricks, as long as the visible behavior remains as described. +``` -So, the result is `"Pete"` here. +### Step 2. Function Declarations -But if there were no `let name` in `makeWorker()`, then the search would go outside and take the global variable as we can see from the chain above. In that case it would be `"John"`. +A function is also a value, like a variable. -```smart header="Closures" -There is a general programming term "closure", that developers generally should know. +**The difference is that a Function Declaration is instantly fully initialized.** -A [closure](https://en.wikipedia.org/wiki/Closure_(computer_programming)) is a function that remembers its outer variables and can access them. In some languages, that's not possible, or a function should be written in a special way to make it happen. But as explained above, in JavaScript, all functions are naturally closures (there is only one exclusion, to be covered in ). +When a Lexical Environment is created, a Function Declaration immediately becomes a ready-to-use function (unlike `let`, that is unusable till the declaration). -That is: they automatically remember where they were created using a hidden `[[Environment]]` property, and all of them can access outer variables. +That's why we can use a function, declared as Function Declaration, even before the declaration itself. -When on an interview, a frontend developer gets a question about "what's a closure?", a valid answer would be a definition of the closure and an explanation that all functions in JavaScript are closures, and maybe a few more words about technical details: the `[[Environment]]` property and how Lexical Environments work. -``` +For example, here's the initial state of the global Lexical Environment when we add a function: -## Code blocks and loops, IIFE +![](closure-function-declaration.svg) -The examples above concentrated on functions. But a Lexical Environment exists for any code block `{...}`. +Naturally, this behavior only applies to Function Declarations, not Function Expressions where we assign a function to a variable, such as `let say = function(name)...`. -A Lexical Environment is created when a code block runs and contains block-local variables. Here are a couple of examples. +### Step 3. Inner and outer Lexical Environment -### If +When a function runs, at the beginning of the call, a new Lexical Environment is created automatically to store local variables and parameters of the call. -In the example below, the `user` variable exists only in the `if` block: +For instance, for `say("John")`, it looks like this (the execution is at the line, labelled with an arrow): -![](lexenv-if.svg) - -When the execution gets into the `if` block, the new "if-only" Lexical Environment is created for it. - -It has the reference to the outer one, so `phrase` can be found. But all variables and Function Expressions, declared inside `if`, reside in that Lexical Environment and can't be seen from the outside. - -For instance, after `if` finishes, the `alert` below won't see the `user`, hence the error. +![](lexical-environment-simple.svg) -### For, while +During the function call we have two Lexical Environments: the inner one (for the function call) and the outer one (global): -For a loop, every iteration has a separate Lexical Environment. If a variable is declared in `for(let ...)`, then it's also in there: +- The inner Lexical Environment corresponds to the current execution of `say`. It has a single property: `name`, the function argument. We called `say("John")`, so the value of the `name` is `"John"`. +- The outer Lexical Environment is the global Lexical Environment. It has the `phrase` variable and the function itself. -```js run -for (let i = 0; i < 10; i++) { - // Each loop has its own Lexical Environment - // {i: value} -} +The inner Lexical Environment has a reference to the `outer` one. -alert(i); // Error, no such variable -``` +**When the code wants to access a variable -- the inner Lexical Environment is searched first, then the outer one, then the more outer one and so on until the global one.** -Please note: `let i` is visually outside of `{...}`. The `for` construct is special here: each iteration of the loop has its own Lexical Environment with the current `i` in it. +If a variable is not found anywhere, that's an error in strict mode (without `use strict`, an assignment to a non-existing variable creates a new global variable, for compatibility with old code). -Again, similarly to `if`, after the loop `i` is not visible. +In this example the search proceeds as follows: -### Code blocks +- For the `name` variable, the `alert` inside `say` finds it immediately in the inner Lexical Environment. +- When it wants to access `phrase`, then there is no `phrase` locally, so it follows the reference to the outer Lexical Environment and finds it there. -We also can use a "bare" code block `{…}` to isolate variables into a "local scope". +![lexical environment lookup](lexical-environment-simple-lookup.svg) -For instance, in a web browser all scripts (except with `type="module"`) share the same global area. So if we create a global variable in one script, it becomes available to others. But that becomes a source of conflicts if two scripts use the same variable name and overwrite each other. -That may happen if the variable name is a widespread word, and script authors are unaware of each other. +### Step 4. Returning a function -If we'd like to avoid that, we can use a code block to isolate the whole script or a part of it: +Let's return to the `makeCounter` example. -```js run -{ - // do some job with local variables that should not be seen outside - - let message = "Hello"; +```js +function makeCounter() { + let count = 0; - alert(message); // Hello + return function() { + return count++; + }; } -alert(message); // Error: message is not defined +let counter = makeCounter(); ``` -The code outside of the block (or inside another script) doesn't see variables inside the block, because the block has its own Lexical Environment. +At the beginning of each `makeCounter()` call, a new Lexical Environment object is created, to store variables for this `makeCounter` run. -### IIFE +So we have two nested Lexical Environments, just like in the example above: -In the past, there were no block-level lexical environments in JavaScript. +![](closure-makecounter.svg) -So programmers had to invent something. And what they did was called "immediately-invoked function expressions" (abbreviated as IIFE). +What's different is that, during the execution of `makeCounter()`, a tiny nested function is created of only one line: `return count++`. We don't run it yet, only create. -That's not a thing we should use nowadays, but you can find them in old scripts, so it's better to understand them. +All functions remember the Lexical Environment in which they were made. Technically, there's no magic here: all functions have the hidden property named `[[Environment]]`, that keeps the reference to the Lexical Environment where the function was created: -An IIFE looks like this: +![](closure-makecounter-environment.svg) -```js run -(function() { +So, `counter.[[Environment]]` has the reference to `{count: 0}` Lexical Environment. That's how the function remembers where it was created, no matter where it's called. The `[[Environment]]` reference is set once and forever at function creation time. - let message = "Hello"; +Later, when `counter()` is called, a new Lexical Environment is created for the call, and its outer Lexical Environment reference is taken from `counter.[[Environment]]`: - alert(message); // Hello +![](closure-makecounter-nested-call.svg) -})(); -``` +Now when the code inside `counter()` looks for `count` variable, it first searches its own Lexical Environment (empty, as there are no local variables there), then the Lexical Environment of the outer `makeCounter()` call, where finds it and changes. -Here a Function Expression is created and immediately called. So the code executes right away and has its own private variables. +**A variable is updated in the Lexical Environment where it lives.** -The Function Expression is wrapped with parenthesis `(function {...})`, because when JavaScript meets `"function"` in the main code flow, it understands it as the start of a Function Declaration. But a Function Declaration must have a name, so this kind of code will give an error: +Here's the state after the execution: -```js run -// Try to declare and immediately call a function -function() { // <-- Error: Unexpected token ( +![](closure-makecounter-nested-call-2.svg) - let message = "Hello"; +If we call `counter()` multiple times, the `count` variable will be increased to `2`, `3` and so on, at the same place. - alert(message); // Hello - -}(); -``` - -Even if we say: "okay, let's add a name", that won't work, as JavaScript does not allow Function Declarations to be called immediately: - -```js run -// syntax error because of parentheses below -function go() { - -}(); // <-- can't call Function Declaration immediately -``` - -So, the parentheses around the function is a trick to show JavaScript that the function is created in the context of another expression, and hence it's a Function Expression: it needs no name and can be called immediately. - -There exist other ways besides parentheses to tell JavaScript that we mean a Function Expression: - -```js run -// Ways to create IIFE - -(function() { - alert("Parentheses around the function"); -}*!*)*/!*(); +```smart header="Closure" +There is a general programming term "closure", that developers generally should know. -(function() { - alert("Parentheses around the whole thing"); -}()*!*)*/!*; +A [closure](https://en.wikipedia.org/wiki/Closure_(computer_programming)) is a function that remembers its outer variables and can access them. In some languages, that's not possible, or a function should be written in a special way to make it happen. But as explained above, in JavaScript, all functions are naturally closures (there is only one exception, to be covered in ). -*!*!*/!*function() { - alert("Bitwise NOT operator starts the expression"); -}(); +That is: they automatically remember where they were created using a hidden `[[Environment]]` property, and then their code can access outer variables. -*!*+*/!*function() { - alert("Unary plus starts the expression"); -}(); +When on an interview, a frontend developer gets a question about "what's a closure?", a valid answer would be a definition of the closure and an explanation that all functions in JavaScript are closures, and maybe a few more words about technical details: the `[[Environment]]` property and how Lexical Environments work. ``` -In all the above cases we declare a Function Expression and run it immediately. Let's note again: nowadays there's no reason to write such code. - ## Garbage collection -Usually, a Lexical Environment is cleaned up and deleted after the function runs. For instance: +Usually, a Lexical Environment is removed from memory with all the variables after the function call finishes. That's because there are no references to it. As any JavaScript object, it's only kept in memory while it's reachable. -```js -function f() { - let value1 = 123; - let value2 = 456; -} - -f(); -``` +...But if there's a nested function that is still reachable after the end of a function, then it has `[[Environment]]` property that references the lexical environment. -Here, two values are technically the properties of the Lexical Environment. But after `f()` finishes, that Lexical Environment becomes unreachable, so it's deleted from the memory. +In that case the Lexical Environment is still reachable even after the completion of the function, so it stays alive. -...But if there's a nested function that is still reachable after the end of `f`, then it has `[[Environment]]` property that references the outer lexical environment, so it's also reachable and alive: +For example: ```js function f() { let value = 123; - function g() { alert(value); } - -*!* - return g; -*/!* + return function() { + alert(value); + } } -let func = f(); // func gets a reference to g -// so it stays and memory and its outer lexical environment stays as well +let g = f(); // g.[[Environment]] stores a reference to the Lexical Environment +// of the corresponding f() call ``` Please note that if `f()` is called many times, and resulting functions are saved, then all corresponding Lexical Environment objects will also be retained in memory. All 3 of them in the code below: @@ -585,20 +345,20 @@ let arr = [f(), f(), f()]; A Lexical Environment object dies when it becomes unreachable (just like any other object). In other words, it exists only while there's at least one nested function referencing it. -In the code below, after `g` becomes unreachable, its enclosing Lexical Environment (and hence the `value`) is cleaned from memory; +In the code below, after the nested function is removed, its enclosing Lexical Environment (and hence the `value`) is cleaned from memory; ```js function f() { let value = 123; - function g() { alert(value); } - - return g; + return function() { + alert(value); + } } -let func = f(); // while func has a reference to g, it stays in memory +let g = f(); // while g function exists, the value stays in memory -func = null; // ...and now the memory is cleaned up +g = null; // ...and now the memory is cleaned up ``` ### Real-life optimizations @@ -649,9 +409,6 @@ let g = f(); 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 in the debugger, but rather a special feature of V8. Perhaps it will be changed sometime. -You always can check for it by running the examples on this page. -``` +That is not a bug in the debugger, but rather a special feature of V8. Perhaps it will be changed sometime. You always can check for it by running the examples on this page. diff --git a/1-js/06-advanced-functions/03-closure/closure-function-declaration.svg b/1-js/06-advanced-functions/03-closure/closure-function-declaration.svg new file mode 100644 index 000000000..97f76e569 --- /dev/null +++ b/1-js/06-advanced-functions/03-closure/closure-function-declaration.svg @@ -0,0 +1 @@ +outernullexecution startphrase: <uninitialized> say: function... \ No newline at end of file diff --git a/1-js/06-advanced-functions/03-closure/closure-makecounter-environment.svg b/1-js/06-advanced-functions/03-closure/closure-makecounter-environment.svg new file mode 100644 index 000000000..b9060bc8a --- /dev/null +++ b/1-js/06-advanced-functions/03-closure/closure-makecounter-environment.svg @@ -0,0 +1 @@ +null[[Environment]]makeCounter: function counter: undefinedcount: 0outerouter \ No newline at end of file diff --git a/1-js/06-advanced-functions/03-closure/closure-makecounter-nested-call-2.svg b/1-js/06-advanced-functions/03-closure/closure-makecounter-nested-call-2.svg new file mode 100644 index 000000000..3e4206ca6 --- /dev/null +++ b/1-js/06-advanced-functions/03-closure/closure-makecounter-nested-call-2.svg @@ -0,0 +1 @@ +count: 1<empty>nullouterouteroutermakeCounter: function counter: functionmodified here \ No newline at end of file diff --git a/1-js/06-advanced-functions/03-closure/closure-makecounter-nested-call.svg b/1-js/06-advanced-functions/03-closure/closure-makecounter-nested-call.svg new file mode 100644 index 000000000..e1bb8cc8f --- /dev/null +++ b/1-js/06-advanced-functions/03-closure/closure-makecounter-nested-call.svg @@ -0,0 +1 @@ +count: 0<empty>nullouterouteroutermakeCounter: function counter: function \ No newline at end of file diff --git a/1-js/06-advanced-functions/03-closure/closure-makecounter.svg b/1-js/06-advanced-functions/03-closure/closure-makecounter.svg new file mode 100644 index 000000000..2a1c4a729 --- /dev/null +++ b/1-js/06-advanced-functions/03-closure/closure-makecounter.svg @@ -0,0 +1 @@ +makeCounter: function counter: undefinedcount: 0nullglobal LexicalEnvironmentLexicalEnvironment of makeCounter() callouterouter \ No newline at end of file diff --git a/1-js/06-advanced-functions/03-closure/closure-variable-phrase.svg b/1-js/06-advanced-functions/03-closure/closure-variable-phrase.svg new file mode 100644 index 000000000..741c05448 --- /dev/null +++ b/1-js/06-advanced-functions/03-closure/closure-variable-phrase.svg @@ -0,0 +1 @@ +phrase: "Bye"phrase: "Hello"phrase: undefinedphrase: <uninitialized>outernullexecution start \ No newline at end of file diff --git a/1-js/06-advanced-functions/03-closure/lexenv-if.svg b/1-js/06-advanced-functions/03-closure/lexenv-if.svg index 48dd7d1bf..b644fe154 100644 --- a/1-js/06-advanced-functions/03-closure/lexenv-if.svg +++ b/1-js/06-advanced-functions/03-closure/lexenv-if.svg @@ -1 +1 @@ -phrase: "Hello"outerouternulluser: "John" \ No newline at end of file +phrase: "Hello"outerouternulluser: "John" \ No newline at end of file diff --git a/1-js/06-advanced-functions/03-closure/lexenv-nested-makecounter-2.svg b/1-js/06-advanced-functions/03-closure/lexenv-nested-makecounter-2.svg index f70fb41f0..66e5200f8 100644 --- a/1-js/06-advanced-functions/03-closure/lexenv-nested-makecounter-2.svg +++ b/1-js/06-advanced-functions/03-closure/lexenv-nested-makecounter-2.svg @@ -1 +1 @@ -makeCounter: functioncounter: undefinedcount: 0outerouternullglobal LexicalEnvironmentLexicalEnvironment of makeCounter() call \ No newline at end of file +makeCounter: functioncounter: undefinedcount: 0outerouternullglobal LexicalEnvironmentLexicalEnvironment of makeCounter() call \ No newline at end of file diff --git a/1-js/06-advanced-functions/03-closure/lexenv-nested-makecounter-3.svg b/1-js/06-advanced-functions/03-closure/lexenv-nested-makecounter-3.svg index 9a3298ca3..28c526c4f 100644 --- a/1-js/06-advanced-functions/03-closure/lexenv-nested-makecounter-3.svg +++ b/1-js/06-advanced-functions/03-closure/lexenv-nested-makecounter-3.svg @@ -1 +1 @@ -makeCounter: functioncounter: undefinedcount: 0outerouternull[[Environment]] \ No newline at end of file +makeCounter: functioncounter: undefinedcount: 0outerouternull[[Environment]] \ No newline at end of file diff --git a/1-js/06-advanced-functions/03-closure/lexenv-nested-makecounter-4.svg b/1-js/06-advanced-functions/03-closure/lexenv-nested-makecounter-4.svg index 02d8a93fb..acc1e8fb9 100644 --- a/1-js/06-advanced-functions/03-closure/lexenv-nested-makecounter-4.svg +++ b/1-js/06-advanced-functions/03-closure/lexenv-nested-makecounter-4.svg @@ -1 +1 @@ -makeCounter: functioncounter: functioncount: 0outerouternull[[Environment]] \ No newline at end of file +makeCounter: functioncounter: functioncount: 0outerouternull[[Environment]] \ No newline at end of file diff --git a/1-js/06-advanced-functions/03-closure/lexenv-nested-makecounter-5.svg b/1-js/06-advanced-functions/03-closure/lexenv-nested-makecounter-5.svg index 6c8f66495..cf91c331d 100644 --- a/1-js/06-advanced-functions/03-closure/lexenv-nested-makecounter-5.svg +++ b/1-js/06-advanced-functions/03-closure/lexenv-nested-makecounter-5.svg @@ -1 +1 @@ -makeCounter: functioncounter: functioncount: 0<empty>outerouterouternull[[Environment]] \ No newline at end of file +makeCounter: functioncounter: functioncount: 0<empty>outerouterouternull[[Environment]] \ No newline at end of file diff --git a/1-js/06-advanced-functions/03-closure/lexenv-nested-makecounter-6.svg b/1-js/06-advanced-functions/03-closure/lexenv-nested-makecounter-6.svg index 3b0ac9d0c..def542ceb 100644 --- a/1-js/06-advanced-functions/03-closure/lexenv-nested-makecounter-6.svg +++ b/1-js/06-advanced-functions/03-closure/lexenv-nested-makecounter-6.svg @@ -1 +1 @@ -makeCounter: functioncounter: functioncount: 1outerouternull[[Environment]]modified here \ No newline at end of file +makeCounter: functioncounter: functioncount: 1outerouternull[[Environment]]modified here \ No newline at end of file diff --git a/1-js/06-advanced-functions/03-closure/lexical-environment-global.svg b/1-js/06-advanced-functions/03-closure/lexical-environment-global.svg index aac8c5927..9620f0485 100644 --- a/1-js/06-advanced-functions/03-closure/lexical-environment-global.svg +++ b/1-js/06-advanced-functions/03-closure/lexical-environment-global.svg @@ -1 +1 @@ -phrase: "Hello"outernullLexicalEnvironment \ No newline at end of file +phrase: "Hello"outernullLexical Environment \ No newline at end of file diff --git a/1-js/06-advanced-functions/03-closure/lexical-environment-simple-lookup.svg b/1-js/06-advanced-functions/03-closure/lexical-environment-simple-lookup.svg index 40e2b92ad..ff0486ede 100644 --- a/1-js/06-advanced-functions/03-closure/lexical-environment-simple-lookup.svg +++ b/1-js/06-advanced-functions/03-closure/lexical-environment-simple-lookup.svg @@ -1 +1 @@ -say: function phrase: "Hello"name: "John"outerouternull \ No newline at end of file +say: function phrase: "Hello"name: "John"outerouternull \ No newline at end of file diff --git a/1-js/06-advanced-functions/03-closure/lexical-environment-simple.svg b/1-js/06-advanced-functions/03-closure/lexical-environment-simple.svg index a962f0e5f..abd77fff9 100644 --- a/1-js/06-advanced-functions/03-closure/lexical-environment-simple.svg +++ b/1-js/06-advanced-functions/03-closure/lexical-environment-simple.svg @@ -1 +1 @@ -say: function phrase: "Hello"name: "John"outerouternullLexicalEnvironment for the call \ No newline at end of file +say: function phrase: "Hello"name: "John"outerouternullLexical Environment of the call \ No newline at end of file diff --git a/1-js/06-advanced-functions/03-closure/lexical-search-order.svg b/1-js/06-advanced-functions/03-closure/lexical-search-order.svg index 44db4b0c1..89a9d110a 100644 --- a/1-js/06-advanced-functions/03-closure/lexical-search-order.svg +++ b/1-js/06-advanced-functions/03-closure/lexical-search-order.svg @@ -1 +1 @@ -123 \ No newline at end of file +123 \ No newline at end of file diff --git a/1-js/06-advanced-functions/03-closure/variable-scope-lookup.svg b/1-js/06-advanced-functions/03-closure/variable-scope-lookup.svg new file mode 100644 index 000000000..674437196 --- /dev/null +++ b/1-js/06-advanced-functions/03-closure/variable-scope-lookup.svg @@ -0,0 +1 @@ +functionUser(name){this.sayHi=function(){alert(name);};}letuser=newUser("John");user.sayHi(); \ No newline at end of file diff --git a/1-js/06-advanced-functions/04-var/article.md b/1-js/06-advanced-functions/04-var/article.md index 02fd43fe5..ab8068f8b 100644 --- a/1-js/06-advanced-functions/04-var/article.md +++ b/1-js/06-advanced-functions/04-var/article.md @@ -1,6 +1,12 @@ # The old "var" +```smart header="This article is for understanding old scripts" +The information in this article is useful for understanding old scripts. + +That's not how we write a new code. +``` + In the very first chapter about [variables](info:variables), we mentioned three ways of variable declaration: 1. `let` @@ -188,6 +194,74 @@ Because all `var` declarations are processed at the function start, we can refer In both examples above `alert` runs without an error, because the variable `phrase` exists. But its value is not yet assigned, so it shows `undefined`. +### IIFE + +As in the past there was only `var`, and it has no block-level visibility, programmers invented a way to emulate it. What they did was called "immediately-invoked function expressions" (abbreviated as IIFE). + +That's not something we should use nowadays, but you can find them in old scripts. + +An IIFE looks like this: + +```js run +(function() { + + let message = "Hello"; + + alert(message); // Hello + +})(); +``` + +Here a Function Expression is created and immediately called. So the code executes right away and has its own private variables. + +The Function Expression is wrapped with parenthesis `(function {...})`, because when JavaScript meets `"function"` in the main code flow, it understands it as the start of a Function Declaration. But a Function Declaration must have a name, so this kind of code will give an error: + +```js run +// Try to declare and immediately call a function +function() { // <-- Error: Unexpected token ( + + let message = "Hello"; + + alert(message); // Hello + +}(); +``` + +Even if we say: "okay, let's add a name", that won't work, as JavaScript does not allow Function Declarations to be called immediately: + +```js run +// syntax error because of parentheses below +function go() { + +}(); // <-- can't call Function Declaration immediately +``` + +So, the parentheses around the function is a trick to show JavaScript that the function is created in the context of another expression, and hence it's a Function Expression: it needs no name and can be called immediately. + +There exist other ways besides parentheses to tell JavaScript that we mean a Function Expression: + +```js run +// Ways to create IIFE + +(function() { + alert("Parentheses around the function"); +}*!*)*/!*(); + +(function() { + alert("Parentheses around the whole thing"); +}()*!*)*/!*; + +*!*!*/!*function() { + alert("Bitwise NOT operator starts the expression"); +}(); + +*!*+*/!*function() { + alert("Unary plus starts the expression"); +}(); +``` + +In all the above cases we declare a Function Expression and run it immediately. Let's note again: nowadays there's no reason to write such code. + ## Summary There are two main differences of `var` compared to `let/const`: @@ -195,6 +269,6 @@ There are two main differences of `var` compared to `let/const`: 1. `var` variables have no block scope, they are visible minimum at the function level. 2. `var` declarations are processed at function start (script start for globals). -There's one more minor difference related to the global object, we'll cover that in the next chapter. +There's one more very minor difference related to the global object, that we'll cover in the next chapter. These differences make `var` worse than `let` most of the time. Block-level variables is such a great thing. That's why `let` was introduced in the standard long ago, and is now a major way (along with `const`) to declare a variable. 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 373500e13..f9bc89169 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 @@ -149,8 +149,8 @@ let user = { name: "John" }; let admin = { name: "Admin" }; // use call to pass different objects as "this" -sayHi.call( user ); // this = John -sayHi.call( admin ); // this = Admin +sayHi.call( user ); // John +sayHi.call( admin ); // Admin ``` And here we use `call` to call `say` with the given context and phrase: @@ -299,13 +299,13 @@ The only syntax difference between `call` and `apply` is that `call` expects a l So these two calls are almost equivalent: ```js -func.call(context, ...args); // pass an array as list with spread operator +func.call(context, ...args); // pass an array as list with spread syntax func.apply(context, args); // is same as using apply ``` There's only a minor difference: -- The spread operator `...` allows to pass *iterable* `args` as the list to `call`. +- The spread syntax `...` allows to pass *iterable* `args` as the list to `call`. - The `apply` accepts only *array-like* `args`. So, these calls complement each other. Where we expect an iterable, `call` works, where we expect an array-like, `apply` works. diff --git a/1-js/06-advanced-functions/10-bind/article.md b/1-js/06-advanced-functions/10-bind/article.md index 16a50942d..787c7d68e 100644 --- a/1-js/06-advanced-functions/10-bind/article.md +++ b/1-js/06-advanced-functions/10-bind/article.md @@ -279,7 +279,7 @@ What if we'd like to fix some arguments, but not the context `this`? For example The native `bind` does not allow that. We can't just omit the context and jump to arguments. -Fortunately, a helper function `partial` for binding only arguments can be easily implemented. +Fortunately, a function `partial` for binding only arguments can be easily implemented. Like this: @@ -313,7 +313,7 @@ The result of `partial(func[, arg1, arg2...])` call is a wrapper `(*)` that call - Then gives it `...argsBound` -- arguments from the `partial` call (`"10:00"`) - Then gives it `...args` -- arguments given to the wrapper (`"Hello"`) -So easy to do it with the spread operator, right? +So easy to do it with the spread syntax, right? Also there's a ready [_.partial](https://lodash.com/docs#partial) implementation from lodash library. diff --git a/1-js/07-object-properties/01-property-descriptors/article.md b/1-js/07-object-properties/01-property-descriptors/article.md index e894f0662..3593bffae 100644 --- a/1-js/07-object-properties/01-property-descriptors/article.md +++ b/1-js/07-object-properties/01-property-descriptors/article.md @@ -66,7 +66,7 @@ Object.defineProperty(obj, propertyName, descriptor) : The object and its property to apply the descriptor. `descriptor` -: Property descriptor to apply. +: Property descriptor object to apply. If the property exists, `defineProperty` updates its flags. Otherwise, it creates the property with the given value and flags; in that case, if a flag is not supplied, it is assumed `false`. diff --git a/1-js/07-object-properties/02-property-accessors/article.md b/1-js/07-object-properties/02-property-accessors/article.md index 726529c5b..52c152811 100644 --- a/1-js/07-object-properties/02-property-accessors/article.md +++ b/1-js/07-object-properties/02-property-accessors/article.md @@ -94,11 +94,7 @@ alert(user.name); // Alice alert(user.surname); // Cooper ``` -As the result, we have a "virtual" property `fullName`. It is readable and writable, but in fact does not exist. - -```smart header="No way to handle `delete`" -There's no similar method to handle deletion of an accessor property. Only getter/setter methods may exist. -``` +As the result, we have a "virtual" property `fullName`. It is readable and writable. ## Accessor descriptors diff --git a/1-js/08-prototypes/03-native-prototypes/native-prototypes-classes.svg b/1-js/08-prototypes/03-native-prototypes/native-prototypes-classes.svg index caa5ec04b..36cc81cd9 100644 --- a/1-js/08-prototypes/03-native-prototypes/native-prototypes-classes.svg +++ b/1-js/08-prototypes/03-native-prototypes/native-prototypes-classes.svg @@ -1 +1 @@ -toString: function other object methodsObject.prototypenullslice: function other array methods[[Prototype]][[Prototype]][[Prototype]][[Prototype]][[Prototype]][[Prototype]][[Prototype]]Array.prototypecall: function other function methodsFunction.prototypetoFixed: function other number methodsNumber.prototype[1, 2, 3]function f(args) { ... }5 \ No newline at end of file +toString: function other object methodsObject.prototypenullslice: function other array methods[[Prototype]][[Prototype]][[Prototype]][[Prototype]][[Prototype]][[Prototype]][[Prototype]]Array.prototypecall: function other function methodsFunction.prototypetoFixed: function other number methodsNumber.prototype[1, 2, 3]function f(args) { ... }5 \ No newline at end of file diff --git a/1-js/09-classes/02-class-inheritance/animal-rabbit-extends.svg b/1-js/09-classes/02-class-inheritance/animal-rabbit-extends.svg index 2f994f7b7..3471904ab 100644 --- a/1-js/09-classes/02-class-inheritance/animal-rabbit-extends.svg +++ b/1-js/09-classes/02-class-inheritance/animal-rabbit-extends.svg @@ -1 +1 @@ -constructor: Animal run: function stop: functionAnimal.prototypeconstructor: Rabbit hide: functionRabbit.prototypeAnimalRabbitnew Rabbit[[Prototype]][[Prototype]]prototypeprototypename: "White Rabbit"constructorconstructorextends \ No newline at end of file +constructor: Animal run: function stop: functionAnimal.prototypeconstructor: Rabbit hide: functionRabbit.prototypeAnimalRabbitnew Rabbit[[Prototype]][[Prototype]]prototypeprototypename: "White Rabbit"constructorconstructorextends \ No newline at end of file diff --git a/1-js/09-classes/02-class-inheritance/article.md b/1-js/09-classes/02-class-inheritance/article.md index 1000fdd84..f031b4163 100644 --- a/1-js/09-classes/02-class-inheritance/article.md +++ b/1-js/09-classes/02-class-inheritance/article.md @@ -499,7 +499,7 @@ In the example below a non-method syntax is used for comparison. `[[HomeObject]] ```js run let animal = { - eat: function() { // intentially writing like this instead of eat() {... + eat: function() { // intentionally writing like this instead of eat() {... // ... } }; diff --git a/1-js/09-classes/02-class-inheritance/super-homeobject-wrong.svg b/1-js/09-classes/02-class-inheritance/super-homeobject-wrong.svg index 8ad09f484..f13d441c9 100644 --- a/1-js/09-classes/02-class-inheritance/super-homeobject-wrong.svg +++ b/1-js/09-classes/02-class-inheritance/super-homeobject-wrong.svg @@ -1 +1 @@ -sayHiplantsayHitreesayHianimalrabbit[[HomeObject]]sayHi \ No newline at end of file +sayHiplantsayHitreesayHianimalrabbit[[HomeObject]]sayHi \ No newline at end of file diff --git a/1-js/09-classes/03-static-properties-methods/article.md b/1-js/09-classes/03-static-properties-methods/article.md index dd09a0262..ab08f2ded 100644 --- a/1-js/09-classes/03-static-properties-methods/article.md +++ b/1-js/09-classes/03-static-properties-methods/article.md @@ -20,7 +20,7 @@ User.staticMethod(); // true That actually does the same as assigning it as a property directly: ```js run -class User() { } +class User { } User.staticMethod = function() { alert(this === User); diff --git a/1-js/09-classes/04-private-protected-properties-methods/article.md b/1-js/09-classes/04-private-protected-properties-methods/article.md index 6d77e8ed7..60ed0ef1b 100644 --- a/1-js/09-classes/04-private-protected-properties-methods/article.md +++ b/1-js/09-classes/04-private-protected-properties-methods/article.md @@ -279,7 +279,7 @@ With private fields that's impossible: `this['#name']` doesn't work. That's a sy ## Summary -In terms of OOP, delimiting of the internal interface from the external one is called [encapsulation]("https://en.wikipedia.org/wiki/Encapsulation_(computer_programming)"). +In terms of OOP, delimiting of the internal interface from the external one is called [encapsulation](https://en.wikipedia.org/wiki/Encapsulation_(computer_programming)). It gives the following benefits: diff --git a/1-js/09-classes/06-instanceof/instanceof.svg b/1-js/09-classes/06-instanceof/instanceof.svg index 8b63f2070..78bff9f12 100644 --- a/1-js/09-classes/06-instanceof/instanceof.svg +++ b/1-js/09-classes/06-instanceof/instanceof.svg @@ -1 +1 @@ -Animal.prototypeObject.prototypeRabbit.prototype[[Prototype]]rabbit[[Prototype]][[Prototype]]null[[Prototype]]= Animal.prototype? \ No newline at end of file +Animal.prototypeObject.prototypeRabbit.prototype[[Prototype]]rabbit[[Prototype]][[Prototype]]null[[Prototype]]= Animal.prototype? \ No newline at end of file diff --git a/1-js/10-error-handling/1-try-catch/article.md b/1-js/10-error-handling/1-try-catch/article.md index 09b1ee398..149a95f1f 100644 --- a/1-js/10-error-handling/1-try-catch/article.md +++ b/1-js/10-error-handling/1-try-catch/article.md @@ -298,7 +298,7 @@ try { *!* alert(e.name); // SyntaxError */!* - alert(e.message); // Unexpected token o in JSON at position 2 + alert(e.message); // Unexpected token b in JSON at position 2 } ``` diff --git a/1-js/10-error-handling/1-try-catch/try-catch-flow.svg b/1-js/10-error-handling/1-try-catch/try-catch-flow.svg index ab17dcda0..ac816e356 100644 --- a/1-js/10-error-handling/1-try-catch/try-catch-flow.svg +++ b/1-js/10-error-handling/1-try-catch/try-catch-flow.svg @@ -1 +1 @@ -BeginNo ErrorsAn error occured in the codeIgnore catch blockIgnore the rest of tryExecute catch blocktry { }// code... \ No newline at end of file +BeginNo ErrorsAn error occured in the codeIgnore catch blockIgnore the rest of tryExecute catch blocktry { }// code... \ No newline at end of file diff --git a/1-js/11-async/01-callbacks/article.md b/1-js/11-async/01-callbacks/article.md index daab93316..a98851ac0 100644 --- a/1-js/11-async/01-callbacks/article.md +++ b/1-js/11-async/01-callbacks/article.md @@ -2,10 +2,12 @@ # Introduction: callbacks -```warn header="We use browser methods here" -To demonstrate the use of callbacks, promises and other abstract concepts, we'll be using some browser methods; specifically, loading scripts and performing simple document manipulations. +```warn header="We use browser methods in examples here" +To demonstrate the use of callbacks, promises and other abstract concepts, we'll be using some browser methods: specifically, loading scripts and performing simple document manipulations. -If you're not familiar with these methods, and their usage in the examples is confusing, or if you would just like to understand them better, you may want to read a few chapters from the [next part](/document) of the tutorial. +If you're not familiar with these methods, and their usage in the examples is confusing, you may want to read a few chapters from the [next part](/document) of the tutorial. + +Although, we'll try to make things clear anyway. There won't be anything really complex browser-wise. ``` Many actions in JavaScript are *asynchronous*. In other words, we initiate them now, but they finish later. @@ -18,13 +20,15 @@ Take a look at the function `loadScript(src)`, that loads a script with the give ```js function loadScript(src) { + // creates a - - - + table.tBodies[0].append(...sortedRows); + diff --git a/2-ui/1-document/07-modifying-document/12-sort-table/source.view/index.html b/2-ui/1-document/07-modifying-document/12-sort-table/source.view/index.html index e41eb229f..9071c88ee 100644 --- a/2-ui/1-document/07-modifying-document/12-sort-table/source.view/index.html +++ b/2-ui/1-document/07-modifying-document/12-sort-table/source.view/index.html @@ -1,33 +1,27 @@ - - - - - - - - - - - - - - - - - - - - - - - -
NameSurnameAge
JohnSmith10
PeteBrown15
AnnLee5
+ + + + + + + + + + + + + + + + + + + + +
NameSurnameAge
JohnSmith10
PeteBrown15
AnnLee5
.........
- - - - + diff --git a/2-ui/1-document/07-modifying-document/12-sort-table/task.md b/2-ui/1-document/07-modifying-document/12-sort-table/task.md index 41d6fca29..7cdba35bc 100644 --- a/2-ui/1-document/07-modifying-document/12-sort-table/task.md +++ b/2-ui/1-document/07-modifying-document/12-sort-table/task.md @@ -6,33 +6,29 @@ importance: 5 There's a table: +```html run - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + +
NameSurnameAge
JohnSmith10
PeteBrown15
AnnLee5
.........
NameSurnameAge
JohnSmith10
PeteBrown15
AnnLee5
.........
+``` There may be more rows in it. diff --git a/2-ui/1-document/07-modifying-document/article.md b/2-ui/1-document/07-modifying-document/article.md index b624da47c..c4796a1d4 100644 --- a/2-ui/1-document/07-modifying-document/article.md +++ b/2-ui/1-document/07-modifying-document/article.md @@ -540,7 +540,7 @@ So if we need to add a lot of text into HTML dynamically, and we're at page load - `"beforeend"` -- insert `html` into `elem`, at the end, - `"afterend"` -- insert `html` right after `elem`. - Also there are also similar methods, `elem.insertAdjacentText` and `elem.insertAdjacentElement`, that insert text strings and elements, but they are rarely used. + Also there are similar methods, `elem.insertAdjacentText` and `elem.insertAdjacentElement`, that insert text strings and elements, but they are rarely used. - To append HTML to the page before it has finished loading: - `document.write(html)` diff --git a/2-ui/1-document/07-modifying-document/before-prepend-append-after.svg b/2-ui/1-document/07-modifying-document/before-prepend-append-after.svg index 1811adea7..6e1fb4874 100644 --- a/2-ui/1-document/07-modifying-document/before-prepend-append-after.svg +++ b/2-ui/1-document/07-modifying-document/before-prepend-append-after.svg @@ -1 +1 @@ -ol.afterol.appendol.prependol.before(…nodes or strings) \ No newline at end of file +ol.afterol.appendol.prependol.before(…nodes or strings) \ No newline at end of file diff --git a/2-ui/1-document/07-modifying-document/insert-adjacent.svg b/2-ui/1-document/07-modifying-document/insert-adjacent.svg index 4b247fd72..64beee037 100644 --- a/2-ui/1-document/07-modifying-document/insert-adjacent.svg +++ b/2-ui/1-document/07-modifying-document/insert-adjacent.svg @@ -1 +1 @@ -ol.insertAdjacentHTML(*, html)afterendbeforeendafterbeginbeforebegin \ No newline at end of file +ol.insertAdjacentHTML(*, html)afterendbeforeendafterbeginbeforebegin \ No newline at end of file diff --git a/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/field.svg b/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/field.svg index f5562f97a..ca8bbc3bd 100644 --- a/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/field.svg +++ b/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/field.svg @@ -1 +1 @@ -(0,0)clientWidth \ No newline at end of file +(0,0)clientWidth \ No newline at end of file diff --git a/2-ui/1-document/09-size-and-scroll/article.md b/2-ui/1-document/09-size-and-scroll/article.md index 102f9c585..024e9a4d9 100644 --- a/2-ui/1-document/09-size-and-scroll/article.md +++ b/2-ui/1-document/09-size-and-scroll/article.md @@ -268,7 +268,7 @@ Elements have the following geometry properties: - `offsetParent` -- is the nearest positioned ancestor or `td`, `th`, `table`, `body`. - `offsetLeft/offsetTop` -- coordinates relative to the upper-left edge of `offsetParent`. - `offsetWidth/offsetHeight` -- "outer" width/height of an element including borders. -- `clientLeft/clientTop` -- the distance from the upper-left outer corner the inner corner. For left-to-right OS they are always the widths of left/top borders. For right-to-left OS the vertical scrollbar is on the left so `clientLeft` includes its width too. +- `clientLeft/clientTop` -- the distances from the upper-left outer corner to the upper-left inner (content + padding) corner. For left-to-right OS they are always the widths of left/top borders. For right-to-left OS the vertical scrollbar is on the left so `clientLeft` includes its width too. - `clientWidth/clientHeight` -- the width/height of the content including paddings, but without the scrollbar. - `scrollWidth/scrollHeight` -- the width/height of the content, just like `clientWidth/clientHeight`, but also include scrolled-out, invisible part of the element. - `scrollLeft/scrollTop` -- width/height of the scrolled out upper part of the element, starting from its upper-left corner. diff --git a/2-ui/1-document/09-size-and-scroll/metric-all.svg b/2-ui/1-document/09-size-and-scroll/metric-all.svg index 273b38528..3c77f09ac 100644 --- a/2-ui/1-document/09-size-and-scroll/metric-all.svg +++ b/2-ui/1-document/09-size-and-scroll/metric-all.svg @@ -1 +1 @@ -Introduction This Ecma Standard is based on several originating technologies, the most well known being JavaScript (Netscape) and JScript (Microsoft). The language was invented by Brendan Eich at Netscape and first appeared in that company’s Navigator 2.0 browser. It has appeared in all subsequent browsers from Netscape and in all browsers from Microsoft starting with Internet Explorer 3.0. The development of this Standard started in November 1996. The first edition of this Ecma Standard was adopted by the Ecma General Assembly of June 1997. That Ecma Standard was submitted to ISO/ IEC JTC 1 for adoption under the fast-track procedure, and approved as international standard ISO/IEC 16262, in April 1998. The Ecma General Assembly of June 1998 approved the second edition of ECMA-262 to keep it fully aligned with ISO/IEC 16262. Changes between the first and the second edition are editorial in nature.scrollHeightoffsetHeightscrollTopclientHeightoffsetTopclientLeftclientWidthclientTopoffsetLeftoffsetWidth \ No newline at end of file +Introduction This Ecma Standard is based on several originating technologies, the most well known being JavaScript (Netscape) and JScript (Microsoft). The language was invented by Brendan Eich at Netscape and first appeared in that company’s Navigator 2.0 browser. It has appeared in all subsequent browsers from Netscape and in all browsers from Microsoft starting with Internet Explorer 3.0. The development of this Standard started in November 1996. The first edition of this Ecma Standard was adopted by the Ecma General Assembly of June 1997. That Ecma Standard was submitted to ISO/ IEC JTC 1 for adoption under the fast-track procedure, and approved as international standard ISO/IEC 16262, in April 1998. The Ecma General Assembly of June 1998 approved the second edition of ECMA-262 to keep it fully aligned with ISO/IEC 16262. Changes between the first and the second edition are editorial in nature.scrollHeightoffsetHeightscrollTopclientHeightoffsetTopclientLeftclientWidthclientTopoffsetLeftoffsetWidth \ No newline at end of file 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 7631676d6..10898dbf7 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 @@ -40,7 +40,7 @@ In modern HTML we should always write `DOCTYPE`. ## Width/height of the document -Theoretically, as the root document element is `documentElement.clientWidth/Height`, and it encloses all the content, we could measure document full size as `documentElement.scrollWidth/scrollHeight`. +Theoretically, as the root document element is `document.documentElement`, and it encloses all the content, we could measure document full size as `document.documentElement.scrollWidth/scrollHeight`. But on that element, for the whole page, these properties do not work as intended. In Chrome/Safari/Opera if there's no scroll, then `documentElement.scrollHeight` may be even less than `documentElement.clientHeight`! Sounds like a nonsense, weird, right? @@ -62,7 +62,7 @@ Why so? Better don't ask. These inconsistencies come from ancient times, not a " DOM elements have their current scroll state in `elem.scrollLeft/scrollTop`. -For document scroll `document.documentElement.scrollLeft/Top` works in most browsers, except oldler WebKit-based ones, like Safari (bug [5991](https://bugs.webkit.org/show_bug.cgi?id=5991)), where we should use `document.body` instead of `document.documentElement`. +For document scroll `document.documentElement.scrollLeft/Top` works in most browsers, except older WebKit-based ones, like Safari (bug [5991](https://bugs.webkit.org/show_bug.cgi?id=5991)), where we should use `document.body` instead of `document.documentElement`. Luckily, we don't have to remember these peculiarities at all, because the scroll is available in the special properties `window.pageXOffset/pageYOffset`: diff --git a/2-ui/1-document/11-coordinates/3-position-at-absolute/solution.view/index.html b/2-ui/1-document/11-coordinates/3-position-at-absolute/solution.view/index.html index d627bee84..56c95d5ec 100644 --- a/2-ui/1-document/11-coordinates/3-position-at-absolute/solution.view/index.html +++ b/2-ui/1-document/11-coordinates/3-position-at-absolute/solution.view/index.html @@ -28,8 +28,8 @@ let box = elem.getBoundingClientRect(); return { - top: box.top + pageYOffset, - left: box.left + pageXOffset + top: box.top + window.pageYOffset, + left: box.left + window.pageXOffset }; } diff --git a/2-ui/1-document/11-coordinates/4-position-inside-absolute/solution.view/index.html b/2-ui/1-document/11-coordinates/4-position-inside-absolute/solution.view/index.html index 7e841397b..b89db3790 100644 --- a/2-ui/1-document/11-coordinates/4-position-inside-absolute/solution.view/index.html +++ b/2-ui/1-document/11-coordinates/4-position-inside-absolute/solution.view/index.html @@ -26,8 +26,8 @@ let box = elem.getBoundingClientRect(); return { - top: box.top + pageYOffset, - left: box.left + pageXOffset + top: box.top + window.pageYOffset, + left: box.left + window.pageXOffset }; } diff --git a/2-ui/1-document/11-coordinates/article.md b/2-ui/1-document/11-coordinates/article.md index 939ba9141..18d56cd27 100644 --- a/2-ui/1-document/11-coordinates/article.md +++ b/2-ui/1-document/11-coordinates/article.md @@ -75,17 +75,17 @@ Please note: ```smart header="Why derived properties are needed? Why does `top/left` exist if there's `x/y`?" Mathematically, a rectangle is uniquely defined with its starting point `(x,y)` and the direction vector `(width,height)`. So the additional derived properties are for convenience. -Technically it's possible for `width/height` to be negative, that allows for "directed" rectangle, e.g. to represent mouse selection with properly marked start and end. +Technically it's possible for `width/height` to be negative, that allows for "directed" rectangle, e.g. to represent mouse selection with properly marked start and end. + +Negative `width/height` values mean that the rectangle starts at its bottom-right corner and then "grows" left-upwards. Here's a rectangle with negative `width` and `height` (e.g. `width=-200`, `height=-100`): ![](coordinates-negative.svg) -The rectangle starts at its bottom-right corner and then spans left/up, as negative `width/height` lead it backwards by coordinates. - -As you can see, `left/top` are not `x/y` here. So these are actually not duplicates. Their formula can be adjusted to cover negative `width/height`, that's simple enough, but rarely needed, as the result of `elem.getBoundingClientRect()` always has positive width/height. +As you can see, `left/top` do not equal `x/y` in such case. -Here we mention negative `width/height` only for you to understand why these seemingly duplicate properties are not actually duplicates. +In practice though, `elem.getBoundingClientRect()` always returns positive width/height, here we mention negative `width/height` only for you to understand why these seemingly duplicate properties are not actually duplicates. ``` ```warn header="Internet Explorer and Edge: no support for `x/y`" @@ -215,8 +215,8 @@ function getCoords(elem) { let box = elem.getBoundingClientRect(); return { - top: box.top + pageYOffset, - left: box.left + pageXOffset + top: box.top + window.pageYOffset, + left: box.left + window.pageXOffset }; } ``` diff --git a/2-ui/1-document/11-coordinates/coordinates-negative.svg b/2-ui/1-document/11-coordinates/coordinates-negative.svg index 347c96d8a..3d3380110 100644 --- a/2-ui/1-document/11-coordinates/coordinates-negative.svg +++ b/2-ui/1-document/11-coordinates/coordinates-negative.svg @@ -1 +1 @@ -bottom(x,y)(x,y)leftrightIntroduction This Ecma Standard is based on several originating technologies, the most well known being JavaScript (Netscape) and JScript (Microsoft). The language was invented by Brendan Eich at top(width,height) \ No newline at end of file +bottom(x,y)(x,y)leftrightIntroduction This Ecma Standard is based on several originating technologies, the most well known being JavaScript (Netscape) and JScript (Microsoft). The language was invented by Brendan Eich at top(width,height) \ No newline at end of file diff --git a/2-ui/1-document/11-coordinates/coordinates.svg b/2-ui/1-document/11-coordinates/coordinates.svg index 2d789c1cf..5e0b6f185 100644 --- a/2-ui/1-document/11-coordinates/coordinates.svg +++ b/2-ui/1-document/11-coordinates/coordinates.svg @@ -1 +1 @@ -heightbottomxleftywidthrightIntroduction This Ecma Standard is based on several originating technologies, the most well known being JavaScript (Netscape) and JScript (Microsoft). The language was invented by Brendan Eich at top \ No newline at end of file +heightbottomxleftywidthrightIntroduction This Ecma Standard is based on several originating technologies, the most well known being JavaScript (Netscape) and JScript (Microsoft). The language was invented by Brendan Eich at top \ No newline at end of file diff --git a/2-ui/1-document/11-coordinates/document-and-window-coordinates-scrolled.svg b/2-ui/1-document/11-coordinates/document-and-window-coordinates-scrolled.svg index 204f02c85..8b4270ff0 100644 --- a/2-ui/1-document/11-coordinates/document-and-window-coordinates-scrolled.svg +++ b/2-ui/1-document/11-coordinates/document-and-window-coordinates-scrolled.svg @@ -1 +1 @@ -Introduction This Ecma Standard is based on several originating technologies, the most well known being JavaScript (Netscape) and JScript (Microsoft). The language was invented by Brendan Eich at Netscape and first appeared in that company’s Navigator 2.0 browser. It has appeared in all subsequent browsers from Netscape and in all browsers from Microsoft starting with Internet Explorer 3.0. The development of this Standard started in November 1996. The first edition of this Ecma Standard was adopted by the Ecma General Assembly of June 1997. That Ecma Standard was submitted to ISO/ IEC JTC 1 for adoption under the fast-track procedure, and approved as international standard ISO/IEC 16262, in April 1998. The Ecma General Assembly of June 1998 approved the second edition of ECMA-262 to keep it fully aligned with ISO/IEC 16262. Changes between the first and the second edition are editorial in nature.😍pageYclientYpageXclientXIntroduction This Ecma Standard is based on several originating technologies, the most well known being JavaScript (Netscape) and JScript (Microsoft). The language was invented by Brendan Eich at Netscape and first appeared in that company’s Navigator 2.0 browser. It has appeared in all subsequent browsers from Netscape and in all browsers from Microsoft starting with Internet Explorer 3.0. The development of this Standard started in November 1996. The first edition of this Ecma Standard was adopted by the Ecma General Assembly of June 1997. That Ecma Standard was submitted to ISO/ IEC JTC 1 for adoption under the fast-track procedure, and approved as international standard ISO/IEC 16262, in April 1998. The Ecma General Assembly of June 1998 approved the second edition of ECMA-262 to keep it fully aligned with ISO/IEC 16262. Changes between the first and the second edition are editorial in nature.pageYclientYpageXclientX😍 \ No newline at end of file +Introduction This Ecma Standard is based on several originating technologies, the most well known being JavaScript (Netscape) and JScript (Microsoft). The language was invented by Brendan Eich at Netscape and first appeared in that company’s Navigator 2.0 browser. It has appeared in all subsequent browsers from Netscape and in all browsers from Microsoft starting with Internet Explorer 3.0. The development of this Standard started in November 1996. The first edition of this Ecma Standard was adopted by the Ecma General Assembly of June 1997. That Ecma Standard was submitted to ISO/ IEC JTC 1 for adoption under the fast-track procedure, and approved as international standard ISO/IEC 16262, in April 1998. The Ecma General Assembly of June 1998 approved the second edition of ECMA-262 to keep it fully aligned with ISO/IEC 16262. Changes between the first and the second edition are editorial in nature.😍pageYclientYpageXclientXIntroduction This Ecma Standard is based on several originating technologies, the most well known being JavaScript (Netscape) and JScript (Microsoft). The language was invented by Brendan Eich at Netscape and first appeared in that company’s Navigator 2.0 browser. It has appeared in all subsequent browsers from Netscape and in all browsers from Microsoft starting with Internet Explorer 3.0. The development of this Standard started in November 1996. The first edition of this Ecma Standard was adopted by the Ecma General Assembly of June 1997. That Ecma Standard was submitted to ISO/ IEC JTC 1 for adoption under the fast-track procedure, and approved as international standard ISO/IEC 16262, in April 1998. The Ecma General Assembly of June 1998 approved the second edition of ECMA-262 to keep it fully aligned with ISO/IEC 16262. Changes between the first and the second edition are editorial in nature.pageYclientYpageXclientX😍 \ No newline at end of file diff --git a/2-ui/2-events/02-bubbling-and-capturing/event-order-bubbling.svg b/2-ui/2-events/02-bubbling-and-capturing/event-order-bubbling.svg index 580022a9f..e3d749898 100644 --- a/2-ui/2-events/02-bubbling-and-capturing/event-order-bubbling.svg +++ b/2-ui/2-events/02-bubbling-and-capturing/event-order-bubbling.svg @@ -1 +1 @@ -123Most deeply nested element \ No newline at end of file +123Most deeply nested element \ No newline at end of file diff --git a/2-ui/2-events/03-event-delegation/4-behavior-tooltip/task.md b/2-ui/2-events/03-event-delegation/4-behavior-tooltip/task.md index 73102a92b..3001b9915 100644 --- a/2-ui/2-events/03-event-delegation/4-behavior-tooltip/task.md +++ b/2-ui/2-events/03-event-delegation/4-behavior-tooltip/task.md @@ -22,8 +22,10 @@ In this task we assume that all elements with `data-tooltip` have only text insi Details: +- The distance between the element and the tooltip should be `5px`. +- The tooltip should be centered relative to the element, if possible. - The tooltip should not cross window edges. Normally it should be above the element, but if the element is at the page top and there's no space for the tooltip, then below it. -- The tooltip is given in the `data-tooltip` attribute. It can be arbitrary HTML. +- The tooltip content is given in the `data-tooltip` attribute. It can be arbitrary HTML. You'll need two events here: - `mouseover` triggers when a pointer comes over an element. diff --git a/2-ui/2-events/03-event-delegation/bagua-bubble.svg b/2-ui/2-events/03-event-delegation/bagua-bubble.svg index b770f8c55..bb9989b45 100644 --- a/2-ui/2-events/03-event-delegation/bagua-bubble.svg +++ b/2-ui/2-events/03-event-delegation/bagua-bubble.svg @@ -1 +1 @@ -<table><td><strong>event.target \ No newline at end of file +<table><td><strong>event.target \ No newline at end of file diff --git a/2-ui/2-events/04-default-browser-action/1-why-return-false-fails/solution.md b/2-ui/2-events/04-default-browser-action/1-why-return-false-fails/solution.md index 2862e6006..4d175ca01 100644 --- a/2-ui/2-events/04-default-browser-action/1-why-return-false-fails/solution.md +++ b/2-ui/2-events/04-default-browser-action/1-why-return-false-fails/solution.md @@ -20,7 +20,7 @@ The fix is simple: } -w3.org +w3.org ``` Also we can use `event.preventDefault()`, like this: @@ -35,5 +35,5 @@ Also we can use `event.preventDefault()`, like this: */!* -w3.org +w3.org ``` diff --git a/2-ui/2-events/04-default-browser-action/1-why-return-false-fails/task.md b/2-ui/2-events/04-default-browser-action/1-why-return-false-fails/task.md index 8fa4fb29a..9a5bb98ac 100644 --- a/2-ui/2-events/04-default-browser-action/1-why-return-false-fails/task.md +++ b/2-ui/2-events/04-default-browser-action/1-why-return-false-fails/task.md @@ -14,7 +14,7 @@ Why in the code below `return false` doesn't work at all? } -the browser will go to w3.org +the browser will go to w3.org ``` The browser follows the URL on click, but we don't want it. diff --git a/2-ui/2-events/04-default-browser-action/2-catch-link-navigation/solution.view/index.html b/2-ui/2-events/04-default-browser-action/2-catch-link-navigation/solution.view/index.html index 90aecd9dc..51ac0838b 100644 --- a/2-ui/2-events/04-default-browser-action/2-catch-link-navigation/solution.view/index.html +++ b/2-ui/2-events/04-default-browser-action/2-catch-link-navigation/solution.view/index.html @@ -16,7 +16,7 @@
#contents

- How about to read Wikipedia or visit W3.org and learn about modern standards? + How about to read Wikipedia or visit W3.org and learn about modern standards?

diff --git a/2-ui/2-events/04-default-browser-action/2-catch-link-navigation/source.view/index.html b/2-ui/2-events/04-default-browser-action/2-catch-link-navigation/source.view/index.html index b88834c96..f0c934391 100644 --- a/2-ui/2-events/04-default-browser-action/2-catch-link-navigation/source.view/index.html +++ b/2-ui/2-events/04-default-browser-action/2-catch-link-navigation/source.view/index.html @@ -16,7 +16,7 @@
#contents

- How about to read Wikipedia or visit W3.org and learn about modern standards? + How about to read Wikipedia or visit W3.org and learn about modern standards?

diff --git a/2-ui/2-events/04-default-browser-action/2-catch-link-navigation/task.md b/2-ui/2-events/04-default-browser-action/2-catch-link-navigation/task.md index 6439376bf..6ca456c2c 100644 --- a/2-ui/2-events/04-default-browser-action/2-catch-link-navigation/task.md +++ b/2-ui/2-events/04-default-browser-action/2-catch-link-navigation/task.md @@ -12,5 +12,5 @@ Like this: Details: -- HTML inside the element may be loaded or regenerated dynamically at any time, so we can't find all links and put handlers on them. Use the event delegation. +- HTML inside the element may be loaded or regenerated dynamically at any time, so we can't find all links and put handlers on them. Use event delegation. - The content may have nested tags. Inside links too, like `...`. diff --git a/2-ui/2-events/04-default-browser-action/article.md b/2-ui/2-events/04-default-browser-action/article.md index 0a0131a1c..8bb2ffc6a 100644 --- a/2-ui/2-events/04-default-browser-action/article.md +++ b/2-ui/2-events/04-default-browser-action/article.md @@ -8,7 +8,7 @@ For instance: - A click on a form submit button - initiates its submission to the server. - Pressing a mouse button over a text and moving it - selects the text. -If we handle an event in JavaScript, we may not want the corresponding browser action to happen, to implement another behavior instead. +If we handle an event in JavaScript, we may not want the corresponding browser action to happen, and want to implement another behavior instead. ## Preventing browser actions @@ -207,7 +207,7 @@ As we can clearly see, `event.stopPropagation()` and `event.preventDefault()` (a ``` ```smart header="Nested context menus architecture" -There are also alternative ways to implement nested context menus. One of them is to have a single global object with a handler for `document.oncontextmenu`, and also methods that allow to store other handlers in it. +There are also alternative ways to implement nested context menus. One of them is to have a single global object with a handler for `document.oncontextmenu`, and also methods that allow us to store other handlers in it. The object will catch any right-click, look through stored handlers and run the appropriate one. @@ -240,5 +240,5 @@ But we should generally keep the semantic meaning of HTML elements. For instance Besides being "just a good thing", that makes your HTML better in terms of accessibility. -Also if we consider the example with ``, then please note: a browser allows to open such links in a new window (by right-clicking them and other means). And people like that. But if we make a button behave as a link using JavaScript and even look like a link using CSS, then ``-specific browser features still won't work for it. +Also if we consider the example with ``, then please note: a browser allows us to open such links in a new window (by right-clicking them and other means). And people like that. But if we make a button behave as a link using JavaScript and even look like a link using CSS, then ``-specific browser features still won't work for it. ``` diff --git a/2-ui/2-events/05-dispatch-events/article.md b/2-ui/2-events/05-dispatch-events/article.md index 771f5527b..ad9092fd8 100644 --- a/2-ui/2-events/05-dispatch-events/article.md +++ b/2-ui/2-events/05-dispatch-events/article.md @@ -211,7 +211,7 @@ Please note: the event must have the flag `cancelable: true`, otherwise the call ## Events-in-events are synchronous -Usually events are processed asynchronously. That is: if the browser is processing `onclick` and in the process a new event occurs, then it awaits till `onclick` processing is finished. +Usually events are processed asynchronously. That is: if the browser is processing `onclick` and in the process a new event occurs, then it waits until the `onclick` processing is finished. The exception is when one event is initiated from within another one. diff --git a/2-ui/3-event-details/1-mouse-events-basics/article.md b/2-ui/3-event-details/1-mouse-events-basics/article.md index 412c40073..42f4b6f50 100644 --- a/2-ui/3-event-details/1-mouse-events-basics/article.md +++ b/2-ui/3-event-details/1-mouse-events-basics/article.md @@ -47,7 +47,7 @@ In cases when a single action initiates multiple events, their order is fixed. T ```online Click the button below and you'll see the events. Try double-click too. -On the teststand below all mouse events are logged, and if there are more than 1 second delay between them, then they are separated by a horizontal ruler. +On the teststand below all mouse events are logged, and if there is more than a 1 second delay between them they are separated by a horizontal ruler. Also we can see the `which` property that allows to detect the mouse button. @@ -110,7 +110,7 @@ So if we want to support combinations like `key:Ctrl`+click, then for Mac it mak Even if we'd like to force Mac users to `key:Ctrl`+click -- that's kind of difficult. The problem is: a left-click with `key:Ctrl` is interpreted as a *right-click* on MacOS, and it generates the `contextmenu` event, not `click` like Windows/Linux. -So if we want users of all operational systems to feel comfortable, then together with `ctrlKey` we should check `metaKey`. +So if we want users of all operating systems to feel comfortable, then together with `ctrlKey` we should check `metaKey`. For JS-code it means that we should check `if (event.ctrlKey || event.metaKey)`. ``` diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/index.html b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/index.html index df0f13656..098232174 100644 --- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/index.html +++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/index.html @@ -20,20 +20,17 @@ diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/test.js b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/test.js index 7c7f6d23d..f5d4aaffb 100644 --- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/test.js +++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/test.js @@ -49,18 +49,18 @@ describe("hoverIntent", function() { } }) - it("mouseover -> immediately no tooltip", function() { + it("mouseover -> when the pointer just arrived, no tooltip", function() { mouse('mouseover', 10, 10); assert.isFalse(isOver); }); - it("mouseover -> pause shows tooltip", function() { + it("mouseover -> after a delay, the tooltip shows up", function() { mouse('mouseover', 10, 10); this.clock.tick(100); assert.isTrue(isOver); }); - it("mouseover -> fast mouseout no tooltip", function() { + it("mouseover -> followed by fast mouseout leads doesn't show tooltip", function() { mouse('mouseover', 10, 10); setTimeout( () => mouse('mouseout', 300, 300, { relatedTarget: document.body}), diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/source.view/hoverIntent.js b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/source.view/hoverIntent.js index caca940b1..a38b42bc2 100644 --- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/source.view/hoverIntent.js +++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/source.view/hoverIntent.js @@ -45,6 +45,7 @@ class HoverIntent { destroy() { /* your code to "disable" the functionality, remove all handlers */ + /* it's needed for the tests to work */ } } diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/source.view/index.html b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/source.view/index.html index 856daf3a8..098232174 100644 --- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/source.view/index.html +++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/source.view/index.html @@ -3,10 +3,9 @@ - Document - + @@ -21,18 +20,17 @@ diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/source.view/style.css b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/source.view/style.css index 980e9457e..fa2f09eba 100644 --- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/source.view/style.css +++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/source.view/style.css @@ -2,6 +2,10 @@ color: red; } +body { + margin: 0; +} + .minutes { color: green; } @@ -20,9 +24,15 @@ top: 0; } -.tooltip { +#tooltip { position: absolute; - background: #eee; - border: 1px brown solid; - padding: 3px; + padding: 10px 20px; + border: 1px solid #b3c9ce; + border-radius: 4px; + text-align: center; + font: italic 14px/1.3 sans-serif; + color: #333; + background: #fff; + z-index: 100000; + box-shadow: 3px 3px 3px rgba(0, 0, 0, .3); } diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/source.view/test.js b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/source.view/test.js index 44369d8c3..f5d4aaffb 100644 --- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/source.view/test.js +++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/source.view/test.js @@ -3,7 +3,7 @@ describe("hoverIntent", function() { function mouse(eventType, x, y, options) { - let eventOptions = Object.assign({ + let eventOptions = Object.assign({ bubbles: true, clientX: x, clientY: y, @@ -11,15 +11,15 @@ describe("hoverIntent", function() { pageY: y, target: elem }, options || {}); - + elem.dispatchEvent(new MouseEvent(eventType, eventOptions)); } let isOver; let hoverIntent; - - + + before(function() { this.clock = sinon.useFakeTimers(); }); @@ -27,11 +27,11 @@ describe("hoverIntent", function() { after(function() { this.clock.restore(); }); - - + + beforeEach(function() { isOver = false; - + hoverIntent = new HoverIntent({ elem: elem, over: function() { @@ -49,18 +49,18 @@ describe("hoverIntent", function() { } }) - it("mouseover -> immediately no tooltip", function() { + it("mouseover -> when the pointer just arrived, no tooltip", function() { mouse('mouseover', 10, 10); assert.isFalse(isOver); }); - - it("mouseover -> pause shows tooltip", function() { + + it("mouseover -> after a delay, the tooltip shows up", function() { mouse('mouseover', 10, 10); this.clock.tick(100); assert.isTrue(isOver); }); - it("mouseover -> fast mouseout no tooltip", function() { + it("mouseover -> followed by fast mouseout leads doesn't show tooltip", function() { mouse('mouseover', 10, 10); setTimeout( () => mouse('mouseout', 300, 300, { relatedTarget: document.body}), @@ -94,5 +94,5 @@ describe("hoverIntent", function() { this.clock.tick(200); assert.isFalse(isOver); }); - + }); diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/article.md b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/article.md index be6090aef..c7ac0d4db 100644 --- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/article.md +++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/article.md @@ -125,7 +125,7 @@ If there are some actions upon leaving the parent element, e.g. an animation run To avoid it, we can check `relatedTarget` in the handler and, if the mouse is still inside the element, then ignore such event. -Alternatively we can use other events: `mouseenter` и `mouseleave`, that we'll be covering now, as they don't have such problems. +Alternatively we can use other events: `mouseenter` and `mouseleave`, that we'll be covering now, as they don't have such problems. ## Events mouseenter and mouseleave @@ -145,7 +145,7 @@ When the pointer leaves an element -- `mouseleave` triggers. ```online This example is similar to the one above, but now the top element has `mouseenter/mouseleave` instead of `mouseover/mouseout`. -As you can see, the only generated events are the ones related to moving the pointer in and out of the top element. Nothing happens when the pointer goes to the child and back. Transitions between descendants are ignores +As you can see, the only generated events are the ones related to moving the pointer in and out of the top element. Nothing happens when the pointer goes to the child and back. Transitions between descendants are ignored [codetabs height=340 src="mouseleave"] ``` diff --git a/2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/solution.view/field.svg b/2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/solution.view/field.svg index f5562f97a..ca8bbc3bd 100644 --- a/2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/solution.view/field.svg +++ b/2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/solution.view/field.svg @@ -1 +1 @@ -(0,0)clientWidth \ No newline at end of file +(0,0)clientWidth \ No newline at end of file diff --git a/2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/solution.view/soccer.js b/2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/solution.view/soccer.js index 1c1443a7e..10ae2eeed 100644 --- a/2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/solution.view/soccer.js +++ b/2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/solution.view/soccer.js @@ -7,7 +7,7 @@ document.addEventListener('mousedown', function(event) { if (!dragElement) return; event.preventDefault(); - + dragElement.ondragstart = function() { return false; }; @@ -19,7 +19,7 @@ document.addEventListener('mousedown', function(event) { function onMouseUp(event) { finishDrag(); }; - + function onMouseMove(event) { moveAt(event.clientX, event.clientY); } @@ -31,9 +31,9 @@ document.addEventListener('mousedown', function(event) { if(isDragging) { return; } - + isDragging = true; - + document.addEventListener('mousemove', onMouseMove); element.addEventListener('mouseup', onMouseUp); @@ -50,10 +50,10 @@ document.addEventListener('mousedown', function(event) { if(!isDragging) { return; } - + isDragging = false; - dragElement.style.top = parseInt(dragElement.style.top) + pageYOffset + 'px'; + dragElement.style.top = parseInt(dragElement.style.top) + window.pageYOffset + 'px'; dragElement.style.position = 'absolute'; document.removeEventListener('mousemove', onMouseMove); @@ -113,4 +113,4 @@ document.addEventListener('mousedown', function(event) { dragElement.style.top = newY + 'px'; } -}); \ No newline at end of file +}); diff --git a/2-ui/3-event-details/4-mouse-drag-and-drop/article.md b/2-ui/3-event-details/4-mouse-drag-and-drop/article.md index 2418ffd66..b74c13f1e 100644 --- a/2-ui/3-event-details/4-mouse-drag-and-drop/article.md +++ b/2-ui/3-event-details/4-mouse-drag-and-drop/article.md @@ -1,12 +1,12 @@ # Drag'n'Drop with mouse events -Drag'n'Drop is a great interface solution. Taking something, dragging and dropping is a clear and simple way to do many things, from copying and moving documents (as in file managers) to ordering (drop into cart). +Drag'n'Drop is a great interface solution. Taking something and dragging and dropping it is a clear and simple way to do many things, from copying and moving documents (as in file managers) to ordering (dropping items into a cart). -In the modern HTML standard there's a [section about Drag and Drop](https://html.spec.whatwg.org/multipage/interaction.html#dnd) with special events such as `dragstart`, `dragend` and so on. +In the modern HTML standard there's a [section about Drag and Drop](https://html.spec.whatwg.org/multipage/interaction.html#dnd) with special events such as `dragstart`, `dragend`, and so on. -They are interesting because they allow to solve simple tasks easily, and also allow to handle drag'n'drop of "external" files into the browser. So we can take a file in the OS file-manager and drop it into the browser window. Then JavaScript gains access to its contents. +These events are useful in that they allow us to solve simple tasks easily. For instance, they allow us to handle the drag'n'drop of "external" files into the browser, so we can take a file in the OS file-manager and drop it into the browser window, thereby giving JavaScript access to its contents. -But native Drag Events also have limitations. For instance, we can't limit dragging by a certain area. Also we can't make it "horizontal" or "vertical" only. There are other drag'n'drop tasks that can't be done using that API. Besides, mobile devices support for such events is almost non-existant. +But native Drag Events also have limitations. For instance, we can't limit dragging by a certain area. Also we can't make it "horizontal" or "vertical" only. And there are other drag'n'drop tasks that can't be done using that API. Also, mobile device support for such events is almost non-existant. So here we'll see how to implement Drag'n'Drop using mouse events. @@ -124,7 +124,7 @@ Let's update our algorithm: ```js // onmousemove - // у мяча ball стоит position:absoute + // ball has position:absoute ball.style.left = event.pageX - *!*shiftX*/!* + 'px'; ball.style.top = event.pageY - *!*shiftY*/!* + 'px'; ``` diff --git a/2-ui/3-event-details/5-keyboard-events/german-layout.svg b/2-ui/3-event-details/5-keyboard-events/german-layout.svg index d107c2240..8a880e8e0 100644 --- a/2-ui/3-event-details/5-keyboard-events/german-layout.svg +++ b/2-ui/3-event-details/5-keyboard-events/german-layout.svg @@ -1 +1 @@ -StrgStrgAl tAlt GrWinWinMenu \ No newline at end of file +StrgStrgAl tAlt GrWinWinMenu \ No newline at end of file diff --git a/2-ui/3-event-details/8-onscroll/3-load-visible-img/source.view/index.html b/2-ui/3-event-details/8-onscroll/3-load-visible-img/source.view/index.html index 5c6027a6f..9953ace66 100644 --- a/2-ui/3-event-details/8-onscroll/3-load-visible-img/source.view/index.html +++ b/2-ui/3-event-details/8-onscroll/3-load-visible-img/source.view/index.html @@ -169,38 +169,8 @@

Neptune

* It's enough that the top or bottom edge of the element are visible */ function isVisible(elem) { - - let coords = elem.getBoundingClientRect(); - - let windowHeight = document.documentElement.clientHeight; - - // top elem edge is visible OR bottom elem edge is visible - let topVisible = coords.top > 0 && coords.top < windowHeight; - let bottomVisible = coords.bottom < windowHeight && coords.bottom > 0; - - return topVisible || bottomVisible; - } - - /** - A variant of the test that considers the element visible if it's no more than - one page after/behind the current screen. - - function isVisible(elem) { - - let coords = elem.getBoundingClientRect(); - - let windowHeight = document.documentElement.clientHeight; - - let extendedTop = -windowHeight; - let extendedBottom = 2 * windowHeight; - - // top visible || bottom visible - let topVisible = coords.top > extendedTop && coords.top < extendedBottom; - let bottomVisible = coords.bottom < extendedBottom && coords.bottom > extendedTop; - - return topVisible || bottomVisible; + // todo: your code } - */ function showVisible() { for (let img of document.querySelectorAll('img')) { diff --git a/2-ui/3-event-details/8-onscroll/article.md b/2-ui/3-event-details/8-onscroll/article.md index 22ff8d859..7b5cf4848 100644 --- a/2-ui/3-event-details/8-onscroll/article.md +++ b/2-ui/3-event-details/8-onscroll/article.md @@ -10,7 +10,7 @@ Here's a small function to show the current scroll: ```js autorun window.addEventListener('scroll', function() { - document.getElementById('showScroll').innerHTML = pageYOffset + 'px'; + document.getElementById('showScroll').innerHTML = window.pageYOffset + 'px'; }); ``` diff --git a/2-ui/4-forms-controls/1-form-elements/1-add-select-option/task.md b/2-ui/4-forms-controls/1-form-elements/1-add-select-option/task.md index 9b218aa7f..a0e74da57 100644 --- a/2-ui/4-forms-controls/1-form-elements/1-add-select-option/task.md +++ b/2-ui/4-forms-controls/1-form-elements/1-add-select-option/task.md @@ -18,3 +18,5 @@ Use JavaScript to: 1. Show the value and the text of the selected option. 2. Add an option: ``. 3. Make it selected. + +Note, if you've done everything right, your alert should show `blues`. diff --git a/2-ui/4-forms-controls/1-form-elements/form-navigation.svg b/2-ui/4-forms-controls/1-form-elements/form-navigation.svg index 113e7da55..b112e250a 100644 --- a/2-ui/4-forms-controls/1-form-elements/form-navigation.svg +++ b/2-ui/4-forms-controls/1-form-elements/form-navigation.svg @@ -1 +1 @@ -formelements[0]elements[1]elements[n]...formform \ No newline at end of file +formelements[0]elements[1]elements[n]...formform \ No newline at end of file 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 6a605bc01..d42013e5b 100644 --- a/2-ui/4-forms-controls/2-focus-blur/article.md +++ b/2-ui/4-forms-controls/2-focus-blur/article.md @@ -49,7 +49,7 @@ Your email please: ``` -Modern HTML allows to do many validations using input attributes: `required`, `pattern` and so on. And sometimes they are just what we need. JavaScript can be used when we want more flexibility. Also we could automatically send the changed value to the server if it's correct. +Modern HTML allows us to do many validations using input attributes: `required`, `pattern` and so on. And sometimes they are just what we need. JavaScript can be used when we want more flexibility. Also we could automatically send the changed value to the server if it's correct. ## Methods focus/blur diff --git a/2-ui/5-loading/01-onload-ondomcontentloaded/article.md b/2-ui/5-loading/01-onload-ondomcontentloaded/article.md index e825d8706..bc637e63e 100644 --- a/2-ui/5-loading/01-onload-ondomcontentloaded/article.md +++ b/2-ui/5-loading/01-onload-ondomcontentloaded/article.md @@ -173,7 +173,7 @@ window.onbeforeunload = function() { }; ``` -For historical reasons, returning a non-empty string also counts as canceling the event. Some time ago browsers used show it as a message, but as the [modern specification](https://html.spec.whatwg.org/#unloading-documents) says, they shouldn't. +For historical reasons, returning a non-empty string also counts as canceling the event. Some time ago browsers used to show it as a message, but as the [modern specification](https://html.spec.whatwg.org/#unloading-documents) says, they shouldn't. Here's an example: diff --git a/2-ui/99-ui-misc/01-mutation-observer/article.md b/2-ui/99-ui-misc/01-mutation-observer/article.md index 4cbff3ea8..6a458fa02 100644 --- a/2-ui/99-ui-misc/01-mutation-observer/article.md +++ b/2-ui/99-ui-misc/01-mutation-observer/article.md @@ -3,7 +3,7 @@ `MutationObserver` is a built-in object that observes a DOM element and fires a callback in case of changes. -We'll first take a look at the syntax, and then explore a real-world use case. +We'll first take a look at the syntax, and then explore a real-world use case, to see where such thing may be useful. ## Syntax @@ -65,7 +65,7 @@ observer.observe(elem, { ``` -Now if we change the text inside `edit`, we'll get a single mutation: +If we run this code in the browser, then focus on the given `
` and change the text inside `edit`, `console.log` will show one mutation: ```js mutationRecords = [{ @@ -76,7 +76,7 @@ mutationRecords = [{ }]; ``` -If we select and remove the `edit` altogether, we'll get multiple mutations: +If we make more complex editing operations, e.g. remove the `edit`, the mutation event may contain multiple mutation records: ```js mutationRecords = [{ @@ -101,15 +101,15 @@ So, `MutationObserver` allows to react on any changes within DOM subtree. When such thing may be useful? -Imagine the situation when you attach a third-party script that adds useful functionality on the page, but also does something unwanted, e.g. shows ads `
Unwanted ads
`. +Imagine the situation when you need to add a third-party script that contains useful functionality, but also does something unwanted, e.g. shows ads `
Unwanted ads
`. Naturally, the third-party script provides no mechanisms to remove it. -Using `MutationObserver`, we can detect when such element appears in our DOM and remove it. While leaving the useful functionality intact. Surely though, creators of that script won't be happy that you took their useful stuff and removed the ads. +Using `MutationObserver`, we can detect when the unwanted element appears in our DOM and remove it. There are other situations when a third-party script adds something into our document, and we'd like to detect, when it happens, to adapt our page, dynamically resize something etc. -`MutationObserver` can easily handle this. +`MutationObserver` allows to implement this. ## Usage for architecture @@ -213,7 +213,7 @@ Please run the previous code (above, observes that element), and then the code b

A demo-element with id="highlight-demo", run the code above to observe it.

-The following code populates its `innerHTML`. Please run the code above first, it will watch and highlight the new content: +The following code populates its `innerHTML`, that causes the `MutationObserver` to react and highlight its contents: ```js run let demoElem = document.getElementById('highlight-demo'); @@ -236,22 +236,26 @@ There's a method to stop observing the node: - `observer.disconnect()` -- stops the observation. -Another method often used with it: +When we stop the observing, it might be possible that some changes were not processed by the observer yet. -- `mutationRecords = observer.takeRecords()` -- gets a list of unprocessed mutation records, those that happened, but the callback did not handle them. +- `observer.takeRecords()` -- gets a list of unprocessed mutation records, those that happened, but the callback did not handle them. + +These methods can be used together, like this: ```js // we'd like to stop tracking changes observer.disconnect(); -// it might have not yet handled some mutations +// handle unprocessed some mutations let mutationRecords = observer.takeRecords(); -// process mutationRecords +... ``` -## Garbage collection +```smart header="Garbage collection interaction" +Observers use weak references to nodes internally. That is: if a node is removed from DOM, and becomes unreachable, then it becomes garbage collected. -Observers use weak references to nodes internally. That is: if a node is removed from DOM, and becomes unreachable, then it becomes garbage collected, an observer doesn't prevent that. +The mere fact that a DOM node is observed doesn't prevent the garbage collection. +``` ## Summary diff --git a/2-ui/99-ui-misc/02-selection-range/article.md b/2-ui/99-ui-misc/02-selection-range/article.md index d6c8762c1..862975dc1 100644 --- a/2-ui/99-ui-misc/02-selection-range/article.md +++ b/2-ui/99-ui-misc/02-selection-range/article.md @@ -428,7 +428,7 @@ Form elements, such as `input` and `textarea` provide [special API for selection Properties: - `input.selectionStart` -- position of selection start (writeable), -- `input.selectionEnd` -- position of selection start (writeable), +- `input.selectionEnd` -- position of selection end (writeable), - `input.selectionDirection` -- selection direction, one of: "forward", "backward" or "none" (if e.g. selected with a double mouse click), Events: diff --git a/2-ui/99-ui-misc/02-selection-range/range-example-p-2-b-3-range.svg b/2-ui/99-ui-misc/02-selection-range/range-example-p-2-b-3-range.svg index cb21d022c..66397f20f 100644 --- a/2-ui/99-ui-misc/02-selection-range/range-example-p-2-b-3-range.svg +++ b/2-ui/99-ui-misc/02-selection-range/range-example-p-2-b-3-range.svg @@ -1 +1 @@ -startContainer (<p>.firstChild)startOffset (=2)commonAncestorContainer (<p>)endContainer (<b>.firstChild)endOffset (=3) \ No newline at end of file +startContainer (<p>.firstChild)startOffset (=2)commonAncestorContainer (<p>)endContainer (<b>.firstChild)endOffset (=3) \ No newline at end of file diff --git a/2-ui/99-ui-misc/02-selection-range/selection-firefox.svg b/2-ui/99-ui-misc/02-selection-range/selection-firefox.svg index 8ce5b47c7..879e1cecb 100644 --- a/2-ui/99-ui-misc/02-selection-range/selection-firefox.svg +++ b/2-ui/99-ui-misc/02-selection-range/selection-firefox.svg @@ -1 +1 @@ -selection \ No newline at end of file +selection \ No newline at end of file diff --git a/2-ui/99-ui-misc/03-event-loop/article.md b/2-ui/99-ui-misc/03-event-loop/article.md index 6240a6b2f..218dd2140 100644 --- a/2-ui/99-ui-misc/03-event-loop/article.md +++ b/2-ui/99-ui-misc/03-event-loop/article.md @@ -335,5 +335,5 @@ That's a way to run code in another, parallel thread. Web Workers can exchange messages with the main process, but they have their own variables, and their own event loop. -Web Workers do not have access to DOM, so they are useful, mainly, for calculations, to use multiplle CPU cores simultaneously. +Web Workers do not have access to DOM, so they are useful, mainly, for calculations, to use multiple CPU cores simultaneously. ``` diff --git a/2-ui/99-ui-misc/03-event-loop/eventLoop-full.svg b/2-ui/99-ui-misc/03-event-loop/eventLoop-full.svg index 044bab6f3..9e4691404 100644 --- a/2-ui/99-ui-misc/03-event-loop/eventLoop-full.svg +++ b/2-ui/99-ui-misc/03-event-loop/eventLoop-full.svg @@ -1 +1 @@ -...mousemoveevent loopmicrotasksrendermicrotasksrenderscriptsetTimeout \ No newline at end of file +...mousemoveevent loopmicrotasksrendermicrotasksrenderscriptsetTimeout \ No newline at end of file diff --git a/2-ui/99-ui-misc/03-event-loop/eventLoop.svg b/2-ui/99-ui-misc/03-event-loop/eventLoop.svg index 57f5188ce..f7de0b6ad 100644 --- a/2-ui/99-ui-misc/03-event-loop/eventLoop.svg +++ b/2-ui/99-ui-misc/03-event-loop/eventLoop.svg @@ -1 +1 @@ -...mousemovescriptevent loopmacrotask queuesetTimeout \ No newline at end of file +...mousemovescriptevent loopmacrotask queuesetTimeout \ No newline at end of file diff --git a/3-frames-and-windows/03-cross-window-communication/article.md b/3-frames-and-windows/03-cross-window-communication/article.md index 0a89521b3..53f5f55fc 100644 --- a/3-frames-and-windows/03-cross-window-communication/article.md +++ b/3-frames-and-windows/03-cross-window-communication/article.md @@ -335,10 +335,6 @@ The full example: [codetabs src="postmessage" height=120] -```smart header="There's no delay" -There's totally no delay between `postMessage` and the `message` event. The event triggers synchronously, faster than `setTimeout(...,0)`. -``` - ## Summary To call methods and access the content of another window, we should first have a reference to it. diff --git a/5-network/02-formdata/article.md b/5-network/02-formdata/article.md index fb028f5c8..1c28296d2 100644 --- a/5-network/02-formdata/article.md +++ b/5-network/02-formdata/article.md @@ -54,14 +54,14 @@ In this example, the server code is not presented, as it's beyound our scope. Th We can modify fields in `FormData` with methods: - `formData.append(name, value)` - add a form field with the given `name` and `value`, -- `formData.append(name, blob, fileName)` - add a field as if it were ``, the third argument `fileName` sets file name (not form field name), as it it were a name of the file in user's filesystem, +- `formData.append(name, blob, fileName)` - add a field as if it were ``, the third argument `fileName` sets file name (not form field name), as it were a name of the file in user's filesystem, - `formData.delete(name)` - remove the field with the given `name`, - `formData.get(name)` - get the value of the field with the given `name`, - `formData.has(name)` - if there exists a field with the given `name`, returns `true`, otherwise `false` A form is technically allowed to have many fields with the same `name`, so multiple calls to `append` add more same-named fields. -There's also method `set`, with the same syntax as `append`. The difference is that `.set` removes all fields with the given `name`, and then appends a new field. So it makes sure there's only field with such `name`, the rest is just like `append`: +There's also method `set`, with the same syntax as `append`. The difference is that `.set` removes all fields with the given `name`, and then appends a new field. So it makes sure there's only one field with such `name`, the rest is just like `append`: - `formData.set(name, value)`, - `formData.set(name, blob, fileName)`. diff --git a/5-network/05-fetch-crossorigin/cors-gmail-messages.svg b/5-network/05-fetch-crossorigin/cors-gmail-messages.svg index 48dc02930..8b6963345 100644 --- a/5-network/05-fetch-crossorigin/cors-gmail-messages.svg +++ b/5-network/05-fetch-crossorigin/cors-gmail-messages.svg @@ -1 +1 @@ -evil.comgot the cookie? okay!gmail.comGET /messagescookie: user=John{"messages": [...]} \ No newline at end of file +evil.comgot the cookie? okay!gmail.comGET /messagescookie: user=John{"messages": [...]} \ No newline at end of file diff --git a/5-network/10-long-polling/long-polling.svg b/5-network/10-long-polling/long-polling.svg index a2fd05b5d..bac56f114 100644 --- a/5-network/10-long-polling/long-polling.svg +++ b/5-network/10-long-polling/long-polling.svg @@ -1 +1 @@ -BrowserServerrequestconnection hangsconnection breaks end of requestdatarequestconnection hangsrequestdataconnection breaks end of request \ No newline at end of file +BrowserServerrequestconnection hangsconnection breaks end of requestdatarequestconnection hangsrequestdataconnection breaks end of request \ No newline at end of file diff --git a/5-network/11-websocket/article.md b/5-network/11-websocket/article.md index 4eaf47ed2..eab41b18f 100644 --- a/5-network/11-websocket/article.md +++ b/5-network/11-websocket/article.md @@ -119,9 +119,9 @@ For instance: - `Sec-WebSocket-Extensions: deflate-frame` means that the browser supports data compression. An extension is something related to transferring the data, functionality that extends WebSocket protocol. The header `Sec-WebSocket-Extensions` is sent automatically by the browser, with the list of all extenions it supports. -- `Sec-WebSocket-Protocol: soap, wamp` means that we'd like to transfer not just any data, but the data in [SOAP](http://en.wikipedia.org/wiki/SOAP) or WAMP ("The WebSocket Application Messaging Protocol") protocols. WebSocket subprotocols are registered in the [IANA catalogue](http://www.iana.org/assignments/websocket/websocket.xml). +- `Sec-WebSocket-Protocol: soap, wamp` means that we'd like to transfer not just any data, but the data in [SOAP](http://en.wikipedia.org/wiki/SOAP) or WAMP ("The WebSocket Application Messaging Protocol") protocols. WebSocket subprotocols are registered in the [IANA catalogue](http://www.iana.org/assignments/websocket/websocket.xml). So, this header describes data formats that we're going to use. - This optional header is set by us, to tell the server which subprotocols our code supports, using the second (optional) parameter of `new WebSocket`. That's the array of subprotocols, e.g. if we'd like to use SOAP or WAMP: + This optional header is set using the second parameter of `new WebSocket`. That's the array of subprotocols, e.g. if we'd like to use SOAP or WAMP: ```js let socket = new WebSocket("wss://javascript.info/chat", ["soap", "wamp"]); diff --git a/6-data-storage/01-cookie/cookie-third-party-2.svg b/6-data-storage/01-cookie/cookie-third-party-2.svg index 373e195d9..94003610a 100644 --- a/6-data-storage/01-cookie/cookie-third-party-2.svg +++ b/6-data-storage/01-cookie/cookie-third-party-2.svg @@ -1 +1 @@ -<img src="https://ads.com/banner.png">site.comads.comGET /banner.pngcookie: id=123 \ No newline at end of file +<img src="https://ads.com/banner.png">site.comads.comGET /banner.pngcookie: id=123 \ No newline at end of file diff --git a/6-data-storage/01-cookie/cookie-third-party-3.svg b/6-data-storage/01-cookie/cookie-third-party-3.svg index 76356a2d0..a67f09c7f 100644 --- a/6-data-storage/01-cookie/cookie-third-party-3.svg +++ b/6-data-storage/01-cookie/cookie-third-party-3.svg @@ -1 +1 @@ -<img src="https://ads.com/banner.png">other.comads.comGET /banner.pngcookie: id=123 \ No newline at end of file +<img src="https://ads.com/banner.png">other.comads.comGET /banner.pngcookie: id=123 \ No newline at end of file diff --git a/6-data-storage/01-cookie/cookie-third-party.svg b/6-data-storage/01-cookie/cookie-third-party.svg index 4a512a498..567f25595 100644 --- a/6-data-storage/01-cookie/cookie-third-party.svg +++ b/6-data-storage/01-cookie/cookie-third-party.svg @@ -1 +1 @@ -<img src="https://ads.com/banner.png">site.comads.comGET /banner.pngSet-Cookie: id=123 \ No newline at end of file +<img src="https://ads.com/banner.png">site.comads.comGET /banner.pngSet-Cookie: id=123 \ No newline at end of file diff --git a/6-data-storage/01-cookie/cookie-xsrf.svg b/6-data-storage/01-cookie/cookie-xsrf.svg index 88843043e..968b42d5b 100644 --- a/6-data-storage/01-cookie/cookie-xsrf.svg +++ b/6-data-storage/01-cookie/cookie-xsrf.svg @@ -1 +1 @@ -<form action="https://bank.com/pay"> .... </form>evil.comgot the cookie? okay!bank.comPOST /paycookie: user=John \ No newline at end of file +<form action="https://bank.com/pay"> .... </form>evil.comgot the cookie? okay!bank.comPOST /paycookie: user=John \ No newline at end of file diff --git a/6-data-storage/03-indexeddb/article.md b/6-data-storage/03-indexeddb/article.md index acf5a9d1a..861a90877 100644 --- a/6-data-storage/03-indexeddb/article.md +++ b/6-data-storage/03-indexeddb/article.md @@ -470,9 +470,9 @@ Methods that involve searching support either exact keys or so-called "range que Ranges are created using following calls: -- `IDBKeyRange.lowerBound(lower, [open])` means: `>lower` (or `≥lower` if `open` is true) -- `IDBKeyRange.upperBound(upper, [open])` means: `lower` if `open` is true) +- `IDBKeyRange.upperBound(upper, [open])` means: `≤upper` (or `= 'js' +// get all keys: id > 'js' books.getAllKeys(IDBKeyRange.lowerBound('js', true)) ``` @@ -580,7 +580,7 @@ request.onsuccess = function() { We can also use `IDBKeyRange` to create ranges and looks for cheap/expensive books: ```js -// find books where price < 5 +// find books where price <= 5 let request = priceIndex.getAll(IDBKeyRange.upperBound(5)); ``` diff --git a/6-data-storage/03-indexeddb/indexeddb-index.svg b/6-data-storage/03-indexeddb/indexeddb-index.svg index de4e7fa02..9bee3ef81 100644 --- a/6-data-storage/03-indexeddb/indexeddb-index.svg +++ b/6-data-storage/03-indexeddb/indexeddb-index.svg @@ -1 +1 @@ -id: 'html' price: 3id: 'css' price: 53: ['html'] 5: ['css'] 10: ['js','nodejs']id: 'js' price: 10id: 'nodejs' price: 10booksindex \ No newline at end of file +id: 'html' price: 3id: 'css' price: 53: ['html'] 5: ['css'] 10: ['js','nodejs']id: 'js' price: 10id: 'nodejs' price: 10booksindex \ No newline at end of file diff --git a/8-web-components/2-custom-elements/article.md b/8-web-components/2-custom-elements/article.md index 443731914..702ff9073 100644 --- a/8-web-components/2-custom-elements/article.md +++ b/8-web-components/2-custom-elements/article.md @@ -397,4 +397,4 @@ Custom elements can be of two types: /*