fixes
This commit is contained in:
parent
c9401b3104
commit
0fcf9f84fa
58 changed files with 673 additions and 643 deletions
|
@ -1,6 +1,6 @@
|
||||||
# Code editors
|
# Code editors
|
||||||
|
|
||||||
The code editor is the place where a programmer spends most his time.
|
A code editor is the place where a programmer spends most of his time.
|
||||||
|
|
||||||
There are two archetypes: IDE and lightweight editors. Many people feel comfortable choosing one tool of each type.
|
There are two archetypes: IDE and lightweight editors. Many people feel comfortable choosing one tool of each type.
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ They are mainly used to instantly open and edit a file.
|
||||||
|
|
||||||
The main difference between a "lightweight editor" and an "IDE" is that IDE works on a project-level, so it loads much more data on start, analyzes the project structure if needed and so on. A lightweight editor is much faster if we need only one file.
|
The main difference between a "lightweight editor" and an "IDE" is that IDE works on a project-level, so it loads much more data on start, analyzes the project structure if needed and so on. A lightweight editor is much faster if we need only one file.
|
||||||
|
|
||||||
In practice, lightweight editors may have a lot of plugins including directory-level syntax analyzers and autocompleters, so there's no strict border between a lightweight editor and an IDE.
|
In practice, lightweight editors may have a lot of plugins including directory-level syntax analyzers and autocompleters, so there's no strict border between a lightweight editor and an IDE.
|
||||||
|
|
||||||
The following options deserve your attention:
|
The following options deserve your attention:
|
||||||
|
|
||||||
|
|
|
@ -1,18 +1,12 @@
|
||||||
# Developer console
|
# Developer console
|
||||||
|
|
||||||
And the one more thing before we get down to coding.
|
|
||||||
|
|
||||||
A code is error-prone. You are quite likely to have errors... Oh what I'm talking? You are *absolutely* going to make errors, at least if you're a human, not a [robot](https://en.wikipedia.org/wiki/Bender_(Futurama)).
|
A code is error-prone. You are quite likely to have errors... Oh what I'm talking? You are *absolutely* going to make errors, at least if you're a human, not a [robot](https://en.wikipedia.org/wiki/Bender_(Futurama)).
|
||||||
|
|
||||||
But in the browser, a user doesn't see the errors by default. So, if something goes wrong in the script, we won't see what's broken and can't fix it.
|
But in the browser, a user doesn't see the errors by default. So, if something goes wrong in the script, we won't see what's broken and can't fix it.
|
||||||
|
|
||||||
To see errors and get a lot of other useful information about scripts, browsers have embedded "developer tools".
|
To see errors and get a lot of other useful information about scripts, browsers have embedded "developer tools".
|
||||||
|
|
||||||
**Most often developers lean towards Chrome or Firefox for the development.**
|
Most often developers lean towards Chrome or Firefox for the development, because developer tools are best there. Other browsers also provide developer tools, sometimes with special features, but usually are in "catching-up" position. So most people have a "favorite" browser and switch to others if a problem is browser-specific.
|
||||||
|
|
||||||
Other browsers also provide developer tools, but are usually in a "catching-up" position, compared to Chrome/Firefox which are the best.
|
|
||||||
|
|
||||||
Sometimes, it may be required to switch to another browser, if a problem is browser-specific, but that's rare.
|
|
||||||
|
|
||||||
Developer tools are really powerful, there are many features. Here, for the start, we'll learn how to open them, look at errors and run JavaScript commands.
|
Developer tools are really powerful, there are many features. Here, for the start, we'll learn how to open them, look at errors and run JavaScript commands.
|
||||||
|
|
||||||
|
@ -34,10 +28,10 @@ It looks somewhat like this:
|
||||||
|
|
||||||
The exact look depends on your Chrome version. It changes from time to time, but should be similar.
|
The exact look depends on your Chrome version. It changes from time to time, but should be similar.
|
||||||
|
|
||||||
- Here we can see the red-colored error message. In this case the script contains a "lalala" command, which was put there just because it is unknown.
|
- Here we can see the red-colored error message. In this case the script contains an unknown "lalala" command.
|
||||||
- On the right, there is a clickable link to the source `bug.html:12` with the line number where the error has occured.
|
- On the right, there is a clickable link to the source `bug.html:12` with the line number where the error has occured.
|
||||||
|
|
||||||
Below the error message there is a blue `>` symbol. It marks a "command line" where we can type JavaScript commands and press enter to run them (`key:Shift+Enter` to input multiline commands).
|
Below the error message there is a blue `>` symbol. It marks a "command line" where we can type JavaScript commands and press `key:Enter` to run them (`key:Shift+Enter` to input multiline commands).
|
||||||
|
|
||||||
Now we can see errors and that's enough for the start. We'll be back to developer tools later and cover debugging more in-depth in the chapter <info:debugging-chrome>.
|
Now we can see errors and that's enough for the start. We'll be back to developer tools later and cover debugging more in-depth in the chapter <info:debugging-chrome>.
|
||||||
|
|
||||||
|
@ -46,21 +40,21 @@ Now we can see errors and that's enough for the start. We'll be back to develope
|
||||||
|
|
||||||
Most other browsers use `key:F12` to open developer tools.
|
Most other browsers use `key:F12` to open developer tools.
|
||||||
|
|
||||||
The look & feel of them is quite similar, once we know how to use one of them (can start with Chrome), can easily switch to another.
|
The look & feel of them is quite similar. Once you know how to use one of them (can start with Chrome), you can easily switch to another.
|
||||||
|
|
||||||
## Safari
|
## Safari
|
||||||
|
|
||||||
Safari (if you use Mac, not Windows/Linux) is a little bit special here. We need to enable the "Develop menu" first.
|
Safari (Mac browser, not supported for Windows/Linux) is a little bit special here. We need to enable the "Develop menu" first.
|
||||||
|
|
||||||
There's a checkbox for that at the bottom of the "Advanced" pane of the preferences:
|
Open Preferences and go to "Advanced" pane. There's a checkbox at the bottom of it:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Now `key:Cmd+Opt+C` can toggle the console. Also note that the new top menu item has appeared with many useful options.
|
Now `key:Cmd+Opt+C` can toggle the console. Also note that the new top menu item named "Develop" has appeared. It has many commands and options.
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|
||||||
- Developer tools allow us to see errors, run commands, examine variables and much more.
|
- Developer tools allow us to see errors, run commands, examine variables and much more.
|
||||||
- They can be opened with `key:F12` for most browsers under Windows. Chrome for Mac needs `key:Cmd+Opt+J`, Safari: `key:Cmd+Opt+C` (need to enable first).
|
- They can be opened with `key:F12` for most browsers under Windows. Chrome for Mac needs `key:Cmd+Opt+J`, Safari: `key:Cmd+Opt+C` (need to enable first).
|
||||||
|
|
||||||
Now we have the environment ready. In the next section we get down to JavaScript.
|
Now we have the environment ready. In the next section we get down to JavaScript.
|
||||||
|
|
|
@ -15,14 +15,14 @@ We can have as many statements in the code as we want. Another statement can be
|
||||||
For example, here we split the message into two:
|
For example, here we split the message into two:
|
||||||
|
|
||||||
```js run no-beautify
|
```js run no-beautify
|
||||||
alert( 'Hello' ); alert( 'World' );
|
alert('Hello'); alert('World');
|
||||||
```
|
```
|
||||||
|
|
||||||
Usually each statement is written on a separate line -- thus the code becomes more readable:
|
Usually each statement is written on a separate line -- thus the code becomes more readable:
|
||||||
|
|
||||||
```js run no-beautify
|
```js run no-beautify
|
||||||
alert( 'Hello' );
|
alert('Hello');
|
||||||
alert( 'World' );
|
alert('World');
|
||||||
```
|
```
|
||||||
|
|
||||||
## Semicolons [#semicolon]
|
## Semicolons [#semicolon]
|
||||||
|
@ -32,8 +32,8 @@ A semicolon may be omitted in most cases when a line break exists.
|
||||||
This would also work:
|
This would also work:
|
||||||
|
|
||||||
```js run no-beautify
|
```js run no-beautify
|
||||||
alert( 'Hello' )
|
alert('Hello')
|
||||||
alert( 'World' )
|
alert('World')
|
||||||
```
|
```
|
||||||
|
|
||||||
Here JavaScript interprets the line break as an "implicit" semicolon. That's also called an [automatic semicolon insertion](https://tc39.github.io/ecma262/#sec-automatic-semicolon-insertion).
|
Here JavaScript interprets the line break as an "implicit" semicolon. That's also called an [automatic semicolon insertion](https://tc39.github.io/ecma262/#sec-automatic-semicolon-insertion).
|
||||||
|
@ -61,23 +61,21 @@ If you're curious to see a concrete example of such an error, check this code ou
|
||||||
[1, 2].forEach(alert)
|
[1, 2].forEach(alert)
|
||||||
```
|
```
|
||||||
|
|
||||||
It shows `1` then `2`.
|
No need to think about the meaning of the brackets `[]` and `forEach` yet. We'll study them later, for now -- it does not matter. Let's just remember the result: it shows `1`, then `2`.
|
||||||
|
|
||||||
No need to think about the meaning of the brackets `[]` and `forEach` yet. We'll study them, for now -- it does not matter. Let's just remember the result.
|
|
||||||
|
|
||||||
Now let's add an `alert` before the code. And *not* finish it with a semicolon:
|
Now let's add an `alert` before the code. And *not* finish it with a semicolon:
|
||||||
|
|
||||||
```js run no-beautify
|
```js run no-beautify
|
||||||
alert( "There will be an error" )
|
alert("There will be an error")
|
||||||
|
|
||||||
[1, 2].forEach(alert)
|
[1, 2].forEach(alert)
|
||||||
```
|
```
|
||||||
|
|
||||||
Now if we run it, only the first `alert` is shown, and then an error.
|
Now if we run it, only the first `alert` is shown, and then we have an error!
|
||||||
|
|
||||||
But everything is fine again if we add a semicolon after `alert`:
|
But everything is fine again if we add a semicolon after `alert`:
|
||||||
```js run
|
```js run
|
||||||
alert( "All fine now" );
|
alert("All fine now");
|
||||||
|
|
||||||
[1, 2].forEach(alert)
|
[1, 2].forEach(alert)
|
||||||
```
|
```
|
||||||
|
@ -85,12 +83,12 @@ alert( "All fine now" );
|
||||||
Now we have the "All fine now" message and then `1` and `2`.
|
Now we have the "All fine now" message and then `1` and `2`.
|
||||||
|
|
||||||
|
|
||||||
The error in the no-semicolon variant occurs because automatic semicolon insertion rules are complex, and in particular, JavaScript does not imply a semicolon before square brackets `[...]`.
|
The error in the no-semicolon variant occurs because JavaScript does not imply a semicolon before square brackets `[...]`.
|
||||||
|
|
||||||
And, because the semicolon is not auto-inserted, the code in the first example is treated as a single statement, like this:
|
So, because the semicolon is not auto-inserted, the code in the first example is treated as a single statement, that's how the engine sees it:
|
||||||
|
|
||||||
```js run no-beautify
|
```js run no-beautify
|
||||||
alert( "There will be an error" )[1, 2].forEach(alert)
|
alert("There will be an error")[1, 2].forEach(alert)
|
||||||
```
|
```
|
||||||
|
|
||||||
But it should be two separate statements, not a single one. Such a merging in this case is just wrong, hence the error. There are other situations when such thing happens.
|
But it should be two separate statements, not a single one. Such a merging in this case is just wrong, hence the error. There are other situations when such thing happens.
|
||||||
|
@ -111,9 +109,9 @@ The rest of the line is a comment. It may occupy a full line of its own or follo
|
||||||
Like here:
|
Like here:
|
||||||
```js run
|
```js run
|
||||||
// This comment occupies a line of its own
|
// This comment occupies a line of its own
|
||||||
alert( 'Hello' );
|
alert('Hello');
|
||||||
|
|
||||||
alert( 'World' ); // This comment follows the statement
|
alert('World'); // This comment follows the statement
|
||||||
```
|
```
|
||||||
|
|
||||||
**Multiline comments start with a slash and a star <code>"/*"</code> and end with a star and a slash <code>"*/"</code>.**
|
**Multiline comments start with a slash and a star <code>"/*"</code> and end with a star and a slash <code>"*/"</code>.**
|
||||||
|
@ -124,8 +122,8 @@ Like this:
|
||||||
/* An example with two messages.
|
/* An example with two messages.
|
||||||
This is a multiline comment.
|
This is a multiline comment.
|
||||||
*/
|
*/
|
||||||
alert( 'Hello' );
|
alert('Hello');
|
||||||
alert( 'World' );
|
alert('World');
|
||||||
```
|
```
|
||||||
|
|
||||||
The content of comments is ignored, so if we put a code inside <code>/* ... */</code> it won't execute.
|
The content of comments is ignored, so if we put a code inside <code>/* ... */</code> it won't execute.
|
||||||
|
@ -134,9 +132,9 @@ Sometimes it comes handy to temporarily disable a part of the code:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
/* Commenting out the code
|
/* Commenting out the code
|
||||||
alert( 'Hello' );
|
alert('Hello');
|
||||||
*/
|
*/
|
||||||
alert( 'World' );
|
alert('World');
|
||||||
```
|
```
|
||||||
|
|
||||||
```smart header="Use hotkeys!"
|
```smart header="Use hotkeys!"
|
||||||
|
|
|
@ -4,9 +4,8 @@ For a long time JavaScript was evolving without compatibility issues. New featur
|
||||||
|
|
||||||
That had the benefit of never breaking the existing code. But the back side is that any mistake or an imprefect decision made by JavaScript creators got stuck in the language forever.
|
That had the benefit of never breaking the existing code. But the back side is that any mistake or an imprefect decision made by JavaScript creators got stuck in the language forever.
|
||||||
|
|
||||||
It had been so before ECMAScript 5 (ES5) appeared which added new features to the language and modified some of the existing ones.
|
It had been so before 2009 when ECMAScript 5 (ES5) appeared. It added new features to the language and modified some of the existing ones. To keep the old code working, most modifications are off by default. One needs to enable them explicitly with a special directive `"use strict"`.
|
||||||
|
|
||||||
To keep the old code working, most modifications are off by default. One needs to enable them explicitly with a special directive `"use strict"`.
|
|
||||||
|
|
||||||
[cut]
|
[cut]
|
||||||
|
|
||||||
|
@ -61,7 +60,7 @@ It is recommended to always start a script with `"use strict"`, for the followin
|
||||||
1. First, all modern browsers support it. Only outdated ones like Internet Explorer 9 and below do not.
|
1. First, all modern browsers support it. Only outdated ones like Internet Explorer 9 and below do not.
|
||||||
2. Second, the modern JavaScript actually forces us into the strict mode. There are several modern language features like "classes" and "modules" that enable strict mode automatically. So, it's hard to evade it.
|
2. Second, the modern JavaScript actually forces us into the strict mode. There are several modern language features like "classes" and "modules" that enable strict mode automatically. So, it's hard to evade it.
|
||||||
|
|
||||||
Here in the tutorial all code (except where said otherwise) works in `"use strict"`. but we'll still note the subtle differences of what happens if you forget it or if the visitor has an outdated browser. So you will also be able to write a code that also works for old IE if you'd like that.
|
Here in the tutorial all code (where not explicitly noted otherwise) works in `"use strict"`. We concentrate on modern JavaScript. But there will be notes about what happens without `"use strict"`, so that you can understand what's going on if you forget it or if you're working with an outdated script that doesn't have it.
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|
||||||
|
|
|
@ -14,8 +14,8 @@ Second, the name of the current visitor:
|
||||||
let currentUserName = "John";
|
let currentUserName = "John";
|
||||||
```
|
```
|
||||||
|
|
||||||
Again, we could shorten that to `userName` if we know beyound the reasonable doubt that the user is current.
|
Again, we could shorten that to `userName` if we know for sure that the user is current.
|
||||||
|
|
||||||
Modern editors and autocomplete make long variable names easy to write. Don't save on them. Say, a name with 3 words in it is fine.
|
Modern editors and autocomplete make long variable names easy to write. Don't save on them. A name with 3 words in it is fine.
|
||||||
|
|
||||||
And if your editor does not have proper autocompletion, get [a new one](/editor).
|
And if your editor does not have proper autocompletion, get [a new one](/editors).
|
||||||
|
|
|
@ -4,5 +4,5 @@ importance: 3
|
||||||
|
|
||||||
# Giving the right name
|
# Giving the right name
|
||||||
|
|
||||||
1. Create the variable to store the name of our planet. Assign the value `"Earth"` to it. What should be its name?
|
1. Create the variable with the name of our planet. How would you name such a variable?
|
||||||
2. Create the variable to store the name of the current visitor. What about its name?
|
2. Create the variable to store the name of the current visitor. How would you name that variable?
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
# Variables
|
# Variables
|
||||||
|
|
||||||
Most of the time, a script needs to work with the information.
|
Most of the time, a script needs to work with information. If it's an online-shop -- that's going to be the goods and a shopping cart. If it's a chat -- users, messages and so on.
|
||||||
|
|
||||||
If it's an online-shop -- that's going to be the goods and a shopping cart. If it's a chat -- users, messages and so on.
|
|
||||||
|
|
||||||
Variables are used to store the information.
|
Variables are used to store the information.
|
||||||
|
|
||||||
|
@ -10,17 +8,17 @@ Variables are used to store the information.
|
||||||
|
|
||||||
## A variable
|
## A variable
|
||||||
|
|
||||||
A [variable]("https://en.wikipedia.org/wiki/Variable_(computer_science)") is a "named storage" for the information. We can use variables to store goodies, visitors and other data.
|
A [variable](https://en.wikipedia.org/wiki/Variable_(computer_science)) is a "named storage" for data. We can use variables to store goodies, visitors and other data.
|
||||||
|
|
||||||
To create a variable in JavaScript, we need to use the `let` keyword.
|
To create a variable in JavaScript, we need to use the `let` keyword.
|
||||||
|
|
||||||
The statement below creates (in other words: *declares* or *defines*) the variable with the name "message":
|
The statement below creates (in other words: *declares* or *defines*) a variable with the name "message":
|
||||||
|
|
||||||
```js
|
```js
|
||||||
let message;
|
let message;
|
||||||
```
|
```
|
||||||
|
|
||||||
Now we can put some data into it:
|
Now we can put some data into it by using the assignment operator `=`:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
let message;
|
let message;
|
||||||
|
@ -37,7 +35,7 @@ let message;
|
||||||
message = 'Hello!';
|
message = 'Hello!';
|
||||||
|
|
||||||
*!*
|
*!*
|
||||||
alert( message ); // shows the variable content
|
alert(message); // shows the variable content
|
||||||
*/!*
|
*/!*
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -92,12 +90,12 @@ In older scripts you may also find another keyword: `var` instead of `let`:
|
||||||
|
|
||||||
The `var` keyword is *almost* the same as `let`. It also declares a variable, but in a slightly different, "old-school" fashion.
|
The `var` keyword is *almost* the same as `let`. It also declares a variable, but in a slightly different, "old-school" fashion.
|
||||||
|
|
||||||
There are subtle differences between `let` and `var`, but they do not matter for us yet. We'll cover them in detail later.
|
There are subtle differences between `let` and `var`, but they do not matter for us yet. We'll cover them in detail later, in the chapter <info:var>.
|
||||||
````
|
````
|
||||||
|
|
||||||
## A real-life analogy
|
## A real-life analogy
|
||||||
|
|
||||||
We can easily grasp the concept of a "variable" if we imagine it as a "box" for the data, with the unique-named sticker on it.
|
We can easily grasp the concept of a "variable" if we imagine it as a "box" for data, with a unique-named sticker on it.
|
||||||
|
|
||||||
For instance, the variable `message` can be imagined as a box labelled `"message"` with the value `"Hello!"` in it:
|
For instance, the variable `message` can be imagined as a box labelled `"message"` with the value `"Hello!"` in it:
|
||||||
|
|
||||||
|
@ -114,14 +112,14 @@ message = 'Hello!';
|
||||||
|
|
||||||
message = 'World!'; // value changed
|
message = 'World!'; // value changed
|
||||||
|
|
||||||
alert( message );
|
alert(message);
|
||||||
```
|
```
|
||||||
|
|
||||||
When the value is changed, the old data is removed from the variable:
|
When the value is changed, the old data is removed from the variable:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
We can also declare two variables and copy the data from one into the other.
|
We can also declare two variables and copy data from one into the other.
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
let hello = 'Hello world!';
|
let hello = 'Hello world!';
|
||||||
|
@ -133,22 +131,22 @@ let message;
|
||||||
message = hello;
|
message = hello;
|
||||||
*/!*
|
*/!*
|
||||||
|
|
||||||
// now two variables have the same data
|
// now two variables hold the same data
|
||||||
alert( hello ); // Hello world!
|
alert(hello); // Hello world!
|
||||||
alert( message ); // Hello world!
|
alert(message); // Hello world!
|
||||||
```
|
```
|
||||||
|
|
||||||
```smart header="Functional languages"
|
```smart header="Functional languages"
|
||||||
It may be interesting to know that there also exist [functional](http://ru.wikipedia.org/wiki/%D0%AF%D0%B7%D1%8B%D0%BA_%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B3%D0%BE_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F) programming languages that forbid to change a variable value. For example, [Scala](http://www.scala-lang.org/) or [Erlang](http://www.erlang.org/).
|
It may be interesting to know that there also exist [functional](https://en.wikipedia.org/wiki/Functional_programming) programming languages that forbid changing a variable value. For example, [Scala](http://www.scala-lang.org/) or [Erlang](http://www.erlang.org/).
|
||||||
|
|
||||||
In such languages, once the value is assined "into the box" -- it's there forever. If we need to store something else -- the language forces to create a new box (declare a new variable), we can't reuse the old one.
|
In such languages, once the value is stored "in the box" -- it's there forever. If we need to store something else -- the language forces to create a new box (declare a new variable), we can't reuse the old one.
|
||||||
|
|
||||||
Though it may seem a little bit odd at the first sight, these languages are quite capable of serious development. More than that, there are areas like parallel computations where this limitation infers certain benefits. Studying of such a language (even if you're not planning to use it soon) is recommended to broaden one's mind.
|
Though it may seem a little bit odd at the first sight, these languages are quite capable of serious development. More than that, there are areas like parallel computations where this limitation infers certain benefits. Studying of such a language (even if not planning to use it soon) is recommended to broaden the mind.
|
||||||
```
|
```
|
||||||
|
|
||||||
## Variable naming [#variable-naming]
|
## Variable naming [#variable-naming]
|
||||||
|
|
||||||
There are two limitations for the variable name in JavaScript:
|
There are two limitations for a variable name in JavaScript:
|
||||||
|
|
||||||
1. The name must contain only letters, digits, symbols `$` and `_`.
|
1. The name must contain only letters, digits, symbols `$` and `_`.
|
||||||
2. The first character must not be a digit.
|
2. The first character must not be a digit.
|
||||||
|
@ -168,9 +166,9 @@ These names are valid:
|
||||||
|
|
||||||
```js run untrusted
|
```js run untrusted
|
||||||
let $ = 1; // declared a variable with the name "$"
|
let $ = 1; // declared a variable with the name "$"
|
||||||
let _ = 2; // and now the variable with the name "_"
|
let _ = 2; // and now a variable with the name "_"
|
||||||
|
|
||||||
alert( $ + _ ); // 3
|
alert($ + _); // 3
|
||||||
```
|
```
|
||||||
|
|
||||||
Examples of incorrect variable names:
|
Examples of incorrect variable names:
|
||||||
|
@ -211,15 +209,17 @@ let return = 5; // also can't name it "return", error!
|
||||||
|
|
||||||
````warn header="An assignment without `use strict`"
|
````warn header="An assignment without `use strict`"
|
||||||
|
|
||||||
Normally, we need to define a variable before using it. But in the old times, it was technically possible to create a variable by a mere assignment of the value. This still works now in if we don't put `use strict`, the behavior is kept for compatibility with old scripts.
|
Normally, we need to define a variable before using it. But in the old times, it was technically possible to create a variable by a mere assignment of the value, without `let`. This still works now in if we don't put `use strict`, the behavior is kept for compatibility with old scripts.
|
||||||
|
|
||||||
```js run no-strict
|
```js run no-strict
|
||||||
|
// note: no "use strict" in this example
|
||||||
|
|
||||||
num = 5; // the variable "num" is created if didn't exist
|
num = 5; // the variable "num" is created if didn't exist
|
||||||
|
|
||||||
alert(num); // 5
|
alert(num); // 5
|
||||||
```
|
```
|
||||||
|
|
||||||
That's a bad practice of course, it gives an error in the strict mode:
|
That's a bad practice, it gives an error in the strict mode:
|
||||||
|
|
||||||
```js run untrusted
|
```js run untrusted
|
||||||
"use strict";
|
"use strict";
|
||||||
|
@ -233,7 +233,7 @@ num = 5; // error: num is not defined
|
||||||
|
|
||||||
## Constants
|
## Constants
|
||||||
|
|
||||||
To declare a constant (unchanging) variable, one can use `const`:
|
To declare a constant (unchanging) variable, one can use `const` instead of `let`:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const myBirthday = '18.04.1982';
|
const myBirthday = '18.04.1982';
|
||||||
|
@ -266,36 +266,41 @@ const COLOR_ORANGE = "#FF7F00";
|
||||||
|
|
||||||
// ...when we need to pick a color
|
// ...when we need to pick a color
|
||||||
let color = COLOR_ORANGE;
|
let color = COLOR_ORANGE;
|
||||||
alert( color ); // #FF7F00
|
alert(color); // #FF7F00
|
||||||
```
|
```
|
||||||
|
|
||||||
`COLOR_ORANGE` is much easier to remember than `"#FF7F00"`. Also it is much easier to mistype in `"#FF7F00"` than in `COLOR_ORANGE`. And when reading the code -- `COLOR_ORANGE` is much more meaningful.
|
Benefits:
|
||||||
|
|
||||||
|
- `COLOR_ORANGE` is much easier to remember than `"#FF7F00"`.
|
||||||
|
- It is much easier to mistype in `"#FF7F00"` than in `COLOR_ORANGE`.
|
||||||
|
- When reading the code -- `COLOR_ORANGE` is much more meaningful than `#FF7F00`.
|
||||||
|
|
||||||
When should we use capitals for a constant, and when -- name them normally? Let's make that clear.
|
When should we use capitals for a constant, and when -- name them normally? Let's make that clear.
|
||||||
|
|
||||||
Being a "constant" just means that the value never changes. But there are constants that are known prior to execution (like a hexadimal value for red), and there are those that are *calculated* during the execution, but do not change since then.
|
Being a "constant" just means that the value never changes. But there are constants that are known prior to execution (like a hexadimal value for red), and there are those that are *calculated* in run-time, during the execution, but do not change after the assignment.
|
||||||
|
|
||||||
|
For instance:
|
||||||
```js
|
```js
|
||||||
const ordinaryConst = /* an expression that uses the current date */;
|
const pageLoadTime = /* time taken by a webpage to load */;
|
||||||
// the expression value is not known prior to execution
|
|
||||||
// such a constant must be named normally
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The value of `pageLoadTime` is not known prior to the page load, so it's named normally. But it's still a constant, because it doesn't change after assignment.
|
||||||
|
|
||||||
In other words, capital-named constants are only used as aliases for "hard-coded" values.
|
In other words, capital-named constants are only used as aliases for "hard-coded" values.
|
||||||
|
|
||||||
## Name things right
|
## Name things right
|
||||||
|
|
||||||
Talking about variables, there's one more exteremely important thing.
|
Talking about variables, there's one more exteremely important thing.
|
||||||
|
|
||||||
Please name the variables sensibly.
|
Please name the variables sensibly. Take time to think if needed.
|
||||||
|
|
||||||
Variable naming is one of the most important and complex skills in programming. A quick glance at variable names can obviously show which code is written by a beginner and which by an experienced guru.
|
Variable naming is one of the most important and complex skills in programming. A quick glance at variable names can reveal which code is written by a beginner and which by an experienced developer.
|
||||||
|
|
||||||
In a real project, most of the time is spent on modifying and extending the existing code base, rather than writing something completely separate of it. And when we return to the code after some time of doing something else, it's much easier to find the information that is well-labelled. Or, in other words, when the variables have good names.
|
In a real project, most of the time is spent on modifying and extending the existing code base, rather than writing something completely separate from the scratch. And when we return to the code after some time of doing something else, it's much easier to find the information that is well-labelled. Or, in other words, when the variables have good names.
|
||||||
|
|
||||||
Please spend some time thinking about the right name for a variable before declaring it. That will repay you a lot.
|
Please spend some time thinking about the right name for a variable before declaring it. That will repay you a lot.
|
||||||
|
|
||||||
Few good-to-follow rules are:
|
Some good-to-follow rules are:
|
||||||
|
|
||||||
- Use human-readable names like `userName` or `shoppingCart`.
|
- Use human-readable names like `userName` or `shoppingCart`.
|
||||||
- Stay away from abbreviations or short names `a`, `b`, `c`, unless you really know what you're doing.
|
- Stay away from abbreviations or short names `a`, `b`, `c`, unless you really know what you're doing.
|
||||||
|
@ -313,7 +318,7 @@ Such a programmer saves a little bit on variable declaration, but looses ten tim
|
||||||
|
|
||||||
An extra variable is good, not evil.
|
An extra variable is good, not evil.
|
||||||
|
|
||||||
Modern JavaScript minifiers and browsers optimize code well enough, so it won't create performance issues. Using different variables for values of different types can even help the engine to optimize better.
|
Modern JavaScript minifiers and browsers optimize code well enough, so it won't create performance issues. Using different variables for different values can even help the engine to optimize.
|
||||||
```
|
```
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
@ -321,7 +326,7 @@ Modern JavaScript minifiers and browsers optimize code well enough, so it won't
|
||||||
We can declare variables to store data. That can be done using `var` or `let` or `const`.
|
We can declare variables to store data. That can be done using `var` or `let` or `const`.
|
||||||
|
|
||||||
- `let` -- is a modern variable declaration. The code must be in strict mode to use `let` in Chrome (V8).
|
- `let` -- is a modern variable declaration. The code must be in strict mode to use `let` in Chrome (V8).
|
||||||
- `var` -- is an old-school variable declaration. We'll study the subtle differences from `let` later.
|
- `var` -- is an old-school variable declaration. Normally we don't use it at all, but we'll cover subtle differences from `let` in the chapter <info:var>, just in case if you'll need them.
|
||||||
- `const` -- is like `let`, but the variable can't be changed.
|
- `const` -- is like `let`, but the variable can't be changed.
|
||||||
|
|
||||||
Variables should be named in a way that allows to easily understand what's inside.
|
Variables should be named in a way that allows to easily understand what's inside.
|
||||||
|
|
|
@ -8,9 +8,9 @@ let message = "hello";
|
||||||
message = 123456;
|
message = 123456;
|
||||||
```
|
```
|
||||||
|
|
||||||
Such languages are called "dynamically typed", meaning that there are language types, but variables are not bound to any of them.
|
Programming languages that allow such thing are called "dynamically typed", meaning that there are data types, but variables are not bound to any of them.
|
||||||
|
|
||||||
There are 7 basic data types in JavaScript. Here we'll study the basics, and in the next chapters we'll talk about each of them in detail.
|
There are 7 basic data types in JavaScript. Here we'll study the basics, and in next chapters we'll talk about each of them in detail.
|
||||||
|
|
||||||
[cut]
|
[cut]
|
||||||
|
|
||||||
|
@ -21,11 +21,11 @@ let n = 123;
|
||||||
n = 12.345;
|
n = 12.345;
|
||||||
```
|
```
|
||||||
|
|
||||||
A *number* type serves both for integer and floating point numbers.
|
The *number* type serves both for integer and floating point numbers.
|
||||||
|
|
||||||
There are many operations for numbers, e.g. multiplication `*`, division `/`, addition `+`, substraction `-` and so on.
|
There are many operations for numbers, e.g. multiplication `*`, division `/`, addition `+`, substraction `-` and so on.
|
||||||
|
|
||||||
Besides regular numbers there are so-called "special numeric values" which also belong to that type: `Infinity`, `-Infinity` and `NaN`.
|
Besides regular numbers, there are so-called "special numeric values" which also belong to that type: `Infinity`, `-Infinity` and `NaN`.
|
||||||
|
|
||||||
- `Infinity` represents the mathematical [Infinity](https://en.wikipedia.org/wiki/Infinity) ∞. It is a special value that's greater than any number.
|
- `Infinity` represents the mathematical [Infinity](https://en.wikipedia.org/wiki/Infinity) ∞. It is a special value that's greater than any number.
|
||||||
|
|
||||||
|
@ -80,23 +80,23 @@ In JavaScript, there are 3 types of quotes.
|
||||||
2. Single quotes: `'Hello'`.
|
2. Single quotes: `'Hello'`.
|
||||||
3. Backticks: <code>`Hello`</code>.
|
3. Backticks: <code>`Hello`</code>.
|
||||||
|
|
||||||
Double and single quotes are "simple" quotes. They mark the beginning and the end of the string, that's all. There's no difference between them in JavaScript.
|
Double and single quotes are "simple" quotes. There's no difference between them in JavaScript.
|
||||||
|
|
||||||
Backticks are "extended functionality" quotes. They allow to embed variables and expressions into a string by wrapping them in `${…}`, for example:
|
Backticks are "extended functionality" quotes. They allow to embed variables and expressions into a string by wrapping them in `${…}`, for example:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
let name = "John";
|
let name = "John";
|
||||||
|
|
||||||
// embed variable
|
// embed a variable
|
||||||
alert( `Hello, ${name}!` ); // Hello, John!
|
alert( `Hello, *!*${name}*/!*!` ); // Hello, John!
|
||||||
|
|
||||||
// embed expression
|
// embed an expression
|
||||||
alert( `the result is ${1 + 2}` ); // the result is 3
|
alert( `the result is *!*${1 + 2}*/!*` ); // the result is 3
|
||||||
```
|
```
|
||||||
|
|
||||||
The expression inside `${…}` is evaluated and the result becomes a part of the string. We can put anything there: a variable like `name` or an arithmetical expression like `1 + 2` or something more complex.
|
The expression inside `${…}` is evaluated and the result becomes a part of the string. We can put anything there: a variable like `name` or an arithmetical expression like `1 + 2` or something more complex.
|
||||||
|
|
||||||
Please note that other quotes do not allow such embedding!
|
Please note that this only can be done in backticks, other quotes do not allow such embedding!
|
||||||
```js run
|
```js run
|
||||||
alert( "the result is ${1 + 2}" ); // the result is ${1 + 2} (double quotes do nothing)
|
alert( "the result is ${1 + 2}" ); // the result is ${1 + 2} (double quotes do nothing)
|
||||||
```
|
```
|
||||||
|
@ -109,7 +109,7 @@ In some languages, there is a special "character" type for a single character. F
|
||||||
In JavaScript, there is no such type. There's only one type: `string`. A string may consist of only one character or many of them.
|
In JavaScript, there is no such type. There's only one type: `string`. A string may consist of only one character or many of them.
|
||||||
```
|
```
|
||||||
|
|
||||||
## A boolean (logical)
|
## A boolean (logical type)
|
||||||
|
|
||||||
The boolean type has only two values: `true` and `false`.
|
The boolean type has only two values: `true` and `false`.
|
||||||
|
|
||||||
|
@ -118,11 +118,11 @@ This type is commonly used to store yes/no values: `true` means "yes, correct",
|
||||||
For instance:
|
For instance:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
let nameChecked = true; // yes, the form field name is checked
|
let nameFieldChecked = true; // yes, name field is checked
|
||||||
let ageChecked = false; // no, the form field age is not checked
|
let ageFieldChecked = false; // no, age field is not checked
|
||||||
```
|
```
|
||||||
|
|
||||||
Boolean values also come as the result of comparisons:
|
Boolean values also come as a result of comparisons:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
let isGreater = 4 > 1;
|
let isGreater = 4 > 1;
|
||||||
|
@ -130,11 +130,11 @@ let isGreater = 4 > 1;
|
||||||
alert( isGreater ); // true (the comparison result is "yes")
|
alert( isGreater ); // true (the comparison result is "yes")
|
||||||
```
|
```
|
||||||
|
|
||||||
We'll cover booleans more deeply later while discussing [logical operators](/logical-ops).
|
We'll cover booleans more deeply later in the chapter <info:logical-operators>.
|
||||||
|
|
||||||
## The "null" value
|
## The "null" value
|
||||||
|
|
||||||
The special `null` value does not belong to any type described above.
|
The special `null` value does not belong to any type of those described above.
|
||||||
|
|
||||||
It forms a separate type of its own, which contains only the `null` value:
|
It forms a separate type of its own, which contains only the `null` value:
|
||||||
|
|
||||||
|
@ -152,24 +152,24 @@ The code above states that the `age` is unknown or empty for some reason.
|
||||||
|
|
||||||
The special value `undefined` stands apart. It makes a type of its own, just like `null`.
|
The special value `undefined` stands apart. It makes a type of its own, just like `null`.
|
||||||
|
|
||||||
The sense of `undefined` is "value is not assigned".
|
The meaning of `undefined` is "value is not assigned".
|
||||||
|
|
||||||
If a variable is declared, but not assigned, then its value is exactly `undefined`:
|
If a variable is declared, but not assigned, then its value is exactly `undefined`:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
let x;
|
let x;
|
||||||
|
|
||||||
alert( x ); // shows "undefined"
|
alert(x); // shows "undefined"
|
||||||
```
|
```
|
||||||
|
|
||||||
Technically, it is possible to assign to `undefined`:
|
Technically, it is possible to assign any variable to `undefined`:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
let x = 123;
|
let x = 123;
|
||||||
|
|
||||||
x = undefined;
|
x = undefined;
|
||||||
|
|
||||||
alert( x ); // "undefined"
|
alert(x); // "undefined"
|
||||||
```
|
```
|
||||||
|
|
||||||
...But it's not recommended to do that. Normally, we use `null` to write an "empty" or an "unknown" value into the variable, and `undefined` is only used for checks, to see if the variable is assigned or similar.
|
...But it's not recommended to do that. Normally, we use `null` to write an "empty" or an "unknown" value into the variable, and `undefined` is only used for checks, to see if the variable is assigned or similar.
|
||||||
|
@ -178,9 +178,9 @@ alert( x ); // "undefined"
|
||||||
|
|
||||||
The `object` type is special.
|
The `object` type is special.
|
||||||
|
|
||||||
All other types are called "primitive", because their values can contain only a single thing (be it a string or a number or whatever). In contrast, objects are used to store collections data and more complex entities. We'll deal with them later after we know enough about primitives.
|
All other types are called "primitive", because their values can contain only a single thing (be it a string or a number or whatever). In contrast, objects are used to store collections data and more complex entities. We'll deal with them later in the chapter <info:object> after we know enough about primitives.
|
||||||
|
|
||||||
The `symbol` type is used to create unique identifiers for objects. We have to mention it here for completeness, but we'd better study them after covering objects.
|
The `symbol` type is used to create unique identifiers for objects. We have to mention it here for completeness, but it's better to study them after objects.
|
||||||
|
|
||||||
## The typeof operator [#type-typeof]
|
## The typeof operator [#type-typeof]
|
||||||
|
|
||||||
|
@ -193,7 +193,7 @@ It supports two forms of syntax:
|
||||||
|
|
||||||
In other words, it works both with the brackets or without them. The result is the same.
|
In other words, it works both with the brackets or without them. The result is the same.
|
||||||
|
|
||||||
The call to `typeof x` returns a string, which has the type name:
|
The call to `typeof x` returns a string with the type name:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
typeof undefined // "undefined"
|
typeof undefined // "undefined"
|
||||||
|
@ -219,22 +219,22 @@ typeof alert // "function" (3)
|
||||||
*/!*
|
*/!*
|
||||||
```
|
```
|
||||||
|
|
||||||
The last three lines may be a little unobvious so here's explanations:
|
The last three lines may need additional explanations:
|
||||||
|
|
||||||
1. `Math` is a built-in object that provides mathematical operations. We will learn it in the chapter <info:number>. Here it serves just as an example of an object.
|
1. `Math` is a built-in object that provides mathematical operations. We will learn it in the chapter <info:number>. Here it serves just as an example of an object.
|
||||||
2. The result of `typeof null` equals to `"object"`. That's wrong. It is an officially recognized error in `typeof`, kept for compatibility. Of course, `null` is not an object. It is a special value with a separate type of its own. So, again, that's an error in the language.
|
2. The result of `typeof null` is `"object"`. That's wrong. It is an officially recognized error in `typeof`, kept for compatibility. Of course, `null` is not an object. It is a special value with a separate type of its own. So, again, that's an error in the language.
|
||||||
3. The result of `typeof alert` is `"function"`, because `alert` is a function of the language. We'll study functions in the near future and see that actually functions belong to the object type. But `typeof` treats them differently. That's very convenient in practice.
|
3. The result of `typeof alert` is `"function"`, because `alert` is a function of the language. We'll study functions in the next chapters, and we'll see that there's no special "function" type in the language. Functions belong to the object type. But `typeof` treats them differently. Formally, it's incorrect, but very convenient in practice.
|
||||||
|
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|
||||||
There are 7 basic types in JavaScript.
|
There are 7 basic types in JavaScript.
|
||||||
|
|
||||||
- `number` for numbers of any kind, can convert into it using `Number(value)`.
|
- `number` for numbers of any kind: integer or floating-point.
|
||||||
- `string` for strings and characters, can convert into it using `String(value)`.
|
- `string` for strings. A string may have one more more characters, there's no separate single-character type.
|
||||||
- `boolean` for `true`/`false`, can convert into it using `Boolean(value)`.
|
- `boolean` for `true`/`false`.
|
||||||
- `null` for unknown values (a standalone type that has a single value `null`).
|
- `null` for unknown values -- a standalone type that has a single value `null`.
|
||||||
- `undefined` for unassigned values (a standalone type that has a single value `undefined`).
|
- `undefined` for unassigned values -- a standalone type that has a single value `undefined`.
|
||||||
- `object` for more complex data structures.
|
- `object` for more complex data structures.
|
||||||
- `symbol` for unique identifiers.
|
- `symbol` for unique identifiers.
|
||||||
|
|
||||||
|
@ -242,6 +242,6 @@ The `typeof` operator allows to see which type is stored in the variable.
|
||||||
|
|
||||||
- Two forms: `typeof x` or `typeof(x)`.
|
- Two forms: `typeof x` or `typeof(x)`.
|
||||||
- Returns a string with the name of the type, like `"string"`.
|
- Returns a string with the name of the type, like `"string"`.
|
||||||
- Mistreats `null` as an `object` -- the old official error in the language.
|
- For `null` returns `"object"` -- that's the error in the language, it's not an object in fact.
|
||||||
|
|
||||||
In nearest chapters we'll concentrate on understanding how to operate on primitives and once we're familiar with that, then we'll move on to objects.
|
In the next chapters we'll concentrate on primitive values and once we're familiar with that, then we'll move on to objects.
|
||||||
|
|
|
@ -17,6 +17,6 @@ undefined + 1 = NaN // (4)
|
||||||
```
|
```
|
||||||
|
|
||||||
1. The addition with a string `"" + 1` converts `1` to a string: `"" + 1 = "1"`, and then we have `"1" + 0`, the same rule is applied.
|
1. The addition with a string `"" + 1` converts `1` to a string: `"" + 1 = "1"`, and then we have `"1" + 0`, the same rule is applied.
|
||||||
2. The substruction `"-"` (like most math operations) only works with numbers, it converts an empty string `""` to zero immediately.
|
2. The substruction `"-"` (like most math operations) only works with numbers, it converts an empty string `""` to `0`.
|
||||||
3. `null` becomes `0` after the numeric conversion.
|
3. `null` becomes `0` after the numeric conversion.
|
||||||
4. `undefined` becomes `NaN` after the numeric conversion.
|
4. `undefined` becomes `NaN` after the numeric conversion.
|
||||||
|
|
|
@ -4,7 +4,7 @@ importance: 5
|
||||||
|
|
||||||
# Type conversions
|
# Type conversions
|
||||||
|
|
||||||
What will be the result of these evaluation?
|
What are results of these expressions?
|
||||||
|
|
||||||
```js no-beautify
|
```js no-beautify
|
||||||
"" + 1 + 0
|
"" + 1 + 0
|
||||||
|
|
|
@ -2,37 +2,31 @@
|
||||||
|
|
||||||
Most of time, operators and functions automatically convert a value to the right type. That's called "type coercion".
|
Most of time, operators and functions automatically convert a value to the right type. That's called "type coercion".
|
||||||
|
|
||||||
For example, `alert` automatically converts any value to a string, to show it. Or mathematical operations convert values to numbers.
|
For example, `alert` automatically converts any value to a string to show it. Mathematical operations convert values to numbers.
|
||||||
|
|
||||||
There are also cases when we need to explicitly convert a value to put things right.
|
There are also cases when we need to explicitly convert a value to put things right.
|
||||||
|
|
||||||
[cut]
|
[cut]
|
||||||
|
|
||||||
```smart header="Not covering objects yet"
|
```smart header="Not talking about objects yet"
|
||||||
In this chapter we don't cover objects yet. Here we study primitives first, and then we'll see what happens with objects in the chapter <info:object-toprimitive>.
|
In this chapter we don't cover objects yet. Here we study primitives first. Later, after we learn objects, we'll see how object conversion works in the chapter <info:object-toprimitive>.
|
||||||
```
|
```
|
||||||
|
|
||||||
## ToString
|
## ToString
|
||||||
|
|
||||||
The string conversion happens when we need a string form of a value.
|
The string conversion happens when we need a string form of a value.
|
||||||
|
|
||||||
For example, `alert` does it:
|
For example, `alert(value)` does it to show the value.
|
||||||
|
|
||||||
```js run
|
|
||||||
let a = true;
|
|
||||||
|
|
||||||
alert(a); // "true"
|
|
||||||
```
|
|
||||||
|
|
||||||
We can also use a call `String(value)` function for that:
|
We can also use a call `String(value)` function for that:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
let a = true;
|
let value = true;
|
||||||
alert(typeof a); // boolean
|
alert(typeof value); // boolean
|
||||||
|
|
||||||
*!*
|
*!*
|
||||||
a = String(a); // now: a = "true"
|
value = String(value); // now value is a string "true"
|
||||||
alert(typeof a); // string
|
alert(typeof value); // string
|
||||||
*/!*
|
*/!*
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -42,7 +36,7 @@ The string conversion is mostly obvious. A `false` becomes `"false"`, `null` bec
|
||||||
|
|
||||||
Numeric conversion happens in mathematical functions and expressions automatically.
|
Numeric conversion happens in mathematical functions and expressions automatically.
|
||||||
|
|
||||||
For example, when division '/' is applied to non-numbers:
|
For example, when division `/` is applied to non-numbers:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
alert( "6" / "2" ); // 3, strings are converted to numbers
|
alert( "6" / "2" ); // 3, strings are converted to numbers
|
||||||
|
@ -54,12 +48,12 @@ We can use a `Number(value)` function to explicitly convert a `value`:
|
||||||
let str = "123";
|
let str = "123";
|
||||||
alert(typeof str); // string
|
alert(typeof str); // string
|
||||||
|
|
||||||
let n = Number(str); // becomes a number 123
|
let num = Number(str); // becomes a number 123
|
||||||
|
|
||||||
alert(typeof n); // number
|
alert(typeof num); // number
|
||||||
```
|
```
|
||||||
|
|
||||||
The explicit conversion is usually required when we read a value coming from a text form field or another string-based source, but we expect a number to be entered.
|
The explicit conversion is usually required when we read a value from a string-based source like a text form, but we expect a number to be entered.
|
||||||
|
|
||||||
If the string is not a valid number, the result of such conversion is `NaN`, for instance:
|
If the string is not a valid number, the result of such conversion is `NaN`, for instance:
|
||||||
|
|
||||||
|
@ -69,7 +63,7 @@ let age = Number("an arbitrary string instead of a number");
|
||||||
alert(age); // NaN, conversion failed
|
alert(age); // NaN, conversion failed
|
||||||
```
|
```
|
||||||
|
|
||||||
The numeric conversion rules:
|
Numeric conversion rules:
|
||||||
|
|
||||||
| Value | Becomes... |
|
| Value | Becomes... |
|
||||||
|-------|-------------|
|
|-------|-------------|
|
||||||
|
@ -97,8 +91,6 @@ Then it concatenates (joins) them:
|
||||||
```js run
|
```js run
|
||||||
alert( 1 + '2' ); // '12' (string to the right)
|
alert( 1 + '2' ); // '12' (string to the right)
|
||||||
alert( '1' + 2 ); // '12' (string to the left)
|
alert( '1' + 2 ); // '12' (string to the left)
|
||||||
|
|
||||||
alert( 1 + 2 ); // 3, numbers (for the contrast)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
That only happens when one of arguments is a string. Otherwise values are converted to numbers.
|
That only happens when one of arguments is a string. Otherwise values are converted to numbers.
|
||||||
|
@ -130,14 +122,14 @@ Some languages (namely PHP) treat `"0"` as `false`. But in JavaScript a non-empt
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
alert( Boolean("0") ); // true
|
alert( Boolean("0") ); // true
|
||||||
alert( Boolean(" ") ); // also true (any non-empty string is true)
|
alert( Boolean(" ") ); // spaces, also true (any non-empty string is true)
|
||||||
```
|
```
|
||||||
````
|
````
|
||||||
|
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|
||||||
There exist three most widely used type conversions: to string, to number and to boolean.
|
There are three most widely used type conversions: to string, to number and to boolean.
|
||||||
|
|
||||||
**`ToString`** -- occurs when we output something, can be performed with `String(value)`. The conversion to string is usually obvious for primitive values.
|
**`ToString`** -- occurs when we output something, can be performed with `String(value)`. The conversion to string is usually obvious for primitive values.
|
||||||
|
|
||||||
|
@ -164,7 +156,7 @@ Follows the rules:
|
||||||
|
|
||||||
Most of these rules are easy to understand and memorize. The notable exceptions where people usually make mistakes are:
|
Most of these rules are easy to understand and memorize. The notable exceptions where people usually make mistakes are:
|
||||||
|
|
||||||
- `undefined` is `NaN` as a number.
|
- `undefined` is `NaN` as a number, not `0`.
|
||||||
- `"0"` is true as a boolean.
|
- `"0"` and space-only strings like `" "` are true as a boolean.
|
||||||
|
|
||||||
Objects are not covered here, we'll return to them later in the chapter <info:object-toprimitive>, devoted exclusively to objects, after we learn more basic things about JavaScript.
|
Objects are not covered here, we'll return to them later in the chapter <info:object-toprimitive> that is devoted exclusively to objects, after we learn more basic things about JavaScript.
|
||||||
|
|
|
@ -4,7 +4,7 @@ importance: 5
|
||||||
|
|
||||||
# The postfix and prefix forms
|
# The postfix and prefix forms
|
||||||
|
|
||||||
What are the final values of all variables in the code below?
|
What are the final values of all variables `a`, `b`, `c` and `d` after the code below?
|
||||||
|
|
||||||
```js
|
```js
|
||||||
let a = 1, b = 1;
|
let a = 1, b = 1;
|
||||||
|
@ -12,4 +12,3 @@ let a = 1, b = 1;
|
||||||
let c = ++a; // ?
|
let c = ++a; // ?
|
||||||
let d = b++; // ?
|
let d = b++; // ?
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -4,11 +4,10 @@ importance: 3
|
||||||
|
|
||||||
# Assignment result
|
# Assignment result
|
||||||
|
|
||||||
What will be values of `a` and `x` in the example below?
|
What are the values of `a` and `x` after the code below?
|
||||||
|
|
||||||
```js
|
```js
|
||||||
let a = 2;
|
let a = 2;
|
||||||
|
|
||||||
let x = 1 + (a *= 2);
|
let x = 1 + (a *= 2);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Operators
|
# Operators
|
||||||
|
|
||||||
Many operators are known to us from the school program. It is an addition `+`, a multiplication `*`, a substraction `-` and so on.
|
Many operators are known to us from school. It is an addition `+`, a multiplication `*`, a substraction `-` and so on.
|
||||||
|
|
||||||
In this chapter we concentrate on aspects that are not covered by the school arithmetic.
|
In this chapter we concentrate on aspects that are not covered by the school arithmetic.
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ Before we move on, let's grasp the common terminology.
|
||||||
|
|
||||||
## Strings concatenation, binary +
|
## Strings concatenation, binary +
|
||||||
|
|
||||||
Now let's see special features of JavaScript operators, beyond school arithmetics.
|
Now let's see special features of JavaScript operators that are beyond school arithmetics.
|
||||||
|
|
||||||
Usually the plus operator `'+'` sums numbers.
|
Usually the plus operator `'+'` sums numbers.
|
||||||
|
|
||||||
|
@ -126,7 +126,7 @@ If an expression has more than one operator, the execution order is defined by t
|
||||||
|
|
||||||
From the school we all know that the multiplication in the expression `1 + 2 * 2` should be calculated before the addition. That's exactly the precedence thing. The multiplication is said to have *a higher precedence* than the addition.
|
From the school we all know that the multiplication in the expression `1 + 2 * 2` should be calculated before the addition. That's exactly the precedence thing. The multiplication is said to have *a higher precedence* than the addition.
|
||||||
|
|
||||||
Brackets override any precedence, so if we're not satisfied with the order, we can use them, like: `(1 + 2) * 2`.
|
Parentheses override any precedence, so if we're not satisfied with the order, we can use them, like: `(1 + 2) * 2`.
|
||||||
|
|
||||||
There are many operators in JavaScript. Every operator has a corresponding precedence number. The one with the bigger number executes first. If the precedence is same -- the execution order is from left to right.
|
There are many operators in JavaScript. Every operator has a corresponding precedence number. The one with the bigger number executes first. If the precedence is same -- the execution order is from left to right.
|
||||||
|
|
||||||
|
@ -173,14 +173,14 @@ alert( b ); // 4
|
||||||
alert( c ); // 4
|
alert( c ); // 4
|
||||||
```
|
```
|
||||||
|
|
||||||
Assignments always evaluate the right value first, then assign it to the left one. So the chain of assignments is executed from right to left: the rightmost expression `2+2` is calculated first, assigned to `c`, then `b = c` works, thus assigning it to `b`, and then `a = b`. At the end, all variables share a single value.
|
Chained assignments evaluate from right to left. First the rightmost expression `2+2` is evaluated then assigned to the variables on the left: `c`, `b` and `a`. At the end, all variables share a single value.
|
||||||
|
|
||||||
````smart header="The assignment operator `\"=\"` returns a value"
|
````smart header="The assignment operator `\"=\"` returns a value"
|
||||||
An operator always returns a value. That's obvious for most of them like an addition `+` or a multiplication `*`. But the assignment operator follows that rule too.
|
An operator always returns a value. That's obvious for most of them like an addition `+` or a multiplication `*`. But the assignment operator follows that rule too.
|
||||||
|
|
||||||
The call `x = value` writes the `value` into `x` *and then returns it*.
|
The call `x = value` writes the `value` into `x` *and then returns it*.
|
||||||
|
|
||||||
So it is actually possible to use an assignment as the part of a more complex expression:
|
Here's the demo that uses an assignment as the part of a more complex expression:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
let a = 1;
|
let a = 1;
|
||||||
|
@ -196,7 +196,7 @@ alert( c ); // 0
|
||||||
|
|
||||||
In the example above, the result of `(a = b + 1)` is the value which is assigned to `a` (that is `3`). It is then used to substract from `3`.
|
In the example above, the result of `(a = b + 1)` is the value which is assigned to `a` (that is `3`). It is then used to substract from `3`.
|
||||||
|
|
||||||
Funny code, isn't it? We should understand how it works, because sometimes we can see it in 3rd-party libraries, but don't write anything like that ourselves. Such tricks definitely don't make the code clearer and more readable.
|
Funny code, isn't it? We should understand how it works, because sometimes we can see it in 3rd-party libraries, but shouldn't write anything like that ourselves. Such tricks definitely don't make the code clearer and readable.
|
||||||
````
|
````
|
||||||
|
|
||||||
## Remainder %
|
## Remainder %
|
||||||
|
@ -215,7 +215,7 @@ alert( 6 % 3 ); // 0 is a remainder of 6 divided by 3
|
||||||
|
|
||||||
## Exponentiation **
|
## Exponentiation **
|
||||||
|
|
||||||
The exponentiation operator `**` is a recent addition to the language. Not all browsers support it yet.
|
The exponentiation operator `**` is a recent addition to the language.
|
||||||
|
|
||||||
For a natural number `b`, the result of `a ** b` is `a` multiplied by itself `b` times.
|
For a natural number `b`, the result of `a ** b` is `a` multiplied by itself `b` times.
|
||||||
|
|
||||||
|
@ -227,14 +227,13 @@ alert( 2 ** 3 ); // 8 (2 * 2 * 2)
|
||||||
alert( 2 ** 4 ); // 16 (2 * 2 * 2 * 2)
|
alert( 2 ** 4 ); // 16 (2 * 2 * 2 * 2)
|
||||||
```
|
```
|
||||||
|
|
||||||
The operator works with non-integer numbers of `a` and `b` as well, for instance:
|
The operator works for non-integer numbers of `a` and `b` as well, for instance:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
alert( 4 ** (1/2) ); // 2 (square root of 4)
|
alert( 4 ** (1/2) ); // 2 (power of 1/2 is the same as a square root, that's maths)
|
||||||
alert( 8 ** (1/3) ); // 2 (cubic root of 8)
|
alert( 8 ** (1/3) ); // 2 (power of 1/3 is the same as a cubic root)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## Increment/decrement: ++, --
|
## Increment/decrement: ++, --
|
||||||
|
|
||||||
Increasing or decreasing a number by one is among the most common numerical operations.
|
Increasing or decreasing a number by one is among the most common numerical operations.
|
||||||
|
@ -271,7 +270,7 @@ Is there any difference? Yes, but we can only see it if we use the retured value
|
||||||
|
|
||||||
Let's clarify. As we know, all operators return a value. Increment/decrement is not an exception here. The prefix form returns the new value, while the postfix form returns the old value (prior to increment/decrement).
|
Let's clarify. As we know, all operators return a value. Increment/decrement is not an exception here. The prefix form returns the new value, while the postfix form returns the old value (prior to increment/decrement).
|
||||||
|
|
||||||
Let's see the examples:
|
To see the difference -- here's the example:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
let counter = 1;
|
let counter = 1;
|
||||||
|
@ -303,7 +302,7 @@ To summarize:
|
||||||
++counter;
|
++counter;
|
||||||
alert( counter ); // 2, the lines above did the same
|
alert( counter ); // 2, the lines above did the same
|
||||||
```
|
```
|
||||||
- If we'd like to use the result of the operator right now, then we need the prefix form:
|
- If we'd like to increase the value *and* use the result of the operator right now, then we need the prefix form:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
let counter = 0;
|
let counter = 0;
|
||||||
|
@ -362,7 +361,7 @@ The list of operators:
|
||||||
- RIGHT SHIFT ( `>>` )
|
- RIGHT SHIFT ( `>>` )
|
||||||
- ZERO-FILL RIGHT SHIFT ( `>>>` )
|
- ZERO-FILL RIGHT SHIFT ( `>>>` )
|
||||||
|
|
||||||
These operators are used very rarely. To understand them, we should delve into low-level number representation, and it would not be optimal to do that right now. Especially because we won't need them any time soon. If you're curious, you can read the [Bitwise Operators](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators) article in MDN. But it would be more practical to do that when a real need arises.
|
These operators are used very rarely. To understand them, we should delve into low-level number representation, and it would not be optimal to do that right now. Especially because we won't need them any time soon. If you're curious, you can read the [Bitwise Operators](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators) article in MDN. It would be more practical to do that when a real need arises.
|
||||||
|
|
||||||
## Modify-in-place
|
## Modify-in-place
|
||||||
|
|
||||||
|
@ -388,18 +387,19 @@ alert( n ); // 14
|
||||||
|
|
||||||
Short "modify-and-assign" operators exist for all arithmetical and bitwise operators: `/=`, `-=` etc.
|
Short "modify-and-assign" operators exist for all arithmetical and bitwise operators: `/=`, `-=` etc.
|
||||||
|
|
||||||
The modify-and-assign call has the same precedence as a normal assignment, so it executes after most other calculations:
|
Such operators have the same precedence as a normal assignment, so they run after most other calculations:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
let n = 2;
|
let n = 2;
|
||||||
|
|
||||||
n *= 3 + 5;
|
n *= 3 + 5;
|
||||||
|
|
||||||
alert( n ); // 16 (same as n *= 8)
|
alert( n ); // 16 (right part evaluated first, same as n *= 8)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Comma
|
## Comma
|
||||||
|
|
||||||
The comma operator `','` is one of most rare and unusual ones. Sometimes it's used to write shorter code, so we need to know it in order understand what's going on.
|
The comma operator `','` is one of most rare and unusual operators. Sometimes it's used to write shorter code, so we need to know it in order to understand what's going on.
|
||||||
|
|
||||||
The comma operator allows to evaluate several expressions, dividing them with a comma `','`. Each of them is evaluated, but result of only the last one is returned.
|
The comma operator allows to evaluate several expressions, dividing them with a comma `','`. Each of them is evaluated, but result of only the last one is returned.
|
||||||
|
|
||||||
|
@ -415,17 +415,23 @@ alert( a ); // 7 (the result of 3+4)
|
||||||
|
|
||||||
Here, the first expression `1+2` is evaluated, and it's result is thrown away, then `3+4` is evaluated and returned as the result.
|
Here, the first expression `1+2` is evaluated, and it's result is thrown away, then `3+4` is evaluated and returned as the result.
|
||||||
|
|
||||||
|
```smart header="Comma has a very low precedence"
|
||||||
|
Please note that the comma operator has very low precedence, lower than `=`, so parentheses are important in the example above.
|
||||||
|
|
||||||
|
Without them: `a=1+2,3+4` evaluates `+` first, summing the numbers into `a=3,7`, then the assignment operator `=` assigns `a=3`, and then the number after the comma `7` is not processed anyhow, so it's ignored.
|
||||||
|
```
|
||||||
|
|
||||||
Why do we need such an operator which throws away everything except the last part?
|
Why do we need such an operator which throws away everything except the last part?
|
||||||
|
|
||||||
Usually it is used in more complex constructs to put several actions in one line.
|
Sometimes people use it in more complex constructs to put several actions in one line.
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
// three operations in one line
|
// three operations in one line
|
||||||
for (*!*a = 1, b = 3, c = a*b*/!*; a < 10; a++) {
|
for (*!*a = 1, b = 3, c = a * b*/!*; a < 10; a++) {
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Such tricks are used in many JavaScript frameworks, that's why we mention about them. But usually they don't benefit to code readability, so take care.
|
Such tricks are used in many JavaScript frameworks, that's why we mention about them. But usually they don't improve the code readability, so we should think well before writing like that.
|
||||||
|
|
|
@ -5,7 +5,7 @@ Many comparison operators we know from maths:
|
||||||
- Greater/less than: <code>a > b</code>, <code>a < b</code>.
|
- Greater/less than: <code>a > b</code>, <code>a < b</code>.
|
||||||
- Greater/less than or equals: <code>a >= b</code>, <code>a <= b</code>.
|
- Greater/less than or equals: <code>a >= b</code>, <code>a <= b</code>.
|
||||||
- Equality check is written as `a == b` (please note the double equation sign `'='`. A single symbol `a = b` would mean an assignment).
|
- Equality check is written as `a == b` (please note the double equation sign `'='`. A single symbol `a = b` would mean an assignment).
|
||||||
- Not equals. In maths the sign is <code>≠</code>, in JavaScript we use an assignment with an exclamation before it: <code>a != b</code>.
|
- Not equals. In maths the notation is <code>≠</code>, in JavaScript it's written as an assignment with an exclamation sign before it: <code>a != b</code>.
|
||||||
|
|
||||||
[cut]
|
[cut]
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ alert( 2 == 1 ); // false (wrong)
|
||||||
alert( 2 != 1 ); // true (correct)
|
alert( 2 != 1 ); // true (correct)
|
||||||
```
|
```
|
||||||
|
|
||||||
A result of a comparison can be assigned to a variable, just like any value:
|
A comparison result can be assigned to a variable, just like any value:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
let result = 5 > 4; // assign the result of the comparison
|
let result = 5 > 4; // assign the result of the comparison
|
||||||
|
@ -48,7 +48,7 @@ alert( 'Bee' > 'Be' ); // true
|
||||||
The algorithm to compare two strings is simple:
|
The algorithm to compare two strings is simple:
|
||||||
|
|
||||||
1. Compare first characters of both strings.
|
1. Compare first characters of both strings.
|
||||||
2. If the first one is greater(or less), then the first string is greater(or less) than the second and we're done.
|
2. If the first one is greater(or less), then the first string is greater(or less) than the second. We're done.
|
||||||
3. Otherwise if first characters are equal, compare the second characters the same way.
|
3. Otherwise if first characters are equal, compare the second characters the same way.
|
||||||
4. Repeat until the end of any string.
|
4. Repeat until the end of any string.
|
||||||
5. If both strings ended simultaneously, then they are equal. Otherwise the longer string is greater.
|
5. If both strings ended simultaneously, then they are equal. Otherwise the longer string is greater.
|
||||||
|
@ -120,7 +120,7 @@ The same thing with an empty string:
|
||||||
alert( '' == false ); // true
|
alert( '' == false ); // true
|
||||||
```
|
```
|
||||||
|
|
||||||
That's the natural consequence of what we've seen before. Operands of different types are converted to a number. An empty string, just like `false`, becomes a zero.
|
That's because operands of different types are converted to a number by the assignment operator `=`. An empty string, just like `false`, becomes a zero.
|
||||||
|
|
||||||
What to do if we'd like to differentiate `0` from `false`?
|
What to do if we'd like to differentiate `0` from `false`?
|
||||||
|
|
||||||
|
@ -136,11 +136,11 @@ alert( 0 === false ); // false, because the types are different
|
||||||
|
|
||||||
There also exists a "strict non-equality" operator `!==`, as an analogy for `!=`.
|
There also exists a "strict non-equality" operator `!==`, as an analogy for `!=`.
|
||||||
|
|
||||||
The string equality check operator is a bit longer to write, but makes more obvious what's going on.
|
The string equality check operator is a bit longer to write, but makes it obvious what's going on and leaves less space for errors.
|
||||||
|
|
||||||
## Comparison with null and undefined
|
## Comparison with null and undefined
|
||||||
|
|
||||||
Let's see more corner cases.
|
Let's see more edge cases.
|
||||||
|
|
||||||
There's a non-intuitive behavior when `null` or `undefined` is compared with other values.
|
There's a non-intuitive behavior when `null` or `undefined` is compared with other values.
|
||||||
|
|
||||||
|
@ -148,13 +148,21 @@ There's a non-intuitive behavior when `null` or `undefined` is compared with oth
|
||||||
For a strict equality check `===`
|
For a strict equality check `===`
|
||||||
: These values are different, because each of them belong to a separate type of it's own.
|
: These values are different, because each of them belong to a separate type of it's own.
|
||||||
|
|
||||||
For a non-strict check `null == undefined`
|
```js run
|
||||||
|
alert( null === undefined ); // false
|
||||||
|
```
|
||||||
|
|
||||||
|
For a non-strict check `==`
|
||||||
: There's a special rule. These two are a "sweet couple": they equal each other (in the sense of `==`), but no any other value.
|
: There's a special rule. These two are a "sweet couple": they equal each other (in the sense of `==`), but no any other value.
|
||||||
|
|
||||||
For maths and evaluation of other comparisons `< > <= >=`
|
```js run
|
||||||
|
alert( null == undefined ); // true
|
||||||
|
```
|
||||||
|
|
||||||
|
For maths and other comparisons `< > <= >=`
|
||||||
: Values `null/undefined` are converted to a number: `null` becomes `0`, while `undefined` becomes `NaN`.
|
: Values `null/undefined` are converted to a number: `null` becomes `0`, while `undefined` becomes `NaN`.
|
||||||
|
|
||||||
Now let's see funny things that happen when we apply those rules. And, what's more important, how do not fall into a trap with unobvious language features.
|
Now let's see funny things that happen when we apply those rules. And, what's more important, how do not fall into a trap with these features.
|
||||||
|
|
||||||
### Strange result: null vs 0
|
### Strange result: null vs 0
|
||||||
|
|
||||||
|
@ -195,7 +203,7 @@ Why did we observe these examples? Should we remember these pecularities all the
|
||||||
|
|
||||||
Just treat any comparison with `undefined/null` except the strict equality `===` with an exceptional care.
|
Just treat any comparison with `undefined/null` except the strict equality `===` with an exceptional care.
|
||||||
|
|
||||||
Don't use comparisons `>= > < <=` with a variable which may be `null/undefined`, unless you are really sure what you're doing. If a variable can have such values, then check it separately.
|
Don't use comparisons `>= > < <=` with a variable which may be `null/undefined`, unless you are really sure what you're doing. If a variable can have such values, then check for them separately.
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ JavaScript-code:
|
||||||
|
|
||||||
```js demo run
|
```js demo run
|
||||||
let name = prompt("What is your name?", "");
|
let name = prompt("What is your name?", "");
|
||||||
alert( name );
|
alert(name);
|
||||||
```
|
```
|
||||||
|
|
||||||
The full page:
|
The full page:
|
||||||
|
@ -10,18 +10,15 @@ The full page:
|
||||||
```html
|
```html
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
let name = prompt("What is your name?", "");
|
let name = prompt("What is your name?", "");
|
||||||
alert( name );
|
alert(name);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@ importance: 4
|
||||||
|
|
||||||
# A simple page
|
# A simple page
|
||||||
|
|
||||||
Create a web-page which asks for a name and outputs it.
|
Create a web-page that asks for a name and outputs it.
|
||||||
|
|
||||||
[demo]
|
[demo]
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ For example:
|
||||||
alert("Hello");
|
alert("Hello");
|
||||||
```
|
```
|
||||||
|
|
||||||
The small window with the message is called a *modal window*. The word "modal" means that the visitor can't interact with the rest of the page, press other buttons etc, until he deals with the window. In this case -- until he presses "OK".
|
The mini-window with the message is called a *modal window*. The word "modal" means that the visitor can't interact with the rest of the page, press other buttons etc, until he deals with the window. In this case -- until he presses "OK".
|
||||||
|
|
||||||
## prompt
|
## prompt
|
||||||
|
|
||||||
|
@ -34,19 +34,19 @@ Function `prompt` accepts two arguments:
|
||||||
result = prompt(title[, default]);
|
result = prompt(title[, default]);
|
||||||
```
|
```
|
||||||
|
|
||||||
It shows a modal window with a field for text and buttons OK/CANCEL.
|
It shows a modal window with a text message, an input field for the visitor and buttons OK/CANCEL.
|
||||||
|
|
||||||
`title`
|
`title`
|
||||||
: Is a modal window title
|
: The text to show to the visitor.
|
||||||
|
|
||||||
`default`
|
`default`
|
||||||
: An optional second parameter, the initial value for the text field.
|
: An optional second parameter, the initial value for the input field.
|
||||||
|
|
||||||
The visitor may type something in the field and press OK. Or he can cancel the input by pressing a CANCEL button or hitting the `key:Esc` key.
|
The visitor may type something in the prompt input field and press OK. Or he can cancel the input by pressing a CANCEL button or hitting the `key:Esc` key.
|
||||||
|
|
||||||
The call to `prompt` returns the text from the field or `null` if the input is canceled.
|
The call to `prompt` returns the text from the field or `null` if the input was canceled.
|
||||||
|
|
||||||
As with `alert`, the `prompt` window is modal.
|
For instance:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
let age = prompt('How old are you?', 100);
|
let age = prompt('How old are you?', 100);
|
||||||
|
@ -92,6 +92,8 @@ alert( isBoss ); // true is OK is pressed
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|
||||||
|
We covered 3 browser-specific functions to interact with the visitor:
|
||||||
|
|
||||||
`alert`
|
`alert`
|
||||||
: shows a message.
|
: shows a message.
|
||||||
|
|
||||||
|
@ -101,6 +103,8 @@ alert( isBoss ); // true is OK is pressed
|
||||||
`confirm`
|
`confirm`
|
||||||
: shows a message and waits the user to press "OK" or "CANCEL". It returns `true` for OK and `false` for CANCEL/`key:Esc`.
|
: shows a message and waits 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 the script execution and don't let the visitor to interact with the rest of the page until he dismisses them.
|
||||||
|
|
||||||
There are two limitations shared by all the methods above:
|
There are two limitations shared by all the methods above:
|
||||||
|
|
||||||
1. The exact location of the modal window is determined by the browser. Usually it's in the center.
|
1. The exact location of the modal window is determined by the browser. Usually it's in the center.
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
@ -10,7 +10,7 @@
|
||||||
if (value == 'ECMAScript') {
|
if (value == 'ECMAScript') {
|
||||||
alert('Right!');
|
alert('Right!');
|
||||||
} else {
|
} else {
|
||||||
alert("Didn't know? ECMAScript!");
|
alert("You don't know? ECMAScript!");
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 56 KiB |
|
@ -1,7 +1,7 @@
|
||||||
|
|
||||||
|
|
||||||
```js run demo
|
```js run demo
|
||||||
let userName = prompt('Who's there?', '');
|
let userName = prompt("Who's there?", '');
|
||||||
|
|
||||||
if (userName == 'Admin') {
|
if (userName == 'Admin') {
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ importance: 5
|
||||||
|
|
||||||
Rewrite `if..else` using multiple ternary operators `'?'`.
|
Rewrite `if..else` using multiple ternary operators `'?'`.
|
||||||
|
|
||||||
For readability, it's recommended to split the code span over lines.
|
For readability, it's recommended to split the code into multiple lines.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
let message;
|
let message;
|
||||||
|
@ -21,4 +21,3 @@ if (login == 'Employee') {
|
||||||
message = '';
|
message = '';
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Conditional operators: if, '?'
|
# Conditional operators: if, '?'
|
||||||
|
|
||||||
Sometimes we need to perform different actions basing on a condition.
|
Sometimes we need to perform different actions basing on a condition.
|
||||||
|
|
||||||
There's an `if` operator for that and also the "question mark" operator: `"?"` for conditional evaluation.
|
There's an `if` operator for that and also the "question mark" operator: `"?"` for conditional evaluation.
|
||||||
|
|
||||||
|
@ -35,9 +35,9 @@ It is recommended to use figure brackets every time with `if`, even if there's o
|
||||||
|
|
||||||
## Boolean conversion
|
## Boolean conversion
|
||||||
|
|
||||||
The `if (…)` operator evaluates the condition in brackets and converts it to boolean type.
|
The `if (…)` operator evaluates the expression in parentheses and converts it to the boolean type.
|
||||||
|
|
||||||
Let's recall the rules. In the logical context:
|
Let's recall the conversion rules:
|
||||||
|
|
||||||
- A number `0`, an empty string `""`, `null`, `undefined` and `NaN` are `false`,
|
- A number `0`, an empty string `""`, `null`, `undefined` and `NaN` are `false`,
|
||||||
- Other values -- `true`.
|
- Other values -- `true`.
|
||||||
|
@ -58,7 +58,7 @@ if (1) { // 1 is truthy
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
We can also pass a pre-evaluated logical value to `if`, like here:
|
We can also pass a pre-evaluated boolean value to `if`, like here:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
let cond = (year == 2015); // equality evaluates to true or false
|
let cond = (year == 2015); // equality evaluates to true or false
|
||||||
|
@ -101,7 +101,9 @@ if (year < 2015) {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
In the code above JavaScript first checks `year < 2015`, if it is falsy then goes to the next condition `year > 2015`. Any number of `else if` may follow with an optional last `else`.
|
In the code above JavaScript first checks `year < 2015`, if it is falsy then goes to the next condition `year > 2015`, and otherwise shows the last `alert`.
|
||||||
|
|
||||||
|
There can be more `else if` blocks. The ending `else` is optional.
|
||||||
|
|
||||||
## Ternary operator '?'
|
## Ternary operator '?'
|
||||||
|
|
||||||
|
@ -110,21 +112,21 @@ Sometimes we need to assign a variable depending on a condition.
|
||||||
For instance:
|
For instance:
|
||||||
|
|
||||||
```js run no-beautify
|
```js run no-beautify
|
||||||
let hasAccess;
|
let accessAllowed;
|
||||||
let age = prompt('How old are you?', '');
|
let age = prompt('How old are you?', '');
|
||||||
|
|
||||||
*!*
|
*!*
|
||||||
if (age > 18) {
|
if (age > 18) {
|
||||||
hasAccess = true;
|
accessAllowed = true;
|
||||||
} else {
|
} else {
|
||||||
hasAccess = false;
|
accessAllowed = false;
|
||||||
}
|
}
|
||||||
*/!*
|
*/!*
|
||||||
|
|
||||||
alert(hasAccess);
|
alert(accessAllowed);
|
||||||
```
|
```
|
||||||
|
|
||||||
The so called "ternary" or "question mark" operator allows to do that shorter and simpler.
|
The so-called "ternary" or "question mark" operator allows to do that shorter and simpler.
|
||||||
|
|
||||||
The operator is represented by a question mark `"?"`. The formal term "ternary" means that the operator has 3 operands. It is actually the one and only operator in JavaScript which has that many.
|
The operator is represented by a question mark `"?"`. The formal term "ternary" means that the operator has 3 operands. It is actually the one and only operator in JavaScript which has that many.
|
||||||
|
|
||||||
|
@ -138,24 +140,25 @@ The `condition` is evaluated, if it's truthy then `value1` is returned, otherwis
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
let hasAccess = (age > 18) ? true : false;
|
let accessAllowed = (age > 18) ? true : false;
|
||||||
```
|
```
|
||||||
|
|
||||||
We can omit brackets around `age > 14`, because the question mark operator has a low precedence. It executes after comparisons, so:
|
Technically, we can omit parentheses around `age > 14`. The question mark operator has a low precedence. It executes after the comparison `>`, so that'll do the same:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
// the same
|
// the comparison operator "age > 18" executes first anyway
|
||||||
let hasAccess = age > 18 ? true : false;
|
// (no need to wrap it into parentheses)
|
||||||
|
let accessAllowed = age > 18 ? true : false;
|
||||||
```
|
```
|
||||||
|
|
||||||
...But brackets make the code more readable. So it's recommended to put them.
|
...But parentheses make the code more readable. So it's recommended to put them.
|
||||||
|
|
||||||
````smart
|
````smart
|
||||||
In the described case it is possible to evade the question mark operator, because the comparison by itself returns `true/false`:
|
In the example above it's possible to evade the question mark operator, because the comparison by itself returns `true/false`:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
// the same
|
// the same
|
||||||
let hasAccess = age > 18;
|
let accessAllowed = age > 18;
|
||||||
```
|
```
|
||||||
````
|
````
|
||||||
|
|
||||||
|
@ -175,12 +178,12 @@ let message = (age < 3) ? 'Hi, baby!' :
|
||||||
alert( message );
|
alert( message );
|
||||||
```
|
```
|
||||||
|
|
||||||
It may be difficult at first to grasp what's going on. But looking more carefully we note that it's just an ordinary sequence of tests.
|
It may be difficult at first to grasp what's going on. But after a closer look we can see that it's just an ordinary sequence of tests.
|
||||||
|
|
||||||
1. The first question mark checks for `age < 3`.
|
1. The first question mark checks whether `age < 3`.
|
||||||
2. If true -- returns `'Hi, baby!'`, otherwise -- goes to the right side of the colon `":"` and checks for `age < 18`.
|
2. If true -- returns `'Hi, baby!'`, otherwise -- goes after the colon `":"` and checks for `age < 18`.
|
||||||
3. If that's true -- returns `'Hello!'`, otherwise checks for `age < 100` and returns `'Greetings!'` if that is so...
|
3. If that's true -- returns `'Hello!'`, otherwise -- goes after the next colon `":"` and checks for `age < 100`.
|
||||||
4. At last, if all checks are falsy, the `message` becomes `'What an unusual age!'`.
|
4. If that's true -- returns `'Greetings!'`, otherwise -- goes after the last colon `":"` and returns `'What an unusual age!'`.
|
||||||
|
|
||||||
The same logic using `if..else`:
|
The same logic using `if..else`:
|
||||||
|
|
||||||
|
@ -211,7 +214,7 @@ let company = prompt('Which company created JavaScript?', '');
|
||||||
|
|
||||||
Depending on the condition `company == 'Netscape'`, either the first or the second part after `"?"` gets executed and shows the alert.
|
Depending on the condition `company == 'Netscape'`, either the first or the second part after `"?"` gets executed and shows the alert.
|
||||||
|
|
||||||
We don't assign a result to a variable here, cause the `alert` doesn't return anything anyway, our purpose is only to execute it.
|
We don't assign a result to a variable here, the idea is to execute different code depending on the condition.
|
||||||
|
|
||||||
**It is not recommended to use the question mark operator in this way.**
|
**It is not recommended to use the question mark operator in this way.**
|
||||||
|
|
||||||
|
@ -234,4 +237,3 @@ if (company == 'Netscape') {
|
||||||
Our eyes scan the code vertically. The constructs which span several lines are easier to understand than a long horizontal instruction set.
|
Our eyes scan the code vertically. The constructs which span several lines are easier to understand than a long horizontal instruction set.
|
||||||
|
|
||||||
The idea of a question mark `'?'` is to return one or another value depending on the condition. Please use it for exactly that. There's `if` to execute different branches of the code.
|
The idea of a question mark `'?'` is to return one or another value depending on the condition. Please use it for exactly that. There's `if` to execute different branches of the code.
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ alert( alert(1) || 2 || alert(3) );
|
||||||
The call to `alert` does not return a value. Or, in other words, it returns `undefined`.
|
The call to `alert` does not return a value. Or, in other words, it returns `undefined`.
|
||||||
|
|
||||||
1. The first OR `||` evaluates it's left operand `alert(1)`. That shows the first message with `1`.
|
1. The first OR `||` evaluates it's left operand `alert(1)`. That shows the first message with `1`.
|
||||||
2. The `alert` returns `undefined`, so OR goes on to the second operand in it's search of a truthy value.
|
2. The `alert` returns `undefined`, so OR goes on to the second operand searching for a truthy value.
|
||||||
3. The second operand `2` is truthy, so the execution is halted, `2` is returned and then shown by the outer alert.
|
3. The second operand `2` is truthy, so the execution is halted, `2` is returned and then shown by the outer alert.
|
||||||
|
|
||||||
There will be no `3`, because the evaluation does not reach `alert(3)`.
|
There will be no `3`, because the evaluation does not reach `alert(3)`.
|
||||||
|
|
|
@ -18,9 +18,9 @@ result = a || b;
|
||||||
|
|
||||||
In classical programming, logical OR is meant to manipulate boolean values. If any of it's arguments is `true`, then it returns `true`, otherwise -- returns `false`.
|
In classical programming, logical OR is meant to manipulate boolean values. If any of it's arguments is `true`, then it returns `true`, otherwise -- returns `false`.
|
||||||
|
|
||||||
In JavaScript the operator is a little bit more tricky and powerful. But first let's see what happens with logical values.
|
In JavaScript the operator is a little bit more tricky and powerful. But first let's see what happens with boolean values.
|
||||||
|
|
||||||
A table of possible logical combinations:
|
There are four possible logical combinations:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
alert( true || true ); // true
|
alert( true || true ); // true
|
||||||
|
@ -41,7 +41,7 @@ if (1 || 0) { // works just like if( true || false )
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Most of time, OR `||` is used in the `if` expression to test if *any* of given conditions is correct.
|
Most of time, OR `||` is used in `if` to test if *any* of given conditions is correct.
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
|
@ -81,31 +81,31 @@ result = value1 || value2 || value3;
|
||||||
The OR `"||"` operator is doing the following:
|
The OR `"||"` operator is doing the following:
|
||||||
|
|
||||||
- Evalutes operands from left to right.
|
- Evalutes operands from left to right.
|
||||||
- For each value converts it to boolean and stops immediately returning it if it's true.
|
- For each value -- converts it to boolean. If it's true then stops and returns that value.
|
||||||
- The value is returned in it's original form, without the conversion.
|
- If operands finished, returns the last value.
|
||||||
|
|
||||||
In other words, it returns the first truthy value or the last one if no such value found.
|
A value is returned in it's original form, without the conversion.
|
||||||
|
|
||||||
|
In other words, a chain of OR `"||"` returns the first truthy value or the last one if no such value found.
|
||||||
|
|
||||||
For instance:
|
For instance:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
alert( 1 || 0 ); // 1 (is truthy)
|
alert( 1 || 0 ); // 1 (1 is truthy)
|
||||||
alert( true || 'no matter what' ); // (true is truthy)
|
alert( true || 'no matter what' ); // (true is truthy)
|
||||||
|
|
||||||
alert( null || 1 ); // 1 (1 is the first truthy)
|
alert( null || 1 ); // 1 (1 is the first truthy value)
|
||||||
alert( null || 0 || 1 ); // 1 (the first truthy)
|
alert( null || 0 || 1 ); // 1 (the first truthy value)
|
||||||
alert( undefined || null || 0 ); // 0 (all falsy, returns the last value)
|
alert( undefined || null || 0 ); // 0 (all falsy, returns the last value)
|
||||||
```
|
```
|
||||||
|
|
||||||
This logic does not contradict to what was spoken above. If you check this behavior with the boolean table, you see that it still works the same.
|
That leads to some interesting usages compared to a "pure, classical, boolean-only OR".
|
||||||
|
|
||||||
But there leads to some interesting usages compared to a "pure, classical, boolean-only OR".
|
|
||||||
|
|
||||||
1. **Getting the first truthy value from the list of variables or expressions.**
|
1. **Getting the first truthy value from the list of variables or expressions.**
|
||||||
|
|
||||||
Imagine we have several variables, which can either contain the data or be `null/undefined`. And we need to choose the first one with data.
|
Imagine we have several variables, which can either contain the data or be `null/undefined`. And we need to choose the first one with data.
|
||||||
|
|
||||||
Using OR `||` for that:
|
We can use OR `||` for that:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
let currentUser = null;
|
let currentUser = null;
|
||||||
|
@ -135,7 +135,7 @@ But there leads to some interesting usages compared to a "pure, classical, boole
|
||||||
alert(x); // undefined, because (x = 1) not evaluated
|
alert(x); // undefined, because (x = 1) not evaluated
|
||||||
```
|
```
|
||||||
|
|
||||||
...And if the first argument were `false`, then `OR` would goes on and evaluate the second one thus running the assignment:
|
...And if the first argument is `false`, then `OR` goes on and evaluates the second one thus running the assignment:
|
||||||
|
|
||||||
```js run no-beautify
|
```js run no-beautify
|
||||||
let x;
|
let x;
|
||||||
|
@ -147,9 +147,9 @@ But there leads to some interesting usages compared to a "pure, classical, boole
|
||||||
|
|
||||||
An assignment is a simple case, other side effects can be involved.
|
An assignment is a simple case, other side effects can be involved.
|
||||||
|
|
||||||
As we can see, such use case is a "shorter way to do `if`". The first operand is converted to boolean and if it's false then the second one is evaluated.
|
As we can see, such use case is a "shorter way to do `if`". The first operand is converted to boolean and if it's false then the second one is evaluated.
|
||||||
|
|
||||||
Most of time it's better to use `if` for that for code clarity.
|
Most of time it's better to use a "regular" `if` to keep the code easy to understand, but sometimes that can be handy.
|
||||||
|
|
||||||
## && (AND)
|
## && (AND)
|
||||||
|
|
||||||
|
@ -159,7 +159,7 @@ The AND operator is represented with two ampersands `&&`:
|
||||||
result = a && b;
|
result = a && b;
|
||||||
```
|
```
|
||||||
|
|
||||||
In classic programming AND returns `true` if both operands are truthy and `false` -- otherwise:
|
In classical programming AND returns `true` if both operands are truthy and `false` -- otherwise:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
alert( true && true ); // true
|
alert( true && true ); // true
|
||||||
|
@ -179,7 +179,7 @@ if (hour == 12 && minute == 30) {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Just as for OR, any value is allowed as an operand of AND and gets converted to a boolean in the process:
|
Just as for OR, any value is allowed as an operand of AND:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
if (1 && 0) { // evaluated as true && false
|
if (1 && 0) { // evaluated as true && false
|
||||||
|
@ -199,10 +199,10 @@ result = value1 && value2 && value3;
|
||||||
The AND `"&&"` operator is doing the following:
|
The AND `"&&"` operator is doing the following:
|
||||||
|
|
||||||
- Evalutes operands from left to right.
|
- Evalutes operands from left to right.
|
||||||
- For each value converts it to a boolean. If the result is `false`, stops and returns it without conversion.
|
- For each value converts it to a boolean. If the result is `false`, stops and returns the original value.
|
||||||
- If values finished (all are truthy), returns the last one.
|
- If values finished (all are truthy), returns the last one.
|
||||||
|
|
||||||
In other words, AND returns the first falsy value or the last one if none found.
|
In other words, AND returns the first falsy value or the last value if none found.
|
||||||
|
|
||||||
The rules above are similar to OR. The difference is that AND returns the first *falsy* value while OR returns the first *truthy* one.
|
The rules above are similar to OR. The difference is that AND returns the first *falsy* value while OR returns the first *truthy* one.
|
||||||
|
|
||||||
|
@ -210,12 +210,12 @@ Examples:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
// if the first operand is truthy,
|
// if the first operand is truthy,
|
||||||
// && returns the second one.
|
// AND returns the second one:
|
||||||
alert( 1 && 0 ); // 0
|
alert( 1 && 0 ); // 0
|
||||||
alert( 1 && 5 ); // 5
|
alert( 1 && 5 ); // 5
|
||||||
|
|
||||||
// now the first operand is falsy,
|
// if the first operand is falsy,
|
||||||
// it is returned, and the second one is ignored
|
// AND returns it, and the second one is ignored
|
||||||
alert( null && 5 ); // null
|
alert( null && 5 ); // null
|
||||||
alert( 0 && "no matter what" ); // 0
|
alert( 0 && "no matter what" ); // 0
|
||||||
```
|
```
|
||||||
|
@ -226,7 +226,7 @@ We can also pass several values in a row. See how the first falsy one is returne
|
||||||
alert( 1 && 2 && null && 3 ); // null
|
alert( 1 && 2 && null && 3 ); // null
|
||||||
```
|
```
|
||||||
|
|
||||||
...And now when all of them are truthy:
|
When all values are truthy, the last value is returned:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
alert( 1 && 2 && 3 ); // 3, the last one
|
alert( 1 && 2 && 3 ); // 3, the last one
|
||||||
|
@ -272,7 +272,7 @@ So it is recommended to use every construct for it's purpose. Use `if` if we wan
|
||||||
|
|
||||||
The boolean NOT operator is represented with an exclamation sign `"!"`.
|
The boolean NOT operator is represented with an exclamation sign `"!"`.
|
||||||
|
|
||||||
The syntax is one of the simplest:
|
The syntax is pretty simple:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
result = !value;
|
result = !value;
|
||||||
|
@ -290,14 +290,14 @@ alert( !true ); // false
|
||||||
alert( !0 ); // true
|
alert( !0 ); // true
|
||||||
```
|
```
|
||||||
|
|
||||||
A double NOT is sometimes used for converting a value to boolean type:
|
A double NOT `!!` is sometimes used for converting a value to boolean type:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
alert( !!"non-empty string" ); // true
|
alert( !!"non-empty string" ); // true
|
||||||
alert( !!null ); // false
|
alert( !!null ); // false
|
||||||
```
|
```
|
||||||
|
|
||||||
That is: the first NOT converts the value to boolean and returns the inverse, and the second NOT inverses it again, so we have a plain value-to-boolean conversion.
|
That is: the first NOT converts the value to boolean and returns the inverse, and the second NOT inverses it again, at the end we have a plain value-to-boolean conversion.
|
||||||
|
|
||||||
There's a little more verbose to do the same -- a built-in `Boolean` function:
|
There's a little more verbose to do the same -- a built-in `Boolean` function:
|
||||||
|
|
||||||
|
@ -305,4 +305,3 @@ There's a little more verbose to do the same -- a built-in `Boolean` function:
|
||||||
alert( Boolean("non-empty string") ); // true
|
alert( Boolean("non-empty string") ); // true
|
||||||
alert( Boolean(null) ); // false
|
alert( Boolean(null) ); // false
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ while (i) {
|
||||||
|
|
||||||
Every loop iteration decreases `i` by `1`. The check `while(i)` stops the loop when `i = 0`.
|
Every loop iteration decreases `i` by `1`. The check `while(i)` stops the loop when `i = 0`.
|
||||||
|
|
||||||
Hence, the steps of the loop make the following sequence ("loop unrolled"):
|
Hence, the steps of the loop form the following sequence ("loop unrolled"):
|
||||||
|
|
||||||
```js
|
```js
|
||||||
let i = 3;
|
let i = 3;
|
||||||
|
@ -23,4 +23,3 @@ alert(i--) // shows 1, decreases i to 0
|
||||||
|
|
||||||
// done, while(i) check stops the loop
|
// done, while(i) check stops the loop
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ importance: 3
|
||||||
|
|
||||||
# Last loop value
|
# Last loop value
|
||||||
|
|
||||||
What is be the last value alerted by this code? Why?
|
What is the last value alerted by this code? Why?
|
||||||
|
|
||||||
```js
|
```js
|
||||||
let i = 3;
|
let i = 3;
|
||||||
|
@ -13,4 +13,3 @@ while (i) {
|
||||||
alert( i-- );
|
alert( i-- );
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ The task demonstrates how postfix/prefix forms can lead to different results whe
|
||||||
|
|
||||||
Then follow `2,3,4…` -- the values show up one after another. The comparison always uses the incremented value, because `++` is before the variable.
|
Then follow `2,3,4…` -- the values show up one after another. The comparison always uses the incremented value, because `++` is before the variable.
|
||||||
|
|
||||||
Finally, `i=4` is incremented to `5`, the comparison `while(5 < 5)` fails and the loop stops. So `5` is not shown.
|
Finally, `i=4` is incremented to `5`, the comparison `while(5 < 5)` fails, and the loop stops. So `5` is not shown.
|
||||||
2. **From 1 to 5**
|
2. **From 1 to 5**
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
|
@ -28,4 +28,3 @@ The task demonstrates how postfix/prefix forms can lead to different results whe
|
||||||
Let's stop on `i=4`. The prefix form `++i` would increment it and use `5` in the comparison. But here we have the postfix form `i++`. So it increments `i` to `5`, but returns the old value. Hence the comparison is actually `while(4 < 5)` -- true, and the control goes on to `alert`.
|
Let's stop on `i=4`. The prefix form `++i` would increment it and use `5` in the comparison. But here we have the postfix form `i++`. So it increments `i` to `5`, but returns the old value. Hence the comparison is actually `while(4 < 5)` -- true, and the control goes on to `alert`.
|
||||||
|
|
||||||
The value `i=5` is the last one, because on the next step `while(5 < 5)` is false.
|
The value `i=5` is the last one, because on the next step `while(5 < 5)` is false.
|
||||||
|
|
||||||
|
|
|
@ -4,9 +4,9 @@ importance: 4
|
||||||
|
|
||||||
# Which values shows the while?
|
# Which values shows the while?
|
||||||
|
|
||||||
For every loop, scribe down which values it shows, in your opinion.
|
For every loop, write down which values it shows, in your opinion. And then compare with the answer.
|
||||||
|
|
||||||
And then compare with the answer.
|
Both loops `alert` same values or not?
|
||||||
|
|
||||||
1. The prefix form `++i`:
|
1. The prefix form `++i`:
|
||||||
|
|
||||||
|
@ -20,4 +20,3 @@ And then compare with the answer.
|
||||||
let i = 0;
|
let i = 0;
|
||||||
while (i++ < 5) alert( i );
|
while (i++ < 5) alert( i );
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ for (let i = 0; i < 5; i++) alert( i );
|
||||||
|
|
||||||
That can be easily deducted from the algorithm of `for`:
|
That can be easily deducted from the algorithm of `for`:
|
||||||
|
|
||||||
1. Execute once `i=0` before everything.
|
1. Execute once `i=0` before everything (begin).
|
||||||
2. Check the condition `i<5`
|
2. Check the condition `i<5`
|
||||||
3. If `true` -- execute the loop body `alert(i)`, and then `i++`
|
3. If `true` -- execute the loop body `alert(i)`, and then `i++`
|
||||||
|
|
||||||
|
|
|
@ -4,9 +4,9 @@ importance: 4
|
||||||
|
|
||||||
# Which values get shown by the "for" loop?
|
# Which values get shown by the "for" loop?
|
||||||
|
|
||||||
For each loop scribe down which values it is going to show.
|
For each loop write down which values it is going to show. Then compare with the answer.
|
||||||
|
|
||||||
Then compare with the answer.
|
Both loops `alert` same values or not?
|
||||||
|
|
||||||
1. The postfix form:
|
1. The postfix form:
|
||||||
|
|
||||||
|
@ -18,4 +18,3 @@ Then compare with the answer.
|
||||||
```js
|
```js
|
||||||
for (let i = 0; i < 5; ++i) alert( i );
|
for (let i = 0; i < 5; ++i) alert( i );
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,6 @@ do {
|
||||||
The loop `do..while` repeats while both checks are truthy:
|
The loop `do..while` repeats while both checks are truthy:
|
||||||
|
|
||||||
1. The check for `num <= 100` -- that is, the entered value is still not greater than `100`.
|
1. The check for `num <= 100` -- that is, the entered value is still not greater than `100`.
|
||||||
2. The check for a truthiness of `num` checks that `num != null` and `num != ""` simultaneously.
|
2. The check `&& num` is false when `num` is `null` or a empty strig. Then the `while` loop stops too.
|
||||||
|
|
||||||
P.S. By the way, if `num` is `null` then `num <= 100` would return `false`, not `true`!
|
P.S. If `num` is `null` then `num <= 100` is `false`, so without the 2nd check the loop wouldn't stop if the user clicks CANCEL. Both checks are required.
|
||||||
|
|
|
@ -4,11 +4,10 @@ importance: 5
|
||||||
|
|
||||||
# Repeat until the input is incorrect
|
# Repeat until the input is incorrect
|
||||||
|
|
||||||
Write a loop which prompts for a number greater than `100`. If the visitor enters another number -- ask him to repeat the input, and so on.
|
Write a loop which prompts for a number greater than `100`. If the visitor enters another number -- ask him to input again.
|
||||||
|
|
||||||
The loop must ask for a number until either the visitor enters a number greater than `100` or cancels the input/enters an empty line.
|
The loop must ask for a number until either the visitor enters a number greater than `100` or cancels the input/enters an empty line.
|
||||||
|
|
||||||
Here we can assume that the visitor only inputs numbers. There's no need to implement the special handling for a non-numeric input in this task.
|
Here we can assume that the visitor only inputs numbers. There's no need to implement a special handling for a non-numeric input in this task.
|
||||||
|
|
||||||
[demo]
|
[demo]
|
||||||
|
|
||||||
|
|
|
@ -13,8 +13,10 @@ For each i in the interval {
|
||||||
The code using a label:
|
The code using a label:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
|
let n = 10;
|
||||||
|
|
||||||
nextPrime:
|
nextPrime:
|
||||||
for (let i = 2; i <= 10; i++) { // for each i...
|
for (let i = 2; i <= n; i++) { // for each i...
|
||||||
|
|
||||||
for (let j = 2; j < i; j++) { // look for a divisor..
|
for (let j = 2; j < i; j++) { // look for a divisor..
|
||||||
if (i % j == 0) continue nextPrime; // not a prime, go next i
|
if (i % j == 0) continue nextPrime; // not a prime, go next i
|
||||||
|
@ -24,5 +26,4 @@ for (let i = 2; i <= 10; i++) { // for each i...
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Surely, there's a lot of space to opimize it. Like, we could look for the divisors from `2` to square root of `i`. But anyway, if we want to be really efficient for large intervals, we need change the approach and rely heavily on advanced maths and algorithms like [Quadratic sieve](https://en.wikipedia.org/wiki/Quadratic_sieve), [General number field sieve](https://en.wikipedia.org/wiki/General_number_field_sieve) etc.
|
There's a lot of space to opimize it. For instance, we could look for the divisors from `2` to square root of `i`. But anyway, if we want to be really efficient for large intervals, we need change the approach and rely on advanced maths and complex algorithms like [Quadratic sieve](https://en.wikipedia.org/wiki/Quadratic_sieve), [General number field sieve](https://en.wikipedia.org/wiki/General_number_field_sieve) etc.
|
||||||
|
|
||||||
|
|
|
@ -4,15 +4,14 @@ importance: 3
|
||||||
|
|
||||||
# Output prime numbers
|
# Output prime numbers
|
||||||
|
|
||||||
An integer number greater than `1` is called a [prime](https://en.wikipedia.org/wiki/Prime_number) if it cannot be not divided without a remainder by anything except `1` and itself.
|
An integer number greater than `1` is called a [prime](https://en.wikipedia.org/wiki/Prime_number) if it cannot be divided without a remainder by anything except `1` and itself.
|
||||||
|
|
||||||
In other words, `n>1` is a prime if the result of it's division by anything except `1` and `n` is not integer.
|
In other words, `n>1` is a prime if it can't be evenly divided by anything except `1` and `n`.
|
||||||
|
|
||||||
For example, `5` is a prime, because it cannot be divided without a remainder by `2`, `3` and `4`.
|
For example, `5` is a prime, because it cannot be divided without a remainder by `2`, `3` and `4`.
|
||||||
|
|
||||||
**Write the code which outputs prime numbers in the interval from `2` to `10`.**
|
**Write the code which outputs prime numbers in the interval from `2` to `n`.**
|
||||||
|
|
||||||
The result will be `2,3,5,7`.
|
For `n=10` the result will be `2,3,5,7`.
|
||||||
|
|
||||||
P.S. The code should be easily modifiable for other intervals.
|
|
||||||
|
|
||||||
|
P.S. The code should work for any `n`, not be hard-tuned for any fixed value.
|
||||||
|
|
|
@ -14,7 +14,8 @@ The `while` loop has the following syntax:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
while (condition) {
|
while (condition) {
|
||||||
// code ("loop body")
|
// code
|
||||||
|
// so-called "loop body"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -30,18 +31,18 @@ while (i < 3) { // shows 0, then 1, then 2
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
There's a special term *iteration* for each loop run. The loop in the example above makes 3 iterations.
|
A single execution of the loop body is called *an iteration*. The loop in the example above makes 3 iterations.
|
||||||
|
|
||||||
If there were no `i++` in the example above, the loop would repeat (in theory) forever, eating 100% CPU. In practice, the browser would show a message about a "hanging" script and let the user stop it.
|
If there were no `i++` in the example above, the loop would repeat (in theory) forever. In practice, the browser provides ways to stop such loops, and for server-side JavaScript we can kill the process.
|
||||||
|
|
||||||
The `while` converts `condition` to a logical value. It can be any expression, not just a comparison.
|
Any expression or a variable can be a loop condition, not just a comparison. They are evaluated and converted to boolean by `while`.
|
||||||
|
|
||||||
For instance, the shorter way to write `while (i!=0)` could be `while (i)`:
|
For instance, the shorter way to write `while (i!=0)` could be `while (i)`:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
let i = 3;
|
let i = 3;
|
||||||
*!*
|
*!*
|
||||||
while (i) { // when i becomes 0, the condition is falsy and the loop stops
|
while (i) { // when i becomes 0, the condition becomes falsy, and the loop stops
|
||||||
*/!*
|
*/!*
|
||||||
alert( i );
|
alert( i );
|
||||||
i--;
|
i--;
|
||||||
|
@ -69,7 +70,7 @@ do {
|
||||||
} while (condition);
|
} while (condition);
|
||||||
```
|
```
|
||||||
|
|
||||||
The loop will first execute the body and then check the condition.
|
The loop will first execute the body, then check the condition, and while it's truthy -- execute it again and again.
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
|
@ -81,11 +82,11 @@ do {
|
||||||
} while (i < 3);
|
} while (i < 3);
|
||||||
```
|
```
|
||||||
|
|
||||||
This form of syntax is rarely used, because the ordinary `while` is more obvious. We don't need to scroll down the code looking for the condition.
|
This form of syntax is rarely used. Usually, if there's no special reason, the other form is preferred: `while(…) {…}`.
|
||||||
|
|
||||||
## The "for" loop
|
## The "for" loop
|
||||||
|
|
||||||
The `for` loop is actually the most often used one.
|
The `for` loop is the most often used one.
|
||||||
|
|
||||||
It looks like this:
|
It looks like this:
|
||||||
|
|
||||||
|
@ -95,7 +96,7 @@ for (begin; condition; step) {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Let's see these parts in an example. For instance, the loop below runs `alert(i)` for `i` from `0` up to (but not including) `3`:
|
Let's learn the meaning of these parts by example. The loop below runs `alert(i)` for `i` from `0` up to (but not including) `3`:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
for (let i = 0; i < 3; i++) { // shows 0, then 1, then 2
|
for (let i = 0; i < 3; i++) { // shows 0, then 1, then 2
|
||||||
|
@ -103,42 +104,45 @@ for (let i = 0; i < 3; i++) { // shows 0, then 1, then 2
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Let's examine the last example part-by-part:
|
Let's examine the `for` statement part by part:
|
||||||
|
|
||||||
| part | | |
|
| part | | |
|
||||||
|-------|----------|----------------------------------------------------------------------------|
|
|-------|----------|----------------------------------------------------------------------------|
|
||||||
| begin | `i=0` | Executes once upon entering the loop. |
|
| begin | `i=0` | Executes once upon entering the loop. |
|
||||||
| condition | `i<3`| Checked before every loop iteration, if fails the loop stops. |
|
| condition | `i<3`| Checked before every loop iteration, if fails the loop stops. |
|
||||||
| body | `alert(i)`| Runs again and again while the condition is truthy |
|
|
||||||
| step| `i++` | Executes after the body on each iteration, but before the condition check. |
|
| step| `i++` | Executes after the body on each iteration, but before the condition check. |
|
||||||
|
| body | `alert(i)`| Runs again and again while the condition is truthy |
|
||||||
|
|
||||||
|
|
||||||
The general loop algorithm works like this:
|
The general loop algorithm works like this:
|
||||||
```
|
```
|
||||||
Begin
|
Run begin
|
||||||
→ (if condition → run body and run step)
|
→ (if condition → run body and run step)
|
||||||
→ ... repeat while the condition is truthy
|
→ (if condition → run body and run step)
|
||||||
|
→ (if condition → run body and run step)
|
||||||
|
→ ...
|
||||||
```
|
```
|
||||||
|
|
||||||
If you are new to loops, then maybe it would help if you go back to the example and reproduce how it runs step-by-step on a piece of paper.
|
If you are new to loops, then maybe it would help if you go back to the example and reproduce how it runs step-by-step on a piece of paper.
|
||||||
|
|
||||||
That's what exactly happens in our case:
|
Here's what exactly happens in our case:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
// for (let i = 0; i < 3; i++) alert(i)
|
// for (let i = 0; i < 3; i++) alert(i)
|
||||||
|
|
||||||
// begin
|
// run begin
|
||||||
let i = 0
|
let i = 0
|
||||||
// if condition → run body and run step
|
// if condition → run body and run step
|
||||||
if (i < 3) { alert(i); i++ }
|
if (i < 3) { alert(i); i++ }
|
||||||
// repeat while the condition is truthy
|
// if condition → run body and run step
|
||||||
if (i < 3) { alert(i); i++ }
|
if (i < 3) { alert(i); i++ }
|
||||||
|
// if condition → run body and run step
|
||||||
if (i < 3) { alert(i); i++ }
|
if (i < 3) { alert(i); i++ }
|
||||||
// ...finish, because now i == 3
|
// ...finish, because now i == 3
|
||||||
```
|
```
|
||||||
|
|
||||||
````smart header="Inline variable declaration"
|
````smart header="Inline variable declaration"
|
||||||
Here the "counter" variable `i` is declared right in the the loop. That's called an "inline" variable declaration. Such variable is visible only inside the loop.
|
Here the "counter" variable `i` is declared right in the loop. That's called an "inline" variable declaration. Such variable is visible only inside the loop.
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
for (*!*let*/!* i = 0; i < 3; i++) {
|
for (*!*let*/!* i = 0; i < 3; i++) {
|
||||||
|
@ -147,13 +151,15 @@ for (*!*let*/!* i = 0; i < 3; i++) {
|
||||||
alert(i); // error, no such variable
|
alert(i); // error, no such variable
|
||||||
```
|
```
|
||||||
|
|
||||||
We can use an existing variable as well:
|
Instead of defining a variable, we can use an existing one:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
let i = 0;
|
let i = 0;
|
||||||
|
|
||||||
for (i = 0; i < 3; i++) { // use an existing variable
|
for (i = 0; i < 3; i++) { // use an existing variable
|
||||||
alert(i); // 0, 1, 2
|
alert(i); // 0, 1, 2
|
||||||
}
|
}
|
||||||
|
|
||||||
alert(i); // 3, visible, because declared outside of the loop
|
alert(i); // 3, visible, because declared outside of the loop
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -162,14 +168,14 @@ alert(i); // 3, visible, because declared outside of the loop
|
||||||
|
|
||||||
### Skipping parts
|
### Skipping parts
|
||||||
|
|
||||||
Any part of the `for` can be skipped.
|
Any part of `for` can be skipped.
|
||||||
|
|
||||||
For example, we can omit `begin` if we don't need to do anything at the loop start.
|
For example, we can omit `begin` if we don't need to do anything at the loop start.
|
||||||
|
|
||||||
Like here:
|
Like here:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
let i = 0; // imagine we have i already declared and assigned
|
let i = 0; // we have i already declared and assigned
|
||||||
|
|
||||||
for (; i < 3; i++) { // no need for "begin"
|
for (; i < 3; i++) { // no need for "begin"
|
||||||
alert( i ); // 0, 1, 2
|
alert( i ); // 0, 1, 2
|
||||||
|
@ -223,7 +229,7 @@ while (true) {
|
||||||
alert( 'Sum: ' + sum );
|
alert( 'Sum: ' + sum );
|
||||||
```
|
```
|
||||||
|
|
||||||
The `break` directive is activated in the line `(*)` if the user enters an empty line or cancels the input. It stops the loop immediately, passing the control to the first line after it's loop. Namely, `alert`.
|
The `break` directive is activated in the line `(*)` if the user enters an empty line or cancels the input. It stops the loop immediately, passing the control to the first line after the loop. Namely, `alert`.
|
||||||
|
|
||||||
The combination: "infinite loop + `break` as needed" is great for situations when the condition must be checked not in beginning/end of the loop, but in the middle. Or even in several places of the body.
|
The combination: "infinite loop + `break` as needed" is great for situations when the condition must be checked not in beginning/end of the loop, but in the middle. Or even in several places of the body.
|
||||||
|
|
||||||
|
@ -248,7 +254,7 @@ for (let i = 0; i < 10; i++) {
|
||||||
For even values of `i` the `continue` directive stops body execution, passing the control to the next iteration of `for` (with the next number). So the `alert` is only called for odd values.
|
For even values of `i` the `continue` directive stops body execution, passing the control to the next iteration of `for` (with the next number). So the `alert` is only called for odd values.
|
||||||
|
|
||||||
````smart header="The directive `continue` helps to decrease nesting level"
|
````smart header="The directive `continue` helps to decrease nesting level"
|
||||||
A loop for odd-only values could look like this:
|
A loop that shows odd values could look like this:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
for (let i = 0; i < 10; i++) {
|
for (let i = 0; i < 10; i++) {
|
||||||
|
@ -268,7 +274,7 @@ But as a side-effect we got one more figure brackets nesting level. If the code
|
||||||
````warn header="No `break/continue` to the right side of '?'"
|
````warn header="No `break/continue` to the right side of '?'"
|
||||||
Please note that syntax constructs that are not expressions cannot be used in `'?'`. In particular, directives `break/continue` are disallowed there.
|
Please note that syntax constructs that are not expressions cannot be used in `'?'`. In particular, directives `break/continue` are disallowed there.
|
||||||
|
|
||||||
For example, if one we take this code:
|
For example, if we take this code:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
if (i > 5) {
|
if (i > 5) {
|
||||||
|
@ -295,7 +301,7 @@ That's just another reason not to use a question mark operator `'?'` instead of
|
||||||
|
|
||||||
Sometimes we need to break out from multiple nested loops at once.
|
Sometimes we need to break out from multiple nested loops at once.
|
||||||
|
|
||||||
For example, in the code below we loop over `i` and `j` asking for values on coordinates `(i, j)` from `(0,0)` to `(3,3)`:
|
For example, in the code below we loop over `i` and `j` prompting for coordinates `(i, j)` from `(0,0)` to `(3,3)`:
|
||||||
|
|
||||||
```js run no-beautify
|
```js run no-beautify
|
||||||
for (let i = 0; i < 3; i++) {
|
for (let i = 0; i < 3; i++) {
|
||||||
|
@ -312,7 +318,7 @@ for (let i = 0; i < 3; i++) {
|
||||||
alert('Done!');
|
alert('Done!');
|
||||||
```
|
```
|
||||||
|
|
||||||
Let's say we need a way to stop the process. Like if we user decides to cancel the input.
|
We need a way to stop the process if the user cancels the input.
|
||||||
|
|
||||||
The ordinary `break` after `input` would only break the inner loop. That's not sufficient. Labels come to the rescue.
|
The ordinary `break` after `input` would only break the inner loop. That's not sufficient. Labels come to the rescue.
|
||||||
|
|
||||||
|
@ -323,7 +329,7 @@ labelName: for(...) {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
We can put the `labelName` after a break statement, and it will break out of the labelled loop.
|
The `break <labelName>` statement in the loop breaks out to the label.
|
||||||
|
|
||||||
Like here:
|
Like here:
|
||||||
|
|
||||||
|
@ -354,7 +360,7 @@ outer:
|
||||||
for (let i = 0; i < 3; i++) { ... }
|
for (let i = 0; i < 3; i++) { ... }
|
||||||
```
|
```
|
||||||
|
|
||||||
The `continue` directive can also be used with a label. In this case the execution would jump onto the next iteration of the labelled loop.
|
The `continue` directive can also be used with a label. In this case the execution jumps to the next iteration of the labelled loop.
|
||||||
|
|
||||||
````warn header="Labels are not a \"goto\""
|
````warn header="Labels are not a \"goto\""
|
||||||
Labels do not allow to jump into an arbitrary place of code.
|
Labels do not allow to jump into an arbitrary place of code.
|
||||||
|
@ -379,6 +385,6 @@ We covered 3 types of loops:
|
||||||
|
|
||||||
To make an "infinite" loop, usually the `while(true)` construct is used. Such a loop, just like any other, can be stopped with the `break` directive.
|
To make an "infinite" loop, usually the `while(true)` construct is used. Such a loop, just like any other, can be stopped with the `break` directive.
|
||||||
|
|
||||||
If we don't want to do anything more on this iteration and would like to forward on to the next one -- the `continue` directive does it.
|
If we don't want to do anything on the current iteration and would like to forward to the next one -- the `continue` directive does it.
|
||||||
|
|
||||||
`Break/continue` support labels before the loop. A label is the only way for `break/continue` to escape the nesting and go to the outer loop.
|
`Break/continue` support labels before the loop. A label is the only way for `break/continue` to escape the nesting and go to the outer loop.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
To be precise, the `if` must use a strict comparison `'==='`.
|
To precisely match the functionality of `switch`, the `if` must use a strict comparison `'==='`.
|
||||||
|
|
||||||
In reality though, probably a simple `'=='` would do.
|
For given strings though, a simple `'=='` works too.
|
||||||
|
|
||||||
```js no-beautify
|
```js no-beautify
|
||||||
if(browser == 'Edge') {
|
if(browser == 'Edge') {
|
||||||
|
@ -17,4 +17,4 @@ if(browser == 'Edge') {
|
||||||
|
|
||||||
Please note: the construct `browser == 'Chrome' || browser == 'Firefox' …` is split into multiple lines for better readability.
|
Please note: the construct `browser == 'Chrome' || browser == 'Firefox' …` is split into multiple lines for better readability.
|
||||||
|
|
||||||
But the `switch` is still neater and more descriptive.
|
But the `switch` construct is still cleaner and more descriptive.
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
The first two checks are a usual `case`. The third one is split into two cases:
|
The first two checks turn into two `case`. The third check is split into two cases:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
let a = +prompt('a?', '');
|
let a = +prompt('a?', '');
|
||||||
|
@ -24,4 +24,3 @@ switch (a) {
|
||||||
Please note: the `break` at the bottom is not required. But we put it to make the code future-proof.
|
Please note: the `break` at the bottom is not required. But we put it to make the code future-proof.
|
||||||
|
|
||||||
In the future, there is a chance that we'd want to add one more `case`, for example `case 4`. And if we forget to add a break before it, at the end of `case 3`, there will be an error. So that's a kind of self-insurance.
|
In the future, there is a chance that we'd want to add one more `case`, for example `case 4`. And if we forget to add a break before it, at the end of `case 3`, there will be an error. So that's a kind of self-insurance.
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ It gives a more descriptive way to compare a value with multiple variants.
|
||||||
|
|
||||||
## The syntax
|
## The syntax
|
||||||
|
|
||||||
The `switch` has one or more `case` blocks and an optional default.
|
The `switch` has one or more `case` blocks and an optional default.
|
||||||
|
|
||||||
It looks like this:
|
It looks like this:
|
||||||
|
|
||||||
|
@ -142,7 +142,7 @@ Now both `3` and `5` show the same message.
|
||||||
|
|
||||||
The ability to "group" cases a side-effect of how `switch/case` works without `break`. Here the execution of `case 3` starts from the line `(*)` and goes through `case 5`, because there's no `break`.
|
The ability to "group" cases a side-effect of how `switch/case` works without `break`. Here the execution of `case 3` starts from the line `(*)` and goes through `case 5`, because there's no `break`.
|
||||||
|
|
||||||
## The type matters
|
## Type matters
|
||||||
|
|
||||||
Let's emphase that the equality check is always strict. The values must be of the same type to match.
|
Let's emphase that the equality check is always strict. The values must be of the same type to match.
|
||||||
|
|
||||||
|
@ -170,4 +170,3 @@ switch (arg) {
|
||||||
1. For `0`, `1`, the first `alert` runs.
|
1. For `0`, `1`, the first `alert` runs.
|
||||||
2. For `2` the second `alert` runs.
|
2. For `2` the second `alert` runs.
|
||||||
3. But for `3`, the result of the `prompt` is a string `"3"`, which is not strictly equal `===` to the number `3`. So we've got a dead code in `case 3`! The `default` variant will execite.
|
3. But for `3`, the result of the `prompt` is a string `"3"`, which is not strictly equal `===` to the number `3`. So we've got a dead code in `case 3`! The `default` variant will execite.
|
||||||
|
|
||||||
|
|
|
@ -14,4 +14,4 @@ function checkAge(age) {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that the brackets around `age > 18` are not required here. They exist for better readabilty.
|
Note that the parentheses around `age > 18` are not required here. They exist for better readabilty.
|
||||||
|
|
|
@ -16,4 +16,4 @@ Create a web-page that prompts for `x` and `n`, and then shows the result of `po
|
||||||
|
|
||||||
[demo]
|
[demo]
|
||||||
|
|
||||||
P.S. In this task the function is allowed to support only natural values of `n`: integers up from `1`.
|
P.S. In this task the function should support only natural values of `n`: integers up from `1`.
|
||||||
|
|
|
@ -26,7 +26,7 @@ The `function` keyword goes first, then goes the *name of the function*, then a
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Our new function can be called by it's name.
|
Our new function can be called by its name: `showMessage()`.
|
||||||
|
|
||||||
For instance:
|
For instance:
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ showMessage();
|
||||||
|
|
||||||
The call `showMessage()` executes the code of the function. Here we will see the message two times.
|
The call `showMessage()` executes the code of the function. Here we will see the message two times.
|
||||||
|
|
||||||
This example clearly demonstrates one of the main purposes of the functions: to evade code duplication.
|
This example clearly demonstrates one of the main purposes of functions: to evade code duplication.
|
||||||
|
|
||||||
If we ever need to change the message or the way it is shown -- it's enough to modify the code in one place: the function which outputs it.
|
If we ever need to change the message or the way it is shown -- it's enough to modify the code in one place: the function which outputs it.
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ function showMessage() {
|
||||||
showMessage(); // Hello, my name is John
|
showMessage(); // Hello, my name is John
|
||||||
```
|
```
|
||||||
|
|
||||||
The function has a full access to an outer variable. It can modify it as well.
|
The function has full access to the outer variable. It can modify it as well.
|
||||||
|
|
||||||
For instance:
|
For instance:
|
||||||
|
|
||||||
|
@ -103,11 +103,9 @@ showMessage();
|
||||||
alert( userName ); // *!*Bob*/!*, the value was modified by the function
|
alert( userName ); // *!*Bob*/!*, the value was modified by the function
|
||||||
```
|
```
|
||||||
|
|
||||||
Sometimes that happens when we forget `let`. Because the outer variable is only used if there's no local one.
|
The outer variable is only used if there's no local one. So an occasional modification may happen if we forget `let`.
|
||||||
|
|
||||||
For example, if we had `let` before `userName` in the line (1) then the function would have a local variable `userName` and use it instead. This process is called "shadowing".
|
If a same-named variable is declared inside the function then it *shadows* the outer one. For instance, in the code below the function uses the local `userName`, the outer one is ignored:
|
||||||
|
|
||||||
In the code below the local `userName` *shadows* the outer one:
|
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
let userName = 'John';
|
let userName = 'John';
|
||||||
|
@ -132,12 +130,12 @@ Variables declared outside of any function, such as the outer `userName` in the
|
||||||
|
|
||||||
Global variables are visible from any function (unless shadowed by locals).
|
Global variables are visible from any function (unless shadowed by locals).
|
||||||
|
|
||||||
Usually, a function declares all variables specific to its task, and global variables only store the data so important that it really must be seen from anywhere. Modern code has little to no globals, most variables reside in their functions.
|
Usually, a function declares all variables specific to its task, and global variables only store project-level data, so important that it really must be seen from anywhere. Modern code has few or no globals, most variables reside in their functions.
|
||||||
```
|
```
|
||||||
|
|
||||||
## Parameters
|
## Parameters
|
||||||
|
|
||||||
We can pass arbitrary data to function using it's parameters (also called *function arguments*) .
|
We can pass arbitrary data to function using parameters (also called *function arguments*) .
|
||||||
|
|
||||||
In the example below, the function has two parameters: `from` and `text`.
|
In the example below, the function has two parameters: `from` and `text`.
|
||||||
|
|
||||||
|
@ -147,14 +145,14 @@ function showMessage(*!*from, text*/!*) { // arguments: from, text
|
||||||
}
|
}
|
||||||
|
|
||||||
*!*
|
*!*
|
||||||
showMessage('Ann', 'Hello!'); // Ann: Hello!
|
showMessage('Ann', 'Hello!'); // Ann: Hello! (*)
|
||||||
showMessage('Ann', "What's up?"); // Ann: What's up?
|
showMessage('Ann', "What's up?"); // Ann: What's up? (**)
|
||||||
*/!*
|
*/!*
|
||||||
```
|
```
|
||||||
|
|
||||||
When the function is called, the values in the brackets are copied to local variables `from` and `next`.
|
When the function is called in lines `(*)` and `(**)`, the given values are copied to local variables `from` and `next`. Then the function uses them.
|
||||||
|
|
||||||
Please note that because the function can modify them. The changes are made to copies, so they won't affect anything outside:
|
Here's one more example: we have a variable `from` and pass it to the function. Please note: the function changes `from`, but the change is not seen outside, because a function always gets a copy of the value:
|
||||||
|
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
|
@ -185,23 +183,26 @@ For instance, the aforementioned function `showMessage(from, text)` can be calle
|
||||||
showMessage("Ann");
|
showMessage("Ann");
|
||||||
```
|
```
|
||||||
|
|
||||||
That's not an error. Such call would output `"Ann: undefined"`, because `text === undefined`.
|
That's not an error. Such call would output `"Ann: undefined"`. There's no `text`, so it's assumed that `text === undefined`.
|
||||||
|
|
||||||
If we want to use a "default" `text` in this case, then we can specify it after `=`:
|
If we want to use a "default" `text` in this case, then we can specify it after `=`:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
function showMessage(from, *!*text = 'no text given'*/!*) {
|
function showMessage(from, *!*text = "no text given"*/!*) {
|
||||||
alert( from + ": " + text );
|
alert( from + ": " + text );
|
||||||
}
|
}
|
||||||
|
|
||||||
showMessage("Ann"); // Ann: no text given
|
showMessage("Ann"); // Ann: no text given
|
||||||
```
|
```
|
||||||
|
|
||||||
Here `'no text given'` is a string, but it can be a more complex expression, which is only evaluated and assigned if the parameter is missing. So, this is also possible:
|
Now if the `text` parameter is not passed, it will get the value `"no text given"`
|
||||||
|
|
||||||
|
Here `"no text given"` is a string, but it can be a more complex expression, which is only evaluated and assigned if the parameter is missing. So, this is also possible:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
function showMessage(from, text = anotherFunction()) {
|
function showMessage(from, text = anotherFunction()) {
|
||||||
// anotherFunction() only executed if no text given
|
// anotherFunction() only executed if no text given
|
||||||
|
// its result becomes the value of text
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -223,15 +224,17 @@ function showMessage(from, text) {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
...Or operator `||`:
|
...Or the `||` operator:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
function showMessage(from, text) {
|
function showMessage(from, text) {
|
||||||
// the argument is considered missing if it's falsy
|
// if text is falsy then text gets the "default" value
|
||||||
text = text || 'no text given';
|
text = text || 'no text given';
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
````
|
````
|
||||||
|
|
||||||
|
|
||||||
|
@ -315,14 +318,14 @@ alert( doNothing() === undefined ); // true
|
||||||
```
|
```
|
||||||
````
|
````
|
||||||
|
|
||||||
````warn header="Never line-break between `return` and its value"
|
````warn header="Never add a newline between `return` and the value"
|
||||||
For long expressions, it may be tempting sometimes to put them on a separate line, like this:
|
For a long expression in `return`, it might be tempting to put it on a separate line, like this:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
return
|
return
|
||||||
(some + long + expression + or + whatever * f(a) + f(b))
|
(some + long + expression + or + whatever * f(a) + f(b))
|
||||||
```
|
```
|
||||||
That doesn't work, because JavaScript assumes a semicolon after `return` in that case:
|
That doesn't work, because JavaScript assumes a semicolon after `return`. That'll work the same as:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
return*!*;*/!*
|
return*!*;*/!*
|
||||||
|
@ -341,10 +344,10 @@ For instance, functions that start with `"show"` -- usually show something.
|
||||||
|
|
||||||
Function starting with...
|
Function starting with...
|
||||||
|
|
||||||
- `"get"` -- allow to get something,
|
- `"get…"` -- return a value,
|
||||||
- `"calc"` -- calculate something,
|
- `"calc…"` -- calculate something,
|
||||||
- `"create"` -- create something,
|
- `"create…"` -- create something,
|
||||||
- `"check"` -- check something and return a boolean, etc.
|
- `"check…"` -- check something and return a boolean, etc.
|
||||||
|
|
||||||
Examples of such names:
|
Examples of such names:
|
||||||
|
|
||||||
|
@ -361,7 +364,7 @@ With prefixes at place, a glance at a function name gives an understanding what
|
||||||
```smart header="One function -- one action"
|
```smart header="One function -- one action"
|
||||||
A function should do exactly what is suggested by its name, no more.
|
A function should do exactly what is suggested by its name, no more.
|
||||||
|
|
||||||
Two independant actions usually deserve two functions, even if they are usually called together (in that case we can make a 3rd function calling those two).
|
Two independant actions usually deserve two functions, even if they are usually called together (in that case we can make a 3rd function that calls those two).
|
||||||
|
|
||||||
Few examples of breaking this rule:
|
Few examples of breaking this rule:
|
||||||
|
|
||||||
|
@ -369,7 +372,7 @@ Few examples of breaking this rule:
|
||||||
- `createForm` -- would be bad if it modifies the document, adding a form to it (should only create it and return).
|
- `createForm` -- would be bad if it modifies the document, adding a form to it (should only create it and return).
|
||||||
- `checkPermission` -- would be bad if displays the `access granted/denied` message (should only perform the check and return the result).
|
- `checkPermission` -- would be bad if displays the `access granted/denied` message (should only perform the check and return the result).
|
||||||
|
|
||||||
These examples reflect few common meanings of prefixes, the final word comes from you and your team. Maybe it's pretty normal for your code to behave differently. But you should to have a firm understanding what a prefix means, what a prefixed function can and what it can not do. All same-prefixed functions should obey the rules. And the team should share the knowledge.
|
These examples assume common meanings of prefixes. What they mean for you is determined by you and your team. Maybe it's pretty normal for your code to behave differently. But you should to have a firm understanding what a prefix means, what a prefixed function can and what it can not do. All same-prefixed functions should obey the rules. And the team should share the knowledge.
|
||||||
```
|
```
|
||||||
|
|
||||||
```smart header="Ultrashort function names"
|
```smart header="Ultrashort function names"
|
||||||
|
@ -382,11 +385,7 @@ These are exceptions. Generally functions names should be concise, but descripti
|
||||||
|
|
||||||
## Functions == Comments
|
## Functions == Comments
|
||||||
|
|
||||||
Functions should be short and do exactly one thing. If that thing is big, maybe it's worth to split the function into parts.
|
Functions should be short and do exactly one thing. If that thing is big, maybe it's worth to split the function into few smaller functions. Sometimes following this rule may be not easy, but it's a definitely good thing.
|
||||||
|
|
||||||
Sometimes following this rule may be not easy, but it's a definitely good thing.
|
|
||||||
|
|
||||||
...So why functions equal comments?
|
|
||||||
|
|
||||||
A separate function is not only easier to test and debug -- its very existence is a great comment!
|
A separate function is not only easier to test and debug -- its very existence is a great comment!
|
||||||
|
|
||||||
|
@ -429,9 +428,7 @@ function isPrime(n) {
|
||||||
|
|
||||||
The second variant is easier to understand isn't it? Instead of the code piece we see a name of the action (`isPrime`). Sometimes people refer to such code as *self-describing*.
|
The second variant is easier to understand isn't it? Instead of the code piece we see a name of the action (`isPrime`). Sometimes people refer to such code as *self-describing*.
|
||||||
|
|
||||||
```summary
|
So, functions can be created even if we don't intend to reuse them. They structure the code and make it readable.
|
||||||
So the idea is: functions can be created even if we don't intend to reuse them. They structure the code and make it readable.
|
|
||||||
```
|
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|
||||||
|
@ -444,12 +441,10 @@ function name(parameters, delimited, by, comma) {
|
||||||
```
|
```
|
||||||
|
|
||||||
- Values passed to function as parameters are copied to its local variables.
|
- Values passed to function as parameters are copied to its local variables.
|
||||||
- A function may access outer variables. But it works only one-way. The code outside of the function doesn't see its local variables.
|
- A function may access outer variables. But it works only from inside out. The code outside of the function doesn't see its local variables.
|
||||||
- A function can return a value. If it doesn't then its result is `undefined`.
|
- A function can return a value. If it doesn't then its result is `undefined`.
|
||||||
|
|
||||||
It is possible for a function to access variables defined outside of it.
|
To make the code clean and easy to understand, it's recommended to use mainly local variables and parameters in the function, not outer variables.
|
||||||
|
|
||||||
But to make the code cleaner and easier to understand, it's recommended to use local variables and parameters instead as much as possible.
|
|
||||||
|
|
||||||
It is always easier to understand a function which gets parameters, works with them and returns a result than a function which gets no parameters, but modifies outer variables as a side-effect.
|
It is always easier to understand a function which gets parameters, works with them and returns a result than a function which gets no parameters, but modifies outer variables as a side-effect.
|
||||||
|
|
||||||
|
@ -457,6 +452,6 @@ Function naming:
|
||||||
|
|
||||||
- A name should clearly describe what the function does. When we see a function call in the code, a good name instantly gives us an understanding what it does and returns.
|
- A name should clearly describe what the function does. When we see a function call in the code, a good name instantly gives us an understanding what it does and returns.
|
||||||
- A function is an action, so function names are usually verbal.
|
- A function is an action, so function names are usually verbal.
|
||||||
- There exist many widespread function prefixes like `create…`, `show…`, `get…`, `check…` and so on. Use them to hint what a function does.
|
- There exist many well-known function prefixes like `create…`, `show…`, `get…`, `check…` and so on. Use them to hint what a function does.
|
||||||
|
|
||||||
Functions are the main building blocks of scripts. Now we covered the basics, so we actually can start creating and using them. But that's only the beginning of the path. We are going to return to them many times, going more deeply in their advanced features.
|
Functions are the main building blocks of scripts. Now we covered the basics, so we actually can start creating and using them. But that's only the beginning of the path. We are going to return to them many times, going more deeply in their advanced features.
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
|
||||||
|
```js run
|
||||||
|
function ask(question, yes, no) {
|
||||||
|
if (confirm(question)) yes()
|
||||||
|
else no();
|
||||||
|
}
|
||||||
|
|
||||||
|
ask(
|
||||||
|
"Do you agree?",
|
||||||
|
*!*
|
||||||
|
() => alert("You agreed."),
|
||||||
|
() => alert("You canceled the execution.")
|
||||||
|
*/!*
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
Looks short and clean, right?
|
|
@ -0,0 +1,17 @@
|
||||||
|
|
||||||
|
# Rewrite with arrow functions
|
||||||
|
|
||||||
|
Replace Function Expressions with arrow functions in the code:
|
||||||
|
|
||||||
|
```js run
|
||||||
|
function ask(question, yes, no) {
|
||||||
|
if (confirm(question)) yes()
|
||||||
|
else no();
|
||||||
|
}
|
||||||
|
|
||||||
|
ask(
|
||||||
|
"Do you agree?",
|
||||||
|
function() { alert("You agreed."); },
|
||||||
|
function() { alert("You canceled the execution."); }
|
||||||
|
);
|
||||||
|
```
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
In JavaScript a function is not a "magical language structure", but a special kind of value.
|
In JavaScript a function is not a "magical language structure", but a special kind of value.
|
||||||
|
|
||||||
|
[cut]
|
||||||
|
|
||||||
The syntax that we used before is called *Function Declaration*:
|
The syntax that we used before is called *Function Declaration*:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
|
@ -10,9 +12,7 @@ function sayHi() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
There is another way of creating a function that is called *Function Expression*.
|
There is another syntax of creating a function that is called *Function Expression*.
|
||||||
|
|
||||||
[cut]
|
|
||||||
|
|
||||||
It looks like this:
|
It looks like this:
|
||||||
|
|
||||||
|
@ -22,9 +22,10 @@ let sayHi = function() {
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
The meaning of these code samples is the same: "create a function and put it into the variable `sayHi`".
|
Here the function is created and assigned to the variable explicitly, like any other value. No matter, how the function is defined -- it's just a value, stored in the variable `sayHi`.
|
||||||
|
|
||||||
No matter, how the function is defined -- it's just a value, stored in the variable `sayHi`.
|
|
||||||
|
The meaning of these code samples is the same: "create a function and put it into the variable `sayHi`".
|
||||||
|
|
||||||
We can even print out that value using `alert`:
|
We can even print out that value using `alert`:
|
||||||
|
|
||||||
|
@ -38,9 +39,11 @@ alert( sayHi ); // shows the function code
|
||||||
*/!*
|
*/!*
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that there are no brackets after `sayHi` in the last line, because we do not intend to run the function. There are programming languages where any mention of a function name causes it's call, but JavaScript is not like that. In JavaScript, a function is a value and we can deal with that as a value. The code above shows its string representation, that is its source code.
|
Please note that the last line does not run the function, because there are no parentheses after `sayHi`. There are programming languages where any mention of a function name causes the execution, but JavaScript is not like that.
|
||||||
|
|
||||||
It is a special value of course, in the sense that we can call it using brackets: `"sayHi()"`.
|
In JavaScript, a function is a value and we can deal with that as a value. The code above shows its string representation, that is the source code.
|
||||||
|
|
||||||
|
It is a special value of course, in the sense that we can call it like `sayHi()`.
|
||||||
|
|
||||||
But it's still a value. So we can work with it like with other kinds of values.
|
But it's still a value. So we can work with it like with other kinds of values.
|
||||||
|
|
||||||
|
@ -53,8 +56,8 @@ function sayHi() { // (1) create
|
||||||
|
|
||||||
let func = sayHi; // (2) copy
|
let func = sayHi; // (2) copy
|
||||||
|
|
||||||
func(); // Hello // (3) call the copy (it works)!
|
func(); // Hello // (3) run the copy (it works)!
|
||||||
sayHi(); // Hello // this works too (why wouldn't it)
|
sayHi(); // Hello // this still works too (why wouldn't it)
|
||||||
```
|
```
|
||||||
|
|
||||||
That's what happens above in detail:
|
That's what happens above in detail:
|
||||||
|
@ -62,7 +65,7 @@ That's what happens above in detail:
|
||||||
1. Function Declaration `(1)` creates the function and puts it into the variable named `sayHi`.
|
1. Function Declaration `(1)` creates the function and puts it into the variable named `sayHi`.
|
||||||
2. Line `(2)` copies it into variable `func`.
|
2. Line `(2)` copies it into variable `func`.
|
||||||
|
|
||||||
Please note again: there are no brackets after `sayHi`. If they were, then `func = sayHi()` would write *the result of the call* `sayHi()` into `func`, not *the function* `sayHi` itself.
|
Please note again: there are no parentheses after `sayHi`. If they were, then `func = sayHi()` would write *the result of the call* `sayHi()` into `func`, not *the function* `sayHi` itself.
|
||||||
3. Now the function can be called both as `sayHi()` and `func()`.
|
3. Now the function can be called both as `sayHi()` and `func()`.
|
||||||
|
|
||||||
Note, that we could also have used a Function Expression to declare `sayHi`, in the first line:
|
Note, that we could also have used a Function Expression to declare `sayHi`, in the first line:
|
||||||
|
@ -77,7 +80,7 @@ let func = sayHi;
|
||||||
Everything would work the same. Even more obvious what's going on, right?
|
Everything would work the same. Even more obvious what's going on, right?
|
||||||
|
|
||||||
|
|
||||||
````smart header="Why semicolon?"
|
````smart header="Why there's a semicolon at the end?"
|
||||||
There might be a question, why Function Expression has a semicolon `;` at the end, and Function Declaration does not:
|
There might be a question, why Function Expression has a semicolon `;` at the end, and Function Declaration does not:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
|
@ -97,7 +100,7 @@ The answer is simple:
|
||||||
|
|
||||||
## Callback functions
|
## Callback functions
|
||||||
|
|
||||||
Let's see an example where function expressions come really handy.
|
Let's see more examples of passing functions as values and using function expressions.
|
||||||
|
|
||||||
We'll write a function `ask(question, yes, no)` with three parameters:
|
We'll write a function `ask(question, yes, no)` with three parameters:
|
||||||
|
|
||||||
|
@ -110,7 +113,7 @@ We'll write a function `ask(question, yes, no)` with three parameters:
|
||||||
`no`
|
`no`
|
||||||
: Function to run if the answer is "No"
|
: Function to run if the answer is "No"
|
||||||
|
|
||||||
The function should ask the `question` and, depending on the user's agreement, call `yes()` or `no()`:
|
The function should ask the `question` and, depending on the user's answer, call `yes()` or `no()`:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
*!*
|
*!*
|
||||||
|
@ -128,15 +131,15 @@ function showCancel() {
|
||||||
alert( "You canceled the execution." );
|
alert( "You canceled the execution." );
|
||||||
}
|
}
|
||||||
|
|
||||||
// usage
|
// usage: functions showOk, showCancel are passed as arguments to ask
|
||||||
ask("Do you agree?", showOk, showCancel);
|
ask("Do you agree?", showOk, showCancel);
|
||||||
```
|
```
|
||||||
|
|
||||||
The code looks kind of too simple, right? Why would anyone need such `ask`?
|
Before we'll explore how we can write it in a much shorter way, let's note that in the browser (and on the server-side in some cases) such functions are quite popular.
|
||||||
|
|
||||||
...It turns out that in the browser (and on the server-side in some cases) such functions are quite popular. The major difference between a real-life implementation and the example above is that real-life functions use more complex ways to interact with the user than a simple `confirm`. In the browser such a function usually draws a nice-looking question window. But that's another story.
|
The major difference between a real-life implementation and the example above is that real-life functions use more complex ways to interact with the user than a simple `confirm`. In the browser such a function usually draws a nice-looking question window. But that's another story.
|
||||||
|
|
||||||
The arguments of `ask` are called *callback functions* or just *callbacks*. The idea is that we pass the functions and expect them to be "called back" in certain circumstances.
|
**The arguments of `ask` are called *callback functions* or just *callbacks*. The idea is that we pass a function and expect it to be "called back" in certain circumstances.**
|
||||||
|
|
||||||
So, `showOk` becomes the callback for the "yes" answer and `showCancel` -- for the "no" answer.
|
So, `showOk` becomes the callback for the "yes" answer and `showCancel` -- for the "no" answer.
|
||||||
|
|
||||||
|
@ -157,7 +160,7 @@ ask(
|
||||||
*/!*
|
*/!*
|
||||||
```
|
```
|
||||||
|
|
||||||
Here functions are declared right inside the `ask(...)` call. They have no name, and so are called *anonymous*. Such functions are not accessible outside of `ask`, but that's just what we want here.
|
Here functions are declared right inside the `ask(...)` call. They have no name, and so are called *anonymous*. Such functions are not accessible outside of `ask` (because they are not assigned to variables), but that's just what we want here.
|
||||||
|
|
||||||
Such code appears in our scripts very naturally, it's in the spirit of JavaScript.
|
Such code appears in our scripts very naturally, it's in the spirit of JavaScript.
|
||||||
|
|
||||||
|
@ -185,9 +188,9 @@ First, the syntax: how to see what is what in the code.
|
||||||
return a + b;
|
return a + b;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
- *Function Expression:* a function, created in the context of an expression.
|
- *Function Expression:* a function, created inside an expression or inside another syntax construct.
|
||||||
|
|
||||||
Here the function is created in the context of an "assignment expression =":
|
Here the function is created at the right side of the "assignment expression =":
|
||||||
```js
|
```js
|
||||||
// Function Expression
|
// Function Expression
|
||||||
let sum = function(a, b) {
|
let sum = function(a, b) {
|
||||||
|
@ -195,19 +198,19 @@ First, the syntax: how to see what is what in the code.
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The more subtle difference is when they are actualy created by the JavaScript engine.
|
The more subtle difference is *when* a function is created by JavaScript engine.
|
||||||
|
|
||||||
**Function Expressions are created when the execution reaches them and are usable since then.**
|
**Function Expression is created when the execution reaches it and is usable since then.**
|
||||||
|
|
||||||
That's kind of obvious. Once the execution flow passes to the right side of the assignment `let sum = function` -- here we go, the function is created and can be used (assigned, called etc) from now on.
|
Once the execution flow passes to the right side of the assignment `let sum = function…` -- here we go, the function is created and can be used (assigned, called etc) from now on.
|
||||||
|
|
||||||
Function Declarations are different.
|
Function Declarations are different.
|
||||||
|
|
||||||
**Function Declarations are usable in the whole script/code block.**
|
**Function Declaration is usable in the whole script/code block.**
|
||||||
|
|
||||||
In other words, when JavaScript *prepares* to run the script/code block, it first looks for Function Declarations in it and creates the functions. We can think of it as an "initialization stage".
|
In other words, when JavaScript *prepares* to run the script or a code block, it first looks for Function Declarations in it and creates the functions. We can think of it as an "initialization stage".
|
||||||
|
|
||||||
And after all Function Declarations are processed, it actually executes it.
|
And after all Function Declarations are processed, the execution goes on.
|
||||||
|
|
||||||
As a natural effect, a function declared as Function Declaration can be called earlier than it is defined.
|
As a natural effect, a function declared as Function Declaration can be called earlier than it is defined.
|
||||||
|
|
||||||
|
@ -244,9 +247,9 @@ Function Expressions are created when the execution reaches them. That would hap
|
||||||
|
|
||||||
When Function Declaration is made within a code block, it is visible everywhere inside that block. But not outside of it.
|
When Function Declaration is made within a code block, it is visible everywhere inside that block. But not outside of it.
|
||||||
|
|
||||||
Sometimes that's handy to declare a local function, only needed in that only block. But can also be a problem.
|
Sometimes that's handy to declare a local function only needed in that only block. But that feature may also cause problems.
|
||||||
|
|
||||||
For instance, let's imagine that we need to declare a function `welcome()` depending on the `age` variable that we get in run time. And then use it sometimes later.
|
For instance, let's imagine that we need to declare a function `welcome()` depending on the `age` variable that we get in run time. And then we plan to use it sometimes later.
|
||||||
|
|
||||||
The code below doesn't work:
|
The code below doesn't work:
|
||||||
|
|
||||||
|
@ -286,7 +289,7 @@ if (age < 18) {
|
||||||
// |
|
// |
|
||||||
function welcome() { // |
|
function welcome() { // |
|
||||||
alert("Hello!"); // | Function Declaration is available
|
alert("Hello!"); // | Function Declaration is available
|
||||||
} // | everywhere in the block when it's declared
|
} // | everywhere in the block where it's declared
|
||||||
// |
|
// |
|
||||||
*!*
|
*!*
|
||||||
welcome(); // / (runs)
|
welcome(); // / (runs)
|
||||||
|
@ -295,12 +298,12 @@ if (age < 18) {
|
||||||
} else {
|
} else {
|
||||||
// \
|
// \
|
||||||
function welcome() { // |
|
function welcome() { // |
|
||||||
alert("Greetings!"); // | in this if test we don't enter this block,
|
alert("Greetings!"); // | if age=16, the the execution does not go here,
|
||||||
} // | so this "welcome" is never created
|
} // | so this "welcome" is never created
|
||||||
// /
|
// /
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now we're out of figure brackets,
|
// Here we're out of figure brackets,
|
||||||
// so we can not see Function Declarations made inside of them.
|
// so we can not see Function Declarations made inside of them.
|
||||||
|
|
||||||
*!*
|
*!*
|
||||||
|
@ -308,7 +311,7 @@ welcome(); // Error: welcome is not defined
|
||||||
*/!*
|
*/!*
|
||||||
```
|
```
|
||||||
|
|
||||||
What can we do to make `welcome` visible outside?
|
What can we do to make `welcome` visible outside of `if`?
|
||||||
|
|
||||||
The right thing would be to use a Function Expression and assign `welcome` to the variable which is declared outside of `if` and has the proper visibility:
|
The right thing would be to use a Function Expression and assign `welcome` to the variable which is declared outside of `if` and has the proper visibility:
|
||||||
|
|
||||||
|
@ -321,13 +324,13 @@ if (age < 18) {
|
||||||
|
|
||||||
welcome = function() {
|
welcome = function() {
|
||||||
alert("Hello!");
|
alert("Hello!");
|
||||||
}
|
};
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
welcome = function() {
|
welcome = function() {
|
||||||
alert("Greetings!");
|
alert("Greetings!");
|
||||||
}
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -336,13 +339,14 @@ welcome(); // ok now
|
||||||
*/!*
|
*/!*
|
||||||
```
|
```
|
||||||
|
|
||||||
Or we could go on to simplify it even further using a question mark operator `?`:
|
Or we could simplify it even further using a question mark operator `?`:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
let age = prompt("What is your age?", 18);
|
let age = prompt("What is your age?", 18);
|
||||||
|
|
||||||
let welcome = (age < 18) ?
|
let welcome = (age < 18) ?
|
||||||
function() { alert("Hello!"); } : function() { alert("Greetings!"); }
|
function() { alert("Hello!"); } :
|
||||||
|
function() { alert("Greetings!"); };
|
||||||
|
|
||||||
*!*
|
*!*
|
||||||
welcome(); // ok now
|
welcome(); // ok now
|
||||||
|
@ -350,22 +354,19 @@ welcome(); // ok now
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
```smart header="What to choose: a Declaration or an Expression?"
|
```smart header="What to choose: Function Declaration or Function Expression?"
|
||||||
As a rule of thumb, a Function Declaration is prefered. It gives more freedom in how to organize our code, because we can call it both above and below.
|
As a rule of thumb, when we need to declare a function, the first to consider is Function Declaration syntax, the one we used before. It gives more freedom in how to organize our code, because we can call such functions before they are declared.
|
||||||
|
|
||||||
It's also a little bit easier to look up Function Declarations in the code, they increase readability of the code.
|
It's also a little bit easier to look up `function f(…) {…}` in the code than `let f = function(…) {…}`. Function Declarations are more "eye-catching".
|
||||||
|
|
||||||
But if a Function Declaration does not fit for some reason (we've seen an example), then a Function Expression should be used.
|
...But if Function Declaration does not suit us for some reason (we've seen an example above), then Function Expression should be used.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## Arrow functions [#arrow-functions]
|
## Arrow functions [#arrow-functions]
|
||||||
|
|
||||||
Enough with the complexities for now. Let's relax with another syntax of functions that can make our code shorter.
|
There's one more syntax for creating functions -- very simple and concise. It's called "arrow functions", because it looks like this:
|
||||||
|
|
||||||
Arrow functions act like a function expression, but look a little bit differently.
|
|
||||||
|
|
||||||
The syntax is:
|
|
||||||
|
|
||||||
```js
|
```js
|
||||||
let func = (arg1, arg2, ...argN) => expression
|
let func = (arg1, arg2, ...argN) => expression
|
||||||
|
@ -381,6 +382,8 @@ let func = function(arg1, arg2, ...argN) {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
...But much shorter.
|
||||||
|
|
||||||
Let's see the example:
|
Let's see the example:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
|
@ -397,7 +400,7 @@ let sum = function(a, b) {
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
If we have only one argument, then brackets can be omitted, making that even shorter:
|
If we have only one argument, then parentheses can be omitted, making that even shorter:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
// same as
|
// same as
|
||||||
|
@ -409,7 +412,7 @@ let double = n => n*2;
|
||||||
alert( double(3) ); // 6
|
alert( double(3) ); // 6
|
||||||
```
|
```
|
||||||
|
|
||||||
If there are no arguments, we can put empty brackets:
|
If there are no arguments, we can put empty parentheses:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
let sayHi = () => alert("Hello!");
|
let sayHi = () => alert("Hello!");
|
||||||
|
@ -424,7 +427,9 @@ For instance, here's the rewritten example with `welcome()`:
|
||||||
```js run
|
```js run
|
||||||
let age = prompt("What is your age?", 18);
|
let age = prompt("What is your age?", 18);
|
||||||
|
|
||||||
let welcome = (age < 18) ? () => alert('Hello') : () => alert("Greetings!");
|
let welcome = (age < 18) ?
|
||||||
|
() => alert('Hello') :
|
||||||
|
() => alert("Greetings!");
|
||||||
|
|
||||||
welcome(); // ok now
|
welcome(); // ok now
|
||||||
```
|
```
|
||||||
|
@ -453,7 +458,7 @@ alert( sum(1, 2) ); // 3
|
||||||
```
|
```
|
||||||
|
|
||||||
```smart header="More to come"
|
```smart header="More to come"
|
||||||
Here we praised arrow functions for shortness. But that's not all! Arrow functions have other interesting features. We'll return to them later in the chapter <info:arrow-functions>.
|
Here we praised arrow functions for brevity. But that's not all! Arrow functions have other interesting features. We'll return to them later in the chapter <info:arrow-functions>.
|
||||||
|
|
||||||
As for now, we can already use them for one-line actions and callbacks.
|
As for now, we can already use them for one-line actions and callbacks.
|
||||||
```
|
```
|
||||||
|
@ -461,18 +466,17 @@ As for now, we can already use them for one-line actions and callbacks.
|
||||||
## Summary
|
## Summary
|
||||||
|
|
||||||
- Functions are values. They can be assigned, copied or declared in any place of the code.
|
- Functions are values. They can be assigned, copied or declared in any place of the code.
|
||||||
- If the function is declared as a separate statement, in the main code flow -- that's called a Function Declaration.
|
- If the function is declared as a separate statement, in the main code flow -- that's called "Function Declaration".
|
||||||
- If the function is created as a part of an expression -- it's a Function Expression.
|
- If the function is created as a part of an expression -- it's "Function Expression".
|
||||||
- Function Declarations are processed before the code block is executed. They are visible everywhere in the block.
|
- Function Declarations are processed before the code block is executed. They are visible everywhere in the block.
|
||||||
- Function Expressions are created when the execution flow reaches them.
|
- Function Expressions are created when the execution flow reaches them.
|
||||||
- Function Expressions allow to specify an optional name for internal needs (Named Function Expression).
|
|
||||||
|
|
||||||
|
|
||||||
In most cases Function Declaration is preferable, because it is visible prior to the declaration itself. And is usually more readable.
|
In most cases when we need to declare a function, Function Declaration is preferable, because it is visible prior to the declaration itself. That gives more flexibility in code organization. And is usually more readable.
|
||||||
|
|
||||||
So we should use Function Expression only when Function Declaration does not fit the task. We've seen a couple of examples of that in the chapter. And will see more in the future.
|
So we should use Function Expression only when Function Declaration does not fit the task. We've seen a couple of examples of that in the chapter. And will see more in the future.
|
||||||
|
|
||||||
Arrow functions are handy for one-liners. The come in two flavors:
|
Arrow functions are handy for one-liners. The come in two flavors:
|
||||||
|
|
||||||
1. Without figure brackets: `(...args) => expression` -- returns the evaluated `expression`. The right side must be a single expression.
|
1. Without figure brackets: `(...args) => expression` -- the right side is an expression: the function evaluates it and returns the result.
|
||||||
2. With figure brackets: `(...args) => { body }` -- they need an explicit `return` statement to return something, but can be more complex and contain multiple statements.
|
2. With figure brackets: `(...args) => { body }` -- brackets allow to write multiple statements inside the function, but we need an explicit `return` to return something.
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
# JavaScript specials
|
# JavaScript specials
|
||||||
|
|
||||||
This chapter aims to list features of JavaScript that we've learned, paying special attention to subtle moments.
|
This chapter briefly recaps the features of JavaScript that we've learned by now, paying special attention to subtle moments.
|
||||||
|
|
||||||
That's especially useful if you came from another language or just as a recap.
|
|
||||||
|
|
||||||
[cut]
|
[cut]
|
||||||
|
|
||||||
|
@ -31,14 +29,16 @@ alert("There will be an error after this message")
|
||||||
|
|
||||||
Most codestyle guides agree that we should put a semicolon after each statement.
|
Most codestyle guides agree that we should put a semicolon after each statement.
|
||||||
|
|
||||||
Semicolons are not required after code blocks `{...}` and syntax constructs with them:
|
Semicolons are not required after code blocks `{...}` and syntax constructs with them like loops:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
function f() {
|
function f() {
|
||||||
// no semicolon needed after function declaration
|
// no semicolon needed after function declaration
|
||||||
}
|
}
|
||||||
|
|
||||||
for(;;) { /* no semicolon after the loop */ }
|
for(;;) {
|
||||||
|
// no semicolon needed after the loop
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
...But even if we can put an "extra" semicolon somewhere, that's not an error, it will be ignored.
|
...But even if we can put an "extra" semicolon somewhere, that's not an error, it will be ignored.
|
||||||
|
@ -59,7 +59,7 @@ The directive must be at the top of a script or at the beginning of a function.
|
||||||
|
|
||||||
Without `"use strict"`, everything still works, but some features behave in old-fasion, "compatible" way. We'd generally prefer the modern behavior.
|
Without `"use strict"`, everything still works, but some features behave in old-fasion, "compatible" way. We'd generally prefer the modern behavior.
|
||||||
|
|
||||||
Advanced features of the language (like classes that we'll study in the future) that enable strict mode implicitly.
|
Some modern features of the language (like classes that we'll study in the future) enable strict mode implicitly.
|
||||||
|
|
||||||
More in: <info:strict-mode>.
|
More in: <info:strict-mode>.
|
||||||
|
|
||||||
|
@ -67,8 +67,8 @@ More in: <info:strict-mode>.
|
||||||
|
|
||||||
Can be declared using:
|
Can be declared using:
|
||||||
|
|
||||||
- `let` (block-level visibility)
|
- `let`
|
||||||
- `const` (can't be changed)
|
- `const` (constant, can't be changed)
|
||||||
- `var` (old-style, will see later)
|
- `var` (old-style, will see later)
|
||||||
|
|
||||||
A variable name can include:
|
A variable name can include:
|
||||||
|
@ -92,7 +92,7 @@ There are 7 data types:
|
||||||
- `undefined` -- a type with a single value `undefined`, meaning "not assigned",
|
- `undefined` -- a type with a single value `undefined`, meaning "not assigned",
|
||||||
- `object` and `symbol` -- for complex data structures and unique identifiers, we didn't learn them yet.
|
- `object` and `symbol` -- for complex data structures and unique identifiers, we didn't learn them yet.
|
||||||
|
|
||||||
The `typeof` operator returns the type for a value, with two special behaviors:
|
The `typeof` operator returns the type for a value, with two exceptions:
|
||||||
```js
|
```js
|
||||||
typeof null == "object" // error in the language
|
typeof null == "object" // error in the language
|
||||||
typeof function(){} == "function" // functions are treated specially
|
typeof function(){} == "function" // functions are treated specially
|
||||||
|
@ -125,7 +125,7 @@ alert( "Visitor: " + userName ); // Alice
|
||||||
alert( "Tea wanted: " + isTeaWanted ); // true
|
alert( "Tea wanted: " + isTeaWanted ); // true
|
||||||
```
|
```
|
||||||
|
|
||||||
More in: <info:uibasic>.
|
More in: <info:alert-prompt-confirm>.
|
||||||
|
|
||||||
## Operators
|
## Operators
|
||||||
|
|
||||||
|
@ -134,9 +134,7 @@ JavaScript supports following operators:
|
||||||
Arithmetical
|
Arithmetical
|
||||||
: Regular: `* + - /`, also `%` for the remainder and `**` for power of a number.
|
: Regular: `* + - /`, also `%` for the remainder and `**` for power of a number.
|
||||||
|
|
||||||
Binary plus `+` concatenates strings.
|
Binary plus `+` concatenates strings. And if any of the operands is a string -- the other one is converted to string too:
|
||||||
|
|
||||||
If any of the operands is a string -- the other one is converted to string too:
|
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
alert( '1' + 2 ); // '12', string
|
alert( '1' + 2 ); // '12', string
|
||||||
|
@ -147,11 +145,10 @@ Assignments
|
||||||
: There is a simple assignment: `a = b` and combined ones like `a *= 2`.
|
: There is a simple assignment: `a = b` and combined ones like `a *= 2`.
|
||||||
|
|
||||||
Bitwise
|
Bitwise
|
||||||
: Bitwise operators work with integers on bit-level: see the [docs](mdn:JavaScript/Refereno
|
: Bitwise operators work with integers on bit-level: see the [docs](mdn:/JavaScript/Reference/Operators/Bitwise_Operators) when they are needed.
|
||||||
ce/Operators/Bitwise_Operators) when they are needed.
|
|
||||||
|
|
||||||
Ternary
|
Ternary
|
||||||
: The only operator with three parameters: `cond ? resultA : result B`
|
: The only operator with three parameters: `cond ? resultA : result B`. If `cond` is truthy, returns `resultA`, otherwise `resultB`.
|
||||||
|
|
||||||
Logical operators
|
Logical operators
|
||||||
: Logical AND `&&` and OR `||` perform short-circuit evaluation and then return the value where it stopped.
|
: Logical AND `&&` and OR `||` perform short-circuit evaluation and then return the value where it stopped.
|
||||||
|
@ -168,8 +165,6 @@ Comparisons
|
||||||
|
|
||||||
The strict equality operator `===` doesn't do the conversion: different types always mean different values for it, so:
|
The strict equality operator `===` doesn't do the conversion: different types always mean different values for it, so:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Values `null` and `undefined` are special: they equal `==` each other and don't equal anything else.
|
Values `null` and `undefined` are special: they equal `==` each other and don't equal anything else.
|
||||||
|
|
||||||
Greater/less comparisons compare strings character-by-character, other types are converted to a number.
|
Greater/less comparisons compare strings character-by-character, other types are converted to a number.
|
||||||
|
@ -177,7 +172,7 @@ Comparisons
|
||||||
Logical operators
|
Logical operators
|
||||||
: There are few others, like a comma operator.
|
: There are few others, like a comma operator.
|
||||||
|
|
||||||
More in: <info:operators>, <info:comparison>, <info:logical-ops>.
|
More in: <info:operators>, <info:comparison>, <info:logical-operators>.
|
||||||
|
|
||||||
## Loops
|
## Loops
|
||||||
|
|
||||||
|
@ -218,7 +213,7 @@ let age = prompt('Your age?', 18);
|
||||||
|
|
||||||
switch (age) {
|
switch (age) {
|
||||||
case 18:
|
case 18:
|
||||||
alert("Won't work"); // the result of prompt is a string, not a number
|
alert("Won't work"); // the result of prompt is a string, not a number
|
||||||
|
|
||||||
case "18":
|
case "18":
|
||||||
alert("This works!"");
|
alert("This works!"");
|
||||||
|
@ -291,6 +286,4 @@ More: see <info:function-basics>, <info:function-expressions-arrows>.
|
||||||
|
|
||||||
## More to come
|
## More to come
|
||||||
|
|
||||||
That was a brief list of JavaScript specials that we need to know to code well.
|
That was a brief list of JavaScript features. As of now we studied only basics. Further in the tutorial you'll find more specials and advanced features of JavaScript.
|
||||||
|
|
||||||
As of now that were only basics. Further in the tutorial you'll find more specials and advanced features of JavaScript.
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
# Debugging in Chrome
|
# Debugging in Chrome
|
||||||
|
|
||||||
Before going further, let's talk about debugging.
|
Before writing more complex code, let's talk about debugging.
|
||||||
|
|
||||||
All modern browsers and most other environments support "debugging" -- a special UI in developer tools that makes finding and fixing errors much easier.
|
All modern browsers and most other environments support "debugging" -- a special UI in developer tools that makes finding and fixing errors much easier.
|
||||||
|
|
||||||
We'll be talking about Chrome here, because they are probably the most feature-rich.
|
We'll be using Chrome here, because it's probably the most feature-rich in this aspect.
|
||||||
|
|
||||||
[cut]
|
[cut]
|
||||||
|
|
||||||
|
@ -28,66 +28,70 @@ Let's click it and select `index.html` and then `hello.js` in the tree view. Tha
|
||||||
|
|
||||||
Here we can see three zones:
|
Here we can see three zones:
|
||||||
|
|
||||||
1. **Resources zone** lists html, javascript, css and other files including images that are attached to the page. Currently active chrome extensions may appear here too.
|
1. **Resources zone** lists html, javascript, css and other files including images that are attached to the page. Chrome extensions may appear here too.
|
||||||
2. **Source zone** shows the source code.
|
2. **Source zone** shows the source code.
|
||||||
3. **Information and control zone** is for debugging, we'll explore it later.
|
3. **Information and control zone** is for debugging, we'll explore it soon.
|
||||||
|
|
||||||
Now you could click the same toggler <span class="devtools" style="background-position:-200px -76px"></span> again to hide the resources list, as we won't need it soon.
|
Now you could click the same toggler <span class="devtools" style="background-position:-200px -76px"></span> again to hide the resources list and give the code some space.
|
||||||
|
|
||||||
## Console
|
## Console
|
||||||
|
|
||||||
If we press `Esc`, then a console opens below, to type commands into.
|
If we press `Esc`, then a console opens below, we can type commands there and press `key:Enter` to execute.
|
||||||
|
|
||||||
After a statement is executed, its result is shown.
|
After a statement is executed, its result is shown below.
|
||||||
|
|
||||||
For example, here `1+2` results in `3` and `hello("debugger")` call shows a message, but there's no result, so it's `undefined`:
|
For example, here `1+2` results in `3`, and `hello("debugger")` returns nothing, so the result is `undefined`:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Breakpoints
|
## Breakpoints
|
||||||
|
|
||||||
Let's examine what's going on within the code. In `hello.js`, click at the line number `4`, right on the `4` digit.
|
Let's examine what's going on within the code. In `hello.js`, click at the line number `4`. Yes, right on the `"4"` digit, not on the code.
|
||||||
|
|
||||||
Contratulations! You've set a breakpoint. Please also click on the number for line `8`.
|
Contratulations! You've set a breakpoint. Please also click on the number for line `8`.
|
||||||
|
|
||||||
Should look like this:
|
Should look like this (blue is where you should click):
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
A *breakpoint* is a point of code where the debugger will automatically pause the JavaScript execution.
|
A *breakpoint* is a point of code where the debugger will automatically pause the JavaScript execution.
|
||||||
|
|
||||||
While the code is paused, we can examine current variables, execute commands in the console etc. That is -- debug it.
|
While the code is paused, we can examine current variables, execute commands in the console etc. In other words, to debug it.
|
||||||
|
|
||||||
Breakpoints also show in the right pane. We can always find a list of breakpoints there.
|
We can always find a list of breakpoints in the right pane. That's useful when we have many breakpoints in various files. It allows to:
|
||||||
|
|
||||||
It allows to:
|
|
||||||
- Quickly jump to the breakpoint in the code (by clicking on it in the right pane).
|
- Quickly jump to the breakpoint in the code (by clicking on it in the right pane).
|
||||||
- Temporarily disable the breakpoint by unchecking it.
|
- Temporarily disable the breakpoint by unchecking it.
|
||||||
- Remove the breakpoint by right-clicking the text and selecting Remove.
|
- Remove the breakpoint by right-clicking and selecting Remove.
|
||||||
- ...And so on.
|
- ...And so on.
|
||||||
|
|
||||||
````smart header="Breakpoint alternatives"
|
```smart header="Conditional breakpoints"
|
||||||
- We can make debugger to pause the code by using the `debugger` command, like this:
|
*Right click* on the line number allows to create a *conditional* breakpoint. It only triggers when the given expression is truthy.
|
||||||
|
|
||||||
```js
|
That's handy when we need to stop only for a certain variable value or for a certain function parameters.
|
||||||
function hello(name) {
|
```
|
||||||
let phrase = `Hello, ${name}!`;
|
|
||||||
|
|
||||||
*!*
|
## Debugger command
|
||||||
debugger; // <-- the debugger stops here
|
|
||||||
*/!*
|
|
||||||
|
|
||||||
say(phrase);
|
We can also pause the code by using the `debugger` command, like this:
|
||||||
}
|
|
||||||
```
|
```js
|
||||||
- *Right click* on the line number allows to create a conditional breakpoint. It only triggers when a given expression is truthy.
|
function hello(name) {
|
||||||
|
let phrase = `Hello, ${name}!`;
|
||||||
|
|
||||||
|
*!*
|
||||||
|
debugger; // <-- the debugger stops here
|
||||||
|
*/!*
|
||||||
|
|
||||||
|
say(phrase);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
That's very convenient when we are in a code editor and don't want to switch to the browser and look up the script in developer tools to set the breakpoint.
|
||||||
|
|
||||||
That's handy when we need to stop only for a certain variable value or for a certain function parameters.
|
|
||||||
````
|
|
||||||
|
|
||||||
## Pause and look around
|
## Pause and look around
|
||||||
|
|
||||||
In our example, `hello()` is called during page load, so the easiest way to activate debugger -- is to reload the page. So let's press `key:F5` (Windows, Linux) or `key:Cmd+R` (Mac).
|
In our example, `hello()` is called during the page load, so the easiest way to activate debugger -- is to reload the page. So let's press `key:F5` (Windows, Linux) or `key:Cmd+R` (Mac).
|
||||||
|
|
||||||
As the breakpoint is set, the execution pauses at the 4th line:
|
As the breakpoint is set, the execution pauses at the 4th line:
|
||||||
|
|
||||||
|
@ -97,13 +101,13 @@ Please open the informational dropdowns to the right (labelled with arrows). The
|
||||||
|
|
||||||
1. **`Watch` -- shows current values for any expressions.**
|
1. **`Watch` -- shows current values for any expressions.**
|
||||||
|
|
||||||
You can click the plus `+` and input an expression. The debugger will shown its value at any moment, automatically recalculating it in in the process.
|
You can click the plus `+` and input an expression. The debugger will shown its value at any moment, automatically recalculating it in in the process of execution.
|
||||||
|
|
||||||
2. **`Call Stack` -- shows the nested calls chain.**
|
2. **`Call Stack` -- shows the nested calls chain.**
|
||||||
|
|
||||||
At the current moment the debugger is inside `hello()` call, called by a script in `index.html` (no function there).
|
At the current moment the debugger is inside `hello()` call, called by a script in `index.html` (no function there, so it's called "anonymous").
|
||||||
|
|
||||||
If you click the item there, the debugger jumps to the corresponding code, and all its variables can be examined as well.
|
If you click on a stack item, the debugger jumps to the corresponding code, and all its variables can be examined as well.
|
||||||
3. **`Scope` -- current variables.**
|
3. **`Scope` -- current variables.**
|
||||||
|
|
||||||
`Local` shows local function variables. You can also see their values highlighted right over the source.
|
`Local` shows local function variables. You can also see their values highlighted right over the source.
|
||||||
|
@ -116,41 +120,43 @@ Please open the informational dropdowns to the right (labelled with arrows). The
|
||||||
|
|
||||||
Now let's time to *trace* the script.
|
Now let's time to *trace* the script.
|
||||||
|
|
||||||
There are buttons for it at the right-top:
|
There are buttons for it at the top of the right pane. Let's engage them.
|
||||||
|
|
||||||
<span class="devtools" style="background-position:-7px -76px"></span> -- continue the execution, hotkey `key:F8`.
|
<span class="devtools" style="background-position:-7px -76px"></span> -- continue the execution, hotkey `key:F8`.
|
||||||
: Resumes the execution. If there are no additional breakpoints, then the execution just continues and the debugger looses the control.
|
: Resumes the execution. If there are no additional breakpoints, then the execution just continues and the debugger looses the control.
|
||||||
|
|
||||||
Let's click it:
|
Here's what we can see after a click on it:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
The execution has resumed, reached another breakpoint inside `say()` and paused there. Take a look at the "Call stack" at the right. It has increased by one more call. We're inside `say()` now.
|
The execution has resumed, reached another breakpoint inside `say()` and paused there. Take a look at the "Call stack" at the right. It has increased by one more call. We're inside `say()` now.
|
||||||
|
|
||||||
<span class="devtools" style="background-position:-137px -76px"></span> -- make a step (run the next command), but *not go into the function*, hotkey `key:F10`.
|
<span class="devtools" style="background-position:-137px -76px"></span> -- make a step (run the next command), but *not go into the function*, hotkey `key:F10`.
|
||||||
: If we click it now, `alert` will be shown. The important thing is that if `alert` were not native, but a JavaScript function, then the execution would "step over it", skipping the function internals.
|
: If we click it now, `alert` will be shown. The important thing is that `alert` can be any function, the execution "steps over it", skipping the function internals.
|
||||||
|
|
||||||
<span class="devtools" style="background-position:-72px -76px"></span> -- make a step, hotkey `key:F11`.
|
<span class="devtools" style="background-position:-72px -76px"></span> -- make a step, hotkey `key:F11`.
|
||||||
: The same as the previous one, but "steps in" nested functions. Clicking this will step through all script actions one by one.
|
: The same as the previous one, but "steps into" nested functions. Clicking this will step through all script actions one by one.
|
||||||
|
|
||||||
<span class="devtools" style="background-position:-104px -76px"></span> -- continue the execution till the end of the current function, hotkey `key:Shift+F11`.
|
<span class="devtools" style="background-position:-104px -76px"></span> -- continue the execution till the end of the current function, hotkey `key:Shift+F11`.
|
||||||
: The execution would stop at the very last line of the current function. That's handy when we accidentally entered a nested call using <span class="devtools" style="background-position:-137px -76px"></span>, but it does not interest us and we want to continue to its end as soon as possible.
|
: The execution would stop at the very last line of the current function. That's handy when we accidentally entered a nested call using <span class="devtools" style="background-position:-72px -76px"></span>, but it does not interest us, and we want to continue to its end as soon as possible.
|
||||||
|
|
||||||
<span class="devtools" style="background-position:-7px -28px"></span> -- enable/disable all breakpoints.
|
<span class="devtools" style="background-position:-7px -28px"></span> -- enable/disable all breakpoints.
|
||||||
: That button does not move the execution. Just a mass on/off for breakpoints.
|
: That button does not move the execution. Just a mass on/off for breakpoints.
|
||||||
|
|
||||||
<span class="devtools" style="background-position:-264px -4px"></span> -- enable/disable automatic pause in case of an error.
|
<span class="devtools" style="background-position:-264px -4px"></span> -- enable/disable automatic pause in case of an error.
|
||||||
: When it's enabled (by default it is), an error pauses the execution and we can analyze variables to see what went wrong. So if our script dies with error, we can open debugger and reload the page to see where it stopped and what was the context at that moment.
|
: When enabled, and the developer tools is open, a script error automatically pauses the execution. Then we can analyze variables to see what went wrong. So if our script dies with an error, we can open debugger, enable this option and reload the page to see where it dies and what's the context at that moment.
|
||||||
|
|
||||||
```smart header="Continue to here"
|
```smart header="Continue to here"
|
||||||
Right click on the line number opens the contextual menu where we can "Continue to here". That's handy when we want to move multiple steps forward, but too lazy to set a breakpoint.
|
Right click on a line of code opens the context menu with a great option called "Continue to here".
|
||||||
|
|
||||||
|
That's handy when we want to move multiple steps forward, but too lazy to set a breakpoint.
|
||||||
```
|
```
|
||||||
|
|
||||||
## Logging
|
## Logging
|
||||||
|
|
||||||
To output something to console, there's `console.log` function.
|
To output something to console, there's `console.log` function.
|
||||||
|
|
||||||
For instance, this logs values from `0` to `4`:
|
For instance, this outputs values from `0` to `4` to console:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
// open console to see
|
// open console to see
|
||||||
|
@ -159,9 +165,9 @@ for (let i = 0; i < 5; i++) {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
To see the output, you can either switch to "Console" tab in developer tools or press `key:Esc` while in another tab: that opens the console at the bottom.
|
Regular users don't see that output, it is in the console. To see it -- either open the Console tab of developer tools or press `key:Esc` while in another tab: that opens the console at the bottom.
|
||||||
|
|
||||||
That's called "logging". If we have enough logging in our code, then we can see what's going on from the records, without the debugger.
|
If we have enough logging in our code, then we can see what's going on from the records, without the debugger.
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|
||||||
|
@ -170,12 +176,10 @@ As we can see, there are 3 main ways to pause a script:
|
||||||
2. The `debugger` statements.
|
2. The `debugger` statements.
|
||||||
3. An error (if dev tools are open and the button <span class="devtools" style="background-position:-264px -4px"></span> is "on")
|
3. An error (if dev tools are open and the button <span class="devtools" style="background-position:-264px -4px"></span> is "on")
|
||||||
|
|
||||||
Then the debugging process usually consists of examining variables and stepping on to see where the execution goes the wrong way.
|
Then we can examine variables and step on to see where the execution goes the wrong way.
|
||||||
|
|
||||||
There's much more power in developer tools.
|
There's much more options in developer tools than covered here. The full manual is at <https://developers.google.com/web/tools/chrome-devtools>
|
||||||
|
|
||||||
The better manual is at <https://developers.google.com/web/tools/chrome-devtools>
|
The information from this chapter is enough to begin debugging, but later, especially if you do a lot of browser stuff, please go there and look through more advanced capabilities of developer tools.
|
||||||
|
|
||||||
The information from this chapter is enough to begin debugging, but later, especially if you are doing in-browser stuff, please go there and look through more advanced capabilities of developer tools.
|
|
||||||
|
|
||||||
Oh, and also you can click at various places of dev tools and just see what's showing up. That's probably the fastest route to learn dev tools. Don't forget about the right click as well!
|
Oh, and also you can click at various places of dev tools and just see what's showing up. That's probably the fastest route to learn dev tools. Don't forget about the right click as well!
|
||||||
|
|
|
@ -38,13 +38,15 @@ if (n < 0) {
|
||||||
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
Nothing is "carved in stone" here, so let's discuss the rules in detail.
|
Now let's discuss the rules and reasons for them in detail.
|
||||||
|
|
||||||
|
Nothing is "carved in stone" here. Everything is optional and can be changed: these are coding rules, not religious dogmas.
|
||||||
|
|
||||||
### Figure brackets
|
### Figure brackets
|
||||||
|
|
||||||
In most JavaScript projects figure brackets are written on the same line. A so-called "egyptian" style. There's also a space before an opening bracket.
|
In most JavaScript projects figure brackets are written on the same line, not on the new line. A so-called "egyptian" style. There's also a space before an opening bracket.
|
||||||
|
|
||||||
A corner-case if a single-line `if/for`. Should we use brackets at all? If yes, then where?
|
An edge case is a single-line `if/for`. Should we use brackets at all? If yes, then where?
|
||||||
|
|
||||||
Here are the annotated variants, so you can judge about their readability on your own:
|
Here are the annotated variants, so you can judge about their readability on your own:
|
||||||
|
|
||||||
|
@ -64,9 +66,9 @@ if (n < 0) {
|
||||||
-->
|
-->
|
||||||

|

|
||||||
|
|
||||||
As a summary, for a really short code one line is acceptable: like `if (cond) return null`.
|
As a summary:
|
||||||
|
- For a really short code one line is acceptable: like `if (cond) return null`.
|
||||||
But a separate line for each statement in brackets is usually better.
|
- But a separate line for each statement in brackets is usually better.
|
||||||
|
|
||||||
### Line length
|
### Line length
|
||||||
|
|
||||||
|
@ -80,7 +82,7 @@ There are two types of indents:
|
||||||
|
|
||||||
- **A horizontal indent: 2(4) spaces.**
|
- **A horizontal indent: 2(4) spaces.**
|
||||||
|
|
||||||
A horizantal identation is made using either 2 or 4 spaces or the "Tab" symbol. Which one to choose is a kind of an old holy war. Spaces are a little more common nowadays.
|
A horizantal identation is made using either 2 or 4 spaces or the "Tab" symbol. Which one to choose is a kind of an old holy war. Spaces are more common nowadays.
|
||||||
|
|
||||||
One of advantages of spaces over tabs is that they allow more flexible configurations of indents than the "Tab" symbol.
|
One of advantages of spaces over tabs is that they allow more flexible configurations of indents than the "Tab" symbol.
|
||||||
|
|
||||||
|
@ -97,7 +99,7 @@ There are two types of indents:
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- **A vertical indent, line breaks for splitting the code in logical blocks.**
|
- **A vertical indent: empty lines for splitting the code in logical blocks.**
|
||||||
|
|
||||||
Even a single function can often be divided in logical blocks. In the example below, the initialization of variables, the main loop and returning the result are split vertically:
|
Even a single function can often be divided in logical blocks. In the example below, the initialization of variables, the main loop and returning the result are split vertically:
|
||||||
|
|
||||||
|
@ -113,7 +115,7 @@ There are two types of indents:
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Insert an additional line break where it helps to make the code more readable. There should not be more than 9 lines of code without a vertical indentation.
|
Insert an extra newline where it helps to make the code more readable. There should not be more than 9 lines of code without a vertical indentation.
|
||||||
|
|
||||||
### A semicolon
|
### A semicolon
|
||||||
|
|
||||||
|
@ -121,7 +123,7 @@ A semicolons should be after each statement. Even if could possibly be skipped.
|
||||||
|
|
||||||
There are languages where a semicolon is truly optional. It's rarely used there.
|
There are languages where a semicolon is truly optional. It's rarely used there.
|
||||||
|
|
||||||
But in JavaScript a line break is sometimes interpreted as a semicolon and sometimes not. That leaves a place for programming errors, so semicolons should be at place.
|
But in JavaScript there are few cases when a line break is sometimes not interpreted as a semicolon. That leaves a place for programming errors, so semicolons should be at place.
|
||||||
|
|
||||||
### Nesting levels
|
### Nesting levels
|
||||||
|
|
||||||
|
@ -189,7 +191,7 @@ function pow(x, n) {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
...But the second one is more readable, because the "edge case" is handled early on, and then we have the "main" code flow, without an additional nesting.
|
...But the second one is more readable, because the "edge case" of `n < 0` is handled early on, and then we have the "main" code flow, without an additional nesting.
|
||||||
|
|
||||||
## Functions below the code
|
## Functions below the code
|
||||||
|
|
||||||
|
@ -238,7 +240,7 @@ If you are writing several "helper" functions and the code to use them, then the
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
3. Mixed, a function is described when it's first used.
|
3. Mixed, a function is described where it's first used.
|
||||||
|
|
||||||
Most of time, the second variant is preferred.
|
Most of time, the second variant is preferred.
|
||||||
|
|
||||||
|
@ -246,11 +248,11 @@ That's because when reading a code, we first want to know "what it does". If the
|
||||||
|
|
||||||
## Style guides
|
## Style guides
|
||||||
|
|
||||||
There are many peculiar details in the code style.
|
There are many details in the code style.
|
||||||
|
|
||||||
As the team becomes bigger, a common agreement on them becomes the "team style guide".
|
As the team becomes bigger, a common agreement on them becomes the "team style guide".
|
||||||
|
|
||||||
There are many open style guides, so there we could just accept the one we like the most.
|
There are many open style guides, for instance:
|
||||||
|
|
||||||
- [Google JavaScript Style Guide](https://google.github.io/styleguide/jsguide.html)
|
- [Google JavaScript Style Guide](https://google.github.io/styleguide/jsguide.html)
|
||||||
- [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript)
|
- [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript)
|
||||||
|
@ -258,17 +260,17 @@ There are many open style guides, so there we could just accept the one we like
|
||||||
|
|
||||||
There exist more there in the wild.
|
There exist more there in the wild.
|
||||||
|
|
||||||
As you become more mature in JavaScript programming, you might want to read them all to pick up the common principles.
|
You can browse them and choose something as a base. As you become more mature in JavaScript programming, you might want to read them all to pick up the common principles.
|
||||||
|
|
||||||
## Style checkers
|
## Style checkers
|
||||||
|
|
||||||
There are great tools that can check the code style automatically. They are called "linters".
|
There are tools that can check the code style automatically. They are called "linters".
|
||||||
|
|
||||||
Please note, they not only check the style, but sometimes help to find bugs, like a typo in variable name or a function.
|
The great thing about them is that style-checking also finds some bugs, like a typo in variable name or a function.
|
||||||
|
|
||||||
So it's really beneficial to install one. Even if you don't want to stick to a "code style". They help to find typos -- and that's already good enough.
|
So it's recommended to install one, even if you don't want to stick to a "code style". They help to find typos -- and that's already good enough.
|
||||||
|
|
||||||
Most well known are:
|
Most well-known tools are:
|
||||||
|
|
||||||
- [JSLint](http://www.jslint.com/) -- one of the oldest open-source solutions.
|
- [JSLint](http://www.jslint.com/) -- one of the oldest open-source solutions.
|
||||||
- [JSHint](http://www.jshint.com/) -- the more "featured" variant of JSLint.
|
- [JSHint](http://www.jshint.com/) -- the more "featured" variant of JSLint.
|
||||||
|
@ -278,7 +280,7 @@ All of them can do the job. The author uses [ESLint](http://eslint.org/).
|
||||||
|
|
||||||
Here are simple steps to start using it:
|
Here are simple steps to start using it:
|
||||||
|
|
||||||
1. Install [Node.JS](https://nodejs.org/), necessary to run them.
|
1. Install [Node.JS](https://nodejs.org/).
|
||||||
2. Install eslint: `npm i -g eslint` (npm is Node.JS package installer).
|
2. Install eslint: `npm i -g eslint` (npm is Node.JS package installer).
|
||||||
3. Create a config file `.eslintrc` in your JavaScript project (the dot at the start is mandatory).
|
3. Create a config file `.eslintrc` in your JavaScript project (the dot at the start is mandatory).
|
||||||
|
|
||||||
|
@ -309,4 +311,4 @@ All syntax rules from this chapter and the style guides aim to increase readabil
|
||||||
|
|
||||||
All of them are debatable.
|
All of them are debatable.
|
||||||
|
|
||||||
When we think about "how to write better?", the sole criterion is "what makes the code more readable and easier to understand? what helps to evade errors?" The answer helps to pick up best practices. And maybe to abandon some in case if they don't contribute.
|
When we think about "how to write better?", the sole criterion is "what makes the code more readable and easier to understand? what helps to evade errors?"
|
||||||
|
|
|
@ -2,11 +2,11 @@
|
||||||
|
|
||||||
Comments are generally a good thing. But novices in programming generally get that wrong. They write comments explaining "what is going on in the code".
|
Comments are generally a good thing. But novices in programming generally get that wrong. They write comments explaining "what is going on in the code".
|
||||||
|
|
||||||
**But the amount of such "explanatory" comments should be minimal.**
|
But the amount of such "explanatory" comments should be minimal.
|
||||||
|
|
||||||
Seriously, a good code should be easy to understand without them.
|
Seriously, a good code should be easy to understand without them.
|
||||||
|
|
||||||
There's a great rule about that: "if the code is not clear without a comment, then may be it should be rewritten instead".
|
There's a great rule about that: "if the code is so unclear that it requires a comment, then maybe it should be rewritten instead".
|
||||||
|
|
||||||
[cut]
|
[cut]
|
||||||
|
|
||||||
|
@ -19,10 +19,10 @@ function showPrimes(n) {
|
||||||
|
|
||||||
*!*
|
*!*
|
||||||
// check if i is a prime number
|
// check if i is a prime number
|
||||||
*/!*
|
|
||||||
for (let j = 2; j < i; j++) {
|
for (let j = 2; j < i; j++) {
|
||||||
if (i % j == 0) continue nextPrime;
|
if (i % j == 0) continue nextPrime;
|
||||||
}
|
}
|
||||||
|
*/!*
|
||||||
|
|
||||||
alert(i);
|
alert(i);
|
||||||
}
|
}
|
||||||
|
@ -96,14 +96,14 @@ function addJuice(container) {
|
||||||
|
|
||||||
That's readable without comments. And also the code structure is better when split. It's clear what every function does, what it takes and what it returns.
|
That's readable without comments. And also the code structure is better when split. It's clear what every function does, what it takes and what it returns.
|
||||||
|
|
||||||
In reality, we can't totally evade "explanatory" comments. There are complex algorithms. And there are smart code tweaks made for optimization. But generally we should try to keep the code as simple as possible, and apply those only when needed.
|
In reality, we can't totally evade "explanatory" comments. There are complex algorithms. And there are smart "tweaks" for purposes of optimization. But generally we should try to keep the code simple and self-descriptive.
|
||||||
|
|
||||||
## Good comments
|
## Good comments
|
||||||
|
|
||||||
Which comments are good?
|
Which comments are good?
|
||||||
|
|
||||||
Describe the architecture
|
Describe the architecture
|
||||||
: That's the list of components, how they interact, what's the control flow in various situations... In short -- the bird's eye view of the code. There's a special diagram language [UML](http://wikipedia.org/wiki/Unified_Modeling_Language) for high-level architecture diagrams. Definitely worth studying.
|
: Provide a high-level overview of components, how they interact, what's the control flow in various situations... In short -- the bird's eye view of the code. There's a special diagram language [UML](http://wikipedia.org/wiki/Unified_Modeling_Language) for high-level architecture diagrams. Definitely worth studying.
|
||||||
|
|
||||||
Document a function usage
|
Document a function usage
|
||||||
: There's a special syntax [JSDoc](http://en.wikipedia.org/wiki/JSDoc) to document a function: usage, parameters, returned value.
|
: There's a special syntax [JSDoc](http://en.wikipedia.org/wiki/JSDoc) to document a function: usage, parameters, returned value.
|
||||||
|
@ -122,7 +122,7 @@ Document a function usage
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Such comments allow to understand the purpose of the function and use it the right way. Even without looking in its code.
|
Such comments allow to understand the purpose of the function and use it the right way without looking in its code.
|
||||||
|
|
||||||
By the way, many editors like [WebStorm](https://www.jetbrains.com/webstorm/) can understand them as well and use them to provide autocomplete and some automatic code-checking.
|
By the way, many editors like [WebStorm](https://www.jetbrains.com/webstorm/) can understand them as well and use them to provide autocomplete and some automatic code-checking.
|
||||||
|
|
||||||
|
@ -134,16 +134,14 @@ Why the task is solved this way?
|
||||||
If there are many ways to solve the task, why this one? Especially when it's not the most obvious one.
|
If there are many ways to solve the task, why this one? Especially when it's not the most obvious one.
|
||||||
|
|
||||||
Without such comments the following situation is possible:
|
Without such comments the following situation is possible:
|
||||||
1. You (or your colleague) open the code written some time ago, and see it's "suboptimal".
|
1. You (or your colleague) open the code written some time ago, and see that it's "suboptimal".
|
||||||
2. You think: "How stupid I was then, and how much smarter I'm now", and rewrite using the "more obvious and correct" variant.
|
2. You think: "How stupid I was then, and how much smarter I'm now", and rewrite using the "more obvious and correct" variant.
|
||||||
3. ...The urge to rewrite was good. But in the process you see that the "more obvious" solution is actually lacking. Hopefully, you revert to the correct variant, but the time was spent.
|
3. ...The urge to rewrite was good. But in the process you see that the "more obvious" solution is actually lacking. You even dimly remember why, because you already tried it long ago. You revert to the correct variant, but the time was wasted.
|
||||||
|
|
||||||
Comments that explain the solution are very important. They help to understand what happens and continue development the right way.
|
Comments that explain the solution are very important. They help to continue development the right way.
|
||||||
|
|
||||||
Any subtle features of the code? Where they are used?
|
Any subtle features of the code? Where they are used?
|
||||||
: If the code has anything subtle, it's definitely worth commenting.
|
: If the code has anything subtle and counter-obvious, it's definitely worth commenting.
|
||||||
|
|
||||||
One of signs of a good developer is his comments. Good comments allow to maintain the code well, return to it after a long delay and use features more effectively.
|
|
||||||
|
|
||||||
## Style guides
|
## Style guides
|
||||||
|
|
||||||
|
@ -212,6 +210,8 @@ For that reason even if you're not concerned about styles, using a linter is rea
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|
||||||
Code style is important, especially in the team. When other people look at your code, the impression is largely defined by the style. Good code is easier to read and understand.
|
One of signs of a good developer is his comments. Good comments allow to maintain the code well, return to it after a long delay and use features more effectively.
|
||||||
|
|
||||||
|
Code style is important too, especially in the team. When other people look at your code, the impression is largely defined by the style. Good code is easier to read and understand.
|
||||||
|
|
||||||
Speaking about style rules: quotes, spaces etc, we should keep in mind that all of them are good *only* if they make the code better. More readable, easier to maintain. That's the main thing to keep in mind when choosing the style or discussing which one is better.
|
Speaking about style rules: quotes, spaces etc, we should keep in mind that all of them are good *only* if they make the code better. More readable, easier to maintain. That's the main thing to keep in mind when choosing the style or discussing which one is better.
|
||||||
|
|
|
@ -1,27 +1,31 @@
|
||||||
# How to write bad code?
|
# How to write bad code?
|
||||||
|
|
||||||
Programmer ninjas of the past used these tricks to make code maintainers cry. Code review gurus search for signs of them in test tasks. Novice developers sometimes use them even better than programmer ninjas (indeed there's power in being a newbie!).
|
Programmer ninjas of the past used these tricks to make code maintainers cry. Code review gurus look for them in test tasks. Novice developers sometimes use them even better than programmer ninjas.
|
||||||
|
|
||||||
Read them carefully and find who you are -- a ninja, a novice, or maybe a code reviewer?
|
Read them carefully and find out who you are -- a ninja, a novice, or maybe a code reviewer?
|
||||||
|
|
||||||
[cut]
|
[cut]
|
||||||
|
|
||||||
|
```warn header="Irony detected"
|
||||||
|
These are rules of writing bad code. Just... You know, some people miss the point.
|
||||||
|
```
|
||||||
|
|
||||||
## Brevity is the soul of wit
|
## Brevity is the soul of wit
|
||||||
|
|
||||||
Make the code as short as possible. Show how smart you are.
|
Make the code as short as possible. Show how smart you are.
|
||||||
|
|
||||||
Let subtle language features guide you.
|
Let subtle language features guide you.
|
||||||
|
|
||||||
For instance, take a look at the ternary operator `'?'`:
|
For instance, take a look at this ternary operator `'?'`:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
// taken from a well-known javascript library
|
// taken from a well-known javascript library
|
||||||
i = i ? i < 0 ? Math.max(0, len + i) : i : 0;
|
i = i ? i < 0 ? Math.max(0, len + i) : i : 0;
|
||||||
```
|
```
|
||||||
|
|
||||||
The developer who comes across such line and tries to understand the value of `i` will probably come to you seeking for an answer.
|
Cool, right? If you write like that, the developer who comes across this line and tries to understand what is the value of `i` will probably have a merry time. Then come to you, seeking for an answer.
|
||||||
|
|
||||||
Tell him that the shorter is the better. Initiate him into the paths of ninja. Don't forget to give him [Tao Te Ching](http://www.w66.eu/elib/html/ttk.html).
|
Tell him that shorter is always better. Initiate him into the paths of ninja.
|
||||||
|
|
||||||
## One-letter variables
|
## One-letter variables
|
||||||
|
|
||||||
|
@ -32,15 +36,15 @@ completed.
|
||||||
|
|
||||||
Another way to code faster (and much worse!) is to use single-letter variable names everywhere. Like `a`, `b` or `c`.
|
Another way to code faster (and much worse!) is to use single-letter variable names everywhere. Like `a`, `b` or `c`.
|
||||||
|
|
||||||
That makes the variable blend in the code like a real ninja in the forest. No one will be able to find it using the "search" of the editor. And even if someone does, he won't be able to "decipher" what it means.
|
A short variable disappears in the code like a real ninja in the forest. No one will be able to find it using the "search" of the editor. And even if someone does, he won't be able to "decipher" what the name `a` or `b` means.
|
||||||
|
|
||||||
...But there's an exception. A real ninja will never use `i` as the counter in a `"for"` loop. Anywhere, but not there. Look, there's so much more exotic letters. For instance, `x` or `y`.
|
...But there's an exception. A real ninja will never use `i` as the counter in a `"for"` loop. Anywhere, but not here. Look around, there are so much more exotic letters. For instance, `x` or `y`.
|
||||||
|
|
||||||
The approach is even more effective when the loop body takes 1-2 pages (make it longer? yes, if you can). Then it becomes impossible to guess that the variable is the loop counter, when looking in the middle of the code.
|
An exotic variable as a loop counter is especially cool if the loop body takes 1-2 pages (make it longer if you can). Then if someone looks deep inside the loop, he won't be able to figure out fast that the variable is the loop counter.
|
||||||
|
|
||||||
## Can't one-letter? Then shorten
|
## Use abbreviations
|
||||||
|
|
||||||
If the team rules forbid to use one-letter and vague names -- shorten them.
|
If the team rules forbid to use one-letter and vague names -- shorten them, make abbreviations.
|
||||||
|
|
||||||
Like this:
|
Like this:
|
||||||
|
|
||||||
|
@ -49,7 +53,7 @@ Like this:
|
||||||
- `browser` -> `brsr`.
|
- `browser` -> `brsr`.
|
||||||
- ...etc
|
- ...etc
|
||||||
|
|
||||||
Only the one with a truly good intuition will be able to understand all such names. Try to shorten everything. Only a worthy person will be able to uphold development of such code.
|
Only the one with a truly good intuition will be able to understand all such names. Try to shorten everything. Only a worthy person will be able to uphold the development of such code.
|
||||||
|
|
||||||
## Soar high. Be abstract.
|
## Soar high. Be abstract.
|
||||||
|
|
||||||
|
@ -62,31 +66,29 @@ The great image has no form.
|
||||||
|
|
||||||
While choosing a name try to use the most abstract word. Like `obj`, `data`, `value`, `item`, `elem` and so on.
|
While choosing a name try to use the most abstract word. Like `obj`, `data`, `value`, `item`, `elem` and so on.
|
||||||
|
|
||||||
- **The ideal variable for a variable is `data`.** Use it everywhere where you can. Indeed, every variable holds *data*, right?
|
- **The ideal name for a variable is `data`.** Use it everywhere where you can. Indeed, every variable holds *data*, right?
|
||||||
|
|
||||||
...But what to do if `data` is already taken? Try `value`, it's also universal. A variable always has a *value*, correct?
|
...But what to do if `data` is already taken? Try `value`, it's also universal. A variable always has a *value*, correct?
|
||||||
|
|
||||||
Taken as well? An experienced ninja should find a way.
|
- **Name the variable by its type: `str`, `num`...**
|
||||||
|
|
||||||
- **Name the variable by its type: `str`, `num`, `list`...**
|
|
||||||
|
|
||||||
...But will that make the code worse? Actually, yes!
|
...But will that make the code worse? Actually, yes!
|
||||||
|
|
||||||
From the one hand, the variable name still means something. It says what's inside the variable: a string, a number or an list. But when a person unfamiliar to the Dao will try to understand the code -- he'll be surprised to see that there's actually no information at all!
|
From one hand, the variable name still means something. It says what's inside the variable: a string, a number or something else. But when an outsider tries to understand the code -- he'll be surprised to see that there's actually no information at all!
|
||||||
|
|
||||||
Actually, the value type is easy to see by debugging. But what's the meaning of the variable? Which string/number it stores? There's just no way to figure out without a good meditation!
|
Actually, the value type is easy to see by debugging. But what's the meaning of the variable? Which string/number it stores? There's just no way to figure out without a good meditation!
|
||||||
|
|
||||||
- **...But what if there's no more such names?** Just add a letter: `item1, item2, elem5, data1`...
|
- **...But what if there are no more such names?** Just add a letter: `item1, item2, elem5, data1`...
|
||||||
|
|
||||||
## Attention test
|
## Attention test
|
||||||
|
|
||||||
Only a truly attentive programmer should be able to understand the code. But how to check that?
|
Only a truly attentive programmer should be able to understand the code. But how to check that?
|
||||||
|
|
||||||
**One of the ways -- is to use similar variable names, like `date` and `data`.**
|
**One of the ways -- use similar variable names, like `date` and `data`.**
|
||||||
|
|
||||||
Mix them where you can.
|
Mix them where you can.
|
||||||
|
|
||||||
A quick read of such code becomes impossible. And when there's a typo... Ummm... We're stuck for long, time to drink some tea.
|
A quick read of such code becomes impossible. And when there's a typo... Ummm... We're stuck for long, time to drink tea.
|
||||||
|
|
||||||
|
|
||||||
## Smart synonyms
|
## Smart synonyms
|
||||||
|
@ -95,21 +97,19 @@ A quick read of such code becomes impossible. And when there's a typo... Ummm...
|
||||||
The hardest thing of all is to find a black cat in a dark room, especially if there is no cat.
|
The hardest thing of all is to find a black cat in a dark room, especially if there is no cat.
|
||||||
```
|
```
|
||||||
|
|
||||||
Don't be bored. Use *similar* names for *same* things.
|
Use *similar* names for *same* things, that makes life more interesting and shows your creativity to the public.
|
||||||
|
|
||||||
For instance, the function prefixes. If a function shows something on the screen -- start it with `display..` (like `displayMessage`), and the similar function that shows a question should start with `show...` (like `showName`).
|
For instance, the function prefixes. If a function shows a message on the screen -- start it with `display…`, like `displayMessage`. And then if another function shows something else, like a user name, start it with `show…` (like `showName`).
|
||||||
|
|
||||||
**Insinuate that there's a subtle difference difference between such functions, while there is none.**
|
Insinuate that there's a subtle difference difference between such functions, while there is none.
|
||||||
|
|
||||||
Make a pact with fellow ninjas of the team: if John starts "showing" functions with `display...` in his code, then Peter could use `render..`, and Ann -- `paint...`.
|
Make a pact with fellow ninjas of the team: if John starts "showing" functions with `display...` in his code, then Peter could use `render..`, and Ann -- `paint...`. Note how more interesting and diverse the code became.
|
||||||
|
|
||||||
...And now the hat trick!
|
...And now the hat trick!
|
||||||
|
|
||||||
**For two functions with important differences -- use the same word!**
|
For two functions with important differences -- use the same prefix!
|
||||||
|
|
||||||
For instance, the function `printPage(page)` will use a printer. And the function `printText(text)` will put the text on-screen.
|
For instance, the function `printPage(page)` will use a printer. And the function `printText(text)` will put the text on-screen. Let an unfamiliar reader think well over things: "Where does `printMessage(message)` put the message? To a printer or on the screen?". To make it really shine, `printMessage(message)` should output it in the new window!
|
||||||
|
|
||||||
Let an unfamiliar reader think well over things: "Where does `printMessage(message)` put the message?". To make it really shine, `printMessage(message)` should output it in the new window!
|
|
||||||
|
|
||||||
## Reuse names
|
## Reuse names
|
||||||
|
|
||||||
|
@ -126,9 +126,11 @@ Instead, reuse existing names. Just write new values into them.
|
||||||
|
|
||||||
In a function try to use only variables passed as parameters.
|
In a function try to use only variables passed as parameters.
|
||||||
|
|
||||||
That would make it impossible to identify what's exactly in the variable *now*. And also where it comes from. A person without a skill would have to analyze the code line-by-line and track the changes through every code branch.
|
That would make it impossible to identify what's exactly in the variable *now*. And also where it comes from. A person with weak intuition would have to analyze the code line-by-line and track the changes through every code branch.
|
||||||
|
|
||||||
**An advanced variant of the approach is to covertly (!) replace the value with something alike, for instance:**
|
**An advanced variant of the approach is to covertly (!) replace the value with something alike in the middle of a loop or a function.**
|
||||||
|
|
||||||
|
For instance:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
function ninjaFunction(elem) {
|
function ninjaFunction(elem) {
|
||||||
|
@ -136,13 +138,13 @@ function ninjaFunction(elem) {
|
||||||
|
|
||||||
elem = clone(elem);
|
elem = clone(elem);
|
||||||
|
|
||||||
// 20 more lines, now working with the new elem!
|
// 20 more lines, now working with the clone of the elem!
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
A fellow programmer who wants to work with `elem` in the second half of the function will be surprised... Only during the debugging, after examining the code he will find out that he worked with he's working with the clone!
|
A fellow programmer who wants to work with `elem` in the second half of the function will be surprised... Only during the debugging, after examining the code he will find out that he's working with a clone!
|
||||||
|
|
||||||
Deadly effective even against an experienced ninja. Met in the code regularly.
|
Deadly effective even against an experienced ninja. Seen in code regularly.
|
||||||
|
|
||||||
## Underscores for fun
|
## Underscores for fun
|
||||||
|
|
||||||
|
@ -154,9 +156,9 @@ A smart ninja puts underscores at one spot of code and evades them at other plac
|
||||||
|
|
||||||
## Show your love
|
## Show your love
|
||||||
|
|
||||||
Let everyone see how magnificent your entities are! Names like `superElement`, `megaFrame` and `niceItem` will definitely enlighten the reader.
|
Let everyone see how magnificent your entities are! Names like `superElement`, `megaFrame` and `niceItem` will definitely enlighten a reader.
|
||||||
|
|
||||||
Indeed, from one hand, something is written: `super..`, `mega..`, `nice..` But from the other hand -- that brings no details. The reader may decide to look for a hidden meaning and meditate for an hour or two.
|
Indeed, from one hand, something is written: `super..`, `mega..`, `nice..` But from the other hand -- that brings no details. A reader may decide to look for a hidden meaning and meditate for an hour or two.
|
||||||
|
|
||||||
## Overlap outer variables
|
## Overlap outer variables
|
||||||
|
|
||||||
|
@ -180,33 +182,15 @@ function render() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
A programmer who jumps inside the `render` will probably miss to notice that the local `user` overlaps the outer one. Then he'll try to work with it assuming that it's the resulf of `authenticateUser()`... The trap is sprung! Hello, debugger...
|
A programmer who jumps inside the `render` will probably miss to notice that there's a local `user` shadowing the outer one.
|
||||||
|
|
||||||
## Powerful functions!
|
Then he'll try to work with `user` it assuming that it's the external variable, the result of `authenticateUser()`... The trap is sprung! Hello, debugger...
|
||||||
|
|
||||||
```quote author="Laozi (Tao Te Ching)"
|
|
||||||
The great Tao flows everywhere,<br>
|
|
||||||
both to the left and to the right.
|
|
||||||
```
|
|
||||||
|
|
||||||
Don't limit the function by what's written in its name. Be wider.
|
|
||||||
|
|
||||||
For instance, a function `validateEmail(email)` could, besides checking the email for correctness, to show an error message and ask to re-enter the email.
|
|
||||||
|
|
||||||
**Add at least two more actions to the main purpose of the function.**
|
|
||||||
|
|
||||||
They should not be obvious from the function name. A true ninja coder will make them not obvious from the code as well.
|
|
||||||
|
|
||||||
**Joining several actions into one protects your code from reuse.**
|
|
||||||
|
|
||||||
Imagine, another developer wants only to check the email, and not output any message. Your function `validateEmail(email)` that does both will not suit him. So he will not break your meditation by asking anything about it.
|
|
||||||
|
|
||||||
|
|
||||||
## Side-effects everywhere!
|
## Side-effects everywhere!
|
||||||
|
|
||||||
There are functions that look like they don't change anything. Like `isReady()`, `checkPermission()`, `findTags()`... They are assumed to carry out calculations, find and return the data, without changing anything outside of them. That's called "no side-effects".
|
There are functions that look like they don't change anything. Like `isReady()`, `checkPermission()`, `findTags()`... They are assumed to carry out calculations, find and return the data, without changing anything outside of them. That's called "no side-effects".
|
||||||
|
|
||||||
|
|
||||||
**A really beautiful trick -- is to add a "useful" action to them, besides the main task.**
|
**A really beautiful trick -- is to add a "useful" action to them, besides the main task.**
|
||||||
|
|
||||||
The expression of dazed surprise on the face of your colleague when he see a function named `is..`, `check..` or `find...` changing something -- will definitely broaden your boundaries of reason.
|
The expression of dazed surprise on the face of your colleague when he see a function named `is..`, `check..` or `find...` changing something -- will definitely broaden your boundaries of reason.
|
||||||
|
@ -217,6 +201,24 @@ Show your original thinking! Let the call of `checkPermission` return not `true/
|
||||||
|
|
||||||
Those developers who try to write `if (checkPermission(..))`, will wonder why it doesn't work. Tell them: "Read the docs!". And give this article.
|
Those developers who try to write `if (checkPermission(..))`, will wonder why it doesn't work. Tell them: "Read the docs!". And give this article.
|
||||||
|
|
||||||
|
|
||||||
|
## Powerful functions!
|
||||||
|
|
||||||
|
```quote author="Laozi (Tao Te Ching)"
|
||||||
|
The great Tao flows everywhere,<br>
|
||||||
|
both to the left and to the right.
|
||||||
|
```
|
||||||
|
|
||||||
|
Don't limit the function by what's written in its name. Be broader.
|
||||||
|
|
||||||
|
For instance, a function `validateEmail(email)` could (besides checking the email for correctness) show an error message and ask to re-enter the email.
|
||||||
|
|
||||||
|
Additional actions should not be obvious from the function name. A true ninja coder will make them not obvious from the code as well.
|
||||||
|
|
||||||
|
**Joining several actions into one protects your code from reuse.**
|
||||||
|
|
||||||
|
Imagine, another developer wants only to check the email, and not output any message. Your function `validateEmail(email)` that does both will not suit him. So he won't break your meditation by asking anything about it.
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|
||||||
All "pieces of advice" above are from the real code... Sometimes, written by experienced developers. Maybe even more experienced than you are ;)
|
All "pieces of advice" above are from the real code... Sometimes, written by experienced developers. Maybe even more experienced than you are ;)
|
||||||
|
|
|
@ -21,4 +21,4 @@ it("Raises x to the power n", function() {
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
P.S. Syntactically it's correct and passes.
|
P.S. Syntactically the test is correct and passes.
|
||||||
|
|
|
@ -1,32 +1,32 @@
|
||||||
# Automated testing with mocha
|
# Automated testing with mocha
|
||||||
|
|
||||||
Automated testing will be used further on in tasks. It's actually a part of the "educational minimum" of a developer.
|
Automated testing will be used further in tasks.
|
||||||
|
|
||||||
|
It's actually a part of the "educational minimum" of a developer.
|
||||||
|
|
||||||
[cut]
|
[cut]
|
||||||
|
|
||||||
## Why we need tests?
|
## Why we need tests?
|
||||||
|
|
||||||
When we write a function, we usually can imagine what it should do, which parameters yield which results.
|
When we write a function, we usually can imagine what it should do: which parameters give which results.
|
||||||
|
|
||||||
During the development, we can check the function by running it and comparing the outcome with the expected one. For instance, we can do it in the console.
|
During the development, we can check the function by running it and comparing the outcome with the expected one. For instance, we can do it in the console.
|
||||||
|
|
||||||
If something's wrong -- then fix the code, run again, check the result -- and so on till it works.
|
If something's wrong -- then we fix the code, run again, check the result -- and so on till it works.
|
||||||
|
|
||||||
But such manual "re-runs" are rather imperfect.
|
But such manual "re-runs" are imperfect.
|
||||||
|
|
||||||
**When testing a code manually -- it's easy to miss something.**
|
**When testing a code by manual re-runs -- it's easy to miss something.**
|
||||||
|
|
||||||
For instance, we're composing a function `f`. Wrote some code, testing: `f(1)` works, but `f(2)` doesn't work. We fix the code and now `f(2)` works. Looks complete? But we forgot to re-test `f(1)`. That may lead to an error.
|
For instance, we're creating a function `f`. Wrote some code, testing: `f(1)` works, but `f(2)` doesn't work. We fix the code and now `f(2)` works. Looks complete? But we forgot to re-test `f(1)`. That may lead to an error.
|
||||||
|
|
||||||
That's actually fair. When we develop something, we keep a lot of possible use cases and mind. But it's hard to expect from programmer to check all of them manually after every change. So it becomes easy to fix one thing and break another one.
|
That's very typical. When we develop something, we keep a lot of possible use cases and mind. But it's hard to expect from programmer to check all of them manually after every change. So it becomes easy to fix one thing and break another one.
|
||||||
|
|
||||||
**Automated testing means that tests are written separately, in addition to the code. They can be executed easily and check all the main use cases.**
|
**Automated testing means that tests are written separately, in addition to the code. They can be executed easily and check all the main use cases.**
|
||||||
|
|
||||||
## Behavior Driven Development (BDD)
|
## Behavior Driven Development (BDD)
|
||||||
|
|
||||||
Let's get down to a technique named [Behavior Driven Development](http://en.wikipedia.org/wiki/Behavior-driven_development) or, in short, BDD. That approach is used among many projects.
|
Let's use a technique named [Behavior Driven Development](http://en.wikipedia.org/wiki/Behavior-driven_development) or, in short, BDD. That approach is used among many projects. BDD is not just about testing. That's more.
|
||||||
|
|
||||||
BDD is not just about testing. That's more.
|
|
||||||
|
|
||||||
**BDD is three things in one: tests AND documentation AND examples.**
|
**BDD is three things in one: tests AND documentation AND examples.**
|
||||||
|
|
||||||
|
@ -36,9 +36,9 @@ Enough words. Let's see the example.
|
||||||
|
|
||||||
Let's say we want to make a function `pow(x, n)` that raises `x` to an integer power `n`. We assume that `n≥0`.
|
Let's say we want to make a function `pow(x, n)` that raises `x` to an integer power `n`. We assume that `n≥0`.
|
||||||
|
|
||||||
That task is quite simple, there's even a `**` operator in JavaScript that can do that, but here we concentrate not on the function itself, but on the development flow, that can be applied to more complex tasks as well.
|
That task is just an example: there's `**` operator in JavaScript that can do that, but here we concentrate on the development flow that can be applied to more complex tasks as well.
|
||||||
|
|
||||||
Before creating the code of `pow`, we can imagine what the function should do and describe it using BDD.
|
Before creating the code of `pow`, we can imagine what the function should do and describe it.
|
||||||
|
|
||||||
Such description is called a *specification* or, in short, a spec, and looks like this:
|
Such description is called a *specification* or, in short, a spec, and looks like this:
|
||||||
|
|
||||||
|
@ -55,10 +55,10 @@ describe("pow", function() {
|
||||||
A spec has three main building blocks that you can see above:
|
A spec has three main building blocks that you can see above:
|
||||||
|
|
||||||
`describe("title", function() { ... })`
|
`describe("title", function() { ... })`
|
||||||
: What functionality we're describing. Uses to group "working horses" -- the `it` blocks. In our case we're describing the function `pow`.
|
: What functionality we're describing. Uses to group "workers" -- the `it` blocks. In our case we're describing the function `pow`.
|
||||||
|
|
||||||
`it("title", function() { ... })`
|
`it("title", function() { ... })`
|
||||||
: In the title of `it` we *in a human-readable way* describe the particular use case, and then goes a function that tests it.
|
: In the title of `it` we *in a human-readable way* describe the particular use case, and the second argument is a function that tests it.
|
||||||
|
|
||||||
`assert.equal(value1, value2)`
|
`assert.equal(value1, value2)`
|
||||||
: The code inside `it` block, if the implementation is correct, should execute without errors.
|
: The code inside `it` block, if the implementation is correct, should execute without errors.
|
||||||
|
@ -73,25 +73,28 @@ The flow of development usually looks like this:
|
||||||
|
|
||||||
1. An initial spec is written, with tests for the most basic functionality.
|
1. An initial spec is written, with tests for the most basic functionality.
|
||||||
2. An initial implementation is created.
|
2. An initial implementation is created.
|
||||||
3. To check whether it works, we run the testing framework [Mocha](http://mochajs.org/) together with assertion framework (we'll pick [Chai](http://chaijs.com/)), the spec and the implementation. Functions `describe` and `it` actually belong to Mocha and `assert.*` come from Chai. If there are errors, we make corrections till everything works.
|
3. To check whether it works, we run the testing framework [Mocha](http://mochajs.org/) (more details soon) that runs the spec. Errors are displayed. We make corrections till everything works.
|
||||||
4. Now we have a working initial implementation with tests.
|
4. Now we have a working initial implementation with tests.
|
||||||
5. We add more use cases to the spec, probably not yet supported by the implementations. Tests start to fail.
|
5. We add more use cases to the spec, probably not yet supported by the implementations. Tests start to fail.
|
||||||
6. Go to 3, alter the implementation till everything works, and repeat the same till the functionality is ready.
|
6. Go to 3, update the implementation till tests give no errors.
|
||||||
|
7. Repeat steps 3-6 till the functionality is ready.
|
||||||
|
|
||||||
So, the development is *iterative*. We write the spec, implement it, then write more tests, make sure they work etc. At the end we have both a working implementation and tests for it.
|
So, the development is *iterative*. We write the spec, implement it, make sure tests pass, then write more tests, make sure they work etc. At the end we have both a working implementation and tests for it.
|
||||||
|
|
||||||
In our case, the first step is complete: we have an initial spec. So let's make an implementation. But before that let's make a "zero" run of the spec, just to see that tests are working (they will all fail).
|
In our case, the first step is complete: we have an initial spec for `pow`. So let's make an implementation. But before that let's make a "zero" run of the spec, just to see that tests are working (they will all fail).
|
||||||
|
|
||||||
## The spec in action
|
## The spec in action
|
||||||
|
|
||||||
Here in the tutorial we'll be using the following JavaScript libraries for tests:
|
Here in the tutorial we'll be using the following JavaScript libraries for tests:
|
||||||
|
|
||||||
- [Mocha](http://mochajs.org/) -- the main framework with common testing functions including `describe` and `it`.
|
- [Mocha](http://mochajs.org/) -- the core framework: it provides common testing functions including `describe` and `it` and the main function that runs tests.
|
||||||
- [Chai](http://chaijs.com) -- the library with many assertions. It allows to use a lot of different assertions, for now we need only `assert.equal`.
|
- [Chai](http://chaijs.com) -- the library with many assertions. It allows to use a lot of different assertions, for now we need only `assert.equal`.
|
||||||
- [Sinon](http://sinonjs.org/) -- to emulate built-in functions, to spy over functions and more, we'll need it much later.
|
- [Sinon](http://sinonjs.org/) -- a library to spy over functions, emulate built-in functions and more, we'll need it much later.
|
||||||
|
|
||||||
These libraries are suitable for both in-browser and server-side testing. Here we'll consider the browser variant.
|
These libraries are suitable for both in-browser and server-side testing. Here we'll consider the browser variant.
|
||||||
|
|
||||||
|
The full HTML page with these frameworks and `pow` spec:
|
||||||
|
|
||||||
```html src="index.html"
|
```html src="index.html"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -107,9 +110,9 @@ The result:
|
||||||
|
|
||||||
[iframe height=250 src="pow-1" border=1 edit]
|
[iframe height=250 src="pow-1" border=1 edit]
|
||||||
|
|
||||||
As of now, the tests fail. That's logical: we have an empty function code in `pow`.
|
As of now, the test fails, there's an error. That's logical: we have an empty function code in `pow`, so `pow(2,3)` returns `undefined` instead of `8`.
|
||||||
|
|
||||||
For the future, let's note that for browser there exist advanced test-runners, like [karma](https://karma-runner.github.io/) and others. So it's generally not a problem to setup environment for many tests. But right now, for a single function, our test page is more than enough.
|
For future, let's note that for there are advanced test-runners, like [karma](https://karma-runner.github.io/) and others. So it's generally not a problem to setup many different tests.
|
||||||
|
|
||||||
## Initial implementation
|
## Initial implementation
|
||||||
|
|
||||||
|
@ -129,7 +132,7 @@ Wow, now it works!
|
||||||
|
|
||||||
What we've done -- is definitely a cheat. The function does not work: an attempt to calculate `pow(3,4)` would give an incorrect result, but tests pass.
|
What we've done -- is definitely a cheat. The function does not work: an attempt to calculate `pow(3,4)` would give an incorrect result, but tests pass.
|
||||||
|
|
||||||
Here we can see a typical situation: tests pass, but the function works wrong. That happens. Our spec is imperfect. We need to add more use cases to it.
|
...But the situation is quite typical, it happens in practice. Tests pass, but the function works wrong. Our spec is imperfect. We need to add more use cases to it.
|
||||||
|
|
||||||
Let's add one more test to see if `pow(3, 4) = 81`.
|
Let's add one more test to see if `pow(3, 4) = 81`.
|
||||||
|
|
||||||
|
@ -167,7 +170,7 @@ We can select one of two ways to organize the test here:
|
||||||
|
|
||||||
The principal difference is that when `assert` triggers an error, the `it` block immediately terminates. So, in the first variant if the first `assert` fails, then we'll never see the result of the second `assert`.
|
The principal difference is that when `assert` triggers an error, the `it` block immediately terminates. So, in the first variant if the first `assert` fails, then we'll never see the result of the second `assert`.
|
||||||
|
|
||||||
**Making tests separate is useful to get more information about what's going on.**
|
Making tests separate is useful to get more information about what's going on, so the second variant is better.
|
||||||
|
|
||||||
And besides that, there's one more rule good to follow.
|
And besides that, there's one more rule good to follow.
|
||||||
|
|
||||||
|
@ -175,13 +178,13 @@ And besides that, there's one more rule good to follow.
|
||||||
|
|
||||||
If we look at the test and see two independent checks in it -- better to split it into two simpler ones.
|
If we look at the test and see two independent checks in it -- better to split it into two simpler ones.
|
||||||
|
|
||||||
For these reasons, the second variant is preferable.
|
So let's continue with the second variant.
|
||||||
|
|
||||||
The result:
|
The result:
|
||||||
|
|
||||||
[iframe height=250 src="pow-2" edit border="1"]
|
[iframe height=250 src="pow-2" edit border="1"]
|
||||||
|
|
||||||
As we could expect, the second test failed. Sure, the function returns `8` while the `assert` expects `27`.
|
As we could expect, the second test failed. Sure, our function always returns `8`, while the `assert` expects `27`.
|
||||||
|
|
||||||
## Improving the implementation
|
## Improving the implementation
|
||||||
|
|
||||||
|
@ -224,7 +227,7 @@ The result:
|
||||||
|
|
||||||
## Nested describe
|
## Nested describe
|
||||||
|
|
||||||
We're going to add more tests. But before that let's note that the helper function `makeTest` and `for` should be grouped together. We won't need `makeTest` in other tests, it's needed only in `for`: their common task is to check how `pow` raises into the given power.
|
We're going to add even more tests. But before that let's note that the helper function `makeTest` and `for` should be grouped together. We won't need `makeTest` in other tests, it's needed only in `for`: their common task is to check how `pow` raises into the given power.
|
||||||
|
|
||||||
Grouping is done with a nested `describe`:
|
Grouping is done with a nested `describe`:
|
||||||
|
|
||||||
|
@ -258,10 +261,10 @@ The nested `describe` defines a new "subgroup" of tests. In the output we can se
|
||||||
|
|
||||||
[iframe height=250 src="pow-4" edit border="1"]
|
[iframe height=250 src="pow-4" edit border="1"]
|
||||||
|
|
||||||
In the future we can add more `it` and `describe` with helper functions of their own.
|
In the future we can add more `it` and `describe` on the top level with helper functions of their own, they won't see `makeTest`.
|
||||||
|
|
||||||
````smart header="`before/after` and `beforeEach/afterEach`"
|
````smart header="`before/after` and `beforeEach/afterEach`"
|
||||||
A `describe` can have `before/after` functions that execute before/after running tests, and also `beforeEach/afterEach` functions that execute before/after *every* `it`.
|
We can setup `before/after` functions that execute before/after running tests, and also `beforeEach/afterEach` functions that execute before/after *every* `it`.
|
||||||
|
|
||||||
For instance:
|
For instance:
|
||||||
|
|
||||||
|
@ -280,20 +283,20 @@ describe("test", function() {
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
The `=>` denotes, as you may recall, [arrow functions](info:function-expressions-arrows#arrow-functions). The running sequence will be:
|
The running sequence will be:
|
||||||
|
|
||||||
```
|
```
|
||||||
Testing started – before all tests
|
Testing started – before all tests (before)
|
||||||
Before a test – enter a test
|
Before a test – enter a test (beforeEach)
|
||||||
1
|
1
|
||||||
After a test – exit a test
|
After a test – exit a test (afterEach)
|
||||||
Before a test – enter a test
|
Before a test – enter a test (beforeEach)
|
||||||
2
|
2
|
||||||
After a test – exit a test
|
After a test – exit a test (afterEach)
|
||||||
Testing finished – after all tests
|
Testing finished – after all tests (after)
|
||||||
```
|
```
|
||||||
|
|
||||||
[edit src="beforeafter" title="Open the example in the sandbox"]
|
[edit src="beforeafter" title="Open the example in the sandbox."]
|
||||||
|
|
||||||
Usually, `beforeEach/afterEach` (`before/each`) are used to perform initialization, zero out counters or do something else between the tests (or test groups).
|
Usually, `beforeEach/afterEach` (`before/each`) are used to perform initialization, zero out counters or do something else between the tests (or test groups).
|
||||||
````
|
````
|
||||||
|
@ -369,9 +372,7 @@ Now it works, all tests pass:
|
||||||
|
|
||||||
[iframe height=300 src="pow-full" edit border="1"]
|
[iframe height=300 src="pow-full" edit border="1"]
|
||||||
|
|
||||||
[edit src="pow-full" title="Open the full final example in the sandbox"]
|
[edit src="pow-full" title="Open the full final example in the sandbox."]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|
||||||
|
@ -385,27 +386,27 @@ The spec can be used in three ways:
|
||||||
|
|
||||||
With the spec, we can safely improve, change, even rewrite the function from the scratch and make sure it still works right.
|
With the spec, we can safely improve, change, even rewrite the function from the scratch and make sure it still works right.
|
||||||
|
|
||||||
That's especially important when a function is used in many places. When we change such a function -- there's just no way to manually check if every place that uses them still works right.
|
That's especially important in large projects when a function is used in many places. When we change such a function -- there's just no way to manually check if every place that uses them still works right.
|
||||||
|
|
||||||
In practice that leads to one of the consequences:
|
Without tests, people have two ways:
|
||||||
|
|
||||||
1. Functions still get changed, refactored no matter what. And then probably users meet bugs and report them. If we can afford that.
|
1. To perform the change, no matter what. And then our users meet bugs and report them. If we can afford that.
|
||||||
2. Or people become afraid to modify such functions, if the punishment for errors is harsh. Then it becomes old, overgrown with cobwebs, no one wants to get into it.
|
2. Or people become afraid to modify such functions, if the punishment for errors is harsh. Then it becomes old, overgrown with cobwebs, no one wants to get into it, and that's not good.
|
||||||
|
|
||||||
**Automatically tested code is contrary to that!**
|
**Automatically tested code is contrary to that!**
|
||||||
|
|
||||||
If the project is covered with tests -- there's just no such problem. We can run tests and a lot of checks are made in a matter of seconds.
|
If the project is covered with tests -- there's just no such problem. We can run tests and see a lot of checks made in a matter of seconds.
|
||||||
|
|
||||||
**Besides, a well-tested code has better architecture.**
|
**Besides, a well-tested code has better architecture.**
|
||||||
|
|
||||||
Naturally, that's because it's easier to change and improve. But not only that.
|
Naturally, that's because it's easier to change and improve it. But not only that.
|
||||||
|
|
||||||
To write tests, the code should be organized in such a way that every function has a clearly described task to do, well-defined input and output. That means a good architecture from the beginning.
|
To write tests, the code should be organized in such a way that every function has a clearly described task, well-defined input and output. That means a good architecture from the beginning.
|
||||||
|
|
||||||
In real life that's sometimes not that easy. Sometimes it's difficult to write a spec before the actual code, because it's not yet clear how it should behave. But generally writing tests makes development faster and more stable.
|
In real life that's sometimes not that easy. Sometimes it's difficult to write a spec before the actual code, because it's not yet clear how it should behave. But in general writing tests makes development faster and more stable.
|
||||||
|
|
||||||
## What now?
|
## What now?
|
||||||
|
|
||||||
Later in the tutorial you will meet many tasks with tests baked-in. So you will more practical examples.
|
Later in the tutorial you will meet many tasks with tests baked-in. So you'll see more practical examples.
|
||||||
|
|
||||||
Writing tests requires good JavaScript knowledge. But we're just starting to learn it. So, to settle down everything, as of now you're not required to write tests, but you should already be able to read them even if they are a little bit more complex than in this chapter.
|
Writing tests requires good JavaScript knowledge. But we're just starting to learn it. So, to settle down everything, as of now you're not required to write tests, but you should already be able to read them even if they are a little bit more complex than in this chapter.
|
||||||
|
|
|
@ -6,8 +6,7 @@
|
||||||
<!-- add mocha framework code -->
|
<!-- add mocha framework code -->
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.js"></script>
|
||||||
<script>
|
<script>
|
||||||
// enable bdd-style testing
|
mocha.setup('bdd'); // minimal setup
|
||||||
mocha.setup('bdd');
|
|
||||||
</script>
|
</script>
|
||||||
<!-- add chai -->
|
<!-- add chai -->
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/chai/3.5.0/chai.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/chai/3.5.0/chai.js"></script>
|
||||||
|
|
|
@ -1,57 +1,57 @@
|
||||||
|
|
||||||
# Polyfills
|
# Polyfills
|
||||||
|
|
||||||
The JavaScript language steadily evolves. The new proposals get analyzed and, if they look worthy, are appended to the list at <https://tc39.github.io/ecma262/> and then progress to the [specification](http://www.ecma-international.org/publications/standards/Ecma-262.htm).
|
The JavaScript language steadily evolves. The new proposals to the language appear regularly, they are analyzed and, if considered worthy, are appended to the list at <https://tc39.github.io/ecma262/> and then progress to the [specification](http://www.ecma-international.org/publications/standards/Ecma-262.htm).
|
||||||
|
|
||||||
Each JS engine has its own idea about what to implement first. It may implement proposals that are not approved yet and fail to implement things that are already in the spec, because they are less interesting or just harder to do.
|
Teams behind JavaScript engines have their own ideas about what to implement first. It may decide to implement proposals that are in draft and postpone things that are already in the spec, because they are less interesting or just harder to do.
|
||||||
|
|
||||||
So it's quite common for an engine to implement only the part of the standard.
|
So it's quite common for an engine to implement only the part of the standard.
|
||||||
|
|
||||||
A good page to see the current state of support for language features is <https://kangax.github.io/compat-table/es6/> (remember the link to use in the future when you know the language).
|
A good page to see the current state of support for language features is <https://kangax.github.io/compat-table/es6/> (it's big, we have a lot to study yet).
|
||||||
|
|
||||||
## Babel.JS
|
## Babel.JS
|
||||||
|
|
||||||
When we use all the modern features of the language, some engines may fail to support such code. Just as it was said, not all features are implemented everywhere.
|
When we use modern features of the language, some engines may fail to support such code. Just as said, not all features are implemented everywhere.
|
||||||
|
|
||||||
Here Babel.JS comes to the rescue.
|
Here Babel.JS comes to the rescue.
|
||||||
|
|
||||||
[Babel.JS](https://babeljs.io) is a [transpiler](https://en.wikipedia.org/wiki/Source-to-source_compiler). It rewrites the modern JavaScript code into the previous standard.
|
[Babel.JS](https://babeljs.io) is a [transpiler](https://en.wikipedia.org/wiki/Source-to-source_compiler). It rewrites modern JavaScript code into the previous standard.
|
||||||
|
|
||||||
Actually, there are two parts in Babel:
|
Actually, there are two parts in Babel:
|
||||||
|
|
||||||
1. The transpiler program, which rewrites the code.
|
1. First, the transpiler program, which rewrites the code. The developer run it on his own computer. It rewrites the code into the older standard. And then the code is delivered to the website for users. Modern project build system like [webpack](http://webpack.github.io/) or [brunch](http://brunch.io/) provide means to run transpiler automatically on every code change, so that doesn't involve any time loss from our side.
|
||||||
|
|
||||||
The transpiler runs on a developer's computer. It rewrites the code, which is then bundled by a project build system (like [webpack](http://webpack.github.io/) or [brunch](http://brunch.io/)). Most build systems can support Babel easily.
|
2. Second, the polyfill.
|
||||||
|
|
||||||
2. The polyfill.
|
The transpiler rewrites the code, so syntax features are covered. But for new functions we need to a special script that implements them. JavaScript is a highly dynamic language, scripts may not just add new functions, but also modify built-in ones, so that they behave according to the modern standard.
|
||||||
|
|
||||||
For some functions we also need add a special script that should run before our scripts and introduce modern functions that the engine may not support by itself. There's a term "polyfill" for such scripts.
|
There's a term "polyfill" for scripts that "fill in" the gap and add missing implementations.
|
||||||
|
|
||||||
The two interesting variants are [babel polyfill](https://babeljs.io/docs/usage/polyfill/) that supports a lot, but is big and the [polyfill.io](http://polyfill.io) service that allows to load/construct polyfills on-demand, depending on the features we need.
|
Two interesting polyfills are:
|
||||||
|
- [babel polyfill](https://babeljs.io/docs/usage/polyfill/) that supports a lot, but is big.
|
||||||
|
- [polyfill.io](http://polyfill.io) service that allows to load/construct polyfills on-demand, depending on the features we need.
|
||||||
|
|
||||||
The transpiler and/or polyfill may be not needed if we orient towards more-or-less modern engines and don't use rarely supported features.
|
So, we need to setup the transpiler and add the polyfill for old engines to support modern features.
|
||||||
|
|
||||||
|
If we orient towards modern engines and do not use features except those supported everywhere, then we don't need to use Babel.JS.
|
||||||
|
|
||||||
## Examples in the tutorial
|
## Examples in the tutorial
|
||||||
|
|
||||||
```warn header="Browser support is required"
|
|
||||||
Examples that use modern JS will work only if your browser supports it.
|
|
||||||
```
|
|
||||||
|
|
||||||
````online
|
````online
|
||||||
Most examples are runnable at-place, like here:
|
Most examples are runnable at-place, like this:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
alert('Press the "Play" button in the upper-right corner to run');
|
alert('Press the "Play" button in the upper-right corner to run');
|
||||||
```
|
```
|
||||||
|
|
||||||
...But if it uses a feature that your browser does not support, an error is shown.
|
Examples that use modern JS will work only if your browser supports it.
|
||||||
|
```
|
||||||
|
|
||||||
That doesn't mean that the example is wrong! It's just the browser lacking the support for certain features yet.
|
```offline
|
||||||
````
|
As you're reading the offline version, examples are not runnable. But they usually work :)
|
||||||
|
```
|
||||||
|
|
||||||
[Chrome Canary](https://www.google.com/chrome/browser/canary.html) is good for more examples.
|
[Chrome Canary](https://www.google.com/chrome/browser/canary.html) is good for all examples, but other modern browsers are mostly fine too.
|
||||||
|
|
||||||
Note that on production we can use Babel to translate the code into suitable for less recent browsers, so there will be no such limitation, the code will run everywhere.
|
Note that on production we can use Babel to translate the code into suitable for less recent browsers, so there will be no such limitation, the code will run everywhere.
|
||||||
|
|
||||||
Now we can go coding, so let's choose a good code editor.
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
# Code quality
|
# Code quality
|
||||||
|
|
||||||
This chapter goes early to explain coding practices that we'll use further in the development.
|
This chapter explains coding practices that we'll use further in the development.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -98,7 +98,7 @@ That is called a "trailing" or "hanging" comma. Makes it easier to add/remove/mo
|
||||||
|
|
||||||
## Square brackets
|
## Square brackets
|
||||||
|
|
||||||
For multiword properties, the dot access won't work:
|
For multiword properties, the dot access doesn't work:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
// this would give a syntax error
|
// this would give a syntax error
|
||||||
|
|
BIN
figures.sketch
BIN
figures.sketch
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue