Merge branch 'master' into patch-7

This commit is contained in:
Ilya Kantor 2019-07-11 09:18:30 +03:00 committed by GitHub
commit 9a12fcf895
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 129 additions and 84 deletions

View file

@ -3,16 +3,15 @@
This book is a *tutorial*. It aims to help you gradually learn the language. But once you're familiar with the basics, you'll need other sources.
## Specification
**The ECMA-262 specification** contains the most in-depth, detailed and formalized information about JavaScript. It defines the language.
But being that formalized, it's difficult to understand at first. So if you need the most trustworthy source of information about the language details, it's the right place. But it's not for everyday use.
But being that formalized, it's difficult to understand at first. So if you need the most trustworthy source of information about the language details, the specification is the right place. But it's not for everyday use.
The latest draft is at <https://tc39.es/ecma262/>.
To read about bleeding-edge features, that are not yet widely supported, see proposals at <https://github.com/tc39/proposals>.
To read about new bleeding-edge features, that are "almost standard", see proposals at <https://github.com/tc39/proposals>.
Also, if you're in developing for the browser, then there are other specs covered in the [second part](info:browser-environment) of the tutorial.

View file

@ -30,7 +30,7 @@ The function `prompt` accepts two arguments:
result = prompt(title, [default]);
```
It shows a modal window with a text message, an input field for the visitor, and the buttons OK/CANCEL.
It shows a modal window with a text message, an input field for the visitor, and the buttons OK/Cancel.
`title`
: The text to show the visitor.
@ -38,7 +38,7 @@ It shows a modal window with a text message, an input field for the visitor, and
`default`
: An optional second parameter, the initial value for the input field.
The visitor may type something in the prompt input field and press OK. Or they can cancel the input by pressing CANCEL or hitting the `key:Esc` key.
The visitor may type something in the prompt input field and press OK. Or they can cancel the input by pressing Cancel or hitting the `key:Esc` key.
The call to `prompt` returns the text from the input field or `null` if the input was canceled.
@ -74,7 +74,7 @@ The syntax:
result = confirm(question);
```
The function `confirm` shows a modal window with a `question` and two buttons: OK and CANCEL.
The function `confirm` shows a modal window with a `question` and two buttons: OK and Cancel.
The result is `true` if OK is pressed and `false` otherwise.
@ -94,10 +94,10 @@ We covered 3 browser-specific functions to interact with visitors:
: shows a message.
`prompt`
: shows a message asking the user to input text. It returns the text or, if CANCEL or `key:Esc` is clicked, `null`.
: shows a message asking the user to input text. It returns the text or, if Cancel button or `key:Esc` is clicked, `null`.
`confirm`
: shows a message and waits for the user to press "OK" or "CANCEL". It returns `true` for OK and `false` for CANCEL/`key:Esc`.
: shows a message and waits for the user to press "OK" or "Cancel". It returns `true` for OK and `false` for Cancel/`key:Esc`.
All these methods are modal: they pause script execution and don't allow the visitor to interact with the rest of the page until the window has been dismissed.

View file

