This commit is contained in:
Ilya Kantor 2016-03-04 19:06:22 +03:00
parent e78e527866
commit 05a93ced80
212 changed files with 3213 additions and 3968 deletions

View file

@ -12,47 +12,41 @@ Scripts are provided and executed a plain text. They don't need a special prepar
In this aspect, JavaScript is very different from another language called [Java](http://en.wikipedia.org/wiki/Java).
[smart header="Why <u>Java</u>Script?"]
```smart header="Why <u>Java</u>Script?"
When JavaScript was created, it initially had another name: "LiveScript". But Java language was very popular at that time, so it was decided that positioning a new language as a "younger brother" of Java would help.
But as it evolved, JavaScript became a fully independent language, with its own specification called [ECMAScript](http://en.wikipedia.org/wiki/ECMAScript), and now it has no relation to Java at all.
[/smart]
```
At present, JavaScript can execute not only in the browser, but also on the server, or actually on any device where a special program called [an interpreter]("http://en.wikipedia.org/wiki/Interpreter_(computing)") is installed. The execution process is called "an interpretation".
The browser has an embedded JavaScript interpreter, sometimes it's also called a "JavaScript engine" or a "JavaScript virtual machine".
Different engines have different "codenames", for example:
<ul>
<li>[V8 engine]("https://en.wikipedia.org/wiki/V8_(JavaScript_engine)") -- in Chrome and Opera.</li>
<li>[Gecko]("https://en.wikipedia.org/wiki/Gecko_(software)") -- in Firefox.</li>
<li>...There are other codenames like "Trident", "Chakra" for different versions of IE, "Nitro" and "SquirrelFish" for Safari etc.</li>
</ul>
- [V8 engine]("https://en.wikipedia.org/wiki/V8_(JavaScript_engine)") -- in Chrome and Opera.
- [Gecko]("https://en.wikipedia.org/wiki/Gecko_(software)") -- in Firefox.
- ...There are other codenames like "Trident", "Chakra" for different versions of IE, "Nitro" and "SquirrelFish" for Safari etc.
The codenames are good to know. They are used when searching for detailed information in the internet. Also, we'll sometimes reference them further in the tutorial. Instead of the words "Chrome supports feature..." we'd rather say "V8 supports feature...", not just because it's more precise, but because that also implies Opera and Node.JS.
[smart header="Compilation and interpretation"]
```smart header="Compilation and interpretation"
There are two general approaches to execute programs: "compilation" and "interpretation".
<ul>
<li>*Compilers* convert the program text (source code) to binary code (or kind-of) without executing it. When a developer wants to publish the program, he runs a compiler with the source code and then distributes the binary files that it produces.</li>
<li>*Interpreters*, and in particular the one embedded in the browser -- get the source code and execute it "as is".</li>
</ul>
- *Compilers* convert the program text (source code) to binary code (or kind-of) without executing it. When a developer wants to publish the program, he runs a compiler with the source code and then distributes the binary files that it produces.
- *Interpreters*, and in particular the one embedded in the browser -- get the source code and execute it "as is".
As we can see, an interpretation is simpler. No intermediate steps involved. But a compilation is more powerful, because the binary code is more "machine-friendly" and runs faster at the end user.
Modern javascript engines actually combine these approaches into one:
<ol>
<li>The script is written and distributed as a plain text (can be compressed/optimized by so-called "javascript minifiers").</li>
<li>The engine (in-browser for the web) reads the script and converts it to the machine language. And then it runs it. That's why JavaScript executes very fast.
Even more than that, the binary code may be adjusted later, through the process of its execution. The engine learns more about the actual data that it works with and then can optimize it better.</li>
</ol>
1. The script is written and distributed as a plain text (can be compressed/optimized by so-called "javascript minifiers").
2. The engine (in-browser for the web) reads the script and converts it to the machine language. And then it runs it. That's why JavaScript executes very fast.
Even more than that, the binary code may be adjusted later, through the process of its execution. The engine learns more about the actual data that it works with and then can optimize it better.
So the term "interpretation" is used mostly for historical reasons. We do know what there's actually a two-stage (at least) process behind it.
[/smart]
```
## What in-browser JavaScript can do?
@ -64,13 +58,11 @@ In the browser JavaScript can do everything related to webpage manipulation, int
For instance, in-browser JavaScript is able to:
<ul>
<li>Add new HTML to the page, change the existing content, modify styles.</li>
<li>React on user actions, run on mouse clicks, pointer movements, key presses.</li>
<li>Send requests over the network to remote servers, download and upload data without reloading the page (a so-called "AJAX" technology).</li>
<li>Get and set cookies, prompt user for the data, show messages.</li>
<li>Store data in-browser ("localStorage").</li>
</ul>
- Add new HTML to the page, change the existing content, modify styles.
- React on user actions, run on mouse clicks, pointer movements, key presses.
- Send requests over the network to remote servers, download and upload data without reloading the page (a so-called "AJAX" technology).
- Get and set cookies, prompt user for the data, show messages.
- Store data in-browser ("localStorage").
## What in-browser JavaScript can NOT do?
@ -78,38 +70,31 @@ JavaScript abilities in the browser are limited for the sake of the user's safet
The examples of such restrictions are:
<ul>
<li>JavaScript on the webpage may not read/write arbitrary files on the hard disk, copy them or execute programs. It has no direct access to OS system functions.
- JavaScript on the webpage may not read/write arbitrary files on the hard disk, copy them or execute programs. It has no direct access to OS system functions.
Modern browsers allow it to work with files, but the access is limited and only provided if the user does certain actions, like "dropping" a file into a browser window or selecting it via an `<input>` tag.
There are ways to interact with camera/microphone and other devices, but they require an explicit user's permission. So a JavaScript-enabled page may not sneakily enable a web-camera, observe the surroundings and send the information to the NSA.
</li>
<li>Different tabs/windows generally do not know about each other. Sometimes they do, for example when one window uses JavaScript to open the other one. Such action is allowed. But even in this case, JavaScript from one page may not access the other if they compe from different sites (from a different domain, protocol or port).
- Different tabs/windows generally do not know about each other. Sometimes they do, for example when one window uses JavaScript to open the other one. Such action is allowed. But even in this case, JavaScript from one page may not access the other if they compe from different sites (from a different domain, protocol or port).
That is called a "Same Origin Policy". To workaround that, *both pages* must contain a special JavaScript code that handles data exchange.
The limitation is again for a user's safety. A page from `http://anysite.com` which a user has opened occasionaly must not be able to open or access another browser tab with the URL `http://gmail.com` and steal information from there.
</li>
<li>JavaScript can easily communicate over the net to the server where the current page came from. But it's ability to receive data from other sites/domains is crippled. Though possible, it requires the explicit agreement (expressed in HTTP headers) from the remote side. Once again, that's safety limitations.
</li>
</ul>
<img src="limitations.png">
- JavaScript can easily communicate over the net to the server where the current page came from. But it's ability to receive data from other sites/domains is crippled. Though possible, it requires the explicit agreement (expressed in HTTP headers) from the remote side. Once again, that's safety limitations.
![](limitations.png)
Such limits do not exist if JavaScript is used outside of the browser, for example on a server. Modern browsers also allow installing plugin/extensions which may get extended permissions.
## Why JavaScript is unique?
There are at least *three* great things about JavaScript:
[compare]
```compare
+ Full integration with HTML/CSS.
+ Simple things done simply.
+ Supported by all major browsers and enabled by default.
[/compare]
```
Combined, these 3 things only exist in JavaScript and no other browser technology.
@ -123,19 +108,17 @@ While planning to learn a new technology, it's beneficial to check it's perspect
Few examples:
<ul>
<li>Write files on disk (in a "sandbox", not to any folder).</li>
<li>A database embedded in the browser, to keep data on a user's computer and effeciently operate on it.</li>
<li>Multitasking with the usage of many CPU cores in one time.</li>
<li>Audio/video playback.</li>
<li>2d and 3d-drawing with hardware acceleration support, just like in modern games.</li>
</ul>
- Write files on disk (in a "sandbox", not to any folder).
- A database embedded in the browser, to keep data on a user's computer and effeciently operate on it.
- Multitasking with the usage of many CPU cores in one time.
- Audio/video playback.
- 2d and 3d-drawing with hardware acceleration support, just like in modern games.
Many new abilities are still in progress, but browsers gradually improve the support for them.
[summary]
```summary
The trend: browser can do more and more, it is becoming more like an all-purpose desktop application.
[/summary]
```
Still, there is a small gotcha with those "extra-fresh" modern browser abilities. Sometimes browsers try to implement them on very early stages when they are nor fully defined neither agreed upon, but are so interesting that the developers just can't wait.
@ -143,9 +126,9 @@ Still, there is a small gotcha with those "extra-fresh" modern browser abilities
But what's great -- eventually all browsers tend to follow the standard. There are much less differences between them now than only a couple years ago.
[summary]
```summary
The trend: browsers, though eager for new features, tend to be compatible with the standard.
[/summary]
```
## New ECMAScript
@ -153,9 +136,9 @@ JavaScript evolves. The upcoming ECMAScript-2016 standard adds more language-lev
Modern browsers improve their engines to raise JavaScript execution script, fix bugs and try to follow the standards.
[summary]
```summary
The trend: JavaScript is becoming faster, gets new syntax and language features.
[/summary]
```
## Languages "over" JavaScript
@ -169,18 +152,13 @@ The transpilation happens automatically, modern tools make the process very fast
Examples of such languages:
<ul>
<li>[CoffeeScript](http://coffeescript.org/) is a "syntax sugar" for JavaScript, it introduces shorter syntax, allowing to write more precise and clear code. Usually Ruby guys like it.</li>
<li>[TypeScript](http://www.typescriptlang.org/) is concentrated on adding "strict data typing", to simplify development and support of complex systems. Developed by Microsoft.</li>
<li>[Dart](https://www.dartlang.org/) is a standalone language that has it's own engine that runs in non-browser environments (like mobile apps). It was initially offered by Google as a replacement for JavaScript, but as of browsers require it to be transpiled to JavaScript just like the ones above.</li>
</ul>
- [CoffeeScript](http://coffeescript.org/) is a "syntax sugar" for JavaScript, it introduces shorter syntax, allowing to write more precise and clear code. Usually Ruby guys like it.
- [TypeScript](http://www.typescriptlang.org/) is concentrated on adding "strict data typing", to simplify development and support of complex systems. Developed by Microsoft.
- [Dart](https://www.dartlang.org/) is a standalone language that has it's own engine that runs in non-browser environments (like mobile apps). It was initially offered by Google as a replacement for JavaScript, but as of browsers require it to be transpiled to JavaScript just like the ones above.
## Summary
<ul>
<li>JavaScript was initially created as a browser-only language, but now used in many other environments as well.</li>
<li>At this moment, JavaScript as a unique position as a most widely adopted browser language with full integration with HTML/CSS.</li>
<li>There are over languages that get "transpiled" to JavaScript and provide certain features. It is recommended to take a look at them, at least briefly, after mastering JavaScript.</li>
</ul>
- JavaScript was initially created as a browser-only language, but now used in many other environments as well.
- At this moment, JavaScript as a unique position as a most widely adopted browser language with full integration with HTML/CSS.
- There are over languages that get "transpiled" to JavaScript and provide certain features. It is recommended to take a look at them, at least briefly, after mastering JavaScript.

View file

@ -3,7 +3,7 @@
The [latest standard](http://www.ecma-international.org/publications/standards/Ecma-262.htm) was approved in June 2015.
As it includes a lot of new features, most browsers implement them partially. You can find the current state of the support at [](https://kangax.github.io/compat-table/es6/).
As it includes a lot of new features, most browsers implement them partially. You can find the current state of the support at <https://kangax.github.io/compat-table/es6/>.
## Single-engine app
@ -23,21 +23,18 @@ Here comes Babel.JS.
Actually, there are two parts in Babel:
<ol>
<li>The transpiler program, which rewrites the code.
1. The transpiler program, which rewrites the code.
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. One just needs to setup the build system itself.</li>
<li>JavaScript library.
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. One just needs to setup the build system itself.
2. JavaScript library.
An additional JavaScript library with modern JavaScript functions for the browsers that do not have them built-in (yet). The library must be attached to each webpage which relies on these functions.</li>
</ol>
An additional JavaScript library with modern JavaScript functions for the browsers that do not have them built-in (yet). The library must be attached to each webpage which relies on these functions.
There is a special "play" mode of Babel.JS which merges both parts in a single in-browser script.
The usage looks like this:
```html
<!--+ run -->
```html run
*!*
<!-- browser.js is on my server please don't hotlink -->
<script src="https://en.js.cx/babel-core/browser.min.js"></script>
@ -57,16 +54,15 @@ Script `browser.min.js` is attached to the top of the page. It automatically tra
The size of `browser.min.js` is above 1 megabyte, because it includes the transpiler. Hence such usage is only for "playing" and not recommended for production.
Also:
<ul>
<li>There is a "try it" page on [](https://babeljs.io/repl/) which allows to run snippets of code.</li>
<li>[JSBin](http://jsbin.com) allows to use "ES6/Babel" mode for JS, see [this snippet](http://jsbin.com/daxihelolo/edit?js,output) as an example.</li>
</ul>
- There is a "try it" page on <https://babeljs.io/repl/> which allows to run snippets of code.
- [JSBin](http://jsbin.com) allows to use "ES6/Babel" mode for JS, see [this snippet](http://jsbin.com/daxihelolo/edit?js,output) as an example.
# Examples on this site
[warn header="Browser support is required"]
```warn header="Browser support is required"
Examples that use ES-2015 will work only if your browser supports it.
[/warn]
```
Sometimes it means that when running an example in a non-supporting browser, an error is shown.
@ -80,7 +76,7 @@ And even if your browser does not support some code, you can run it through Babe
That would be fine, because on production everyone's using Babel anyway.
Once again, let's note that the most up-to-date situation with support is reflected on [](https://kangax.github.io/compat-table/es6/).
Once again, let's note that the most up-to-date situation with support is reflected on <https://kangax.github.io/compat-table/es6/>.
Now we can go coding, but we need a good code editor for that. That is discussed in the next session.

View file

@ -4,12 +4,10 @@ For the comfortable development we need a good code editor.
It should support at least:
<ol>
<li>Syntax highlight.</li>
<li>Autocompletion.</li>
<li>Folding -- collapsing/opening blocks of code.</li>
<li>...the more features -- the better.</li>
</ol>
1. Syntax highlight.
2. Autocompletion.
3. Folding -- collapsing/opening blocks of code.
4. ...the more features -- the better.
[cut]
@ -21,13 +19,11 @@ An IDE operates on a "whole project": loads it and then can navigate between fil
If you haven't considered selecting an IDE, pleae look at the following variants:
<ul>
<li>IntelliJ editors: [WebStorm](http://www.jetbrains.com/webstorm/) for frontend development and [PHPStorm (PHP)](http://www.jetbrains.com/phpstorm/), [IDEA (Java)](http://www.jetbrains.com/idea/), [RubyMine (Ruby)](http://www.jetbrains.com/ruby/) and other if you need additional languages.</li>
<li>Visual Studio is fine if you're a .NET developer.</li>
<li>Eclipse-based products, like [Aptana](http://www.aptana.com/) and Zend Studio.</li>
<li>[Komodo IDE](http://www.activestate.com/komodo-ide) and it's lightweight free version [Komodo Edit](http://www.activestate.com/komodo-edit).</li>
<li>[Netbeans](http://netbeans.org/)</li>
</ul>
- IntelliJ editors: [WebStorm](http://www.jetbrains.com/webstorm/) for frontend development and [PHPStorm (PHP)](http://www.jetbrains.com/phpstorm/), [IDEA (Java)](http://www.jetbrains.com/idea/), [RubyMine (Ruby)](http://www.jetbrains.com/ruby/) and other if you need additional languages.
- Visual Studio is fine if you're a .NET developer.
- Eclipse-based products, like [Aptana](http://www.aptana.com/) and Zend Studio.
- [Komodo IDE](http://www.activestate.com/komodo-ide) and it's lightweight free version [Komodo Edit](http://www.activestate.com/komodo-edit).
- [Netbeans](http://netbeans.org/)
All of them with the exception of Visual Studio are cross-platform.
@ -45,22 +41,19 @@ In practice, "lightweight" editors may have a lot of plugins including directory
The following options deserve your attention:
<ul>
<li><a href="http://www.sublimetext.com">Sublime Text</a> (cross-platform, shareware).</li>
<li><a href="https://atom.io/">Atom</a> (cross-platform, free).</li>
<li><a href="http://sourceforge.net/projects/notepad-plus/">Notepad++</a> (Windows, free).</li>
<li>Vim, Emacs are cool. If you know how to use them.</li>
</ul>
- <a href="http://www.sublimetext.com">Sublime Text</a> (cross-platform, shareware).
- <a href="https://atom.io/">Atom</a> (cross-platform, free).
- <a href="http://sourceforge.net/projects/notepad-plus/">Notepad++</a> (Windows, free).
- Vim, Emacs are cool. If you know how to use them.
## My favorites
I believe one should have both an IDE for projects and a lightweight editor for quick and easy file editing.
I'm using:
<ul>
<li>[WebStorm](http://www.jetbrains.com/webstorm/) for JS, and if there is one more language in the project, then I switch to other Jetbrains editors like [PHPStorm](http://www.jetbrains.com/phpstorm/) (PHP), [IDEA](http://www.jetbrains.com/idea/) (Java), [RubyMine](http://www.jetbrains.com/ruby/) (Ruby). There are editors for other languages too, but I didn't use them.</li>
<li>As a lightweight editor -- <a href="http://www.sublimetext.com">Sublime Text</a>.</li>
</ul>
- [WebStorm](http://www.jetbrains.com/webstorm/) for JS, and if there is one more language in the project, then I switch to other Jetbrains editors like [PHPStorm](http://www.jetbrains.com/phpstorm/) (PHP), [IDEA](http://www.jetbrains.com/idea/) (Java), [RubyMine](http://www.jetbrains.com/ruby/) (Ruby). There are editors for other languages too, but I didn't use them.
- As a lightweight editor -- <a href="http://www.sublimetext.com">Sublime Text</a>.
If you don't know what to choose -- you can consider these ones.

View file

@ -26,24 +26,22 @@ Open the page [bug.html](bug.html).
There's an error in the JavaScript code on it. An ordinary visitor won't see it, so let's open developer tools.
Press the key [key F12] or, if you're on Mac, then [key Cmd+Opt+J].
Press the key `key:F12` or, if you're on Mac, then `key:Cmd+Opt+J`.
The developer tools will open on the Console tab by default.
It looks somewhat like this:
<img src="chrome.png">
![](chrome.png)
The exact look depends on your Chrome version. It changes from time to time, but should be similar.
<ul>
<li>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.</li>
<li>On the right, there is a clickable link to the source `bug.html:12` with the line number where the error has occured.</li>
</ul>
- 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.
- 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 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 [](/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>.
## Safari
@ -51,22 +49,19 @@ For Safari, we need to enable the "Develop menu" first.
There's a checkbox for that at the bottom of the "Advanced" pane of the preferences:
<img src="safari.png">
![](safari.png)
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 has appeared with many useful options.
## Other browsers
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.
## Summary
<ul>
<li>Developer tools allow us to see errors, run commands, examine variables and much more.</li>
<li>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).
</li>
</ul>
- 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).
Now we have the environment ready. In the next section we get down to JavaScript.

View file

@ -1,9 +1,12 @@
# Show an alert
importance: 5
[importance 5]
---
# Show an alert
Create a page that shows a message "I'm JavaScript!".
Do it in a sandbox, or on your hard drive, doesn't matter, just ensure that it works.
[demo src="solution"]

View file

@ -3,20 +3,20 @@
In this chapter we'll create a simple script and see it working.
[cut]
## The "script" tag
[smart header="What if I want to move faster?"]
In the case if the reader of these lines has developed with JavaScript already or has a lot of experience in another language, he can skip detailed explanatins and jump to [](/javascript-specials). There he can find an essense of important features.
```smart header="What if I want to move faster?"
In the case if the reader of these lines has developed with JavaScript already or has a lot of experience in another language, he can skip detailed explanatins and jump to <info:javascript-specials>. There he can find an essense of important features.
If you have enough time and want to learn things in details then read on :)
[/smart]
```
JavaScript programs can be inserted in any place of HTML with the help of the `<script>` tag.
For instance:
```html
<!--+ run height=100 -->
```html run height=100
<!DOCTYPE HTML>
<html>
@ -37,57 +37,48 @@ For instance:
</html>
```
[online]
```online
You can run the example clicking on a "Play" button in it's right-top corner.
[/online]
```
The `<script>` tag contains JavaScript code which is automatically executed when the browser meets the tag.
Please note the execution sequence:
<ol>
<li>Browser starts to parse the document and display the page.</li>
<li>When the browser meets `<script>`, it switches to the JavaScript execution mode. In this mode it executes the script.</li>
<li>The `alert` command shows a message and pauses the execution.</li>
<li>*Note that a part of the page before the script is shown already at this moment.*</li>
<li>When the script is finished, it gets back to the HTML-mode, and *only then* it shows the rest of the document.</li>
</ol>
1. Browser starts to parse the document and display the page.
2. When the browser meets `<script>`, it switches to the JavaScript execution mode. In this mode it executes the script.
3. The `alert` command shows a message and pauses the execution.
4. *Note that a part of the page before the script is shown already at this moment.*
5. When the script is finished, it gets back to the HTML-mode, and *only then* it shows the rest of the document.
A visitor won't see the content after the script until it is executed. In other words, a `<script>` tag blocks rendering.
## The modern markup
In the past, `<script>` had a few necessary attributes.
We can find the following in the old code:
<dl>
<dt>The `type` attribute: <code>&lt;script <u>type</u>=...&gt;</code></dt>
The `type` attribute: <code>&lt;script <u>type</u>=...&gt;</code>
<dd>The old standard HTML4 required a script to have the type. Usually it was `type="text/javascript"`. The modern HTML standard assumes this `type` by default, no attribute is required.
</dd>
: The old standard HTML4 required a script to have the type. Usually it was `type="text/javascript"`. The modern HTML standard assumes this `type` by default, no attribute is required.
<dt>The `language` attribute: <code>&lt;script <u>language</u>=...&gt;</code></dt>
<dd>This attribute was meant to show the language of the script. As of now, this attribute makes no sense, the language is JavaScript by default. No need to use it.</dd>
<dt>Comments before and after scripts.</dt>
<dd>In really ancient books and guides, one may find comments inside `<script>`, like this:
The `language` attribute: <code>&lt;script <u>language</u>=...&gt;</code>
: This attribute was meant to show the language of the script. As of now, this attribute makes no sense, the language is JavaScript by default. No need to use it.
```html
<!--+ no-beautify -->
Comments before and after scripts.
: In really ancient books and guides, one may find comments inside `<script>`, like this:
```html no-beautify
<script type="text/javascript"><!--
...
//--></script>
```
These comments were supposed to hide the code from an old browser that did't understand a `<script>` tag. But all browsers born in the past 15 years know about `<script>`, so that's not an issue. So if you see such code somewhere you know the guide is really old and probably not worth looking into.
</dd>
</dl>
## Summary
<ul>
<li>We can use a `<script>` tag without attributes to add JavaScript code to the page.</li>
<li>A `<script>` tag blocks page rendering. Can be bad. Later we'll see how to evade that.</li>
</ul>
- We can use a `<script>` tag without attributes to add JavaScript code to the page.
- A `<script>` tag blocks page rendering. Can be bad. Later we'll see how to evade that.

View file

@ -1,7 +1,6 @@
JavaScript-code:
```js
//+ demo run
```js demo run
let name = prompt("What is your name?", "");
alert( name );
```

View file

@ -1,8 +1,10 @@
# A simple page
importance: 4
[importance 4]
---
# A simple page
Create a web-page which asks for a name and outputs it.
[demo /]
[demo]

View file

@ -3,6 +3,7 @@
This chapter covers basic UI operations: `alert`, `prompt` and `confirm`. They allow to ask a visitor for the input and show the results.
[cut]
## alert
Syntax:
@ -15,8 +16,7 @@ This shows a message and pauses the script execution until the user presses "OK"
For example:
```js
//+ run
```js run
alert( "Hello" );
```
@ -26,50 +26,45 @@ The small window with the message is called a *modal window*. The word "modal" m
Function `prompt` accepts two arguments:
```js
//+ no-beautify
```js no-beautify
result = prompt(title, default);
```
It shows a modal window with the given `title`, a field for text, initially filled with the `default` string and buttons OK/CANCEL.
The visitor may type something in the field and press OK. Or he can cancel the input by pressing a CANCEL button or the [key Esc] key.
The visitor may type something in the field and press OK. Or he can cancel the input by pressing a CANCEL button or the `key:Esc` key.
The call to `prompt` returns the text from the field or `null` if te input is canceled.
[warn header="Safari 5.1+ does not return `null`"]
Safari is the only browser which does not return `null` when the input was canceled. It returns an empty string instead.
```warn header="Safari does not return `null`"
Safari returns an empty string instead of `null` on cancellation. So we can't be sure whether the user actually entered an empty line or he cancelled the input.
To make the code compatible with that browser, we should consider both an empty line and `null` as a cancellation.
[/warn]
A compatible practice is to treat both an empty line and `null` the same, as a cancellation.
```
As with `alert`, the `prompt` window is modal.
```js
//+ run
```js run
let age = prompt('How old are you?', 100);
alert(`You are ${age} years old!`); // You are 100 years old! (for default prompt value)
alert(`You are ${age} years old!`); // You are 100 years old!
```
[warn header="Always supply a `default`"]
````warn header="IE: always supply a `default`"
The second parameter is optional. But if we don't supply it, Internet Explorer would insert the text `"undefined"` into the prompt.
Run this code in Internet Explorer to see that:
```js
//+ run
```js run
let test = prompt("Test");
```
So, to look good in IE, it's recommended to always provide the second argument:
```js
//+ run
```js run
let test = prompt("Test", ''); // <-- for IE
```
[/warn]
````
## confirm
@ -85,8 +80,7 @@ The result is `true` if OK is pressed and `false` otherwise.
For example:
```js
//+ run
```js run
let isBoss = confirm("Are you the boss?");
alert( isBoss ); // true is OK is pressed
@ -94,15 +88,18 @@ alert( isBoss ); // true is OK is pressed
## Summary
<ul>
<li>`alert` shows a message.</li>
<li>`prompt` shows a message asking the user to input text. It returns the text or, if CANCEL or [key Esc] is clicked, all browsers except Safari return `null`.</li>
<li>`confirm` shows a message and waits the user to press "OK" or "CANCEL". It returns `true` for OK and `false` for CANCEL/[key Esc].</li>
</ul>
There are two limitations shared by all the methods above:
<ol>
<li>The exact location of the modal window is determined by the browser. Usually it's in the center.</li>
<li>The exact look of the window also depends on the browser. We can't modify it.</li>
</ol>
`alert`
: shows a message.
That is the price for simplicity. There are other ways to show nicer windows and interact with the visitor, but if "bells and whistles" do not matter much, these methods work just fine.
`prompt`
: shows a message asking the user to input text. It returns the text or, if CANCEL or `key:Esc` is clicked, all browsers except Safari return `null`.
`confirm`
: shows a message and waits the user to press "OK" or "CANCEL". It returns `true` for OK and `false` for CANCEL/`key:Esc`.
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.
2. The exact look of the window also depends on the browser. We can't modify it.
That is the price for simplicity. There are other ways to show nicer windows and richer interaction with the visitor, but if "bells and whistles" do not matter much, these methods work just fine.

View file

@ -4,8 +4,7 @@ Any string except an empty one (and `"0"` is not empty) becomes `true` in the lo
We can run and check:
```js
//+ run
```js run
if ("0") {
alert( 'Hello' );
}

View file

@ -1,6 +1,8 @@
# if (a string with zero)
importance: 5
[importance 5]
---
# if (a string with zero)
Will `alert` be shown?

View file

@ -1,6 +1,4 @@
```html
<!--+ run src="ifelse_task2/index.html" -->
```
[html run src="ifelse_task2/index.html"]

View file

@ -1,11 +1,14 @@
# The name of JavaScript
importance: 2
[importance 2]
---
# The name of JavaScript
Using the `if..else` construct, write the code which asks: 'What is the "official" name of JavaScript?'
If the visitor enters "ECMAScript", then output "Right!", otherwise -- output: "Didn't know? ECMAScript!"
<img src="ifelse_task2.png">
![](ifelse_task2.png)
[demo src="ifelse_task2"]

View file

@ -1,7 +1,6 @@
```js
//+ run
```js run
let value = prompt('Type a number', 0);
if (value > 0) {

View file

@ -1,14 +1,14 @@
# Show the sign
importance: 2
[importance 2]
---
# Show the sign
Using `if..else`, write the code which gets a number via `prompt` and then shows in `alert`:
<ul>
<li>`1`, if the value is greater than zero,</li>
<li>`-1`, if less than zero,</li>
<li>`0`, if equals zero.</li>
</ul>
- `1`, if the value is greater than zero,
- `-1`, if less than zero,
- `0`, if equals zero.
In this task we assume that the input is always a number.

View file

@ -1,7 +1,6 @@
```js
//+ run demo
```js run demo
let userName = prompt('Who's there?', '');
if (userName == 'Admin') {

View file

@ -1,22 +1,23 @@
# Check the login
importance: 3
[importance 3]
---
# Check the login
Write the code which asks for a login with `prompt`.
If the visitor enters `"Admin"`, then `prompt` for a password, if the input is an empty line or [key Esc] -- show "Canceled.", if it's another string -- then show "I don't know you".
If the visitor enters `"Admin"`, then `prompt` for a password, if the input is an empty line or `key:Esc` -- show "Canceled.", if it's another string -- then show "I don't know you".
The password is checked as follows:
<ul>
<li>If it equals "TheMaster", then show "Welcome!",</li>
<li>Another string -- show "Wrong password",</li>
<li>For an empty string or cancelled input, show "Canceled."</li>
</ul>
- If it equals "TheMaster", then show "Welcome!",
- Another string -- show "Wrong password",
- For an empty string or cancelled input, show "Canceled."
The schema:
<img src="ifelse_task.png">
![](ifelse_task.png)
Please use nested `if` blocks. Mind the overall readability of the code.
[demo /]
[demo]

View file

@ -1,6 +1,8 @@
# Rewrite 'if' into '?'
importance: 5
[importance 5]
---
# Rewrite 'if' into '?'
Rewrite this `if` using the ternary operator `'?'`:

View file

@ -1,6 +1,8 @@
# Rewrite 'if..else' into '?'
importance: 5
[importance 5]
---
# Rewrite 'if..else' into '?'
Rewrite `if..else` using multiple ternary operators `'?'`.

View file

@ -1,6 +1,8 @@
# Conditional operators: if, '?'
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.
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.
[cut]
@ -10,8 +12,7 @@ The "if" operator gets a condition, evaluates it and -- if the result is `true`
For example:
```js
//+ run
```js run
let year = prompt('In which year was ECMAScript-2015 specification published?', '');
*!*
@ -19,7 +20,7 @@ if (year == 2015) alert( 'You are right!' );
*/!*
```
In the example above, the condition is a simple equality: `year == 2015`, but it can be much more complex.
In the example above, the condition is a simple equality check: `year == 2015`, but it can be much more complex.
If there's more than one command to execute -- we can use a code block in figure brackets:
@ -37,10 +38,9 @@ It is recommended to use figure brackets every time with `if`, even if there's o
The `if (…)` operator evaluates the condition in brackets and converts it to boolean type.
Let's recall the rules. In the logical context:
<ul>
<li>A number `0`, an empty string `""`, `null`, `undefined` and `NaN` are `false`,</li>
<li>Other values -- `true`.</li>
</ul>
- A number `0`, an empty string `""`, `null`, `undefined` and `NaN` are `false`,
- Other values -- `true`.
So, the code under this condition would never execute:
@ -73,8 +73,7 @@ if (cond) {
The `if` operator may contain an optional "else" block. It executes when the condition is wrong.
For example:
```js
//+ run
```js run
let year = prompt('In which year was ECMAScript-2015 specification published?', '');
if (year == 2015) {
@ -90,8 +89,7 @@ Sometimes we'd like to test several variants of a condition. There's an `else if
For example:
```js
//+ run
```js run
let year = prompt('In which year was ECMAScript-2015 specification published?', '');
if (year < 2015) {
@ -105,20 +103,18 @@ 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`.
## Ternary operator '?'
Sometimes we need to assign a variable depending on a condition.
For instance:
```js
//+ run no-beautify
```js run no-beautify
let hasAccess;
let age = prompt('How old are you?', '');
*!*
if (age > 14) {
if (age > 18) {
hasAccess = true;
} else {
hasAccess = false;
@ -142,36 +138,33 @@ The `condition` is evaluated, if it's truthy then `value1` is returned, otherwis
For example:
```js
let hasAccess = (age > 14) ? true : false;
let hasAccess = (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:
```js
// the same
let hasAccess = age > 14 ? true : false;
let hasAccess = age > 18 ? true : false;
```
...But brackets 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`:
```js
// the same
let hasAccess = age > 14;
let hasAccess = age > 18;
```
But that's only in this case. Generally, the question mark can return any value.
[/smart]
````
## Multiple '?'
A sequence of question mark `"?"` operators allows to return a value depending on more than one condition.
For instance:
```js
//+ run
```js run
let age = prompt('age?', 18);
let message = (age < 3) ? 'Hi, baby!' :
@ -184,9 +177,12 @@ 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.
The question mark first checks for `age < 3`. If true -- returns `'Hi, baby!'`, otherwise -- goes to the right side of the colon `":"` and checks for `age < 18`. If that's true -- returns `'Hello!'`, otherwise checks for `age < 100` and returns `'Greetings!'` if that is so... At last, if all checks are falsy, the `message` becomes `'What an unusual age!'`.
1. The first question mark checks for `age < 3`.
2. If true -- returns `'Hi, baby!'`, otherwise -- goes to the right side of 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...
4. At last, if all checks are falsy, the `message` becomes `'What an unusual age!'`.
The same with `if..else`:
The same logic using `if..else`:
```js
if (age < 3) {
@ -204,8 +200,7 @@ if (age < 3) {
Sometimes the question mark `'?'` is used as a replacement for `if`:
```js
//+ run no-beautify
```js run no-beautify
let company = prompt('Which company created JavaScript?', '');
*!*
@ -216,16 +211,15 @@ 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.
We don't assign a result to a variable here, the `alert` doesn't return anything anyway.
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.
**It is not recommended to use a question mark in this way.**
**It is not recommended to use the question mark operator in this way.**
The notation seem to be shorter than `if`, that appeals to some programmers. Although it is less readable.
The notation seem to be shorter than `if`, that appeals to some programmers. But it is less readable.
Here's the same with `if` for comparison:
```js
//+ run no-beautify
```js run no-beautify
let company = prompt('Which company created JavaScript?', '');
*!*

View file

@ -1,7 +1,6 @@
The answer is `2`, that's the first truthy value.
```js
//+ run
```js run
alert( null || 2 || undefined );
```

View file

@ -1,6 +1,8 @@
# What's the result of OR?
importance: 5
[importance 5]
---
# What's the result of OR?
What the code below is going to output?

View file

@ -1,16 +1,13 @@
The answer: first `1`, then `2`.
```js
//+ run
```js run
alert( alert(1) || 2 || alert(3) );
```
The call to `alert` does not return a value. Or, in other words, it returns `undefined`.
<ol>
<li>The first OR `||` evaluates it's left operand `alert(1)`. That shows the first message with `1`.</li>
<li>The `alert` returns `undefined`, so OR goes on to the second operand in it's search of a truthy value.</li>
<li>The second operand `2` is truthy, so the execution is halted, `2` is returned and then shown by the outer alert.</li>
</ol>
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.
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)`.

View file

@ -1,6 +1,8 @@
# What's the result of OR'ed alerts?
importance: 3
[importance 3]
---
# What's the result of OR'ed alerts?
What the code below will output?

View file

@ -1,7 +1,6 @@
The answer: `null`, because it's the first falsy value from the list.
```js
//+ run
```js run
alert( 1 && null && 2 );
```

View file

@ -1,6 +1,8 @@
# What is the result of AND?
importance: 5
[importance 5]
---
# What is the result of AND?
What this code is going to show?

View file

@ -1,7 +1,6 @@
The answer: `1`, and then `undefined`.
```js
//+ run
```js run
alert( alert(1) && alert(2) );
```

View file

@ -1,6 +1,8 @@
# What is the result of AND'ed alerts?
importance: 3
[importance 3]
---
# What is the result of AND'ed alerts?
What will this code show?

View file

@ -1,7 +1,6 @@
The answer: `3`.
```js
//+ run
```js run
alert( null || 2 && 3 || 4 );
```

View file

@ -1,6 +1,8 @@
# The result of OR AND OR
importance: 5
[importance 5]
---
# The result of OR AND OR
What will be the result?

View file

@ -1,6 +1,8 @@
# Check the range between
importance: 3
[importance 3]
---
# Check the range between
Write an "if" condition to check that `age` is between `14` and `90` inclusively.

View file

@ -1,6 +1,8 @@
# Check the range outside
importance: 3
[importance 3]
---
# Check the range outside
Write an `if` condition to check that `age` is NOT between 14 and 90 inclusively.

View file

@ -2,8 +2,7 @@ The answer: the first and the third will execute.
Details:
```js
//+ run
```js run
// Runs.
// The result of -1 || 0 = -1, truthy
if (-1 || 0) alert( 'first' );

View file

@ -1,6 +1,8 @@
# A question about "if"
importance: 5
[importance 5]
---
# A question about "if"
Which of these `alert`s are going to execute?

View file

@ -22,8 +22,7 @@ In JavaScript the operator is a little bit more tricky and powerful. But first l
A table of possible logical combinations:
```js
//+ run
```js run
alert( true || true ); // true
alert( false || true ); // true
alert( true || false ); // true
@ -36,19 +35,17 @@ If an operand is not boolean, then it's converted to boolean for the evaluation.
For instance, a number `1` is treated as `true`, a number `0` -- as `false`:
```js
//+ run
```js run
if (1 || 0) { // works just like if( true || false )
alert( 'truthy!' );
}
```
Mainly, OR is used in the `if` expression to test if *any* of given conditions is correct.
Most of time, OR `||` is used in the `if` expression to test if *any* of given conditions is correct.
For example:
```js
//+ run
```js run
let hour = 9;
*!*
@ -60,8 +57,7 @@ if (hour < 10 || hour > 18) {
We can pass more conditions:
```js
//+ run
```js run
let hour = 12;
let isWeekend = true;
@ -84,18 +80,15 @@ result = value1 || value2 || value3;
The OR `"||"` operator is doing the following:
<ul>
<li>Evalutes operands from left to right.</li>
<li>For each value converts it to boolean and stops immediately if it's true.</li>
<li>Returns the value where it stopped. The value is returned in it's original form, without the conversion.</li>
</ul>
- Evalutes operands from left to right.
- For each value converts it to boolean and stops immediately returning it if it's true.
- The value is returned in it's original form, without the conversion.
In other words, it returns the first truthy value or the last one if no such value found.
For instance:
```js
//+ run
```js run
alert( 1 || 0 ); // 1 (is truthy)
alert( true || 'no matter what' ); // (true is truthy)
@ -108,15 +101,13 @@ This logic does not contradict to what was spoken above. If you check this behav
But there leads to some interesting usages compared to a "pure, classical, boolean-only OR".
<ol>
<li>**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:
Using OR `||` for that:
```js
//+ run
```js run
let currentUser = null;
let defaultUser = "John";
@ -128,41 +119,37 @@ alert( name ); // selects "John" the first truthy value
```
If both `currentUser` and `defaultUser` were falsy then `"unnamed"` would be the result.
</li>
<li>**Short-circuit evaluation.**
2. **Short-circuit evaluation.**
Operands can be not only values, but arbitrary expressions. OR evaluates and tests them from left to right. The evaluation stops when a truthy value is reached, and the value is returned. The process is called "a short-circuit evaluation", because it goes as short as possible from left to right.
This is clearly seen when the expression given as the second argument has a side effect. Like a variable assignment.
If we run the example below, `x` will not get assigned:
If we run the example below, `x` would not get assigned:
```js
//+ run no-beautify
```js run no-beautify
let x;
*!*true*/!* || (x = 1);
alert(x); // undefined, (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:
```js
//+ run no-beautify
```js run no-beautify
let x;
*!*false*/!* || (x = 1);
alert(x); // 1
```
An assignment is a simple case, other side effects can be involved.
As we can see, such use case is a "shorter way to `if`". The first operand is converted to boolean and if it's false then the second one is evaluated. It's recommended to use `if` for that for code clarity.
</li>
</ol>
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.
## && (AND)
@ -174,8 +161,7 @@ result = a && b;
In classic programming AND returns `true` if both operands are truthy and `false` -- otherwise:
```js
//+ run
```js run
alert( true && true ); // true
alert( false && true ); // false
alert( true && false ); // false
@ -184,8 +170,7 @@ alert( false && false ); // false
An example with `if`:
```js
//+ run
```js run
let hour = 12;
let minute = 30;
@ -196,34 +181,34 @@ 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:
```js
//+ run
```js run
if (1 && 0) { // evaluated as true && false
alert( "won't work, because the result is falsy" );
}
```
More formally, given multiple AND'ed values:
## AND seeks the first falsy value
Given multiple AND'ed values:
```js
result = value1 && value2 && value3;
```
The AND `"&&"` operator is doing the following:
<ul>
<li>Evalutes operands from left to right.</li>
<li>For each value converts it to a boolean. If the result is `false`, stops.</li>
<li>Returns the value where it stopped "as is", without the conversion.</li>
</ul>
In other words, AND returns the first falsy value or the last one if all are truthy.
- 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.
- 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.
The rules above are similar to OR. The difference is that AND returns the first *falsy* value while OR returns the first *truthy* one.
Examples:
```js
//+ run
```js run
// if the first operand is truthy,
// && returns the second one.
alert( 1 && 0 ); // 0
@ -235,38 +220,33 @@ alert( null && 5 ); // null
alert( 0 && "no matter what" ); // 0
```
We can also pass several values in a row. The first falsy one is returned:
We can also pass several values in a row. See how the first falsy one is returned:
```js
//+ run
```js run
alert( 1 && 2 && null && 3 ); // null
```
...Or the last one if all of them are truthy:
...And now when all of them are truthy:
```js
//+ run
alert( 1 && 2 && 3 ); // 3, all truthy
```js run
alert( 1 && 2 && 3 ); // 3, the last one
```
[smart header="AND `&&` executes before OR `||`"]
````smart header="AND `&&` executes before OR `||`"
The precedence of the AND `&&` operator is higher than OR `||`, so it executes before OR.
In the code below `1 && 0` is calculated first:
```js
//+ run
```js run
alert( 5 || 1 && 0 ); // 5
```
[/smart]
````
Just like OR, the AND `&&` operator can sometimes replace `if`.
For instance:
```js
//+ run
```js run
let x = 1;
(x > 0) && alert( 'Greater than zero!' );
@ -276,8 +256,7 @@ The action in the right part of `&&` would execute only if the evaluation reache
So we basically have an analogue for:
```js
//+ run
```js run
let x = 1;
if (x > 0) {
@ -291,7 +270,7 @@ So it is recommended to use every construct for it's purpose. Use `if` if we wan
## ! (NOT)
The boolean NOT operator is represented with an exclamation `"!"`.
The boolean NOT operator is represented with an exclamation sign `"!"`.
The syntax is one of the simplest:
@ -301,33 +280,28 @@ result = !value;
The operator accepts a single argument and does the following:
<ol>
<li>Converts the operand to boolean type: `true/false`.</li>
<li>Returns an inverse value.</li>
</ol>
1. Converts the operand to boolean type: `true/false`.
2. Returns an inverse value.
For instance:
```js
//+ run
```js run
alert( !true ); // false
alert( !0 ); // true
```
A double NOT is sometimes used for converting a value to boolean type:
```js
//+ run
```js run
alert( !!"non-empty string" ); // true
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.
Although, there's a more obvious way to do that: a built-in `Boolean` function:
There's a little more verbose to do the same -- a built-in `Boolean` function:
```js
//+ run
```js run
alert( Boolean("non-empty string") ); // true
alert( Boolean(null) ); // false
```

View file

@ -1,7 +1,6 @@
The answer: `1`.
```js
//+ run
```js run
let i = 3;
while (i) {

View file

@ -1,6 +1,8 @@
# Last loop value
importance: 3
[importance 3]
---
# Last loop value
What is be the last value alerted by this code? Why?

View file

@ -1,10 +1,8 @@
The task demonstrates how postfix/prefix forms can lead to different results when used in comparisons.
<ol>
<li>**From 1 to 4**
1. **From 1 to 4**
```js
//+ run
```js run
let i = 0;
while (++i < 5) alert( i );
```
@ -14,11 +12,9 @@ The first value is `i=1`, because `++i` first increments `i` and then returns th
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.
</li>
<li>**From 1 to 5**
2. **From 1 to 5**
```js
//+ run
```js run
let i = 0;
while (i++ < 5) alert( i );
```
@ -32,6 +28,4 @@ Then follow `2,3,4…`
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.
</li>
</ol>

View file

@ -1,26 +1,23 @@
# Which values shows the while?
importance: 4
[importance 4]
---
# Which values shows the while?
For every loop, scribe down which values it shows, in your opinion.
And then compare with the answer.
<ol>
<li>The prefix form `++i`:
1. The prefix form `++i`:
```js
let i = 0;
while (++i < 5) alert( i );
```
</li>
<li>The postfix form `i++`
2. The postfix form `i++`
```js
let i = 0;
while (i++ < 5) alert( i );
```
</li>
</ol>

View file

@ -1,18 +1,16 @@
**The answer: from `0` to `4` in both cases.**
```js
//+ run
```js run
for (let i = 0; i < 5; ++i) alert( i );
for (let i = 0; i < 5; i++) alert( i );
```
That can be easily deducted from the algorithm of `for`:
<ol>
<li>Execute once `i=0` before everything.</li>
<li>Check the condition `i<5`</li>
<li>If `true` -- execute the loop body `alert(i)`, and then `i++`</li>
</ol>
1. Execute once `i=0` before everything.
2. Check the condition `i<5`
3. If `true` -- execute the loop body `alert(i)`, and then `i++`
The increment `i++` is separated from the condition check (2). That's just another statement.

View file

@ -1,24 +1,21 @@
# Which values get shown by the "for" loop?
importance: 4
[importance 4]
---
# Which values get shown by the "for" loop?
For each loop scribe down which values it is going to show.
Then compare with the answer.
<ol>
<li>The postfix form:
1. The postfix form:
```js
for (let i = 0; i < 5; i++) alert( i );
```
</li>
<li>The prefix form:
2. The prefix form:
```js
for (let i = 0; i < 5; ++i) alert( i );
```
</li>
</ol>

View file

@ -1,7 +1,6 @@
```js
//+ run demo
```js run demo
for (let i = 2; i <= 10; i++) {
if (i % 2 == 0) {
alert( i );

View file

@ -1,7 +1,9 @@
# Output even numbers in the loop
importance: 5
[importance 5]
---
# Output even numbers in the loop
Use the `for` loop to output even numbers from `2` to `10`.
[demo /]
[demo]

View file

@ -1,7 +1,6 @@
```js
//+ run
```js run
let i = 0;
while (i < 3) {
alert( `number ${i}!` );

View file

@ -1,11 +1,12 @@
# Replace "for" with "while"
importance: 5
[importance 5]
---
# Replace "for" with "while"
Rewrite the code changing the `for` loop to `while` without altering it's behavior (the output should stay same).
```js
//+ run
```js run
for (let i = 0; i < 3; i++) {
alert( `number ${i}!` );
}

View file

@ -1,6 +1,5 @@
```js
//+ run demo
```js run demo
let num;
do {
@ -9,9 +8,8 @@ do {
```
The loop `do..while` repeats while both checks are truthy:
<ol>
<li>The check for `num <= 100` -- that is, the entered value is still not greater than `100`.</li>
<li>The check for a truthiness of `num` checks that `num != null` and `num != ""` simultaneously.</li>
</ol>
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.
P.S. By the way, if `num` is `null` then `num <= 100` would return `false`, not `true`!

View file

@ -1,6 +1,8 @@
# Repeat until the input is incorrect
importance: 5
[importance 5]
---
# 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.
@ -8,4 +10,5 @@ The loop must ask for a number until either the visitor enters a number greater
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.
[demo /]
[demo]

View file

@ -12,8 +12,7 @@ For each i in the interval {
The code using a label:
```js
//+ run
```js run
nextPrime:
for (let i = 2; i < 10; i++) { // for each i...

View file

@ -1,6 +1,8 @@
# Output prime numbers
importance: 3
[importance 3]
---
# 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.

View file

@ -7,6 +7,7 @@ For example, when we need to output goods from the list one after another. Or ju
*Loops* are a way to repeat the same part of code multiple times.
[cut]
## The "while" loop
The `while` loop has the following syntax:
@ -21,8 +22,7 @@ While the `condition` is `true` -- the `code` from the loop body is executed.
For instance, the loop below outputs `i` while `i<3`:
```js
//+ run
```js run
let i = 0;
while (i < 3) { // shows 0, then 1, then 2
alert( i );
@ -38,8 +38,7 @@ The `while` converts `condition` to a logical value. It can be any expression, n
For instance, the shorter way to write `while (i!=0)` could be `while (i)`:
```js
//+ run
```js run
let i = 3;
*!*
while (i) { // when i becomes 0, the condition is falsy and the loop stops
@ -49,18 +48,16 @@ while (i) { // when i becomes 0, the condition is falsy and the loop stops
}
```
[smart header="Brackes are not required for a single-line body"]
````smart header="Brackes are not required for a single-line body"
If the loop body has a single statement, we can omit the brackets `{…}`:
```js
//+ run
```js run
let i = 3;
*!*
while (i) alert(i--);
*/!*
```
[/smart]
````
## The "do..while" loop
@ -76,8 +73,7 @@ The loop will first execute the body and then check the condition.
For example:
```js
//+ run
```js run
let i = 0;
do {
alert( i );
@ -101,8 +97,7 @@ for (begin; condition; step) {
Let's see these parts in an example. The loop below runs `alert(i)` for `i` from `0` up to (but not including) `3`:
```js
//+ run
```js run
let i;
for (i = 0; i < 3; i++) { // shows 0, then 1, then 2
@ -110,45 +105,41 @@ for (i = 0; i < 3; i++) { // shows 0, then 1, then 2
}
```
Here the parts are:
<ul>
<li>**Begin:** `i=0`.</li>
<li>**Condition:** `i<3`.</li>
<li>**Step:** `i++`.</li>
<li>**Body:** `alert(i)`, the code inside figure brackets. Brackets not required for a single statement.</li>
</ul>
Let's split the last example into parts:
The `for` loop execution follows these steps:
begin: `i=0`
: Executes once upon entering the loop.
<ol>
<li>**Begin**: `i=0` executes only once upon entering the loop.</li>
<li>**Condition**: `i<3` is checked before every iteration. If it fails, the loop stops.</li>
<li>**Body**: `alert(i)` runs is the condition is truthy.</li>
<li>**Step**: `i++` executes after the `body` on each iteration, but before the `condition` check.</li>
<li>Continue to step 2.</li>
</ol>
condition: `i<3`
: Checked before every loop iteration, if fails the loop stops.
In other words, the execution flow is:
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.
The execution flow is:
```
Begin
→ (if condition → run body and step)
→ (if condition → run body and step)
→ (if condition → run body and run step)
→ (if condition → run body and run step)
→ ... repeat until the condition is falsy.
```
[smart header="Inline variable declaration"]
````smart header="Inline variable declaration"
We can declare a "counter" variable right in the beginning of the loop.
```js
//+ run no-beautify
```js run no-beautify
for (*!*let*/!* i = 0; i < 3; i++) {
alert(i); // 0, 1, 2
}
```
[/smart]
The variable will be visible only inside the loop.
````
## Skipping of "for" parts
## Skipping parts
Any part of the `for` can be skipped.
@ -156,8 +147,7 @@ For example, we can omit `begin` if we don't need to do anything at the loop sta
Like here:
```js
//+ run
```js run
let i = 0;
for (; i < 3; i++) {
@ -169,8 +159,7 @@ It would work same as `for(let i=0; ...)`.
We can also remove the `step` part:
```js
//+ run
```js run
let i = 0;
for (; i < 3;) {
@ -189,12 +178,11 @@ for (;;) {
Please note that the semicolons `;` must present, otherwise it would be a syntax error.
[smart header="`for..in`"]
There is also a special construct `for..in` to iterate over object properties.
We'll get to it later while [talking about objects](#for..in).
[/smart]
```smart header="`for..in` and `for..of`"
There are special constructs: `for..in` and `for..of` for more advanced iterations over objects.
We'll get to them later, in chapters about objects.
```
## Breaking the loop
@ -223,29 +211,29 @@ 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`.
Actually, the composition: "an infinite loop + break" is a great thing for situations when the condition must be checked not in beginning/end of the loop, but in the middle.
The composition: "infinite loop + break as needed" is a great thing 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.
## Continue to the next iteration [#continue]
The `continue` directive is a younger sister of `break`. It doesn't stop the whole loop. Instead if stops the current iteration and forces the loop to start a new one (if the condition allows).
The `continue` directive is a "lighter version" of `break`. It doesn't stop the whole loop. Instead if stops the current iteration and forces the loop to start a new one (if the condition allows).
We can use it if we're done on the current iteration and would like to move on to the next.
The loop above uses `continue` to output only odd values:
```js
//+ run no-beautify
```js run no-beautify
for (let i = 0; i < 10; i++) {
// if true, skip the remaining part of the body
*!*if (i % 2 == 0) continue;*/!*
alert(i);
alert(i); // 1, then 3, 5, 7, 9
}
```
For even `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="`continue` allows to decrease nesting level"]
````smart header="The directive `continue` helps to decrease nesting level"
A loop for odd-only values could look like this:
```js
@ -261,12 +249,12 @@ for (let i = 0; i < 10; i++) {
From the technical point of view it's identical. Surely, we can just wrap the code in the `if` block instead of `continue`.
But as a side-effect we got one more figure brackets nesting level. If the code inside `if` is longer than a few lines, that may decrease the overall readability.
[/smart]
````
[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.
For example, if one would rewrite an `if` like that into a question mark:
For example, if one we took this code:
```js
if (i > 5) {
@ -276,15 +264,18 @@ if (i > 5) {
}
```
...Then the code like this will give a syntax error:
...And rewrote it using a question mark:
```js
//+ no-beautify
```js no-beautify
(i > 5) ? alert(i) : *!*continue*/!*; // continue not allowed here
```
...Then it won't work. The code like this will give a syntax error:
That's just another reason not to use a question mark operator `'?'` instead of `if`.
[/warn]
````
## Labels for break/continue
@ -292,9 +283,7 @@ 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)`:
```js
//+ run no-beautify
```js run no-beautify
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
@ -324,8 +313,7 @@ We can put the `labelName` after a break statement, and it will break out of the
Like here:
```js
//+ run no-beautify
```js run no-beautify
*!*outer:*/!* for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
@ -347,15 +335,14 @@ So the control goes straight from `(*)` to `alert('Done!')`.
We can also move a label into the separate string:
```js
//+ no-beautify
```js no-beautify
outer:
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.
[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.
For example, it is impossible to do like this:
@ -365,18 +352,16 @@ break label; // jumps to label? No.
label: for(...)
```
The call to a `break` is only possible from inside the loop, and the label must be somewhere upwards from the `break`.
[/warn]
The call to a `break/continue` is only possible from inside the loop, and the label must be somewhere upwards from the directive.
````
## Summary
There are 3 types of loops in JavaScript:
<ul>
<li>`while` -- the condition is checked before each iteration.</li>
<li>`do..while` -- the condition is checked after each iteration.</li>
<li>`for` -- the condition is checked before each iteration, additional settings available.</li>
</ul>
- `while` -- the condition is checked before each iteration.
- `do..while` -- the condition is checked after each iteration.
- `for` -- the condition is checked before each iteration, additional settings available.
To make in "infinite" loop, usually the `while(true)` construct is used. Such a loop, just like any other, can be stopped with the `break` directive.

View file

@ -2,8 +2,7 @@ To be precise, the `if` must use a strict comparison `'==='`.
In reality though, probably a simple `'=='` would do.
```js
//+ no-beautify
```js no-beautify
if(browser == 'Edge') {
alert("You've got the Edge!");
} else if (browser == 'Chrome'

View file

@ -1,6 +1,8 @@
# Rewrite the "switch" into an "if"
importance: 5
[importance 5]
---
# Rewrite the "switch" into an "if"
Write the code using `if..else` which would correspond to the following `switch`:

View file

@ -1,7 +1,6 @@
The first two checks are a usual `case`. The third one is split into two cases:
```js
//+ run
```js run
let a = +prompt('a?', '');
switch (a) {

View file

@ -1,11 +1,12 @@
# Rewrite "if" into "switch"
importance: 4
[importance 4]
---
# Rewrite "if" into "switch"
Rewrite the code below using a single `switch` statement:
```js
//+ run
```js run
let a = +prompt('a?', '');
if (a == 0) {

View file

@ -5,12 +5,12 @@ A `switch` statement can replace multiple `if` checks.
It gives a more descriptive way to compare a value with multiple variants.
[cut]
## The syntax
It looks like this:
```js
//+ no-beautify
```js no-beautify
switch(x) {
case 'value1': // if (x === 'value1')
...
@ -26,20 +26,15 @@ switch(x) {
}
```
<ul>
<li>The value of `x` is checked for a strict equality to the value from the first `case`, that is: `value1`, then to the second `value2` and so on.
</li>
<li>If the equality is found -- `switch` starts to execute the code starting from the corresponding `case`, and to the nearest `break` (or to the end of `switch`).
</li>
<li>If no case matched then the `default` code is executed (if exists).</li>
</ul>
- The value of `x` is checked for a strict equality to the value from the first `case`, that is: `value1`, then to the second `value2` and so on.
- If the equality is found -- `switch` starts to execute the code starting from the corresponding `case`, and to the nearest `break` (or to the end of `switch`).
- If no case matched then the `default` code is executed (if exists).
## An example
An example of `switch` (the executed code is highlighted):
```js
//+ run
```js run
let a = 2 + 2;
switch (a) {
@ -67,8 +62,7 @@ Then `4`. That's the match, so the execution starts from `case 4` and till the n
An example without `break`:
```js
//+ run
```js run
let a = 2 + 2;
switch (a) {
@ -93,13 +87,12 @@ alert( 'Too big' );
alert( "I don't know such values" );
```
[smart header="Any expresion can be a `switch/case` argument"]
````smart header="Any expresion can be a `switch/case` argument"
Both `switch` and case allow arbitrary expresions.
For example:
```js
//+ run
```js run
let a = "1";
let b = 0;
@ -114,7 +107,7 @@ switch (+a) {
alert('no-no, see the code above, it executes');
}
```
[/smart]
````
## Grouping of "case"
@ -122,9 +115,7 @@ Several variants of `case` can be grouped.
For example, if we want the same code for `case 3` and `case 5`:
```js
//+ run no-beautify
```js run no-beautify
let a = 2 + 2;
switch (a) {
@ -153,9 +144,7 @@ Let's emphase that the equality check is always strict. The values must be of th
For example, let's consider the code:
```js
//+ run
```js run
let arg = prompt("Enter a value?")
switch (arg) {
case '0':
@ -174,10 +163,7 @@ switch (arg) {
}
```
<ol>
<li>For `0`, `1`, the first `alert` runs.</li>
<li>For `2` the second `alert` runs.</li>
<li>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.</li>
</ol>
1. For `0`, `1`, the first `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.

View file

@ -1,6 +1,8 @@
# Is "else" required?
importance: 4
[importance 4]
---
# Is "else" required?
The following function returns `true` if the parameter `age` is greater than `18`.

View file

@ -1,8 +1,9 @@
importance: 4
---
# Rewrite the function using '?' or '||'
[importance 4]
The following function returns `true` if the parameter `age` is greater than `18`.
Otherwise it asks for a confirmation and returns its result.
@ -20,7 +21,7 @@ function checkAge(age) {
Rewrite it, to perform the same, but without `if`, in a single line.
Make two variants of `checkAge`:
<ol>
<li>Using a question mark operator `'?'`</li>
<li>Using OR `||`</li>
</ol>
1. Using a question mark operator `'?'`
2. Using OR `||`

View file

@ -1,6 +1,8 @@
# Function min(a, b)
importance: 1
[importance 1]
---
# Function min(a, b)
Write a function `min(a,b)` which returns the least of two numbers `a` and `b`.

View file

@ -1,6 +1,5 @@
```js
//+ run demo
```js run demo
function pow(x, n) {
let result = x;

View file

@ -1,6 +1,8 @@
# Function pow(x,n)
importance: 4
[importance 4]
---
# Function pow(x,n)
Write a function `pow(x,n)` that returns `x` in power `n`. Or, in other words, multiplies `x` by itself `n` times and returns the result.
@ -12,6 +14,6 @@ pow(1, 100) = 1 * 1 * ...*1 = 1
Create a web-page that prompts for `x` and `n`, and then shows the result of `pow(x,n)`.
[demo /]
[demo]
P.S. In this task the function is allowed to support only natural values of `n`: integers up from `1`.

View file

@ -24,14 +24,13 @@ function showMessage() {
The `function` keyword goes first, then goes the *name of the function*, then a list of *parameters* in the brackets (empty in the example above) and finally the code of the function, also named "the function body".
<img src="function_basics.png">
![](function_basics.png)
Our new function can be called by it's name.
For instance:
```js
//+ run
```js run
function showMessage() {
alert( 'Hello everyone!' );
}
@ -54,8 +53,7 @@ A variable declared inside a function is only visible inside that function.
For example:
```js
//+ run
```js run
function showMessage() {
*!*
let message = "Hello, I'm JavaScript!"; // local variable
@ -73,8 +71,7 @@ alert( message ); // <-- Error! The variable is local to the function
A function can access an outer variable as well, for example:
```js
//+ run no-beautify
```js run no-beautify
let *!*userName*/!* = 'John';
function showMessage() {
@ -89,8 +86,7 @@ The function has a full access to an outer variable. It can modify it as well.
For instance:
```js
//+ run
```js run
let *!*userName*/!* = 'John';
function showMessage() {
@ -115,8 +111,7 @@ For example, if we had `let` before `userName` in the line (1) then the function
In the code below the local `userName` shadows the outer one:
```js
//+ run
```js run
let userName = 'John';
function showMessage() {
@ -131,18 +126,16 @@ function showMessage() {
// the function will create and use it's own userName
showMessage();
*!*
alert( userName ); // John, the function did not access the outer variable
*/!*
```
[smart header="Global variables"]
```smart header="Global variables"
Variables declared outside of any function, such as the outer `userName` in the code above, are called *global*.
Global variables are visible from any function.
They should only be used if the data is so important that it really must be seen from anywhere.
[/smart]
```
## Parameters
@ -150,39 +143,38 @@ We can pass arbitrary data to function using it's parameters (also called *funct
In the example below, the function has two parameters: `from` and `text`.
```js
//+ run no-beautify
```js run no-beautify
function showMessage(*!*from, text*/!*) { // arguments: from, text
from = "[" + from + "]";
alert(from + ': ' + text);
}
*!*
showMessage('Ann', 'Hello!'); // [Ann]: Hello!
showMessage('Ann', "What's up?"); // [Ann]: What's up?
showMessage('Ann', 'Hello!'); // Ann: Hello!
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`. The function can modify them.
When the function is called, the values in the brackets are copied to local variables `from` and `next`.
Note that the changes are not seen from outside:
Please note that because the function can modify them. The changes are made to copies, so they won't affect anything outside:
```js
//+ run
```js run
function showMessage(from, text) {
*!*
from = '[' + from + ']'; // changes the local from
from = '*' + from + '*'; // make "from" look nicer
*/!*
alert( from + ': ' + text );
}
let from = "Ann";
showMessage(from, "Hello");
showMessage(from, "Hello"); // *Ann*: Hello
// the old value of "from" is still here, the function modified a local copy
// the value of "from" is the same, the function modified a local copy
alert( from ); // Ann
```
@ -198,10 +190,9 @@ showMessage("Ann");
That's not an error. Such call would output `"Ann: undefined"`, because `text === undefined`.
But we can modify the function to detect missed parameter and assign a "default value" to it:
If we want to track when the function is called with a single argument and use a "default" value in this case, then we can check if `text` is defined, like here:
```js
//+ run
```js run
function showMessage(from, text) {
*!*
if (text === undefined) {
@ -218,13 +209,9 @@ showMessage("Ann"); // Ann: no text given
*/!*
```
Optional arguments are usually given at the end of the list.
There are also other ways to supply "default values" for missing arguments:
There are three most used ways to assign default values:
<ol>
<li>We can check if the argument equals `undefined`, and if yes then assign a default value to it. That's demonstrated by the example above.</li>
<li>Use operator `||`:
- Use operator `||`:
```js
function showMessage(from, text) {
@ -234,30 +221,25 @@ function showMessage(from, text) {
```
This way is shorter, but the argument is considered missing also if it's falsy, like an empty line, `0` or `null`.
</li>
<li>ES-2015 introduced a neater syntax for default values:
- ES-2015 introduced a neater syntax for default values:
```js
//+ run
```js run
function showMessage(from, *!*text = 'no text given'*/!*) {
alert( from + ": " + text );
}
showMessage("Ann"); // Ann: no text for you
showMessage("Ann"); // Ann: no text given
```
Here `'no text given'` is a string, but it can be any other value or expression, which is only evaluated and assigned if the parameter is missing.
</li>
</ol>
## Returning a value
A function can return a value into the calling code as the result.
A function can return a value back into the calling code as the result.
The simplest example would be a function that sums two values:
```js
//+ run no-beautify
```js run no-beautify
function sum(a, b) {
*!*return*/!* a + b;
}
@ -268,15 +250,18 @@ alert( result ); // 3
The directive `return` can be in any place of the function. When the execution reaches it, the function stops, and the value is returned to the calling code (assigned to `result` above).
There may be many uses of `return` in a function. For instance:
There may be many occurences of `return` in a single function. For instance:
```js
//+ run
```js run
function checkAge(age) {
if (age > 18) {
*!*
return true;
*/!*
} else {
*!*
return confirm('Got a permission from the parents?');
*/!*
}
}
@ -308,85 +293,74 @@ function showMovie(age) {
In the code above, if `checkAge(age)` returns `false`, then `showMovie` won't proceed to the `alert`.
[smart header="A function with an empty `return` or without it returns `undefined`"]
````smart header="A function with an empty `return` or without it returns `undefined`"
If a function does not return a value, it is the same as if it returns `undefined`:
```js
//+ run
```js run
function doNothing() { /* empty */ }
alert( doNothing() ); // undefined
alert( doNothing() === undefined ); // true
```
An empty `return` is also the same as `return undefined`:
```js
//+ run
```js run
function doNothing() {
return;
}
alert( doNothing() === undefined ); // true
```
[/smart]
````
## Naming a function [#function-naming]
Functions are actions. So their name is usually a verb.
Functions are actions. So their name is usually a verb. It should briefly, but as accurately as possible describe what the function does. So that a person who reads the code gets the right clue.
Usually, function names have verbal prefixes which vaguely describe the action.
It is a widespread practice to start a function with a verbal prefix which vaguely describes the action. There must be an agreement within the team on the meaning of the prefixes.
There is an agreement within the team on the terms here. For instance, functions that start with `"show"` -- usually show something:
```js
//+ no-beautify
showMessage(..) // shows a message
```
For instance, functions that start with `"show"` -- usually show something.
Function starting with...
<ul>
<li>`"get"` -- allow to get something,</li>
<li>`"calc"` -- calculate something,</li>
<li>`"create"` -- create something,</li>
<li>`"check"` -- check something and return a boolean, etc.</li>
</ul>
- `"get"` -- allow to get something,
- `"calc"` -- calculate something,
- `"create"` -- create something,
- `"check"` -- check something and return a boolean, etc.
Examples of such names:
```js
//+ no-beautify
getAge(..) // return the age (get it somehow)
calcSum(..) // calculate a sum and return the result
createForm(..) // create a form, usually returns it
checkPermission(..) // check a permission, return true/false
```js no-beautify
showMessage(..) // shows a message
getAge(..) // returns the age (gets it somehow)
calcSum(..) // calculates a sum and returns the result
createForm(..) // creates a form (and usually returns it)
checkPermission(..) // checks a permission, returns true/false
```
The agreement about prefixes is very convenient. A glance on a function name, even on the prefix of it, gives an understanding what it does and what kind of value it returns.
With prefixes at place, a glance at a function name gives an understanding what kind of work it does and what kind of value it returns.
[smart header="One function -- one action"]
A function should do exactly what is suggested by its name.
```smart header="One function -- one action"
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).
Few examples of breaking this rule:
<ul>
<li>`getAge` -- if shows the age to the visitor (should only get).</li>
<li>`createForm` -- if modifies the document, adds a form to it (should only create it and return).</li>
<li>`checkPermission` -- if displays the `access granted/denied` message or stores the result of the check (should only perform the check and return the result).</li>
</ul>
[/smart]
- `getAge` -- would be bad if it shows an `alert` with the age (should only get).
- `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).
[smart header="Ultrashort function names"]
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.
```
```smart header="Ultrashort function names"
Functions that are used *very often* sometimes have ultrashort names.
For example, [jQuery](http://jquery.com) framework defines a function `$`, [LoDash](http://lodash.com/) library has it's core function named `_`.
These are exceptions. Generally functions names should be concise, but descriptive.
[/smart]
```
## Summary
@ -398,11 +372,9 @@ function name(parameters, delimited, by, comma) {
}
```
<ul>
<li>Values passed to function as parameters are copied to its local variables.</li>
<li>A function may access outer variables. But it works only one-way. The code outside of the function doesn't see its local variables.</li>
<li>A function can return a value. If it doesn't then its result is `undefined`.</li>
</ul>
- 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 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.
@ -412,14 +384,9 @@ It is always easier to understand a function which gets parameters, works with t
Function naming:
<ul>
<li>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.</li>
<li>A function is an action, so function names are usually verbal.</li>
<li>There is a bunch of commonly adapted verbal prefixes like `create…`, `show…`, `get…`, `check…` etc which can help. The main point is to be consistent about their meaning.</li>
</ul>
Functions are the main building blocks of scripts. Now we covered the basics, so we actually can use them.
But we are going to return to them, going more deeply in their advanced features.
- 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.
- There is a bunch of commonly adapted verbal prefixes like `create…`, `show…`, `get…`, `check…` etc which can help. The main point is to be consistent about their meaning.
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.

View file

@ -1,40 +1,6 @@
# Function expressions
Function Expression is an analternative syntax for declaring a function.
It looks like this:
```js
//+ run
let func = function(parameters) {
// body
};
```
For instance:
```js
//+ run
let sayHi = function(person) {
alert( `Hello, ${person}` );
};
sayHi('John'); // Hello, John
```
The function `sayHi` created in the example above is identical to:
```js
function sayHi(person) {
alert( `Hello, ${person}` );
}
```
## Function is a value
Function Expression clearly demonstrates one simple thing.
**In JavaScript, a function is a value.**
In JavaScript, a function is a value.
We can declare it as we did before:
@ -44,7 +10,9 @@ function sayHi() {
}
```
...Or as a function expression:
This syntax is called a "Function Declaration".
...But there is another way of creating a function:
```js
let sayHi = function() {
@ -52,14 +20,17 @@ let sayHi = function() {
}
```
The meaning of these lines is the same: create a function, put it into the variable `sayHi`.
The latter syntax is called a "Function Expression".
Yes, no matter, how it 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 show it using `alert`:
Yes, no matter, how the function is defined -- it's just a value, stored in the variable `sayHi`.
```js
//+ run
Let's stress: a function is not a "magical language structure". Both syntaxes mean the same: create a special "function" value and put it into the variable.
We can even print out that value using `alert`:
```js run
function sayHi() {
alert( "Hello" );
}
@ -69,12 +40,18 @@ alert( sayHi ); // shows the function code
*/!*
```
Note that there are no brackets after `sayHi` in the last line. The function is not called there. Instead the `alert` shows it's string representation, that is the source code.
Note that there are no brackets after `sayHi` in the last line.
As the function is a value, we can also copy it to another variable:
The function is not called there. There are programming languages where any use of 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, like print string representation, that is the source code.
```js
//+ run no-beautify
It is a special value though, in the sense that we can call it using brackets: `"sayHi()"`, but only if we explicitly put brackets into the code.
Other actions with functions are also available, in the same fashion as with other values.
We can copy a function to another variable:
```js run no-beautify
function sayHi() { // (1) create
alert( "Hello" );
}
@ -83,88 +60,81 @@ let func = sayHi; // (2) copy
func(); // Hello // (3) call the copy (it works)!
sayHi = null; // (4) nullify the old variable
sayHi(); // error! (null now)
sayHi = null; // (4) store null in the old variable
sayHi(); // error! (now null, the function is overwritten)
func(); // the copy still works
```
<ol>
<li>Function declaration `(1)` creates the function and puts it into the variable `sayHi`"</li>
<li>Line `(2)` copies it into variable `func`.
In more detail:
1. Function Declaration `(1)` creates the function and puts it into the variable `sayHi`"
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.</li>
<li>At the moment `(3)` the function can be called both as `sayHi()` and `func()`.</li>
<li>...We can overwrite `sayHi` easily. As `func`, it is a normal variable. Naturally, the call attempt would fail in the case `(4)`.</li>
</ol>
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.
3. At the moment `(3)` the function can be called both as `sayHi()` and `func()`.
4. ...We can overwrite `sayHi`, it have had the function, but now it stores `null`. Naturally, the call attempt would fail.
5. But `func` still has the function, it is still callable.
Again, it does not matter how to create a function here. If we change the first line above into a Function Expression: `let sayHi = function() {`, everything would be the same.
Note, that we could use a Function Expression in the first line: `let sayHi = function() { ... }`. Everything would work the same.
[smart header="A function is a value representing an \"action\""]
```smart header="A function is a value representing an \"action\""
Regular values like strings or numbers represent the *data*.
A function can be perceived as an *action*.
We can copy it between variables and run when we want.
[/smart]
Function Expressions are very convenient for creating an action in-place and passing it along the code.
For instance, let's consider the following real-life task.
Function `ask(question, yes, no)` should accept a textual `question` and two other functions: `yes` and `no`. It asks a question and, if the user responds positively, executes `yes()`, otherwise `no()`.
It could look like this:
```js
//+ run
function ask(question, yes, no) {
if (confirm(question)) yes()
else no();
}
```
In real-life `ask` would be usually much more complex, because it would draw a nice window and take care about how to present the `question` to the user etc. But here we consider the general form of such function, so `confirm` is enough.
## Why Function Expression?
So, how do we use it?
Now let's go back: we have two ways of declaring a function. Why so? What's about Function Expressions that makes it a good addition?
If we had only Function Declarations in our toolbox, we could declare `showOk/showCancel` and pass them:
**Function Expressions are very convenient for creating an action in-place and passing it along the code.**
There is a built-in [setTimeout](https://developer.mozilla.org/en/docs/Web/API/WindowTimers/setTimeout) function in JavaScript, that can schedule a function to run after a given period of time.
```js
function showOk() {
alert( "Ok, proceeding." );
The syntax is: `setTimeout(func, ms, ...arguments)`:
`func`
: The function to run.
`ms`
: The number of milliseconds (1/1000 of a second) to schedule the call after.
`arguments`
: One of more arguments to pass to the function.
What if we wanted to say "Hello" in a second?
One could write a code like this:
```js run
function sayHi() {
alert("Hello!")
}
function showCancel() {
alert( "Execution canceled." );
}
// usage
ask("Should we proceed?", showOk, showCancel);
setTimeout(sayHi, 1000);
```
...But Function Expressions allow us to solve the task much more elegantly:
That would work. But we have declared a function that has no future use.
```js
ask("Should we proceed?",
function() { alert( "Ok, proceeding." ); },
function() { alert( "Execution canceled." ); },
);
A Function Expression is much cleaner:
```js run
setTimeout(function() {
alert("Hello!")
}, 1000);
```
Here we can declare actions in-place, exactly when we need it.
Such functions are sometimes called "anonymous" meaning that they are defined without a name. So to say, we can't reuse them outside of `ask`, because there is no variable for them. But for this kind of task it's exactly what we want.
Such functions are sometimes called "anonymous" meaning that they are defined without a name. So to say, we can't reuse them, because there is no variable for them. But here it's exactly what we want.
Creating functions in-place is very natural and in the spirit of JavaScript.
## Function Expression vs Function Declaration
The "classic" syntax of the function that looks like `function name(params) {...}` is called a "Function Declaration".
Let's briefly reformulate the distinction between these two.
We can formulate the following distinction:
<ul>
<li>*Function Declaration* -- is a function, declared as a separate statement.
- *Function Declaration:* a function, declared as a separate statement, in the main code flow.
```js
// Function Declaration
@ -172,11 +142,9 @@ function sum(a, b) {
return a + b;
}
```
- *Function Expression:* a function, created in the context of an expression.
</li>
<li>*Function Expression* -- is a function, created in the context of an expression.
In the example above the function was created in the context of an "assignment expression":
Here the function is created in the context of an "assignment expression", it's an expression:
```js
// Function Expression
let sum = function(a, b) {
@ -184,29 +152,35 @@ let sum = function(a, b) {
}
```
</li>
</ul>
Here the function is created inside another function call:
The main differences between them are visibility and creation time.
```js
// Function Expression
setTimeout(function() {
alert("Hello!")
}, 1000);
```
### Creation time
There are more differences between them besides the syntax.
**Function Expressions are created when the execution reaches them.**
That's easy. Once the execution flow passes to the right side of the assignment -- here we go, the function is made and can be used (assigned, called etc) from now on.
That's rather obvious. Once the execution flow passes to the right side of the assignment -- here we go, the function is made and can be used (assigned, called etc) from now on.
Function Declarations are different. They are treated in a special way.
Function Declarations are different.
When JavaScript prepares to run the code block, it looks for Function Declarations in it and creates the functions. We can think of it as an "initialization stage". Then it runs the code.
**Function Declarations are created before the script or a code block begins to execute and are visible in the whole scropt/block.**
**Function Declarations are created before the code block begins to execute and is visible in the whole block.**
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". Then it runs the code.
If the Function Declaration is not inside a code block `{...}`, then it is created when the script starts and is visible in the whole script. All declarations in the examples above are not in the code block.
As a natural effect, a function declared as Function Declaration can be called earlier than it is defined.
For instance, this works:
```js
//+ run refresh untrusted
```js run refresh untrusted
*!*
sayHi("John"); // Hello, John
*/!*
@ -220,8 +194,7 @@ Function Declaration `sayHi` is created when the script starts and is visible ev
...And if there were Function Expression, then it wouldn't work:
```js
//+ run refresh untrusted
```js run refresh untrusted
*!*
sayHi("John"); // error!
*/!*
@ -231,17 +204,18 @@ let sayHi = function(name) { // (*)
};
```
Function Expressions are created in the process of evaluation of the expression with them. So, in the code above, the function would be created and assigned to `sayHi` only when the execution reaches the line `(*)`. Too late.
Function Expressions are created when the execution reaches them. No magic. That would happen only in the line `(*)`. Too late.
Now let's explore the visibility thing.
### Visibility
Now let's explore the visibility differences.
Imagine, we need to declare `welcome()` depending on some data we get in run-time.
For instance:
```js
//+ run
```js run
let age = prompt("What is your age?", 18);
if (age < 18) {
@ -263,33 +237,34 @@ welcome(); // Error: welcome is not defined
*/!*
```
In the code above, we mean to create function `welcome()` depending on the `age`. So, that it can't be called later, probably from another place of the code, in case of an event or such.
In the code above, we'd like to create function `welcome()` depending on the `age`. So, that it can't be called later, probably from another place of the code, in case of an event or such, doesn't matter now.
But that doesn't work, because Function Declaration `function welcome()` is visible only inside the block where it resides.
But that doesn't work.
**A Function Declaration is visible only inside the code block where it resides.**
We can call it from within the block, but not from outside:
```js
//+ run
let age = 15; // let's consider 15 as an example
```js run
let age = 16; // take 16 as an example
if (age < 18) {
*!*
welcome(); // \ (works)
welcome(); // \ (runs)
*/!*
// |
function welcome() { // |
alert("Hello!"); // | available here
alert("Hello!"); // | welcome is available everywhere in its block
} // |
// |
*!*
welcome(); // / (works)
welcome(); // / (runs)
*/!*
} else {
// \
function welcome() { // |
alert("Greetings!"); // | available here
alert("Greetings!"); // | another welcome is limited to its own block
} // |
// /
}
@ -299,13 +274,9 @@ welcome(); // Error: outside of the block there's no welcome
*/!*
```
What can we do to fix the problem? One of the ways is to use a Function Expression and assign `welcome` to the variable which has the proper visibility:
```js
//+ run
What can we do to fix the problem? One of the ways is to use a Function Expression and assign `welcome` to the variable which is declared outside of `if` and has the proper visibility:
```js run
let age = prompt("What is your age?", 18);
let welcome;
@ -331,9 +302,7 @@ welcome(); // ok now
Or we could go on to simplify it even further using a question mark operator `?`:
```js
//+ run
```js run
let age = prompt("What is your age?", 18);
let welcome = (age < 18) ?
@ -349,26 +318,19 @@ welcome(); // ok now
*/!*
```
[smart header="So, a Declaration or an Expression?"]
As a rule of thumb, if we want just to declare a function, then a Function Declaration is prefered. It gives more freedom in how to organize our code, because we can put "helper functions" below. It's also a little bit easier to read and look up them in the text.
Only if a Function Declaration does not suit us for some reason, then a Function Expression should be used.
[/smart]
```smart header="What to choose: a Declaration or an 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. It's also a little bit easier to look up Function Declarations in the text.
But if a Function Declaration does not suit us for some reason, or we need to create an anonymous function "at-place", then a Function Expression should be used.
```
## Summary
<ul>
<li>
Functions are values. They can be assigned, copied or declared in any place of the code.
<ul>
<li>If the function is declared as a separate statement -- it's called a Function Declaration.</li>
<li>If the function is created as a part of an expression -- it's a Function Expression.</li>
</ul>
</li>
<li>Function Declarations are processed before the code block is executed. So they are available everywhere in the block. Or in the whole script if not enclosed in a block.</li>
<li>Function Expressions are created when the execution flow reaches them.</li>
</ul>
- 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 -- it's called a Function Declaration.
- If the function is created as a part of an expression -- it's a Function Expression.
- Function Declarations are processed before the code block is executed. So they are available everywhere in the block. Or in the whole script if not enclosed in a block.
- Function Expressions are created when the execution flow reaches them.
Novice programmers sometimes overuse Function Expression by creating many functions with `let func = function()`.
@ -376,8 +338,7 @@ But in most cases Function Declaration is preferable.
Compare, which code is more readable:
```js
//+ no-beautify
```js no-beautify
// Function Expression
let f = function() { ... }
@ -385,6 +346,6 @@ let f = function() { ... }
function f() { ... }
```
Function Declaration is shorter and more obvious. The additional bonus -- it can be called before the declaration.
Function Declaration is shorter and more obvious. The additional bonus -- it can be called before the actual declaration.
Use Function Expression only when the function must be created at-place, inside another expression.
Use Function Expression to write elegant code when the function must be created at-place, inside another expression or when Function Declaration doesn't fit well for the task.

View file

@ -1,12 +1,8 @@
The HTML code:
```html
<!--+ src="index.html" -->
```
[html src="index.html"]
For the file `alert.js` in the same folder:
```js
//+ src="alert.js"
```
[js src="alert.js"]

View file

@ -1,7 +1,9 @@
importance: 5
---
# Show an alert with an external script
[importance 5]
Take the solution of the previous task [](/task/hello-alert). Modify it by extracting the script content into an external file `alert.js`, residing in the same folder.
Take the solution of the previous task <info:task/hello-alert>. Modify it by extracting the script content into an external file `alert.js`, residing in the same folder.
Open the page, ensures that the alert works.

View file

@ -1,6 +1,5 @@
Answers:
<ol>
<li>The first is `big.js`, that's a normal sequence for external `<script>` tags.</li>
<li>The first is `small.js`, because `async` makes script behave independently of each other and the page. The first to loads runs first.</li>
<li>The first is `big.js`, because "deferred" scripts keep relative execution order.</li>
</ol>
1. The first is `big.js`, that's a normal sequence for external `<script>` tags.
2. The first is `small.js`, because `async` makes script behave independently of each other and the page. The first to loads runs first.
3. The first is `big.js`, because "deferred" scripts keep relative execution order.

View file

@ -1,6 +1,8 @@
# Which script executes first?
importance: 4
[importance 4]
---
# Which script executes first?
In the questions below, there are two scripts: `small.js` and `big.js`.

View file

@ -18,7 +18,6 @@ We can give a full URL al well, for instance:
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.2.0/lodash.js"></script>
```
To attach several scripts, use multiple tags:
```html
@ -27,7 +26,7 @@ To attach several scripts, use multiple tags:
```
[smart]
```smart
As a rule, only simplest scripts are put into HTML. More complex ones reside in separate files.
The benefit of a separate file is that the browser will download it and then store in its [cache](https://en.wikipedia.org/wiki/Web_cache).
@ -35,10 +34,9 @@ The benefit of a separate file is that the browser will download it and then sto
After it, other pages which want the same script will take it from the cache instead of downloading it. So the file is actually downloaded only once.
That saves traffic and makes pages faster.
[/smart]
```
[warn header="If `src` is set, the script content is ignored."]
````warn header="If `src` is set, the script content is ignored."
A single `<script>` tag may not have both an `src` and the code inside.
This won't work:
@ -59,7 +57,7 @@ The example above can be split into two scripts to work:
alert(1);
</script>
```
[/warn]
````
## Asynchronous scripts: defer/async
@ -69,8 +67,7 @@ As we noted before, when the browser meets a `<script>` tag, it must execute it
For example, in the code below -- until all rabbits are counted, the bottom `<p>` is not shown:
```html
<!--+ run height=100 -->
```html run height=100
<!DOCTYPE HTML>
<html>
<body>
@ -118,7 +115,7 @@ Sometimes, a script may contain a very important code that really must be loaded
Usually it's ok that a visitor can see the page content while the script is loading.
[warn header="Blocking is dangerous"]
````warn header="Blocking is dangerous"
There are situations when such blocking is even dangerous.
Let's say we attach a script from the banner system, or a 3rd-party integration code.
@ -139,15 +136,14 @@ And what if their server is overloaded and responds slowly? Our visitors will wa
Here's an example of such "slow" script (the delay is artificial here):
```html
<!--+ run height=100 -->
```html run height=100
Wait. The text belown will shown up only after the script executes.
<script src="/article/external-script/banner.js?speed=0"></script>
<p>…Important information!</p>
```
[/warn]
````
So, how to "fix" the blocking behavior?
@ -155,41 +151,35 @@ Our first attempt could be to put all such scripts to the bottom of the `<body>`
But the solution is not perfect:
<ol>
<li>The script won't start loading until the whole page loads. If the page is large, then the delay may be significant. We'd like the browser to start loading a script early, but still do not block the page.</li>
<li>If there is more than one script at the bottom of the page, and the first script is slow, then the second one will have to wait for it. Browser executes only one `<script>` tag in one moment. So scripts queue one after another. That's not always welcome: ads and counter should run independently.</li>
</ol>
1. The script won't start loading until the whole page loads. If the page is large, then the delay may be significant. We'd like the browser to start loading a script early, but still do not block the page.
2. If there is more than one script at the bottom of the page, and the first script is slow, then the second one will have to wait for it. Browser executes only one `<script>` tag in one moment. So scripts queue one after another. That's not always welcome: ads and counter should run independently.
And here come the attributes `async` and `defer`.
<dl>
<dt>The `async` attribute.</dt>
<dd>The script is executed asynchronously. In other words, when the browser meets `<script async src="...">`, it does not stop showing the page. It just initiates script loading and goes on. When the script loads -- it runs.</dd>
<dt>The `defer` attribute.</dt>
The `async` attribute.
: The script is executed asynchronously. In other words, when the browser meets `<script async src="...">`, it does not stop showing the page. It just initiates script loading and goes on. When the script loads -- it runs.
The `defer` attribute.
<dd>The script with `defer` also executes asynchronously, like async. But there are two essential differences:
<ol>
<li>The browser guarantees to keep the relative order of "deferred" scripts.</li>
<li>A "deferred" script always executes after HTML-document is fully loaded.</li>
</ol>
1. The browser guarantees to keep the relative order of "deferred" scripts.
2. A "deferred" script always executes after HTML-document is fully loaded.
We'll discuss them more in-depth further in this chapter.
</dl>
[smart header="`async` together with `defer`"]
```smart header="`async` together with `defer`"
We can't use both `defer` and `async` on a single script. If we do that, `defer` will be ignored.
[/smart]
```
[warn header="Attributes `async/defer` -- only for external scripts"]
```warn header="Attributes `async/defer` -- only for external scripts"
Attribute `async/defer` work only when set on a script with `src`.
On a script without `src` like <code>&lt;script&gt;...&lt;/script&gt;</code>, they will be ignored.
[/warn]
```
Let's modify the "blocking script" example that we've seen before, adding `async`:
```html
<!--+ run height=100 -->
```html run height=100
Wait. The text belown will shown up only after the script executes.
<script *!*async*/!* src="/article/external-script/banner.js?speed=0"></script>
@ -199,7 +189,6 @@ Wait. The text belown will shown up only after the script executes.
Now if we run it, we'll see that the whole document is displayed immediately, and the external script runs when it loads.
## Defer vs Async: order
Let's discuss these differences in more detail.
@ -246,17 +235,14 @@ And in another case a script may need the whole document to do some work with it
## Summary
<ul>
<li>Scripts in an external file can be inserted on the page via `<script src="path"></script>`.</li>
<li>Normally, the browser doesn't show the document after the script until it executes. Unless the script has `async` or `defer` attributes.</li>
<li>Both `async` and `defer` allow the browser to start script loading and then continue to parse/show the page. They only work on external scripts.</li>
<li>The difference is that `defer` keeps the relative script order and always executes after the document is fully loaded. In contrast, `async` script executes when it loads, without any conditions.</li>
</ul>
- Scripts in an external file can be inserted on the page via `<script src="path"></script>`.
- Normally, the browser doesn't show the document after the script until it executes. Unless the script has `async` or `defer` attributes.
- Both `async` and `defer` allow the browser to start script loading and then continue to parse/show the page. They only work on external scripts.
- The difference is that `defer` keeps the relative script order and always executes after the document is fully loaded. In contrast, `async` script executes when it loads, without any conditions.
Before inserting an external `<script src="…">` tag, we should always consider the side-effect of blocking the page rendering. Especially if it's a 3rd-party script. And if we don't want that, then `defer/async` can come in handy.
[smart header="Running ahead..."]
````smart header="Running ahead..."
For an advanced reader who knows that new tags can be added on page dynamically, we'd like to note that dynamic `<script>` tags behave as if they have `async` by default.
In other words, they run as they load without an order.
@ -280,7 +266,5 @@ addScript('3.js'); // that is: 1 -> 2 -> 3
```
We'll cover page dynamic tags and page manipulation in detail later, in the second part of the tutorial.
[/smart]
````

View file

@ -2,8 +2,7 @@
Вы могли заметить следующие недостатки, сверху-вниз:
```js
//+ no-beautify
```js no-beautify
function pow(x,n) // <- отсутствует пробел между аргументами
{ // <- фигурная скобка на отдельной строке
var result=1; // <- нет пробелов вокруг знака =

View file

@ -1,11 +1,12 @@
# Ошибки в стиле
importance: 4
[importance 4]
---
# Ошибки в стиле
Какие недостатки вы видите в стиле этого примера?
```js
//+ no-beautify
```js no-beautify
function pow(x,n)
{
var result=1;

View file

@ -7,11 +7,12 @@ That is actually an art of programming -- to take a complex task and describe it
One thing to help is a good code style. In this chapter we cover it's components.
[cut]
## Syntax
A cheatsheep with the rules (more details below):
A cheatsheet with the rules (more details below):
<img src="code-style.png">
![](code-style.png)
<!--
```js
function pow(x, n) {
@ -34,6 +35,7 @@ if (n < 0) {
alert( pow(x, n) );
}
```
-->
Nothing is "carved in stone" here, so let's discuss the rules in detail.
@ -42,56 +44,62 @@ Nothing is "carved in stone" here, so let's discuss the rules in detail.
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.
If the code has only one line, then there are options:
A corner-case if 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:
<!--
```js
//+ no-beautify
```js no-beautify
if (n < 0) {alert(`Power ${n} is not supported`);}
if (n < 0) alert(`Power ${n} is not supported`);
if (n < 0)
alert(`Power ${n} is not supported`);
if (n < 0) {
alert(`Power ${n} is not supported`);
}
```
-->
<img src="figure-bracket-style.png">
![](figure-bracket-style.png)
For a real short code like `if (cond) return null`, one line is acceptable... But still a separate line for each statement is usually easier to read.
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.
### Line length
Line length is limited. No one likes to eye-follow a long horizontal line. Doesn't matter if it's a text or a OR'ed list of `if` conditions. It's better to split it.
The maximal line length should be limited. No one likes to eye-follow a long horizontal line. Doesn't matter if it's a text or an OR'ed list of `if` conditions. It's better to split it.
Maximal line length is agreed on the team-level. It's usually 80 or 120 characters.
The maximal line length is agreed on the team-level. It's usually 80 or 120 characters.
### Indents
There are two types of indents:
<ul>
<li>**A horizontal indent: 2(4) spaces.**
- **A horizontal indent: 2(4) spaces.**
Usually spaces are used, because they allow more flexible configurations of indents than the "Tab" symbol.
A horizantal identation is made using either spaces or the "Tab" symbol (displayed as 2 or 4 or even 8 spaces, but 8 is too much).
For instance, we can align the arguments with the opening bracket:
Which one to choose is a kind of an old holy war. Spaces are a little more common nowadays.
```js
//+ no-beautify
One of advantages of spaces over tabs is that they allow more flexible configurations of indents than the "Tab" symbol.
For instance, we can align the arguments with the opening bracket, like this:
```js no-beautify
show(parameters,
aligned,
one,
after,
another);
another
) {
// ...
}
```
</li>
<li>**A vertical indent, line breaks for splitting the code in logical blocks.**
- **A vertical indent, line breaks 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:
@ -108,35 +116,20 @@ function pow(x, n) {
```
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.
</li>
</ul>
### A semicolon
Semicolons must be after each statement. Even if could possiblty be skipped.
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.
But in JavaScript a line break is sometimes interpreted as a semicolon and sometimes not. That leaves a place for programming errors, so semicolons must be at place, just as discussed [before](#semicolon).
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.
## The Naming
The general rule:
<ul>
<li>Variable name should be a noun.</li>
<li>Function name should be a verb, or start with a verbal prefix. There can be exceptions if covered by another rule.</li>
</ul>
English language and camel-case notation is used.
Also discussed before -- [function naming](#function-naming) and [variables naming](#variable-naming).
## Nesting levels
### Nesting levels
There should not be too many nesting levels.
Sometimes it's a good idea to [use the "continue"](#continue) directive in the loop to evade extra nesting in `if(..) { ... }`:
Sometimes it's a good idea to use the ["continue"](info:while-for#continue) directive in the loop to evade extra nesting in `if(..) { ... }`:
Instead of:
@ -191,7 +184,7 @@ function isEven(n) {
If there's a `return` inside the `if` block, then we need no `else` after it.
...Of course we can write even shorter here:
By the way, the very this function we can be implemented in a shorter way:
```js
function isEven(n) {
@ -199,19 +192,40 @@ function isEven(n) {
}
```
...But if the code `!(n % 2)` is less obvious for you than the former variant then use the former one.
But the code became less obvious.
...Of course we can write even shorter here:
**The most important not shortness, but simplicity and readability of the code.**
A shorter code is not always simpler to understand and maintain.
## The Naming
As a general rule:
- A variable name should be a noun.
- A function name should be a verb, or start with a verbal prefix. There can be exceptions if covered by another rule.
The camel-case notation is used for long words.
We discussed that before -- see [function basics](info:function-basics#function-naming) and [variables](info:variables#variable-naming).
A name should be descriptive and long enough, with the exception of:
- loop counter variables, `i` is a well-recognized name for a loop counter.
- functions/libraries that are well known and used very often.
- other cases when the code readability doesn't suffer.
**The most important for us is not shortness, but simplicity and readability of the code.**
It's quite not always the case that a brief code is simpler to understand.
## 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.
Sometimes following this rule may be not easy, but it's a definitely good thing. So what's with the comments?
Sometimes following this rule may be not easy, but it's a definitely good thing.
A separate function is not only easier to test and debug -- it's very existance is a great comment.
...So what's here about comments?
A separate function is not only easier to test and debug -- it's very existance is a great comment!
For instance, compare the two functions `showPrimes(n)` below. Each one outputs [prime numbers](https://en.wikipedia.org/wiki/Prime_number) up to `n`.
@ -256,8 +270,7 @@ The second variant is easier to understand isn't it? Instead of the code piece w
There are three way to place the "helper" functions used in the code.
<ol>
<li>Above the code that uses them:
1. Above the code that uses them:
```js
// *!*function declarations*/!*
@ -278,9 +291,7 @@ var elem = createElement();
setHandler(elem);
walkAround();
```
</li>
<li>Code first, then functions
2. Code first, then functions
```js
// *!*the code which uses the functions*/!*
@ -302,10 +313,7 @@ function walkAround() {
...
}
```
</li>
<li>Mixed, a function is described when it's first used.</li>
</ol>
3. Mixed, a function is described when it's first used.
Most of time, the second variant is preferred.
@ -331,17 +339,14 @@ That's because when reading a code, we first want to know "what it does". If the
## Хорошие комментарии
А какие комментарии полезны и приветствуются?
<ul>
<li>**Архитектурный комментарий -- "как оно, вообще, устроено".**
- **Архитектурный комментарий -- "как оно, вообще, устроено".**
Какие компоненты есть, какие технологии использованы, поток взаимодействия. О чём и зачем этот скрипт. Взгляд с высоты птичьего полёта. Эти комментарии особенно нужны, если вы не один, а проект большой.
Для описания архитектуры, кстати, создан специальный язык [UML](http://ru.wikipedia.org/wiki/Unified_Modeling_Language), красивые диаграммы, но можно и без этого. Главное -- чтобы понятно.
</li>
<li>**Справочный комментарий перед функцией -- о том, что именно она делает, какие параметры принимает и что возвращает.**
- **Справочный комментарий перед функцией -- о том, что именно она делает, какие параметры принимает и что возвращает.**
Для таких комментариев существует синтаксис [JSDoc](http://en.wikipedia.org/wiki/JSDoc).
@ -362,9 +367,7 @@ function pow(x, n) {
Кстати, они автоматически обрабатываются многими редакторами, например [Aptana](http://aptana.com) и редакторами от [JetBrains](http://www.jetbrains.com/), которые учитывают их при автодополнении, а также выводят их в автоподсказках при наборе кода.
Кроме того, есть инструменты, например [JSDoc 3](https://github.com/jsdoc3/jsdoc), которые умеют генерировать по таким комментариям документацию в формате HTML. Более подробную информацию об этом можно также найти на сайте [](http://usejsdoc.org/).
</li>
</ul>
Кроме того, есть инструменты, например [JSDoc 3](https://github.com/jsdoc3/jsdoc), которые умеют генерировать по таким комментариям документацию в формате HTML. Более подробную информацию об этом можно также найти на сайте <http://usejsdoc.org/>.
**...Но куда более важными могут быть комментарии, которые объясняют не *что*, а *почему* в коде происходит именно это!**
@ -374,30 +377,23 @@ function pow(x, n) {
Например:
<dl>
<dt>Есть несколько способов решения задачи. Почему выбран именно этот?</dt>
<dd>
Например, пробовали решить задачу по-другому, но не получилось -- напишите об этом. Почему вы выбрали именно этот способ решения? Особенно это важно в тех случаях, когда используется не первый приходящий в голову способ, а какой-то другой.
Есть несколько способов решения задачи. Почему выбран именно этот?
: Например, пробовали решить задачу по-другому, но не получилось -- напишите об этом. Почему вы выбрали именно этот способ решения? Особенно это важно в тех случаях, когда используется не первый приходящий в голову способ, а какой-то другой.
Без этого возможна, например, такая ситуация:
<ul>
<li>Вы открываете код, который был написан какое-то время назад, и видите, что он "неоптимален".</li>
<li>Думаете: "Какой я был дурак", и переписываете под "более очевидный и правильный" вариант.</li>
<li>...Порыв, конечно, хороший, да только этот вариант вы уже обдумали раньше. И отказались, а почему -- забыли. В процессе переписывания вспомнили, конечно (к счастью), но результат - потеря времени на повторное обдумывание.</li>
</ul>
- Вы открываете код, который был написан какое-то время назад, и видите, что он "неоптимален".
- Думаете: "Какой я был дурак", и переписываете под "более очевидный и правильный" вариант.
- ...Порыв, конечно, хороший, да только этот вариант вы уже обдумали раньше. И отказались, а почему -- забыли. В процессе переписывания вспомнили, конечно (к счастью), но результат - потеря времени на повторное обдумывание.
Комментарии, которые объясняют выбор решения, очень важны. Они помогают понять происходящее и предпринять правильные шаги при развитии кода.
</dd>
<dt>Какие неочевидные возможности обеспечивает этот код? Где ещё они используются?</dt>
<dd>
В хорошем коде должно быть минимум неочевидного. Но там, где это есть -- пожалуйста, комментируйте.
</dd>
</dl>
Какие неочевидные возможности обеспечивает этот код? Где ещё они используются?
: В хорошем коде должно быть минимум неочевидного. Но там, где это есть -- пожалуйста, комментируйте.
[smart header="Комментарии -- это важно"]
```smart header="Комментарии -- это важно"
Один из показателей хорошего разработчика -- качество комментариев, которые позволяют эффективно поддерживать код, возвращаться к нему после любой паузы и легко вносить изменения.
[/smart]
```
## Руководства по стилю
@ -407,13 +403,11 @@ function pow(x, n) {
Большинство есть на английском, сообщите мне, если найдёте хороший перевод:
<ul>
<li>[Google JavaScript Style Guide](http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml)</li>
<li>[JQuery Core Style Guidelines](http://docs.jquery.com/JQuery_Core_Style_Guidelines)</li>
<li>[Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript)</li>
<li>[Idiomatic.JS](https://github.com/rwldrn/idiomatic.js) (есть [перевод](https://github.com/rwldrn/idiomatic.js/tree/master/translations/ru_RU))</li>
<li>[Dojo Style Guide](http://dojotoolkit.org/community/styleGuide)</li>
</ul>
- [Google JavaScript Style Guide](http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml)
- [JQuery Core Style Guidelines](http://docs.jquery.com/JQuery_Core_Style_Guidelines)
- [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript)
- [Idiomatic.JS](https://github.com/rwldrn/idiomatic.js) (есть [перевод](https://github.com/rwldrn/idiomatic.js/tree/master/translations/ru_RU))
- [Dojo Style Guide](http://dojotoolkit.org/community/styleGuide)
Для того, чтобы начать разработку, вполне хватит элементов стилей, обозначенных в этой главе. В дальнейшем, посмотрев эти руководства, вы можете выработать и свой стиль, но лучше не делать его особенно "уникальным и неповторимым", себе дороже потом будет с людьми сотрудничать.
@ -423,11 +417,9 @@ function pow(x, n) {
Самые известные -- это:
<ul>
<li>[JSLint](http://www.jslint.com/) -- проверяет код на соответствие [стилю JSLint](http://www.jslint.com/lint.html), в онлайн-интерфейсе вверху можно ввести код, а внизу различные настройки проверки, чтобы сделать её более мягкой. </li>
<li>[JSHint](http://www.jshint.com/) -- вариант JSLint с большим количеством настроек.</li>
<li>[Closure Linter](https://developers.google.com/closure/utilities/) -- проверка на соответствие [Google JavaScript Style Guide](http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml).</li>
</ul>
- [JSLint](http://www.jslint.com/) -- проверяет код на соответствие [стилю JSLint](http://www.jslint.com/lint.html), в онлайн-интерфейсе вверху можно ввести код, а внизу различные настройки проверки, чтобы сделать её более мягкой.
- [JSHint](http://www.jshint.com/) -- вариант JSLint с большим количеством настроек.
- [Closure Linter](https://developers.google.com/closure/utilities/) -- проверка на соответствие [Google JavaScript Style Guide](http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml).
В частности, JSLint и JSHint интегрированы с большинством редакторов, они гибко настраиваются под нужный стиль и совершенно незаметно улучшают разработку, подсказывая, где и что поправить.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 76 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

After

Width:  |  Height:  |  Size: 152 KiB

Before After
Before After

View file

@ -12,23 +12,20 @@
Операторы разделяются точкой с запятой:
```js
//+ run no-beautify
```js run no-beautify
alert('Привет'); alert('Мир');
```
Как правило, перевод строки тоже подразумевает точку с запятой. Так тоже будет работать:
```js
//+ run no-beautify
```js run no-beautify
alert('Привет')
alert('Мир')
```
...Однако, иногда JavaScript не вставляет точку с запятой. Например:
```js
//+ run no-beautify
```js run no-beautify
let a = 2
+3
@ -37,8 +34,7 @@ alert(a); // 5
Бывают случаи, когда это ведёт к ошибкам, которые достаточно трудно найти и исправить, например:
```js
//+ run
```js run
alert("После этого сообщения будет ошибка")
[1, 2].forEach(alert)
@ -50,23 +46,19 @@ alert("После этого сообщения будет ошибка")
Поддерживаются однострочные комментарии `// ...` и многострочные `/* ... */`:
Подробнее: [](/structure).
Подробнее: <info:structure>.
## Переменные и типы
<ul>
<li>Объявляются директивой `let`. Могут хранить любое значение:
- Объявляются директивой `let`. Могут хранить любое значение:
```js
let x = 5;
x = "Петя";
```
- Есть 5 "примитивных" типов и объекты:
</li>
<li>Есть 5 "примитивных" типов и объекты:
```js
//+ no-beautify
```js no-beautify
x = 1; // число
x = "Тест"; // строка, кавычки могут быть одинарные или двойные
x = true; // булево значение true/false
@ -77,8 +69,7 @@ x = undefined; // спец. значение (само себе тип)
Также есть специальные числовые значения `Infinity` (бесконечность) и `NaN`.
Значение `NaN` обозначает ошибку и является результатом числовой операции, если она некорректна.
</li>
<li>**Значение `null` не является "ссылкой на нулевой адрес/объект" или чем-то подобным. Это просто специальное значение.**
- **Значение `null` не является "ссылкой на нулевой адрес/объект" или чем-то подобным. Это просто специальное значение.**
Оно присваивается, если мы хотим указать, что значение переменной неизвестно.
@ -87,9 +78,7 @@ x = undefined; // спец. значение (само себе тип)
```js
let age = null; // возраст неизвестен
```
</li>
<li>**Значение `undefined` означает "переменная не присвоена".**
- **Значение `undefined` означает "переменная не присвоена".**
Например:
@ -100,12 +89,10 @@ alert( x ); // undefined
Можно присвоить его и явным образом: `x = undefined`, но так делать не рекомендуется.
Про объекты мы поговорим в главе [](/object), они в JavaScript сильно отличаются от большинства других языков.
</li>
<li>В имени переменной могут быть использованы любые буквы или цифры, но цифра не может быть первой. Символы доллар `$` и подчёркивание `_` допускаются наравне с буквами.</li>
</ul>
Про объекты мы поговорим в главе <info:object>, они в JavaScript сильно отличаются от большинства других языков.
- В имени переменной могут быть использованы любые буквы или цифры, но цифра не может быть первой. Символы доллар `$` и подчёркивание `_` допускаются наравне с буквами.
Подробнее: [](/variables), [](/types-intro).
Подробнее: <info:variables>, <info:types-intro>.
## Строгий режим
@ -121,27 +108,24 @@ alert( x ); // undefined
Одно из важных изменений в современном стандарте -- все переменные нужно объявлять через `let`. Есть и другие, которые мы изучим позже, вместе с соответствующими возможностями языка.
## Взаимодействие с посетителем
Простейшие функции для взаимодействия с посетителем в браузере:
<dl>
<dt>["prompt(вопрос[, по_умолчанию])"](https://developer.mozilla.org/en/DOM/window.prompt)</dt>
<dd>Задать `вопрос` и возвратить введённую строку, либо `null`, если посетитель нажал "Отмена".</dd>
<dt>["confirm(вопрос)"](https://developer.mozilla.org/en/DOM/window.confirm)</dt>
<dd>Задать `вопрос` и предложить кнопки "Ок", "Отмена". Возвращает, соответственно, `true/false`.</dd>
<dt>["alert(сообщение)"](https://developer.mozilla.org/en/DOM/window.alert)</dt>
<dd>Вывести сообщение на экран.</dd>
</dl>
["prompt(вопрос[, по_умолчанию])"](https://developer.mozilla.org/en/DOM/window.prompt)
: Задать `вопрос` и возвратить введённую строку, либо `null`, если посетитель нажал "Отмена".
["confirm(вопрос)"](https://developer.mozilla.org/en/DOM/window.confirm)
: Задать `вопрос` и предложить кнопки "Ок", "Отмена". Возвращает, соответственно, `true/false`.
["alert(сообщение)"](https://developer.mozilla.org/en/DOM/window.alert)
: Вывести сообщение на экран.
Все эти функции являются *модальными*, т.е. не позволяют посетителю взаимодействовать со страницей до ответа.
Например:
```js
//+ run
```js run
let userName = prompt("Введите имя?", "Василий");
let isTeaWanted = confirm("Вы хотите чаю?");
@ -149,29 +133,24 @@ alert( "Посетитель: " + userName );
alert( "Чай: " + isTeaWanted );
```
Подробнее: [](/uibasic).
Подробнее: <info:uibasic>.
## Особенности операторов
<ul>
<li>**Для сложения строк используется оператор `+`.**
- **Для сложения строк используется оператор `+`.**
Если хоть один аргумент -- строка, то другой тоже приводится к строке:
```js
//+ run
```js run
alert( 1 + 2 ); // 3, число
alert( '1' + 2 ); // '12', строка
alert( 1 + '2' ); // '12', строка
```
</li>
<li>**Сравнение `===` проверяет точное равенство, включая одинаковый тип.** Это самый очевидный и надёжный способ сравнения.
- **Сравнение `===` проверяет точное равенство, включая одинаковый тип.** Это самый очевидный и надёжный способ сравнения.
**Остальные сравнения `== < <= > >=` осуществляют числовое приведение типа:**
```js
//+ run
```js run
alert( 0 == false ); // true
alert( true > 0 ); // true
```
@ -180,16 +159,14 @@ alert( true > 0 ); // true
**Исключение: значения `null` и `undefined` ведут себя в сравнениях не как ноль.**
<ul>
<li>Они равны `null == undefined` друг другу и не равны ничему ещё. В частности, не равны нулю.</li>
<li>В других сравнениях (кроме `===`) значение `null` преобразуется к нулю, а `undefined` -- становится `NaN` ("ошибка").</li>
</ul>
<li>Они равны `null == undefined` друг другу и не равны ничему ещё. В частности, не равны нулю.
- В других сравнениях (кроме `===`) значение `null` преобразуется к нулю, а `undefined` -- становится `NaN` ("ошибка").
Такое поведение может привести к неочевидным результатам, поэтому лучше всего использовать для сравнения с ними `===`. Оператор `==` тоже можно, если не хотите отличать `null` от `undefined`.
Например, забавное следствие этих правил для `null`:
```js
//+ run no-beautify
```js run no-beautify
alert( null > 0 ); // false, т.к. null преобразовано к 0
alert( null >= 0 ); // true, т.к. null преобразовано к 0
alert( null == 0 ); // false, в стандарте явно указано, что null равен лишь undefined
@ -199,20 +176,18 @@ alert( null == 0 ); // false, в стандарте явно указано, ч
</li>
</ol>
<li>**Сравнение строк -- лексикографическое, символы сравниваются по своим unicode-кодам.**
Поэтому получается, что строчные буквы всегда больше, чем прописные:
```js
//+ run
```js run
alert( 'а' > 'Я' ); // true
```
</li>
</ul>
Подробнее: [](/operators), [](/comparison).
Подробнее: <info:operators>, <info:comparison>.
## Логические операторы
@ -228,19 +203,17 @@ alert( 'а' > 'Я' ); // true
Например:
```js
//+ run
```js run
alert( 0 && 1 ); // 0
alert( 1 && 2 && 3 ); // 3
alert( null || 1 || 2 ); // 1
```
Подробнее: [](/logical-ops).
Подробнее: <info:logical-ops>.
## Циклы
<ul>
<li>Поддерживаются три вида циклов:
- Поддерживаются три вида циклов:
```js
// 1
@ -258,10 +231,8 @@ for let i = 0; i < 10; i++) {
...
}
```
</li>
<li>Переменную можно объявлять прямо в цикле, но видна она будет и за его пределами.</li>
<li>Поддерживаются директивы `break/continue` для выхода из цикла/перехода на следующую итерацию.
- Переменную можно объявлять прямо в цикле, но видна она будет и за его пределами.
- Поддерживаются директивы `break/continue` для выхода из цикла/перехода на следующую итерацию.
Для выхода одновременно из нескольких уровней цикла можно задать метку.
@ -279,10 +250,8 @@ for(;;) {
```
Переход на метку возможен только изнутри цикла, и только на внешний блок по отношению к данному циклу. В произвольное место программы перейти нельзя.
</li>
</ul>
Подробнее: [](/while-for).
Подробнее: <info:while-for>.
## Конструкция switch
@ -290,8 +259,7 @@ for(;;) {
Например:
```js
//+ run
```js run
let age = prompt('Ваш возраст', 18);
switch (age) {
@ -307,14 +275,13 @@ switch (age) {
}
```
Подробнее: [](/switch).
Подробнее: <info:switch>.
## Функции
Синтаксис функций в JavaScript:
```js
//+ run
```js run
// function имя(список параметров) { тело }
function sum(a, b) {
let result = a + b;
@ -326,23 +293,17 @@ function sum(a, b) {
alert( sum(1, 2) ); // 3
```
<ul>
<li>`sum` -- имя функции, ограничения на имя функции -- те же, что и на имя переменной.</li>
<li>Переменные, объявленные через `let` внутри функции, видны везде внутри этой функции, блоки `if`, `for` и т.п. на видимость не влияют.</li>
<li>Параметры копируются в локальные переменные `a`, `b`.
</li>
<li>Функция без `return` считается возвращающей `undefined`. Вызов `return` без значения также возвращает `undefined`:
- `sum` -- имя функции, ограничения на имя функции -- те же, что и на имя переменной.
- Переменные, объявленные через `let` внутри функции, видны везде внутри этой функции, блоки `if`, `for` и т.п. на видимость не влияют.
- Параметры копируются в локальные переменные `a`, `b`.
- Функция без `return` считается возвращающей `undefined`. Вызов `return` без значения также возвращает `undefined`:
```js
//+ run no-beautify
```js run no-beautify
function f() { }
alert( f() ); // undefined
```
</li>
</ul>
Подробнее: [](/function-basics).
Подробнее: <info:function-basics>.
## Function Declaration и Expression
@ -350,8 +311,7 @@ alert( f() ); // undefined
Её можно создать в любом месте кода и присвоить в переменную, вот так:
```js
//+ run
```js run
let sum = function(a, b) {
let result = a + b;
@ -367,7 +327,7 @@ alert( sum(1, 2) ); // 3
Обычно это удобно, но может быть проблемой, если нужно объявить функцию в зависимости от условия. В этом случае, а также в других ситуациях, когда хочется создать функцию "здесь и сейчас", используют Function Expression.
Детали: [](/function-declaration-expression).
Детали: <info:function-declaration-expression>.
## Named Function Expression
@ -377,8 +337,7 @@ alert( sum(1, 2) ); // 3
Например, создадим функцию для вычисления факториала как Function Expression и дадим ей имя `me`:
```js
//+ run
```js run
let factorial = function me(n) {
return (n == 1) ? n : n * me(n - 1);
}
@ -391,7 +350,7 @@ alert( me ); // ошибка, нет такой переменной
Ограничение видимости для имени не работает в IE8-, но вызов с его помощью работает во всех браузерах.
Более развёрнуто: [](/named-function-expression).
Более развёрнуто: <info:named-function-expression>.
## Итого

View file

@ -3,6 +3,7 @@
In this section we explore the code structure and statements.
[cut]
## Statements
We've already seen a statement: `alert('Hello, world!')`, which shows the message.
@ -11,15 +12,13 @@ Another statement can be separated with a semicolon.
For example, here we split the message into two:
```js
//+ run no-beautify
```js run no-beautify
alert( 'Hello' ); alert( 'World' );
```
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( 'World' );
```
@ -30,8 +29,7 @@ The semicolon may be omitted in most cases when a line break exists.
This would also work:
```js
//+ run no-beautify
```js run no-beautify
alert( 'Hello' )
alert( 'World' )
```
@ -42,8 +40,7 @@ In this case JavaScript interprets the line break as a splitter and automaticall
Consider this code as an example:
```js
//+ run no-beautify
```js run no-beautify
alert(3 +
1
+ 2);
@ -57,11 +54,10 @@ JavaScript does not insert semicolons here. It is intuitively obvious that the f
Errors which come appear in such cases are quite hard to find and fix.
[smart header="An example of the error"]
````smart header="An example of the error"
For a curious reader who might be interested in a concrete example, check this code out:
```js
//+ run
```js run
[1, 2].forEach(alert)
```
@ -71,8 +67,7 @@ Please don't think about the meaning of `[...].forEach` just for now -- it does
Now let's prepend an `alert` *without a semicolon* before it:
```js
//+ run no-beautify
```js run no-beautify
alert( "..." ) // works
[1, 2].forEach(alert) // doesn't work!
```
@ -80,21 +75,18 @@ alert( "..." ) // works
Now only the phrase is shown, not the numbers. And we can see an error in the developer console.
But everything's fine if we add a semicolon:
```js
//+ run
```js run
alert( "With the semicolon everything works" ); // printed
[1, 2].forEach(alert) // printed too
```
The error in the former variant occurs because JavaScript engine does not autoinsert a semicolon before square brackets `[...]`, so it was actually treated as a one-line statement:
```js
//+ run no-beautify
```js run no-beautify
// without semicolon after alert it becomes
alert( "..." )[1, 2].forEach(alert) // doesn't work!
```
[/smart]
````
As a conclusion, it's recommended to put semicolons between statements even if they are separated by newlines. This rule is widely adopted by the community.
@ -109,8 +101,7 @@ Comments can be put into any place of the script. They don't affect it's executi
It may occupy a full line of it's own or follow a statement.
Like this:
```js
//+ run
```js run
// This says "Hello" (the comment occupies a line of it's own)
alert( 'Hello' );
@ -119,8 +110,7 @@ alert( 'World' ); // ...this says "World" (the comment follows a statement)
*Multiline comments* start with a slash and a star <code>"/&#42;"</code> and end with a star and a slash <code>"&#42;/"</code>, like this:
```js
//+ run
```js run
/* An example with two messages.
This is a multiline comment.
*/
@ -132,31 +122,29 @@ The content of comments is ignored, so if we put a code inside <code>/&#42; ...
Sometimes it's used to temporarily disable a part of the code.
```js
//+ run
```js run
/* Commenting out the code
alert( 'Hello' );
*/
alert( 'World' );
```
[smart header="Use hotkeys!"]
In most editors a line of code can be commented out by [key Ctrl+/] hotkey for a single-line comment and something like [key Ctrl+Shift+/] -- for multiline comments (select a code and press the hotkey).
[/smart]
```smart header="Use hotkeys!"
In most editors a line of code can be commented out by `key:Ctrl+/` hotkey for a single-line comment and something like `key:Ctrl+Shift+/` -- for multiline comments (select a code and press the hotkey).
```
[warn header="Nested comments are not supported!"]
````warn header="Nested comments are not supported!"
There may not be comments inside comments.
This code will die with an error:
```js
//+ run no-beautify
```js run no-beautify
/*
/* nested comment ?!? */
*/
alert( 'World' );
```
[/warn]
````
Don't hesitate to comment.
@ -164,18 +152,15 @@ Comments increase the overall code footprint, but that's not a problem at all, b
There are various types of comments, answering different questions:
<ul>
<li>What the code does?</li>
<li>Why the code is written like that?</li>
<li>Which counter-intuitive or implicit connections it has with other parts of the script?</li>
</ul>
- What the code does?
- Why the code is written like that?
- Which counter-intuitive or implicit connections it has with other parts of the script?
Further in the tutorial we'll make more notes about how to write the code better, easier to read and maintain. We'll also talk more about comments.
[smart header="The good code is inherently readable and self-commenting"]
```smart header="The good code is inherently readable and self-commenting"
Please note that the first type of comments ("what the code does") should be used to describe a "high-level" action, like the overall architecture, a function or a chunk of code. It's purpose is to give an overview, so a reader doesn't need to delve into the code and figure out.
Novice programmers sometimes tend to elaborate too much. Please don't. The good code is inherently readable. No need to describe what few lines do. Unless it's something hard to grasp, and *then* it's worth to consider rewriting the code at the first place rather than commenting it.
[/smart]
```

View file

@ -23,36 +23,52 @@ For example
...
```
[warn header="There's no way to cancel `use strict`"]
```warn header="There's no way to cancel `use strict`"
There is no directive `"no use strict"` or alike, that would return the old behavior.
Once we enter the strict mode, there's no return.
[/warn]
```
````warn header="Ensure that 'use strict' is at the top"
Please make sure that `"use strict"` is on the top of the script, otherwise the strict mode may not be enabled.
There is no strict mode here:
```js no-strict
alert("some code");
// "use strict" below is ignored, must be not on the top
"use strict";
// strict mode is not activated
```
Only comments may appear above `"use strict"`.
````
```smart header="`use strict` for functions"
We will learn [functions](/function-basics) very soon.
Looking ahead let's just note that `"use strict"` can be put at the start of a function instead of the whole script. Then the strict mode is enabled in this function only. But that limitation is exceptionally rarely needed.
```
[smart header="`use strict` for functions"]
We will learn [functions](/function-basics) very soon. Looking ahead let's just note that `"use strict"` can be put at the start of a function instead of the whole script. Then the strict mode is enabled in this function only.
[/smart]
## Do I really need "use strict"?
Actually, yes. All modern browsers support it.
Technically, we can `"use strict"`, the code will work in the "old mode". But it is suggested that you use it all the time.
More than that, there are several JavaScript features that enable strict mode implicitly, "by default". Namely, "classes" and "modules" automatically switch the interpreter to "strict mode".
1. First, because all modern browsers support it, except Internet Explorer 9 and lower.
2. Second, the modern JavaScript actually pushes us into the strict mode! There are several JavaScript features that enable strict mode automatically. Namely, "classes" and "modules" automatically switch the interpreter to "strict mode". So, it's hard to evade it.
3. And the last. If we're going to support Internet Explorer 9 and lower, that will not be a problem. It does not support `"use strict"` but the differences between the "old" and the "modern" modes are not that huge.
So there's no way to stay modern and evade `"use strict"`.
The only downside is Internet Explorer prior to version 10. Those browsers do not support `"use strict"`, so if we plan to support them, then our code must be compatible with the old behavior. But that's an easy thing to do if we keep it in mind.
Sometimes we can pass by a 3rd party library that do not work correctly when the calling code is in the strict mode. But that happens very rarely, and can usually be fixed on a per-library basis.
Here in the tutorial all code works correctly in `"use strict"`.
## Summary
<ul>
<li>JavaScript without `"use strict"` may execute differently in some cases. Further in the tutorial we'll see what's different. Thankfully, not so much.</li>
<li>Several modern features of the language enable `"use strict"` implicitly, so there's just no way to evade it.</li>
<li>It is strongly advised to `"use strict"` everywhere, but keep in mind compability if we are to support old IE.</li>
</ul>
- JavaScript without `"use strict"` may execute differently in some cases. Further in the tutorial we'll see what's different. Thankfully, not so much.
- Several modern features of the language enable `"use strict"` implicitly, so there's just no way to evade it.
- It is strongly advised to `"use strict"` everywhere, but keep in mind compability if we are to support old IE.

View file

@ -1,7 +1,6 @@
In the code below, each line corresponds to the item in the task list.
```js
//+ run
```js run
let admin, name; // can declare two variables at once
name = "John";

View file

@ -1,10 +1,10 @@
importance: 2
---
# Working with variables
[importance 2]
<ol>
<li>Declare two variables: `admin` and `name`. </li>
<li>Assign the value `"John"` to `name`.</li>
<li>Copy the value from `name` to `admin`.</li>
<li>Show the value of `admin` using `alert` (must output "John").</li>
</ol>
1. Declare two variables: `admin` and `name`.
2. Assign the value `"John"` to `name`.
3. Copy the value from `name` to `admin`.
4. Show the value of `admin` using `alert` (must output "John").

View file

@ -18,4 +18,4 @@ Again, we could shorten that to `userName` if we know beyound the reasonable dou
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.
And if your editor does not help here, get [a new one](/editor).
And if your editor does not have proper autocompletion, get [a new one](/editor).

View file

@ -1,8 +1,8 @@
importance: 3
---
# Giving the right name
[importance 3]
<ol>
<li>Create the variable to store the name of our planet. Assign the value `"Earth"` to it. What should be its name?</li>
<li>Create the variable to store the name of the current visitor. What about its name?</li>
</ol>
1. Create the variable to store the name of our planet. Assign the value `"Earth"` to it. What should be its name?
2. Create the variable to store the name of the current visitor. What about its name?

View file

@ -20,7 +20,7 @@ The statement below creates (in other words: *declares* or *defines*) the variab
let message;
```
Now we can store some data in it:
Now we can put some data into it:
```js
let message;
@ -32,8 +32,7 @@ message = 'Hello'; // store the string
The string is now saved into the memory area assosiated with that variable. We can access it using the variable name:
```js
//+ run
```js run
let message;
message = 'Hello!';
@ -44,14 +43,14 @@ alert( message ); // shows the variable content
To be concise we can merge the variable declaration and assignment into a single line:
```js
```js run
let message = 'Hello!';
alert( message ); // same as above
```
We can also declare multiple variables in one line:
```js
//+ no-beautify
```js no-beautify
let user = 'John', age = 25, message = 'Hello';
```
@ -59,14 +58,13 @@ That might seem shorter, but it's recommended, for the sake of beter readability
The rewritten code is a bit longer, but easier to read:
```js
//+ no-beautify
```js no-beautify
let user = 'John';
let age = 25;
let message = 'Hello';
```
[smart header="`var` instead of `let`"]
````smart header="`var` instead of `let`"
In older scripts you may also find another keyword: `var` instead of `let`:
```js
@ -75,8 +73,8 @@ 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 subtle differences does not matter for us yet. We'll cover them in detail later when going deeper into the language.
[/smart]
The subtle differences does not matter for us yet. We'll cover them in detail later.
````
## Real-life analogy
@ -84,14 +82,13 @@ We can easily grasp the concept of a "variable" if we imagine it as a "box" for
For instance, the variable `message` is a box with the value `"Hello!"` labelled `"message"`:
<img src="variable.png">
![](variable.png)
We can put any value into the box. And later -- we can change it.
We can put any value into the box (variable).
The value can be changed as many times as we need:
Also we can change it. The value can be changed as many times as we need:
```js
//+ run
```js run
let message;
message = 'Hello!';
@ -103,18 +100,17 @@ alert( message );
When the value is changed, the old data is removed from the variable:
<img src="variable-change.png">
![](variable-change.png)
We can also declare two variables and copy the data from one into the other.
```js
//+ run
```js run
let hello = 'Hello world!';
let message;
*!*
// copy value
// copy 'Hello world' into message
message = hello;
*/!*
@ -123,22 +119,20 @@ alert( hello ); // Hello world!
alert( message ); // Hello world!
```
[smart]
```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/).
In such languages, once the value is in the box -- it's there forever. If we need to store something else -- please create a new box (declare a new variable), can't reuse the old one.
In such languages, once the value is "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 the mind.
[/smart]
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.
```
## Variable naming [#variable-naming]
There are two limitations for the variable name in JavaScript:
<ol>
<li>The name must contain only letters, digits, symbols `$` and `_`.</li>
<li>The first character must not be a digit.</li>
</ol>
1. The name must contain only letters, digits, symbols `$` and `_`.
2. The first character must not be a digit.
Valid names, for instance:
@ -153,8 +147,7 @@ What's interesting -- the dollar sign `'$'` and the underscore `'_'` are conside
These names are valid:
```js
//+ run untrusted
```js run untrusted
let $ = 1; // declared a variable with the name "$"
let _ = 2; // and now the variable with the name "_"
@ -163,50 +156,45 @@ alert( $ + _ ); // 3
Examples of incorrect variable names:
```js
//+ no-beautify
```js no-beautify
let 1a; // cannot start with a digit
let my-name; // a hyphen '-' is not allowed in the name
```
[smart header="Case matters"]
```smart header="Case matters"
Variables named `apple` and `AppLE` -- are two different variables.
[/smart]
[smart header="Non-english letters are allowed, but not recommended"]
```
````smart header="Non-english letters are allowed, but not recommended"
It is possible to use any language, including cyrillic letters or even hieroglyphs, like this:
```js
let имя = 123;
let 我 = 456;
let имя = '...';
let 我 = '...';
```
Technically, there is no error here, but there is an international tradition to use english words in variable names. Even if you're writing a small script, it may have a long life ahead. People coming with another language background may need to read it some time.
[/smart]
Technically, there is no error here, such names are allowed, but there is an international tradition to use English in variable names. Even if we're writing a small script, it may have a long life ahead. People coming with another language background may need to read it some time.
````
[warn header="Reserved names"]
````warn header="Reserved names"
There is a list of reserved words, which cannot be used as variable names, because they are used by the language itself.
For example, words `let`, `class`, `return`, `function` are reserved.
The code below will give a syntax error:
```js
//+ run no-beautify
```js run no-beautify
let let = 5; // can't name a variable "let", error!
let return = 5; // also can't name it "return", error!
```
[/warn]
````
## Non-Strict mode assignment
Without strict mode, it is possible to create a variable without a `let`, by a mere assignment of the value:
```js
//+ run no-strict
```js run no-strict
num = 5; // the variable "num" is created if didn't exist
alert(num);
@ -216,8 +204,7 @@ alert(num);
The code with `"use strict"` will give an error:
```js
//+ run untrusted
```js run untrusted
"use strict";
*!*
@ -225,32 +212,14 @@ num = 5; // error: num is not defined
*/!*
```
[smart header="Ensure that 'use strict' is at the top"]
Please make sure that `"use strict"` is on the top of the script, otherwise the strict mode may not be enabled.
There is no strict mode here:
```js
//+ run untrusted no-strict
alert("some code");
// "use strict" below is ignored, must be not on the top
"use strict";
*!*
num = 5; // No error! strict mode is not activated
*/!*
```
Only comments may appear in the script before `"use strict"`, they are not counted.
[/smart]
## V8 needs "use strict" for "let"
In Chrome browser, Opera and Node.JS, powered by V8 engine, `"use strict"` is required if we want to use `let` and many other modern features of the language.
Here, in the tutorial, there's a code which executes examples, and it auto-adds `"use strict"` most of time (except those rare cases which are described in the text as "without use strict").
Here in the tutorial most examples are executed in the strict mode (except rare cases which are described in the text as "without use strict").
But when you write JS, make sure that you do not forget `"use strict"`. Otherwise using `let` in V8 will give you an error.
But when you write JS, make sure that you do not forget `"use strict"`. Otherwise V8 will remind you about it by giving an error on `let`.
## Constants
@ -258,24 +227,32 @@ Variables with a fixed value are called "constant variables" or just *constants*
To declare a constant variable, one can use `const`:
```js
//+ run
```js run
const myBirthday = '18.04.1982';
myBirthday = '01.01.2001'; // error!
myBirthday = '01.01.2001'; // error, can't reassign the constant!
```
A constant variable never changes, so the further code can rely on that to calculate dependant values. For example, the age or a sign of the zodiac.
We can be sure that the calculated variables always hold a valid value, because `myBirthday` is not going to change.
```js run
const myBirthday = '18.04.1982';
[smart header="CONSTANT_NAMING"]
There is a widespread practice to use constants as aliases for difficult-to-remember and hard-coded prior to execution values. Such constants are named using capitals and underscores.
// do some calculations to get the age
const age = calculateAgeBasedOn(myBirthday);
```
Surely, the `age` is constant too. And it always corresponds to `myBirthday`, both constants are not going to change.
### Uppercases constants
There is a widespread practice to use constants as aliases for difficult-to-remember and hard-coded prior to execution values.
Such constants are named using capitals and underscores.
Like this:
```js
//+ run
```js run
const COLOR_RED = "#F00";
const COLOR_GREEN = "#0F0";
const COLOR_BLUE = "#00F";
@ -285,18 +262,22 @@ let color = COLOR_ORANGE;
alert( color ); // #FF7F00
```
`COLOR_ORANGE` is much easier to understand and remember than `"#FF7F00"`. Also it is much easier to make a typo in `"#FF7F00"` than in `COLOR_ORANGE`. Finally, that makes the code more readable.
[/smart]
`COLOR_ORANGE` is much easier to understand and remember than `"#FF7F00"`. Also it is much easier to make a typo in `"#FF7F00"` than in `COLOR_ORANGE`.
Such uppercased name is only used for constants that are "hard-coded" (written in the code before its execution).
An example of the opposite is a constant which value is calculated basing on a webpage content or a user input.
## Name things right
And there's one more thing.
We're almost done with the initial understanding of variables, but there's one more thing.
Please name the variables sensibly.
Variable naming is one of the most important and complex skills in programming. Just looking at variable names can obviously show which code is written by a beginner and which by an experienced guru.
In the real project, most of time is spent on modifying and extending the existing code, rather than writing something completely torn-off and new.
In the real project, most of time is spent on modifying and extending the existing code, rather than writing something completely torn-off of the current base.
And when we return to the code after some time of absence, it's much easier to find the information that is well-labelled. Or, in other words, when the variables are named right.
@ -304,33 +285,31 @@ Please spend some time thinking about the right name for a variable before decla
Few good-to-follow rules are:
<ul>
<li>Use human-readable names like `userName` or `shoppingCart` instead of abbreviations or short names `a`, `b`, `c`.</li>
<li>Make the name maximally descriptive and concise. Examples of bad names are `data` and `value`. Any variable stores a "data" or a "value" after all. So such name says nothing. It is only ok to use them if it's exceptionally obvious which data or value is meant.</li>
<li>Agree on terms within the team and in your own mind. If a site visitor is called a "user" then we should name variables like `currentUser` or `newUser`, but not `currentVisitor` or a `newManInTown`.</li>
</ul>
- 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.
- Make the name maximally descriptive and concise. Examples of bad names are `data` and `value`. Any variable stores a "data" or a "value" after all. So such name says nothing. It is only ok to use them if it's exceptionally obvious which data or value is meant.
- Agree on terms within the team and in your own mind. If a site visitor is called a "user" then we should name variables like `currentUser` or `newUser`, but not `currentVisitor` or a `newManInTown`.
Sounds simple? In fact it is. Creating best descriptive-and-concise names isn't. Go hard for it.
Sounds simple? Indeed it is. But creating good descriptive-and-concise names in practice is not. Go for it.
[smart header="Reuse or create?"]
There are some lazy programmers who instead of declaring a new variable, tend to reuse the existing one.
```smart header="Reuse or create?"
And the last piece of advice. There are some lazy programmers who instead of declaring a new variable, tend to reuse the existing one.
As the result, the variable is like a box where people throw different things without changing the sticker. What is inside it now? Who knows... We need to come closer and check.
Such a programmer saves a little bit on variable declaration, but looses ten times more on debugging the code.
An extra variable is good, not evil.
[/smart]
P.S. Modern JavaScript minifiers and browsers optimize code well enough so that won't create a performance issue.
```
## Summary
We can declare variables to store data. That can be done using `var` or `let` or `const`.
<ul>
<li>`let` -- is a modern variable declaration. The code must be in strict mode to use `let` in Chrome (V8).</li>
<li>`var` -- is an old-school variable declaration. We'll study the subtle differences from `let` later, after we get familiar with the basics.</li>
<li>`const` -- is like `let`, but the variable can't be changed.</li>
</ul>
- `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.
- `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.

View file

@ -15,31 +15,31 @@ n = 12.345;
A *number* type serves both for integer and floating point numbers.
Besides numbers there are special "numeric values" which 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`.
<ul>
<li>`Infinity` is meant to represent the mathematical [Infinity](https://en.wikipedia.org/wiki/Infinity).
- `Infinity` represents the mathematical [Infinity](https://en.wikipedia.org/wiki/Infinity). It is a value that's greater than any number.
We can get it as a result of division by zero:
```js
//+ run
```js run
alert( 1 / 0 ); // Infinity
alert( -1 / 0 ); // -Infinity
```
</li>
<li>`NaN` represents a computational error. It is a result of an incorrect or an undefined mathematical operation, for instance:
```js
//+ run
Also we can use it directly:
```js run
alert( Infinity > 123456789 ); // true
```
- `NaN` represents a computational error. It is a result of an incorrect or an undefined mathematical operation, for instance:
```js run
alert( "not a number" * 2 ); // NaN
```
</li>
</ul>
These values formally belong to the "number" type. Of course they are not numbers in a common sense of this word.
We'll cover working with numbers in the chapter [](/number).
We'll see more into working with numbers in the chapter <info:number>.
## A string
@ -51,31 +51,33 @@ let phrase = `can embed ${str}`;
In JavaScript, there are 3 types of quotes.
<ol>
<li>Double quotes: `"Hello"`.</li>
<li>Single quotes: `'Hello'`.</li>
<li>Backtricks: <code>&#96;Hello&#96;</code>.</li>
</ol>
1. Double quotes: `"Hello"`.
2. Single quotes: `'Hello'`.
3. Backtricks: <code>&#96;Hello&#96;</code>.
Double and single quotes are essentially the same.
Backtricks 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";
// embed variable
alert( `Hello, ${name}!` ); // Hello, John!
// embed expression
alert( `the result is ${1 + 2}` ); // the result is 3
```
The expression inside `${…}` is evaluated and the result becomes a part of the string.
[smart header="There is no *character* type."]
```smart header="There is no *character* type."
In some languages, there is a special "character" type for a single character. For example, in the C language it is `char`.
In JavaScript, there is only one type: `string`.
[/smart]
In JavaScript, there is no such type. There's only one type `string` for both a single character and long texts.
```
We'll cover strings more thoroughly in the chapter [](/string).
We'll cover strings more thoroughly in the chapter <info:string>.
## A boolean (logical)
@ -85,16 +87,14 @@ This type is commonly used to store the yes/no values.
For instance:
```js
//+ no-beautify
```js no-beautify
let checked = true; // the form field is checked
checked = false; // the form field is not checked
```
Boolean values also come as the result of comparisons:
```js
//+ run
```js run
let isGreater = 4 > 1;
alert(isGreater); // true
```
@ -125,17 +125,17 @@ The sense of `undefined` is "value is not assigned".
If a variable is declared, but not assigned, then its value is exactly `undefined`:
```js
//+ run
```js run
let x;
alert( x ); // shows "undefined"
```
Technically, it is possible to assign to `undefined`:
```js
//+ run
```js run
let x = 123;
x = undefined;
alert( x ); // "undefined"
@ -145,15 +145,71 @@ alert( x ); // "undefined"
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.
## Object type
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* of various data and more complex entities.
An object is defined with the figure brackets `{…}`.
For instance, here we create a `user` object with the name and the age:
```js
let user = {
name: "John",
age: 30
};
```
Now `user` is a complex entity, can be imagine of a shelf with two folders labelled "name" and "age".
We can access either via the dot notation:
```js
// get parts of the object:
alert( user.name ); // John
alert( user.age ); // 30
```
Also we can add new information to the user and remove it any time:
```js
user.isAdmin = true;
delete user.age;
```
Objects in JavaScript are very powerful. This topic is so huge. We'll be closely working with objects since the chapter <info:object> and continue studying them in following parts of the tutorial.
## Symbol type
The `symbol` type stands apart from the others. Symbols are rarely used. Probably we won't need them any time soon, but it's the 7th and the last type of the language. So we must mention it for the sake of completeness.
Type "symbol" represents an unique identifier.
A value of this type can be created like this:
```js
let id = Symbol("id");
```
Symbols are used in advanced operations with objects. As of now, it's enough to understand that JavaScript symbols is a separate primitive type used for identifiers.
Symbols in JavaScript are different from symbols in Ruby language (if you are familiar with it, please don't get trapped by the same word). We'll get back to them after in-depth study of objects.
## The typeof operator [#type-typeof]
The `typeof` operator returns the type of the argument.
It allows two forms of syntax:
<ol>
<li>As an operator: `typeof x`.</li>
<li>Function style: `typeof(x)`.</li>
</ol>
1. As an operator: `typeof x`.
2. Function style: `typeof(x)`.
In other words, it works both with the brackets or without them. They result is the same.
@ -177,16 +233,15 @@ typeof null // "object" (1)
*/!*
*!*
typeof function(){} // "function" (2)
typeof alert // "function" (2)
*/!*
```
Please note the last two lines, because `typeof` behaves specially there.
<ol>
<li>The result of `typeof null` equals to `"object"`. That is an officially recognized error in the language that is kept for compatibility. In fact, `null` is not an object, but a special value from a data type of its own.</li>
<li>Functions are yet to be covered. As of now let's just note that functions do not make a separate type, instead they are a special kind of objects in JavaScript. But `typeof` treats them separately returning `"function"`. That's actually just fine and very convenient in practice.</li>
</ol>
1. The result of `typeof null` equals to `"object"`. That is an officially recognized error in `typeof` implementation, kept for compatibility. Of course, `null` is not an object. It is a special value with a separate type of its own.
2. The result of `typeof alert` is `"function"`, because `alert` is a function of the language. We'll study more functions in near future. Formally, there is no special type for functions, but `typeof` makes them look different. That's very convenient in practice.
## Type conversions
@ -198,17 +253,15 @@ let message = "hello";
message = 123456;
```
But sometimes we need to convert a value of one type to another. That is mostly useful because each type has it's own features. So we are really going to benefit from storing a number as a number, not a string with it.
...But sometimes we need to convert a value from one type to another. That is useful because each type has it's own features. So we are really going to benefit from storing a number as a number, not a string with it.
There are many type conversions in JavaScript, fully listed in [the specification](http://www.ecma-international.org/ecma-262/6.0/index.html#sec-type-conversion).
Three conversions that happen the most often are:
Three conversions that happen most often are:
<ol>
<li>String conversion.</li>
<li>Numeric conversion.</li>
<li>Boolean conversion.</li>
</ol>
1. String conversion.
2. Numeric conversion.
3. Boolean conversion.
### String conversion
@ -216,8 +269,7 @@ The string conversion happens when we need a string form of a value.
For example, `alert` does it:
```js
//+ run
```js run
let a = true;
alert( a ); // "true"
@ -225,15 +277,14 @@ alert( a ); // "true"
We can also use a call `String(value)` function for that:
```js
//+ run
```js run
let a = true;
a = String(a); // now: a = "true"
alert(typeof a); // string
```
The string conversion is the most obvious. A `false` becomes `"false"`, `null` becomes `"null"` etc.
The string conversion is obvious. A `false` becomes `"false"`, `null` becomes `"null"` etc.
### Numeric conversion
@ -241,136 +292,79 @@ Numeric conversion happens in mathematical functions and expressions automatical
For example, a mathematical operation like division '/' can be applied to non-numbers:
```js
//+ run
```js run
alert( "6" / "2" ); // 3, strings become numbers
```
If we want to ensure that a value is a number, we can use a `Number(value)` function to do it explicitly:
We can use a `Number(value)` function to convert any `value` to a number:
```js
//+ run
let n = Number("6");
```js run
let str = "123";
alert(typeof str); // string
let n = Number(str); // becomes a number 123
alert(typeof n); // number
```
It's handy when there is a value comes from a text form field, or another source which is string-bsed.
The conversion is usually applied when we have a numeric value coming from a text form field or another string-based source.
If the string is not a number, the result of such conversion is `NaN`, for instance:
```js
//+ run
let age = Number("a user-supplied string");
```js run
let age = Number("an arbitrary string instead of a number");
alert(age); // NaN, conversion failed
alert(typeof age); // number, because NaN belongs to the "number" type
```
The rules of transformation:
The numeric conversion rules:
<table>
<thead>
<tr><th>Value</th><th>Becomes...</th></tr>
</thead>
<tbody>
<tr><td>`undefined`</td><td>`NaN`</td></tr>
<tr><td>`null`</td><td>`0`</td></tr>
<tr style="white-space:nowrap"><td>`true / false`</td><td>`1 / 0`</td></tr>
<tr><td>A string</td><td>Whitespaces from the start and the end are cut off.<br>Then, if the remaining string is empty, the result is `0`, otherwise -- the value is "read" from the string. An error gives `NaN`.</td></tr>
</tbody>
</table>
| Value | Becomes... |
|-------|-------------|
|`undefined`|`NaN`|
|`null`|`0`|
|<code>true&nbsp;/&nbsp;false</code> | `1 / 0` |
| A string | Whitespaces from the start and the end are cut off. Then, if the remaining string is empty, the result is `0`, otherwise the value is "read" from the string. An error gives `NaN`. |
Other examples:
Examples:
```js
//+ run
```js run
alert( Number(" 123 ") ); // 123
alert( Number("123z") ); // NaN (error reading a number at "z")
alert( Number(true) ); // 1
alert( Number(false) ); // 0
```
Please note that `null` and `undefined` are similar in many aspects, but here they are not. A `null` becomes a zero, but `undefined` becomes `NaN`.
Please note that `null` and `undefined` behave differently here. A `null` becomes a zero, but `undefined` becomes `NaN`.
### Boolean conversion
Boolean conversion is happens automatically in logical operations (to be covered), but also can be performed manually with the call of `Boolean(value)`.
The conversion rule is simple here:
<ul>
<li>All values that are intuitively "empty" become `false`. These are: `0`, an empty string, `null`, `undefined` and `NaN`.</li>
<li>Other values become `true`.</li>
</ul>
The conversion rules are simple here:
[warn header="Please note: a string `\"0\"` is `true`"]
- All values that are intuitively "empty" become `false`. These are: `0`, an empty string, `null`, `undefined` and `NaN`.
- Other values become `true`.
````warn header="Please note: a string `\"0\"` is `true`"
Some languages (namely PHP) treat `"0"` as `false`. But in JavaScript a non-empty string is always `false`, no matter what is in it.
```js
//+ run
```js run
alert( Boolean("0") ); // true
alert( Boolean(" ") ); // any non-empty string, even whitespaces are true
```
[/warn]
## Object and Symbol
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 of various data and more complex entities.
### Object
An object is defined with the figure brackets `{…}`.
For instance, here we create a `user` object with the name and the age:
```js
let user = {
name: "John",
age: 30
};
```
We can access the data form the object via the dot notation:
```js
alert( user.name ); // John
alert( user.age ); // 30
```
Objects are very capable and tunable in JavaScript. This topic is huge, we'll start working with objects in the chapter [](/object) and continue studying them in other parts of the tutorial.
### Symbol
The `symbol` type is used to create unique identifiers.
```js
let id = Symbol("id");
```
...And then we could use `id` as a special kind of identifier for object properties.
We'll see more about object properties in the following chapters. As of now, let's just say that JavaScript symbols is a separate primitive type. And they are different from symbols in Ruby language (if you are familiar with it, please don't get trapped by the same word).
We list symbols here for the sake of completeness, their in-depth study will follow after objects.
````
## Summary
<ul>
<li>There are 7 basic types in JavaScript. Six "primitive" types: `number`, `string`, `boolean`, `null`, `undefined`, `symbol` and `object`.</li>
<li>The `typeof` operator allows to see which type is stored in the variable, but note that it mistakingly returns `"object"` for `null`.</li>
</ul>
- There are 7 basic types in JavaScript. Six "primitive" types: `number`, `string`, `boolean`, `null`, `undefined`, `symbol` and an `object` type.
- The `typeof` operator allows to see which type is stored in the variable, but note that it mistakingly returns `"object"` for `null`.
Type conversions usually happen automatically, but there are also functions for the manual conversion:
<ul>
<li>String</li>
<li>Number</li>
<li>Boolean</li>
</ul>
Further we'll cover each type in a separate chapter, devoted to this type only. But first let's move on to study operators and other language constructs. It will be more productive.
- String
- Number
- Boolean
Now let's study operators and other language constructs that actually form our code.

View file

@ -1,15 +1,12 @@
The answer is:
<ul>
<li>`a = 2`</li>
<li>`b = 2`</li>
<li>`c = 2`</li>
<li>`d = 1`</li>
</ul>
- `a = 2`
- `b = 2`
- `c = 2`
- `d = 1`
```js
//+ run no-beautify
```js run no-beautify
let a = 1, b = 1;
alert( ++a ); // 2, prefix form returns the new value
@ -18,3 +15,4 @@ alert( b++ ); // 1, postfix form returns the old value
alert( a ); // 2, incremented once
alert( b ); // 2, incremented once
```

View file

@ -1,6 +1,8 @@
# The postfix and prefix forms
importance: 5
[importance 5]
---
# The postfix and prefix forms
What are the final values of all variables in the code below?
@ -11,4 +13,3 @@ let c = ++a; // ?
let d = b++; // ?
```

View file

@ -1,6 +1,5 @@
The answer is:
<ul>
<li>`a = 4` (multiplied by 2)</li>
<li>`x = 5` (calculated as 1 + 4)</li>
</ul>
- `a = 4` (multiplied by 2)
- `x = 5` (calculated as 1 + 4)

View file

@ -1,6 +1,8 @@
# Assignment result
importance: 3
[importance 3]
---
# Assignment result
What will be values of `a` and `x` in the example below?

View file

@ -1,26 +1,23 @@
```js
//+ no-beautify
```js no-beautify
"" + 1 + 0 = "10" // (1)
"" - 1 + 0 = -1 // (2)
true + false = 1
6 / "3" = 2
"2" * "3" = 6
4 + 5 + "px" = "9px"
"$" + 4 + 5 = "$45"
"4" - 2 = 2
"4px" - 2 = NaN
7 / 0 = Infinity
"$" + 4 + 5 = "$45"
"4" - 2 = 2
"4px" - 2 = NaN
7 / 0 = Infinity
" -9\n" + 5 = " -9\n5"
" -9\n" - 5 = -14
null + 1 = 1 // (3)
undefined + 1 = NaN // (4)
```
<ol>
<li>The plus `"+"` operator in this case first converts `1` to a string: `"" + 1 = "1"`, and then adds `0`.</li>
<li>The minus `"-"` operator only works with numbers, it converts an empty string `""` to zero immediately.</li>
<li>`null` becomes `0` after the numeric conversion.</li>
<li>`undefined` becomes `NaN` after the numeric conversion.</li>
</ol>
1. The plus `"+"` operator in this case first converts `1` to a string: `"" + 1 = "1"`, and then adds `0`.
2. The minus `"-"` operator only works with numbers, it converts an empty string `""` to zero immediately.
3. `null` becomes `0` after the numeric conversion.
4. `undefined` becomes `NaN` after the numeric conversion.

View file

@ -1,23 +1,24 @@
# Type conversions
importance: 5
[importance 5]
---
# Type conversions
Let's recap type conversions in the context of operators.
What will be the result for these expressions?
```js
//+ no-beautify
```js no-beautify
"" + 1 + 0
"" - 1 + 0
true + false
6 / "3"
"2" * "3"
2" * "3"
4 + 5 + "px"
"$" + 4 + 5
"4" - 2
"4px" - 2
7 / 0
$" + 4 + 5
"4" - 2
"4px" - 2
7 / 0
" -9\n" + 5
" -9\n" - 5
null + 1

View file

@ -3,18 +3,17 @@
Many operators are known to us from the school program. 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.
[cut]
## Terms: "unary", "binary", "operand"
Before we move on, let's make a dip in the common terminology, used in the development.
<ul>
<li>*An operand* -- is what operators are applied to. For instance in multiplication `5 * 2` there are two operands: the left operand is `5`, and the right operand is `2`. Another word is "an argument of an operator".</li>
<li>An operator is called *unary* if it has a single operand. For example, the unary minus `"-"` reverses the sign of the number:
- *An operand* -- is what operators are applied to. For instance in multiplication `5 * 2` there are two operands: the left operand is `5`, and the right operand is `2`. Another word is "an argument of an operator".
- An operator is called *unary* if it has a single operand. For example, the unary minus `"-"` reverses the sign of the number:
```js
//+ run
```js run
let x = 1;
*!*
@ -22,20 +21,14 @@ x = -x;
*/!*
alert( x ); // -1, unary minus was applied
```
- An operator is called *binary* if it has two operands. The same minus exists in the binary form as well:
</li>
<li>An operator is called *binary* if it has two operands. The same minus exists in the binary form as well:
```js
//+ run no-beautify
```js run no-beautify
let x = 1, y = 3;
alert( y - x ); // 2, binary minus
```
Formally, we're talking about the two different operators here: the unary minus (single operand, reverses the sign) and the binary minus (two operands, substracts).
</li>
</ul>
## Strings concatenation, binary +
@ -52,8 +45,7 @@ If one of operands of `+` is a string, then the other one is converted to string
For example:
```js
//+ run
```js run
alert( '1' + 2 ); // "12"
alert( 2 + '1' ); // "21"
```
@ -64,22 +56,20 @@ The string concatenation and conversion is the special feature of the binary plu
For instance:
```js
//+ run
```js run
alert( 2 - '1' ); // 1
alert( 6 / '2' ); // 3
```
## Numeric conversion, unary +
The plus `+` exist in two forms. The binary form that we've just seen above and the unary form.
The plus `+` exist in two forms. The binary form that we used above and the unary form.
The unary plus or, in other words, the plus `+` applied to a single value, doesn't do anything with numbers, but if the operand is not a number, then it is converted into it.
For example:
```js
//+ run
```js run
// No effect on numbers
let x = 1;
alert( +x ); // 1
@ -92,14 +82,15 @@ alert( +true ); // 1
alert( +"" ); // 0
```
It actually does the same as `Number()`, but it's shorter.
As an example of use, let's imagine that we are getting values from HTML-form fields. Then they are usually strings.
What if we want to sum them?
The binary plus would add them as strings:
```js
//+ run
```js run
let apples = "2";
let oranges = "3";
@ -108,53 +99,48 @@ alert( apples + oranges ); // "23", the binary plus concatenates strings
If we want to treat them as numbers, then we can convert and then sum:
```js
//+ run
```js run
let apples = "2";
let oranges = "3";
alert( Number(apples) + Number(oranges) ); // 5
// or the shorter variant:
*!*
alert( +apples + +oranges ); // 5
// both values converted to numbers before the binary plus
*/!*
```
The conversion can also be done with the unary plus:
From a mathematician's standpoint the abundance of pluses may seem strange. But from a programmer's standpoint -- there's nothing special: unary pluses are applied first, they convert strings to numbers, and then the binary plus sums them up.
```js
//+ run
let apples = "2";
let oranges = "3";
alert( +apples + +oranges ); // 5, both operands converted to numbers before the binary plus
```
From a mathematician's standpoint the abundance of pluses may seem strange. But from a programmer's -- there's nothing special: unary pluses are applied first, they convert strings to numbers, and then the binary plus sums them up.
Why did unary pluses work before the binary one? As we're going to see, that's because of their *precedence*.
Why did unary pluses are applied to values before the binary one? As we're going to see, that's because of their *higher precedence*.
## Operators precedence
If an expression has more than one operator, the execution order is defined by their *precedence*.
If an expression has more than one operator, the execution order is defined by their *precedence*, that is an implicit order among the operators.
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 has *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`.
Brackets 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. For clarity and internal needs there exists a [precedence table](https://developer.mozilla.org/en/JavaScript/Reference/operators/operator_precedence). 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.
An extract from the table:
<table>
<tr><td>...</td><td>...</td><td>...</td></tr>
<tr><td>15</td><td>unary plus</td><td>`+`</td></tr>
<tr><td>15</td><td>unary minus</td><td>`-`</td></tr>
<tr><td>14</td><td>multiplication</td><td>`*`</td></tr>
<tr><td>14</td><td>division</td><td>`/`</td></tr>
<tr><td>13</td><td>addition</td><td>`+`</td></tr>
<tr><td>13</td><td>substraction</td><td>`-`</td></tr>
<tr><td>...</td><td>...</td><td>...</td></tr>
<tr><td>3</td><td>assignment</td><td>`=`</td></tr>
<tr><td>...</td><td>...</td><td>...</td></tr>
</table>
| Precedence | Name | Sign |
|------------|------|------|
| ... | ... | ... |
| 15 | unary plus | `+` |
| 15 | unary minus | `-` |
| 14 | multiplication | `*` |
| 14 | division | `/` |
| 13 | addition (binary) | `+` |
| 13 | substraction | `-` |
| ... | ... | ... |
| 3 | assignment | `=` |
| ... | ... | ... |
As we can see, the "unary plus" has a priority of `15`, higher than `13` for the ordinary "addition". That's why in the expression `+apples + +oranges` unary pluses worked first, and then the addition.
@ -162,7 +148,7 @@ As we can see, the "unary plus" has a priority of `15`, higher than `13` for the
Let's note that an assignment `=` is also an operator. It is listed in the precedence table with the very low priority of `3`.
That's why when we assign a variable, like `x = 2 * 2 + 1`, the maths is done first, and then the assignment is evaluated.
That's why when we assign a variable, like `x = 2 * 2 + 1`, then the calculations are done first, and afterwards the `=` is evaluated, storing the result in `x`.
```js
let x = 2 * 2 + 1;
@ -172,8 +158,7 @@ alert( x ); // 5
It is possible to chain assignments:
```js
//+ run
```js run
let a, b, c;
*!*
@ -187,15 +172,14 @@ alert( c ); // 4
The assignments chain is evaluated 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.
[smart header="The assignment operator `\"=\"` returns a value"]
An operator always returns a value. The assignment is not an exception.
````smart header="The assignment operator `\"=\"` returns a value"
An operator always returns a value. That's obvious for `+` and `*`. But the assignment 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:
```js
//+ run
```js run
let a = 1;
let b = 2;
@ -210,7 +194,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`.
Funny code, isn't it? We should understand how it works, but don't write anything like that ourselves. Such tricks definitely don't make the code neater and more readable.
[/smart]
````
## Remainder %
@ -220,8 +204,7 @@ The result of `a % b` is the remainder of the integer division of `a` by `b`.
For instance:
```js
//+ run
```js run
alert( 5 % 2 ); // 1 is a remainder of 5 divided by 2
alert( 8 % 3 ); // 2 is a remainder of 8 divided by 3
alert( 6 % 3 ); // 0 is a remainder of 6 divided by 3
@ -233,36 +216,28 @@ Increasing or decreasing a number by one is among the most common numerical oper
So, there are special operators for that:
<ul>
<li>**Increment** `++` increases a variable by 1:
- **Increment** `++` increases a variable by 1:
```js
//+ run no-beautify
let i = 2;
i++; // works same as i = i + 1, but shorter
alert(i); // 3
```js run no-beautify
let counter = 2;
counter++; // works same as counter = counter + 1, but shorter
alert( counter ); // 3
```
- **Decrement** `--` decreases a variable by 1:
```js run no-beautify
let counter = 2;
counter--; // works same as counter = counter - 1, but shorter
alert( counter ); // 1
```
</li>
<li>**Decrement** `--` decreases a variable by 1:
```js
//+ run no-beautify
let i = 2;
i--; // works same as i = i - 1, but shorter
alert(i); // 1
```
</li>
</ul>
[warn]
```warn
Increment/decrement can be applied only to a variable. An attempt to use it on a value like `5++` will give an error.
[/warn]
```
Operators `++` and `--` can be put both after and before the variable.
When the operator goes after the variable, it is called a "postfix form": `i++`. The "prefix form" is when the operator stands before the variable: `++i`.
When the operator goes after the variable, it is called a "postfix form": `counter++`. The "prefix form" is when the operator stands before the variable: `++counter`.
Both of these records do the same: increase `i` by `1`.
@ -272,95 +247,78 @@ As we know, all operators return a value. Increment/decrement is not an exceptio
Let's see the examples:
```js
//+ run
let i = 1;
let a = ++i; // (*)
```js run
let counter = 1;
let a = ++counter; // (*)
alert(a); // *!*2*/!*
```
Here in the line `(*)` the prefix call `++i` increments `i` and returns the new value that is `2`. So the `alert` shows `2`.
Here in the line `(*)` the prefix call `++counter` increments `i` and returns the new value that is `2`. So the `alert` shows `2`.
Now let's use the postfix form:
```js
//+ run
let i = 1;
let a = i++; // (*) changed ++i to i++
```js run
let counter = 1;
let a = counter++; // (*) changed ++counter to counter++
alert(a); // *!*1*/!*
```
In the line `(*)` the *postfix* form `i++` also increments `i`, but returns the *old* value (prior to increment). So the `alert` shows `1`.
In the line `(*)` the *postfix* form `counter++` also increments `i`, but returns the *old* value (prior to increment). So the `alert` shows `1`.
To summarize:
<ul>
<li>If the result of increment/decrement is not used, then there is no difference which form to use:
```js
//+ run
let i = 0;
i++;
++i;
alert( i ); // 2, the lines above did the same
- If the result of increment/decrement is not used, then there is no difference which form to use:
```js run
let counter = 0;
counter++;
++counter;
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:
```js run
let counter = 0;
alert( ++counter ); // 1
```
- If we'd like to increment, but use the previous value, then we need the postfix form:
```js run
let counter = 0;
alert( counter++ ); // 0
```
</li>
<li>If we'd like to use the result of the operator right now, then we need the prefix form:
```js
//+ run
let i = 0;
alert( ++i ); // 1
```
</li>
<li>If we'd like to increment, but use the previous value, then we need the postfix form:
```js
//+ run
let i = 0;
alert( i++ ); // 0
```
</li>
</ul>
[smart header="Increment/decrement among other operators"]
Operators `++/--` usually stand alone. But they can be used inside an expression as well.
Their priority is higher than most other arithmetical operations.
````smart header="Increment/decrement among other operators"
Operators `++/--` can be used inside an expression as well. Their priority is higher than most other arithmetical operations.
For instance:
```js
//+ run
let i = 1;
alert( 2 * ++i ); // 4
```js run
let counter = 1;
alert( 2 * ++counter ); // 4
```
Compare with:
```js
//+ run
let i = 1;
alert( 2 * i++ ); // 2, because i++ returns the pre-increment value
```js run
let counter = 1;
alert( 2 * counter++ ); // 2, because counter++ returns the "old" value
```
Though technically allowable, such notation usually makes the code less readable. One line does multiple things -- not good.
During a fast "vertical" eye-scanning of the code it's easy to miss such `i++`, and it would be not obvious that the variable increases.
While reading the code, a fast "vertical" eye-scan can easily miss such `counter++`, and it won't be obvious that the variable increases.
Three lines: one line -- one action is advised:
The "one line -- one action" style is advised:
```js
//+ run
let i = 1;
alert( 2 * i );
i++;
```js run
let counter = 1;
alert( 2 * counter );
counter++;
```
[/smart]
````
## Bitwise operators
@ -370,17 +328,15 @@ These operators are not JavaScript-specific. They are supported in most programm
The list of operators:
<ul>
<li>AND ( `&` )</li>
<li>OR ( `|` )</li>
<li>XOR ( `^` )</li>
<li>NOT ( `~` )</li>
<li>LEFT SHIFT ( `<<` )</li>
<li>RIGHT SHIFT ( `>>` )</li>
<li>ZERO-FILL RIGHT SHIFT ( `>>>` )</li>
</ul>
- AND ( `&` )
- OR ( `|` )
- XOR ( `^` )
- NOT ( `~` )
- LEFT SHIFT ( `<<` )
- RIGHT SHIFT ( `>>` )
- ZERO-FILL RIGHT SHIFT ( `>>>` )
These operators are used quite rarely. Their study implies the knowledge of low-level number representation, and it would be suboptimal to delve into that now. If a reader wants to learn them right now, they are explained in 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 return to this topic later when a need arises.
These operators are used very rarely. To understand them, we should delve into the low-level number representation, and it would be suboptimal to do that now. Especially because we won't need them any time soon. A curious reader may 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 return to this topic later when a real need arises.
## Modify-in-place
@ -396,8 +352,7 @@ n = n * 2;
This notation can be shortened using operators `+=` and *=`:
```js
//+ run
```js run
let n = 2;
n += 5; // now n=7 (same as n = n + 5)
n *= 2; // now n=14 (same as n = n * 2)
@ -409,15 +364,13 @@ Short "modify-and-assign" operators exist for `+,-,*,/` and bitwise `<<,>>,>>>,&
The modify-and-assign call has the same precedence as a normal assignment, so it executes after most other calculations:
```js
//+ run
```js run
let n = 2;
n *= 3 + 5;
alert( n ); // 16 (n = 2 * 8)
alert( n ); // 16 (same as n *= 8)
```
## 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.
@ -426,8 +379,7 @@ The comma operator allows to evaluate several expressions, dividing them with a
For example:
```js
//+ run
```js run
*!*
a = (1+2, 3+4);
*/!*

View file

@ -1,20 +1,21 @@
```js
//+ no-beautify
```js no-beautify
5 > 4 → true
"apple" > "pineapple" → false
"2" > "12" → true // (1)
undefined == null → true // (2)
undefined === null → false // (3)
null == "\n0\n" → false // (4)
"2" > "12" → true
undefined == null → true
undefined === null → false
null == "\n0\n" → false
null === +"\n0\n" → false
```
Some of the reasons:
<ol>
<li>The string `"2"` is indeed greater than `"12"`, because strings are compared character-by-character. The first character of `"2"` is greater than the first character of `"12"` (that is `"1"`).</li>
<li>Values `null` and `undefined` equal each other only.</li>
<li>Strict equality is strict. Different types from both sides lead to false.</li>
<li>See (2) for the reason.</li>
</ol>
1. Obviously, true.
2. Dictionary comparison, hence true.
3. Again, dictionary comparison, first char of `"2"` is greater than the first char of `"1"`.
4. Values `null` and `undefined` equal each other only.
5. Strict equality is strict. Different types from both sides lead to false.
6. See (4).
7. Strict equality of different types.

View file

@ -1,11 +1,12 @@
# Comparisons
importance: 5
[importance 5]
---
# Comparisons
What will be the result for expressions?
```js
//+ no-beautify
```js no-beautify
5 > 4
"apple" > "pineapple"
"2" > "12"

View file

@ -2,12 +2,10 @@
Many comparison operators we know from the maths:
<ul>
<li>Greater/less than: <code>a &gt; b</code>, <code>a &lt; b</code>.</li>
<li>Greater/less than or equals: <code>a &gt;= b</code>, <code>a &lt;= b</code>.</li>
<li>Equality check is written as `a == b` (please note the double equation sign `'='`. A single symbol `a = b` would mean an assignment).</li>
<li>Not equals. In maths the sign is <code>&ne;</code>, in JavaScript we use an assignment with an exclamation before it: <code>a != b</code>.</li>
</ul>
- Greater/less than: <code>a &gt; b</code>, <code>a &lt; b</code>.
- Greater/less than or equals: <code>a &gt;= b</code>, <code>a &lt;= b</code>.
- 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>&ne;</code>, in JavaScript we use an assignment with an exclamation before it: <code>a != b</code>.
[cut]
@ -15,15 +13,12 @@ Many comparison operators we know from the maths:
Just as all other operators, a comparison returns a value. The value is of the boolean type.
<ul>
<li>`true` -- means "yes", "correct" or "the truth".</li>
<li>`false` -- means "no", "wrong" or "a lie".</li>
</ul>
- `true` -- means "yes", "correct" or "the truth".
- `false` -- means "no", "wrong" or "a lie".
For example:
```js
//+ run
```js run
alert( 2 > 1 ); // true (correct)
alert( 2 == 1 ); // false (wrong)
alert( 2 != 1 ); // true (correct)
@ -31,8 +26,7 @@ alert( 2 != 1 ); // true (correct)
A result of a comparison can be assigned to a variable, just like any value:
```js
//+ run
```js run
let result = 5 > 4; // assign the result of the comparison
alert( result ); // true
```
@ -45,70 +39,61 @@ In other words, strings are compared letter-by-letter.
For example:
```js
//+ run
```js run
alert( 'Z' > 'A' ); // true
alert( 'Glow' > 'Glee' ); // true
alert( 'Bee' > 'Be' ); // true
```
The algorithm to compare two strings is simple:
<ol>
<li>Compare the first characters of both strings.</li>
<li>If the first one is greater(or less), then the first string is greater(or less) than the second and we're done.</li>
<li>Otherwise if first characters are equal, compare the second characters the same way.</li>
<li>Repeat until the end of any string.</li>
<li>If both strings ended simultaneously, then they are equal. Otherwise the longer string is greater.</li>
</ol>
1. Compare the 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.
3. Otherwise if first characters are equal, compare the second characters the same way.
4. Repeat until the end of any string.
5. If both strings ended simultaneously, then they are equal. Otherwise the longer string is greater.
In the example above, the comparison `'Z' > 'A'` gets the result at the first step.
Strings `"Glow"` and `"Glee"` are compared character-by-character:
<ol>
<li>`G` is the same as `G`.</li>
<li>`l` is the same as `l`.</li>
<li>`o` is greater than `e`. Stop here. The first string is greater.</li>
</ol>
[smart header="Not a real dictionary"]
The strings "dictionary" comparison algorithm given above is roughly equivalent to the one used in book dictionaries or phone books. But it's not exactly the same.
1. `G` is the same as `G`.
2. `l` is the same as `l`.
3. `o` is greater than `e`. Stop here. The first string is greater.
For instance, the case matters. A capital letter `"A"` is not equal to the lowercase `"a"`. Which one is greater? It is defined by a character's index in the internal encoding (Unicode) table. We'll get back to specific details and consequences in the chapter [](/strings).
[/smart]
```smart header="Not a real dictionary"
The comparison algorithm given above is roughly equivalent to the one used in book dictionaries or phone books. But it's not exactly the same.
For instance, the case matters. A capital letter `"A"` is not equal to the lowercase `"a"`. Which one is greater? Actually, the lowercase `"a"` is. Why? Because the lowercase character has a greater index in the internal encoding table (Unicode). We'll get back to specific details and consequences in the chapter <info:string>.
```
## Comparison of different types
When compared values are of different types, they get autoconverted to numbers.
When compared values belong to different types, they get autoconverted to numbers.
For example:
```js
//+ run
```js run
alert( '2' > 1 ); // true, string '2' becomes a number 2
alert( '01' == 1 ); // true, string '01' becomes a number 1
```
For boolean values, `true` becomes `1` and `false` becomes `0`, that's why:
```js
//+ run
```js run
alert( true == 1 ); // true
alert( false == 0 ); // true
```
[smart header="A funny consequence"]
````smart header="A funny consequence"
It is possible that in the same time:
<ul>
<li>Two values are equal.</li>
<li>One of them is `true` as a boolean and the other one is `false` as a boolean.</li>
</ul>
- Two values are equal.
- One of them is `true` as a boolean and the other one is `false` as a boolean.
For example:
```js
//+ run
```js run
let a = 0;
alert( Boolean(a) ); // false
@ -119,23 +104,19 @@ alert(a == b); // true!
```
From JavaScript standpoint that's quite normal. An equality check converts using the numeric conversion (hence `"0"` becomes `0`), while `Boolean` conversion uses another set of rules.
[/smart]
````
## Strict equality
A regular equality check `==` has a "problem": it cannot differ `0` from `false`:
A regular equality check `"=="` has a problem. It cannot differ `0` from `false`:
```js
//+ run
```js run
alert( 0 == false ); // true
```
The same thing with an empty string:
```js
//+ run
```js run
alert( '' == false ); // true
```
@ -145,12 +126,11 @@ What to do if we'd like to differentiate `0` from `false`?
**A strict equality operator `===` checks the equality without type conversion.**
In other words, `a === b` immediately returns `false` if `a` and `b` belong to different types. Without attempting to use numeric conversion as `==` would do.
In other words, `a === b` immediately returns `false` if `a` and `b` belong to different types. Without attempting to convert them.
Let's try it:
```js
//+ run
```js run
alert( 0 === false ); // false, because the types are different
```
@ -160,41 +140,31 @@ The string equality check is a bit longer to write, but it's more obvious what's
## Comparison with null and undefined
Let's see more corner cases.
There's a non-intuitive behavior when `null` or `undefined` is compared with other values.
For a strict equality check `===` the result is simple: `null` and `undefined` belong to different types of their own. So `null === null` and no other value. The similar thing is true for `undefined`.
- There's a special rule when testing the equality `null == undefined`. These two are a "sweet couple. They equal (in the sense of `==`) each other, but no one else.
But what for `null > 0` or `null == undefined`? The trap is that `null/undefined` may feel like `false` or a zero in this case. But it is not so.
- For a strict equality check `===` these values are different, because each of them belong to a separate type of it's own.
<ol>
<li>For an equality check `==` there's a simple rule. Values `null` and `undefined` are equal `==` to each other and non-equal to any other value.</li>
<li>For other non-strict comparisons a numeric conversion is involved. After it `null` becomes `0`, while `undefined` becomes `NaN`.</li>
</ol>
- For evaluation of other comparisons including `<`, `>`, `<=`, `>=`, these values are converted to a number. We surely remember that `null` becomes `0`, while `undefined` becomes `NaN`.
Let's see few notable examples.
Now let's see funny things that happen when we apply those rules.
### Strange result: null vs 0
Let's compare `null` with a zero:
```js
//+ run
```js run
alert( null > 0 ); // false
alert( null == 0 ); // false
```
Okay, from the code above we may decide that `null` is not greater and not equal to zero.
But...
```js
//+ run
alert( null >= 0 ); // *!*true*/!*
```
How can that be possible? If `null` is "not greater than zero and not equal to zero" then how `null >= 0` can be true?
Yeah, mathematically that's strange. The last result states that "`null` is equal or greater than zero", and the former comparisons contradict it.
Yeah, mathematically that's strange. But the reason is that an equality check `==` and comparisons `> < >= <=` work differently. Comparisons convert `null` to a number, hence treat it as `0`. That's why `null >= 0` is true and `null > 0` is false.
The reason is that an equality check `==` and comparisons `> < >= <=` work differently. Comparisons convert `null` to a number, hence treat it as `0`. That's why `null >= 0` is true and `null > 0` is false.
From the other hand, equality has a rule that a "sweet couple" `undefined` and `null` match each other and no other value.
@ -204,8 +174,7 @@ That's why we have a strange-looking situation above.
The value `undefined` shouldn't participate in comparisons at all:
```js
//+ run
```js run
alert( undefined > 0 ); // false (1)
alert( undefined < 0 ); // false (2)
alert( undefined == 0 ); // false (3)
@ -214,26 +183,25 @@ alert( undefined == 0 ); // false (3)
Why does it dislike a zero so much? Always false!
We've got such result, because:
<ul>
<li>Comparisons `(1)` and `(2)` return `false` because `undefined` gets converted to `NaN`. And `NaN` is a special numeric value which returns `false` for all comparisons.</li>
<li>The equality check `(3)` returns `false`, because `undefined` only equals `null` and no other value.</li>
</ul>
Why did we observe these examples? Should we remember these pecularities all the time? Well, they will become known and obvious over the time, but actually there's no need. Just one little conclusion is enough.
- Comparisons `(1)` and `(2)` return `false` because `undefined` gets converted to `NaN`. And `NaN` is a special numeric value which returns `false` for all comparisons.
- The equality check `(3)` returns `false`, because `undefined` only equals `null` and no other value.
**Any comparison with `undefined/null` except the strict equality `===` should be done with exceptional care.**
### Conclusion
For clarity it is preferable not to use comparisons `>= > < <=` with a variable which may be `null/undefined` at all.
Why did we observe these examples? Should we remember these pecularities all the time? Well,not really. Actually, these tricky things will become obvious over the time, but most of time one little conclusion is enough.
We can always make a separate check for `null` or add an explicit type conversion. Makes the code less bug-proof.
Any comparison with `undefined/null` except the strict equality `===` should be done with exceptional care.
**It is preferable not to use comparisons `>= > < <=` with a variable which may be `null/undefined`.**
We can always make a separate check for `null` or add an explicit type conversion.
## Summary
<ul>
<li>Comparison operators return a logical value.</li>
<li>Strings are compared letter-by-letter in the "dictionary" order.</li>
<li>When values of different types are compared, they get converted to numbers (with the exclusion of a strict equality check).</li>
<li>Values `null` and `undefined` equal `==` each other and do not equal any other value.</li>
<li>Be careful when using comparisons like `>` or `<` with variables that can occasionaly be `null/undefined`. Making a separate check for `null/undefined` is a good idea.</li>
</ul>
- Comparison operators return a logical value.
- Strings are compared letter-by-letter in the "dictionary" order.
- When values of different types are compared, they get converted to numbers (with the exclusion of a strict equality check).
- Values `null` and `undefined` equal `==` each other and do not equal any other value.
- Be careful when using comparisons like `>` or `<` with variables that can occasionaly be `null/undefined`. Making a separate check for `null/undefined` is a good idea.

View file

@ -14,35 +14,34 @@
Зайдите на [страницу с примером](debugging/index.html) браузером Chrome.
Откройте инструменты разработчика: [key F12] или в меню `Инструменты > Инструменты Разработчика`.
Откройте инструменты разработчика: `key:F12` или в меню `Инструменты > Инструменты Разработчика`.
Выберите сверху `Sources`.
<img src="chrome_sources.png">
![](chrome_sources.png)
Вы видите три зоны:
<ol>
<li>**Зона исходных файлов.** В ней находятся все подключённые к странице файлы, включая JS/CSS. Выберите `pow.js`, если он не выбран.</li>
<li>**Зона текста.** В ней находится текст файлов.</li>
<li>**Зона информации и контроля.** Мы поговорим о ней позже.</li>
</ol>
1. **Зона исходных файлов.** В ней находятся все подключённые к странице файлы, включая JS/CSS. Выберите `pow.js`, если он не выбран.
2. **Зона текста.** В ней находится текст файлов.
3. **Зона информации и контроля.** Мы поговорим о ней позже.
Обычно зона исходных файлов при отладке не нужна. Скройте её кнопкой <span class="devtools" style="background-position:-200px -76px"></span>.
## Общие кнопки управления
<img src="chrome_sources_buttons.png">
![](chrome_sources_buttons.png)
Три наиболее часто используемые кнопки управления:
<dl>
<dt>Формат <span class="devtools" style="background-position:-264px 94px"></span></dt>
<dd>Нажатие форматирует текст текущего файла, расставляет отступы. Нужна, если вы хотите разобраться в чужом коде, плохо отформатированном или сжатом.</dd>
<dt>Консоль <span class="devtools" style="background-position:-70px 94px"></span></dt>
<dd>Очень полезная кнопка, открывает тут же консоль для запуска команд. Можно смотреть код и тут же запускайть функции. Её нажатие можно заменить на клавишу <code class="key">Esc</code>.</dd>
<dt>Окно <span class="devtools" style="background-position:-6px 70px"></span></dt>
<dd>Если код очень большой, то можно вынести инструменты разработки вбок или в отдельное окно, зажав эту кнопку и выбрав соответствующий вариант из списка.</dd>
</dl>
Формат <span class="devtools" style="background-position:-264px 94px"></span>
: Нажатие форматирует текст текущего файла, расставляет отступы. Нужна, если вы хотите разобраться в чужом коде, плохо отформатированном или сжатом.
Консоль <span class="devtools" style="background-position:-70px 94px"></span>
: Очень полезная кнопка, открывает тут же консоль для запуска команд. Можно смотреть код и тут же запускайть функции. Её нажатие можно заменить на клавишу <code class="key">Esc</code>.
Окно <span class="devtools" style="background-position:-6px 70px"></span>
: Если код очень большой, то можно вынести инструменты разработки вбок или в отдельное окно, зажав эту кнопку и выбрав соответствующий вариант из списка.
## Точки остановки
@ -52,26 +51,22 @@
Выглядет это должно примерно так:
<img src="chrome_sources_breakpoint.png">
![](chrome_sources_breakpoint.png)
Слово *Брейкпойнт* (breakpoint) -- часто используемый английский жаргонизм. Это то место в коде, где отладчик будет *автоматически* останавливать выполнение JavaScript, как только оно до него дойдёт.
**В остановленном коде можно посмотреть текущие значения переменных, выполнять команды и т.п., в общем -- отлаживать его.**
Вы можете видеть, что информация о точке остановки появилась справа, в подвкладке Breakpoints.
Вкладка Breakpoints очень удобна, когда код большой, она позволяет:
<ul>
<li>Быстро перейти на место кода, где стоит брейкпойнт кликом на текст.</li>
<li>Временно выключить брейкпойнт кликом на чекбокс.</li>
<li>Быстро удалить брейкпойнт правым кликом на текст и выбором Remove, и так далее.</li>
</ul>
- Быстро перейти на место кода, где стоит брейкпойнт кликом на текст.
- Временно выключить брейкпойнт кликом на чекбокс.
- Быстро удалить брейкпойнт правым кликом на текст и выбором Remove, и так далее.
[smart header="Дополнительные возможности"]
<ul>
<li>Остановку можно инициировать и напрямую из кода скрипта, командой `debugger`:
````smart header="Дополнительные возможности"
- Остановку можно инициировать и напрямую из кода скрипта, командой `debugger`:
```js
function pow(x, n) {
@ -80,42 +75,36 @@ function pow(x, n) {
...
}
```
</li>
<li>*Правый клик* на номер строки `pow.js` позволит создать условную точку остановки (conditional breakpoint), т.е. задать условие, при котором точка остановки сработает.
- *Правый клик* на номер строки `pow.js` позволит создать условную точку остановки (conditional breakpoint), т.е. задать условие, при котором точка остановки сработает.
Это удобно, если остановка нужна только при определённом значении переменной или параметра функции.
</li>
</ul>
[/smart]
````
## Остановиться и осмотреться
Наша функция выполняется сразу при загрузке страницы, так что самый простой способ активировать отладчик JavaScript -- перезагрузить её. Итак, нажимаем [key F5] (Windows, Linux) или [key Cmd+R] (Mac).
Наша функция выполняется сразу при загрузке страницы, так что самый простой способ активировать отладчик JavaScript -- перезагрузить её. Итак, нажимаем `key:F5` (Windows, Linux) или `key:Cmd+R` (Mac).
Если вы сделали всё, как описано выше, то выполнение прервётся как раз на 6й строке.
<img src="chrome_sources_break.png">
![](chrome_sources_break.png)
Обратите внимание на информационные вкладки справа (отмечены стрелками).
В них мы можем посмотреть текущее состояние:
<ol>
<li>**`Watch Expressions` -- показывает текущие значения любых выражений.**
Можно раскрыть эту вкладку, нажать мышью `+` на ней и ввести любое выражение. Отладчик будет отображать его значение на текущий момент, автоматически перевычисляя его при проходе по коду.</li>
<li>**`Call Stack` -- стек вызовов, все вложенные вызовы, которые привели к текущему месту кода.**
1. **`Watch Expressions` -- показывает текущие значения любых выражений.**
На текущий момент видно, отладчик находится в функции `pow` (pow.js, строка 6), вызванной из анонимного кода (index.html, строка 13).</li>
<li>**`Scope Variables` -- переменные.**
Можно раскрыть эту вкладку, нажать мышью `+` на ней и ввести любое выражение. Отладчик будет отображать его значение на текущий момент, автоматически перевычисляя его при проходе по коду.
2. **`Call Stack` -- стек вызовов, все вложенные вызовы, которые привели к текущему месту кода.**
На текущий момент видно, отладчик находится в функции `pow` (pow.js, строка 6), вызванной из анонимного кода (index.html, строка 13).
3. **`Scope Variables` -- переменные.**
На текущий момент строка 6 ещё не выполнилась, поэтому `result` равен `undefined`.
В `Local` показываются переменные функции: объявленные через `var` и параметры. Вы также можете там видеть ключевое слово `this`, если вы не знаете, что это такое -- ничего страшного, мы это обсудим позже, в следующих главах учебника.
В `Global` -- глобальные переменные и функции.
</li>
</ol>
## Управление выполнением
@ -123,9 +112,8 @@ function pow(x, n) {
Обратим внимание на панель управления справа-сверху, в ней есть 6 кнопок:
<dl>
<dt><img style="vertical-align:middle" src="manage1.png"> -- продолжить выполнение, горячая клавиша [key F8].</dt>
<dd>Продолжает выполнения скрипта с текущего момента в обычном режиме. Если скрипт не встретит новых точек остановки, то в отладчик управление больше не вернётся.
![|style="vertical-align:middle"](manage1.png) -- продолжить выполнение, горячая клавиша `key:F8`.
: Продолжает выполнения скрипта с текущего момента в обычном режиме. Если скрипт не встретит новых точек остановки, то в отладчик управление больше не вернётся.
Нажмите на эту кнопку.
@ -134,56 +122,51 @@ function pow(x, n) {
При этом вы увидите, что выполнение стоит на той же строке, но в `Call Stack` появился новый вызов.
Походите по стеку вверх-вниз -- вы увидите, что действительно аргументы разные.
</dd>
<dt><img style="vertical-align:middle" src="manage2.png"> -- сделать шаг, не заходя внутрь функции, горячая клавиша [key F10].</dt>
<dd>Выполняет одну команду скрипта. Если в ней есть вызов функции -- то отладчик обходит его стороной, т.е. не переходит на код внутри.
![|style="vertical-align:middle"](manage2.png) -- сделать шаг, не заходя внутрь функции, горячая клавиша `key:F10`.
: Выполняет одну команду скрипта. Если в ней есть вызов функции -- то отладчик обходит его стороной, т.е. не переходит на код внутри.
Эта кнопка очень удобна, если в текущей строке вызывается функция JS-фреймворка или какая-то другая, которая нас ну совсем не интересует. Тогда выполнение продолжится дальше, без захода в эту функцию, что нам и нужно.
Обратим внимание, в данном случае эта кнопка при нажатии всё-таки перейдёт внутрь вложенного вызова `pow`, так как внутри `pow` находится брейкпойнт, а на включённых брейкпойнтах отладчик останавливается всегда.
</dd>
<dt><img style="vertical-align:middle" src="manage3.png"> -- сделать шаг, горячая клавиша [key F11].</dt>
<dd>Выполняет одну команду скрипта и переходит к следующей. Если есть вложенный вызов, то заходит внутрь функции.
![|style="vertical-align:middle"](manage3.png) -- сделать шаг, горячая клавиша `key:F11`.
: Выполняет одну команду скрипта и переходит к следующей. Если есть вложенный вызов, то заходит внутрь функции.
Эта кнопка позволяет подробнейшим образом пройтись по очереди по командам скрипта.
</dd>
<dt><img style="vertical-align:middle" src="manage4.png"> -- выполнять до выхода из текущей функции, горячая клавиша [key Shift+F11].</dt>
<dd>Выполняет команды до завершения текущей функции.
![|style="vertical-align:middle"](manage4.png) -- выполнять до выхода из текущей функции, горячая клавиша `key:Shift+F11`.
: Выполняет команды до завершения текущей функции.
Эта кнопка очень удобна в случае, если мы нечаянно вошли во вложенный вызов, который нам не интересен -- чтобы быстро из него выйти.
</dd>
<dt><img style="vertical-align:middle" src="manage5.png"> -- отключить/включить все точки остановки.</dt>
<dd>Эта кнопка никак не двигает нас по коду, она позволяет временно отключить все точки остановки в файле.
</dd>
<dt><img style="vertical-align:middle" src="manage6.png"> -- включить/отключить автоматическую остановку при ошибке.</dt>
<dd>Эта кнопка -- одна из самых важных.
![|style="vertical-align:middle"](manage5.png) -- отключить/включить все точки остановки.
: Эта кнопка никак не двигает нас по коду, она позволяет временно отключить все точки остановки в файле.
![|style="vertical-align:middle"](manage6.png) -- включить/отключить автоматическую остановку при ошибке.
: Эта кнопка -- одна из самых важных.
Нажмите её несколько раз. В старых версиях Chrome у неё три режима -- нужен фиолетовый, в новых -- два, тогда достаточно синего.
Когда она включена, то при ошибке в коде он автоматически остановится и мы сможем посмотреть в отладчике текущие значения переменных, при желании выполнить команды и выяснить, как так получилось.
</dd>
</dl>
**Процесс отладки заключается в том, что мы останавливаем скрипт, смотрим, что с переменными, переходим дальше и ищем, где поведение отклоняется от правильного.**
[smart header="Continue to here"]
```smart header="Continue to here"
Правый клик на номер строки открывает контекстное меню, в котором можно запустить выполнение кода до неё (Continue to here). Это удобно, когда хочется сразу прыгнуть вперёд и breakpoint неохота ставить.
[/smart]
```
## Консоль
При отладке, кроме просмотра переменных и передвижения по скрипту, бывает полезно запускать команды JavaScript. Для этого нужна консоль.
В неё можно перейти, нажав кнопку "Console" вверху-справа, а можно и открыть в дополнение к отладчику, нажав на кнопку <span class="devtools" style="background-position:-72px -28px"></span> или клавишей [key ESC].
В неё можно перейти, нажав кнопку "Console" вверху-справа, а можно и открыть в дополнение к отладчику, нажав на кнопку <span class="devtools" style="background-position:-72px -28px"></span> или клавишей `key:ESC`.
**Самая любимая команда разработчиков: `console.log(...)`.**
Она пишет переданные ей аргументы в консоль, например:
```js
//+ run
```js run
// результат будет виден в консоли
for (var i = 0; i < 5; i++) {
console.log("значение", i);
@ -200,10 +183,10 @@ for (var i = 0; i < 5; i++) {
Например, прервите отладку -- для этого достаточно закрыть инструменты разрабтчика -- и откройте [страницу с ошибкой](error/index.html).
Перейдите во вкладку Console инструментов разработчика ([key Ctrl+Shift+J] / [key Cmd+Shift+J]).
Перейдите во вкладку Console инструментов разработчика (`key:Ctrl+Shift+J` / `key:Cmd+Shift+J`).
В консоли вы увидите что-то подобное:
<img src="console_error.png">
![](console_error.png)
Красная строка -- это сообщение об ошибке.
@ -214,46 +197,32 @@ for (var i = 0; i < 5; i++) {
Более подробно прояснить произошедшее нам поможет отладчик. Он может "заморозить" выполнение скрипта на момент ошибки и дать нам возможность посмотреть значения переменных и стека на тот момент.
Для этого:
<ol>
<li>Перейдите на вкладку Sources.</li>
<li>Включите остановку при ошибке, кликнув на кнопку <img style="vertical-align:middle" src="manage6.png"></li>
<li>Перезагрузите страницу.</li>
</ol>
1. Перейдите на вкладку Sources.
2. Включите остановку при ошибке, кликнув на кнопку ![|style="vertical-align:middle"](manage6.png)
3. Перезагрузите страницу.
После перезагрузки страницы JavaScript-код запустится снова и отладчик остановит выполнение на строке с ошибкой:
<img src="chrome_break_error.png">
![](chrome_break_error.png)
Можно посмотреть значения переменных. Открыть консоль и попробовать запустить что-то в ней. Поставить брейкпойнты раньше по коду и посмотреть, что привело к такой печальной картине, и так далее.
## Итого
Отладчик позволяет:
<ul>
<li>Останавливаться на отмеченном месте (breakpoint) или по команде `debugger`.</li>
<li>Выполнять код -- по одной строке или до определённого места.</li>
<li>Смотреть переменные, выполнять команды в консоли и т.п.</li>
</ul>
- Останавливаться на отмеченном месте (breakpoint) или по команде `debugger`.
- Выполнять код -- по одной строке или до определённого места.
- Смотреть переменные, выполнять команды в консоли и т.п.
В этой главе кратко описаны возможности отладчика Google Chrome, относящиеся именно к работе с кодом.
Пока что это всё, что нам надо, но, конечно, инструменты разработчика умеют много чего ещё. В частности, вкладка Elements -- позволяет работать со страницей (понадобится позже), Timeline -- смотреть, что именно делает браузер и сколько это у него занимает и т.п.
Осваивать можно двумя путями:
<ol>
<li>[Официальная документация](https://developer.chrome.com/devtools) (на англ.)</li>
<li>Кликать в разных местах и смотреть, что получается. Не забывать о клике правой кнопкой мыши.</li>
</ol>
1. [Официальная документация](https://developer.chrome.com/devtools) (на англ.)
2. Кликать в разных местах и смотреть, что получается. Не забывать о клике правой кнопкой мыши.
Мы ещё вернёмся к отладчику позже, когда будем работать с HTML.
[head]
<style>
span.devtools {
display: inline-block;
background-image: url(/article/debugging-chrome/statusbarButtonGlyphs.svg);
height:16px;
width:16px;
}
</style>
[/head]

View file

@ -1,11 +1,10 @@
# Как писать неподдерживаемый код?
[warn header="Познай свой код"]
```warn header="Познай свой код"
Эта статья представляет собой мой вольный перевод [How To Write Unmaintainable Code](http://mindprod.com/jgloss/unmain.html) ("как писать неподдерживаемый код") с дополнениями, актуальными для JavaScript.
Возможно, в каких-то из этих советов вам даже удастся узнать "этого парня в зеркале".
[/warn]
```
Предлагаю вашему вниманию советы мастеров древности, следование которым создаст дополнительные рабочие места для JavaScript-разработчиков.
@ -17,17 +16,16 @@
Явно кривой код может написать любой дурак. Это заметят, и вас уволят, а код будет переписан с нуля. Вы не можете такого допустить. Эти советы учитывают такую возможность. Да здравствует дзен.
[cut]
## Соглашения -- по настроению
[quote author="Сериал \"Симпсоны\", серия Helter Shelter"]
```quote author="Сериал \"Симпсоны\", серия Helter Shelter"
Рабочий-чистильщик осматривает дом:<br>
"...Вот только жук у вас необычный...<br>
И чтобы с ним справиться, я должен жить как жук, стать жуком, думать как жук."<br>
(грызёт стол Симпсонов)
[/quote]
```
Чтобы помешать другому программисту исправить ваш код, вы должны понять путь его мыслей.
@ -45,9 +43,10 @@
### Пример из jQuery
[warn header="jQuery / DOM"]
```warn header="jQuery / DOM"
Этот пример требует знаний jQuery/DOM, если пока их у вас нет -- пропустите его, ничего страшного, но обязательно вернитесь к нему позже. Подобное стоит многих часов отладки.
[/warn]
```
Во фреймворке jQuery есть метод [wrap](http://api.jquery.com/wrap/), который обёртывает один элемент вокруг другого:
```js
@ -131,29 +130,27 @@ i = i ? i < 0 ? Math.max(0, len + i) : i : 0;
### Будьте абстрактны при выборе имени
[quote author="Лао-цзы"]Лучший кувшин лепят всю жизнь.<br>
```quote author="Лао-цзы"
Лучший кувшин лепят всю жизнь.<br>
Высокая музыка неподвластна слуху.<br>
Великий образ не имеет формы.[/quote]
Великий образ не имеет формы.
```
При выборе имени старайтесь применить максимально абстрактное слово, например `obj`, `data`, `value`, `item`, `elem` и т.п.
<ul>
<li>**Идеальное имя для переменной: `data`.** Используйте это имя везде, где можно. В конце концов, каждая переменная содержит *данные*, не правда ли?
- **Идеальное имя для переменной: `data`.** Используйте это имя везде, где можно. В конце концов, каждая переменная содержит *данные*, не правда ли?
Но что делать, если имя `data` уже занято? Попробуйте `value`, оно не менее универсально. Ведь каждая переменная содержит *значение*.
Занято и это? Есть и другой вариант.
</li>
<li>**Называйте переменную по типу данных, которые она хранит: `obj`, `num`, `arr`...**
- **Называйте переменную по типу данных, которые она хранит: `obj`, `num`, `arr`...**
Насколько это усложнит разработку? Как ни странно, намного!
Казалось бы, название переменной содержит информацию, говорит о том, что в переменной -- число, объект или массив... С другой стороны, **когда непосвящённый будет разбирать этот код -- он с удивлением обнаружит, что информации нет!**
Ведь как раз тип легко понять, запустив отладчик и посмотрев, что внутри. Но в чём смысл этой переменной? Что за массив/объект/число в ней хранится? Без долгой медитации над кодом тут не обойтись!
</li>
<li>**Что делать, если и эти имена кончились? Просто добавьте цифру:** `item1, item2, elem5, data1`...</li>
</ul>
- **Что делать, если и эти имена кончились? Просто добавьте цифру:** `item1, item2, elem5, data1`...
### Похожие имена
@ -169,7 +166,9 @@ i = i ? i < 0 ? Math.max(0, len + i) : i : 0;
### Хитрые синонимы
[quote author="Конфуций"]Очень трудно найти чёрную кошку в тёмной комнате, особенно когда её там нет.[/quote]
```quote author="Конфуций"
Очень трудно найти чёрную кошку в тёмной комнате, особенно когда её там нет.
```
**Чтобы было не скучно -- используйте *похожие названия* для обозначения *одинаковых действий*.**
@ -237,10 +236,10 @@ function ninjaFunction(elem) {
### Перекрывайте внешние переменные
[quote author="Гуань Инь-цзы"]
```quote author="Гуань Инь-цзы"
Находясь на свету, нельзя ничего увидеть в темноте.<br>
Пребывая же в темноте, увидишь все, что находится на свету.
[/quote]
```
Почему бы не использовать одинаковые переменные внутри и снаружи функции? Это просто и не требует придумывать новых имён.
@ -273,7 +272,6 @@ function render() {
Представьте, что другому разработчику нужно только проверить адрес, а сообщение -- не выводить. Ваша функция `validateEmail(email)`, которая делает и то и другое, ему не подойдёт. Работодатель будет вынужден оплатить создание новой.
## Внимание.. Сюр-при-из!
Есть функции, название которых говорит о том, что они ничего не меняют. Например, `isReady`, `checkPermission`, `findTags`... Предполагается, что при вызове они произведут некие вычисления, или найдут и возвратят полезные данные, но при этом их не изменят. В трактатах это называется "отсутствие сторонних эффектов".
@ -294,8 +292,6 @@ function render() {
Возможно, даже больше вашего, так что не судите опрометчиво ;)
<ul>
<li>Следуйте нескольким из них -- и ваш код станет полон сюрпризов.</li>
<li>Следуйте многим -- и ваш код станет истинно вашим, никто не захочет изменять его.</li>
<li>Следуйте всем -- и ваш код станет ценным уроком для молодых разработчиков, ищущих просветления.</li>
</ul>
- Следуйте нескольким из них -- и ваш код станет полон сюрпризов.
- Следуйте многим -- и ваш код станет истинно вашим, никто не захочет изменять его.
- Следуйте всем -- и ваш код станет ценным уроком для молодых разработчиков, ищущих просветления.

View file

@ -1,12 +1,14 @@
# Сделать pow по спецификации
importance: 5
[importance 5]
---
# Сделать pow по спецификации
Исправьте код функции `pow`, чтобы тесты проходили.
Для этого ниже в задаче вы найдёте ссылку на песочницу.
Она содержит HTML с тестами. Обратите внимание, что HTML-страница в ней короче той, что обсуждалась в статье [](/testing). Это потому что библиотеки Chai, Mocha и Sinon объединены в один файл:
Она содержит HTML с тестами. Обратите внимание, что HTML-страница в ней короче той, что обсуждалась в статье <info:testing>. Это потому что библиотеки Chai, Mocha и Sinon объединены в один файл:
```html
<script src="https://js.cx/test/libs.js"></script>

View file

@ -28,8 +28,7 @@ describe("любое число, кроме нуля, в степени 0 рав
И не забудем добавить отдельный тест для нуля:
```js
//+ no-beautify
```js no-beautify
...
it("ноль в нулевой степени даёт NaN", function() {
assert( isNaN(pow(0, 0)), "0 в степени 0 не NaN");

View file

@ -1,6 +1,8 @@
# Добавьте тест к задаче
importance: 5
[importance 5]
---
# Добавьте тест к задаче
Добавьте к [предыдущей задаче](/task/pow-nan-spec) тесты, которые будут проверять, что любое число, кроме нуля, в нулевой степени равно `1`, а ноль в нулевой степени даёт `NaN` (это математически корректно, результат 0<sup>0</sup> не определён).

View file

@ -1,6 +1,8 @@
# Что не так в тесте?
importance: 5
[importance 5]
---
# Что не так в тесте?
Что не так в этом тесте функции `pow`?

View file

@ -34,7 +34,6 @@ BDD -- это не просто тесты. Это гораздо больше.
Допустим, мы хотим разработать функцию `pow(x, n)`, которая возводит `x` в целую степень `n`, для простоты `n≥0`.
Ещё до разработки мы можем представить себе, что эта функция будет делать и описать это по методике BDD.
Это описание называется *спецификация* (или, как говорят в обиходе, "спека") и выглядит так:
@ -50,31 +49,29 @@ describe("pow", function() {
```
У спецификации есть три основных строительных блока, которые вы видите в примере выше:
<dl>
<dt>`describe(название, function() { ... })`</dt>
<dd>Задаёт, что именно мы описываем, используется для группировки "рабочих лошадок" -- блоков `it`. В данном случае мы описываем функцию `pow`.</dd>
<dt>`it(название, function() { ... })`</dt>
<dd>В названии блока `it` *человеческим языком* описывается, что должна делать функция, далее следует *тест*, который проверяет это.</dd>
<dt>`assert.equal(value1, value2)`</dt>
<dd>Код внутри `it`, если реализация верна, должен выполняться без ошибок.
`describe(название, function() { ... })`
: Задаёт, что именно мы описываем, используется для группировки "рабочих лошадок" -- блоков `it`. В данном случае мы описываем функцию `pow`.
`it(название, function() { ... })`
: В названии блока `it` *человеческим языком* описывается, что должна делать функция, далее следует *тест*, который проверяет это.
`assert.equal(value1, value2)`
: Код внутри `it`, если реализация верна, должен выполняться без ошибок.
Различные функции вида `assert.*` используются, чтобы проверить, делает ли `pow` то, что задумано. Пока что нас интересует только одна из них -- `assert.equal`, она сравнивает свой первый аргумент со вторым и выдаёт ошибку в случае, когда они не равны. В данном случае она проверяет, что результат `pow(2, 3)` равен `8`.
Есть и другие виды сравнений и проверок, которые мы увидим далее.</dd>
</dl>
Есть и другие виды сравнений и проверок, которые мы увидим далее.
## Поток разработки
Как правило, поток разработки таков:
<ol>
<li>Пишется спецификация, которая описывает самый базовый функционал.</li>
<li>Делается начальная реализация.</li>
<li>Для проверки соответствия спецификации мы задействуем одновременно фреймворк, в нашем случае [Mocha](http://mochajs.org/) вместе со спецификацией и реализацией. Фреймворк запускает все тесты `it` и выводит ошибки, если они возникнут. При ошибках вносятся исправления.</li>
<li>Спецификация расширяется, в неё добавляются возможности, которые пока, возможно, не поддерживаются реализацией.</li>
<li>Идём на пункт 3, делаем реализацию, и так далее, до победного конца.</li>
</ol>
1. Пишется спецификация, которая описывает самый базовый функционал.
2. Делается начальная реализация.
3. Для проверки соответствия спецификации мы задействуем одновременно фреймворк, в нашем случае [Mocha](http://mochajs.org/) вместе со спецификацией и реализацией. Фреймворк запускает все тесты `it` и выводит ошибки, если они возникнут. При ошибках вносятся исправления.
4. Спецификация расширяется, в неё добавляются возможности, которые пока, возможно, не поддерживаются реализацией.
5. Идём на пункт 3, делаем реализацию, и так далее, до победного конца.
Разработка ведётся *итеративно*, один проход за другим, пока спецификация и реализация не будут завершены.
@ -85,27 +82,23 @@ describe("pow", function() {
Для запуска тестов нужны соответствующие JavaScript-библиотеки.
Мы будем использовать:
<ul>
<li>[Mocha](http://mochajs.org/) -- эта библиотека содержит общие функции для тестирования, включая `describe` и `it`.</li>
<li>[Chai](http://chaijs.com) -- библиотека поддерживает разнообразные функции для проверок. Есть разные "стили" проверки результатов, с которыми мы познакомимся позже, на текущий момент мы будем использовать лишь `assert.equal`.</li>
<li>[Sinon](http://sinonjs.org/) -- для эмуляции и хитрой подмены функций "заглушками", понадобится позднее.</li>
</ul>
- [Mocha](http://mochajs.org/) -- эта библиотека содержит общие функции для тестирования, включая `describe` и `it`.
- [Chai](http://chaijs.com) -- библиотека поддерживает разнообразные функции для проверок. Есть разные "стили" проверки результатов, с которыми мы познакомимся позже, на текущий момент мы будем использовать лишь `assert.equal`.
- [Sinon](http://sinonjs.org/) -- для эмуляции и хитрой подмены функций "заглушками", понадобится позднее.
Эти библиотеки позволяют тестировать JS не только в браузере, но и на сервере Node.JS. Здесь мы рассмотрим браузерный вариант, серверный использует те же функции.
Пример HTML-страницы для тестов:
```html
<!--+ src="index.html" -->
```
[html src="index.html"]
Эту страницу можно условно разделить на четыре части:
<ol>
<li>Блок `<head>` -- в нём мы подключаем библиотеки и стили для тестирования, нашего кода там нет.</li>
<li>Блок `<script>` с реализацией спецификации, в нашем случае -- с кодом для `pow`.</li>
<li>Далее подключаются тесты, файл `test.js` содержит `describe("pow", ...)`, который был описан выше. Методы `describe` и `it` принадлежат библиотеке Mocha, так что важно, что она была подключена выше.</li>
<li>Элемент `<div id="mocha">` будет использоваться библиотекой Mocha для вывода результатов. Запуск тестов инициируется командой `mocha.run()`.</li>
</ol>
1. Блок `<head>` -- в нём мы подключаем библиотеки и стили для тестирования, нашего кода там нет.
2. Блок `<script>` с реализацией спецификации, в нашем случае -- с кодом для `pow`.
3. Далее подключаются тесты, файл `test.js` содержит `describe("pow", ...)`, который был описан выше. Методы `describe` и `it` принадлежат библиотеке Mocha, так что важно, что она была подключена выше.
4. Элемент `<div id="mocha">` будет использоваться библиотекой Mocha для вывода результатов. Запуск тестов инициируется командой `mocha.run()`.
Результат срабатывания:
@ -143,8 +136,7 @@ function pow() {
Здесь есть два варианта организации кода:
<ol>
<li>Первый вариант -- добавить `assert` в тот же `it`:
1. Первый вариант -- добавить `assert` в тот же `it`:
```js
describe("pow", function() {
@ -158,9 +150,7 @@ describe("pow", function() {
});
```
</li>
<li>Второй вариант -- сделать два теста:
2. Второй вариант -- сделать два теста:
```js
describe("pow", function() {
@ -176,9 +166,6 @@ describe("pow", function() {
});
```
</li>
</ol>
Их принципиальное различие в том, что если `assert` обнаруживает ошибку, то он тут же прекращает выполнение блоки `it`. Поэтому в первом варианте, если вдруг первый `assert` "провалился", то про результат второго мы никогда не узнаем.
**Таким образом, разделить эти тесты может быть полезно, чтобы мы получили больше информации о происходящем.**
@ -192,6 +179,7 @@ describe("pow", function() {
По этим причинам второй вариант здесь предпочтительнее.
Результат:
[iframe height=250 src="pow-2" edit border="1"]
Как и следовало ожидать, второй тест не проходит. Ещё бы, ведь функция всё время возвращает `8`.
@ -232,8 +220,8 @@ describe("pow", function() {
```
Результат:
[iframe height=250 src="pow-3" edit border="1"]
[iframe height=250 src="pow-3" edit border="1"]
## Вложенный describe
@ -273,13 +261,12 @@ describe("pow", function() {
В будущем мы сможем в добавить другие тесты `it` и блоки `describe` со своими вспомогательными функциями.
[smart header="before/after и beforeEach/afterEach"]
````smart header="before/after и beforeEach/afterEach"
В каждом блоке `describe` можно также задать функции `before/after`, которые будут выполнены до/после запуска тестов, а также `beforeEach/afterEach`, которые выполняются до/после каждого `it`.
Например:
```js
//+ no-beautify
```js no-beautify
describe("Тест", function() {
before(function() { alert("Начало тестов"); });
@ -307,10 +294,10 @@ describe("Тест", function() {
Конец тестов
```
[edit src="beforeafter"]Открыть пример с тестами в песочнице[/edit]
[edit src="beforeafter" title="Открыть пример с тестами в песочнице"]
Как правило, `beforeEach/afterEach` (`before/each`) используют, если необходимо произвести инициализацию, обнулить счётчики или сделать что-то ещё в таком духе между тестами (или их группами).
[/smart]
````
## Расширение спецификации
@ -343,6 +330,7 @@ describe("pow", function() {
```
Результат с новыми тестами:
[iframe height=450 src="pow-nan" edit border="1"]
Конечно, новые тесты не проходят, так как наша реализация ещё не поддерживает их. Так и задумано: сначала написали заведомо не работающие тесты, а затем пишем реализацию под них.
@ -363,15 +351,13 @@ describe("pow", function() {
Вот самые востребованные `assert`-проверки, встроенные в Chai:
<ul>
<li>`assert(value)` -- проверяет что `value` является `true` в логическом контексте.</li>
<li>`assert.equal(value1, value2)` -- проверяет равенство `value1 == value2`.</li>
<li>`assert.strictEqual(value1, value2)` -- проверяет строгое равенство `value1 === value2`.</li>
<li>`assert.notEqual`, `assert.notStrictEqual` -- проверки, обратные двум предыдущим.</li>
<li>`assert.isTrue(value)` -- проверяет, что `value === true`</li>
<li>`assert.isFalse(value)` -- проверяет, что `value === false`</li>
<li>...более полный список -- в [документации](http://chaijs.com/api/assert/)</li>
</ul>
- `assert(value)` -- проверяет что `value` является `true` в логическом контексте.
- `assert.equal(value1, value2)` -- проверяет равенство `value1 == value2`.
- `assert.strictEqual(value1, value2)` -- проверяет строгое равенство `value1 === value2`.
- `assert.notEqual`, `assert.notStrictEqual` -- проверки, обратные двум предыдущим.
- `assert.isTrue(value)` -- проверяет, что `value === true`
- `assert.isFalse(value)` -- проверяет, что `value === false`
- ...более полный список -- в [документации](http://chaijs.com/api/assert/)
В нашем случае хорошо бы иметь проверку `assert.isNaN`, но, увы, такого метода нет, поэтому приходится использовать самый общий `assert(...)`. В этом случае для того, чтобы сделать сообщение об ошибке понятнее, желательно добавить к `assert` описание.
@ -405,25 +391,21 @@ describe("pow", function() {
В коде тестов выше можно было бы добавить описание и к `assert.equal`, указав в конце: `assert.equal(value1, value2, "описание")`, но с равенством обычно и так всё понятно, поэтому мы так делать не будем.
## Итого
Итак, разработка завершена, мы получили полноценную спецификацию и код, который её реализует.
Задачи выше позволяют дополнить её, и в результате может получиться что-то в таком духе:
```js
//+ src="pow-full/test.js"
```
[js src="pow-full/test.js"]
[edit src="pow-full"]Открыть полный пример с реализацией в песочнице[/edit]
[edit src="pow-full" title="Открыть полный пример с реализацией в песочнице"]
Эту спецификацию можно использовать как:
<ol>
<li>**Тесты**, которые гарантируют правильность работы кода.</li>
<li>**Документацию** по функции, что она конкретно делает.</li>
<li>**Примеры** использования функции, которые демонстрируют её работу внутри `it`.</li>
</ol>
1. **Тесты**, которые гарантируют правильность работы кода.
2. **Документацию** по функции, что она конкретно делает.
3. **Примеры** использования функции, которые демонстрируют её работу внутри `it`.
Имея спецификацию, мы можем улучшать, менять, переписывать функцию и легко контролировать её работу, просматривая тесты.

View file

@ -10,15 +10,13 @@
У строки есть *свойство* `length`, содержащее длину:
```js
//+ run
```js run
alert( "Привет, мир!".length ); // 12
```
Можно и записать строку в переменную, а потом запросить её свойство:
```js
//+ run
```js run
var str = "Привет, мир!";
alert( str.length ); // 12
```
@ -27,8 +25,7 @@ alert( str.length ); // 12
Также у строк есть *метод* `toUpperCase()`, который возвращает строку в верхнем регистре:
```js
//+ run
```js run
var hello = "Привет, мир!";
*!*
@ -36,14 +33,12 @@ alert( hello.toUpperCase() ); // "ПРИВЕТ, МИР!"
*/!*
```
[warn header="Вызов метода -- через круглые скобки!"]
````warn header="Вызов метода -- через круглые скобки!"
Обратите внимание, для вызова метода обязательно нужны круглые скобки.
Посмотрите, например, результат обращения к `toUpperCase` без скобок:
```js
//+ run
```js run
var hello = "Привет";
*!*
@ -55,25 +50,22 @@ alert( hello.toUpperCase ); // function...
А чтобы получить результат -- нужно произвести её вызов, добавив скобки:
```js
//+ run
```js run
var hello = "Привет";
*!*
alert( hello.toUpperCase() ); // ПРИВЕТ
*/!*
```
````
[/warn]
Более подробно с различными свойствами и методами строк мы познакомимся в главе [](/string).
Более подробно с различными свойствами и методами строк мы познакомимся в главе <info:string>.
## Метод num.toFixed(n)
Есть методы и у чисел, например `num.toFixed(n)`. Он округляет число `num` до `n` знаков после запятой, при необходимости добивает нулями до данной длины и возвращает в виде строки (удобно для форматированного вывода):
```js
//+ run
```js run
var n = 12.345;
alert( n.toFixed(2) ); // "12.35"
@ -81,20 +73,18 @@ alert( n.toFixed(0) ); // "12"
alert( n.toFixed(5) ); // "12.34500"
```
Детали работы `toFixed` разобраны в главе [](/number).
Детали работы `toFixed` разобраны в главе <info:number>.
[warn header="Обращение к методам чисел"]
````warn header="Обращение к методам чисел"
К методу числа можно обратиться и напрямую:
```js
//+ run
```js run
alert( 12.34.toFixed(1) ); // 12.3
```
...Но если число целое, то будет проблема:
```js
//+ run no-beautify
```js run no-beautify
alert(12.toFixed(1)); // ошибка!
```
@ -102,12 +92,10 @@ alert(12.toFixed(1)); // ошибка!
Это -- особенность синтаксиса JavaScript. Вот так -- будет работать:
```js
//+ run
```js run
alert( 12..toFixed(1) ); // 12.0
```
[/warn]
````
## Итого

View file

@ -1,7 +1,6 @@
Узнать количество реально переданных аргументов можно по значению `arguments.length`:
```js
//+ run
```js run
function f(x) {
alert( arguments.length ? 1 : 0 );
}

Some files were not shown because too many files have changed in this diff Show more