This commit is contained in:
Ilya Kantor 2019-07-31 17:48:00 +03:00
parent 85e67ebb5b
commit 19cd4ab574
12 changed files with 56 additions and 59 deletions

View file

@ -29,13 +29,12 @@ To create a new `Date` object call `new Date()` with one of the following argume
alert( Jan02_1970 ); alert( Jan02_1970 );
``` ```
The number of milliseconds that has passed since the beginning of 1970 is called a *timestamp*. An integer number representing the number of milliseconds that has passed since the beginning of 1970 is called a *timestamp*.
It's a lightweight numeric representation of a date. We can always create a date from a timestamp using `new Date(timestamp)` and convert the existing `Date` object to a timestamp using the `date.getTime()` method (see below). It's a lightweight numeric representation of a date. We can always create a date from a timestamp using `new Date(timestamp)` and convert the existing `Date` object to a timestamp using the `date.getTime()` method (see below).
`new Date(datestring)` `new Date(datestring)`
: If there is a single argument, and it's a string, then it is parsed with the `Date.parse` algorithm (see below). : If there is a single argument, and it's a string, then it is parsed automatically. The algorithm is the same as `Date.parse` uses, we'll cover it later.
```js run ```js run
let date = new Date("2017-01-26"); let date = new Date("2017-01-26");
@ -131,12 +130,12 @@ Besides the given methods, there are two special ones that do not have a UTC-var
The following methods allow to set date/time components: The following methods allow to set date/time components:
- [`setFullYear(year [, month, date])`](mdn:js/Date/setFullYear) - [`setFullYear(year, [month], [date])`](mdn:js/Date/setFullYear)
- [`setMonth(month [, date])`](mdn:js/Date/setMonth) - [`setMonth(month, [date])`](mdn:js/Date/setMonth)
- [`setDate(date)`](mdn:js/Date/setDate) - [`setDate(date)`](mdn:js/Date/setDate)
- [`setHours(hour [, min, sec, ms])`](mdn:js/Date/setHours) - [`setHours(hour, [min], [sec], [ms])`](mdn:js/Date/setHours)
- [`setMinutes(min [, sec, ms])`](mdn:js/Date/setMinutes) - [`setMinutes(min, [sec], [ms])`](mdn:js/Date/setMinutes)
- [`setSeconds(sec [, ms])`](mdn:js/Date/setSeconds) - [`setSeconds(sec, [ms])`](mdn:js/Date/setSeconds)
- [`setMilliseconds(ms)`](mdn:js/Date/setMilliseconds) - [`setMilliseconds(ms)`](mdn:js/Date/setMilliseconds)
- [`setTime(milliseconds)`](mdn:js/Date/setTime) (sets the whole date by milliseconds since 01.01.1970 UTC) - [`setTime(milliseconds)`](mdn:js/Date/setTime) (sets the whole date by milliseconds since 01.01.1970 UTC)
@ -318,7 +317,7 @@ As a result, the first benchmark will have less CPU resources than the second. T
**For more reliable benchmarking, the whole pack of benchmarks should be rerun multiple times.** **For more reliable benchmarking, the whole pack of benchmarks should be rerun multiple times.**
Here's the code example: For example, like this:
```js run ```js run
function diffSubtract(date1, date2) { function diffSubtract(date1, date2) {
@ -424,4 +423,4 @@ alert(`Loading started ${performance.now()}ms ago`);
// more than 3 digits after the decimal point are precision errors, but only the first 3 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, almost any device and environment allows to get more precision, it's just not in `Date`.

View file

@ -6,7 +6,7 @@ importance: 5
In simple cases of circular references, we can exclude an offending property from serialization by its name. In simple cases of circular references, we can exclude an offending property from serialization by its name.
But sometimes there are many backreferences. And names may be used both in circular references and normal properties. But sometimes we can't just use the name, as it may be used both in circular references and normal properties. So we can check the property by its value.
Write `replacer` function to stringify everything, but remove properties that reference `meetup`: Write `replacer` function to stringify everything, but remove properties that reference `meetup`:
@ -22,7 +22,7 @@ let meetup = {
}; };
*!* *!*
// circular references // circular references
room.occupiedBy = meetup; room.occupiedBy = meetup;
meetup.self = meetup; meetup.self = meetup;
*/!* */!*
@ -39,4 +39,3 @@ alert( JSON.stringify(meetup, function replacer(key, value) {
} }
*/ */
``` ```

View file

@ -21,7 +21,7 @@ let user = {
alert(user); // {name: "John", age: 30} alert(user); // {name: "John", age: 30}
``` ```
...But in the process of development, new properties are added, old properties are renamed and removed. Updating such `toString` every time can become a pain. We could try to loop over properties in it, but what if the object is complex and has nested objects in properties? We'd need to implement their conversion as well. And, if we're sending the object over a network, then we also need to supply the code to "read" our object on the receiving side. ...But in the process of development, new properties are added, old properties are renamed and removed. Updating such `toString` every time can become a pain. We could try to loop over properties in it, but what if the object is complex and has nested objects in properties? We'd need to implement their conversion as well.
Luckily, there's no need to write the code to handle all this. The task has been solved already. Luckily, there's no need to write the code to handle all this. The task has been solved already.
@ -76,7 +76,7 @@ Please note that a JSON-encoded object has several important differences from th
`JSON.stringify` can be applied to primitives as well. `JSON.stringify` can be applied to primitives as well.
Natively supported JSON types are: JSON supports following data types:
- Objects `{ ... }` - Objects `{ ... }`
- Arrays `[ ... ]` - Arrays `[ ... ]`
@ -100,7 +100,7 @@ alert( JSON.stringify(true) ); // true
alert( JSON.stringify([1, 2, 3]) ); // [1,2,3] alert( JSON.stringify([1, 2, 3]) ); // [1,2,3]
``` ```
JSON is data-only cross-language specification, so some JavaScript-specific object properties are skipped by `JSON.stringify`. JSON is data-only language-independent specification, so some JavaScript-specific object properties are skipped by `JSON.stringify`.
Namely: Namely:
@ -213,9 +213,9 @@ alert( JSON.stringify(meetup, *!*['title', 'participants']*/!*) );
// {"title":"Conference","participants":[{},{}]} // {"title":"Conference","participants":[{},{}]}
``` ```
Here we are probably too strict. The property list is applied to the whole object structure. So participants are empty, because `name` is not in the list. Here we are probably too strict. The property list is applied to the whole object structure. So the objects in `participants` are empty, because `name` is not in the list.
Let's include every property except `room.occupiedBy` that would cause the circular reference: Let's include in the list every property except `room.occupiedBy` that would cause the circular reference:
```js run ```js run
let room = { let room = {
@ -244,7 +244,7 @@ Now everything except `occupiedBy` is serialized. But the list of properties is
Fortunately, we can use a function instead of an array as the `replacer`. Fortunately, we can use a function instead of an array as the `replacer`.
The function will be called for every `(key, value)` pair and should return the "replaced" value, which will be used instead of the original one. The function will be called for every `(key, value)` pair and should return the "replaced" value, which will be used instead of the original one. Or `undefined` if the value is to be skipped.
In our case, we can return `value` "as is" for everything except `occupiedBy`. To ignore `occupiedBy`, the code below returns `undefined`: In our case, we can return `value` "as is" for everything except `occupiedBy`. To ignore `occupiedBy`, the code below returns `undefined`:
@ -262,7 +262,7 @@ let meetup = {
room.occupiedBy = meetup; // room references meetup room.occupiedBy = meetup; // room references meetup
alert( JSON.stringify(meetup, function replacer(key, value) { alert( JSON.stringify(meetup, function replacer(key, value) {
alert(`${key}: ${value}`); // to see what replacer gets alert(`${key}: ${value}`);
return (key == 'occupiedBy') ? undefined : value; return (key == 'occupiedBy') ? undefined : value;
})); }));
@ -283,7 +283,7 @@ Please note that `replacer` function gets every key/value pair including nested
The first call is special. It is made using a special "wrapper object": `{"": meetup}`. In other words, the first `(key, value)` pair has an empty key, and the value is the target object as a whole. That's why the first line is `":[object Object]"` in the example above. The first call is special. It is made using a special "wrapper object": `{"": meetup}`. In other words, the first `(key, value)` pair has an empty key, and the value is the target object as a whole. That's why the first line is `":[object Object]"` in the example above.
The idea is to provide as much power for `replacer` as possible: it has a chance to analyze and replace/skip the whole object if necessary. The idea is to provide as much power for `replacer` as possible: it has a chance to analyze and replace/skip even the whole object if necessary.
## Formatting: spacer ## Formatting: spacer
@ -393,7 +393,7 @@ alert( JSON.stringify(meetup) );
*/ */
``` ```
As we can see, `toJSON` is used both for the direct call `JSON.stringify(room)` and for the nested object. As we can see, `toJSON` is used both for the direct call `JSON.stringify(room)` and when `room` is nested is another encoded object.
## JSON.parse ## JSON.parse
@ -402,7 +402,7 @@ To decode a JSON-string, we need another method named [JSON.parse](mdn:js/JSON/p
The syntax: The syntax:
```js ```js
let value = JSON.parse(str[, reviver]); let value = JSON.parse(str, [reviver]);
``` ```
str str
@ -432,7 +432,7 @@ user = JSON.parse(user);
alert( user.friends[1] ); // 1 alert( user.friends[1] ); // 1
``` ```
The JSON may be as complex as necessary, objects and arrays can include other objects and arrays. But they must obey the format. The JSON may be as complex as necessary, objects and arrays can include other objects and arrays. But they must obey the same JSON format.
Here are typical mistakes in hand-written JSON (sometimes we have to write it for debugging purposes): Here are typical mistakes in hand-written JSON (sometimes we have to write it for debugging purposes):
@ -481,7 +481,7 @@ Whoops! An error!
The value of `meetup.date` is a string, not a `Date` object. How could `JSON.parse` know that it should transform that string into a `Date`? The value of `meetup.date` is a string, not a `Date` object. How could `JSON.parse` know that it should transform that string into a `Date`?
Let's pass to `JSON.parse` the reviving function that returns all values "as is", but `date` will become a `Date`: Let's pass to `JSON.parse` the reviving function as the second argument, that returns all values "as is", but `date` will become a `Date`:
```js run ```js run
let str = '{"title":"Conference","date":"2017-11-30T12:00:00.000Z"}'; let str = '{"title":"Conference","date":"2017-11-30T12:00:00.000Z"}';

View file

@ -326,18 +326,18 @@ In other words, a company has departments.
Now let's say we want a function to get the sum of all salaries. How can we do that? Now let's say we want a function to get the sum of all salaries. How can we do that?
An iterative approach is not easy, because the structure is not simple. The first idea may be to make a `for` loop over `company` with nested subloop over 1st level departments. But then we need more nested subloops to iterate over the staff in 2nd level departments like `sites`. ...And then another subloop inside those for 3rd level departments that might appear in the future? Should we stop on level 3 or make 4 levels of loops? If we put 3-4 nested subloops in the code to traverse a single object, it becomes rather ugly. An iterative approach is not easy, because the structure is not simple. The first idea may be to make a `for` loop over `company` with nested subloop over 1st level departments. But then we need more nested subloops to iterate over the staff in 2nd level departments like `sites`... And then another subloop inside those for 3rd level departments that might appear in the future? If we put 3-4 nested subloops in the code to traverse a single object, it becomes rather ugly.
Let's try recursion. Let's try recursion.
As we can see, when our function gets a department to sum, there are two possible cases: As we can see, when our function gets a department to sum, there are two possible cases:
1. Either it's a "simple" department with an *array of people* -- then we can sum the salaries in a simple loop. 1. Either it's a "simple" department with an *array* of people -- then we can sum the salaries in a simple loop.
2. Or it's *an object with `N` subdepartments* -- then we can make `N` recursive calls to get the sum for each of the subdeps and combine the results. 2. Or it's *an object* with `N` subdepartments -- then we can make `N` recursive calls to get the sum for each of the subdeps and combine the results.
The (1) is the base of recursion, the trivial case. The 1st case is the base of recursion, the trivial case, when we get an array.
The (2) is the recursive step. A complex task is split into subtasks for smaller departments. They may in turn split again, but sooner or later the split will finish at (1). The 2nd case when we gen an object is the recursive step. A complex task is split into subtasks for smaller departments. They may in turn split again, but sooner or later the split will finish at (1).
The algorithm is probably even easier to read from the code: The algorithm is probably even easier to read from the code:

View file

@ -8,7 +8,7 @@ For instance:
- `Object.assign(dest, src1, ..., srcN)` -- copies properties from `src1..N` into `dest`. - `Object.assign(dest, src1, ..., srcN)` -- copies properties from `src1..N` into `dest`.
- ...and so on. - ...and so on.
In this chapter we'll learn how to do the same. And, more importantly, how to feel comfortable working with such functions and arrays. In this chapter we'll learn how to do the same. And also, how to pass arrays to such functions as parameters.
## Rest parameters `...` ## Rest parameters `...`
@ -96,9 +96,7 @@ showName("Julius", "Caesar");
showName("Ilya"); showName("Ilya");
``` ```
In old times, rest parameters did not exist in the language, and using `arguments` was the only way to get all arguments of the function, no matter their total number. In old times, rest parameters did not exist in the language, and using `arguments` was the only way to get all arguments of the function. And it still works, we can find it in the old code.
And it still works, we can use it today.
But the downside is that although `arguments` is both array-like and iterable, it's not an array. It does not support array methods, so we can't call `arguments.map(...)` for example. But the downside is that although `arguments` is both array-like and iterable, it's not an array. It does not support array methods, so we can't call `arguments.map(...)` for example.
@ -119,9 +117,10 @@ function f() {
f(1); // 1 f(1); // 1
``` ```
````
As we remember, arrow functions don't have their own `this`. Now we know they don't have the special `arguments` object either. As we remember, arrow functions don't have their own `this`. Now we know they don't have the special `arguments` object either.
````
## Spread operator [#spread-operator] ## Spread operator [#spread-operator]

View file

@ -80,13 +80,13 @@
<text id="..." font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal" fill="#8A704D"> <text id="..." font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal" fill="#8A704D">
<tspan x="403" y="94">...</tspan> <tspan x="403" y="94">...</tspan>
</text> </text>
<text id="makeArmy()" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal" fill="#8A704D"> <text id="makeArmy()-LexicalEn" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal" fill="#8A704D">
<tspan x="403" y="64">makeArmy()</tspan> <tspan x="403" y="64">makeArmy()</tspan>
<tspan x="403" y="79">Lexical Environment</tspan> <tspan x="403" y="79">LexicalEnvironment</tspan>
</text> </text>
<text id="for-block-Lexical-En" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal" fill="#8A704D"> <text id="for-block-LexicalEnv" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal" fill="#8A704D">
<tspan x="278" y="14">for block</tspan> <tspan x="278" y="14">for block</tspan>
<tspan x="278" y="29">Lexical Environment</tspan> <tspan x="278" y="29">LexicalEnvironment</tspan>
</text> </text>
</g> </g>
</g> </g>

Before

Width:  |  Height:  |  Size: 116 KiB

After

Width:  |  Height:  |  Size: 116 KiB

Before After
Before After

View file

@ -70,7 +70,7 @@ The Lexical Environment object consists of two parts:
1. *Environment Record* -- an object that stores all local variables as its properties (and some other information like the value of `this`). 1. *Environment Record* -- an object that stores all local variables as its properties (and some other information like the value of `this`).
2. A reference to the *outer lexical environment*, the one associated with the outer code. 2. A reference to the *outer lexical environment*, the one associated with the outer code.
**So, a "variable" is just a property of the special internal object, `Environment Record`. "To get or change a variable" means "to get or change a property of that object".** **A "variable" is just a property of the special internal object, `Environment Record`. "To get or change a variable" means "to get or change a property of that object".**
For instance, in this simple code, there is only one Lexical Environment: For instance, in this simple code, there is only one Lexical Environment:
@ -80,7 +80,7 @@ This is a so-called global Lexical Environment, associated with the whole script
On the picture above, the rectangle means Environment Record (variable store) and the arrow means the outer reference. The global Lexical Environment has no outer reference, so it points to `null`. On the picture above, the rectangle means Environment Record (variable store) and the arrow means the outer reference. The global Lexical Environment has no outer reference, so it points to `null`.
Here's the bigger picture of what happens when a `let` changes: And that's how it changes when a variable is defined and assigned:
![lexical environment](lexical-environment-global-2.svg) ![lexical environment](lexical-environment-global-2.svg)
@ -119,7 +119,7 @@ Now let's go on and explore what happens when a function accesses an outer varia
During the call, `say()` uses the outer variable `phrase`, let's look at the details of what's going on. During the call, `say()` uses the outer variable `phrase`, let's look at the details of what's going on.
First, when a function runs, a new function Lexical Environment is created automatically. That's a general rule for all functions. That Lexical Environment is used to store local variables and parameters of the call. When a function runs, a new Lexical Environment is created automatically to store local variables and parameters of the call.
For instance, for `say("John")`, it looks like this (the execution is at the line, labelled with an arrow): For instance, for `say("John")`, it looks like this (the execution is at the line, labelled with an arrow):

View file

@ -57,11 +57,11 @@
<tspan x="675" y="67">null</tspan> <tspan x="675" y="67">null</tspan>
</text> </text>
<polyline id="Rectangle-1-Copy" fill="#EC6B4E" points="3 39.7741935 3 27 12 33.3870968"></polyline> <polyline id="Rectangle-1-Copy" fill="#EC6B4E" points="3 39.7741935 3 27 12 33.3870968"></polyline>
<text id="global-Lexical-Envir" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal" fill="#8A704D"> <text id="global-LexicalEnviro" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal" fill="#8A704D">
<tspan x="440" y="15">global Lexical Environment</tspan> <tspan x="440" y="15">global LexicalEnvironment</tspan>
</text> </text>
<text id="Lexical-Environment" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal" fill="#8A704D"> <text id="LexicalEnvironment-o" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal" fill="#8A704D">
<tspan x="275" y="15">Lexical Environment</tspan> <tspan x="275" y="15">LexicalEnvironment</tspan>
<tspan x="275" y="29">of makeCounter() call</tspan> <tspan x="275" y="29">of makeCounter() call</tspan>
</text> </text>
</g> </g>

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 107 KiB

Before After
Before After

View file

@ -51,7 +51,7 @@
<tspan x="4" y="19">count: 0</tspan> <tspan x="4" y="19">count: 0</tspan>
</text> </text>
</g> </g>
<g id="Group" transform="translate(606.000000, 48.000000)"> <g id="Group" transform="translate(612.000000, 48.000000)">
<path id="Line" d="M16.5,21.5 L2.5,21.5 L2.5,19.5 L16.5,19.5 L16.5,13.5 L30.5,20.5 L16.5,27.5 L16.5,21.5 Z" fill="#EE6B47" fill-rule="nonzero"></path> <path id="Line" d="M16.5,21.5 L2.5,21.5 L2.5,19.5 L16.5,19.5 L16.5,13.5 L30.5,20.5 L16.5,27.5 L16.5,21.5 Z" fill="#EE6B47" fill-rule="nonzero"></path>
<text id="outer" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal" fill="#8A704D"> <text id="outer" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal" fill="#8A704D">
<tspan x="0" y="11">outer</tspan> <tspan x="0" y="11">outer</tspan>
@ -64,7 +64,7 @@
</text> </text>
</g> </g>
<text id="null" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal" fill="#8A704D"> <text id="null" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal" fill="#8A704D">
<tspan x="651" y="71">null</tspan> <tspan x="657" y="71">null</tspan>
</text> </text>
<text id="[[Environment]]" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal" fill="#8A704D"> <text id="[[Environment]]" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal" fill="#8A704D">
<tspan x="184" y="192">[[Environment]]</tspan> <tspan x="184" y="192">[[Environment]]</tspan>

Before

Width:  |  Height:  |  Size: 128 KiB

After

Width:  |  Height:  |  Size: 128 KiB

Before After
Before After

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 149 KiB

After

Width:  |  Height:  |  Size: 149 KiB

Before After
Before After

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Before After
Before After

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 107 KiB

Before After
Before After