@ -17,7 +17,7 @@ let double = "double-quoted";
let backticks = `backticks`;
```
Single and double quotes are essentially the same. Backticks, however, allow us to embed any expression into the string, including function calls:
Single and double quotes are essentially the same. Backticks, however, allow us to embed any expression into the string, by wrapping it in `${…}`:
```js run
function sum(a, b) {
@ -39,9 +39,12 @@ let guestList = `Guests:
alert(guestList); // a list of guests, multiple lines
```
If we try to use single or double quotes in the same way, there will be an error:
Looks natural, right? But single or double quotes do not work this way.
If we use them and try to use multiple lines, there'll be an error:
```js run
let guestList = "Guests: // Error: Unexpected token ILLEGAL
let guestList = "Guests: // Error: Unexpected token ILLEGAL
* John";
```
@ -49,10 +52,9 @@ Single and double quotes come from ancient times of language creation when the n
Backticks also allow us to specify a "template function" before the first backtick. The syntax is: <code>func&#96;string&#96;</code>. The function `func` is called automatically, receives the string and embedded expressions and can process them. You can read more about it in the [docs](mdn:/JavaScript/Reference/Template_literals#Tagged_template_literals). This is called "tagged templates". This feature makes it easier to wrap strings into custom templating or other functionality, but it is rarely used.
## Special characters
It is still possible to create multiline strings with single quotes by using a so-called "newline character", written as `\n`, which denotes a line break:
It is still possible to create multiline strings with single and double quotes by using a so-called "newline character", written as `\n`, which denotes a line break:
```js run
let guestList = "Guests:\n * John\n * Pete\n * Mary";
@ -60,27 +62,33 @@ let guestList = "Guests:\n * John\n * Pete\n * Mary";
alert(guestList); // a multiline list of guests
```
For example, these two lines describe the same:
For example, these two lines are equal, just written differently:
```js run
alert( "Hello\nWorld" ); // two lines using a "newline symbol"
let str1 = "Hello\nWorld"; // two lines using a "newline symbol"
// two lines using a normal newline and backticks
alert( `Hello
World` );
let str2 = `Hello
World`;
alert(str1 == str2); // true
```
There are other, less common "special" characters as well. Here's the list:
There are other, less common "special" characters.
Here's the full list:
| Character | Description |
|-----------|-------------|
|`\b`|Backspace|
|`\f`|Form feed|
|`\n`|New line|
|`\r`|Carriage return|
|`\r`|Carriage return: not used alone. Windows text files use a combination of two characters `\n\r` to represent a line break. |
|`\'`, `\"`|Quotes|
|`\\`|Backslash|
|`\t`|Tab|
|`\uNNNN`|A unicode symbol with the hex code `NNNN`, for instance `\u00A9` -- is a unicode for the copyright symbol `©`. It must be exactly 4 hex digits. |
|`\u{NNNNNNNN}`|Some rare characters are encoded with two unicode symbols, taking 4 bytes. This long unicode requires braces around it.|
|`\b`, `\f`,`\v` | Backspace, Form Feed, Vertical Tab -- kept for compatibility, not used nowadays. |
|`\xXX`|Unicode character with the given hexadimal unicode `XX`, e.g. `'\x7A'` is the same as `'z'`.|
|`\uXXXX`|A unicode symbol with the hex code `XXXX` in UTF-16 encoding, for instance `\u00A9` -- is a unicode for the copyright symbol `©`. It must be exactly 4 hex digits. |
|`\u{X…XXXXXX}` (1 to 6 hex characters)|A unicode symbol with the given UTF-32 encoding. Some rare characters are encoded with two unicode symbols, taking 4 bytes. This way we can insert long codes. |
Examples with unicode:
@ -102,7 +110,7 @@ alert( 'I*!*\'*/!*m the Walrus!' ); // *!*I'm*/!* the Walrus!
As you can see, we have to prepend the inner quote by the backslash `\'`, because otherwise it would indicate the string end.
Of course, that refers only to the quotes that are the same as the enclosing ones. So, as a more elegant solution, we could switch to double quotes or backticks instead:
Of course, only to the quotes that are the same as the enclosing ones need to be escaped. So, as a more elegant solution, we could switch to double quotes or backticks instead:
```js run
alert( `I'm the Walrus!` ); // I'm the Walrus!
@ -120,7 +128,6 @@ alert( `The backslash: \\` ); // The backslash: \
## String length
The `length` property has the string length:
```js run
@ -189,7 +196,7 @@ For instance:
```js run
let str = 'Hi';
str = 'h' + str[1]; // replace the string
str = 'h' + str[1]; // replace the string
alert( str ); // hi
```
@ -242,10 +249,8 @@ let str = 'Widget with id';
alert( str.indexOf('id', 2) ) // 12
```
If we're interested in all occurrences, we can run `indexOf` in a loop. Every new call is made with the position after the previous match:
```js run
let str = 'As sly as a fox, as strong as an ox';
@ -367,7 +372,7 @@ The methods [str.startsWith](mdn:js/String/startsWith) and [str.endsWith](mdn:js
```js run
alert( "Widget".startsWith("Wid") ); // true, "Widget" starts with "Wid"
alert( "Widget".endsWith("get") ); // true, "Widget" ends with "get"
alert( "Widget".endsWith("get") ); // true, "Widget" ends with "get"
```
## Getting a substring
@ -401,7 +406,6 @@ There are 3 methods in JavaScript to get a substring: `substring`, `substr` and
alert( str.slice(-4, -1) ); // gif
```
`str.substring(start [, end])`
: Returns the part of the string *between* `start` and `end`.
@ -409,7 +413,6 @@ There are 3 methods in JavaScript to get a substring: `substring`, `substr` and
For instance:
```js run
let str = "st*!*ring*/!*ify";
@ -425,7 +428,6 @@ There are 3 methods in JavaScript to get a substring: `substring`, `substr` and
Negative arguments are (unlike slice) not supported, they are treated as `0`.
`str.substr(start [, length])`
: Returns the part of the string from `start`, with the given `length`.
@ -451,11 +453,10 @@ Let's recap these methods to avoid any confusion:
| `substring(start, end)` | between `start` and `end` | negative values mean `0` |
| `substr(start, length)` | from `start` get `length` characters | allows negative `start` |
```smart header="Which one to choose?"
All of them can do the job. Formally, `substr` has a minor drawback: it is described not in the core JavaScript specification, but in Annex B, which covers browser-only features that exist mainly for historical reasons. So, non-browser environments may fail to support it. But in practice it works everywhere.
The author finds themself using `slice` almost all the time.
Of the other two variants, `slice` is a little bit more flexible, it allows negative arguments and shorter to write. So, it's enough to remember solely `slice` of these three methods.
```
## Comparing strings
@ -527,10 +528,9 @@ The characters are compared by their numeric code. The greater code means that t
- All lowercase letters go after uppercase letters because their codes are greater.
- Some letters like `Ö` stand apart from the main alphabet. Here, it's code is greater than anything from `a` to `z`.
### Correct comparisons
The "right" algorithm to do string comparisons is more complex than it may seem, because alphabets are different for different languages. The same-looking letter may be located differently in different alphabets.
The "right" algorithm to do string comparisons is more complex than it may seem, because alphabets are different for different languages.
So, the browser needs to know the language to compare.
@ -550,7 +550,7 @@ For instance:
alert( 'Österreich'.localeCompare('Zealand') ); // -1
```
This method actually has two additional arguments specified in [the documentation](mdn:js/String/localeCompare), which allows it to specify the language (by default taken from the environment) and setup additional rules like case sensitivity or should `"a"` and `"á"` be treated as the same etc.
This method actually has two additional arguments specified in [the documentation](mdn:js/String/localeCompare), which allows it to specify the language (by default taken from the environment, letter order depends on the language) and setup additional rules like case sensitivity or should `"a"` and `"á"` be treated as the same etc.
## Internals, Unicode
@ -580,7 +580,7 @@ We actually have a single symbol in each of the strings above, but the `length`
`String.fromCodePoint` and `str.codePointAt` are few rare methods that deal with surrogate pairs right. They recently appeared in the language. Before them, there were only [String.fromCharCode](mdn:js/String/fromCharCode) and [str.charCodeAt](mdn:js/String/charCodeAt). These methods are actually the same as `fromCodePoint/codePointAt`, but don't work with surrogate pairs.
But, for instance, getting a symbol can be tricky, because surrogate pairs are treated as two characters:
Getting a symbol can be tricky, because surrogate pairs are treated as two characters:
```js run
alert( '𝒳'[0] ); // strange symbols...
@ -608,7 +608,7 @@ In many languages there are symbols that are composed of the base character with
For instance, the letter `a` can be the base character for: `àáâäãåā`. Most common "composite" character have their own code in the UTF-16 table. But not all of them, because there are too many possible combinations.
To support arbitrary compositions, UTF-16 allows us to use several unicode characters. The base character and one or many "mark" characters that "decorate" it.
To support arbitrary compositions, UTF-16 allows us to use several unicode characters: the base character followed by one or many "mark" characters that "decorate" it.
For instance, if we have `S` followed by the special "dot above" character (code `\u0307`), it is shown as Ṡ.
@ -634,7 +634,7 @@ For instance:
alert( 'S\u0307\u0323' ); // Ṩ, S + dot above + dot below
alert( 'S\u0323\u0307' ); // Ṩ, S + dot below + dot above
alert( 'S\u0307\u0323' == 'S\u0323\u0307' ); // false
alert( 'S\u0307\u0323' == 'S\u0323\u0307' ); // false, different characters (?!)
```
To solve this, there exists a "unicode normalization" algorithm that brings each string to the single "normal" form.
@ -657,10 +657,9 @@ In reality, this is not always the case. The reason being that the symbol `Ṩ`
If you want to learn more about normalization rules and variants -- they are described in the appendix of the Unicode standard: [Unicode Normalization Forms](http://www.unicode.org/reports/tr15/), but for most practical purposes the information from this section is enough.
## Summary
- There are 3 types of quotes. Backticks allow a string to span multiple lines and embed expressions.
- There are 3 types of quotes. Backticks allow a string to span multiple lines and embed expressions `${…}`.
- Strings in JavaScript are encoded using UTF-16.
- We can use special characters like `\n` and insert letters by their unicode using `\u...`.
- To get a character, use: `[]`.
@ -673,6 +672,6 @@ There are several other helpful methods in strings:
- `str.trim()` -- removes ("trims") spaces from the beginning and end of the string.
- `str.repeat(n)` -- repeats the string `n` times.
- ...and more. See the [manual](mdn:js/String) for details.
- ...and more to be found in the [manual](mdn:js/String).
Strings also have methods for doing search/replace with regular expressions. But that topic deserves a separate chapter, so we'll return to that later.
Strings also have methods for doing search/replace with regular expressions. But that's big topic, so it's explained in a separate tutorial section <info:regular-expressions>.

View file

@ -146,7 +146,7 @@ Returning promises allows us to build chains of asynchronous actions.
## Example: loadScript
Let's use this feature with the promisified `loadScript`, defined in the [previous chapter](/promise-basics#loadscript), to load scripts one by one, in sequence:
Let's use this feature with the promisified `loadScript`, defined in the [previous chapter](info:promise-basics#loadscript), to load scripts one by one, in sequence:
```js run
loadScript("/article/promise-chaining/one.js")
@ -207,9 +207,7 @@ Sometimes it's ok to write `.then` directly, because the nested function has acc
````smart header="Thenables"
To be precise, `.then` may return an arbitrary "thenable" object, and it will be treated the same way as a promise.
A "thenable" object is any object with a method `.then`.
To be precise, `.then` may return a so-called "thenable" object - an arbitrary object that has method `.then`, and it will be treated the same way as a promise.
The idea is that 3rd-party libraries may implement "promise-compatible" objects of their own. They can have extended set of methods, but also be compatible with native promises, because they implement `.then`.
@ -244,7 +242,7 @@ This feature allows to integrate custom objects with promise chains without havi
In frontend programming promises are often used for network requests. So let's see an extended example of that.
We'll use the [fetch](mdn:api/WindowOrWorkerGlobalScope/fetch) method to load the information about the user from the remote server. The method is quite complex, it has many optional parameters, but the basic usage is quite simple:
We'll use the [fetch](info:fetch) method to load the information about the user from the remote server. It has a lot of optional parameters covered in separate chapters, but the basic syntax is quite simple:
```js
let promise = fetch(url);

View file

@ -178,7 +178,7 @@ No problem, just prepend it with `async`, like this:
})();
```
Now we have an the async generator, iterable with `for await...of`.
Now we have the async generator, iterable with `for await...of`.
It's indeed very simple. We add the `async` keyword, and the generator now can use `await` inside of it, rely on promises and other async functions.

View file

@ -296,7 +296,7 @@ There are two notable differences of external module scripts:
<script type="module" src="my.js"></script>
```
2. External scripts that are fetched from another domain require [CORS](mdn:Web/HTTP/CORS) headers. In other words, if a module script is fetched from another domain, the remote server must supply a header `Access-Control-Allow-Origin: *` (may use fetching domain instead of `*`) to indicate that the fetch is allowed.
2. External scripts that are fetched from another origin (e.g. another site) require [CORS](mdn:Web/HTTP/CORS) headers, as described in the chapter <info:fetch-crossorigin>. In other words, if a module script is fetched from another origin, the remote server must supply a header `Access-Control-Allow-Origin: *` (may use site domain instead of `*`) to indicate that the fetch is allowed.
```html
<!-- another-site.com must supply Access-Control-Allow-Origin -->
<!-- otherwise, the script won't execute -->

View file

@ -209,16 +209,20 @@ For instance, these are all perfectly valid default exports:
export default class { // no class name
constructor() { ... }
}
```
```js
export default function(user) { // no function name
alert(`Hello, ${user}!`);
}
```
```js
// export a single value, without making a variable
export default ['Jan', 'Feb', 'Mar','Apr', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
```
That's fine, because `export default` is only one per file. Contrary to that, omitting a name for named imports would be an error:
Not giving a name is fine, because `export default` is only one per file. Contrary to that, omitting a name for named imports would be an error:
```js
export class { // Error! (non-default export needs a name)

View file

@ -25,9 +25,9 @@ if(...) {
}
```
That's because, import/export aim to provide a backbone for the code structure. That's a good thing, as code structure can be analyzed, modules can be gathered and bundled together, unused exports can be removed (tree-shaken). That's possible only because everything is fixed.
That's because `import`/`export` aim to provide a backbone for the code structure. That's a good thing, as code structure can be analyzed, modules can be gathered and bundled together, unused exports can be removed ("tree-shaken"). That's possible only because the structure of imports/exports is simple and fixed.
But how do we import a module dynamically, on-demand?
But how can we import a module dynamically, on-demand?
## The import() function
@ -45,10 +45,58 @@ import(modulePath)
Or, we could use `let module = await import(modulePath)` if inside an async function.
Like this:
For instance, if we have the following `say.js`:
```js
// 📁 say.js
export function hi() {
alert(`Hello`);
}
export function bye() {
alert(`Bye`);
}
```
...Then dynamic import can be like this:
```js
let {hi, bye} = await import('./say.js');
hi();
bye();
```
Or, for the default export:
```js
// 📁 say.js
export default function() {
alert("Module loaded (export default)!");
}
```
To import it, we need to get `default` property of the module object, as explained in the [previous chapter](info:import-export).
So, the dynamic import will be like this:
```js
let {default: say} = await import('./say.js'); // map .default to say variable
say();
```
Here's the full example:
[codetabs src="say" current="index.html"]
So, dynamic imports are very simple to use.
So, dynamic imports are very simple to use, and they allow to import modules at run-time.
Also, dynamic imports work in regular scripts, they don't require `script type="module"`.
```smart
Although `import()` looks like a function call, it's a special syntax that just happens to use parentheses (similar to `super()`).
That means that import doesn't inherit from `Function.prototype` so we cannot call or apply it.
```

View file

@ -42,9 +42,7 @@ Two more details:
1. Rendering never happens while the engine executes a task.
Doesn't matter if the task takes a long time. Changes to DOM are painted only after the task is complete.
2. If a task takes too long, the browser can't do other tasks, process user events, so after a time it suggests "killing" it.
Usually, the whole page dies with the task.
2. If a task takes too long, the browser can't do other tasks, process user events, so after a time it raises an alert like "Page Unresponsive" and suggesting to kill the task with the whole page.
Now let's see how we can apply that knowledge.
@ -52,7 +50,7 @@ Now let's see how we can apply that knowledge.
Let's say we have a CPU-hungry task.
For example, syntax-highlighting (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.
For example, syntax-highlighting (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 of time.
While the engine is busy with syntax highlighting, it can't do other DOM-related stuff, process user events, etc. It may even cause the browser to "hang", which is unacceptable.
@ -115,7 +113,7 @@ A single run of `count` does a part of the job `(*)`, and then re-schedules itse
2. Second run counts: `i=1000001..2000000`.
3. ...and so on.
Pauses between `count` executions provide just enough "air" for the JavaScript engine to do something else, to react on other user actions.
Now, if a new side task (e.g. `onclick` event) appears while the engine is busy executing part 1, it gets queued and then executes when part 1 finished, before the next part. Periodic returns to event loop between `count` executions provide just enough "air" for the JavaScript engine to do something else, to react on other user actions.
The notable thing is that both variants -- with and without splitting the job by `setTimeout` -- are comparable in speed. There's no much difference in the overall counting time.
@ -154,11 +152,11 @@ If you run it, it's easy to notice that it takes significantly less time.
Why?
That's simple: remember, there's the in-browser minimal delay of 4ms for many nested `setTimeout` calls. Even if we set `0`, it's `4ms` (or a bit more). So the earlier we schedule it - the faster it runs.
That's simple: as you remember, there's the in-browser minimal delay of 4ms for many nested `setTimeout` calls. Even if we set `0`, it's `4ms` (or a bit more). So the earlier we schedule it - the faster it runs.
## Use case: progress bar
## Use case: progress indication
Another benefit of splitting heavy tasks for browser scripts is that we can show a progress bar.
Another benefit of splitting heavy tasks for browser scripts is that we can show progress indication.
Usually the browser renders after the currently running code is complete. Doesn't matter if the task takes a long time. Changes to DOM are painted only after the task is finished.
@ -185,9 +183,9 @@ Here's the demo, the changes to `i` won't show up until the function finishes, s
...But we also may want to show something during the task, e.g. a progress bar.
If we use `setTimeout` to split the heavy task into pieces, then changes are painted out in-between them.
If we split the heavy task into pieces using `setTimeout`, then changes are painted out in-between them.
This looks better:
This looks prettier:
```html run
<div id="progress"></div>

View file

@ -1,12 +1,12 @@
# Fetch users from GitHub
Create an async function `getUsers(names)`, that gets an array of GitHub user names, fetches them from GitHub and returns an array of GitHub users instead.
Create an async function `getUsers(names)`, that gets an array of GitHub logins, fetche the users from GitHub and returns an array of GitHub users.
The GitHub url with user informaiton is: `https://api.github.com/users/USERNAME`.
The GitHub url with user informaiton for the given `USERNAME` is: `https://api.github.com/users/USERNAME`.
There's a test example in the sandbox.
Important details:
1. There should be one `fetch` request per user. And requests shouldn't wait for each other. So that the data arrives as soon as possible.
2. If a request fails, or if there's no such user, the function should return `null` in the resulting array.
2. If any request fails, or if there's no such user, the function should return `null` in the resulting array.

View file

@ -8,11 +8,9 @@ The constructor is:
let formData = new FormData([form]);
```
If HTML `form` element is provided, it automatically captures its fields.
If HTML `form` element is provided, it automatically captures its fields. As you may have already guessed, `FormData` is an object to store and send form data.
Network methods, such as `fetch` accept `FormData` objects as a body. They are encoded and sent out with `Content-Type: form/multipart`.
So, from the server point of view, that looks like a usual form submission.
The special thing about `FormData` is that network methods, such as `fetch`, can accept a `FormData` object as a body. It's encoded and sent out with `Content-Type: form/multipart`. So, from the server point of view, that looks like a usual form submission.
## Sending a simple form
@ -80,7 +78,7 @@ for(let [name, value] of formData) {
## Sending a form with a file
The form is always sent as `Content-Type: form/multipart`. So, `<input type="file">` fields are sent also, similar to a usual form submission.
The form is always sent as `Content-Type: form/multipart`, this encoding allows to send files. So, `<input type="file">` fields are sent also, similar to a usual form submission.
Here's an example with such form:
@ -109,11 +107,11 @@ Here's an example with such form:
</script>
```
## Sending a form with blob
## Sending a form with Blob data
As we've seen in the chapter <info:fetch>, sending a dynamically generated `Blob`, e.g. an image, is easy. We can supply it directly as `fetch` body.
As we've seen in the chapter <info:fetch>, sending a dynamically generated `Blob`, e.g. an image, is easy. We can supply it directly as `fetch` parameter `body`.
In practice though, it's often more convenient to send an image as a part of the form, with additional fields, such as "name" and other metadata.
In practice though, it's often convenient to send an image not separately, but as a part of the form, with additional fields, such as "name" and other metadata.
Also, servers are usually more suited to accept multipart-encoded forms, rather than raw binary data.
@ -159,11 +157,11 @@ Please note how the image `Blob` is added:
formData.append("image", imageBlob, "image.png");
```
That's same as if there were `<input type="file" name="image">` in the form, and the visitor submitted a file `image.png` from their filesystem.
That's same as if there were `<input type="file" name="image">` in the form, and the visitor submitted a file named `image.png` (3rd argument) from their filesystem.
## Summary
[FormData](https://xhr.spec.whatwg.org/#interface-formdata) objects are used to read HTML form and submit it using `fetch` or another network method.
[FormData](https://xhr.spec.whatwg.org/#interface-formdata) objects are used to capture HTML form and submit it using `fetch` or another network method.
We can either create `new FormData(form)` from an HTML form, or create an empty object, and then append fields with methods:

View file

@ -32,14 +32,15 @@ function onUpload(req, res) {
let fileStream;
// for byte 0, create a new file, otherwise check the size and append to existing one
if (startByte == 0) {
// if startByte is 0 or not set, create a new file, otherwise check the size and append to existing one
if (!startByte) {
upload.bytesReceived = 0;
fileStream = fs.createWriteStream(filePath, {
flags: 'w'
});
debug("New file created: " + filePath);
} else {
// we can check on-disk file size as well to be sure
if (upload.bytesReceived != startByte) {
res.writeHead(400, "Wrong start byte");
res.end(upload.bytesReceived);

View file

@ -5,7 +5,7 @@ class Uploader {
this.onProgress = onProgress;
// create fileId that uniquely identifies the file
// we can also add user session identifier, to make it even more unique
// we could also add user session identifier (if had one), to make it even more unique
this.fileId = file.name + '-' + file.size + '-' + +file.lastModifiedDate;
}

View file

@ -1,7 +1,7 @@
The tutorial is free to read.
If you'd like to do something else with it, please get a permission from Ilya Kantor, iliakan@javascript.ru.
If you'd like to do something else with it, please get a permission from Ilya Kantor, iliakan@javascript.info.
As of now, we license the tutorial to almost everyone for free under the terms of an "open" CC-BY-NC-SA license. Just please be so kind to contact me.
@ -9,7 +9,7 @@ As of now, we license the tutorial to almost everyone for free under the terms o
The full license text is at <https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode>.
You are free to:
It gives the right to:
- **Share** copy and redistribute the tutorial in any medium or material.
- **Adapt** remix, transform, and build upon the material.