Merge branch 'master' of https://github.com/iliakan/javascript-tutorial-en
This commit is contained in:
commit
0f1455da90
34 changed files with 132 additions and 132 deletions
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -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 ];
|
||||||
|
|
||||||
|
|
|
@ -11,9 +11,9 @@ 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 ];
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -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":
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -457,7 +457,7 @@ function showMenu({
|
||||||
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
|
||||||
}
|
}
|
||||||
|
@ -488,7 +488,7 @@ 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
|
||||||
|
|
|
@ -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`.
|
||||||
|
|
|
@ -68,7 +68,7 @@ pow(x, n) =
|
||||||
```
|
```
|
||||||
|
|
||||||
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`.
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
@ -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.
|
||||||
|
|
|
@ -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:
|
||||||
|
|
||||||
|
@ -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:
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
@ -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()];
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -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));
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -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!
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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`.
|
||||||
|
|
|
@ -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"
|
||||||
|
|
||||||
|
|
|
@ -153,7 +153,7 @@ We can use special characters in it:
|
||||||
|`$&`|the whole match|
|
|`$&`|the whole match|
|
||||||
|<code>$`</code>|a part of the string before the match|
|
|<code>$`</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"`:
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue