From 5189d51cd796963ac2482cf8f138296b9eab003d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20=C4=90=E1=BB=A9c=20H=C3=A0o?= <39975444+haond10adp@users.noreply.github.com> Date: Sat, 26 Sep 2020 09:16:33 +0700 Subject: [PATCH 01/39] minor change --- 2-ui/5-loading/02-script-async-defer/article.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/2-ui/5-loading/02-script-async-defer/article.md b/2-ui/5-loading/02-script-async-defer/article.md index ae92dee85..df9dfb5e4 100644 --- a/2-ui/5-loading/02-script-async-defer/article.md +++ b/2-ui/5-loading/02-script-async-defer/article.md @@ -3,7 +3,7 @@ In modern websites, scripts are often "heavier" than HTML: their download size is larger, and processing time is also longer. -When the browser loads HTML and comes across a `` tag, it can't continue building the DOM. It must execute the script right now. The same happens for external scripts ``: the browser must wait until the script downloads, execute it, and only after process the rest of the page. +When the browser loads HTML and comes across a `` tag, it can't continue building the DOM. It must execute the script right now. The same happens for external scripts ``: the browser must wait for the script to download, execute the downloaded script, and only then can it process the rest of the page. That leads to two important issues: From 02e82ad409525c87db8dec0b1f350a62cb071ecf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20=C4=90=E1=BB=A9c=20H=C3=A0o?= <39975444+haond10adp@users.noreply.github.com> Date: Sat, 26 Sep 2020 17:08:31 +0700 Subject: [PATCH 02/39] Update article.md --- 2-ui/5-loading/02-script-async-defer/article.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/2-ui/5-loading/02-script-async-defer/article.md b/2-ui/5-loading/02-script-async-defer/article.md index df9dfb5e4..24af956dc 100644 --- a/2-ui/5-loading/02-script-async-defer/article.md +++ b/2-ui/5-loading/02-script-async-defer/article.md @@ -185,7 +185,7 @@ But there are also essential differences between them: | | Order | `DOMContentLoaded` | |---------|---------|---------| -| `async` | *Load-first order*. Their document order doesn't matter -- which loads first | Irrelevant. May load and execute while the document has not yet been fully downloaded. That happens if scripts are small or cached, and the document is long enough. | +| `async` | *Load-first order*. Their document order doesn't matter -- which loads first runs first | Irrelevant. May load and execute while the document has not yet been fully downloaded. That happens if scripts are small or cached, and the document is long enough. | | `defer` | *Document order* (as they go in the document). | Execute after the document is loaded and parsed (they wait if needed), right before `DOMContentLoaded`. | In practice, `defer` is used for scripts that need the whole DOM and/or their relative execution order is important. From 73550e58ac46f4af3b18ad46c650dcd8c27e1b92 Mon Sep 17 00:00:00 2001 From: Milo Silva <56648742+Mr0cket@users.noreply.github.com> Date: Sun, 27 Sep 2020 11:08:51 +0200 Subject: [PATCH 03/39] Update 'properties' to 'property values' The task asks to multiply numeric object 'properties', however the term 'object properties' include both the 'property keys' of the object as well as the 'property values' of the object. It is clear the intention of the task is to change property values. This change improves clarity of the task description. --- 1-js/04-object-basics/01-object/8-multiply-numeric/task.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/1-js/04-object-basics/01-object/8-multiply-numeric/task.md b/1-js/04-object-basics/01-object/8-multiply-numeric/task.md index 33eb89220..6878ca088 100644 --- a/1-js/04-object-basics/01-object/8-multiply-numeric/task.md +++ b/1-js/04-object-basics/01-object/8-multiply-numeric/task.md @@ -2,9 +2,9 @@ importance: 3 --- -# Multiply numeric properties by 2 +# Multiply numeric property values by 2 -Create a function `multiplyNumeric(obj)` that multiplies all numeric properties of `obj` by `2`. +Create a function `multiplyNumeric(obj)` that multiplies all numeric property values of `obj` by `2`. For instance: From 2c65914e3bc4762c2c86570fb7ec9761c859fc7e Mon Sep 17 00:00:00 2001 From: Zhi Yin Date: Sun, 27 Sep 2020 16:13:21 -0400 Subject: [PATCH 04/39] correct minor typo --- .../2-async-iterators-generators/article.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/1-js/12-generators-iterators/2-async-iterators-generators/article.md b/1-js/12-generators-iterators/2-async-iterators-generators/article.md index 704ba0672..10375f635 100644 --- a/1-js/12-generators-iterators/2-async-iterators-generators/article.md +++ b/1-js/12-generators-iterators/2-async-iterators-generators/article.md @@ -155,7 +155,7 @@ Now let's recall generators, as they allow to make iteration code much shorter. For sheer simplicity, omitting some important stuff, they are "functions that generate (yield) values". They are explained in detail in the chapter [](info:generators). -Generators are labelled with `function*` (note the start) and use `yield` to generate a value, then we can use `for..of` to loop over them. +Generators are labelled with `function*` (note the star) and use `yield` to generate a value, then we can use `for..of` to loop over them. This example generates a sequence of values from `start` to `end`: From af95ad157013f779b8f758ffa39e8fb2c36aad23 Mon Sep 17 00:00:00 2001 From: Denis Ivanov Date: Mon, 28 Sep 2020 17:02:28 +0300 Subject: [PATCH 05/39] Fix wrong variables names. --- 6-data-storage/03-indexeddb/article.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/6-data-storage/03-indexeddb/article.md b/6-data-storage/03-indexeddb/article.md index 5aeeceeaa..af39d2c49 100644 --- a/6-data-storage/03-indexeddb/article.md +++ b/6-data-storage/03-indexeddb/article.md @@ -551,7 +551,7 @@ openRequest.onupgradeneeded = function() { // we must create the index here, in versionchange transaction let books = db.createObjectStore('books', {keyPath: 'id'}); *!* - let index = inventory.createIndex('price_idx', 'price'); + let index = books.createIndex('price_idx', 'price'); */!* }; ``` @@ -698,7 +698,7 @@ let request = priceIdx.openCursor(IDBKeyRange.upperBound(5)); request.onsuccess = function() { let cursor = request.result; if (cursor) { - let key = cursor.primaryKey; // next object store key (id field) + let primaryKey = cursor.primaryKey; // next object store key (id field) let value = cursor.value; // next object store object (book object) let key = cursor.key; // next index key (price) console.log(key, value); @@ -718,7 +718,7 @@ Let's use a thin promise wrapper further Then, instead of `onsuccess/onerror` we can write like this: ```js -let db = await idb.openDb('store', 1, db => { +let db = await idb.openDB('store', 1, db => { if (db.oldVersion == 0) { // perform the initialization db.createObjectStore('books', {keyPath: 'id'}); From c970f424c2f7d011167982201ae75502b3a2ef51 Mon Sep 17 00:00:00 2001 From: Vse Mozhe Buty Date: Mon, 28 Sep 2020 17:54:20 +0300 Subject: [PATCH 06/39] Fix link in 1.2.18 The current link is redirected to the parent chapter. This fix is in sync with https://javascript.info/operators#bitwise-operators and #2117 --- 1-js/02-first-steps/18-javascript-specials/article.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/1-js/02-first-steps/18-javascript-specials/article.md b/1-js/02-first-steps/18-javascript-specials/article.md index 91be0aa45..97d0457bb 100644 --- a/1-js/02-first-steps/18-javascript-specials/article.md +++ b/1-js/02-first-steps/18-javascript-specials/article.md @@ -144,7 +144,7 @@ Assignments : There is a simple assignment: `a = b` and combined ones like `a *= 2`. Bitwise -: Bitwise operators work with 32-bit integers at the lowest, bit-level: see the [docs](mdn:/JavaScript/Reference/Operators/Bitwise_Operators) when they are needed. +: Bitwise operators work with 32-bit integers at the lowest, bit-level: see the [docs](mdn:/JavaScript/Guide/Expressions_and_Operators#Bitwise) when they are needed. Conditional : The only operator with three parameters: `cond ? resultA : resultB`. If `cond` is truthy, returns `resultA`, otherwise `resultB`. From f11dd6d07bf700e002a2066987bcca2ad279f951 Mon Sep 17 00:00:00 2001 From: CrypterSr <59452633+CrypterSr@users.noreply.github.com> Date: Thu, 1 Oct 2020 23:24:12 +0530 Subject: [PATCH 07/39] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4b19fc5cf..6f538f64c 100755 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ The type of the material is defined by the file inside the folder: - `index.md` stands for a chapter - `article.md` stands for an article - - `task.md` stands for a task (solution must be provided in `solution.md` file aswell) + - `task.md` stands for a task (solution must be provided in `solution.md` file as well) Each of these files starts from the `# Main header`. From af9b33925c60a257384efb3bf40274b15aa74138 Mon Sep 17 00:00:00 2001 From: Anurag Chauahan <72224544+Anurag-Chauhan-289@users.noreply.github.com> Date: Fri, 2 Oct 2020 00:46:50 +0530 Subject: [PATCH 08/39] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4b19fc5cf..8d7ce909f 100755 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # The Modern JavaScript Tutorial -This repository hosts the English content of the Modern JavaScript Tutorial, published at [https://javascript.info](https://javascript.info). +This repository hosts the English content of the Modern JavaScript Tutorial, published in [https://javascript.info](https://javascript.info). ## Translations @@ -28,7 +28,7 @@ The type of the material is defined by the file inside the folder: - `index.md` stands for a chapter - `article.md` stands for an article - - `task.md` stands for a task (solution must be provided in `solution.md` file aswell) + - `task.md` stands for a task (solution must be provided in `solution.md` file as well) Each of these files starts from the `# Main header`. From 1d13d1155dfd6f6ea23c14d09503485bf97b43b7 Mon Sep 17 00:00:00 2001 From: Anurag Chauahan <72224544+Anurag-Chauhan-289@users.noreply.github.com> Date: Fri, 2 Oct 2020 00:53:48 +0530 Subject: [PATCH 09/39] Update article.md --- 3-frames-and-windows/01-popup-windows/article.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/3-frames-and-windows/01-popup-windows/article.md b/3-frames-and-windows/01-popup-windows/article.md index ab9515743..7540b0304 100644 --- a/3-frames-and-windows/01-popup-windows/article.md +++ b/3-frames-and-windows/01-popup-windows/article.md @@ -7,7 +7,7 @@ Basically, you just run: window.open('https://javascript.info/') ``` -...And it will open a new window with given URL. Most modern browsers are configured to open new tabs instead of separate windows. +...And it will open a new window with given URL. Most modern browsers are configured to open url in new tabs instead of separate windows. Popups exist from really ancient times. The initial idea was to show another content without closing the main window. As of now, there are other ways to do that: we can load content dynamically with [fetch](info:fetch) and show it in a dynamically generated `
`. So, popups is not something we use everyday. @@ -15,7 +15,7 @@ Also, popups are tricky on mobile devices, that don't show multiple windows simu Still, there are tasks where popups are still used, e.g. for OAuth authorization (login with Google/Facebook/...), because: -1. A popup is a separate window with its own independent JavaScript environment. So opening a popup from a third-party non-trusted site is safe. +1. A popup is a separate window which has its own independent JavaScript environment. So opening a popup from a third-party, non-trusted site is safe. 2. It's very easy to open a popup. 3. A popup can navigate (change URL) and send messages to the opener window. @@ -89,7 +89,7 @@ There is also a number of less supported browser-specific features, which are us ## Example: a minimalistic window -Let's open a window with minimal set of features just to see which of them browser allows to disable: +Let's open a window with minimal set of features, just to see which of them browser allows to disable: ```js run let params = `scrollbars=no,resizable=no,status=no,location=no,toolbar=no,menubar=no, From 6fca8a4b310953a87ffedf0d06b1bbed0ef90485 Mon Sep 17 00:00:00 2001 From: Ilya Kantor Date: Mon, 5 Oct 2020 14:54:28 +0300 Subject: [PATCH 10/39] minor fixes --- 2-ui/5-loading/03-onload-onerror/article.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/2-ui/5-loading/03-onload-onerror/article.md b/2-ui/5-loading/03-onload-onerror/article.md index 162c9060e..590e54ab4 100644 --- a/2-ui/5-loading/03-onload-onerror/article.md +++ b/2-ui/5-loading/03-onload-onerror/article.md @@ -41,8 +41,8 @@ document.head.append(script); *!* script.onload = function() { - // the script creates a helper function "_" - alert(_); // the function is available + // the script creates a variable "_" + alert( _.VERSION ); // shows library version }; */!* ``` From 4320617f8a0b1103cfcb9e34426a4172fdb718ad Mon Sep 17 00:00:00 2001 From: Vse Mozhe Buty Date: Mon, 5 Oct 2020 19:59:16 +0300 Subject: [PATCH 11/39] Fix syntax enumeration in 1.4.7 Currently, syntax of the 1 and 3 cases is the same. --- 1-js/04-object-basics/07-optional-chaining/article.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/1-js/04-object-basics/07-optional-chaining/article.md b/1-js/04-object-basics/07-optional-chaining/article.md index 0d832e687..e2dd08ba9 100644 --- a/1-js/04-object-basics/07-optional-chaining/article.md +++ b/1-js/04-object-basics/07-optional-chaining/article.md @@ -164,7 +164,7 @@ The `?.` syntax has three forms: 1. `obj?.prop` -- returns `obj.prop` if `obj` exists, otherwise `undefined`. 2. `obj?.[prop]` -- returns `obj[prop]` if `obj` exists, otherwise `undefined`. -3. `obj?.method()` -- calls `obj.method()` if `obj` exists, otherwise returns `undefined`. +3. `obj.method?.()` -- calls `obj.method()` if `obj.method` exists, otherwise returns `undefined`. As we can see, all of them are straightforward and simple to use. The `?.` checks the left part for `null/undefined` and allows the evaluation to proceed if it's not so. From f409905f7b390770238c8fa40ac14ab680148c5c Mon Sep 17 00:00:00 2001 From: Ilya Kantor Date: Tue, 6 Oct 2020 00:47:35 +0300 Subject: [PATCH 12/39] minor fixes --- .../07-optional-chaining/article.md | 44 +++--- .../article.md | 129 ++++++++++-------- 2 files changed, 98 insertions(+), 75 deletions(-) diff --git a/1-js/04-object-basics/07-optional-chaining/article.md b/1-js/04-object-basics/07-optional-chaining/article.md index e2dd08ba9..1168ff2b0 100644 --- a/1-js/04-object-basics/07-optional-chaining/article.md +++ b/1-js/04-object-basics/07-optional-chaining/article.md @@ -3,13 +3,15 @@ [recent browser="new"] -The optional chaining `?.` is an error-proof way to access nested object properties, even if an intermediate property doesn't exist. +The optional chaining `?.` is a safe way to access nested object properties, even if an intermediate property doesn't exist. -## The problem +## The "non-existing property" problem If you've just started to read the tutorial and learn JavaScript, maybe the problem hasn't touched you yet, but it's quite common. -For example, some of our users have addresses, but few did not provide them. Then we can't safely read `user.address.street`: +As an example, let's consider objects for user data. Most of our users enter addresses, but some did not provide them. + +In such case, when we attempt to get `user.address.street`, we'll get an error: ```js run let user = {}; // the user happens to be without address @@ -17,7 +19,7 @@ let user = {}; // the user happens to be without address alert(user.address.street); // Error! ``` -Or, in the web development, we'd like to get an information about an element on the page, but it may not exist: +Another example. In the web development, we may need to get an information about an element on the page, that sometimes doesn't exist: ```js run // Error if the result of querySelector(...) is null @@ -34,7 +36,7 @@ let user = {}; // user has no address alert( user && user.address && user.address.street ); // undefined (no error) ``` -AND'ing the whole path to the property ensures that all components exist, but is cumbersome to write. +AND'ing the whole path to the property ensures that all components exist (if not, the evaluation stops), but is cumbersome to write. ## Optional chaining @@ -70,7 +72,7 @@ We should use `?.` only where it's ok that something doesn't exist. For example, if according to our coding logic `user` object must be there, but `address` is optional, then `user.address?.street` would be better. -So, if `user` happens to be undefined due to a mistake, we'll know about it and fix it. Otherwise, coding errors can be silenced where not appropriate, and become more difficult to debug. +So, if `user` happens to be undefined due to a mistake, we'll see a programming error about it and fix it. Otherwise, coding errors can be silenced where not appropriate, and become more difficult to debug. ``` ````warn header="The variable before `?.` must be declared" @@ -80,25 +82,27 @@ If there's no variable `user` at all, then `user?.anything` triggers an error: // ReferenceError: user is not defined user?.address; ``` -There must be `let/const/var user`. The optional chaining works only for declared variables. +There must be a declaration (e.g. `let/const/var user`). The optional chaining works only for declared variables. ```` ## Short-circuiting As it was said before, the `?.` immediately stops ("short-circuits") the evaluation if the left part doesn't exist. -So, if there are any further function calls or side effects, they don't occur: +So, if there are any further function calls or side effects, they don't occur. + +For instance: ```js run let user = null; let x = 0; -user?.sayHi(x++); // nothing happens +user?.sayHi(x++); // no "sayHi", so the execution doesn't reach x++ alert(x); // 0, value not incremented ``` -## Other cases: ?.(), ?.[] +## Other variants: ?.(), ?.[] The optional chaining `?.` is not an operator, but a special syntax construct, that also works with functions and square brackets. @@ -121,9 +125,9 @@ user2.admin?.(); */!* ``` -Here, in both lines we first use the dot `.` to get `admin` property, because the user object must exist, so it's safe read from it. +Here, in both lines we first use the dot (`user1.admin`) to get `admin` property, because the user object must exist, so it's safe read from it. -Then `?.()` checks the left part: if the admin function exists, then it runs (for `user1`). Otherwise (for `user2`) the evaluation stops without errors. +Then `?.()` checks the left part: if the admin function exists, then it runs (that's so for `user1`). Otherwise (for `user2`) the evaluation stops without errors. The `?.[]` syntax also works, if we'd like to use brackets `[]` to access properties instead of dot `.`. Similar to previous cases, it allows to safely read a property from an object that may not exist. @@ -148,19 +152,23 @@ Also we can use `?.` with `delete`: delete user?.name; // delete user.name if user exists ``` -```warn header="We can use `?.` for safe reading and deleting, but not writing" -The optional chaining `?.` has no use at the left side of an assignment: +````warn header="We can use `?.` for safe reading and deleting, but not writing" +The optional chaining `?.` has no use at the left side of an assignment. +For example: ```js run -// the idea of the code below is to write user.name, if user exists +let user = null; user?.name = "John"; // Error, doesn't work // because it evaluates to undefined = "John" ``` +It's just not that smart. +```` + ## Summary -The `?.` syntax has three forms: +The optional chaining `?.` syntax has three forms: 1. `obj?.prop` -- returns `obj.prop` if `obj` exists, otherwise `undefined`. 2. `obj?.[prop]` -- returns `obj[prop]` if `obj` exists, otherwise `undefined`. @@ -170,6 +178,4 @@ As we can see, all of them are straightforward and simple to use. The `?.` check A chain of `?.` allows to safely access nested properties. -Still, we should apply `?.` carefully, only where it's ok that the left part doesn't to exist. - -So that it won't hide programming errors from us, if they occur. +Still, we should apply `?.` carefully, only where it's acceptable that the left part doesn't to exist. So that it won't hide programming errors from us, if they occur. diff --git a/9-regular-expressions/15-regexp-catastrophic-backtracking/article.md b/9-regular-expressions/15-regexp-catastrophic-backtracking/article.md index 59467f938..c66a1a219 100644 --- a/9-regular-expressions/15-regexp-catastrophic-backtracking/article.md +++ b/9-regular-expressions/15-regexp-catastrophic-backtracking/article.md @@ -1,20 +1,20 @@ # Catastrophic backtracking -Some regular expressions are looking simple, but can execute veeeeeery long time, and even "hang" the JavaScript engine. +Some regular expressions are looking simple, but can execute a veeeeeery long time, and even "hang" the JavaScript engine. -Sooner or later most developers occasionally face such behavior, because it's quite easy to create such a regexp. - -The typical symptom -- a regular expression works fine sometimes, but for certain strings it "hangs", consuming 100% of CPU. +Sooner or later most developers occasionally face such behavior. The typical symptom -- a regular expression works fine sometimes, but for certain strings it "hangs", consuming 100% of CPU. In such case a web-browser suggests to kill the script and reload the page. Not a good thing for sure. -For server-side JavaScript it may become a vulnerability if regular expressions process user data. +For server-side JavaScript such a regexp may hang the server process, that's even worse. So we definitely should take a look at it. ## Example -Let's say we have a string, and we'd like to check if it consists of words `pattern:\w+` with an optional space `pattern:\s?` after each. +Let's say we have a string, and we'd like to check if it consists of words `pattern:\w+` with an optional space `pattern:\s?` after each. + +An obvious way to construct a regexp would be to take a word followed by an optional space `pattern:\w+\s?` and then repeat it with `*`. -We'll use a regexp `pattern:^(\w+\s?)*$`, it specifies 0 or more such words. +That leads us to the regexp `pattern:^(\w+\s?)*$`, it specifies zero or more such words, that start at the beginning `pattern:^` and finish at the end `pattern:$` of the line. In action: @@ -25,9 +25,9 @@ alert( regexp.test("A good string") ); // true alert( regexp.test("Bad characters: $@#") ); // false ``` -It seems to work. The result is correct. Although, on certain strings it takes a lot of time. So long that JavaScript engine "hangs" with 100% CPU consumption. +The regexp seems to work. The result is correct. Although, on certain strings it takes a lot of time. So long that JavaScript engine "hangs" with 100% CPU consumption. -If you run the example below, you probably won't see anything, as JavaScript will just "hang". A web-browser will stop reacting on events, the UI will stop working. After some time it will suggest to reloaad the page. So be careful with this: +If you run the example below, you probably won't see anything, as JavaScript will just "hang". A web-browser will stop reacting on events, the UI will stop working (most browsers allow only scrolling). After some time it will suggest to reload the page. So be careful with this: ```js run let regexp = /^(\w+\s?)*$/; @@ -37,24 +37,22 @@ let str = "An input string that takes a long time or even makes this regexp to h alert( regexp.test(str) ); ``` -Some regular expression engines can handle such search, but most of them can't. +To be fair, let's note that some regular expression engines can handle such a search effectively. But most of them can't. Browser engines usually hang. ## Simplified example -What's the matter? Why the regular expression "hangs"? +What's the matter? Why the regular expression hangs? To understand that, let's simplify the example: remove spaces `pattern:\s?`. Then it becomes `pattern:^(\w+)*$`. And, to make things more obvious, let's replace `pattern:\w` with `pattern:\d`. The resulting regular expression still hangs, for instance: - - ```js run let regexp = /^(\d+)*$/; -let str = "012345678901234567890123456789!"; +let str = "012345678901234567890123456789z"; -// will take a very long time +// will take a very long time (careful!) alert( regexp.test(str) ); ``` @@ -62,45 +60,49 @@ So what's wrong with the regexp? First, one may notice that the regexp `pattern:(\d+)*` is a little bit strange. The quantifier `pattern:*` looks extraneous. If we want a number, we can use `pattern:\d+`. -Indeed, the regexp is artificial. But the reason why it is slow is the same as those we saw above. So let's understand it, and then the previous example will become obvious. +Indeed, the regexp is artificial, we got it by simplifying the previous example. But the reason why it is slow is the same. So let's understand it, and then the previous example will become obvious. -What happens during the search of `pattern:^(\d+)*$` in the line `subject:123456789!` (shortened a bit for clarity), why does it take so long? +What happens during the search of `pattern:^(\d+)*$` in the line `subject:123456789z` (shortened a bit for clarity, please note a non-digit character `subject:z` at the end, it's important), why does it take so long? -1. First, the regexp engine tries to find a number `pattern:\d+`. The plus `pattern:+` is greedy by default, so it consumes all digits: +Here's what the regexp engine does: + +1. First, the regexp engine tries to find the content of the parentheses: the number `pattern:\d+`. The plus `pattern:+` is greedy by default, so it consumes all digits: ``` \d+....... (123456789)z ``` - Then it tries to apply the star quantifier, but there are no more digits, so it the star doesn't give anything. + After all digits are consumed, `pattern:\d+` is considered found (as `match:123456789`). + + Then the star quantifier `pattern:(\d+)*` applies. But there are no more digits in the text, so the star doesn't give anything. - The next in the pattern is the string end `pattern:$`, but in the text we have `subject:!`, so there's no match: + The next character in the pattern is the string end `pattern:$`. But in the text we have `subject:z` instead, so there's no match: ``` X \d+........$ - (123456789)! + (123456789)z ``` 2. As there's no match, the greedy quantifier `pattern:+` decreases the count of repetitions, backtracks one character back. - Now `pattern:\d+` takes all digits except the last one: + Now `pattern:\d+` takes all digits except the last one (`match:12345678`): ``` \d+....... - (12345678)9! + (12345678)9z ``` -3. Then the engine tries to continue the search from the new position (`9`). +3. Then the engine tries to continue the search from the next position (right after `match:12345678`). - The star `pattern:(\d+)*` can be applied -- it gives the number `match:9`: + The star `pattern:(\d+)*` can be applied -- it gives one more match of `pattern:\d+`, the number `match:9`: ``` \d+.......\d+ - (12345678)(9)! + (12345678)(9)z ``` - The engine tries to match `pattern:$` again, but fails, because meets `subject:!`: + The engine tries to match `pattern:$` again, but fails, because it meets `subject:z` instead: ``` X @@ -118,7 +120,7 @@ What happens during the search of `pattern:^(\d+)*$` in the line `subject:123456 ``` X \d+......\d+ - (1234567)(89)! + (1234567)(89)z ``` The first number has 7 digits, and then two numbers of 1 digit each: @@ -126,7 +128,7 @@ What happens during the search of `pattern:^(\d+)*$` in the line `subject:123456 ``` X \d+......\d+\d+ - (1234567)(8)(9)! + (1234567)(8)(9)z ``` The first number has 6 digits, and then a number of 3 digits: @@ -134,7 +136,7 @@ What happens during the search of `pattern:^(\d+)*$` in the line `subject:123456 ``` X \d+.......\d+ - (123456)(789)! + (123456)(789)z ``` The first number has 6 digits, and then 2 numbers: @@ -142,23 +144,19 @@ What happens during the search of `pattern:^(\d+)*$` in the line `subject:123456 ``` X \d+.....\d+ \d+ - (123456)(78)(9)! + (123456)(78)(9)z ``` ...And so on. -There are many ways to split a set of digits `123456789` into numbers. To be precise, there are 2n-1, where `n` is the length of the set. +There are many ways to split a sequence of digits `123456789` into numbers. To be precise, there are 2n-1, where `n` is the length of the sequence. -For `n=20` there are about 1 million combinations, for `n=30` - a thousand times more. Trying each of them is exactly the reason why the search takes so long. +- For `123456789` we have `n=9`, that gives 511 combinations. +- For a longer sequence with `n=20` there are about one million (1048575) combinations. +- For `n=30` - a thousand times more (1073741823 combinations). -What to do? - -Should we turn on the lazy mode? - -Unfortunately, that won't help: if we replace `pattern:\d+` with `pattern:\d+?`, the regexp will still hang. The order of combinations will change, but not their total count. - -Some regular expression engines have tricky tests and finite automations that allow to avoid going through all combinations or make it much faster, but not all engines, and not in all cases. +Trying each of them is exactly the reason why the search takes so long. ## Back to words and strings @@ -176,7 +174,15 @@ The reason is that a word can be represented as one `pattern:\w+` or many: For a human, it's obvious that there may be no match, because the string ends with an exclamation sign `!`, but the regular expression expects a wordly character `pattern:\w` or a space `pattern:\s` at the end. But the engine doesn't know that. -It tries all combinations of how the regexp `pattern:(\w+\s?)*` can "consume" the string, including variants with spaces `pattern:(\w+\s)*` and without them `pattern:(\w+)*` (because spaces `pattern:\s?` are optional). As there are many such combinations, the search takes a lot of time. +It tries all combinations of how the regexp `pattern:(\w+\s?)*` can "consume" the string, including variants with spaces `pattern:(\w+\s)*` and without them `pattern:(\w+)*` (because spaces `pattern:\s?` are optional). As there are many such combinations (we've seen it with digits), the search takes a lot of time. + +What to do? + +Should we turn on the lazy mode? + +Unfortunately, that won't help: if we replace `pattern:\w+` with `pattern:\w+?`, the regexp will still hang. The order of combinations will change, but not their total count. + +Some regular expression engines have tricky tests and finite automations that allow to avoid going through all combinations or make it much faster, but most engines don't, and it doesn't always help. ## How to fix? @@ -184,7 +190,7 @@ There are two main approaches to fixing the problem. The first is to lower the number of possible combinations. -Let's rewrite the regular expression as `pattern:^(\w+\s)*\w*` - we'll look for any number of words followed by a space `pattern:(\w+\s)*`, and then (optionally) a word `pattern:\w*`. +Let's make the space non-optional by rewriting the regular expression as `pattern:^(\w+\s)*\w*$` - we'll look for any number of words followed by a space `pattern:(\w+\s)*`, and then (optionally) a final word `pattern:\w*`. This regexp is equivalent to the previous one (matches the same) and works well: @@ -197,26 +203,30 @@ alert( regexp.test(str) ); // false Why did the problem disappear? -Now the star `pattern:*` goes after `pattern:\w+\s` instead of `pattern:\w+\s?`. It became impossible to represent one word of the string with multiple successive `pattern:\w+`. The time needed to try such combinations is now saved. +That's because now the space is mandatory. -For example, the previous pattern `pattern:(\w+\s?)*` could match the word `subject:string` as two `pattern:\w+`: +The previous regexp, if we omit the space, becomes `pattern:(\w+)*`, leading to many combinations of `\w+` within a single word + +So `subject:input` could be matched as two repetitions of `pattern:\w+`, like this: -```js run -\w+\w+ -string +``` +\w+ \w+ +(inp)(ut) ``` -The previous pattern, due to the optional `pattern:\s` allowed variants `pattern:\w+`, `pattern:\w+\s`, `pattern:\w+\w+` and so on. +The new pattern is different: `pattern:(\w+\s)*` specifies repetitions of words followed by a space! The `subject:input` string can't be matched as two repetitions of `pattern:\w+\s`, because the space is mandatory. -With the rewritten pattern `pattern:(\w+\s)*`, that's impossible: there may be `pattern:\w+\s` or `pattern:\w+\s\w+\s`, but not `pattern:\w+\w+`. So the overall combinations count is greatly decreased. +The time needed to try a lot of (actually most of) combinations is now saved. ## Preventing backtracking -It's not always convenient to rewrite a regexp. And it's not always obvious how to do it. +It's not always convenient to rewrite a regexp though. In the example above it was easy, but it's not always obvious how to do it. + +Besides, a rewritten regexp is usually more complex, and that's not good. Regexps are complex enough without extra efforts. -The alternative approach is to forbid backtracking for the quantifier. +Luckily, there's an alternative approach. We can forbid backtracking for the quantifier. -The regular expressions engine tries many combinations that are obviously wrong for a human. +The root of the problem is that the regexp engine tries many combinations that are obviously wrong for a human. E.g. in the regexp `pattern:(\d+)*$` it's obvious for a human, that `pattern:+` shouldn't backtrack. If we replace one `pattern:\d+` with two separate `pattern:\d+\d+`, nothing changes: @@ -230,19 +240,26 @@ E.g. in the regexp `pattern:(\d+)*$` it's obvious for a human, that `pattern:+` And in the original example `pattern:^(\w+\s?)*$` we may want to forbid backtracking in `pattern:\w+`. That is: `pattern:\w+` should match a whole word, with the maximal possible length. There's no need to lower the repetitions count in `pattern:\w+`, try to split it into two words `pattern:\w+\w+` and so on. -Modern regular expression engines support possessive quantifiers for that. They are like greedy ones, but don't backtrack (so they are actually simpler than regular quantifiers). +Modern regular expression engines support possessive quantifiers for that. Regular quantifiers become possessive if we add `pattern:+` after them. That is, we use `pattern:\d++` instead of `pattern:\d+` to stop `pattern:+` from backtracking. + +Possessive quantifiers are in fact simpler than "regular" ones. They just match as many as they can, without any backtracking. The search process without bracktracking is simpler. There are also so-called "atomic capturing groups" - a way to disable backtracking inside parentheses. -Unfortunately, in JavaScript they are not supported. But there's another way. +...But the bad news is that, unfortunately, in JavaScript they are not supported. + +We can emulate them though using a "lookahead transform". ### Lookahead to the rescue! -We can prevent backtracking using lookahead. +So we've come to real advanced topics. We'd like a quantifier, such as `pattern:+` not to backtrack, because sometimes backtracking makes no sense. + +The pattern to take as much repetitions of `pattern:\w` as possible without backtracking is: `pattern:(?=(\w+))\1`. Of course, we could take another pattern instead of `pattern:\w`. -The pattern to take as much repetitions of `pattern:\w` as possible without backtracking is: `pattern:(?=(\w+))\1`. +That may seem odd, but it's actually a very simple transform. Let's decipher it: + - Lookahead `pattern:?=` looks forward for the longest word `pattern:\w+` starting at the current position. - The contents of parentheses with `pattern:?=...` isn't memorized by the engine, so wrap `pattern:\w+` into parentheses. Then the engine will memorize their contents - ...And allow us to reference it in the pattern as `pattern:\1`. From 545f5147906fbec5f3c0e94ba3bd4652ee24712a Mon Sep 17 00:00:00 2001 From: Alexey Pyltsyn Date: Tue, 6 Oct 2020 02:14:09 +0300 Subject: [PATCH 13/39] Remove extra trailing style tag --- 8-web-components/6-shadow-dom-style/article.md | 1 - 1 file changed, 1 deletion(-) diff --git a/8-web-components/6-shadow-dom-style/article.md b/8-web-components/6-shadow-dom-style/article.md index 2be81fbb2..83a6962fa 100644 --- a/8-web-components/6-shadow-dom-style/article.md +++ b/8-web-components/6-shadow-dom-style/article.md @@ -259,7 +259,6 @@ For example, in shadow DOM we can use `--user-card-field-color` CSS variable to
Name:
Birthday:
- ``` Then, we can declare this property in the outer document for ``: From f4cf5808c81d6d09c1f56fdac613073fc1e1c3ac Mon Sep 17 00:00:00 2001 From: Ilya Kantor Date: Tue, 6 Oct 2020 13:04:27 +0300 Subject: [PATCH 14/39] minor fixes --- 1-js/04-object-basics/07-optional-chaining/article.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/1-js/04-object-basics/07-optional-chaining/article.md b/1-js/04-object-basics/07-optional-chaining/article.md index 1168ff2b0..c4a042130 100644 --- a/1-js/04-object-basics/07-optional-chaining/article.md +++ b/1-js/04-object-basics/07-optional-chaining/article.md @@ -9,17 +9,19 @@ The optional chaining `?.` is a safe way to access nested object properties, eve If you've just started to read the tutorial and learn JavaScript, maybe the problem hasn't touched you yet, but it's quite common. -As an example, let's consider objects for user data. Most of our users enter addresses, but some did not provide them. +As an example, let's consider objects for user data. Most of our users have addresses in `user.address` property, with the street `user.address.street`, but some did not provide them. In such case, when we attempt to get `user.address.street`, we'll get an error: ```js run -let user = {}; // the user happens to be without address +let user = {}; // the user without "address" property alert(user.address.street); // Error! ``` -Another example. In the web development, we may need to get an information about an element on the page, that sometimes doesn't exist: +That's the expected result, JavaScript works like this, but many practical cases we'd prefer to get `undefined` instead of an error (meaning "no street"). + +...And another example. In the web development, we may need to get an information about an element on the page, that sometimes doesn't exist: ```js run // Error if the result of querySelector(...) is null From 176e5f839f6d1f804f70e25a56026b7cc932bbfc Mon Sep 17 00:00:00 2001 From: Vse Mozhe Buty Date: Thu, 8 Oct 2020 17:59:04 +0300 Subject: [PATCH 15/39] Fix typos in 1.5.3 --- 1-js/05-data-types/03-string/article.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/1-js/05-data-types/03-string/article.md b/1-js/05-data-types/03-string/article.md index 765823d7c..a16a88beb 100644 --- a/1-js/05-data-types/03-string/article.md +++ b/1-js/05-data-types/03-string/article.md @@ -110,7 +110,7 @@ alert( 'I*!*\'*/!*m the Walrus!' ); // *!*I'm*/!* the Walrus! As you can see, we have to prepend the inner quote by the backslash `\'`, because otherwise it would indicate the string end. -Of course, only to the quotes that are the same as the enclosing ones need to be escaped. So, as a more elegant solution, we could switch to double quotes or backticks instead: +Of course, only the quotes that are the same as the enclosing ones need to be escaped. So, as a more elegant solution, we could switch to double quotes or backticks instead: ```js run alert( `I'm the Walrus!` ); // I'm the Walrus! @@ -345,7 +345,7 @@ It is usually not recommended to use language features in a non-obvious way, but Just remember: `if (~str.indexOf(...))` reads as "if found". -To be precise though, as big numbers are truncated to 32 bits by `~` operator, there exist other numbers that give `0`, the smallest is `~4294967295=0`. That makes such check is correct only if a string is not that long. +To be precise though, as big numbers are truncated to 32 bits by `~` operator, there exist other numbers that give `0`, the smallest is `~4294967295=0`. That makes such check correct only if a string is not that long. Right now we can see this trick only in the old code, as modern JavaScript provides `.includes` method (see below). From bdb3def468cc53289d18e11a25d394b529f16481 Mon Sep 17 00:00:00 2001 From: Peter Roche <46547072+paroche@users.noreply.github.com> Date: Fri, 9 Oct 2020 02:40:53 -0600 Subject: [PATCH 16/39] Update article.md As promised. --- 1-js/01-getting-started/1-intro/article.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/1-js/01-getting-started/1-intro/article.md b/1-js/01-getting-started/1-intro/article.md index af201373f..ca5c46de9 100644 --- a/1-js/01-getting-started/1-intro/article.md +++ b/1-js/01-getting-started/1-intro/article.md @@ -68,7 +68,7 @@ Examples of such restrictions include: Modern browsers allow it to work with files, but the access is limited and only provided if the user does certain actions, like "dropping" a file into a browser window or selecting it via an `` tag. There are ways to interact with camera/microphone and other devices, but they require a user's explicit permission. So a JavaScript-enabled page may not sneakily enable a web-camera, observe the surroundings and send the information to the [NSA](https://en.wikipedia.org/wiki/National_Security_Agency). -- Different tabs/windows generally do not know about each other. Sometimes they do, for example when one window uses JavaScript to open the other one. But even in this case, JavaScript from one page may not access the other if they come from different sites (from a different domain, protocol or port). +- Different tabs/windows generally do not know about each other. Sometimes they do; for example when one window uses JavaScript to open the other one. But even in this case, JavaScript from one page may not access the other if they come from different sites (from a different domain, protocol or port). This is called the "Same Origin Policy". To work around that, *both pages* must agree for data exchange and contain a special JavaScript code that handles it. We'll cover that in the tutorial. From f880f1fb631922782efed00b4bfe2188d749d3aa Mon Sep 17 00:00:00 2001 From: Vse Mozhe Buty Date: Fri, 9 Oct 2020 17:42:08 +0300 Subject: [PATCH 17/39] Make code example more realistic and safe 1. Without a real sorting part, the data output is not the same as with real sort (as the array is not really sorted when the callback returns `undefined`). 2. Current example may somehow encourage a reader to write sorting callbacks with undetermined behavior. --- 1-js/05-data-types/05-array-methods/article.md | 1 + 1 file changed, 1 insertion(+) 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 3afba3865..edd9ddee4 100644 --- a/1-js/05-data-types/05-array-methods/article.md +++ b/1-js/05-data-types/05-array-methods/article.md @@ -426,6 +426,7 @@ By the way, if we ever want to know which elements are compared -- nothing preve ```js run [1, -2, 15, 2, 0, 8].sort(function(a, b) { alert( a + " <> " + b ); + return a - b; }); ``` From b2b83b7472399082b453697c0b5a31ab9bb98dd0 Mon Sep 17 00:00:00 2001 From: Vse Mozhe Buty Date: Fri, 9 Oct 2020 17:47:25 +0300 Subject: [PATCH 18/39] Correct outdated info As of the last versions of V8 (i.e. Chrome and Node.js), the current info seems outdated. See also https://v8.dev/blog/array-sort --- 1-js/05-data-types/05-array-methods/article.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 3afba3865..0581d34c2 100644 --- a/1-js/05-data-types/05-array-methods/article.md +++ b/1-js/05-data-types/05-array-methods/article.md @@ -419,7 +419,7 @@ Now it works as intended. Let's step aside and think what's happening. The `arr` can be array of anything, right? It may contain numbers or strings or objects or whatever. We have a set of *some items*. To sort it, we need an *ordering function* that knows how to compare its elements. The default is a string order. -The `arr.sort(fn)` method implements a generic sorting algorithm. We don't need to care how it internally works (an optimized [quicksort](https://en.wikipedia.org/wiki/Quicksort) most of the time). It will walk the array, compare its elements using the provided function and reorder them, all we need is to provide the `fn` which does the comparison. +The `arr.sort(fn)` method implements a generic sorting algorithm. We don't need to care how it internally works (an optimized [quicksort](https://en.wikipedia.org/wiki/Quicksort) or [Timsort](https://en.wikipedia.org/wiki/Timsort) most of the time). It will walk the array, compare its elements using the provided function and reorder them, all we need is to provide the `fn` which does the comparison. By the way, if we ever want to know which elements are compared -- nothing prevents from alerting them: From 54c8247210c76dddc5306a649afc8d72f9a16b7a Mon Sep 17 00:00:00 2001 From: Vse Mozhe Buty Date: Wed, 7 Oct 2020 22:55:26 +0300 Subject: [PATCH 19/39] Fix link in 1.5.3 The page of the current link has not this anchor anymore. --- 1-js/05-data-types/03-string/article.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/1-js/05-data-types/03-string/article.md b/1-js/05-data-types/03-string/article.md index 765823d7c..9e0b692bb 100644 --- a/1-js/05-data-types/03-string/article.md +++ b/1-js/05-data-types/03-string/article.md @@ -312,7 +312,7 @@ if (str.indexOf("Widget") != -1) { #### The bitwise NOT trick -One of the old tricks used here is the [bitwise NOT](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Bitwise_NOT) `~` operator. It converts the number to a 32-bit integer (removes the decimal part if exists) and then reverses all bits in its binary representation. +One of the old tricks used here is the [bitwise NOT](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_NOT) `~` operator. It converts the number to a 32-bit integer (removes the decimal part if exists) and then reverses all bits in its binary representation. In practice, that means a simple thing: for 32-bit integers `~n` equals `-(n+1)`. From edef0b430697f25ba3c8b84b963c2199010953a7 Mon Sep 17 00:00:00 2001 From: Ilya Kantor Date: Fri, 9 Oct 2020 19:01:26 +0300 Subject: [PATCH 20/39] minor fixes --- .../02-object-copy/article.md | 41 ++++++++++++------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/1-js/04-object-basics/02-object-copy/article.md b/1-js/04-object-basics/02-object-copy/article.md index d40eba2f7..1e2ed1aa3 100644 --- a/1-js/04-object-basics/02-object-copy/article.md +++ b/1-js/04-object-basics/02-object-copy/article.md @@ -1,10 +1,12 @@ -# Object copying, references +# Object references and copying -One of the fundamental differences of objects vs primitives is that they are stored and copied "by reference". +One of the fundamental differences of objects versus primitives is that objects are stored and copied "by reference", as opposed to primitive values: strings, numbers, booleans, etc -- that are always copied "as a whole value". -Primitive values: strings, numbers, booleans -- are assigned/copied "as a whole value". +That's easy to understand if we look a bit "under a cover" of what happens when we copy a value. -For instance: +Let's start with a primitive, such as a string. + +Here we put a copy of `message` into `phrase`: ```js let message = "Hello!"; @@ -15,11 +17,13 @@ As a result we have two independent variables, each one is storing the string `" ![](variable-copy-value.svg) +Quite an obvious result, right? + Objects are not like that. -**A variable stores not the object itself, but its "address in memory", in other words "a reference" to it.** +**A variable assigned to an object stores not the object itself, but its "address in memory", in other words "a reference" to it.** -Here's the picture for the object: +Let's look at an example of such variable: ```js let user = { @@ -27,9 +31,17 @@ let user = { }; ``` +And here's how it's actually stored in memory: + ![](variable-contains-reference.svg) -Here, the object is stored somewhere in memory. And the variable `user` has a "reference" to it. +The object is stored somewhere in memory (at the right of the picture), while the `user` variable (at the left) has a "reference" to it. + +We may think of an object variable, such as `user`, as of a sheet of paper with the address. + +When we perform actions with the object, e.g. take a property `user.name`, JavaScript engine looks into that address and performs the operation on the actual object. + +Now here's why it's important. **When an object variable is copied -- the reference is copied, the object is not duplicated.** @@ -45,6 +57,8 @@ Now we have two variables, each one with the reference to the same object: ![](variable-copy-reference.svg) +As you can see, there's still one object, now with two variables that reference it. + We can use any variable to access the object and modify its contents: ```js run @@ -59,15 +73,14 @@ admin.name = 'Pete'; // changed by the "admin" reference alert(*!*user.name*/!*); // 'Pete', changes are seen from the "user" reference ``` -The example above demonstrates that there is only one object. As if we had a cabinet with two keys and used one of them (`admin`) to get into it. Then, if we later use another key (`user`) we can see changes. -## Comparison by reference +It's just as if we had a cabinet with two keys and used one of them (`admin`) to get into it. Then, if we later use another key (`user`) we can see changes. -The equality `==` and strict equality `===` operators for objects work exactly the same. +## Comparison by reference -**Two objects are equal only if they are the same object.** +Two objects are equal only if they are the same object. -Here two variables reference the same object, thus they are equal: +For instance, here `a` and `b` reference the same object, thus they are equal: ```js run let a = {}; @@ -77,7 +90,7 @@ alert( a == b ); // true, both variables reference the same object alert( a === b ); // true ``` -And here two independent objects are not equal, even though both are empty: +And here two independent objects are not equal, even though they look alike (both are empty): ```js run let a = {}; @@ -86,7 +99,7 @@ let b = {}; // two independent objects alert( a == b ); // false ``` -For comparisons like `obj1 > obj2` or for a comparison against a primitive `obj == 5`, objects are converted to primitives. We'll study how object conversions work very soon, but to tell the truth, such comparisons occur very rarely, usually as a result of a coding mistake. +For comparisons like `obj1 > obj2` or for a comparison against a primitive `obj == 5`, objects are converted to primitives. We'll study how object conversions work very soon, but to tell the truth, such comparisons are needed very rarely, usually they appear as a result of a programming mistake. ## Cloning and merging, Object.assign From 858ad69e92b68b09803130e2333e48ca8b4eb135 Mon Sep 17 00:00:00 2001 From: Vse Mozhe Buty Date: Fri, 9 Oct 2020 23:46:13 +0300 Subject: [PATCH 21/39] Fix some possible typos and omissions in 1.5.5 --- 1-js/05-data-types/05-array-methods/article.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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 3afba3865..4b198dfd2 100644 --- a/1-js/05-data-types/05-array-methods/article.md +++ b/1-js/05-data-types/05-array-methods/article.md @@ -44,7 +44,7 @@ The syntax is: arr.splice(start[, deleteCount, elem1, ..., elemN]) ``` -It modified `arr` starting from the index `start`: removes `deleteCount` elements and then inserts `elem1, ..., elemN` at their place. Returns the array of removed elements. +It modifies `arr` starting from the index `start`: removes `deleteCount` elements and then inserts `elem1, ..., elemN` at their place. Returns the array of removed elements. This method is easy to grasp by examples. @@ -700,7 +700,7 @@ alert(soldiers[1].age); // 23 If in the example above we used `users.filter(army.canJoin)`, then `army.canJoin` would be called as a standalone function, with `this=undefined`, thus leading to an instant error. -A call to `users.filter(army.canJoin, army)` can be replaced with `users.filter(user => army.canJoin(user))`, that does the same. The former is used more often, as it's a bit easier to understand for most people. +A call to `users.filter(army.canJoin, army)` can be replaced with `users.filter(user => army.canJoin(user))`, that does the same. The latter is used more often, as it's a bit easier to understand for most people. ## Summary @@ -711,7 +711,7 @@ A cheat sheet of array methods: - `pop()` -- extracts an item from the end, - `shift()` -- extracts an item from the beginning, - `unshift(...items)` -- adds items to the beginning. - - `splice(pos, deleteCount, ...items)` -- at index `pos` delete `deleteCount` elements and insert `items`. + - `splice(pos, deleteCount, ...items)` -- at index `pos` deletes `deleteCount` elements and inserts `items`. - `slice(start, end)` -- creates a new array, copies elements from index `start` till `end` (not inclusive) into it. - `concat(...items)` -- returns a new array: copies all members of the current one and adds `items` to it. If any of `items` is an array, then its elements are taken. @@ -729,7 +729,7 @@ A cheat sheet of array methods: - `sort(func)` -- sorts the array in-place, then returns it. - `reverse()` -- reverses the array in-place, then returns it. - `split/join` -- convert a string to array and back. - - `reduce(func, initial)` -- calculate a single value over the array by calling `func` for each element and passing an intermediate result between the calls. + - `reduce/reduceRight(func, initial)` -- calculate a single value over the array by calling `func` for each element and passing an intermediate result between the calls. - Additionally: - `Array.isArray(arr)` checks `arr` for being an array. @@ -738,7 +738,7 @@ Please note that methods `sort`, `reverse` and `splice` modify the array itself. These methods are the most used ones, they cover 99% of use cases. But there are few others: -- [arr.some(fn)](mdn:js/Array/some)/[arr.every(fn)](mdn:js/Array/every) checks the array. +- [arr.some(fn)](mdn:js/Array/some)/[arr.every(fn)](mdn:js/Array/every) check the array. The function `fn` is called on each element of the array similar to `map`. If any/all results are `true`, returns `true`, otherwise `false`. From dea1a2d375e881369f7de86554e41023d4b7a261 Mon Sep 17 00:00:00 2001 From: Vse Mozhe Buty Date: Sat, 10 Oct 2020 00:05:41 +0300 Subject: [PATCH 22/39] Add a note on some()/every() short circuit --- 1-js/05-data-types/05-array-methods/article.md | 2 ++ 1 file changed, 2 insertions(+) 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 3afba3865..849207f13 100644 --- a/1-js/05-data-types/05-array-methods/article.md +++ b/1-js/05-data-types/05-array-methods/article.md @@ -741,6 +741,8 @@ These methods are the most used ones, they cover 99% of use cases. But there are - [arr.some(fn)](mdn:js/Array/some)/[arr.every(fn)](mdn:js/Array/every) checks the array. The function `fn` is called on each element of the array similar to `map`. If any/all results are `true`, returns `true`, otherwise `false`. + + These methods behave sort of like `||` and `&&` operators: if `fn` returns a truthy value, `arr.some()` immediately returns `true` and stops iterating over the rest items; if `fn` returns a falsy value, `arr.every()` immediately returns `false` and stops iterating over the rest items as well. We can use `every` to compare arrays: ```js run From 7b70f79592fc2db1f2fa6d203836d3d6f408f241 Mon Sep 17 00:00:00 2001 From: Vse Mozhe Buty Date: Sat, 10 Oct 2020 00:17:09 +0300 Subject: [PATCH 23/39] Mention arr.flat()/arr.flatMap() in 1.5.5 --- 1-js/05-data-types/05-array-methods/article.md | 2 ++ 1 file changed, 2 insertions(+) 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 3afba3865..a271e5e96 100644 --- a/1-js/05-data-types/05-array-methods/article.md +++ b/1-js/05-data-types/05-array-methods/article.md @@ -755,6 +755,8 @@ These methods are the most used ones, they cover 99% of use cases. But there are - [arr.copyWithin(target, start, end)](mdn:js/Array/copyWithin) -- copies its elements from position `start` till position `end` into *itself*, at position `target` (overwrites existing). +- [arr.flat(depth)](mdn:js/Array/flat)/[arr.flatMap(fn)](mdn:js/Array/flatMap) create a new flat array from a multidimensional array. + For the full list, see the [manual](mdn:js/Array). From the first sight it may seem that there are so many methods, quite difficult to remember. But actually that's much easier. From 2336288174b129d260839369efd959c7bbc8593f Mon Sep 17 00:00:00 2001 From: Taruna06 <56425322+Taruna06@users.noreply.github.com> Date: Sat, 10 Oct 2020 10:20:26 +0530 Subject: [PATCH 24/39] typo --- 1-js/01-getting-started/1-intro/article.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/1-js/01-getting-started/1-intro/article.md b/1-js/01-getting-started/1-intro/article.md index ca5c46de9..251a347e9 100644 --- a/1-js/01-getting-started/1-intro/article.md +++ b/1-js/01-getting-started/1-intro/article.md @@ -1,6 +1,6 @@ # An Introduction to JavaScript -Let's see what's so special about JavaScript, what we can achieve with it, and which other technologies play well with it. +Let's see what's so special about JavaScript, what we can achieve with it, and what other technologies play well with it. ## What is JavaScript? @@ -116,6 +116,6 @@ There are more. Of course, even if we use one of transpiled languages, we should ## Summary -- JavaScript was initially created as a browser-only language, but is now used in many other environments as well. -- Today, JavaScript has a unique position as the most widely-adopted browser language with full integration with HTML/CSS. +- JavaScript was initially created as a browser-only language, but it is now used in many other environments as well. +- Today, JavaScript has a unique position as the most widely-adopted browser language with full integration in HTML/CSS. - There are many languages that get "transpiled" to JavaScript and provide certain features. It is recommended to take a look at them, at least briefly, after mastering JavaScript. From 4ee44dc0024b6970ecfedafc145be32a21d26853 Mon Sep 17 00:00:00 2001 From: Vse Mozhe Buty Date: Sat, 10 Oct 2020 21:11:53 +0300 Subject: [PATCH 25/39] Fix a typo in a 1.5.5 task solution --- 1-js/05-data-types/05-array-methods/7-map-objects/solution.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/1-js/05-data-types/05-array-methods/7-map-objects/solution.md b/1-js/05-data-types/05-array-methods/7-map-objects/solution.md index 5d8bf4a13..2d8d4fb0e 100644 --- a/1-js/05-data-types/05-array-methods/7-map-objects/solution.md +++ b/1-js/05-data-types/05-array-methods/7-map-objects/solution.md @@ -25,7 +25,7 @@ alert( usersMapped[0].id ); // 1 alert( usersMapped[0].fullName ); // John Smith ``` -Please note that in for the arrow functions we need to use additional brackets. +Please note that in the arrow functions we need to use additional brackets. We can't write like this: ```js From 31884669672e351f9a33f9b56a4db5c1639338b4 Mon Sep 17 00:00:00 2001 From: Vse Mozhe Buty Date: Sat, 10 Oct 2020 21:22:06 +0300 Subject: [PATCH 26/39] Make a solution of 1.5.5 task more correct Proposed solution is in sync with some previous sort function examples. It also makes sorting stable (does not skip `0` result). --- 1-js/05-data-types/05-array-methods/8-sort-objects/solution.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/1-js/05-data-types/05-array-methods/8-sort-objects/solution.md b/1-js/05-data-types/05-array-methods/8-sort-objects/solution.md index 9f1ade707..cfaf9761a 100644 --- a/1-js/05-data-types/05-array-methods/8-sort-objects/solution.md +++ b/1-js/05-data-types/05-array-methods/8-sort-objects/solution.md @@ -1,6 +1,6 @@ ```js run no-beautify function sortByAge(arr) { - arr.sort((a, b) => a.age > b.age ? 1 : -1); + arr.sort((a, b) => a.age - b.age); } let john = { name: "John", age: 25 }; From 1273ae44e37311b0806d2e0e50204be8b77b9ac4 Mon Sep 17 00:00:00 2001 From: Ilya Kantor Date: Sun, 11 Oct 2020 17:12:11 +0300 Subject: [PATCH 27/39] minor fixes --- 1-js/04-object-basics/04-object-methods/article.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/1-js/04-object-basics/04-object-methods/article.md b/1-js/04-object-basics/04-object-methods/article.md index 75bd1856a..d3fbf3daf 100644 --- a/1-js/04-object-basics/04-object-methods/article.md +++ b/1-js/04-object-basics/04-object-methods/article.md @@ -32,11 +32,11 @@ user.sayHi = function() { user.sayHi(); // Hello! ``` -Here we've just used a Function Expression to create the function and assign it to the property `user.sayHi` of the object. +Here we've just used a Function Expression to create a function and assign it to the property `user.sayHi` of the object. -Then we can call it. The user can now speak! +Then we can call it as `user.sayHi()`. The user can now speak! -A function that is the property of an object is called its *method*. +A function that is a property of an object is called its *method*. So, here we've got a method `sayHi` of the object `user`. From e7953a475954e05600befd5a4b8a58398af62e49 Mon Sep 17 00:00:00 2001 From: Ilya Kantor Date: Sun, 11 Oct 2020 17:14:36 +0300 Subject: [PATCH 28/39] minor fixes --- 1-js/04-object-basics/04-object-methods/article.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/1-js/04-object-basics/04-object-methods/article.md b/1-js/04-object-basics/04-object-methods/article.md index d3fbf3daf..19f304a64 100644 --- a/1-js/04-object-basics/04-object-methods/article.md +++ b/1-js/04-object-basics/04-object-methods/article.md @@ -160,7 +160,7 @@ let user = { let admin = user; user = null; // overwrite to make things obvious -admin.sayHi(); // Whoops! inside sayHi(), the old name is used! error! +admin.sayHi(); // TypeError: Cannot read property 'name' of null! ``` If we used `this.name` instead of `user.name` inside the `alert`, then the code would work. From 3fbbcd1559ece14c8a867ea949e2bd2f6c2ff94e Mon Sep 17 00:00:00 2001 From: Ilya Kantor Date: Sun, 11 Oct 2020 17:15:40 +0300 Subject: [PATCH 29/39] minor fixes --- 1-js/04-object-basics/04-object-methods/article.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/1-js/04-object-basics/04-object-methods/article.md b/1-js/04-object-basics/04-object-methods/article.md index 19f304a64..3e21ca08f 100644 --- a/1-js/04-object-basics/04-object-methods/article.md +++ b/1-js/04-object-basics/04-object-methods/article.md @@ -167,7 +167,7 @@ If we used `this.name` instead of `user.name` inside the `alert`, then the code ## "this" is not bound -In JavaScript, keyword `this` behaves unlike most other programming languages. It can be used in any function. +In JavaScript, keyword `this` behaves unlike most other programming languages. It can be used in any function, even if it's not a method of an object. There's no syntax error in the following example: From d3b38c28370191711eb9dfac8681b50b8962ffb6 Mon Sep 17 00:00:00 2001 From: Ilya Kantor Date: Sun, 11 Oct 2020 17:16:04 +0300 Subject: [PATCH 30/39] minor fixes --- 1-js/04-object-basics/04-object-methods/article.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/1-js/04-object-basics/04-object-methods/article.md b/1-js/04-object-basics/04-object-methods/article.md index 3e21ca08f..0d3c1f392 100644 --- a/1-js/04-object-basics/04-object-methods/article.md +++ b/1-js/04-object-basics/04-object-methods/article.md @@ -160,7 +160,9 @@ let user = { let admin = user; user = null; // overwrite to make things obvious -admin.sayHi(); // TypeError: Cannot read property 'name' of null! +*!* +admin.sayHi(); // TypeError: Cannot read property 'name' of null +*/!* ``` If we used `this.name` instead of `user.name` inside the `alert`, then the code would work. From 75aed55284d8d4a78defb897347017e6145199e2 Mon Sep 17 00:00:00 2001 From: joaquinelio Date: Mon, 12 Oct 2020 06:36:48 -0300 Subject: [PATCH 31/39] assign "it" --- 2-ui/1-document/05-basic-dom-node-properties/article.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/2-ui/1-document/05-basic-dom-node-properties/article.md b/2-ui/1-document/05-basic-dom-node-properties/article.md index 76469c187..bd1400cf9 100644 --- a/2-ui/1-document/05-basic-dom-node-properties/article.md +++ b/2-ui/1-document/05-basic-dom-node-properties/article.md @@ -413,7 +413,7 @@ In most cases, we expect the text from a user, and want to treat it as text. We The "hidden" attribute and the DOM property specifies whether the element is visible or not. -We can use it in HTML or assign using JavaScript, like this: +We can use it in HTML or assign it using JavaScript, like this: ```html run height="80"
Both divs below are hidden
From bb703101b9b43cbf76df9a787b9b986d53bcc7f3 Mon Sep 17 00:00:00 2001 From: Dorell James Date: Tue, 13 Oct 2020 00:07:01 +0800 Subject: [PATCH 32/39] Update 2-ui/4-forms-controls/1-form-elements/article.md This updates a small grammar error and adds some helpful info. Hoping it's better now. Let me know if you need any more changes and I'll update it immediately. --- 2-ui/4-forms-controls/1-form-elements/article.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/2-ui/4-forms-controls/1-form-elements/article.md b/2-ui/4-forms-controls/1-form-elements/article.md index 01af1f400..552c1f491 100644 --- a/2-ui/4-forms-controls/1-form-elements/article.md +++ b/2-ui/4-forms-controls/1-form-elements/article.md @@ -205,7 +205,9 @@ Here is an example: ``` -Unlike most other controls, `` allows to select multiple options at once if it has `multiple` attribute. Although such functionality is available, it is rarely used because of the different ways of doing the selection which vary in different operating systems and browser. And also, because you have to inform the user that multiple selection is available, it is more user-friendly to use checkboxes instead. + +In cases that you have to, then use the first way: add/remove the `selected` property from `