works
|
@ -68,7 +68,7 @@ The examples of such restrictions are:
|
|||
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](https://en.wikipedia.org/wiki/National_Security_Agency).
|
||||
- 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. 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.
|
||||
|
||||
|
|
61
1-js/1-getting-started/2-editor/article.md
Normal file
|
@ -0,0 +1,61 @@
|
|||
# Code editors
|
||||
|
||||
The code editor is the place where a programmer spends most his time.
|
||||
|
||||
There are two archetypes: IDE and lightweight editors. Many people feel comfortable choosing one tool of each type.
|
||||
|
||||
[cut]
|
||||
|
||||
## IDE
|
||||
|
||||
The term [IDE](https://en.wikipedia.org/wiki/Integrated_development_environment) (Integrated Development Environment) is a powerful editor with many additional features that usually operates on a "whole project".
|
||||
|
||||
An IDE loads the project (can be many files), and then allows navigate between files, provides autocompletion based on the whole project, does other "project-level" stuff.
|
||||
|
||||
If you haven't considered selecting an IDE, pleae look at the following variants:
|
||||
|
||||
- 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.
|
||||
|
||||
Most IDEs are paid, but have a trial period. Their cost is usually negligible compared to a qualified developer's salary, so just choose the best one for you.
|
||||
|
||||
## Lightweight editors
|
||||
|
||||
"Lightweight editors" are not as powerful as IDEs, but they're fast, elegant and simple.
|
||||
|
||||
They are mainly used to instantly open and edit a file.
|
||||
|
||||
The main difference between a "lightweight editor" and an "IDE" is that the latter works on a project-level, meaning it has to load a lot of data to start, and the former one opens just the files. That's much faster.
|
||||
|
||||
In practice, "lightweight" editors may have a lot of plugins including directory-level syntax analyzers and autocompleters, so there's no strict border between a lightweight editor and an IDE.
|
||||
|
||||
The following options deserve your attention:
|
||||
|
||||
- [Sublime Text](http://www.sublimetext.com) (cross-platform, shareware).
|
||||
- [Atom](https://atom.io/) (cross-platform, free).
|
||||
- [Notepad++](http://sourceforge.net/projects/notepad-plus/) (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:
|
||||
|
||||
- [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 -- [Sublime Text](http://www.sublimetext.com).
|
||||
|
||||
If you don't know what to choose -- you can consider these ones.
|
||||
|
||||
## Let's not argue
|
||||
|
||||
The editors in the lists above are those that me or my friends -- good developers use for a long time and are happy with.
|
||||
|
||||
There are other great editors in our big world, please choose the one you like the most.
|
||||
|
||||
The choice of an editor, like any other tool, is individual and depends on your projects, habbits, personal preferences.
|
66
1-js/1-getting-started/3-devtools/article.md
Normal file
|
@ -0,0 +1,66 @@
|
|||
# Developer console
|
||||
|
||||
And the one more thing before we get down to coding.
|
||||
|
||||
A code is error-prone. You are quite likely to have errors... Oh what I'm talking? You are *absolutely* going to make errors, at least if you're a human, not a [robot](https://en.wikipedia.org/wiki/Bender_(Futurama)).
|
||||
|
||||
But in the browser, a user doesn't see the errors by default. So, if something goes wrong in the script, we won't see what's broken and can't fix it.
|
||||
|
||||
To see errors and get a lot of other useful information about scripts, browsers have embedded "developer tools".
|
||||
|
||||
**It is recommended to use Chrome or Firefox for the development.**
|
||||
|
||||
Other browsers also provide developer tools, but are usually in a "catching-up" position, compared to Chrome/Firefox which are the best.
|
||||
|
||||
If there is an error in the certain browser only, then we can use it's developer tools, but usually -- Chrome/Firefox.
|
||||
|
||||
Developer tools are really powerful, there are many features. On this stage let's just look how to open them, look at errors and run JavaScript commands.
|
||||
|
||||
[cut]
|
||||
|
||||
## Google Chrome
|
||||
|
||||
Open the page [bug.html](bug.html).
|
||||
|
||||
There's an error in the JavaScript code on it. It's hidden from a regular visitor's eyes, so let's open developer tools to see it.
|
||||
|
||||
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:
|
||||
|
||||

|
||||
|
||||
The exact look depends on your Chrome version. It changes from time to time, but should be similar.
|
||||
|
||||
- Here we can see the red-colored error message. In this case the script contains a "lalala" command, which was put there just because it is unknown.
|
||||
- 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).
|
||||
|
||||
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>.
|
||||
|
||||
|
||||
## Firefox, Edge and others
|
||||
|
||||
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.
|
||||
|
||||
## Safari
|
||||
|
||||
Safari (if you use Mac, not Windows/Linux) is a little bit special here. We need to enable the "Develop menu" first.
|
||||
|
||||
There's a checkbox for that at the bottom of the "Advanced" pane of the preferences:
|
||||
|
||||

|
||||
|
||||
Now `key:Cmd+Opt+C` can toggle the console. Also note that the new top menu item has appeared with many useful options.
|
||||
|
||||
## Summary
|
||||
|
||||
- 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.
|
17
1-js/1-getting-started/3-devtools/bug.html
Normal file
|
@ -0,0 +1,17 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
There is an error in the script on this page.
|
||||
<script>
|
||||
lalala
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
BIN
1-js/1-getting-started/3-devtools/chrome.png
Normal file
After Width: | Height: | Size: 74 KiB |
BIN
1-js/1-getting-started/3-devtools/chrome@2x.png
Normal file
After Width: | Height: | Size: 68 KiB |
BIN
1-js/1-getting-started/3-devtools/safari.png
Normal file
After Width: | Height: | Size: 105 KiB |
BIN
1-js/1-getting-started/3-devtools/safari@2x.png
Normal file
After Width: | Height: | Size: 285 KiB |
|
@ -7,7 +7,7 @@ In this chapter we'll create a simple script and see it working.
|
|||
## The "script" tag
|
||||
|
||||
```smart header="What if I want to move faster?"
|
||||
In the case if the reader 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.
|
||||
In the case if you've developed with JavaScript already or have a lot of experience in another language, you can skip detailed explanatins and jump to <info:javascript-specials>. There you can find an essense of important features.
|
||||
|
||||
If you have enough time and want to learn things in details then read on :)
|
||||
```
|
||||
|
|
|
@ -90,7 +90,7 @@ For example, in the code below -- until all rabbits are counted, the bottom `<p>
|
|||
|
||||
The behavior is called "synchronous". Usually it causes no problems, but there's an important consequence.
|
||||
|
||||
**If the script is external, then until the browser executes it, it can't show the page below.**
|
||||
**If a script is external, then until the browser downloads and executes it, a visitor has to wait.**
|
||||
|
||||
So, in this document, until `big.js` loads and executes, the `<body>` content is hidden:
|
||||
|
||||
|
@ -107,21 +107,22 @@ So, in this document, until `big.js` loads and executes, the `<body>` content is
|
|||
</html>
|
||||
```
|
||||
|
||||
The question is -- do we really want to hide the body until the script finishes?
|
||||
The question is -- do we really want the visitor to wait until the script finishes?
|
||||
|
||||
Most of time, we don't.
|
||||
|
||||
Sometimes, a script may contain a very important code that really must be loaded before the rest of the page is parsed (and the scripts below executed). But that's only sometimes.
|
||||
|
||||
Usually a visitor should see the page content while the script is loading.
|
||||
Usually a visitor should be able to see the page while the script is loading. That's especially true for sites with an important content (like the tutorial) -- even if some interfaces are yet inactive (scripts not loaded yet), the visitor can read the text and navigate.
|
||||
|
||||
There are situations when such blocking is even dangerous. For example, when we attach a script from the banner system, or a 3rd-party integration code.
|
||||
|
||||
Like this:
|
||||
|
||||
```html run height=100
|
||||
Wait. The text belown will shown up only after the script executes.
|
||||
Wait. The text below will shown up only after the script executes.
|
||||
|
||||
<!-- this banner script takes time to load -->
|
||||
<script src="/article/external-script/banner.js?speed=0"></script>
|
||||
|
||||
<p>…Important information!</p>
|
||||
|
@ -135,8 +136,8 @@ Our first attempt could be to put all such scripts to the bottom of the `<body>`
|
|||
|
||||
But the solution is not perfect:
|
||||
|
||||
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 web analytics scripts should run immediately as they load.
|
||||
1. The script won't start loading until the HTML loads. If the HTML 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 are many scripts at the bottom of the page, then they queue up. Browser executes only one `<script>` tag in one moment. Please note that it tries to downloads scripts in parallel (for better network utilization), but the execution order is still strictly one after another. So, the first script can block the rest if it's slow. That's not always welcome: some scripts, like ads and web analytics scripts should run independantly and immediately as they load, not block each other.
|
||||
|
||||
And here come the attributes `async` and `defer`.
|
||||
|
||||
|
@ -147,25 +148,15 @@ The `defer` attribute.
|
|||
: The script with `defer` also executes asynchronously, like async. But there are two essential differences:
|
||||
|
||||
1. The browser guarantees to keep the relative order of "deferred" scripts.
|
||||
2. A "deferred" script always executes after HTML-document is fully loaded.
|
||||
2. A "deferred" script always executes after the HTML-document is fully loaded.
|
||||
|
||||
We'll discuss them more in-depth further in this chapter.
|
||||
|
||||
```smart header="Either `async` or `defer`"
|
||||
We can't use both `defer` and `async` on a single script. If we do that, `async` takes precedence, `defer` is ignored.
|
||||
```
|
||||
|
||||
```warn header="Attributes `async/defer` are only for external scripts"
|
||||
The attributes `async/defer` work only when set on a script with `src`.
|
||||
|
||||
On a script without `src` like <code><script>...</script></code>, they will be ignored.
|
||||
```
|
||||
|
||||
Let's modify the "blocking script" example that we've seen before, adding `async`:
|
||||
|
||||
```html run height=100
|
||||
Wait. The text belown will shown up only after the script executes.
|
||||
|
||||
<!-- This banner script take some time to load... But nobody cares. -->
|
||||
<script *!*async*/!* src="/article/external-script/banner.js?speed=0"></script>
|
||||
|
||||
<p>…Important information!</p>
|
||||
|
@ -173,12 +164,11 @@ 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
|
||||
Let's see more examples with `defer` and `async` to clearly understand the similarities and the differences.
|
||||
|
||||
Please note that there is one similarity and two differences between `defer` and `async`.
|
||||
Both attributes allow the browser to show the page without waiting for the script to load. But...
|
||||
|
||||
1. **Both attributes allow the browser to show the page without waiting for the script to load.**
|
||||
2. **Deferred scripts keep the relative order, while async scripts do not.**
|
||||
1. Deferred scripts keep the relative order, while async scripts do not.
|
||||
|
||||
For example, in the code below (with `async`) there are two scripts. The one which loads first will run first.
|
||||
|
||||
|
@ -198,7 +188,7 @@ Please note that there is one similarity and two differences between `defer` and
|
|||
|
||||
This feature of "deferred" scripts is important when `2.js` relies on the result of `1.js` and we must be sure that the order is determined.
|
||||
|
||||
3. **A script with `defer` always waits for the HTML-document to fully load. The `async` script runs immediately as it loads.**
|
||||
2. A script with `defer` always waits for the HTML-document to fully load. The `async` script runs immediately as it loads.
|
||||
|
||||
For instance, when the document is large, like:
|
||||
|
||||
|
@ -212,25 +202,35 @@ Please note that there is one similarity and two differences between `defer` and
|
|||
|
||||
...Here `async.js` executes when it loads -- possibly, before the document is fully loaded. In contrast, `defer.js` always waits for the full document to be ready.
|
||||
|
||||
The choice between `defer` and `async` here depends on our intentions. Sometimes a script doesn't need the document at all (like a web counter), so it should execute ASAP. In this case `async` is superb.
|
||||
So, if a script doesn't need the rest of the document (like a web counter), then `async` is superb. And in another case a script may need the whole document to do some work with it. Then `defer` is preferable.
|
||||
|
||||
|
||||
```smart header="Either `async` or `defer`"
|
||||
We can't use both `defer` and `async` on a single script. If we do that, `async` takes precedence, `defer` is ignored.
|
||||
```
|
||||
|
||||
```warn header="Attributes `async/defer` are only for external scripts"
|
||||
The attributes `async/defer` work only when set on a script with `src`.
|
||||
|
||||
On a script without `src` like <code><script>...</script></code>, they will be ignored.
|
||||
```
|
||||
|
||||
And in another case a script may need the whole document to do some work with it. Then `defer` is preferable.
|
||||
|
||||
## Summary
|
||||
|
||||
- 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.
|
||||
- The browser doesn't show the content below 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..."
|
||||
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 `async`. In other words, they run as they load.
|
||||
For an advanced reader who already knows how to add new tags on the page using DOM: dynamic `<script>` tags behave as `async`. In other words, they run as they load, independantly.
|
||||
|
||||
But we can change that. If we set `script.async = false`, then such scripts are run in the load of insertion.
|
||||
But we can adjust that. If we set `script.async = false`, then such scripts are run in the load of insertion. That's a way to keep the relative order for dynamically added tags.
|
||||
|
||||
The code example:
|
||||
For example:
|
||||
```js
|
||||
function addScript(src){
|
||||
let script = document.createElement('script');
|
||||
|
@ -246,6 +246,6 @@ addScript('2.js'); // but execute in the order of insertion
|
|||
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.
|
||||
We'll surely cover dynamic tags and page manipulation in detail later, in the second part of the tutorial.
|
||||
````
|
||||
|
||||
|
|
|
@ -54,7 +54,6 @@ Looking ahead let's just note that `"use strict"` can be put at the start of a f
|
|||
```
|
||||
|
||||
|
||||
|
||||
## Start with "use strict"
|
||||
|
||||
It is recommended to always start a script with `"use strict"`, for the following reasons:
|
||||
|
@ -62,12 +61,10 @@ It is recommended to always start a script with `"use strict"`, for the followin
|
|||
1. First, because all modern browsers support it, except Internet Explorer 9 and lower.
|
||||
2. Second, the modern JavaScript actually forces us into the strict mode. There are several modern language features like "classes" and "modules" that enable strict mode automatically. So, it's hard to evade it.
|
||||
|
||||
If we're going to support Internet Explorer 9-, then we should also `"use strict"`, but write a code in a compatible way for the outdated browsers to work too. As we'll see, it's not that difficult.
|
||||
|
||||
Here in the tutorial all code (except where said otherwise) works in `"use strict"`.
|
||||
Here in the tutorial all code (except where said otherwise) works in `"use strict"`. but we'll still note the subtle differences of what happens if you forget it or if the visitor has an outdated browser. So you will also be able to write a code for IE8 and below if you'd like that.
|
||||
|
||||
## Summary
|
||||
|
||||
- The `"use strict"` directive switches the engine to the "modern" mode, changing the behavior of some builtin features.
|
||||
- Several modern features of the language enable `"use strict"` implicitly.
|
||||
- It's recommended to start scripts with `"use strict"` all the time.
|
||||
- It's recommended to start scripts with `"use strict"`.
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
We generally use upper case for constants that are "hard-coded" or, in other words, when the value is directly written into the code.
|
||||
|
||||
In this code, `birthday` is just like that. So we could use the upper case for it.
|
||||
|
||||
In contrast, `age` is evaluated in run-time. Today we have one age, a year after we'll have another one. It is constant in a sense that it does not change through the code execution. But it is a bit "less of a constant" than `birthday`, so we should keep the lower case for it.
|
26
1-js/2-first-steps/05-variables/3-uppercast-constant/task.md
Normal file
|
@ -0,0 +1,26 @@
|
|||
importance: 4
|
||||
|
||||
---
|
||||
|
||||
# Uppercase const?
|
||||
|
||||
Examine the following code:
|
||||
|
||||
```js
|
||||
const birthday = '18.04.1982';
|
||||
|
||||
const age = calculateAgeBasedOn(birthday);
|
||||
```
|
||||
|
||||
Here we have a constant `birthday` date and the `age` is calculated with the help of a function (its code is not provided cause it doesn't matter here).
|
||||
|
||||
Would it be right to name both constants using the upper case? Like this:
|
||||
|
||||
```js
|
||||
const BIRTHDAY = '18.04.1982';
|
||||
|
||||
const AGE = calculateAgeBasedOn(BIRTHDAY);
|
||||
```
|
||||
|
||||
...Or we should use the upper case for only one of them? Or just use lower case everywhere?
|
||||
|
|
@ -73,20 +73,20 @@ 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.
|
||||
There are subtle differences between `let` and `var`, but they do not matter for us yet. We'll cover them in detail later.
|
||||
````
|
||||
|
||||
## Real-life analogy
|
||||
## A real-life analogy
|
||||
|
||||
We can easily grasp the concept of a "variable" if we imagine it as a "box" for the data, with the unique-named sticker on it.
|
||||
|
||||
For instance, the variable `message` is a box with the value `"Hello!"` labelled `"message"`:
|
||||
For instance, the variable `message` can be imagined as a box labelled `"message"` with the value `"Hello!"` in it:
|
||||
|
||||

|
||||
|
||||
We can put any value into the box (variable).
|
||||
We can put any value into the box.
|
||||
|
||||
Also we can change it. 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 needed:
|
||||
|
||||
```js run
|
||||
let message;
|
||||
|
@ -110,7 +110,7 @@ let hello = 'Hello world!';
|
|||
let message;
|
||||
|
||||
*!*
|
||||
// copy 'Hello world' into message
|
||||
// copy 'Hello world' from hello into message
|
||||
message = hello;
|
||||
*/!*
|
||||
|
||||
|
@ -122,7 +122,7 @@ alert( message ); // Hello world!
|
|||
```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 -- the language forces to create a new box (declare a new variable), we can't reuse the old one.
|
||||
In such languages, once the value is assined "into the box" -- it's there forever. If we need to store something else -- the language forces to create a new box (declare a new variable), we can't reuse the old one.
|
||||
|
||||
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.
|
||||
```
|
||||
|
@ -143,7 +143,7 @@ let test123;
|
|||
|
||||
When the name contains multiple words, [camelCase](https://en.wikipedia.org/wiki/CamelCase) is commonly used. That is: words go one after another, each word starts with a capital letter: `myVeryLongName`.
|
||||
|
||||
What's interesting -- the dollar sign `'$'` and the underscore `'_'` are considered ordinary symbols, just like letters.
|
||||
What's interesting -- the dollar sign `'$'` and the underscore `'_'` also can be used in names. They are regular symbols, just like letters, without any special meaning.
|
||||
|
||||
These names are valid:
|
||||
|
||||
|
@ -182,7 +182,7 @@ There is a list of reserved words, which cannot be used as variable names, becau
|
|||
|
||||
For example, words `let`, `class`, `return`, `function` are reserved.
|
||||
|
||||
The code below will give a syntax error:
|
||||
The code below gives a syntax error:
|
||||
|
||||
```js run no-beautify
|
||||
let let = 5; // can't name a variable "let", error!
|
||||
|
@ -190,19 +190,17 @@ let return = 5; // also can't name it "return", error!
|
|||
```
|
||||
````
|
||||
|
||||
## Non-Strict mode assignment
|
||||
````warn header="An assignment without `use strict`"
|
||||
|
||||
Without strict mode, it is possible to create a variable without a `let`, by a mere assignment of the value:
|
||||
Normally, we need to define a variable before using it. But in the old times, it was technically possible to create a variable by a mere assignment of the value. This still works now in if we don't put `use strict`, the behavior is kept for compatibility with old scripts.
|
||||
|
||||
```js run no-strict
|
||||
num = 5; // the variable "num" is created if didn't exist
|
||||
|
||||
alert(num);
|
||||
alert(num); // 5
|
||||
```
|
||||
|
||||
...But that is an old feature of the language, kept for compatibility with the old scripts. It's usage has been frowned-upon for a long time. It's an error in the strict mode.
|
||||
|
||||
The code with `"use strict"` will give an error:
|
||||
That's a bad practice of course, it gives an error in the strict mode:
|
||||
|
||||
```js run untrusted
|
||||
"use strict";
|
||||
|
@ -212,20 +210,17 @@ num = 5; // error: num is not defined
|
|||
*/!*
|
||||
```
|
||||
|
||||
|
||||
## 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 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 V8 will remind you about it by giving an error on `let`.
|
||||
````
|
||||
|
||||
## Constants
|
||||
|
||||
Variables with a fixed value are called "constant variables" or just *constants*.
|
||||
To declare a constant (unchanging) variable, one can use `const`:
|
||||
|
||||
To declare a constant variable, one can use `const`:
|
||||
```js
|
||||
const myBirthday = '18.04.1982';
|
||||
```
|
||||
|
||||
The variable declared using `const` are called "constants". They can not be changed. An attempt to do it would cause an error:
|
||||
|
||||
```js run
|
||||
const myBirthday = '18.04.1982';
|
||||
|
@ -233,16 +228,8 @@ const myBirthday = '18.04.1982';
|
|||
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.
|
||||
When a programmer is sure that the variable should never change, he can use `const` to guarantee it, and also to clearly show that fact to everyone.
|
||||
|
||||
```js run
|
||||
const myBirthday = '18.04.1982';
|
||||
|
||||
// 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
|
||||
|
||||
|
@ -258,28 +245,27 @@ const COLOR_GREEN = "#0F0";
|
|||
const COLOR_BLUE = "#00F";
|
||||
const COLOR_ORANGE = "#FF7F00";
|
||||
|
||||
// ...when we need to pick a color
|
||||
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`.
|
||||
|
||||
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.
|
||||
The upper case is only used for constants that are "hard-coded" (written in the code before its execution).
|
||||
|
||||
|
||||
## Name things right
|
||||
|
||||
We're almost done with the initial understanding of variables, but there's one more thing.
|
||||
Talking about variables, there's an exteremely important 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.
|
||||
Variable naming is one of the most important and complex skills in programming. A quick glance at variable names can obviously show which code is written by a beginner and which by an experienced guru.
|
||||
|
||||
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.
|
||||
In a real project, most of the time is spent on modifying and extending the existing code, rather than writing something completely new.
|
||||
|
||||
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.
|
||||
And when we return to the code after some time of doing something else, it's much easier to find the information that is well-labelled. Or, in other words, when the variables are named right.
|
||||
|
||||
Please spend some time thinking about the right name for a variable before declaring it. That will repay you a lot.
|
||||
|
||||
|
@ -287,13 +273,13 @@ Few good-to-follow rules are:
|
|||
|
||||
- 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.
|
||||
- Make the name maximally descriptive and concise. Examples of bad names are `data` and `value`. Such a name says nothing. It is only ok to use them if it's exceptionally obvious from the context 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? Indeed it is. But creating good descriptive-and-concise names in practice is not. Go for it.
|
||||
|
||||
```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.
|
||||
And the last note. There are some lazy programmers who, instead of declaring a new variable, tend to reuse the existing ones.
|
||||
|
||||
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.
|
||||
|
||||
|
@ -301,7 +287,7 @@ Such a programmer saves a little bit on variable declaration, but looses ten tim
|
|||
|
||||
An extra variable is good, not evil.
|
||||
|
||||
P.S. Modern JavaScript minifiers and browsers optimize code well enough so that won't create a performance issue.
|
||||
Modern JavaScript minifiers and browsers optimize code well enough, so it won't create performance issues. Using different variables for values of different types can even help the engine to optimize better.
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
|
|
@ -44,8 +44,8 @@ Besides regular numbers there are so-called "special numeric values" which also
|
|||
|
||||
So, in a long mathematical expression if we have `NaN` in one place, it propagates to the whole result.
|
||||
|
||||
```smart header="Maths is safe"
|
||||
Maths is safe in JavaScript. We can do anything: divide by zero, treat non-numeric strings as numbers, etc.
|
||||
```smart header="Mathematical operations are safe"
|
||||
Doing maths is safe in JavaScript. We can do anything: divide by zero, treat non-numeric strings as numbers, etc.
|
||||
|
||||
The script will never die. At worst we'll get `NaN` as the result.
|
||||
```
|
||||
|
@ -68,7 +68,7 @@ In JavaScript, there are 3 types of quotes.
|
|||
2. Single quotes: `'Hello'`.
|
||||
3. Backtricks: <code>`Hello`</code>.
|
||||
|
||||
Double and single quotes are essentially the same.
|
||||
Double and single quotes are similar, "simple" quotes.
|
||||
|
||||
Backticks are "extended functionality" quotes. They allow to embed variables and expressions into a string by wrapping them in `${…}`, for example:
|
||||
|
||||
|
@ -96,7 +96,7 @@ In JavaScript, there is no such type. There's only one type: `string`. A string
|
|||
|
||||
The boolean type has only two values: `true` and `false`.
|
||||
|
||||
This type is commonly used to store the yes/no values.
|
||||
This type is commonly used to store yes/no values.
|
||||
|
||||
For instance:
|
||||
|
||||
|
@ -108,8 +108,9 @@ checked = false; // the form field is not checked
|
|||
Boolean values also come as the result of comparisons:
|
||||
|
||||
```js run
|
||||
let isGreater = 4 > 1;
|
||||
alert( isGreater ); // true
|
||||
let isGreater = 4 > 1;
|
||||
|
||||
alert( isGreater ); // true (the comparison result is "yes")
|
||||
```
|
||||
|
||||
We'll cover booleans more deeply while discussing logical operators.
|
||||
|
@ -132,7 +133,7 @@ The code above states that the `age` is unknown.
|
|||
|
||||
## The "undefined" value
|
||||
|
||||
The special value `undefined` stands separately. It makes a type of its own, just like `null`.
|
||||
The special value `undefined` stands apart. It makes a type of its own, just like `null`.
|
||||
|
||||
The sense of `undefined` is "value is not assigned".
|
||||
|
||||
|
@ -154,21 +155,21 @@ x = undefined;
|
|||
alert( x ); // "undefined"
|
||||
```
|
||||
|
||||
...But it's not recommended to do that, because such assignment contradicts to the sense of `undefined`.
|
||||
...But it's not recommended to do that. Normally, we use `null` to write an "empty" or an "unknown" value into the variable, and `undefined` is only used for checks, to see if the variable is assigned or similar.
|
||||
|
||||
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
|
||||
## Objects
|
||||
|
||||
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.
|
||||
In contrast, objects are used to store *keyed collections* of various data and more complex entities.
|
||||
|
||||
An object is defined with the figure brackets `{…}`.
|
||||
An object is defined with the figure brackets `{…}` with an optional list of "key: value" pairs. In programming that's sometimes called an "associative array" or a "hash".
|
||||
|
||||
For instance, here we create a `user` object with the name and the age:
|
||||
A `key` is a string, `value` can be anything.
|
||||
|
||||
For instance, here we create a `user` object with two properties:
|
||||
|
||||
```js
|
||||
let user = {
|
||||
|
@ -177,12 +178,14 @@ let user = {
|
|||
};
|
||||
```
|
||||
|
||||
Now `user` is a complex entity, can be imagine of a shelf with two folders labelled "name" and "age".
|
||||
The `user` object can be imagined as an information shelf with two items labelled "name" and "age".
|
||||
|
||||
We can access either via the dot notation:
|
||||

|
||||
|
||||
Both values (also called "fields" or "properties") are accessible using the dot notation:
|
||||
|
||||
```js
|
||||
// get parts of the object:
|
||||
// get fields of the object:
|
||||
alert( user.name ); // John
|
||||
alert( user.age ); // 30
|
||||
```
|
||||
|
@ -193,13 +196,17 @@ Also we can add new information to the user any time later:
|
|||
user.isAdmin = true;
|
||||
```
|
||||
|
||||

|
||||
|
||||
...Or remove it with the help of `delete` operator:
|
||||
|
||||
```js
|
||||
delete user.age;
|
||||
```
|
||||
|
||||
Any string can be used as a property name. But if it has multiple words, then we should use another kind of notation to access it. Namely, square brackets:
|
||||

|
||||
|
||||
If the string which denotes the key (also called a "property name") has multiple words, then we should use square brackets notation to access it:
|
||||
|
||||
```js
|
||||
user["likes to swim?"] = true;
|
||||
|
@ -207,29 +214,39 @@ user["likes to swim?"] = true;
|
|||
|
||||
See, the dot requires the property name to be a valid variable identifier. That is: no spaces and other limitations. Square brackets work with any string.
|
||||
|
||||
We can split the code above into two lines by putting the property name into a variable:
|
||||
|
||||
```js
|
||||
// this would give a syntax error
|
||||
user.likes to swim? = true;
|
||||
```
|
||||
|
||||
Another powerful feature of square bracket notation is that they allow to access a property by the name from the variable:
|
||||
|
||||
```js
|
||||
let key = "likes to swim?";
|
||||
user[key] = true; // same as above
|
||||
```
|
||||
|
||||
Objects in JavaScript are very powerful. Here we just started the topic that is really huge. We'll be closely working with objects in the next parts of the tutorial.
|
||||
Here we have a variable `key` which contains the property name, probably evaluated or calculated at run-time.
|
||||
|
||||
Most of time, the dot is used to access object properties, but when we need a complex property name or to pass the name as a variable, then -- we go square brackets.
|
||||
|
||||
Objects in JavaScript are very powerful. Here we've just scratched the surface of the topic that is really huge. We'll be closely working with objects and learning more about them in further parts of the tutorial.
|
||||
|
||||
## Symbol type
|
||||
|
||||
The `symbol` type stands apart from the others. 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.
|
||||
The `symbol` type is used in conjunction with objects. 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 here for the sake of completeness.
|
||||
|
||||
Type "symbol" represents an unique identifier with a given name.
|
||||
A "symbol" represents an unique identifier with a given name.
|
||||
|
||||
A value of this type can be created like this:
|
||||
A value of this type can be created using `Symbol(name)`:
|
||||
|
||||
```js
|
||||
// id is a symbol with the name "id"
|
||||
let id = Symbol("id");
|
||||
```
|
||||
|
||||
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) in that two symbols with the same name are different:
|
||||
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). Two symbols with the same name are not the same:
|
||||
|
||||
```js run
|
||||
let id1 = Symbol("id");
|
||||
|
@ -238,25 +255,46 @@ let id2 = Symbol("id");
|
|||
alert(id1 == id2); // false
|
||||
```
|
||||
|
||||
Symbols is a separate primitive type used for identifiers, which are guaranteed to be unique.
|
||||
Symbols is a special primitive type used for identifiers, which are guaranteed to be unique. So, even if we create many symbols with the same name, they are still unique.
|
||||
|
||||
So if we want to create a "hidden" property in an object, only for us, and ensure that no other part of code can occasionally access it, we can create a symbol for it:
|
||||
The use case for symbols is to create "concealed" properties of an object, which only make sense locally, that no other part of code can occasionally access or overwrite.
|
||||
|
||||
For instance, if we want to store an "identifier" for the object `user`, we can create a symbol with the name `id` for it:
|
||||
|
||||
```js run
|
||||
let user = { name: "John" };
|
||||
let id = Symbol("id");
|
||||
|
||||
user[id] = "Secret ID Value";
|
||||
alert( user[id] ); // we can access the data
|
||||
user[id] = "ID Value";
|
||||
alert( user[id] ); // we can access the data using the symbol as the key
|
||||
```
|
||||
|
||||
Now, if another script wants his own "id" property inside `user`, it can create its own `Symbol("id")`. There will be no conflict, because symbols are always different.
|
||||
Now let's imagine that another script wants to have his own "id" property inside `user`, for his own purposes. That may be another javascript library, so the scripts are completely unaware for each other.
|
||||
|
||||
Symbols are widely used by the JavaScript language itself to store "system" properties. We'll find out more details after studying objects.
|
||||
No problem. It can create its own `Symbol("id")`. There will be no conflict, because symbols are always different, even if they have the same name.
|
||||
|
||||
Please note that in the same case if we used a string `"id"` instead of a symbol here, then there would be a conflict:
|
||||
|
||||
```js run
|
||||
let user = { name: "John" };
|
||||
|
||||
// our script
|
||||
user.id = "ID Value";
|
||||
|
||||
// ...if later another script the uses same name for its purposes...
|
||||
|
||||
user.id = "ID 2"
|
||||
// then boom! overwritten! it did not mean to harm the colleague, but did it!
|
||||
```
|
||||
|
||||
Two `Symbol("id")` are not equal, that's why they would allow to store values safely.
|
||||
|
||||
|
||||
Symbols are widely used by the JavaScript language itself to store "system" properties which we'll learn in later chapters.
|
||||
|
||||
## The typeof operator [#type-typeof]
|
||||
|
||||
The `typeof` operator returns the type of the argument.
|
||||
The `typeof` operator returns the type of the argument. It's handy in the case when we want to process values of different types differently, or just to make a quick check.
|
||||
|
||||
It allows two forms of syntax:
|
||||
|
||||
|
@ -289,10 +327,10 @@ typeof alert // "function" (2)
|
|||
*/!*
|
||||
```
|
||||
|
||||
Please note the last two lines, because `typeof` behaves specially there.
|
||||
Please note the last two lines, because `typeof` behaves a little bit strange there.
|
||||
|
||||
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.
|
||||
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. So `typeof` just says it wrong here.
|
||||
2. The result of `typeof alert` is `"function"`, because `alert` is a function of the language. We'll study functions in the near future and see that actually functions belong to the object type. But `typeof` treats them differently. That's very convenient in practice.
|
||||
|
||||
|
||||
## Type conversions
|
||||
|
@ -305,19 +343,19 @@ let message = "hello";
|
|||
message = 123456;
|
||||
```
|
||||
|
||||
...But sometimes we need to convert a value from one type to another. For example, `alert` automatically converts any value to a string, to show it. Or, so to say, an `if (value)` converts it's argument into a boolean to see if it's `true` or `false`.
|
||||
...But sometimes we need to convert a value from one type to another. For example, `alert` automatically converts any value to a string, to show it. Or, so to say, an `if(value)` test converts the `value` to boolean type to see if it's `true` or `false`.
|
||||
|
||||
There are also cases when we need to convert between types to ensure that we store the right data the right way, or to use special features of a certain type.
|
||||
There are also cases when we need to explicitly convert between types to ensure that we store the right data the right way or to use special features of a certain type.
|
||||
|
||||
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 most often are:
|
||||
Three conversions that happen the most often:
|
||||
|
||||
1. String conversion.
|
||||
2. Numeric conversion.
|
||||
3. Boolean conversion.
|
||||
|
||||
Let's see how and when they happen.
|
||||
Let's see how they work and when they happen.
|
||||
|
||||
### String conversion
|
||||
|
||||
|
@ -335,9 +373,12 @@ We can also use a call `String(value)` function for that:
|
|||
|
||||
```js run
|
||||
let a = true;
|
||||
alert(typeof a); // boolean
|
||||
|
||||
*!*
|
||||
a = String(a); // now: a = "true"
|
||||
alert(typeof a); // string
|
||||
*/!*
|
||||
```
|
||||
|
||||
The string conversion is obvious. A `false` becomes `"false"`, `null` becomes `"null"` etc.
|
||||
|
@ -380,7 +421,7 @@ The numeric conversion rules:
|
|||
|`undefined`|`NaN`|
|
||||
|`null`|`0`|
|
||||
|<code>true / 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`. |
|
||||
| `string` | Whitespaces from the start and the end are removed. Then, if the remaining string is empty, the result is `0`, otherwise –- the number is "read" from the string. An error gives `NaN`. |
|
||||
|
||||
Examples:
|
||||
|
||||
|
@ -391,15 +432,17 @@ alert( Number(true) ); // 1
|
|||
alert( Number(false) ); // 0
|
||||
```
|
||||
|
||||
Please note that `null` and `undefined` behave differently here. A `null` becomes a zero, but `undefined` becomes `NaN`.
|
||||
Please note that `null` and `undefined` behave differently here: `null` becomes a zero, while `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)`.
|
||||
Boolean conversion is the simplest one.
|
||||
|
||||
The conversion rules are simple here:
|
||||
It happens in logical operations (later we'll meet `if` tests and other kinds), but also can be performed manually with the call of `Boolean(value)`.
|
||||
|
||||
- All values that are intuitively "empty" become `false`. These are: `0`, an empty string, `null`, `undefined` and `NaN`.
|
||||
The conversion rule:
|
||||
|
||||
- Values that are intuitively "empty", like `0`, an empty string, `null`, `undefined` and `NaN` become `false`.
|
||||
- Other values become `true`.
|
||||
|
||||
````warn header="Please note: a string `\"0\"` is `true`"
|
||||
|
|
BIN
1-js/2-first-steps/07-types/object-user-delete.png
Normal file
After Width: | Height: | Size: 3.8 KiB |
BIN
1-js/2-first-steps/07-types/object-user-delete@2x.png
Normal file
After Width: | Height: | Size: 8.6 KiB |
BIN
1-js/2-first-steps/07-types/object-user-isadmin.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
1-js/2-first-steps/07-types/object-user-isadmin@2x.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
1-js/2-first-steps/07-types/object-user.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
BIN
1-js/2-first-steps/07-types/object-user@2x.png
Normal file
After Width: | Height: | Size: 7.6 KiB |
|
@ -1,5 +1,4 @@
|
|||
|
||||
|
||||
```js no-beautify
|
||||
"" + 1 + 0 = "10" // (1)
|
||||
"" - 1 + 0 = -1 // (2)
|
||||
|
|
|
@ -4,9 +4,9 @@ importance: 5
|
|||
|
||||
# Type conversions
|
||||
|
||||
Let's recap type conversions in the context of operators.
|
||||
Let's recap type conversions given in the [previous capter](/types) in the context of operators.
|
||||
|
||||
What will be the result for these expressions?
|
||||
What will be the result of these evaluation?
|
||||
|
||||
```js no-beautify
|
||||
"" + 1 + 0
|
||||
|
@ -15,7 +15,7 @@ true + false
|
|||
6 / "3"
|
||||
2" * "3"
|
||||
4 + 5 + "px"
|
||||
$" + 4 + 5
|
||||
"$" + 4 + 5
|
||||
"4" - 2
|
||||
"4px" - 2
|
||||
7 / 0
|
||||
|
|
|
@ -10,8 +10,8 @@ In this chapter we concentrate on aspects that are not covered by the school ari
|
|||
|
||||
Before we move on, let's make a dip in the common terminology, used in the development.
|
||||
|
||||
- *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:
|
||||
- *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`. Sometimes operands are called "arguments".
|
||||
- An operator is *unary* if it has a single operand. For example, the unary minus `"-"` reverses the sign of the number:
|
||||
|
||||
```js run
|
||||
let x = 1;
|
||||
|
@ -21,17 +21,19 @@ Before we move on, let's make a dip in the common terminology, used in the devel
|
|||
*/!*
|
||||
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:
|
||||
- An operator is *binary* if it has two operands. The same minus exists in the binary form as well:
|
||||
|
||||
```js run no-beautify
|
||||
let x = 1, y = 3;
|
||||
alert( y - x ); // 2, binary minus
|
||||
alert( y - x ); // 2, binary minus substracts values
|
||||
```
|
||||
|
||||
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).
|
||||
|
||||
## Strings concatenation, binary +
|
||||
|
||||
Now let's get into Javascript specials.
|
||||
|
||||
Usually the plus operator `'+'` sums numbers.
|
||||
|
||||
But if the binary `+` is applied to strings, it merges (concatenates) them:
|
||||
|
@ -54,18 +56,18 @@ Note that it doesn't matter whether the first operand is a string or the second
|
|||
|
||||
The string concatenation and conversion is the special feature of the binary plus `"+"`. Other arithmetic operators work only with numbers. They always convert their operands to numbers.
|
||||
|
||||
For instance:
|
||||
For instance, substraction and division:
|
||||
|
||||
```js run
|
||||
alert( 2 - '1' ); // 1
|
||||
alert( 6 / '2' ); // 3
|
||||
alert( '6' / '2' ); // 3
|
||||
```
|
||||
|
||||
## Numeric conversion, unary +
|
||||
|
||||
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.
|
||||
The unary plus or, in other words, the plus operator `+` 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:
|
||||
|
||||
|
@ -77,14 +79,16 @@ alert( +x ); // 1
|
|||
let y = -2;
|
||||
alert( +y ); // -2
|
||||
|
||||
*!*
|
||||
// Converts non-numbers
|
||||
alert( +true ); // 1
|
||||
alert( +true ); // 1
|
||||
alert( +"" ); // 0
|
||||
*/!*
|
||||
```
|
||||
|
||||
It actually does the same as `Number()`, but it's shorter.
|
||||
It actually does the same as `Number(true)` or `Number("")`, but shorter.
|
||||
|
||||
As an example of use, let's imagine that we are getting values from HTML-form fields. Then they are usually strings.
|
||||
A need to convert string to number arises very often. For example, if we are getting values from HTML form fields, then are usually strings.
|
||||
|
||||
What if we want to sum them?
|
||||
|
||||
|
@ -105,16 +109,16 @@ let oranges = "3";
|
|||
|
||||
alert( Number(apples) + Number(oranges) ); // 5
|
||||
|
||||
// or the shorter variant:
|
||||
*!*
|
||||
alert( +apples + +oranges ); // 5
|
||||
// or the shorter variant:
|
||||
// both values converted to numbers before the binary plus
|
||||
alert( +apples + +oranges ); // 5
|
||||
*/!*
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
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*.
|
||||
Why are unary pluses applied to values before the binary one? As we're going to see, that's because of their *higher precedence*.
|
||||
|
||||
## Operators precedence
|
||||
|
||||
|
@ -142,7 +146,7 @@ An extract from the table:
|
|||
| 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.
|
||||
As we can see, the "unary plus" has a priority of `15`, higher than `13` for the "addition" (binary plus). That's why in the expression `+apples + +oranges` unary pluses worked first, and then the addition.
|
||||
|
||||
## Assignment
|
||||
|
||||
|
@ -170,10 +174,10 @@ alert( b ); // 4
|
|||
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.
|
||||
Assignments always evaluate the right value first, then assign it to the left one. So the chain of assignments is executed from right to left: the rightmost expression `2+2` is calculated first, assigned to `c`, then `b = c` works, thus assigning it to `b`, and then `a = b`. At the end, all variables share a single value.
|
||||
|
||||
````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.
|
||||
An operator always returns a value. That's obvious for most of them like an addition `+` or a multiplication `*`. But the assignment follows that rule too.
|
||||
|
||||
The call `x = value` writes the `value` into `x` *and then returns it*.
|
||||
|
||||
|
@ -193,7 +197,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.
|
||||
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 clearer and more readable.
|
||||
````
|
||||
|
||||
## Remainder %
|
||||
|
@ -235,15 +239,16 @@ So, there are special operators for that:
|
|||
Increment/decrement can be applied only to a variable. An attempt to use it on a value like `5++` will give an error.
|
||||
```
|
||||
|
||||
Operators `++` and `--` can be put both after and before the variable.
|
||||
Operators `++` and `--` can be placed both after and before the variable.
|
||||
|
||||
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`.
|
||||
- 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`.
|
||||
|
||||
Is there any difference? Yes, but we can see it if we use the result of `++/--`.
|
||||
Is there any difference? Yes, but we can only see it if we use the retured value of `++/--`.
|
||||
|
||||
As we know, all operators return a value. Increment/decrement is not an exception here. The prefix form returns the new value, while the postfix form returns the old value (prior to increment/decrement).
|
||||
Let's clarify. As we know, all operators return a value. Increment/decrement is not an exception here. The prefix form returns the new value, while the postfix form returns the old value (prior to increment/decrement).
|
||||
|
||||
Let's see the examples:
|
||||
|
||||
|
@ -291,7 +296,7 @@ To summarize:
|
|||
```
|
||||
|
||||
````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.
|
||||
Operators `++/--` can be used inside an expression as well. Their precedence is higher than most other arithmetical operations.
|
||||
|
||||
For instance:
|
||||
|
||||
|
@ -336,7 +341,7 @@ The list of operators:
|
|||
- RIGHT SHIFT ( `>>` )
|
||||
- ZERO-FILL RIGHT SHIFT ( `>>>` )
|
||||
|
||||
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.
|
||||
These operators are used very rarely. To understand them, we should delve into low-level number representation, and it would not be optimal to do that right now. Especially because we won't need them any time soon. If you're curious, you can read the [Bitwise Operators](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators) article in MDN. But it would be more practical to return to this topic later when a real need arises.
|
||||
|
||||
## Modify-in-place
|
||||
|
||||
|
@ -350,7 +355,7 @@ n = n + 5;
|
|||
n = n * 2;
|
||||
```
|
||||
|
||||
This notation can be shortened using operators `+=` and *=`:
|
||||
This notation can be shortened using operators `+=` and `*=`:
|
||||
|
||||
```js run
|
||||
let n = 2;
|
||||
|
@ -360,7 +365,7 @@ n *= 2; // now n=14 (same as n = n * 2)
|
|||
alert( n ); // 14
|
||||
```
|
||||
|
||||
Short "modify-and-assign" operators exist for `+,-,*,/` and bitwise `<<,>>,>>>,&,|,^`.
|
||||
Short "modify-and-assign" operators exist for all arithmetical and bitwise operators: `/=`, `-=` etc.
|
||||
|
||||
The modify-and-assign call has the same precedence as a normal assignment, so it executes after most other calculations:
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Comparisons
|
||||
|
||||
Many comparison operators we know from the maths:
|
||||
Many comparison operators we know from maths:
|
||||
|
||||
- Greater/less than: <code>a > b</code>, <code>a < b</code>.
|
||||
- Greater/less than or equals: <code>a >= b</code>, <code>a <= b</code>.
|
||||
|
@ -61,7 +61,7 @@ Strings `"Glow"` and `"Glee"` are compared character-by-character:
|
|||
2. `l` is the same as `l`.
|
||||
3. `o` is greater than `e`. Stop here. The first string is greater.
|
||||
|
||||
```smart header="Not a real dictionary"
|
||||
```smart header="Not a dictionary, but Unicode"
|
||||
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>.
|
||||
|
@ -126,7 +126,7 @@ 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 convert them.
|
||||
In other words, if `a` and `b` are of different types then `a === b` immediately returns `false`, without an attempt to convert them.
|
||||
|
||||
Let's try it:
|
||||
|
||||
|
@ -136,7 +136,7 @@ alert( 0 === false ); // false, because the types are different
|
|||
|
||||
There also exists a "strict non-equality" operator `!==`, as an analogy for `!=`.
|
||||
|
||||
The string equality check is a bit longer to write, but it's more obvious what's going on.
|
||||
The string equality check operator is a bit longer to write, but makes more obvious what's going on.
|
||||
|
||||
## Comparison with null and undefined
|
||||
|
||||
|
@ -144,13 +144,13 @@ Let's see more corner cases.
|
|||
|
||||
There's a non-intuitive behavior when `null` or `undefined` is compared with other values.
|
||||
|
||||
- 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.
|
||||
|
||||
- For a strict equality check `===` these values are different, because each of them belong to a separate type of it's own.
|
||||
|
||||
- For evaluation of other comparisons including `<`, `>`, `<=`, `>=`, these values are converted to a number. We surely remember that `null` becomes `0`, while `undefined` becomes `NaN`.
|
||||
- For a non-strict check `null == undefined`, there's a special rule. These two are a "sweet couple". They equal each other (in the sense of `==`), but no any other value.
|
||||
|
||||
Now let's see funny things that happen when we apply those rules.
|
||||
- For maths and evaluation of other comparisons including `<`, `>`, `<=`, `>=`, values `null/undefined` are converted to a number: `null` becomes `0`, while `undefined` becomes `NaN`.
|
||||
|
||||
Now let's see funny things that happen when we apply those rules. And then, later, how do not fall into a trap with unobvious language features.
|
||||
|
||||
### Strange result: null vs 0
|
||||
|
||||
|
@ -162,13 +162,13 @@ alert( null == 0 ); // false
|
|||
alert( null >= 0 ); // *!*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. The last result states that "`null` is equal or greater than zero". Then one of the comparisons above must be correct, but they are both falsy.
|
||||
|
||||
Well, every programmer language has its own unobvious features. That was an example for Javascript.
|
||||
|
||||
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.
|
||||
|
||||
That's why we have a strange-looking situation above.
|
||||
From the other hand, equality `==` for `undefined` and `null` works without any conversions. There's just a rule that they equal each other and don't equal anything else. That's why `null == 0` is false.
|
||||
|
||||
### An uncomparable undefined
|
||||
|
||||
|
@ -187,15 +187,14 @@ We've got such result, because:
|
|||
- 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.
|
||||
|
||||
### Conclusion
|
||||
### How to live a good life
|
||||
|
||||
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.
|
||||
Why did we observe these examples? Should we remember these pecularities all the time? Well, not really. Actually, these tricky things will gradually become familiar over the time, but there's a solid way to evade any problems with them.
|
||||
|
||||
Any comparison with `undefined/null` except the strict equality `===` should be done with exceptional care.
|
||||
Just treat any comparison with `undefined/null` except the strict equality `===` with an exceptional care.
|
||||
|
||||
**It is preferable not to use comparisons `>= > < <=` with a variable which may be `null/undefined`.**
|
||||
Don't use comparisons `>= > < <=` with a variable which may be `null/undefined`, unless you are really sure what you're doing. If a variable can have such values, then check it separately.
|
||||
|
||||
We can always make a separate check for `null` or add an explicit type conversion.
|
||||
|
||||
## Summary
|
||||
|
||||
|
|
|
@ -8,6 +8,8 @@ It gives a more descriptive way to compare a value with multiple variants.
|
|||
|
||||
## The syntax
|
||||
|
||||
The `switch` has one or more `case` blocks and an optional default.
|
||||
|
||||
It looks like this:
|
||||
|
||||
```js no-beautify
|
||||
|
@ -111,9 +113,9 @@ switch (+a) {
|
|||
|
||||
## Grouping of "case"
|
||||
|
||||
Several variants of `case` can be grouped.
|
||||
Several variants of `case` which share the same code can be grouped.
|
||||
|
||||
For example, if we want the same code for `case 3` and `case 5`:
|
||||
For example, if we want the same code to run for `case 3` and `case 5`:
|
||||
|
||||
```js run no-beautify
|
||||
let a = 2 + 2;
|
||||
|
@ -124,7 +126,7 @@ switch (a) {
|
|||
break;
|
||||
|
||||
*!*
|
||||
case 3: // (*)
|
||||
case 3: // (*) grouped two cases
|
||||
case 5:
|
||||
alert('Wrong!');
|
||||
alert('How about to take maths classes?');
|
||||
|
@ -136,7 +138,9 @@ switch (a) {
|
|||
}
|
||||
```
|
||||
|
||||
The grouping is just a side-effect of how `switch/case` works without `break`. Here the execution of `case 3` starts from the line `(*)` and goes through `case 5`, because there's no `break`.
|
||||
Now both `3` and `5` show the same message.
|
||||
|
||||
The ability to "group" cases a side-effect of how `switch/case` works without `break`. Here the execution of `case 3` starts from the line `(*)` and goes through `case 5`, because there's no `break`.
|
||||
|
||||
## The type matters
|
||||
|
||||
|
|
|
@ -75,7 +75,7 @@ A function can access an outer variable as well, for example:
|
|||
let *!*userName*/!* = 'John';
|
||||
|
||||
function showMessage() {
|
||||
let message = 'Hello, my name is ' + *!*userName*/!*;
|
||||
let message = 'Hello, ' + *!*userName*/!*;
|
||||
alert(message);
|
||||
}
|
||||
|
||||
|
@ -90,26 +90,24 @@ For instance:
|
|||
let *!*userName*/!* = 'John';
|
||||
|
||||
function showMessage() {
|
||||
userName = "Bob"; // (1) changed the outer variable
|
||||
*!*userName*/!* = "Bob"; // (1) changed the outer variable
|
||||
|
||||
let message = 'Hello, my name is ' + *!*userName*/!*;
|
||||
let message = 'Hello, ' + *!*userName*/!*;
|
||||
alert(message);
|
||||
}
|
||||
|
||||
alert( userName ); // John before the function call
|
||||
alert( userName ); // *!*John*/!* before the function call
|
||||
|
||||
showMessage();
|
||||
|
||||
*!*
|
||||
alert( userName ); // Bob, the value was modified by the function
|
||||
*/!*
|
||||
alert( userName ); // *!*Bob*/!*, the value was modified by the function
|
||||
```
|
||||
|
||||
Let's note that an outer variable is only used if there's no local with the same name.
|
||||
Sometimes that happens when we forget `let`. Because the outer variable is only used if there's no local one.
|
||||
|
||||
For example, if we had `let` before `userName` in the line (1) then the function would have a local variable `userName` and use it instead of the outer one. This process is called "shadowing".
|
||||
For example, if we had `let` before `userName` in the line (1) then the function would have a local variable `userName` and use it instead. This process is called "shadowing".
|
||||
|
||||
In the code below the local `userName` shadows the outer one:
|
||||
In the code below the local `userName` *shadows* the outer one:
|
||||
|
||||
```js run
|
||||
let userName = 'John';
|
||||
|
@ -119,22 +117,22 @@ function showMessage() {
|
|||
let userName = "Bob"; // declare a local variable
|
||||
*/!*
|
||||
|
||||
let message = 'Hello, my name is ' + userName;
|
||||
let message = 'Hello, ' + userName; // *!*Bob*/!*
|
||||
alert(message);
|
||||
}
|
||||
|
||||
// the function will create and use it's own userName
|
||||
showMessage();
|
||||
|
||||
alert( userName ); // John, the function did not access the outer variable
|
||||
alert( userName ); // *!*John*/!*, unchanged, the function did not access the outer variable
|
||||
```
|
||||
|
||||
```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.
|
||||
Global variables are visible from any function (unless shadowed by locals).
|
||||
|
||||
They should only be used if the data is so important that it really must be seen from anywhere.
|
||||
Usually, a function declares all variables specific to its task, and global variables only store the data so important that it really must be seen from anywhere. Modern code has little to no globals, most variables reside in their functions.
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
|
|
@ -24,9 +24,9 @@ The latter syntax is called a "Function Expression".
|
|||
|
||||
The meaning of these code samples is the same: "create a function and put it into the variable `sayHi`".
|
||||
|
||||
Yes, no matter, how the function is defined -- it's just a value, stored in the variable `sayHi`.
|
||||
Let's stress: a function is not a "magical language structure", but a kind of value. Both syntaxes mean the same: create a special "function" value and put it into the variable.
|
||||
|
||||
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.
|
||||
No matter, how the function is defined -- it's just a value, stored in the variable `sayHi`.
|
||||
|
||||
We can even print out that value using `alert`:
|
||||
|
||||
|
@ -40,9 +40,7 @@ alert( sayHi ); // shows the function code
|
|||
*/!*
|
||||
```
|
||||
|
||||
Note that there are no brackets after `sayHi` in the last line.
|
||||
|
||||
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. The code above shows its string representation, that is its source code.
|
||||
Note that there are no brackets after `sayHi` in the last line, because we do not intend to run the function. There are programming languages where any mention of a function name causes it's call, but JavaScript is not like that. In JavaScript, a function is a value and we can deal with that as a value. The code above shows its string representation, that is its source code.
|
||||
|
||||
It is a special value of course, in the sense that we can call it using brackets: `"sayHi()"`.
|
||||
|
||||
|
@ -61,27 +59,54 @@ func(); // Hello // (3) call the copy (it works)!
|
|||
sayHi(); // Hello // this works too (why wouldn't it)
|
||||
```
|
||||
|
||||
In more detail:
|
||||
1. Function Declaration `(1)` creates the function and puts it into the variable `sayHi`"
|
||||
That's what happens above in detail:
|
||||
|
||||
1. Function Declaration `(1)` creates the function and puts it into the variable named `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.
|
||||
3. Now the function can be called both as `sayHi()` and `func()`.
|
||||
|
||||
Note, that we could also have used a Function Expression to declare `sayHi`, in the first line: `let sayHi = function() { ... }`. Everything would work the same.
|
||||
Note, that we could also have used a Function Expression to declare `sayHi`, in the first line:
|
||||
|
||||
```js
|
||||
let sayHi = function() { ... }
|
||||
|
||||
let func = sayHi;
|
||||
// ...
|
||||
```
|
||||
|
||||
Everything would work the same. Even more obvious what's going on, right?
|
||||
|
||||
## Function is an object
|
||||
|
||||
Every value in Javascript has the type. What type of value is a function?
|
||||
|
||||
**In Javascript, a function is an object.**
|
||||
In Javascript, a function is an object.
|
||||
|
||||
We can abuse this by adding properties to it and using them in the code:
|
||||
For example, all functions have property `name` (function name) and `length` (number of arguments):
|
||||
|
||||
```js run
|
||||
function sayHi() {
|
||||
alert("Hi");
|
||||
sayHi.counter++;
|
||||
}
|
||||
sayHi.counter = 0;
|
||||
|
||||
alert( sayHi.name ); // sayHi
|
||||
alert( sayHi.length ); // 0
|
||||
```
|
||||
|
||||
We can add our own properties to it as well:
|
||||
|
||||
```js run
|
||||
function sayHi() {
|
||||
alert("Hi");
|
||||
|
||||
*!*
|
||||
// let's count how many times we run
|
||||
sayHi.counter++;
|
||||
*/!*
|
||||
}
|
||||
sayHi.counter = 0; // initial value
|
||||
|
||||
sayHi(); // Hi
|
||||
sayHi(); // Hi
|
||||
|
@ -89,8 +114,18 @@ sayHi(); // Hi
|
|||
alert( `Called ${sayHi.counter} times` ); // Called 2 times
|
||||
```
|
||||
|
||||
There are many well-known Javascript libraries that make use of this. They create a function and attach many other functions to it as its properties. For instance, the [jquery](https://jquery.com) library creates a function named `$`, the library [lodash](https://lodash.com) creates a function `_`. So, we have something that can do the job by itself and also carries a bunch of other functionality.
|
||||
|
||||
```warn header="A property is not a variable"
|
||||
A property assigned to a function like `sayHi.counter = 0` does *not* define a local variable `counter` inside it. In other words, a property `sayHi.counter` and `let counter` inside the function (if we have it) are two unrelated things.
|
||||
|
||||
We can treat a function as an object for convenience, store properties in it, that has no effect on its execution.
|
||||
```
|
||||
|
||||
There are many well-known Javascript libraries that make a great use of custom function properties.
|
||||
|
||||
They create a "main" function and attach many other "helper" functions to it. For instance, the [jquery](https://jquery.com) library creates a function named `$`. The [lodash](https://lodash.com) library creates a function `_`. And then adds `_.clone`, `_.keyBy` and other properties to (see the [docs](https://lodash.com/docs) when you want learn more about them).
|
||||
|
||||
So, a function can do a useful job by itself and also carry a bunch of other functionality in properties.
|
||||
|
||||
```smart header="A function is a value representing an \"action\""
|
||||
Regular values like strings or numbers represent the *data*.
|
||||
|
@ -103,13 +138,11 @@ We can copy it between variables and run when we want. We can even add propertie
|
|||
|
||||
## Function Expression as a method
|
||||
|
||||
Now let's go back: we have two ways of declaring a function. Do we really need both? What's about Function Expressions that makes it a good addition?
|
||||
Now let's step back and reconsider. We have two ways of declaring a function. Do we really need both? What's so good about Function Expressions that makes it useful?
|
||||
|
||||
Actually, yes. We'll see many examples below, but let's start with objects.
|
||||
Actually, yes, we do. For example, we can assign functions to object properties using function expressions.
|
||||
|
||||
As we remember from the chapter <info:types>, objects are data structures meant to store collections of data.
|
||||
|
||||
Most often, we create objects to represent entities of the real world, like here:
|
||||
As we remember from the chapter <info:types>, objects are data structures meant to store collections of data. Most often, we create objects to represent entities of the real world, like users, goodies and so on:
|
||||
|
||||
```js
|
||||
let user = {
|
||||
|
@ -118,7 +151,7 @@ let user = {
|
|||
};
|
||||
```
|
||||
|
||||
In the real world, a user can `act`, like: select something from the shopping cart, login, logout etc. For the start, let's teach him to say hello:
|
||||
In the real world, a user can `act`: to select something from the shopping cart, to login, to logout etc. For the start, let's teach him to say hello:
|
||||
|
||||
```js run
|
||||
let user = {
|
||||
|
@ -143,8 +176,6 @@ That is how a so-called "object-oriented code" is written. We make objects which
|
|||
|
||||
An object stores its data in regular properties (like `name`, `age` etc) and has functions to express itself. Function properties are usually called *methods*. So, one can say that in the code above "`sayHi` is a method of the object `user`".
|
||||
|
||||
|
||||
|
||||
Of course we could use a Function Declaration for the same purpose:
|
||||
|
||||
```js run
|
||||
|
@ -165,14 +196,18 @@ user.sayHi(); // Hello!
|
|||
|
||||
That would also work, but is longer. Also we get an "extra" function `sayHi` outside of the `user` object. Here we don't want it.
|
||||
|
||||
```smart header="Object-oriented programming"
|
||||
When we write our code using objects to represent entities, that's called an [object-oriented programming](https://en.wikipedia.org/wiki/Object-oriented_programming), in short: "OOP".
|
||||
|
||||
As of now, we already know how to create an object with `{...}` and how to store data and add a method to it. But we will study it in detail later when we get enough familarity with basic functions of the language.
|
||||
```
|
||||
|
||||
|
||||
## Function Expression vs Function Declaration
|
||||
|
||||
Let's formulate key differences between Function Declarations and Expressions.
|
||||
Let's formulate the key differences between Function Declarations and Expressions.
|
||||
|
||||
### What's where
|
||||
|
||||
Here's the exact distinction between these two.
|
||||
Here's the syntax distinction between these two.
|
||||
|
||||
- *Function Declaration:* a function, declared as a separate statement, in the main code flow.
|
||||
|
||||
|
@ -184,7 +219,7 @@ Here's the exact distinction between these two.
|
|||
```
|
||||
- *Function Expression:* a function, created in the context of an expression.
|
||||
|
||||
Here the function is created in the context of an "assignment expression":
|
||||
Here the function is created in the context of an "assignment expression =":
|
||||
```js
|
||||
// Function Expression
|
||||
let sum = function(a, b) {
|
||||
|
@ -192,23 +227,23 @@ Here's the exact distinction between these two.
|
|||
}
|
||||
```
|
||||
|
||||
### Creation time
|
||||
|
||||
Another difference is when they are actualy created by the JavaScript engine.
|
||||
|
||||
**Function Expressions are created when the execution reaches them.**
|
||||
**Function Expressions are created when the execution reaches them and are usable since then.**
|
||||
|
||||
That's kind of obvious. Once the execution flow passes to the right side of the assignment `let sum = function` -- here we go, the function is made and can be used (assigned, called etc) from now on.
|
||||
That's kind of obvious. Once the execution flow passes to the right side of the assignment `let sum = function` -- here we go, the function is created and can be used (assigned, called etc) from now on.
|
||||
|
||||
Function Declarations are different.
|
||||
|
||||
**Function Declarations are created before the script or a code block begins to execute and are visible in the whole script/block.**
|
||||
**Function Declarations are usable in the whole script/code 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".
|
||||
In other words, when JavaScript *prepares* to run the script/code block, it first looks for Function Declarations in it and creates the functions. We can think of it as an "initialization stage".
|
||||
|
||||
And after all Function Declarations are processed, it actually executes it.
|
||||
|
||||
As a natural effect, a function declared as Function Declaration can be called earlier than it is defined.
|
||||
|
||||
For instance, this works:
|
||||
For example, this works:
|
||||
|
||||
```js run refresh untrusted
|
||||
*!*
|
||||
|
@ -229,21 +264,22 @@ Function Declaration `sayHi` is created when JavaScript is preparing to start th
|
|||
sayHi("John"); // error!
|
||||
*/!*
|
||||
|
||||
let sayHi = function(name) { // (*)
|
||||
let sayHi = function(name) { // (*) no magic any more
|
||||
alert( `Hello, ${name}` );
|
||||
};
|
||||
```
|
||||
|
||||
Now there's no magic. Function Expressions are created when the execution reaches them. That would happen only in the line `(*)`. Too late.
|
||||
Function Expressions are created when the execution reaches them. That would happen only in the line `(*)`. Too late.
|
||||
|
||||
### Function Declaration in a block
|
||||
|
||||
### Visibility
|
||||
When Function Declaration is made within a code block, it is visible everywhere inside that block. But not outside of it.
|
||||
|
||||
Now let's explore the visibility differences.
|
||||
Sometimes that's handy to declare a local function, only needed in that only block. But can also be a problem.
|
||||
|
||||
Imagine, we need to declare `welcome()` depending on some data we get in run-time.
|
||||
For instance, let's imagine that we need to declare a function `welcome()` depending on the `age` variable that we get in run time. And then use it sometimes later.
|
||||
|
||||
For instance:
|
||||
The code below doesn't work:
|
||||
|
||||
```js run
|
||||
let age = prompt("What is your age?", 18);
|
||||
|
@ -267,11 +303,7 @@ welcome(); // Error: welcome is not defined
|
|||
*/!*
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
**A Function Declaration is visible only inside the code block where it resides.**
|
||||
The Function Declaration is visible only inside the code block where it resides.
|
||||
|
||||
We can call it from within the block, but not from outside:
|
||||
|
||||
|
@ -294,8 +326,8 @@ if (age < 18) {
|
|||
} else {
|
||||
// \
|
||||
function welcome() { // |
|
||||
alert("Greetings!"); // | as we don't enter this block,
|
||||
} // | this "welcome" is never created
|
||||
alert("Greetings!"); // | in this if test we don't enter this block,
|
||||
} // | so this "welcome" is never created
|
||||
// /
|
||||
}
|
||||
|
||||
|
@ -307,7 +339,9 @@ welcome(); // Error: welcome is not defined
|
|||
*/!*
|
||||
```
|
||||
|
||||
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:
|
||||
What can we do to make `welcome` visible outside?
|
||||
|
||||
The right thing would be to use a Function Expression and assign `welcome` to the variable which is declared outside of `if` and has the proper visibility:
|
||||
|
||||
```js run
|
||||
let age = prompt("What is your age?", 18);
|
||||
|
@ -346,27 +380,30 @@ welcome(); // ok now
|
|||
*/!*
|
||||
```
|
||||
|
||||
```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 code, they increase readability of the code.
|
||||
|
||||
But if a Function Declaration does not suit us for some reason, then a Function Expression should be used.
|
||||
```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 code, they increase readability of the code.
|
||||
|
||||
But if a Function Declaration does not fit for some reason (we've seen an example), then a Function Expression should be used.
|
||||
```
|
||||
|
||||
## Arrow functions basics [#arrow-functions]
|
||||
## Arrow functions [#arrow-functions]
|
||||
|
||||
Enough with the complexities for now. Let's relax with arrow functions that may add elegance to our code.
|
||||
Enough with the complexities for now. Let's relax with another syntax of functions that can make our code shorter.
|
||||
|
||||
They act like a function expression, but look a little bit differently.
|
||||
Arrow functions act like a function expression, but look a little bit differently.
|
||||
|
||||
The syntax:
|
||||
The syntax is:
|
||||
|
||||
```js
|
||||
let func = (arg1, arg2, ...argN) => expression
|
||||
```
|
||||
|
||||
...Creates a function that gets arguments `arg1..argN`, evaludates the `expression` on the right side with their use and returns its result.
|
||||
...This creates a function `func` that has arguments `arg1..argN`, evaludates the `expression` on the right side with their use and returns its result.
|
||||
|
||||
It is roughly the same as:
|
||||
In other words, it's roughly the same as:
|
||||
|
||||
```js
|
||||
let func = function(arg1, arg2, ...argN) {
|
||||
|
@ -382,25 +419,48 @@ let sum = (a, b) => a + b;
|
|||
alert( sum(1, 2) ); // 3
|
||||
```
|
||||
|
||||
For a single argument function, we can omit the brackets, making that even shorter:
|
||||
Here the function is same as:
|
||||
|
||||
```js
|
||||
let sum = function(a, b) {
|
||||
return a + b;
|
||||
}
|
||||
```
|
||||
|
||||
If we have only one argument, then brackets can be omitted, making that even shorter:
|
||||
|
||||
```js run
|
||||
// brackets () not needed around a single argument n
|
||||
// same as
|
||||
// let double = function(n) { return n*2 }
|
||||
let double = n => n*2;
|
||||
|
||||
alert( double(3) ); // 6
|
||||
```
|
||||
|
||||
As you can see, this extra-concise syntax is well-suited for one-line functions. Very handy to create a function in the middle of a more complex expresion.
|
||||
|
||||
Note that to the right side of `=>` must be a single valid expression.
|
||||
|
||||
If we need something more complex, like multiple statements, we can enclose it in figure brackets. Then use a normal `return` within them.
|
||||
|
||||
Like here:
|
||||
If there are no arguments, we can put empty brackets. For instance, here's the rewritten example with `welcome()`:
|
||||
|
||||
```js run
|
||||
let sum = (a, b) => {
|
||||
let age = prompt("What is your age?", 18);
|
||||
|
||||
let welcome = (age < 18) ? () => alert('Hello') : () => alert("Greetings!");
|
||||
|
||||
welcome(); // ok now
|
||||
```
|
||||
|
||||
The syntax may appear unfamiliar and not very readable at first, but that quickly changes as the eyes get used to the structure.
|
||||
|
||||
Arrow functions are very convenient for simple one-line actions, when we're just lazy to write many words.
|
||||
|
||||
```smart header="Multiline arrow functions"
|
||||
|
||||
The examples above took arguments from the left of `=>` and evaluate the right-side expression with them.
|
||||
|
||||
Sometimes we need something a little bit more complex, like multiple expressions or statements. It is also possible, but we should enclose them in figure brackets. Then use a normal `return` within them.
|
||||
|
||||
Like this:
|
||||
|
||||
```js run
|
||||
let sum = (a, b) => { // the figure bracket opens a multiline function
|
||||
let result = a + b;
|
||||
*!*
|
||||
return result; // if we use figure brackets, must use return
|
||||
|
@ -411,9 +471,9 @@ alert( sum(1, 2) ); // 3
|
|||
```
|
||||
|
||||
```smart header="More to come"
|
||||
Arrow functions have other interesting features in them. We'll get to them later.
|
||||
Here we praised arrow functions for shortness. But that's not all! Arrow functions have other interesting features in them. We'll return to them later and see where else they shine.
|
||||
|
||||
But as for now, we can already use them for one-line actions.
|
||||
As for now, we can already use them for one-line actions.
|
||||
```
|
||||
|
||||
## new Function
|
||||
|
@ -436,31 +496,24 @@ let func = new Function('', str);
|
|||
func();
|
||||
```
|
||||
|
||||
It is used in very specific cases, like when we receive a code from the server as a string, or to dynamically compile a function from a template string. The need for such uses arises at more advanced stages of the development.
|
||||
It is used in very specific cases, like when we receive the code from the server, or to dynamically compile a function from a template. The need for such uses arises at advanced stages of the development.
|
||||
|
||||
|
||||
## Summary
|
||||
|
||||
|
||||
|
||||
- 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 declared as a separate statement, in the main code flow -- that'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 Declarations are processed before the code block is executed. They are visible 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()`.
|
||||
|
||||
But in most cases Function Declaration is preferable.
|
||||
|
||||
Compare, which code is more readable:
|
||||
If we simple want to create a function, then in most cases Function Declaration is preferable. Novice programmers sometimes overuse Function Expression by creating many functions with `let func = function()`, but compare, which code is more readable:
|
||||
|
||||
```js no-beautify
|
||||
// Function Expression
|
||||
let f = function() { ... }
|
||||
let f = function() { /* expression */ }
|
||||
|
||||
// Function Declaration
|
||||
function f() { ... }
|
||||
function f() { /* declaration */ }
|
||||
```
|
||||
|
||||
Function Declaration is shorter and more obvious. The additional bonus -- it can be called before the actual declaration.
|
||||
|
@ -469,10 +522,10 @@ Function Declaration is shorter and more obvious. The additional bonus -- it can
|
|||
|
||||
We also touched two other ways to create a function:
|
||||
|
||||
- Arrow functions: `(...args) => expr` or `{ ... }`
|
||||
|
||||
They provide a short way to create a function that evaluates the given `expr`. More to come about them.
|
||||
- Arrow functions are handy for one-liners. The come in two flavours:
|
||||
1. Without figure brackets: `(...args) => expression` -- returns the evaluated `expression.
|
||||
2. With brackets: `(...args) => { body }` -- need an explicit `return` statement to return something, but can be more complex.
|
||||
|
||||
- `new Function(args, body)`
|
||||
|
||||
This syntax allows to create a function from a string, that may be composed dynamically during the execution, from the incoming data.
|
||||
This syntax allows to create a function from a string, that may be composed dynamically during the execution.
|
||||
|
|
|
@ -1,22 +1,19 @@
|
|||
# Primitives as objects
|
||||
# Methods of primitives
|
||||
|
||||
As we know from the chapter <info:types>, there are 7 data types in JavaScript:
|
||||
In this section we'll see how JavaScript allows to work with primitives (strings, numbers etc) as if they were objects. They have methods and such.
|
||||
|
||||
- Six primitive types: `string`, `number`, `boolean`, `symbol`, `null` and `undefined`.
|
||||
- One `object` type for anything else.
|
||||
|
||||
In this section we'll see how JavaScript allows to work with primitives as if they were objects. This ability may even lead to opinions like 'everything in JavaScript behaves as an object'. Of course in fact it's not so. And here we plan to make it clear.
|
||||
Actually, most things in Javascript behave as objects. Of course primitives are not objects (and here we plan to make it even more clear), but can be used like them.
|
||||
|
||||
Let's formulate the definitive distinction between primitives and objects.
|
||||
|
||||
A primitive
|
||||
: Is a single value. Like a number `123`. Or a string `"Hello"`.
|
||||
: Is a value of a primitive type. There are 6 primitive types: `string`, `number`, `boolean`, `symbol`, `null` and `undefined`.
|
||||
|
||||
An object
|
||||
: Is capable of storing multiple values as properties.
|
||||
An empty object: `{}`. An object with two properties: `{name: "John", age: 30}`. There are other kinds of objects in JavaScript too, we'll study them later.
|
||||
Can be created with `{}`, for instance: `{name: "John", age: 30}`. There are other kinds of objects in JavaScript, e.g. functions are objects.
|
||||
|
||||
One of the best thing about objects is that they are not just a "collection of values". We can store a function as one of properties:
|
||||
One of the best thing about objects is that we can store a function as one of properties:
|
||||
|
||||
```js run
|
||||
let john = {
|
||||
|
@ -29,15 +26,17 @@ let john = {
|
|||
john.sayHi(); // Hi buddy!
|
||||
```
|
||||
|
||||
That's very convenient in practice.
|
||||
So, here we've made an object `john` with the method `sayHi`.
|
||||
|
||||
There are many kinds of objects, including those that work with dates, errors, HTML elements etc. They have different properties and methods.
|
||||
There exist many built-in objects, including those that work with dates, errors, HTML elements etc. They have different properties and methods.
|
||||
|
||||
But features come at a price. Objects are "heavier" than primitives. They require additional resources for the machinery. But accessing properties and methods looks good. We can easily create methods of our own.
|
||||
But features come at a price!
|
||||
|
||||
Objects are "heavier" than primitives. They require additional resources to support the internal property-managing machinery. But properties and methods are useful in programming, and engines try to optimize that, so the price is usually fair.
|
||||
|
||||
## A primitive as an object
|
||||
|
||||
So here's the paradox faced by the creator of JavaScript:
|
||||
Here's the paradox faced by the creator of JavaScript:
|
||||
|
||||
- There are many things one would want to do with a string or a number, could be great to access them as methods.
|
||||
- Primitives must store a single value, be as fast and lightweight as possible.
|
||||
|
@ -50,7 +49,7 @@ The solution looks a little bit awkward, but here it is.
|
|||
|
||||
The "object wrappers" are different for each primitive type and are named specifically: `String`, `Number`, `Boolean` and `Symbol`. Thus they provide different sets of methods.
|
||||
|
||||
For instance, let's consider the method [str.toUpperCase()](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/String/toUpperCase) that returns the capitalized string.
|
||||
For instance, there exists a method [str.toUpperCase()](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/String/toUpperCase) that returns the capitalized string.
|
||||
|
||||
Here's how it works:
|
||||
|
||||
|
@ -62,9 +61,9 @@ alert( str.toUpperCase() ); // HELLO
|
|||
|
||||
Simple, right? And here's what actually happens in `str.toUpperCase()`:
|
||||
|
||||
1. The string `str` is a primitive. So in the moment of accessing it's property a special object is created that both knows the value of the string and has useful methods, like `toUpperCase()`.
|
||||
1. The string `str` is a primitive. So in the moment of accessing its property a special object is created that both knows the value of the string and has useful methods, like `toUpperCase()`.
|
||||
2. That method runs and returns a new string (shown by `alert`).
|
||||
3. The special object disappears, leaving the primitive `str` alone.
|
||||
3. The special object is destroyed, leaving the primitive `str` alone.
|
||||
|
||||
So, primitives can provide methods, but they still remain lightweight.
|
||||
|
||||
|
@ -78,10 +77,7 @@ let n = 1.23456;
|
|||
alert( n.toFixed(2) ); // 1.23
|
||||
```
|
||||
|
||||
We'll see more into specific methods in next chapters.
|
||||
|
||||
Now please ensure that you really grasped that by solving the first task in this chapter (below).
|
||||
|
||||
We'll see more specific methods in next chapters.
|
||||
|
||||
````warn header="null/undefined have no methods"
|
||||
Special types `null` and `undefined` are exceptions. They have no corresponding "wrapper objects" and provide no methods. In a sense, they are "the most primitive".
|
||||
|
@ -94,7 +90,6 @@ alert(null.test); // error
|
|||
|
||||
## Summary
|
||||
|
||||
- Primitives except `null` and `undefined` provide various methods. We plan to study those in the next sections.
|
||||
- Still, primitives are not objects. Formally, methods work via temporary objects that are created "just for the call". The JavaScript engines are tuned to optimize that internally, so it's not expensive at all.
|
||||
- Primitives except `null` and `undefined` provide various methods. We plan to study those in the next chapters.
|
||||
- Primitives are not objects, but still have methods. Formally, these methods work via temporary objects, but JavaScript engines are tuned to optimize that internally, so they are not expensive to call.
|
||||
|
||||
Further we'll get back to data structures and study methods in more detail.
|