This commit is contained in:
Ilya Kantor 2017-11-01 12:57:35 +03:00
commit 0f1455da90
34 changed files with 132 additions and 132 deletions

View file

@ -108,7 +108,7 @@ Modern tools make the transpilation very fast and transparent, actually allowing
Examples of such languages: Examples of such languages:
- [CoffeeScript](http://coffeescript.org/) is a "syntax sugar" for JavaScript, it introduces shorter syntax, allowing to write more precise and clear code. Usually Ruby devs like it. - [CoffeeScript](http://coffeescript.org/) is a "syntactic sugar" for JavaScript, it introduces shorter syntax, allowing to write more precise and clear code. Usually Ruby devs like it.
- [TypeScript](http://www.typescriptlang.org/) is concentrated on adding "strict data typing", to simplify development and support of complex systems. It is developed by Microsoft. - [TypeScript](http://www.typescriptlang.org/) is concentrated on adding "strict data typing", to simplify development and support of complex systems. It is developed by Microsoft.
- [Dart](https://www.dartlang.org/) is a standalone language that has its own engine that runs in non-browser environments (like mobile apps). It was initially offered by Google as a replacement for JavaScript, but as of now, browsers require it to be transpiled to JavaScript just like the ones above. - [Dart](https://www.dartlang.org/) is a standalone language that has its own engine that runs in non-browser environments (like mobile apps). It was initially offered by Google as a replacement for JavaScript, but as of now, browsers require it to be transpiled to JavaScript just like the ones above.

View file

@ -3,9 +3,9 @@ function getAverageAge(users) {
return users.reduce((prev, user) => prev + user.age, 0) / users.length; return users.reduce((prev, user) => prev + user.age, 0) / users.length;
} }
let john = { name: "John", age: 25 } let john = { name: "John", age: 25 };
let pete = { name: "Pete", age: 30 } let pete = { name: "Pete", age: 30 };
let mary = { name: "Mary", age: 29 } let mary = { name: "Mary", age: 29 };
let arr = [ john, pete, mary ]; let arr = [ john, pete, mary ];

View file

@ -11,12 +11,12 @@ The formula for the average is `(age1 + age2 + ... + ageN) / N`.
For instance: For instance:
```js no-beautify ```js no-beautify
let john = { name: "John", age: 25 } let john = { name: "John", age: 25 };
let pete = { name: "Pete", age: 30 } let pete = { name: "Pete", age: 30 };
let mary = { name: "Mary", age: 29 } let mary = { name: "Mary", age: 29 };
let arr = [ john, pete, mary ]; let arr = [ john, pete, mary ];
alert( getAverageAge(arr) ); // (25+30+29)/3 = 28 alert( getAverageAge(arr) ); // (25 + 30 + 29) / 3 = 28
``` ```

View file

@ -2,7 +2,7 @@ importance: 4
--- ---
# Filter "in place" # Filter range
Write a function `filterRange(arr, a, b)` that gets an array `arr`, looks for elements between `a` and `b` in it and returns an array of them. Write a function `filterRange(arr, a, b)` that gets an array `arr`, looks for elements between `a` and `b` in it and returns an array of them.

View file

@ -3,7 +3,7 @@
```js run ```js run
let arr = [5, 2, 1, -10, 8]; let arr = [5, 2, 1, -10, 8];
arr.sort((a,b) => b - a); arr.sort((a, b) => b - a);
alert( arr ); alert( arr );
``` ```

View file

@ -21,8 +21,8 @@ usersMapped = [
] ]
*/ */
alert( usersMapped[0].id ) // 1 alert( usersMapped[0].id ); // 1
alert( usersMapped[0].fullName ) // John Smith alert( usersMapped[0].fullName ); // John Smith
``` ```
Please note that in for the arrow functions we need to use additional brackets. Please note that in for the arrow functions we need to use additional brackets.

View file

@ -12,6 +12,6 @@ let arr = [ john, pete, mary ];
sortByName(arr); sortByName(arr);
// now sorted is: [john, mary, pete] // now sorted is: [john, mary, pete]
alert(arr[1].name) // Mary alert(arr[1].name); // Mary
``` ```

View file

@ -9,15 +9,15 @@ Write the function `sortByName(users)` that gets an array of objects with proper
For instance: For instance:
```js no-beautify ```js no-beautify
let john = { name: "John", age: 25 } let john = { name: "John", age: 25 };
let pete = { name: "Pete", age: 30 } let pete = { name: "Pete", age: 30 };
let mary = { name: "Mary", age: 28 } let mary = { name: "Mary", age: 28 };
let arr = [ john, pete, mary ]; let arr = [ john, pete, mary ];
sortByName(arr); sortByName(arr);
// now: [john, mary, pete] // now: [john, mary, pete]
alert(arr[1].name) // Mary alert(arr[1].name); // Mary
``` ```

View file

@ -12,7 +12,7 @@ shuffle(arr);
alert(arr); alert(arr);
``` ```
That somewhat works, because `Math.random()-0.5` is a random number that may be positive or negative, so the sorting function reorders elements randomly. That somewhat works, because `Math.random() - 0.5` is a random number that may be positive or negative, so the sorting function reorders elements randomly.
But because the sorting function is not meant to be used this way, not all permutations have the same probability. But because the sorting function is not meant to be used this way, not all permutations have the same probability.
@ -33,14 +33,14 @@ let count = {
'312': 0 '312': 0
}; };
for(let i = 0; i < 1000000; i++) { for (let i = 0; i < 1000000; i++) {
let array = [1, 2, 3]; let array = [1, 2, 3];
shuffle(array); shuffle(array);
count[array.join('')]++; count[array.join('')]++;
} }
// show counts of all possible permutations // show counts of all possible permutations
for(let key in count) { for (let key in count) {
alert(`${key}: ${count[key]}`); alert(`${key}: ${count[key]}`);
} }
``` ```
@ -66,8 +66,8 @@ There are other good ways to do the task. For instance, there's a great algorith
```js ```js
function shuffle(array) { function shuffle(array) {
for(let i = array.length - 1; i > 0; i--) { for (let i = array.length - 1; i > 0; i--) {
let j = Math.floor(Math.random() * (i+1)); // random index from 0 to i let j = Math.floor(Math.random() * (i + 1)); // random index from 0 to i
[array[i], array[j]] = [array[j], array[i]]; // swap elements [array[i], array[j]] = [array[j], array[i]]; // swap elements
} }
} }
@ -77,8 +77,8 @@ Let's test it the same way:
```js run ```js run
function shuffle(array) { function shuffle(array) {
for(let i = array.length - 1; i > 0; i--) { for (let i = array.length - 1; i > 0; i--) {
let j = Math.floor(Math.random() * (i+1)); let j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]]; [array[i], array[j]] = [array[j], array[i]];
} }
} }
@ -93,14 +93,14 @@ let count = {
'312': 0 '312': 0
}; };
for(let i = 0; i < 1000000; i++) { for (let i = 0; i < 1000000; i++) {
let array = [1, 2, 3]; let array = [1, 2, 3];
shuffle(array); shuffle(array);
count[array.join('')]++; count[array.join('')]++;
} }
// show counts of all possible permutations // show counts of all possible permutations
for(let key in count) { for (let key in count) {
alert(`${key}: ${count[key]}`); alert(`${key}: ${count[key]}`);
} }
``` ```

View file

@ -121,7 +121,7 @@ Arrays and strings are most widely used built-in iterables.
For a string, `for..of` loops over its characters: For a string, `for..of` loops over its characters:
```js run ```js run
for(let char of "test") { for (let char of "test") {
alert( char ); // t, then e, then s, then t alert( char ); // t, then e, then s, then t
} }
``` ```
@ -130,8 +130,8 @@ And it works right with surrogate pairs!
```js run ```js run
let str = '𝒳😂'; let str = '𝒳😂';
for(let char of str) { for (let char of str) {
alert(char); // 𝒳, and then 😂 alert( char ); // 𝒳, and then 😂
} }
``` ```
@ -139,7 +139,7 @@ for(let char of str) {
Normally, internals of iterables are hidden from the external code. There's a `for..of` loop, that works, that's all it needs to know. Normally, internals of iterables are hidden from the external code. There's a `for..of` loop, that works, that's all it needs to know.
But to understand things a little bit more deeper let's see how to create an iterator explicitly. But to understand things a little bit deeper let's see how to create an iterator explicitly.
We'll iterate over a string the same way as `for..of`, but with direct calls. This code gets a string iterator and calls it "manually": We'll iterate over a string the same way as `for..of`, but with direct calls. This code gets a string iterator and calls it "manually":
@ -151,7 +151,7 @@ let str = "Hello";
let iterator = str[Symbol.iterator](); let iterator = str[Symbol.iterator]();
while(true) { while (true) {
let result = iterator.next(); let result = iterator.next();
if (result.done) break; if (result.done) break;
alert(result.value); // outputs characters one by one alert(result.value); // outputs characters one by one
@ -184,7 +184,7 @@ let arrayLike = { // has indexes and length => array-like
*!* *!*
// Error (no Symbol.iterator) // Error (no Symbol.iterator)
for(let item of arrayLike) {} for (let item of arrayLike) {}
*/!* */!*
``` ```
@ -258,7 +258,7 @@ Technically here it does the same as:
let str = '𝒳😂'; let str = '𝒳😂';
let chars = []; // Array.from internally does the same loop let chars = []; // Array.from internally does the same loop
for(let char of str) { for (let char of str) {
chars.push(char); chars.push(char);
} }

View file

@ -15,7 +15,7 @@ We'll use the letter-sorted variants as map keys to store only one value per eac
function aclean(arr) { function aclean(arr) {
let map = new Map(); let map = new Map();
for(let word of arr) { for (let word of arr) {
// split the word by letters, sort them and join back // split the word by letters, sort them and join back
*!* *!*
let sorted = word.toLowerCase().split('').sort().join(''); // (*) let sorted = word.toLowerCase().split('').sort().join(''); // (*)

View file

@ -137,17 +137,17 @@ let recipeMap = new Map([
]); ]);
// iterate over keys (vegetables) // iterate over keys (vegetables)
for(let vegetable of recipeMap.keys()) { for (let vegetable of recipeMap.keys()) {
alert(vegetable); // cucumber, tomateos, onion alert(vegetable); // cucumber, tomateos, onion
} }
// iterate over values (amounts) // iterate over values (amounts)
for(let amount of recipeMap.values()) { for (let amount of recipeMap.values()) {
alert(amount); // 500, 350, 50 alert(amount); // 500, 350, 50
} }
// iterate over [key, value] entries // iterate over [key, value] entries
for(let entry of recipeMap) { // the same as of recipeMap.entries() for (let entry of recipeMap) { // the same as of recipeMap.entries()
alert(entry); // cucumber,500 (and so on) alert(entry); // cucumber,500 (and so on)
} }
``` ```
@ -199,7 +199,7 @@ set.add(mary);
// set keeps only unique values // set keeps only unique values
alert( set.size ); // 3 alert( set.size ); // 3
for(let user of set) { for (let user of set) {
alert(user.name); // John (then Pete and Mary) alert(user.name); // John (then Pete and Mary)
} }
``` ```
@ -213,7 +213,7 @@ We can loop over a set either with `for..of` or using `forEach`:
```js run ```js run
let set = new Set(["oranges", "apples", "bananas"]); let set = new Set(["oranges", "apples", "bananas"]);
for(let value of set) alert(value); for (let value of set) alert(value);
// the same with forEach: // the same with forEach:
set.forEach((value, valueAgain, set) => { set.forEach((value, valueAgain, set) => {
@ -332,7 +332,7 @@ That's useful for situations when we have a main storage for the objects somewhe
Let's see an example. Let's see an example.
For instance, we have a code that keeps a visit count for each user. The information is stored in a map: a user is the key and the visit count is the value. When a user leaves, we don't want to store his visit count any more. For instance, we have a code that keeps a visit count for each user. The information is stored in a map: a user is the key and the visit count is the value. When a user leaves, we don't want to store his visit count anymore.
One way would be to keep track of leaving users and clean up the storage manually: One way would be to keep track of leaving users and clean up the storage manually:
@ -345,7 +345,7 @@ let visitsCountMap = new Map();
// john is the key for the map // john is the key for the map
visitsCountMap.set(john, 123); visitsCountMap.set(john, 123);
// now john leaves us, we don't need him any more // now john leaves us, we don't need him anymore
john = null; john = null;
*!* *!*
@ -364,7 +364,7 @@ let visitsCountMap = new WeakMap();
visitsCountMap.set(john, 123); visitsCountMap.set(john, 123);
// now john leaves us, we don't need him any more // now john leaves us, we don't need him anymore
john = null; john = null;
// there are no references except WeakMap, // there are no references except WeakMap,
@ -408,7 +408,7 @@ The most notable limitation of `WeakMap` and `WeakSet` is the absence of iterati
## Summary ## Summary
- `Map` -- is a a collection of keyed values. - `Map` -- is a collection of keyed values.
The differences from a regular `Object`: The differences from a regular `Object`:
@ -421,12 +421,12 @@ The most notable limitation of `WeakMap` and `WeakSet` is the absence of iterati
- Unlike an array, does not allow to reorder elements. - Unlike an array, does not allow to reorder elements.
- Keeps the insertion order. - Keeps the insertion order.
- `WeakMap` -- a variant of `Map` that allows only objects as keys and removes them once they become unaccessible by other means. - `WeakMap` -- a variant of `Map` that allows only objects as keys and removes them once they become inaccessible by other means.
- It does not support operations on the structure as a whole: no `size`, no `clear()`, no iterations. - It does not support operations on the structure as a whole: no `size`, no `clear()`, no iterations.
- `WeakSet` -- is a variant of `Set` that only stores objects and removes them once they become unaccessible by other means. - `WeakSet` -- is a variant of `Set` that only stores objects and removes them once they become inaccessible by other means.
- Also does not support `size/clear()` and iterations. - Also does not support `size/clear()` and iterations.
`WeakMap` and `WeakSet` are used as "secondary" data structures in additional to the "main" object storage. Once the object is removed from the main storage, so it only stays in `WeakMap/WeakSet`, they clean up aumatically. `WeakMap` and `WeakSet` are used as "secondary" data structures in addition to the "main" object storage. Once the object is removed from the main storage, so it only stays in `WeakMap/WeakSet`, they clean up automatically.

View file

@ -58,7 +58,7 @@ let user = {
}; };
// loop over values // loop over values
for(let value of Object.values(user)) { for (let value of Object.values(user)) {
alert(value); // John, then 30 alert(value); // John, then 30
} }
``` ```

View file

@ -99,7 +99,7 @@ let user = {
// loop over keys-and-values // loop over keys-and-values
*!* *!*
for(let [key, value] of Object.entries(user)) { for (let [key, value] of Object.entries(user)) {
*/!* */!*
alert(`${key}:${value}`); // name:John, then age:30 alert(`${key}:${value}`); // name:John, then age:30
} }
@ -113,7 +113,7 @@ user.set("name", "John");
user.set("age", "30"); user.set("age", "30");
*!* *!*
for(let [key, value] of user.entries()) { for (let [key, value] of user.entries()) {
*/!* */!*
alert(`${key}:${value}`); // name:John, then age:30 alert(`${key}:${value}`); // name:John, then age:30
} }
@ -136,7 +136,7 @@ alert(rest.length); // 2
*/!* */!*
``` ```
The value of `rest` is the array of the remaining array elements. We can use any other variable name in place of `rest`, just make sure it has three dots before it and goes last in the destructuring assignmemnt. The value of `rest` is the array of the remaining array elements. We can use any other variable name in place of `rest`, just make sure it has three dots before it and goes last in the destructuring assignment.
### Default values ### Default values
@ -155,7 +155,7 @@ If we want a "default" value to replace the missing one, we can provide it using
```js run ```js run
*!* *!*
// default values // default values
let [name="Guest", surname="Anonymous"] = ["Julius"]; let [name = "Guest", surname = "Anonymous"] = ["Julius"];
*/!* */!*
alert(name); // Julius (from array) alert(name); // Julius (from array)
@ -168,7 +168,7 @@ For instance, here we use the `prompt` function for two defaults. But it will ru
```js run ```js run
// runs only prompt for surname // runs only prompt for surname
let [name=prompt('name?'), surname=prompt('surname?')] = ["Julius"]; let [name = prompt('name?'), surname = prompt('surname?')] = ["Julius"];
alert(name); // Julius (from array) alert(name); // Julius (from array)
alert(surname); // whatever prompt gets alert(surname); // whatever prompt gets
@ -248,7 +248,7 @@ let options = {
}; };
*!* *!*
let {width=100, height=200, title} = options; let {width = 100, height = 200, title} = options;
*/!* */!*
alert(title); // Menu alert(title); // Menu
@ -266,7 +266,7 @@ let options = {
}; };
*!* *!*
let {width=prompt("width?"), title=prompt("title?")} = options; let {width = prompt("width?"), title = prompt("title?")} = options;
*/!* */!*
alert(title); // Menu alert(title); // Menu
@ -281,7 +281,7 @@ let options = {
}; };
*!* *!*
let {width:w=100, height:h=200, title} = options; let {width: w = 100, height: h = 200, title} = options;
*/!* */!*
alert(title); // Menu alert(title); // Menu
@ -434,7 +434,7 @@ let options = {
function showMenu(*!*{title = "Untitled", width = 200, height = 100, items = []}*/!*) { function showMenu(*!*{title = "Untitled", width = 200, height = 100, items = []}*/!*) {
// title, items taken from options, // title, items taken from options,
// width, height defaults used // width, height defaults used
alert( title + ' ' + width + ' ' + height ); // My Menu 100 200 alert( `${title} ${width} ${height}` ); // My Menu 200 100
alert( items ); // Item1, Item2 alert( items ); // Item1, Item2
} }
@ -452,12 +452,12 @@ let options = {
*!* *!*
function showMenu({ function showMenu({
title = "Untitled", title = "Untitled",
width:w = 100, // width goes to w width: w = 100, // width goes to w
height:h = 200, // height goes to h height: h = 200, // height goes to h
items: [item1, item2] // items first element goes to item1, second to item2 items: [item1, item2] // items first element goes to item1, second to item2
}) { }) {
*/!* */!*
alert( title + ' ' + w + ' ' + h ); // My Menu 100 200 alert( `${title} ${w} ${h}` ); // My Menu 100 200
alert( item1 ); // Item1 alert( item1 ); // Item1
alert( item2 ); // Item2 alert( item2 ); // Item2
} }
@ -487,8 +487,8 @@ We can fix this by making `{}` the default value for the whole destructuring thi
```js run ```js run
// simplified parameters a bit for clarity // simplified parameters a bit for clarity
function showMenu(*!*{ title="Menu", width=100, height=200 } = {}*/!*) { function showMenu(*!*{ title = "Menu", width = 100, height = 200 } = {}*/!*) {
alert( title + ' ' + width + ' ' + height ); alert( `${title} ${width} ${height}` );
} }
showMenu(); // Menu 100 200 showMenu(); // Menu 100 200

View file

@ -100,7 +100,7 @@ There are also their UTC-counterparts, that return day, month, year and so on fo
If your local time zone is shifted relative to UTC, then the code below shows different hours: If your local time zone is shifted relative to UTC, then the code below shows different hours:
```js run ```js run
// currend date // current date
let date = new Date(); let date = new Date();
// the hour in your current time zone // the hour in your current time zone
@ -208,7 +208,7 @@ let date = new Date();
alert(+date); // the number of milliseconds, same as date.getTime() alert(+date); // the number of milliseconds, same as date.getTime()
``` ```
The important side effect: dates can be substracted, the result is their difference in ms. The important side effect: dates can be subtracted, the result is their difference in ms.
That can be used for time measurements: That can be used for time measurements:
@ -251,7 +251,7 @@ for (let i = 0; i < 100000; i++) {
let end = Date.now(); // done let end = Date.now(); // done
*/!* */!*
alert( `The loop took ${end - start} ms` ); // substract numbers, not dates alert( `The loop took ${end - start} ms` ); // subtract numbers, not dates
``` ```
## Benchmarking ## Benchmarking
@ -262,7 +262,7 @@ For instance, let's measure two functions that calculate the difference between
```js ```js
// we have date1 and date2, which function faster returns their difference in ms? // we have date1 and date2, which function faster returns their difference in ms?
function diffSubstract(date1, date2) { function diffSubtract(date1, date2) {
return date2 - date1; return date2 - date1;
} }
@ -281,7 +281,7 @@ The first idea may be to run them many times in a row and measure the time diffe
Let's measure: Let's measure:
```js run ```js run
function diffSubstract(date1, date2) { function diffSubtract(date1, date2) {
return date2 - date1; return date2 - date1;
} }
@ -298,7 +298,7 @@ function bench(f) {
return Date.now() - start; return Date.now() - start;
} }
alert( 'Time of diffSubstract: ' + bench(diffSubstract) + 'ms' ); alert( 'Time of diffSubtract: ' + bench(diffSubtract) + 'ms' );
alert( 'Time of diffGetTime: ' + bench(diffGetTime) + 'ms' ); alert( 'Time of diffGetTime: ' + bench(diffGetTime) + 'ms' );
``` ```
@ -306,7 +306,7 @@ Wow! Using `getTime()` is so much faster! That's because there's no type convers
Okay, we have something. But that's not a good benchmark yet. Okay, we have something. But that's not a good benchmark yet.
Imagine that at the time of running `bench(diffSubstract)` CPU was doing something in parallel, and it was taking resources. And by the time of running `bench(diffGetTime)` the work has finished. Imagine that at the time of running `bench(diffSubtract)` CPU was doing something in parallel, and it was taking resources. And by the time of running `bench(diffGetTime)` the work has finished.
A pretty real scenario for a modern multi-process OS. A pretty real scenario for a modern multi-process OS.
@ -317,7 +317,7 @@ As a result, the first benchmark will have less CPU resources than the second. T
Here's the code example: Here's the code example:
```js run ```js run
function diffSubstract(date1, date2) { function diffSubtract(date1, date2) {
return date2 - date1; return date2 - date1;
} }
@ -340,12 +340,12 @@ let time2 = 0;
*!* *!*
// run bench(upperSlice) and bench(upperLoop) each 10 times alternating // run bench(upperSlice) and bench(upperLoop) each 10 times alternating
for (let i = 0; i < 10; i++) { for (let i = 0; i < 10; i++) {
time1 += bench(diffSubstract); time1 += bench(diffSubtract);
time2 += bench(diffGetTime); time2 += bench(diffGetTime);
} }
*/!* */!*
alert( 'Total time for diffSubstract: ' + time1 ); alert( 'Total time for diffSubtract: ' + time1 );
alert( 'Total time for diffGetTime: ' + time2 ); alert( 'Total time for diffGetTime: ' + time2 );
``` ```
@ -353,12 +353,12 @@ Modern JavaScript engines start applying advanced optimizations only to "hot cod
```js ```js
// added for "heating up" prior to the main loop // added for "heating up" prior to the main loop
bench(diffSubstract); bench(diffSubtract);
bench(diffGetTime); bench(diffGetTime);
// now benchmark // now benchmark
for (let i = 0; i < 10; i++) { for (let i = 0; i < 10; i++) {
time1 += bench(diffSubstract); time1 += bench(diffSubtract);
time2 += bench(diffGetTime); time2 += bench(diffGetTime);
} }
``` ```
@ -405,8 +405,8 @@ alert(date);
- Date and time in JavaScript are represented with the [Date](mdn:js/Date) object. We can't create "only date" or "only time": `Date` objects always carry both. - Date and time in JavaScript are represented with the [Date](mdn:js/Date) object. We can't create "only date" or "only time": `Date` objects always carry both.
- Months are counted from zero (yes, January is a zero month). - Months are counted from zero (yes, January is a zero month).
- Days of week in `getDay()` are also counted from zero (that's Sunday). - Days of week in `getDay()` are also counted from zero (that's Sunday).
- `Date` auto-corrects itself when out-of-range components are set. Good for adding/substracting days/months/hours. - `Date` auto-corrects itself when out-of-range components are set. Good for adding/subtracting days/months/hours.
- Dates can be substracted, giving their difference in milliseconds. That's because a `Date` becomes the timestamp if converted to a number. - Dates can be subtracted, giving their difference in milliseconds. That's because a `Date` becomes the timestamp when converted to a number.
- Use `Date.now()` to get the current timestamp fast. - Use `Date.now()` to get the current timestamp fast.
Note that unlike many other systems, timestamps in JavaScript are in milliseconds, not in seconds. Note that unlike many other systems, timestamps in JavaScript are in milliseconds, not in seconds.
@ -417,7 +417,7 @@ Also, sometimes we need more precise time measurements. JavaScript itself does n
alert(`Loading started ${performance.now()}ms ago`); alert(`Loading started ${performance.now()}ms ago`);
// Something like: "Loading started 34731.26000000001ms ago" // Something like: "Loading started 34731.26000000001ms ago"
// .26 is microseconds (260 microseconds) // .26 is microseconds (260 microseconds)
// more than 3 digits after the decimal point are precision errors, but only 3 first are correct // more than 3 digits after the decimal point are precision errors, but only the first 3 are correct
``` ```
Node.JS has `microtime` module and other ways. Technically, any device and environment allows to get more precision, it's just not in `Date`. Node.JS has `microtime` module and other ways. Technically, any device and environment allows to get more precision, it's just not in `Date`.

View file

@ -31,7 +31,7 @@ There are two ways to implement it.
let result = 1; let result = 1;
// multiply result by x n times in the loop // multiply result by x n times in the loop
for(let i = 0; i < n; i++) { for (let i = 0; i < n; i++) {
result *= x; result *= x;
} }
@ -67,8 +67,8 @@ pow(x, n) =
else = x * pow(x, n - 1) else = x * pow(x, n - 1)
``` ```
1. If `n==1`, then everything is trivial. It is called *the base* of recursion, because it immediately produces the obvious result: `pow(x, 1)` equals `x`. 1. If `n == 1`, then everything is trivial. It is called *the base* of recursion, because it immediately produces the obvious result: `pow(x, 1)` equals `x`.
2. Otherwise, we can represent `pow(x, n)` as `x * pow(x, n-1)`. In maths, one would write <code>x<sup>n</sup> = x * x<sup>n-1</sup></code>. This is called *a recursive step*: we transform the task into a simpler action (multiplication by `x`) and a simpler call of the same task (`pow` with lower `n`). Next steps simplify it further and further untill `n` reaches `1`. 2. Otherwise, we can represent `pow(x, n)` as `x * pow(x, n - 1)`. In maths, one would write <code>x<sup>n</sup> = x * x<sup>n-1</sup></code>. This is called *a recursive step*: we transform the task into a simpler action (multiplication by `x`) and a simpler call of the same task (`pow` with lower `n`). Next steps simplify it further and further until `n` reaches `1`.
We can also say that `pow` *recursively calls itself* till `n == 1`. We can also say that `pow` *recursively calls itself* till `n == 1`.
@ -91,7 +91,7 @@ Here we can rewrite the same using the ternary `?` operator instead of `if` to m
```js run ```js run
function pow(x, n) { function pow(x, n) {
return (n == 1) ? x : (x * pow(x, n-1)); return (n == 1) ? x : (x * pow(x, n - 1));
} }
``` ```
```` ````
@ -134,7 +134,7 @@ We can sketch it as:
</li> </li>
</ul> </ul>
That's when the function starts to execute. The condition `n == 1` is falsy, so the flow continues into the second branch of `if`: That's when the function starts to execute. The condition `n == 1` is false, so the flow continues into the second branch of `if`:
```js run ```js run
function pow(x, n) { function pow(x, n) {
@ -160,7 +160,7 @@ The variables are same, but the line changes, so the context is now:
</li> </li>
</ul> </ul>
To calculate `x*pow(x, n-1)`, we need to make a subcall of `pow` with new arguments `pow(2, 2)`. To calculate `x * pow(x, n - 1)`, we need to make a subcall of `pow` with new arguments `pow(2, 2)`.
### pow(2, 2) ### pow(2, 2)
@ -230,7 +230,7 @@ function pow(x, n) {
There are no more nested calls, so the function finishes, returning `2`. There are no more nested calls, so the function finishes, returning `2`.
As the function finishes, its execution context is not needed any more, so it's removed from the memory. The previous one is restored off the top of the stack: As the function finishes, its execution context is not needed anymore, so it's removed from the memory. The previous one is restored off the top of the stack:
<ul class="function-execution-context-list"> <ul class="function-execution-context-list">
@ -244,7 +244,7 @@ As the function finishes, its execution context is not needed any more, so it's
</li> </li>
</ul> </ul>
The execution of `pow(2, 2)` is resumed. It has the result of the subcall `pow(2, 1)`, so it also can finish the evaluation of `x * pow(x, n-1)`, returning `4`. The execution of `pow(2, 2)` is resumed. It has the result of the subcall `pow(2, 1)`, so it also can finish the evaluation of `x * pow(x, n - 1)`, returning `4`.
Then the previous context is restored: Then the previous context is restored:
@ -269,7 +269,7 @@ A loop-based algorithm is more memory-saving:
function pow(x, n) { function pow(x, n) {
let result = 1; let result = 1;
for(let i = 0; i < n; i++) { for (let i = 0; i < n; i++) {
result *= x; result *= x;
} }
@ -360,7 +360,7 @@ function sumSalaries(department) {
return department.reduce((prev, current) => prev + current.salary, 0); // sum the array return department.reduce((prev, current) => prev + current.salary, 0); // sum the array
} else { // case (2) } else { // case (2)
let sum = 0; let sum = 0;
for(let subdep of Object.values(department)) { for (let subdep of Object.values(department)) {
sum += sumSalaries(subdep); // recursively call for subdepartments, sum the results sum += sumSalaries(subdep); // recursively call for subdepartments, sum the results
} }
return sum; return sum;
@ -395,7 +395,7 @@ A company *department* is:
- Either an array of people. - Either an array of people.
- Or an object with *departments*. - Or an object with *departments*.
For web-developers there are much better known examples: HTML and XML documents. For web-developers there are much better-known examples: HTML and XML documents.
In the HTML document, an *HTML-tag* may contain a list of: In the HTML document, an *HTML-tag* may contain a list of:
- Text pieces. - Text pieces.

View file

@ -1,6 +1,6 @@
# Rest parameters and spread operator # Rest parameters and spread operator
Many JavaScript built-in functions support on arbitrary number of arguments. Many JavaScript built-in functions support an arbitrary number of arguments.
For instance: For instance:
@ -35,7 +35,7 @@ For instance, to gather all arguments into array `args`:
function sumAll(...args) { // args is the name for the array function sumAll(...args) { // args is the name for the array
let sum = 0; let sum = 0;
for(let arg of args) sum += arg; for (let arg of args) sum += arg;
return sum; return sum;
} }
@ -207,7 +207,7 @@ alert( [...str] ); // H,e,l,l,o
The spread operator internally uses iterators to gather elements, the same way as `for..of` does. The spread operator internally uses iterators to gather elements, the same way as `for..of` does.
So, for a string, `for..of` returns characters and `...str` becomes `"h","e","l","l","o"`. The list of characters is passed to array initializer `[...str]`. So, for a string, `for..of` returns characters and `...str` becomes `"H","e","l","l","o"`. The list of characters is passed to array initializer `[...str]`.
For this particular task we could also use `Array.from`, because it converts an iterable (like a string) into an array: For this particular task we could also use `Array.from`, because it converts an iterable (like a string) into an array:

View file

@ -296,7 +296,7 @@ let counter2 = makeCounter();
alert( counter1() ); // 0 alert( counter1() ); // 0
alert( counter1() ); // 1 alert( counter1() ); // 1
alert( counter2() ); // 0 (independant) alert( counter2() ); // 0 (independent)
``` ```
@ -423,7 +423,7 @@ For instance, after `if` finishes, the `alert` below won't see the `user`, hence
For a loop, every run has a separate Lexical Environment. If the variable is declared in `for`, then it's also local to that Lexical Environment: For a loop, every run has a separate Lexical Environment. If the variable is declared in `for`, then it's also local to that Lexical Environment:
```js run ```js run
for(let i = 0; i < 10; i++) { for (let i = 0; i < 10; i++) {
// Each loop has its own Lexical Environment // Each loop has its own Lexical Environment
// {i: value} // {i: value}
} }
@ -567,7 +567,7 @@ Lexical Environment objects that we've been talking about are subjects to same m
return function() { alert(value); }; return function() { alert(value); };
} }
// 3 functions in array, every of them links to Lexical Environment // 3 functions in array, every one of them links to Lexical Environment
// from the corresponding f() run // from the corresponding f() run
// LE LE LE // LE LE LE
let arr = [f(), f(), f()]; let arr = [f(), f(), f()];
@ -641,6 +641,6 @@ g();
```warn header="See ya!" ```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. This feature of V8 is good to know. If you are debugging with Chrome/Opera, sooner or later you will meet it.
That is not a bug of debugger, but a special feature of V8. Maybe it will be changed some time. That is not a bug of debugger, but a special feature of V8. Maybe it will be changed sometime.
You always can check for it by running examples on this page. You always can check for it by running examples on this page.
``` ```

View file

@ -52,7 +52,7 @@ If we used `let test` on the 2nd line, then it wouldn't be visible to `alert`. B
The same thing for loops: `var` cannot be block- or loop-local: The same thing for loops: `var` cannot be block- or loop-local:
```js ```js
for(var i = 0; i < 10; i++) { for (var i = 0; i < 10; i++) {
// ... // ...
} }
@ -61,7 +61,7 @@ alert(i); // 10, "i" is visible after loop, it's a global variable
*/!* */!*
``` ```
If a code block in inside a function, then `var` becomes a function-level variable: If a code block is inside a function, then `var` becomes a function-level variable:
```js ```js
function sayHi() { function sayHi() {
@ -76,7 +76,7 @@ sayHi();
alert(phrase); // Error: phrase is not defined alert(phrase); // Error: phrase is not defined
``` ```
As we can see, `var` pierces through `if`, `for` or other code blocks. That's because long time ago in JavaScript blocks had no Lexical Environments. And `var` is a reminiscence of that. As we can see, `var` pierces through `if`, `for` or other code blocks. That's because a long time ago in JavaScript blocks had no Lexical Environments. And `var` is a reminiscence of that.
## "var" are processed at the function start ## "var" are processed at the function start
@ -184,4 +184,4 @@ There are two main differences of `var`:
There's one more minor difference related to the global object, we'll cover that in the next chapter. There's one more minor difference related to the global object, we'll cover that in the next chapter.
These differences are actually a bad thing most of time. First, we can't create block-local variables. And hoisting just creates more space for errors. So, for new scripts `var` is used exceptionally rarely. These differences are actually a bad thing most of the time. First, we can't create block-local variables. And hoisting just creates more space for errors. So, for new scripts `var` is used exceptionally rarely.

View file

@ -158,7 +158,7 @@ The `setTimeout` above schedules next call right at the end of the current one `
Recursive `setTimeout` is more flexible method than `setInterval`. This way the next call may be scheduled differently, depending on the results of the current one. Recursive `setTimeout` is more flexible method than `setInterval`. This way the next call may be scheduled differently, depending on the results of the current one.
For instance, we need to write a service that each 5 seconds sends a request to server asking for data, but in case the server is overloaded, it should increase the interval to 10, 20, 40 seconds... For instance, we need to write a service that every 5 seconds sends a request to server asking for data, but in case the server is overloaded, it should increase the interval to 10, 20, 40 seconds...
Here's the pseudocode: Here's the pseudocode:
```js ```js
@ -178,7 +178,7 @@ let timerId = setTimeout(function request() {
``` ```
And if we regulary have CPU-hungry tasks, then we can measure the time taken by the execution and plan the next call sooner or later. And if we regularly have CPU-hungry tasks, then we can measure the time taken by the execution and plan the next call sooner or later.
**Recursive `setTimeout` guarantees a delay between the executions, `setInterval` -- does not.** **Recursive `setTimeout` guarantees a delay between the executions, `setInterval` -- does not.**
@ -209,7 +209,7 @@ Did you notice?...
**The real delay between `func` calls for `setInterval` is less than in the code!** **The real delay between `func` calls for `setInterval` is less than in the code!**
That's natural, because the time taken by `func` execution "consumes" a part of the interval. That's natural, because the time is taken by `func` execution "consumes" a part of the interval.
It is possible that `func` execution turns out to be longer than we expected and takes more than 100ms. It is possible that `func` execution turns out to be longer than we expected and takes more than 100ms.
@ -235,7 +235,7 @@ setTimeout(function() {...}, 100);
For `setInterval` the function stays in memory until `cancelInterval` is called. For `setInterval` the function stays in memory until `cancelInterval` is called.
There's a side-effect. A function references the outer lexical environment, so, while it lives, outer variables live too. They may take much more memory than the function itself. So when we don't need the scheduled function any more, it's better to cancel it, even if it's very small. There's a side-effect. A function references the outer lexical environment, so, while it lives, outer variables live too. They may take much more memory than the function itself. So when we don't need the scheduled function anymore, it's better to cancel it, even if it's very small.
```` ````
## setTimeout(...,0) ## setTimeout(...,0)
@ -260,7 +260,7 @@ The first line "puts the call into calendar after 0ms". But the scheduler will o
There's a trick to split CPU-hungry task using `setTimeout`. There's a trick to split CPU-hungry task using `setTimeout`.
For instance, syntax highlighting script (used to colorize code examples on this page) is quite CPU-heavy. To hightlight the code, it performs the analysis, creates many colored elements, adds them to the document -- for a big text that takes a lot. It may even cause the browser to "hang", that's unacceptable. For instance, syntax highlighting script (used to colorize code examples on this page) is quite CPU-heavy. To highlight the code, it performs the analysis, creates many colored elements, adds them to the document -- for a big text that takes a lot. It may even cause the browser to "hang", that's unacceptable.
So we can split the long text to pieces. First 100 lines, then plan another 100 lines using `setTimeout(...,0)`, and so on. So we can split the long text to pieces. First 100 lines, then plan another 100 lines using `setTimeout(...,0)`, and so on.
@ -276,7 +276,7 @@ let start = Date.now();
function count() { function count() {
// do a heavy job // do a heavy job
for(let j = 0; j < 1e9; j++) { for (let j = 0; j < 1e9; j++) {
i++; i++;
} }
@ -401,7 +401,7 @@ Here's the demo:
let i = 0; let i = 0;
function count() { function count() {
for(let j = 0; j < 1e6; j++) { for (let j = 0; j < 1e6; j++) {
i++; i++;
// put the current i into the <div> // put the current i into the <div>
// (we'll talk more about innerHTML in the specific chapter, should be obvious here) // (we'll talk more about innerHTML in the specific chapter, should be obvious here)

View file

@ -36,9 +36,9 @@ describe("spy", function() {
calc.wrappedSum = spy(calc.sum); calc.wrappedSum = spy(calc.sum);
assert.equal(calculator.wrappedSum(1, 2), 3); assert.equal(calc.wrappedSum(1, 2), 3);
assert(calc.sum.calledWith(1, 2)); assert(calc.sum.calledWith(1, 2));
assert(calc.sum.calledOn(calculator)); assert(calc.sum.calledOn(calc));
}); });
}); });

View file

@ -22,7 +22,7 @@ work = spy(work);
work(1, 2); // 3 work(1, 2); // 3
work(4, 5); // 9 work(4, 5); // 9
for(let args of work.calls) { for (let args of work.calls) {
alert( 'call:' + args.join() ); // "call:1,2", "call:4,5" alert( 'call:' + args.join() ); // "call:1,2", "call:4,5"
} }
``` ```

View file

@ -135,7 +135,7 @@ func.call(context, arg1, arg2, ...)
It runs `func` providing the first argument as `this`, and the next as the arguments. It runs `func` providing the first argument as `this`, and the next as the arguments.
To put it simple, these two calls do almost the same: To put it simply, these two calls do almost the same:
```js ```js
func(1, 2, 3); func(1, 2, 3);
func.call(obj, 1, 2, 3) func.call(obj, 1, 2, 3)
@ -241,7 +241,7 @@ There are many solutions possible:
1. Implement a new (or use a third-party) map-like data structure that is more versatile and allows multi-keys. 1. Implement a new (or use a third-party) map-like data structure that is more versatile and allows multi-keys.
2. Use nested maps: `cache.set(min)` will be a `Map` that stores the pair `(max, result)`. So we can get `result` as `cache.get(min).get(max)`. 2. Use nested maps: `cache.set(min)` will be a `Map` that stores the pair `(max, result)`. So we can get `result` as `cache.get(min).get(max)`.
3. Join two values into one. In our particular case we can just use a string `"min,max"` as the `Map` key. For flexibility, we can allow to provide a *hashing function* for the decorator, that knows how to make a one value from many. 3. Join two values into one. In our particular case we can just use a string `"min,max"` as the `Map` key. For flexibility, we can allow to provide a *hashing function* for the decorator, that knows how to make one value from many.
For many practical applications, the 3rd variant is good enough, so we'll stick to it. For many practical applications, the 3rd variant is good enough, so we'll stick to it.
@ -319,7 +319,7 @@ let wrapper = function() {
That's called *call forwarding*. The `wrapper` passes everything it gets: the context `this` and arguments to `anotherFunction` and returns back its result. That's called *call forwarding*. The `wrapper` passes everything it gets: the context `this` and arguments to `anotherFunction` and returns back its result.
When an external code calls such `wrapper`, it is undistinguishable from the call of the original function. When an external code calls such `wrapper`, it is indistinguishable from the call of the original function.
Now let's bake it all into the more powerful `cachingDecorator`: Now let's bake it all into the more powerful `cachingDecorator`:
@ -438,7 +438,7 @@ So, technically it takes `this` and joins `this[0]`, `this[1]` ...etc together.
*Decorator* is a wrapper around a function that alters its behavior. The main job is still carried out by the function. *Decorator* is a wrapper around a function that alters its behavior. The main job is still carried out by the function.
It is generally safe to replace a function or a method with a decorated one, except for one little thing. If the original function had properties on it, like `func.calledCount` or whatever, then the decorated one will not provide them. Because that is a wrapper. So one need to be careful if one uses them. Some decorators provide their own properties. It is generally safe to replace a function or a method with a decorated one, except for one little thing. If the original function had properties on it, like `func.calledCount` or whatever, then the decorated one will not provide them. Because that is a wrapper. So one needs to be careful if one uses them. Some decorators provide their own properties.
Decorators can be seen as "features" or "aspects" that can be added to a function. We can add one or add many. And all this without changing its code! Decorators can be seen as "features" or "aspects" that can be added to a function. We can add one or add many. And all this without changing its code!

View file

@ -125,4 +125,4 @@ Arrow functions:
- Can't be called with `new`. - Can't be called with `new`.
- (They also don't have `super`, but we didn't study it. Will be in the chapter <info:class-inheritance>). - (They also don't have `super`, but we didn't study it. Will be in the chapter <info:class-inheritance>).
That's because they are meant for short pieces of code that does not have their own "context", but rather works in the current one. And they really shine in that use case. That's because they are meant for short pieces of code that do not have their own "context", but rather works in the current one. And they really shine in that use case.

View file

@ -15,7 +15,7 @@ Object properties, besides a **`value`**, have three special attributes (so-call
- **`enumerable`** -- if `true`, then listed in loops, otherwise not listed. - **`enumerable`** -- if `true`, then listed in loops, otherwise not listed.
- **`configurable`** -- if `true`, the property can be deleted and these attributes can be modified, otherwise not. - **`configurable`** -- if `true`, the property can be deleted and these attributes can be modified, otherwise not.
We didn't see them yet, because generally they do not show up. When we create a property "the usual way", all of them are `true`. But we also can change them any time. We didn't see them yet, because generally they do not show up. When we create a property "the usual way", all of them are `true`. But we also can change them anytime.
First, let's see how to get those flags. First, let's see how to get those flags.
@ -156,7 +156,7 @@ let user = {
}; };
// By default, both our properties are listed: // By default, both our properties are listed:
for(let key in user) alert(key); // name, toString for (let key in user) alert(key); // name, toString
``` ```
If we don't like it, then we can set `enumerable:false`. Then it won't appear in `for..in` loop, just like the built-in one: If we don't like it, then we can set `enumerable:false`. Then it won't appear in `for..in` loop, just like the built-in one:
@ -178,7 +178,7 @@ Object.defineProperty(user, "toString", {
*!* *!*
// Now our toString disappears: // Now our toString disappears:
*/!* */!*
for(let key in user) alert(key); // name for (let key in user) alert(key); // name
``` ```
Non-enumerable properties are also excluded from `Object.keys`: Non-enumerable properties are also excluded from `Object.keys`:
@ -282,7 +282,7 @@ let clone = Object.defineProperties({}, Object.getOwnPropertyDescriptors(obj));
Normally when we clone an object, we use an assignment to copy properties, like this: Normally when we clone an object, we use an assignment to copy properties, like this:
```js ```js
for(let key in user) { for (let key in user) {
clone[key] = user[key] clone[key] = user[key]
} }
``` ```

View file

@ -55,7 +55,7 @@ let scrollHeight = Math.max(
document.body.clientHeight, document.documentElement.clientHeight document.body.clientHeight, document.documentElement.clientHeight
); );
alert('Full document width, with scrolled out part: ' + scrollHeight); alert('Full document height, with scrolled out part: ' + scrollHeight);
``` ```
Why so? Better don't ask. These inconsistencies come from ancient times, not a "smart" logic. Why so? Better don't ask. These inconsistencies come from ancient times, not a "smart" logic.

View file

@ -18,7 +18,7 @@ For instance, if the height of the whole HTML document is 2000px, then:
document.documentElement.getBoundingClientRect().top = 0 document.documentElement.getBoundingClientRect().top = 0
// window-relative bottom = 2000 // window-relative bottom = 2000
// the document is long, so that is probably far beyound the window bottom // the document is long, so that is probably far beyond the window bottom
document.documentElement.getBoundingClientRect().bottom = 2000 document.documentElement.getBoundingClientRect().bottom = 2000
``` ```

View file

@ -12,7 +12,7 @@ Like this:
Please note two important features of the scroll: Please note two important features of the scroll:
1. **The scroll is "elastic".** We can scroll a little beyound the document start or end in some browsers/devices (empty space below is shown, and then the document will automatically "bounces back" to normal). 1. **The scroll is "elastic".** We can scroll a little beyond the document start or end in some browsers/devices (empty space below is shown, and then the document will automatically "bounces back" to normal).
2. **The scroll is imprecise.** When we scroll to page end, then we may be in fact like 0-50px away from the real document bottom. 2. **The scroll is imprecise.** When we scroll to page end, then we may be in fact like 0-50px away from the real document bottom.
So, "scrolling to the end" should mean that the visitor is no more than 100px away from the document end. So, "scrolling to the end" should mean that the visitor is no more than 100px away from the document end.

View file

@ -4,9 +4,9 @@ An element receives a focus when the user either clicks on it or uses the `key:T
Focusing generally means: "prepare to accept the data here", so that's the moment when we can run the code to initialize or load something. Focusing generally means: "prepare to accept the data here", so that's the moment when we can run the code to initialize or load something.
The moment of loosing the focus ("blur") can be even more important. That's when a user clicks somewhere else or presses `key:Tab` to go to the next form field, or there are other means as well. The moment of losing the focus ("blur") can be even more important. That's when a user clicks somewhere else or presses `key:Tab` to go to the next form field, or there are other means as well.
Loosing the focus generally means: "the data has been entered", so we can run the code to check it or even to save it to the server and so on. Losing the focus generally means: "the data has been entered", so we can run the code to check it or even to save it to the server and so on.
There are important peculiarities when working with focus events. We'll do the best to cover them here. There are important peculiarities when working with focus events. We'll do the best to cover them here.
@ -14,7 +14,7 @@ There are important peculiarities when working with focus events. We'll do the b
## Events focus/blur ## Events focus/blur
The `focus` event is called on focusing, and `blur` -- when the element looses the focus. The `focus` event is called on focusing, and `blur` -- when the element loses the focus.
Let's use them for validation of an input field. Let's use them for validation of an input field.
@ -90,7 +90,7 @@ It works in all browsers except Firefox ([bug](https://bugzilla.mozilla.org/show
If we enter something into the input and then try to use `key:Tab` or click away from the `<input>`, then `onblur` returns the focus back. If we enter something into the input and then try to use `key:Tab` or click away from the `<input>`, then `onblur` returns the focus back.
Please note that we can't "prevent loosing focus" by calling `event.preventDefault()` in `onblur`, because `onblur` works *after* the element lost the focus. Please note that we can't "prevent losing focus" by calling `event.preventDefault()` in `onblur`, because `onblur` works *after* the element lost the focus.
```warn header="JavaScript-initiated focus loss" ```warn header="JavaScript-initiated focus loss"
A focus loss can occur for many reasons. A focus loss can occur for many reasons.
@ -214,7 +214,7 @@ So here's another working variant:
## Summary ## Summary
Events `focus` and `blur` trigger on focusing/loosing focus on the element. Events `focus` and `blur` trigger on focusing/losing focus on the element.
Their specials are: Their specials are:
- They do not bubble. Can use capturing state instead or `focusin/focusout`. - They do not bubble. Can use capturing state instead or `focusin/focusout`.

View file

@ -27,7 +27,7 @@ If we have a reference to another window (a popup or iframe), and that window co
If it comes from another origin, then we can only change its location. Please note: not *read* the location, but *modify* it, redirect it to another place. That's safe, because the URL may contain sensitive parameters, so reading it from another origin is prohibited, but changing is not. If it comes from another origin, then we can only change its location. Please note: not *read* the location, but *modify* it, redirect it to another place. That's safe, because the URL may contain sensitive parameters, so reading it from another origin is prohibited, but changing is not.
Also such windows windows may exchange messages. Soon about that later. Also such windows may exchange messages. Soon about that later.
````warn header="Exclusion: subdomains may be same-origin" ````warn header="Exclusion: subdomains may be same-origin"

View file

@ -153,7 +153,7 @@ We can use special characters in it:
|`$&`|the whole match| |`$&`|the whole match|
|<code>$&#096;</code>|a part of the string before the match| |<code>$&#096;</code>|a part of the string before the match|
|`$'`|a part of the string after the match| |`$'`|a part of the string after the match|
|`$n`|if `n` is a 1-2 digit number, then it means the contents of n-th parentheses counting fro left to right| |`$n`|if `n` is a 1-2 digit number, then it means the contents of n-th parentheses counting from left to right|
For instance let's use `$&` to replace all entries of `"John"` by `"Mr.John"`: For instance let's use `$&` to replace all entries of `"John"` by `"Mr.John"`:

View file

@ -101,9 +101,9 @@ So it matches `pattern:\bHello\b` and `pattern:\bJava\b`, but not `pattern:\bHel
```js run ```js run
alert( "Hello, Java!".match(/\bHello\b/) ); // Hello alert( "Hello, Java!".match(/\bHello\b/) ); // Hello
alert( "Hello, Java!".match(/\Java\b/) ); // Java alert( "Hello, Java!".match(/\bJava\b/) ); // Java
alert( "Hello, Java!".match(/\Hell\b/) ); // null alert( "Hello, Java!".match(/\bHell\b/) ); // null
alert( "Hello, Java!".match(/\Java!\b/) ); // null alert( "Hello, Java!".match(/\bJava!\b/) ); // null
``` ```
Once again let's note that `pattern:\b` makes the searching engine to test for the boundary, so that `pattern:Java\b` finds `match:Java` only when followed by a word boundary, but it does not add a letter to the result. Once again let's note that `pattern:\b` makes the searching engine to test for the boundary, so that `pattern:Java\b` finds `match:Java` only when followed by a word boundary, but it does not add a letter to the result.

View file

@ -30,7 +30,7 @@ alert( "function g()".match(/g\(\)/) ); // "g()"
If we're looking for a backslash `\`, then we should double it: If we're looking for a backslash `\`, then we should double it:
```js run ```js run
alert( "1\2".match(/\\/) ); // '\' alert( "1\\2".match(/\\/) ); // '\'
``` ```
## A slash ## A slash

View file

@ -365,7 +365,7 @@ loadJson('/article/promise-chaining/user.json')
## Error handling ## Error handling
Asynchronous actions may sometimes fail: in case of an error the corresponding promises becomes rejected. For instance, `fetch` fails if the remote server is not available. We can use `.catch` to handle errors (rejections). Asynchronous actions may sometimes fail: in case of an error the corresponding promise becomes rejected. For instance, `fetch` fails if the remote server is not available. We can use `.catch` to handle errors (rejections).
Promise chaining is great at that aspect. When a promise rejects, the control jumps to the closest rejection handler down the chain. That's very convenient in practice. Promise chaining is great at that aspect. When a promise rejects, the control jumps to the closest rejection handler down the chain. That's very convenient in practice.