Compare commits
1 commit
master
...
paroche-pa
Author | SHA1 | Date | |
---|---|---|---|
![]() |
7dd438a25c |
1
.gitignore
vendored
|
@ -21,4 +21,3 @@ sftp-config.json
|
|||
Thumbs.db
|
||||
|
||||
|
||||
/svgs
|
|
@ -1,10 +1,10 @@
|
|||
# An Introduction to JavaScript
|
||||
|
||||
Let's see what's so special about JavaScript, what we can achieve with it, and what other technologies play well with it.
|
||||
Let's see what's so special about JavaScript, what we can achieve with it, and which other technologies play well with it.
|
||||
|
||||
## What is JavaScript?
|
||||
|
||||
*JavaScript* was initially created to "make web pages alive".
|
||||
*JavaScript* was initially created to *"make web pages alive"*.
|
||||
|
||||
The programs in this language are called *scripts*. They can be written right in a web page's HTML and run automatically as the page loads.
|
||||
|
||||
|
@ -24,18 +24,18 @@ The browser has an embedded engine sometimes called a "JavaScript virtual machin
|
|||
|
||||
Different engines have different "codenames". For example:
|
||||
|
||||
- [V8](https://en.wikipedia.org/wiki/V8_(JavaScript_engine)) -- in Chrome, Opera and Edge.
|
||||
- [V8](https://en.wikipedia.org/wiki/V8_(JavaScript_engine)) -- in Chrome and Opera.
|
||||
- [SpiderMonkey](https://en.wikipedia.org/wiki/SpiderMonkey) -- in Firefox.
|
||||
- ...There are other codenames like "Chakra" for IE, "JavaScriptCore", "Nitro" and "SquirrelFish" for Safari, etc.
|
||||
- ...There are other codenames like "Trident" and "Chakra" for different versions of IE, "ChakraCore" for Microsoft Edge, "Nitro" and "SquirrelFish" for Safari, etc.
|
||||
|
||||
The terms above are good to remember because they are used in developer articles on the internet. We'll use them too. For instance, if "a feature X is supported by V8", then it probably works in Chrome, Opera and Edge.
|
||||
The terms above are good to remember because they are used in developer articles on the internet. We'll use them too. For instance, if "a feature X is supported by V8", then it probably works in Chrome and Opera.
|
||||
|
||||
```smart header="How do engines work?"
|
||||
|
||||
Engines are complicated. But the basics are easy.
|
||||
|
||||
1. The engine (embedded if it's a browser) reads ("parses") the script.
|
||||
2. Then it converts ("compiles") the script to machine code.
|
||||
2. Then it converts ("compiles") the script to the machine language.
|
||||
3. And then the machine code runs, pretty fast.
|
||||
|
||||
The engine applies optimizations at each step of the process. It even watches the compiled script as it runs, analyzes the data that flows through it, and further optimizes the machine code based on that knowledge.
|
||||
|
@ -43,7 +43,7 @@ The engine applies optimizations at each step of the process. It even watches th
|
|||
|
||||
## What can in-browser JavaScript do?
|
||||
|
||||
Modern JavaScript is a "safe" programming language. It does not provide low-level access to memory or the CPU, because it was initially created for browsers which do not require it.
|
||||
Modern JavaScript is a "safe" programming language. It does not provide low-level access to memory or CPU, because it was initially created for browsers which do not require it.
|
||||
|
||||
JavaScript's capabilities greatly depend on the environment it's running in. For instance, [Node.js](https://wikipedia.org/wiki/Node.js) supports functions that allow JavaScript to read/write arbitrary files, perform network requests, etc.
|
||||
|
||||
|
@ -59,25 +59,25 @@ For instance, in-browser JavaScript is able to:
|
|||
|
||||
## What CAN'T in-browser JavaScript do?
|
||||
|
||||
JavaScript's abilities in the browser are limited to protect the user's safety. The aim is to prevent an evil webpage from accessing private information or harming the user's data.
|
||||
JavaScript's abilities in the browser are limited for the sake of the user's safety. The aim is to prevent an evil webpage from accessing private information or harming the user's data.
|
||||
|
||||
Examples of such restrictions include:
|
||||
|
||||
- JavaScript on a webpage may not read/write arbitrary files on the hard disk, copy them or execute programs. It has no direct access to OS functions.
|
||||
- JavaScript on a webpage may not read/write arbitrary files on the hard disk, copy them or execute programs. It has no direct access to OS system functions.
|
||||
|
||||
Modern browsers allow it to work with files, but the access is limited and only provided if the user does certain actions, like "dropping" a file into a browser window or selecting it via an `<input>` tag.
|
||||
|
||||
There are ways to interact with the camera/microphone and other devices, but they require a user's explicit 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. But even in this case, JavaScript from one page may not access the other page if they come from different sites (from a different domain, protocol or port).
|
||||
There are ways to interact with camera/microphone and other devices, but they require a user's explicit 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. But even in this case, JavaScript from one page may not access the other if they come from different sites (from a different domain, protocol or port).
|
||||
|
||||
This is called the "Same Origin Policy". To work around that, *both pages* must agree for data exchange and must contain special JavaScript code that handles it. We'll cover that in the tutorial.
|
||||
This is called the "Same Origin Policy". To work around that, *both pages* must agree for data exchange and contain a special JavaScript code that handles it. We'll cover that in the tutorial.
|
||||
|
||||
This limitation is, again, for the user's safety. A page from `http://anysite.com` which a user has opened must not be able to access another browser tab with the URL `http://gmail.com`, for example, and steal information from there.
|
||||
This limitation is, again, for the user's safety. A page from `http://anysite.com` which a user has opened must not be able to access another browser tab with the URL `http://gmail.com` and steal information from there.
|
||||
- JavaScript can easily communicate over the net to the server where the current page came from. But its ability to receive data from other sites/domains is crippled. Though possible, it requires explicit agreement (expressed in HTTP headers) from the remote side. Once again, that's a safety limitation.
|
||||
|
||||

|
||||
|
||||
Such limitations do not exist if JavaScript is used outside of the browser, for example on a server. Modern browsers also allow plugins/extensions which may ask for extended permissions.
|
||||
Such limits do not exist if JavaScript is used outside of the browser, for example on a server. Modern browsers also allow plugin/extensions which may ask for extended permissions.
|
||||
|
||||
## What makes JavaScript unique?
|
||||
|
||||
|
@ -86,13 +86,13 @@ There are at least *three* great things about JavaScript:
|
|||
```compare
|
||||
+ Full integration with HTML/CSS.
|
||||
+ Simple things are done simply.
|
||||
+ Supported by all major browsers and enabled by default.
|
||||
+ Support by all major browsers and enabled by default.
|
||||
```
|
||||
JavaScript is the only browser technology that combines these three things.
|
||||
|
||||
That's what makes JavaScript unique. That's why it's the most widespread tool for creating browser interfaces.
|
||||
|
||||
That said, JavaScript can be used to create servers, mobile applications, etc.
|
||||
That said, JavaScript also allows to create servers, mobile applications, etc.
|
||||
|
||||
## Languages "over" JavaScript
|
||||
|
||||
|
@ -100,23 +100,21 @@ The syntax of JavaScript does not suit everyone's needs. Different people want d
|
|||
|
||||
That's to be expected, because projects and requirements are different for everyone.
|
||||
|
||||
So, recently a plethora of new languages appeared, which are *transpiled* (converted) to JavaScript before they run in the browser.
|
||||
So recently a plethora of new languages appeared, which are *transpiled* (converted) to JavaScript before they run in the browser.
|
||||
|
||||
Modern tools make the transpilation very fast and transparent, actually allowing developers to code in another language and auto-converting it "under the hood".
|
||||
|
||||
Examples of such languages:
|
||||
|
||||
- [CoffeeScript](https://coffeescript.org/) is "syntactic sugar" for JavaScript. It introduces shorter syntax, allowing us to write clearer and more precise code. Usually, Ruby devs like it.
|
||||
- [TypeScript](https://www.typescriptlang.org/) is concentrated on adding "strict data typing" to simplify the development and support of complex systems. It is developed by Microsoft.
|
||||
- [Flow](https://flow.org/) also adds data typing, but in a different way. Developed by Facebook.
|
||||
- [CoffeeScript](http://coffeescript.org/) is a "syntactic sugar" for JavaScript. It introduces shorter syntax, allowing us to write clearer and more precise code. Usually, Ruby devs like it.
|
||||
- [TypeScript](http://www.typescriptlang.org/) is concentrated on adding "strict data typing" to simplify the development and support of complex systems. It is developed by Microsoft.
|
||||
- [Flow](http://flow.org/) also adds data typing, but in a different way. Developed by Facebook.
|
||||
- [Dart](https://www.dartlang.org/) is a standalone language that has its own engine that runs in non-browser environments (like mobile apps), but also can be transpiled to JavaScript. Developed by Google.
|
||||
- [Brython](https://brython.info/) is a Python transpiler to JavaScript that enables the writing of applications in pure Python without JavaScript.
|
||||
- [Kotlin](https://kotlinlang.org/docs/reference/js-overview.html) is a modern, concise and safe programming language that can target the browser or Node.
|
||||
|
||||
There are more. Of course, even if we use one of these transpiled languages, we should also know JavaScript to really understand what we're doing.
|
||||
There are more. Of course, even if we use one of transpiled languages, we should also know JavaScript to really understand what we're doing.
|
||||
|
||||
## Summary
|
||||
|
||||
- JavaScript was initially created as a browser-only language, but it is now used in many other environments as well.
|
||||
- Today, JavaScript has a unique position as the most widely-adopted browser language, fully integrated with HTML/CSS.
|
||||
- JavaScript was initially created as a browser-only language, but is now used in many other environments as well.
|
||||
- Today, JavaScript has a unique position as the most widely-adopted browser language with full integration with HTML/CSS.
|
||||
- There are many languages that get "transpiled" to JavaScript and provide certain features. It is recommended to take a look at them, at least briefly, after mastering JavaScript.
|
||||
|
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
@ -1,7 +1,7 @@
|
|||
|
||||
# Manuals and specifications
|
||||
|
||||
This book is a *tutorial*. It aims to help you gradually learn the language. But once you're familiar with the basics, you'll need other resources.
|
||||
This book is a *tutorial*. It aims to help you gradually learn the language. But once you're familiar with the basics, you'll need other sources.
|
||||
|
||||
## Specification
|
||||
|
||||
|
@ -9,19 +9,24 @@ This book is a *tutorial*. It aims to help you gradually learn the language. But
|
|||
|
||||
But being that formalized, it's difficult to understand at first. So if you need the most trustworthy source of information about the language details, the specification is the right place. But it's not for everyday use.
|
||||
|
||||
A new specification version is released every year. Between these releases, the latest specification draft is at <https://tc39.es/ecma262/>.
|
||||
A new specification version is released every year. In-between these releases, the latest specification draft is at <https://tc39.es/ecma262/>.
|
||||
|
||||
To read about new bleeding-edge features, including those that are "almost standard" (so-called "stage 3"), see proposals at <https://github.com/tc39/proposals>.
|
||||
|
||||
Also, if you're developing for the browser, then there are other specifications covered in the [second part](info:browser-environment) of the tutorial.
|
||||
Also, if you're in developing for the browser, then there are other specs covered in the [second part](info:browser-environment) of the tutorial.
|
||||
|
||||
## Manuals
|
||||
|
||||
- **MDN (Mozilla) JavaScript Reference** is the main manual with examples and other information. It's great to get in-depth information about individual language functions, methods etc.
|
||||
- **MDN (Mozilla) JavaScript Reference** is a manual with examples and other information. It's great to get in-depth information about individual language functions, methods etc.
|
||||
|
||||
You can find it at <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference>.
|
||||
One can find it at <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference>.
|
||||
|
||||
Although, it's often best to use an internet search instead. Just use "MDN [term]" in the query, e.g. <https://google.com/search?q=MDN+parseInt> to search for the `parseInt` function.
|
||||
Although, it's often best to use an internet search instead. Just use "MDN [term]" in the query, e.g. <https://google.com/search?q=MDN+parseInt> to search for `parseInt` function.
|
||||
|
||||
|
||||
- **MSDN** – Microsoft manual with a lot of information, including JavaScript (often referred to as JScript). If one needs something specific to Internet Explorer, better go there: <http://msdn.microsoft.com/>.
|
||||
|
||||
Also, we can use an internet search with phrases such as "RegExp MSDN" or "RegExp MSDN jscript".
|
||||
|
||||
## Compatibility tables
|
||||
|
||||
|
@ -29,9 +34,9 @@ JavaScript is a developing language, new features get added regularly.
|
|||
|
||||
To see their support among browser-based and other engines, see:
|
||||
|
||||
- <https://caniuse.com> - per-feature tables of support, e.g. to see which engines support modern cryptography functions: <https://caniuse.com/#feat=cryptography>.
|
||||
- <http://caniuse.com> - per-feature tables of support, e.g. to see which engines support modern cryptography functions: <http://caniuse.com/#feat=cryptography>.
|
||||
- <https://kangax.github.io/compat-table> - a table with language features and engines that support those or don't support.
|
||||
|
||||
All these resources are useful in real-life development, as they contain valuable information about language details, their support, etc.
|
||||
All these resources are useful in real-life development, as they contain valuable information about language details, their support etc.
|
||||
|
||||
Please remember them (or this page) for the cases when you need in-depth information about a particular feature.
|
||||
|
|
|
@ -13,7 +13,7 @@ An IDE loads the project (which can be many files), allows navigation between fi
|
|||
If you haven't selected an IDE yet, consider the following options:
|
||||
|
||||
- [Visual Studio Code](https://code.visualstudio.com/) (cross-platform, free).
|
||||
- [WebStorm](https://www.jetbrains.com/webstorm/) (cross-platform, paid).
|
||||
- [WebStorm](http://www.jetbrains.com/webstorm/) (cross-platform, paid).
|
||||
|
||||
For Windows, there's also "Visual Studio", not to be confused with "Visual Studio Code". "Visual Studio" is a paid and mighty Windows-only editor, well-suited for the .NET platform. It's also good at JavaScript. There's also a free version [Visual Studio Community](https://www.visualstudio.com/vs/community/).
|
||||
|
||||
|
@ -31,6 +31,8 @@ In practice, lightweight editors may have a lot of plugins including directory-l
|
|||
|
||||
The following options deserve your attention:
|
||||
|
||||
- [Atom](https://atom.io/) (cross-platform, free).
|
||||
- [Visual Studio Code](https://code.visualstudio.com/) (cross-platform, free).
|
||||
- [Sublime Text](http://www.sublimetext.com) (cross-platform, shareware).
|
||||
- [Notepad++](https://notepad-plus-plus.org/) (Windows, free).
|
||||
- [Vim](http://www.vim.org/) and [Emacs](https://www.gnu.org/software/emacs/) are also cool if you know how to use them.
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<body>
|
||||
|
||||
<script>
|
||||
alert( "I'm JavaScript!" );
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,2 +0,0 @@
|
|||
|
||||
[html src="index.html"]
|
|
@ -9,7 +9,7 @@ So first, let's see how we attach a script to a webpage. For server-side environ
|
|||
|
||||
## The "script" tag
|
||||
|
||||
JavaScript programs can be inserted almost anywhere into an HTML document using the `<script>` tag.
|
||||
JavaScript programs can be inserted into any part of an HTML document with the help of the `<script>` tag.
|
||||
|
||||
For instance:
|
||||
|
||||
|
@ -46,7 +46,7 @@ The `<script>` tag contains JavaScript code which is automatically executed when
|
|||
The `<script>` tag has a few attributes that are rarely used nowadays but can still be found in old code:
|
||||
|
||||
The `type` attribute: <code><script <u>type</u>=...></code>
|
||||
: The old HTML standard, HTML4, required a script to have a `type`. Usually it was `type="text/javascript"`. It's not required anymore. Also, the modern HTML standard totally changed the meaning of this attribute. Now, it can be used for JavaScript modules. But that's an advanced topic, we'll talk about modules in another part of the tutorial.
|
||||
: The old HTML standard, HTML4, required a script to have a `type`. Usually it was `type="text/javascript"`. It's not required anymore. Also, the modern HTML standard totally changed the meaning of this attribute. Now, it can be used for JavaScript modules. But that's an advanced topic; we'll talk about modules in another part of the tutorial.
|
||||
|
||||
The `language` attribute: <code><script <u>language</u>=...></code>
|
||||
: This attribute was meant to show the language of the script. This attribute no longer makes sense because JavaScript is the default language. There is no need to use it.
|
||||
|
@ -73,12 +73,12 @@ Script files are attached to HTML with the `src` attribute:
|
|||
<script src="/path/to/script.js"></script>
|
||||
```
|
||||
|
||||
Here, `/path/to/script.js` is an absolute path to the script from the site root. One can also provide a relative path from the current page. For instance, `src="script.js"`, just like `src="./script.js"`, would mean a file `"script.js"` in the current folder.
|
||||
Here, `/path/to/script.js` is an absolute path to the script from the site root. One can also provide a relative path from the current page. For instance, `src="script.js"` would mean a file `"script.js"` in the current folder.
|
||||
|
||||
We can give a full URL as well. For instance:
|
||||
|
||||
```html
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.2.0/lodash.js"></script>
|
||||
```
|
||||
|
||||
To attach several scripts, use multiple tags:
|
||||
|
|
|
@ -46,7 +46,7 @@ alert(3 +
|
|||
+ 2);
|
||||
```
|
||||
|
||||
The code outputs `6` because JavaScript does not insert semicolons here. It is intuitively obvious that if the line ends with a plus `"+"`, then it is an "incomplete expression", so a semicolon there would be incorrect. And in this case, that works as intended.
|
||||
The code outputs `6` because JavaScript does not insert semicolons here. It is intuitively obvious that if the line ends with a plus `"+"`, then it is an "incomplete expression", so the semicolon is not required. And in this case that works as intended.
|
||||
|
||||
**But there are situations where JavaScript "fails" to assume a semicolon where it is really needed.**
|
||||
|
||||
|
@ -56,41 +56,45 @@ Errors which occur in such cases are quite hard to find and fix.
|
|||
If you're curious to see a concrete example of such an error, check this code out:
|
||||
|
||||
```js run
|
||||
alert("Hello");
|
||||
|
||||
[1, 2].forEach(alert);
|
||||
[1, 2].forEach(alert)
|
||||
```
|
||||
|
||||
No need to think about the meaning of the brackets `[]` and `forEach` yet. We'll study them later. For now, just remember the result of running the code: it shows `Hello`, then `1`, then `2`.
|
||||
No need to think about the meaning of the brackets `[]` and `forEach` yet. We'll study them later. For now, just remember the result of the code: it shows `1` then `2`.
|
||||
|
||||
Now let's remove the semicolon after the `alert`:
|
||||
Now, let's add an `alert` before the code and *not* finish it with a semicolon:
|
||||
|
||||
```js run no-beautify
|
||||
alert("Hello")
|
||||
alert("There will be an error")
|
||||
|
||||
[1, 2].forEach(alert);
|
||||
[1, 2].forEach(alert)
|
||||
```
|
||||
|
||||
The difference compared to the code above is only one character: the semicolon at the end of the first line is gone.
|
||||
Now if we run the code, only the first `alert` is shown and then we have an error!
|
||||
|
||||
If we run this code, only the first `Hello` shows (and there's an error, you may need to open the console to see it). There are no numbers any more.
|
||||
But everything is fine again if we add a semicolon after `alert`:
|
||||
```js run
|
||||
alert("All fine now");
|
||||
|
||||
That's because JavaScript does not assume a semicolon before square brackets `[...]`. So, the code in the last example is treated as a single statement.
|
||||
[1, 2].forEach(alert)
|
||||
```
|
||||
|
||||
Here's how the engine sees it:
|
||||
Now we have the "All fine now" message followed by `1` and `2`.
|
||||
|
||||
|
||||
The error in the no-semicolon variant occurs because JavaScript does not assume a semicolon before square brackets `[...]`.
|
||||
|
||||
So, because the semicolon is not auto-inserted, the code in the first example is treated as a single statement. Here's how the engine sees it:
|
||||
|
||||
```js run no-beautify
|
||||
alert("Hello")[1, 2].forEach(alert);
|
||||
alert("There will be an error")[1, 2].forEach(alert)
|
||||
```
|
||||
|
||||
Looks weird, right? Such merging in this case is just wrong. We need to put a semicolon after `alert` for the code to work correctly.
|
||||
|
||||
This can happen in other situations also.
|
||||
But it should be two separate statements, not one. Such a merging in this case is just wrong, hence the error. This can happen in other situations.
|
||||
````
|
||||
|
||||
We recommend putting semicolons between statements even if they are separated by newlines. This rule is widely adopted by the community. Let's note once again -- *it is possible* to leave out semicolons most of the time. But it's safer -- especially for a beginner -- to use them.
|
||||
|
||||
## Comments [#code-comments]
|
||||
## Comments
|
||||
|
||||
As time goes on, programs become more and more complex. It becomes necessary to add *comments* which describe what the code does and why.
|
||||
|
||||
|
@ -132,7 +136,7 @@ alert('World');
|
|||
```
|
||||
|
||||
```smart header="Use hotkeys!"
|
||||
In most editors, a line of code can be commented out by pressing the `key:Ctrl+/` hotkey for a single-line comment and something like `key:Ctrl+Shift+/` -- for multiline comments (select a piece of code and press the hotkey). For Mac, try `key:Cmd` instead of `key:Ctrl` and `key:Option` instead of `key:Shift`.
|
||||
In most editors, a line of code can be commented out by pressing the `key:Ctrl+/` hotkey for a single-line comment and something like `key:Ctrl+Shift+/` -- for multiline comments (select a piece of code and press the hotkey). For Mac, try `key:Cmd` instead of `key:Ctrl`.
|
||||
```
|
||||
|
||||
````warn header="Nested comments are not supported!"
|
||||
|
|
|
@ -19,7 +19,8 @@ For example:
|
|||
...
|
||||
```
|
||||
|
||||
Quite soon we're going to learn functions (a way to group commands), so let's note in advance that `"use strict"` can be put at the beginning of a function. Doing that enables strict mode in that function only. But usually people use it for the whole script.
|
||||
We will learn functions (a way to group commands) soon. Looking ahead, let's note that `"use strict"` can be put at the beginning of the function body instead of the whole script. Doing that enables strict mode in that function only. But usually, people use it for the whole script.
|
||||
|
||||
|
||||
````warn header="Ensure that \"use strict\" is at the top"
|
||||
Please make sure that `"use strict"` is at the top of your scripts, otherwise strict mode may not be enabled.
|
||||
|
@ -46,13 +47,11 @@ Once we enter strict mode, there's no going back.
|
|||
|
||||
## Browser console
|
||||
|
||||
When you use a [developer console](info:devtools) to run code, please note that it doesn't `use strict` by default.
|
||||
For the future, when you use a browser console to test features, please note that it doesn't `use strict` by default.
|
||||
|
||||
Sometimes, when `use strict` makes a difference, you'll get incorrect results.
|
||||
|
||||
So, how to actually `use strict` in the console?
|
||||
|
||||
First, you can try to press `key:Shift+Enter` to input multiple lines, and put `use strict` on top, like this:
|
||||
You can try to press `key:Shift+Enter` to input multiple lines, and put `use strict` on top, like this:
|
||||
|
||||
```js
|
||||
'use strict'; <Shift+Enter for a newline>
|
||||
|
@ -62,28 +61,25 @@ First, you can try to press `key:Shift+Enter` to input multiple lines, and put `
|
|||
|
||||
It works in most browsers, namely Firefox and Chrome.
|
||||
|
||||
If it doesn't, e.g. in an old browser, there's an ugly, but reliable way to ensure `use strict`. Put it inside this kind of wrapper:
|
||||
If it doesn't, the most reliable way to ensure `use strict` would be to input the code into console like this:
|
||||
|
||||
```js
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
// ...your code here...
|
||||
// ...your code...
|
||||
})()
|
||||
```
|
||||
|
||||
## Should we "use strict"?
|
||||
## Always "use strict"
|
||||
|
||||
The question may sound obvious, but it's not so.
|
||||
We have yet to cover the differences between strict mode and the "default" mode.
|
||||
|
||||
One could recommend to start scripts with `"use strict"`... But you know what's cool?
|
||||
In the next chapters, as we learn language features, we'll note the differences between the strict and default modes. Luckily, there aren't many and they actually make our lives better.
|
||||
|
||||
Modern JavaScript supports "classes" and "modules" - advanced language structures (we'll surely get to them), that enable `use strict` automatically. So we don't need to add the `"use strict"` directive, if we use them.
|
||||
For now, it's enough to know about it in general:
|
||||
|
||||
**So, for now `"use strict";` is a welcome guest at the top of your scripts. Later, when your code is all in classes and modules, you may omit it.**
|
||||
|
||||
As of now, we've got to know about `use strict` in general.
|
||||
|
||||
In the next chapters, as we learn language features, we'll see the differences between the strict and old modes. Luckily, there aren't many and they actually make our lives better.
|
||||
|
||||
All examples in this tutorial assume strict mode unless (very rarely) specified otherwise.
|
||||
1. The `"use strict"` directive switches the engine to the "modern" mode, changing the behavior of some built-in features. We'll see the details later in the tutorial.
|
||||
2. Strict mode is enabled by placing `"use strict"` at the top of a script or function. Several language features, like "classes" and "modules", enable strict mode automatically.
|
||||
3. Strict mode is supported by all modern browsers.
|
||||
4. We recommended always starting scripts with `"use strict"`. All examples in this tutorial assume strict mode unless (very rarely) specified otherwise.
|
||||
|
|
|
@ -6,7 +6,7 @@ That's simple:
|
|||
let ourPlanetName = "Earth";
|
||||
```
|
||||
|
||||
Note, we could use a shorter name `planet`, but it might not be obvious what planet it refers to. It's nice to be more verbose. At least until the variable isNotTooLong.
|
||||
Note, we could use a shorter name `planet`, but it might be not obvious what planet it refers to. It's nice to be more verbose. At least until the variable isNotTooLong.
|
||||
|
||||
## The name of the current visitor
|
||||
|
||||
|
|
|
@ -12,14 +12,13 @@ const birthday = '18.04.1982';
|
|||
const age = someCode(birthday);
|
||||
```
|
||||
|
||||
Here we have a constant `birthday` for the date, and also the `age` constant.
|
||||
|
||||
The `age` is calculated from `birthday` using `someCode()`, which means a function call that we didn't explain yet (we will soon!), but the details don't matter here, the point is that `age` is calculated somehow based on the `birthday`.
|
||||
Here we have a constant `birthday` date and the `age` is calculated from `birthday` with the help of some code (it is not provided for shortness, and because details don't matter here).
|
||||
|
||||
Would it be right to use upper case for `birthday`? For `age`? Or even for both?
|
||||
|
||||
```js
|
||||
const BIRTHDAY = '18.04.1982'; // make birthday uppercase?
|
||||
const BIRTHDAY = '18.04.1982'; // make uppercase?
|
||||
|
||||
const AGE = someCode(BIRTHDAY); // make age uppercase?
|
||||
const AGE = someCode(BIRTHDAY); // make uppercase?
|
||||
```
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ Now, we can put some data into it by using the assignment operator `=`:
|
|||
let message;
|
||||
|
||||
*!*
|
||||
message = 'Hello'; // store the string 'Hello' in the variable named message
|
||||
message = 'Hello'; // store the string
|
||||
*/!*
|
||||
```
|
||||
|
||||
|
@ -64,7 +64,6 @@ let message = 'Hello';
|
|||
```
|
||||
|
||||
Some people also define multiple variables in this multiline style:
|
||||
|
||||
```js no-beautify
|
||||
let user = 'John',
|
||||
age = 25,
|
||||
|
@ -81,6 +80,7 @@ let user = 'John'
|
|||
|
||||
Technically, all these variants do the same thing. So, it's a matter of personal taste and aesthetics.
|
||||
|
||||
|
||||
````smart header="`var` instead of `let`"
|
||||
In older scripts, you may also find another keyword: `var` instead of `let`:
|
||||
|
||||
|
@ -104,7 +104,6 @@ For instance, the variable `message` can be imagined as a box labeled `"message"
|
|||
We can put any value in the box.
|
||||
|
||||
We can also change it as many times as we want:
|
||||
|
||||
```js run
|
||||
let message;
|
||||
|
||||
|
@ -136,20 +135,6 @@ alert(hello); // Hello world!
|
|||
alert(message); // Hello world!
|
||||
```
|
||||
|
||||
````warn header="Declaring twice triggers an error"
|
||||
A variable should be declared only once.
|
||||
|
||||
A repeated declaration of the same variable is an error:
|
||||
|
||||
```js run
|
||||
let message = "This";
|
||||
|
||||
// repeated 'let' leads to an error
|
||||
let message = "That"; // SyntaxError: 'message' has already been declared
|
||||
```
|
||||
So, we should declare a variable once and then refer to it without `let`.
|
||||
````
|
||||
|
||||
```smart header="Functional languages"
|
||||
It's interesting to note that there exist [functional](https://en.wikipedia.org/wiki/Functional_programming) programming languages, like [Scala](http://www.scala-lang.org/) or [Erlang](http://www.erlang.org/) that forbid changing variable values.
|
||||
|
||||
|
@ -194,7 +179,7 @@ let my-name; // hyphens '-' aren't allowed in the name
|
|||
```
|
||||
|
||||
```smart header="Case matters"
|
||||
Variables named `apple` and `APPLE` are two different variables.
|
||||
Variables named `apple` and `AppLE` are two different variables.
|
||||
```
|
||||
|
||||
````smart header="Non-Latin letters are allowed, but not recommended"
|
||||
|
@ -205,7 +190,7 @@ let имя = '...';
|
|||
let 我 = '...';
|
||||
```
|
||||
|
||||
Technically, there is no error here. Such names are allowed, but there is an international convention to use English in variable names. Even if we're writing a small script, it may have a long life ahead. People from other countries may need to read it some time.
|
||||
Technically, there is no error here, such names are allowed, but there is an international tradition to use English in variable names. Even if we're writing a small script, it may have a long life ahead. People from other countries may need to read it some time.
|
||||
````
|
||||
|
||||
````warn header="Reserved names"
|
||||
|
@ -262,6 +247,7 @@ myBirthday = '01.01.2001'; // error, can't reassign the constant!
|
|||
|
||||
When a programmer is sure that a variable will never change, they can declare it with `const` to guarantee and clearly communicate that fact to everyone.
|
||||
|
||||
|
||||
### Uppercase constants
|
||||
|
||||
There is a widespread practice to use constants as aliases for difficult-to-remember values that are known prior to execution.
|
||||
|
@ -292,14 +278,13 @@ When should we use capitals for a constant and when should we name it normally?
|
|||
Being a "constant" just means that a variable's value never changes. But there are constants that are known prior to execution (like a hexadecimal value for red) and there are constants that are *calculated* in run-time, during the execution, but do not change after their initial assignment.
|
||||
|
||||
For instance:
|
||||
|
||||
```js
|
||||
const pageLoadTime = /* time taken by a webpage to load */;
|
||||
```
|
||||
|
||||
The value of `pageLoadTime` is not known prior to the page load, so it's named normally. But it's still a constant because it doesn't change after assignment.
|
||||
|
||||
In other words, capital-named constants are only used as aliases for "hard-coded" values.
|
||||
In other words, capital-named constants are only used as aliases for "hard-coded" values.
|
||||
|
||||
## Name things right
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="392" height="192" viewBox="0 0 392 192"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="combined" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="variable-change.svg"><g id="noun_1211_cc" transform="translate(52.585 -3.958)"><path id="Shape" fill="#DBAF88" d="M24.415 93.154h112.557v42.95c0 .373-.078.863-.278 1.295-.2.432-16.575 35.56-16.575 35.56v-54.36l16.855-25.445 18.44-28.196H42.856l-18.44 28.196zM24.415 121.958v-28l-15 28"/><g id="Rectangle-5-+-"World!"" transform="translate(0 56.354)"><path id="Rectangle-5" fill="#FBF2EC" stroke="#AF6E24" stroke-width="2" d="M18.276 1.413L71.416 58.4 54.553 74.124 1.413 17.138 18.276 1.413z"/><text id=""World!"" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold" transform="rotate(47 38.498 40.367)"><tspan x="9.006" y="46.867">"World!"</tspan></text></g><g id="Rectangle-5-+-"World!"-2" transform="rotate(-67 95.75 -34.63)"><path id="Rectangle-5" fill="#FBF2EC" stroke="#AF6E24" stroke-width="2" d="M18.276 1.413L71.416 58.4 54.553 74.124 1.413 17.138 18.276 1.413z"/><text id=""Hello!"" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold" transform="rotate(47 38.578 39.758)"><tspan x="9.085" y="46.258">"Hello!"</tspan></text></g><path id="Shape" fill="#DBAF88" d="M7.415 123.958v54.73c0 3.42 1.483 5.27 4.387 5.27h100.086c3.122 0 5.527-2.547 5.527-3.475v-56.525h-110z"/></g><text id="message" fill="#FFF" font-family="OpenSans-Bold, Open Sans" font-size="18" font-weight="bold"><tspan x="77" y="157">message</tspan></text><path id="Fill-54" fill="#DBAF88" d="M58.112 51.808S47.657 40.623 40.719 36.155l-.505 5.542a76.036 76.036 0 00-33.769 4.595l4.169 11.032a64.248 64.248 0 0128.531-3.882l-.505 5.542c5.581-3.329 19.472-7.176 19.472-7.176" transform="rotate(11 32.278 47.57)"/><path id="Fill-54" fill="#DBAF88" d="M287.797 28.186s-10.454-11.185-17.393-15.653l-.505 5.541a76.036 76.036 0 00-33.769 4.596l4.169 11.032a64.248 64.248 0 0128.531-3.882l-.504 5.541c5.58-3.328 19.47-7.175 19.47-7.175" transform="rotate(2 261.964 23.947)"/><g id="noun_48910_cc" transform="translate(298 5)"><path id="Shape" d="M50.983 6H36.016C35.456 6 35 6.626 35 7.395V12h17V7.395C52 6.626 51.543 6 50.983 6z"/><path id="Shape" fill="#DBAF88" d="M84.193 9.36h-26.39V6.085C57.803 2.729 54.99 0 51.528 0H36.47c-3.46 0-6.275 2.729-6.275 6.085V9.36H3.807C1.705 9.36 0 11.012 0 13.05v.26C0 15.348 1.705 17 3.807 17h80.386C86.295 17 88 15.348 88 13.31v-.26c0-2.038-1.706-3.69-3.807-3.69zM53 12H36V7.395C36 6.626 36.457 6 37.016 6h14.968C52.544 6 53 6.626 53 7.395V12zM74.955 20.045H8.044c-3.89 0-7.044-.68-7.044 3.266l5.282 78.382c0 3.943 3.155 7.307 7.045 7.307h56.347c3.89 0 7.044-3.364 7.044-7.307L82 23.31c-.001-3.947-3.155-3.266-7.045-3.266zM26.757 98.999c-1.283.039-2.353-.8-2.396-1.878l-2.36-61.095c-.041-1.078.964-1.985 2.242-2.025 1.283-.04 2.353.801 2.396 1.879l2.36 61.096c.041 1.076-.963 1.984-2.242 2.023zM43 97.049C43 98.126 42.328 99 41.5 99s-1.5-.876-1.5-1.951V35.95c0-1.078.672-1.951 1.5-1.951s1.5.873 1.5 1.951V97.05zm18.639.072c-.042 1.078-1.113 1.917-2.396 1.878-1.28-.04-2.283-.947-2.242-2.024l2.36-61.095c.042-1.078 1.112-1.919 2.394-1.879 1.28.042 2.285.947 2.244 2.025l-2.36 61.095z"/></g></g></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="392" height="192" viewBox="0 0 392 192"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="combined" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="variable-change.svg"><g id="noun_1211_cc" transform="translate(52 -5)"><path id="Shape" fill="#E8C48F" d="M25 94.196h112.558v42.95c0 .373-.079.862-.279 1.294-.2.433-16.574 35.56-16.574 35.56v-54.36l16.854-25.444L156 66H43.44L25 94.196zM25 123V95l-15 28"/><g id="Rectangle-5-+-"World!"" transform="translate(0 57)"><path id="Rectangle-5" fill="#FFF9EB" stroke="#8A704D" stroke-width="2" d="M18.861 1.809L2 17.533l53.14 56.986L72 58.794 18.861 1.81z"/><text id=""World!"" fill="#8A704D" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold" transform="rotate(47 40.083 39.762)"><tspan x="10.591" y="46.262">"World!"</tspan></text></g><g id="Rectangle-5-+-"World!"-2" transform="rotate(-67 96.824 -33.912)"><path id="Rectangle-5" fill="#FFF9EB" stroke="#8A704D" stroke-width="2" d="M18.861 1.809L2 17.533l53.14 56.986L72 58.794 18.861 1.81z"/><text id=""Hello!"" fill="#8A704D" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold" transform="rotate(47 40.083 39.762)"><tspan x="10.591" y="46.262">"Hello!"</tspan></text></g><path id="Shape" fill="#E8C48F" d="M8 125v54.73c0 3.42 1.484 5.27 4.387 5.27h100.086c3.122 0 5.527-2.548 5.527-3.476V125H8z"/></g><text id="message" fill="#FFF" font-family="OpenSans-Bold, Open Sans" font-size="18" font-weight="bold"><tspan x="77" y="157">message</tspan></text><path id="Fill-54" fill="#E8C48F" d="M58.112 51.808S47.657 40.623 40.719 36.155l-.505 5.542a76.036 76.036 0 00-33.769 4.595l4.169 11.032a64.248 64.248 0 0128.531-3.882l-.505 5.542c5.581-3.329 19.472-7.176 19.472-7.176" transform="rotate(11 32.278 47.57)"/><path id="Fill-54" fill="#E8C48F" d="M287.797 28.186s-10.454-11.185-17.393-15.653l-.505 5.541a76.036 76.036 0 00-33.769 4.596l4.169 11.032a64.248 64.248 0 0128.531-3.882l-.504 5.541c5.58-3.328 19.47-7.175 19.47-7.175" transform="rotate(2 261.964 23.947)"/><g id="noun_48910_cc" transform="translate(298 5)"><path id="Shape" d="M50.983 6H36.016C35.456 6 35 6.626 35 7.395V12h17V7.395C52 6.626 51.543 6 50.983 6z"/><path id="Shape" fill="#E8C48F" d="M84.193 9.36h-26.39V6.085C57.803 2.729 54.99 0 51.528 0H36.47c-3.46 0-6.275 2.729-6.275 6.085V9.36H3.807C1.705 9.36 0 11.012 0 13.05v.26C0 15.348 1.705 17 3.807 17h80.386C86.295 17 88 15.348 88 13.31v-.26c0-2.038-1.706-3.69-3.807-3.69zM53 12H36V7.395C36 6.626 36.457 6 37.016 6h14.968C52.544 6 53 6.626 53 7.395V12zM74.955 20.045H8.044c-3.89 0-7.044-.68-7.044 3.266l5.282 78.382c0 3.943 3.155 7.307 7.045 7.307h56.347c3.89 0 7.044-3.364 7.044-7.307L82 23.31c-.001-3.947-3.155-3.266-7.045-3.266zM26.757 98.999c-1.283.039-2.353-.8-2.396-1.878l-2.36-61.095c-.041-1.078.964-1.985 2.242-2.025 1.283-.04 2.353.801 2.396 1.879l2.36 61.096c.041 1.076-.963 1.984-2.242 2.023zM43 97.049C43 98.126 42.328 99 41.5 99s-1.5-.876-1.5-1.951V35.95c0-1.078.672-1.951 1.5-1.951s1.5.873 1.5 1.951V97.05zm18.639.072c-.042 1.078-1.113 1.917-2.396 1.878-1.28-.04-2.283-.947-2.242-2.024l2.36-61.095c.042-1.078 1.112-1.919 2.394-1.879 1.28.042 2.285.947 2.244 2.025l-2.36 61.095z"/></g></g></g></svg>
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
|
@ -1 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="166" height="145" viewBox="0 0 166 145"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="combined" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="noun_1211_cc-+-Message" transform="translate(13 3)"><g id="noun_1211_cc"><path id="Shape" fill="#DBAF88" d="M17 37.196h112.558v42.95c0 .373-.079.862-.279 1.294-.2.433-16.574 35.56-16.574 35.56V62.64l16.854-25.444L148 9H35.44L17 37.196zM17 66V38L2 66"/><g id="Rectangle-5-+-"World!"" transform="translate(15)"><path id="Rectangle-5" fill="#FBF2EC" stroke="#AF6E24" stroke-width="2" d="M18.861 1.809l53.14 56.985L55.14 74.52 1.999 17.533 18.86 1.81z"/><text id=""Hello!"" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold" transform="rotate(47 40.083 39.762)"><tspan x="10.591" y="46.262">"Hello!"</tspan></text></g><path id="Shape" fill="#DBAF88" d="M0 68v54.73c0 3.42 1.484 5.27 4.387 5.27h100.086c3.122 0 5.527-2.548 5.527-3.476V68H0z"/></g><text id="message" fill="#FFF" font-family="OpenSans-Bold, Open Sans" font-size="18" font-weight="bold"><tspan x="17" y="105">message</tspan></text></g></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="166" height="145" viewBox="0 0 166 145"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="combined" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="variable.svg"><g id="noun_1211_cc-+-Message" transform="translate(13 3)"><g id="noun_1211_cc"><path id="Shape" fill="#E8C48F" d="M17 37.196h112.558v42.95c0 .373-.079.862-.279 1.294-.2.433-16.574 35.56-16.574 35.56V62.64l16.854-25.444L148 9H35.44L17 37.196zM17 66V38L2 66"/><g id="Rectangle-5-+-"World!"" transform="translate(15)"><path id="Rectangle-5" fill="#FFF9EB" stroke="#8A704D" stroke-width="2" d="M18.861 1.809L2 17.533l53.14 56.986L72 58.794 18.861 1.81z"/><text id=""Hello!"" fill="#8A704D" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold" transform="rotate(47 40.083 39.762)"><tspan x="10.591" y="46.262">"Hello!"</tspan></text></g><path id="Shape" fill="#E8C48F" d="M0 68v54.73c0 3.42 1.484 5.27 4.387 5.27h100.086c3.122 0 5.527-2.548 5.527-3.476V68H0z"/></g><text id="message" fill="#FFF" font-family="OpenSans-Bold, Open Sans" font-size="18" font-weight="bold"><tspan x="17" y="105">message</tspan></text></g></g></g></svg>
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
@ -1,10 +1,6 @@
|
|||
# Data types
|
||||
|
||||
A value in JavaScript is always of a certain type. For example, a string or a number.
|
||||
|
||||
There are eight basic data types in JavaScript. Here, we'll cover them in general and in the next chapters we'll talk about each of them in detail.
|
||||
|
||||
We can put any type in a variable. For example, a variable can at one moment be a string and then store a number:
|
||||
A variable in JavaScript can contain any data. A variable can at one moment be a string and at another be a number:
|
||||
|
||||
```js
|
||||
// no error
|
||||
|
@ -12,7 +8,9 @@ let message = "hello";
|
|||
message = 123456;
|
||||
```
|
||||
|
||||
Programming languages that allow such things, such as JavaScript, are called "dynamically typed", meaning that there exist data types, but variables are not bound to any of them.
|
||||
Programming languages that allow such things are called "dynamically typed", meaning that there are data types, but variables are not bound to any of them.
|
||||
|
||||
There are eight basic data types in JavaScript. Here, we'll cover them in general and in the next chapters we'll talk about each of them in detail.
|
||||
|
||||
## Number
|
||||
|
||||
|
@ -46,15 +44,13 @@ Besides regular numbers, there are so-called "special numeric values" which also
|
|||
alert( "not a number" / 2 ); // NaN, such division is erroneous
|
||||
```
|
||||
|
||||
`NaN` is sticky. Any further mathematical operation on `NaN` returns `NaN`:
|
||||
`NaN` is sticky. Any further operation on `NaN` returns `NaN`:
|
||||
|
||||
```js run
|
||||
alert( NaN + 1 ); // NaN
|
||||
alert( 3 * NaN ); // NaN
|
||||
alert( "not a number" / 2 - 1 ); // NaN
|
||||
alert( "not a number" / 2 + 5 ); // NaN
|
||||
```
|
||||
|
||||
So, if there's a `NaN` somewhere in a mathematical expression, it propagates to the whole result (there's only one exception to that: `NaN ** 0` is `1`).
|
||||
So, if there's a `NaN` somewhere in a mathematical expression, it propagates to the whole result.
|
||||
|
||||
```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.
|
||||
|
@ -66,41 +62,25 @@ Special numeric values formally belong to the "number" type. Of course they are
|
|||
|
||||
We'll see more about working with numbers in the chapter <info:number>.
|
||||
|
||||
## BigInt [#bigint-type]
|
||||
## BigInt
|
||||
|
||||
In JavaScript, the "number" type cannot safely represent integer values larger than <code>(2<sup>53</sup>-1)</code> (that's `9007199254740991`), or less than <code>-(2<sup>53</sup>-1)</code> for negatives.
|
||||
|
||||
To be really precise, the "number" type can store larger integers (up to <code>1.7976931348623157 * 10<sup>308</sup></code>), but outside of the safe integer range <code>±(2<sup>53</sup>-1)</code> there'll be a precision error, because not all digits fit into the fixed 64-bit storage. So an "approximate" value may be stored.
|
||||
|
||||
For example, these two numbers (right above the safe range) are the same:
|
||||
|
||||
```js
|
||||
console.log(9007199254740991 + 1); // 9007199254740992
|
||||
console.log(9007199254740991 + 2); // 9007199254740992
|
||||
```
|
||||
|
||||
So to say, all odd integers greater than <code>(2<sup>53</sup>-1)</code> can't be stored at all in the "number" type.
|
||||
|
||||
For most purposes <code>±(2<sup>53</sup>-1)</code> range is quite enough, but sometimes we need the entire range of really big integers, e.g. for cryptography or microsecond-precision timestamps.
|
||||
In JavaScript, the "number" type cannot represent integer values larger than <code>2<sup>53</sup></code> (or less than <code>-2<sup>53</sup></code> for negatives), that's a technical limitation caused by their internal representation. That's about 16 decimal digits, so for most purposes the limitation isn't a problem, but sometimes we need really big numbers, e.g. for cryptography or microsecond-precision timestamps.
|
||||
|
||||
`BigInt` type was recently added to the language to represent integers of arbitrary length.
|
||||
|
||||
A `BigInt` value is created by appending `n` to the end of an integer:
|
||||
A `BigInt` is created by appending `n` to the end of an integer literal:
|
||||
|
||||
```js
|
||||
// the "n" at the end means it's a BigInt
|
||||
const bigInt = 1234567890123456789012345678901234567890n;
|
||||
```
|
||||
|
||||
As `BigInt` numbers are rarely needed, we don't cover them here, but devoted them a separate chapter <info:bigint>. Read it when you need such big numbers.
|
||||
As `BigInt` numbers are rarely needed, we devoted them a separate chapter <info:bigint>.
|
||||
|
||||
|
||||
```smart header="Compatibility issues"
|
||||
Right now, `BigInt` is supported in Firefox/Chrome/Edge/Safari, but not in IE.
|
||||
```smart header="Compatability issues"
|
||||
Right now `BigInt` is supported in Firefox and Chrome, but not in Safari/IE/Edge.
|
||||
```
|
||||
|
||||
You can check [*MDN* BigInt compatibility table](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#Browser_compatibility) to know which versions of a browser are supported.
|
||||
|
||||
## String
|
||||
|
||||
A string in JavaScript must be surrounded by quotes.
|
||||
|
@ -143,7 +123,7 @@ We'll cover strings more thoroughly in the chapter <info:string>.
|
|||
```smart header="There is no *character* type."
|
||||
In some languages, there is a special "character" type for a single character. For example, in the C language and in Java it is called "char".
|
||||
|
||||
In JavaScript, there is no such type. There's only one type: `string`. A string may consist of zero characters (be empty), one character or many of them.
|
||||
In JavaScript, there is no such type. There's only one type: `string`. A string may consist of only one character or many of them.
|
||||
```
|
||||
|
||||
## Boolean (logical type)
|
||||
|
@ -183,7 +163,7 @@ In JavaScript, `null` is not a "reference to a non-existing object" or a "null p
|
|||
|
||||
It's just a special value which represents "nothing", "empty" or "value unknown".
|
||||
|
||||
The code above states that `age` is unknown.
|
||||
The code above states that `age` is unknown or empty for some reason.
|
||||
|
||||
## The "undefined" value
|
||||
|
||||
|
@ -194,39 +174,43 @@ The meaning of `undefined` is "value is not assigned".
|
|||
If a variable is declared, but not assigned, then its value is `undefined`:
|
||||
|
||||
```js run
|
||||
let age;
|
||||
let x;
|
||||
|
||||
alert(age); // shows "undefined"
|
||||
alert(x); // shows "undefined"
|
||||
```
|
||||
|
||||
Technically, it is possible to explicitly assign `undefined` to a variable:
|
||||
Technically, it is possible to assign `undefined` to any variable:
|
||||
|
||||
```js run
|
||||
let age = 100;
|
||||
let x = 123;
|
||||
|
||||
// change the value to undefined
|
||||
age = undefined;
|
||||
x = undefined;
|
||||
|
||||
alert(age); // "undefined"
|
||||
alert(x); // "undefined"
|
||||
```
|
||||
|
||||
...But we don't recommend doing that. Normally, one uses `null` to assign an "empty" or "unknown" value to a variable, while `undefined` is reserved as a default initial value for unassigned things.
|
||||
...But we don't recommend doing that. Normally, we use `null` to assign an "empty" or "unknown" value to a variable, and we use `undefined` for checks like seeing if a variable has been assigned.
|
||||
|
||||
## Objects and Symbols
|
||||
|
||||
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 data and more complex entities.
|
||||
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 data and more complex entities. We'll deal with them later in the chapter <info:object> after we learn more about primitives.
|
||||
|
||||
Being that important, objects deserve a special treatment. We'll deal with them later in the chapter <info:object>, after we learn more about primitives.
|
||||
|
||||
The `symbol` type is used to create unique identifiers for objects. We have to mention it here for the sake of completeness, but also postpone the details till we know objects.
|
||||
The `symbol` type is used to create unique identifiers for objects. We mention it here for completeness, but we'll study it after objects.
|
||||
|
||||
## The typeof operator [#type-typeof]
|
||||
|
||||
The `typeof` operator returns the type of the argument. It's useful when we want to process values of different types differently or just want to do a quick check.
|
||||
|
||||
A call to `typeof x` returns a string with the type name:
|
||||
It supports two forms of syntax:
|
||||
|
||||
1. As an operator: `typeof x`.
|
||||
2. As a function: `typeof(x)`.
|
||||
|
||||
In other words, it works with parentheses or without them. The result is the same.
|
||||
|
||||
The call to `typeof x` returns a string with the type name:
|
||||
|
||||
```js
|
||||
typeof undefined // "undefined"
|
||||
|
@ -257,37 +241,25 @@ typeof alert // "function" (3)
|
|||
The last three lines may need additional explanation:
|
||||
|
||||
1. `Math` is a built-in object that provides mathematical operations. We will learn it in the chapter <info:number>. Here, it serves just as an example of an object.
|
||||
2. The result of `typeof null` is `"object"`. That's an officially recognized error in `typeof`, coming from very early days of JavaScript and kept for compatibility. Definitely, `null` is not an object. It is a special value with a separate type of its own. The behavior of `typeof` is wrong here.
|
||||
3. The result of `typeof alert` is `"function"`, because `alert` is a function. We'll study functions in the next chapters where we'll also see that there's no special "function" type in JavaScript. Functions belong to the object type. But `typeof` treats them differently, returning `"function"`. That also comes from the early days of JavaScript. Technically, such behavior isn't correct, but can be convenient in practice.
|
||||
|
||||
```smart header="The `typeof(x)` syntax"
|
||||
You may also come across another syntax: `typeof(x)`. It's the same as `typeof x`.
|
||||
|
||||
To put it clear: `typeof` is an operator, not a function. The parentheses here aren't a part of `typeof`. It's the kind of parentheses used for mathematical grouping.
|
||||
|
||||
Usually, such parentheses contain a mathematical expression, such as `(2 + 2)`, but here they contain only one argument `(x)`. Syntactically, they allow to avoid a space between the `typeof` operator and its argument, and some people like it.
|
||||
|
||||
Some people prefer `typeof(x)`, although the `typeof x` syntax is much more common.
|
||||
```
|
||||
2. The result of `typeof null` is `"object"`. That's wrong. It is an officially recognized error in `typeof`, kept for compatibility. Of course, `null` is not an object. It is a special value with a separate type of its own. So, again, this is an error in the language.
|
||||
3. The result of `typeof alert` is `"function"`, because `alert` is a function. We'll study functions in the next chapters where we'll also see that there's no special "function" type in JavaScript. Functions belong to the object type. But `typeof` treats them differently, returning `"function"`. That's not quite correct, but very convenient in practice.
|
||||
|
||||
## Summary
|
||||
|
||||
There are 8 basic data types in JavaScript.
|
||||
|
||||
- Seven primitive data types:
|
||||
- `number` for numbers of any kind: integer or floating-point, integers are limited by <code>±(2<sup>53</sup>-1)</code>.
|
||||
- `bigint` for integer numbers of arbitrary length.
|
||||
- `string` for strings. A string may have zero or more characters, there's no separate single-character type.
|
||||
- `boolean` for `true`/`false`.
|
||||
- `null` for unknown values -- a standalone type that has a single value `null`.
|
||||
- `undefined` for unassigned values -- a standalone type that has a single value `undefined`.
|
||||
- `symbol` for unique identifiers.
|
||||
- And one non-primitive data type:
|
||||
- `object` for more complex data structures.
|
||||
- `number` for numbers of any kind: integer or floating-point, integers are limited by ±2<sup>53</sup>.
|
||||
- `bigint` is for integer numbers of arbitrary length.
|
||||
- `string` for strings. A string may have one or more characters, there's no separate single-character type.
|
||||
- `boolean` for `true`/`false`.
|
||||
- `null` for unknown values -- a standalone type that has a single value `null`.
|
||||
- `undefined` for unassigned values -- a standalone type that has a single value `undefined`.
|
||||
- `object` for more complex data structures.
|
||||
- `symbol` for unique identifiers.
|
||||
|
||||
The `typeof` operator allows us to see which type is stored in a variable.
|
||||
|
||||
- Usually used as `typeof x`, but `typeof(x)` is also possible.
|
||||
- Two forms: `typeof x` or `typeof(x)`.
|
||||
- Returns a string with the name of the type, like `"string"`.
|
||||
- For `null` returns `"object"` -- this is an error in the language, it's not actually an object.
|
||||
|
||||
|
|
|
@ -7,9 +7,7 @@ For example, `alert` automatically converts any value to a string to show it. Ma
|
|||
There are also cases when we need to explicitly convert a value to the expected type.
|
||||
|
||||
```smart header="Not talking about objects yet"
|
||||
In this chapter, we won't cover objects. For now, we'll just be talking about primitives.
|
||||
|
||||
Later, after we learn about objects, in the chapter <info:object-toprimitive> we'll see how objects fit in.
|
||||
In this chapter, we won't cover objects. Instead, we'll study primitives first. Later, after we learn about objects, we'll see how object conversion works in the chapter <info:object-toprimitive>.
|
||||
```
|
||||
|
||||
## String Conversion
|
||||
|
@ -70,7 +68,7 @@ Numeric conversion rules:
|
|||
|`undefined`|`NaN`|
|
||||
|`null`|`0`|
|
||||
|<code>true and false</code> | `1` and `0` |
|
||||
| `string` | Whitespaces (includes spaces, tabs `\t`, newlines `\n` etc.) from the start and end are removed. If the remaining string is empty, the result is `0`. Otherwise, the number is "read" from the string. An error gives `NaN`. |
|
||||
| `string` | Whitespaces from the start and end are removed. If the remaining string is empty, the result is `0`. Otherwise, the number is "read" from the string. An error gives `NaN`. |
|
||||
|
||||
Examples:
|
||||
|
||||
|
@ -130,7 +128,7 @@ The conversion follows the rules:
|
|||
|`undefined`|`NaN`|
|
||||
|`null`|`0`|
|
||||
|<code>true / false</code> | `1 / 0` |
|
||||
| `string` | The string is read "as is", whitespaces (includes spaces, tabs `\t`, newlines `\n` etc.) from both sides are ignored. An empty string becomes `0`. An error gives `NaN`. |
|
||||
| `string` | The string is read "as is", whitespaces from both sides are ignored. An empty string becomes `0`. An error gives `NaN`. |
|
||||
|
||||
**`Boolean Conversion`** -- Occurs in logical operations. Can be performed with `Boolean(value)`.
|
||||
|
|
@ -9,8 +9,9 @@ true + false = 1
|
|||
"$" + 4 + 5 = "$45"
|
||||
"4" - 2 = 2
|
||||
"4px" - 2 = NaN
|
||||
" -9 " + 5 = " -9 5" // (3)
|
||||
" -9 " - 5 = -14 // (4)
|
||||
7 / 0 = Infinity
|
||||
" -9 " + 5 = " -9 5" // (3)
|
||||
" -9 " - 5 = -14 // (4)
|
||||
null + 1 = 1 // (5)
|
||||
undefined + 1 = NaN // (6)
|
||||
" \t \n" - 2 = -2 // (7)
|
||||
|
@ -22,4 +23,4 @@ undefined + 1 = NaN // (6)
|
|||
4. The subtraction always converts to numbers, so it makes `" -9 "` a number `-9` (ignoring spaces around it).
|
||||
5. `null` becomes `0` after the numeric conversion.
|
||||
6. `undefined` becomes `NaN` after the numeric conversion.
|
||||
7. Space characters are trimmed off string start and end when a string is converted to a number. Here the whole string consists of space characters, such as `\t`, `\n` and a "regular" space between them. So, similarly to an empty string, it becomes `0`.
|
||||
7. Space characters, are trimmed off string start and end when a string is converted to a number. Here the whole string consists of space characters, such as `\t`, `\n` and a "regular" space between them. So, similarly to an empty string, it becomes `0`.
|
|
@ -16,6 +16,7 @@ true + false
|
|||
"$" + 4 + 5
|
||||
"4" - 2
|
||||
"4px" - 2
|
||||
7 / 0
|
||||
" -9 " + 5
|
||||
" -9 " - 5
|
||||
null + 1
|
|
@ -1,8 +1,8 @@
|
|||
# Basic operators, maths
|
||||
# Operators
|
||||
|
||||
We know many operators from school. They are things like addition `+`, multiplication `*`, subtraction `-`, and so on.
|
||||
|
||||
In this chapter, we’ll start with simple operators, then concentrate on JavaScript-specific aspects, not covered by school arithmetic.
|
||||
In this chapter, we'll concentrate on aspects of operators that are not covered by school arithmetic.
|
||||
|
||||
## Terms: "unary", "binary", "operand"
|
||||
|
||||
|
@ -28,59 +28,9 @@ Before we move on, let's grasp some common terminology.
|
|||
|
||||
Formally, in the examples above we have two different operators that share the same symbol: the negation operator, a unary operator that reverses the sign, and the subtraction operator, a binary operator that subtracts one number from another.
|
||||
|
||||
## Maths
|
||||
## String concatenation, binary +
|
||||
|
||||
The following math operations are supported:
|
||||
|
||||
- Addition `+`,
|
||||
- Subtraction `-`,
|
||||
- Multiplication `*`,
|
||||
- Division `/`,
|
||||
- Remainder `%`,
|
||||
- Exponentiation `**`.
|
||||
|
||||
The first four are straightforward, while `%` and `**` need a few words about them.
|
||||
|
||||
### Remainder %
|
||||
|
||||
The remainder operator `%`, despite its appearance, is not related to percents.
|
||||
|
||||
The result of `a % b` is the [remainder](https://en.wikipedia.org/wiki/Remainder) of the integer division of `a` by `b`.
|
||||
|
||||
For instance:
|
||||
|
||||
```js run
|
||||
alert( 5 % 2 ); // 1, a remainder of 5 divided by 2
|
||||
alert( 8 % 3 ); // 2, a remainder of 8 divided by 3
|
||||
```
|
||||
|
||||
### Exponentiation **
|
||||
|
||||
The exponentiation operator `a ** b` raises `a` to the power of `b`.
|
||||
|
||||
In school maths, we write that as a<sup>b</sup>.
|
||||
|
||||
For instance:
|
||||
|
||||
```js run
|
||||
alert( 2 ** 2 ); // 2² = 4
|
||||
alert( 2 ** 3 ); // 2³ = 8
|
||||
alert( 2 ** 4 ); // 2⁴ = 16
|
||||
```
|
||||
|
||||
Just like in maths, the exponentiation operator is defined for non-integer numbers as well.
|
||||
|
||||
For example, a square root is an exponentiation by ½:
|
||||
|
||||
```js run
|
||||
alert( 4 ** (1/2) ); // 2 (power of 1/2 is the same as a square root)
|
||||
alert( 8 ** (1/3) ); // 2 (power of 1/3 is the same as a cubic root)
|
||||
```
|
||||
|
||||
|
||||
## String concatenation with binary +
|
||||
|
||||
Let's meet features of JavaScript operators that are beyond school arithmetics.
|
||||
Now, let's see special features of JavaScript operators that are beyond school arithmetics.
|
||||
|
||||
Usually, the plus operator `+` sums numbers.
|
||||
|
||||
|
@ -91,7 +41,7 @@ let s = "my" + "string";
|
|||
alert(s); // mystring
|
||||
```
|
||||
|
||||
Note that if any of the operands is a string, then the other one is converted to a string too.
|
||||
Note that if one of the operands is a string, the other one is converted to a string too.
|
||||
|
||||
For example:
|
||||
|
||||
|
@ -100,28 +50,22 @@ alert( '1' + 2 ); // "12"
|
|||
alert( 2 + '1' ); // "21"
|
||||
```
|
||||
|
||||
See, it doesn't matter whether the first operand is a string or the second one.
|
||||
See, it doesn't matter whether the first operand is a string or the second one. The rule is simple: if either operand is a string, the other one is converted into a string as well.
|
||||
|
||||
However, note that operations run from left to right. If there are two numbers followed by a string, the numbers will be added before being converted to a string:
|
||||
|
||||
Here's a more complex example:
|
||||
|
||||
```js run
|
||||
alert(2 + 2 + '1' ); // "41" and not "221"
|
||||
```
|
||||
|
||||
Here, operators work one after another. The first `+` sums two numbers, so it returns `4`, then the next `+` adds the string `1` to it, so it's like `4 + '1' = '41'`.
|
||||
String concatenation and conversion is a special feature of the binary plus `+`. Other arithmetic operators work only with numbers and always convert their operands to numbers.
|
||||
|
||||
For instance, subtraction and division:
|
||||
|
||||
```js run
|
||||
alert('1' + 2 + 2); // "122" and not "14"
|
||||
```
|
||||
Here, the first operand is a string, the compiler treats the other two operands as strings too. The `2` gets concatenated to `'1'`, so it's like `'1' + 2 = "12"` and `"12" + 2 = "122"`.
|
||||
|
||||
The binary `+` is the only operator that supports strings in such a way. Other arithmetic operators work only with numbers and always convert their operands to numbers.
|
||||
|
||||
Here's the demo for subtraction and division:
|
||||
|
||||
```js run
|
||||
alert( 6 - '2' ); // 4, converts '2' to a number
|
||||
alert( '6' / '2' ); // 3, converts both operands to numbers
|
||||
alert( 2 - '1' ); // 1
|
||||
alert( '6' / '2' ); // 3
|
||||
```
|
||||
|
||||
## Numeric conversion, unary +
|
||||
|
@ -189,27 +133,26 @@ Parentheses override any precedence, so if we're not satisfied with the default
|
|||
|
||||
There are many operators in JavaScript. Every operator has a corresponding precedence number. The one with the larger number executes first. If the precedence is the same, the execution order is from left to right.
|
||||
|
||||
Here's an extract from the [precedence table](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence) (you don't need to remember this, but note that unary operators are higher than corresponding binary ones):
|
||||
Here's an extract from the [precedence table](https://developer.mozilla.org/en/JavaScript/Reference/operators/operator_precedence) (you don't need to remember this, but note that unary operators are higher than corresponding binary ones):
|
||||
|
||||
| Precedence | Name | Sign |
|
||||
|------------|------|------|
|
||||
| ... | ... | ... |
|
||||
| 14 | unary plus | `+` |
|
||||
| 14 | unary negation | `-` |
|
||||
| 13 | exponentiation | `**` |
|
||||
| 12 | multiplication | `*` |
|
||||
| 12 | division | `/` |
|
||||
| 11 | addition | `+` |
|
||||
| 11 | subtraction | `-` |
|
||||
| 16 | unary plus | `+` |
|
||||
| 16 | unary negation | `-` |
|
||||
| 14 | multiplication | `*` |
|
||||
| 14 | division | `/` |
|
||||
| 13 | addition | `+` |
|
||||
| 13 | subtraction | `-` |
|
||||
| ... | ... | ... |
|
||||
| 2 | assignment | `=` |
|
||||
| 3 | assignment | `=` |
|
||||
| ... | ... | ... |
|
||||
|
||||
As we can see, the "unary plus" has a priority of `14` which is higher than the `11` of "addition" (binary plus). That's why, in the expression `"+apples + +oranges"`, unary pluses work before the addition.
|
||||
As we can see, the "unary plus" has a priority of `16` which is higher than the `13` of "addition" (binary plus). That's why, in the expression `"+apples + +oranges"`, unary pluses work before the addition.
|
||||
|
||||
## Assignment
|
||||
|
||||
Let's note that an assignment `=` is also an operator. It is listed in the precedence table with the very low priority of `2`.
|
||||
Let's note that an assignment `=` is also an operator. It is listed in the precedence table with the very low priority of `3`.
|
||||
|
||||
That's why, when we assign a variable, like `x = 2 * 2 + 1`, the calculations are done first and then the `=` is evaluated, storing the result in `x`.
|
||||
|
||||
|
@ -219,11 +162,24 @@ let x = 2 * 2 + 1;
|
|||
alert( x ); // 5
|
||||
```
|
||||
|
||||
### Assignment = returns a value
|
||||
It is possible to chain assignments:
|
||||
|
||||
The fact of `=` being an operator, not a "magical" language construct has an interesting implication.
|
||||
```js run
|
||||
let a, b, c;
|
||||
|
||||
All operators in JavaScript return a value. That's obvious for `+` and `-`, but also true for `=`.
|
||||
*!*
|
||||
a = b = c = 2 + 2;
|
||||
*/!*
|
||||
|
||||
alert( a ); // 4
|
||||
alert( b ); // 4
|
||||
alert( c ); // 4
|
||||
```
|
||||
|
||||
Chained assignments evaluate from right to left. First, the rightmost expression `2 + 2` is evaluated and then assigned to the variables on the left: `c`, `b` and `a`. At the end, all the variables share a single value.
|
||||
|
||||
````smart header="The assignment operator `\"=\"` returns a value"
|
||||
An operator always returns a value. That's obvious for most of them like addition `+` or multiplication `*`. But the assignment operator follows this rule too.
|
||||
|
||||
The call `x = value` writes the `value` into `x` *and then returns it*.
|
||||
|
||||
|
@ -243,74 +199,49 @@ alert( c ); // 0
|
|||
|
||||
In the example above, the result of expression `(a = b + 1)` is the value which was assigned to `a` (that is `3`). It is then used for further evaluations.
|
||||
|
||||
Funny code, isn't it? We should understand how it works, because sometimes we see it in JavaScript libraries.
|
||||
Funny code, isn't it? We should understand how it works, because sometimes we see it in JavaScript libraries, but shouldn't write anything like that ourselves. Such tricks definitely don't make code clearer or readable.
|
||||
````
|
||||
|
||||
Although, please don't write the code like that. Such tricks definitely don't make code clearer or readable.
|
||||
## Remainder %
|
||||
|
||||
### Chaining assignments
|
||||
The remainder operator `%`, despite its appearance, is not related to percents.
|
||||
|
||||
Another interesting feature is the ability to chain assignments:
|
||||
The result of `a % b` is the remainder of the integer division of `a` by `b`.
|
||||
|
||||
For instance:
|
||||
|
||||
```js run
|
||||
let a, b, c;
|
||||
|
||||
*!*
|
||||
a = b = c = 2 + 2;
|
||||
*/!*
|
||||
|
||||
alert( a ); // 4
|
||||
alert( b ); // 4
|
||||
alert( c ); // 4
|
||||
alert( 5 % 2 ); // 1 is a remainder of 5 divided by 2
|
||||
alert( 8 % 3 ); // 2 is a remainder of 8 divided by 3
|
||||
alert( 6 % 3 ); // 0 is a remainder of 6 divided by 3
|
||||
```
|
||||
|
||||
Chained assignments evaluate from right to left. First, the rightmost expression `2 + 2` is evaluated and then assigned to the variables on the left: `c`, `b` and `a`. At the end, all the variables share a single value.
|
||||
## Exponentiation **
|
||||
|
||||
Once again, for the purposes of readability it's better to split such code into few lines:
|
||||
The exponentiation operator `**` is a recent addition to the language.
|
||||
|
||||
```js
|
||||
c = 2 + 2;
|
||||
b = c;
|
||||
a = c;
|
||||
```
|
||||
That's easier to read, especially when eye-scanning the code fast.
|
||||
For a natural number `b`, the result of `a ** b` is `a` multiplied by itself `b` times.
|
||||
|
||||
## Modify-in-place
|
||||
|
||||
We often need to apply an operator to a variable and store the new result in that same variable.
|
||||
|
||||
For example:
|
||||
|
||||
```js
|
||||
let n = 2;
|
||||
n = n + 5;
|
||||
n = n * 2;
|
||||
```
|
||||
|
||||
This notation can be shortened using the operators `+=` and `*=`:
|
||||
For instance:
|
||||
|
||||
```js run
|
||||
let n = 2;
|
||||
n += 5; // now n = 7 (same as n = n + 5)
|
||||
n *= 2; // now n = 14 (same as n = n * 2)
|
||||
|
||||
alert( n ); // 14
|
||||
alert( 2 ** 2 ); // 4 (2 * 2)
|
||||
alert( 2 ** 3 ); // 8 (2 * 2 * 2)
|
||||
alert( 2 ** 4 ); // 16 (2 * 2 * 2 * 2)
|
||||
```
|
||||
|
||||
Short "modify-and-assign" operators exist for all arithmetical and bitwise operators: `/=`, `-=`, etc.
|
||||
The operator works for non-integer numbers as well.
|
||||
|
||||
Such operators have the same precedence as a normal assignment, so they run after most other calculations:
|
||||
For instance:
|
||||
|
||||
```js run
|
||||
let n = 2;
|
||||
|
||||
n *= 3 + 5; // right part evaluated first, same as n *= 8
|
||||
|
||||
alert( n ); // 16
|
||||
alert( 4 ** (1/2) ); // 2 (power of 1/2 is the same as a square root, that's maths)
|
||||
alert( 8 ** (1/3) ); // 2 (power of 1/3 is the same as a cubic root)
|
||||
```
|
||||
|
||||
## Increment/decrement
|
||||
|
||||
<!-- Can't use -- in title, because the built-in parser turns it into a 'long dash' – -->
|
||||
<!-- Can't use -- in title, because built-in parse turns it into – -->
|
||||
|
||||
Increasing or decreasing a number by one is among the most common numerical operations.
|
||||
|
||||
|
@ -437,7 +368,41 @@ The list of operators:
|
|||
- RIGHT SHIFT ( `>>` )
|
||||
- ZERO-FILL RIGHT SHIFT ( `>>>` )
|
||||
|
||||
These operators are used very rarely, when we need to fiddle with numbers on the very lowest (bitwise) level. We won't need these operators any time soon, as web development has little use of them, but in some special areas, such as cryptography, they are useful. You can read the [Bitwise Operators](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#bitwise_operators) chapter on MDN when a need arises.
|
||||
These operators are used very rarely. To understand them, we need to delve into low-level number representation and it would not be optimal to do that right now, especially since 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 on MDN. It would be more practical to do that when a real need arises.
|
||||
|
||||
## Modify-in-place
|
||||
|
||||
We often need to apply an operator to a variable and store the new result in that same variable.
|
||||
|
||||
For example:
|
||||
|
||||
```js
|
||||
let n = 2;
|
||||
n = n + 5;
|
||||
n = n * 2;
|
||||
```
|
||||
|
||||
This notation can be shortened using the operators `+=` and `*=`:
|
||||
|
||||
```js run
|
||||
let n = 2;
|
||||
n += 5; // now n = 7 (same as n = n + 5)
|
||||
n *= 2; // now n = 14 (same as n = n * 2)
|
||||
|
||||
alert( n ); // 14
|
||||
```
|
||||
|
||||
Short "modify-and-assign" operators exist for all arithmetical and bitwise operators: `/=`, `-=`, etc.
|
||||
|
||||
Such operators have the same precedence as a normal assignment, so they run after most other calculations:
|
||||
|
||||
```js run
|
||||
let n = 2;
|
||||
|
||||
n *= 3 + 5;
|
||||
|
||||
alert( n ); // 16 (right part evaluated first, same as n *= 8)
|
||||
```
|
||||
|
||||
## Comma
|
||||
|
|
@ -14,7 +14,7 @@ Some of the reasons:
|
|||
|
||||
1. Obviously, true.
|
||||
2. Dictionary comparison, hence false. `"a"` is smaller than `"p"`.
|
||||
3. Again, dictionary comparison, first char `"2"` is greater than the first char `"1"`.
|
||||
3. Again, dictionary comparison, first char of `"2"` is greater than the first char of `"1"`.
|
||||
4. Values `null` and `undefined` equal each other only.
|
||||
5. Strict equality is strict. Different types from both sides lead to false.
|
||||
6. Similar to `(4)`, `null` only equals `undefined`.
|
|
@ -1,21 +1,15 @@
|
|||
# Comparisons
|
||||
|
||||
We know many comparison operators from maths.
|
||||
|
||||
In JavaScript they are written like this:
|
||||
We know many comparison operators 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>.
|
||||
- Equals: `a == b`, please note the double equality sign `==` means the equality test, while a single one `a = b` means an assignment.
|
||||
- Not equals: In maths the notation is <code>≠</code>, but in JavaScript it's written as <code>a != b</code>.
|
||||
|
||||
In this article we'll learn more about different types of comparisons, how JavaScript makes them, including important peculiarities.
|
||||
|
||||
At the end you'll find a good recipe to avoid "JavaScript quirks"-related issues.
|
||||
- Equals: `a == b` (please note the double equals sign `=`. A single symbol `a = b` would mean an assignment).
|
||||
- Not equals. In maths the notation is <code>≠</code>, but in JavaScript it's written as an assignment with an exclamation sign before it: <code>a != b</code>.
|
||||
|
||||
## Boolean is the result
|
||||
|
||||
All comparison operators return a boolean value:
|
||||
Like all other operators, a comparison returns a value. In this case, the value is a boolean.
|
||||
|
||||
- `true` -- means "yes", "correct" or "the truth".
|
||||
- `false` -- means "no", "wrong" or "not the truth".
|
||||
|
@ -57,9 +51,7 @@ The algorithm to compare two strings is simple:
|
|||
4. Repeat until the end of either string.
|
||||
5. If both strings end at the same length, then they are equal. Otherwise, the longer string is greater.
|
||||
|
||||
In the first example above, the comparison `'Z' > 'A'` gets to a result at the first step.
|
||||
|
||||
The second comparison `'Glow'` and `'Glee'` needs more steps as strings are compared character-by-character:
|
||||
In the examples above, the comparison `'Z' > 'A'` gets to a result at the first step while the strings `"Glow"` and `"Glee"` are compared character-by-character:
|
||||
|
||||
1. `G` is the same as `G`.
|
||||
2. `l` is the same as `l`.
|
||||
|
@ -200,12 +192,13 @@ We get these results 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`, `undefined`, and no other value.
|
||||
|
||||
### Avoid problems
|
||||
### Evade problems
|
||||
|
||||
Why did we go over these examples? Should we remember these peculiarities all the time? Well, not really. Actually, these tricky things will gradually become familiar over time, but there's a solid way to avoid problems with them:
|
||||
Why did we go over these examples? Should we remember these peculiarities all the time? Well, not really. Actually, these tricky things will gradually become familiar over time, but there's a solid way to evade problems with them:
|
||||
|
||||
- Treat any comparison with `undefined/null` except the strict equality `===` with exceptional care.
|
||||
- Don't use comparisons `>= > < <=` with a variable which may be `null/undefined`, unless you're really sure of what you're doing. If a variable can have these values, check for them separately.
|
||||
Just treat any comparison with `undefined/null` except the strict equality `===` with exceptional care.
|
||||
|
||||
Don't use comparisons `>= > < <=` with a variable which may be `null/undefined`, unless you're really sure of what you're doing. If a variable can have these values, check for them separately.
|
||||
|
||||
## Summary
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
The reason is that prompt returns user input as a string.
|
||||
|
||||
So variables have values `"1"` and `"2"` respectively.
|
||||
|
||||
```js run
|
||||
let a = "1"; // prompt("First number?", 1);
|
||||
let b = "2"; // prompt("Second number?", 2);
|
||||
|
||||
alert(a + b); // 12
|
||||
```
|
||||
|
||||
What we should do is to convert strings to numbers before `+`. For example, using `Number()` or prepending them with `+`.
|
||||
|
||||
For example, right before `prompt`:
|
||||
|
||||
```js run
|
||||
let a = +prompt("First number?", 1);
|
||||
let b = +prompt("Second number?", 2);
|
||||
|
||||
alert(a + b); // 3
|
||||
```
|
||||
|
||||
Or in the `alert`:
|
||||
|
||||
```js run
|
||||
let a = prompt("First number?", 1);
|
||||
let b = prompt("Second number?", 2);
|
||||
|
||||
alert(+a + +b); // 3
|
||||
```
|
||||
|
||||
Using both unary and binary `+` in the latest code. Looks funny, doesn't it?
|
|
@ -1,18 +0,0 @@
|
|||
importance: 5
|
||||
|
||||
---
|
||||
|
||||
# Fix the addition
|
||||
|
||||
Here's a code that asks the user for two numbers and shows their sum.
|
||||
|
||||
It works incorrectly. The output in the example below is `12` (for default prompt values).
|
||||
|
||||
Why? Fix it. The result should be `3`.
|
||||
|
||||
```js run
|
||||
let a = prompt("First number?", 1);
|
||||
let b = prompt("Second number?", 2);
|
||||
|
||||
alert(a + b); // 12
|
||||
```
|
|
@ -1,10 +1,18 @@
|
|||
# Interaction: alert, prompt, confirm
|
||||
|
||||
As we'll be using the browser as our demo environment, let's see a couple of functions to interact with the user: `alert`, `prompt` and `confirm`.
|
||||
In this part of the tutorial we cover JavaScript language "as is", without environment-specific tweaks.
|
||||
|
||||
But we'll still be using the browser as our demo environment, so we should know at least a few of its user-interface functions. In this chapter, we'll get familiar with the browser functions `alert`, `prompt` and `confirm`.
|
||||
|
||||
## alert
|
||||
|
||||
This one we've seen already. It shows a message and waits for the user to press "OK".
|
||||
Syntax:
|
||||
|
||||
```js
|
||||
alert(message);
|
||||
```
|
||||
|
||||
This shows a message and pauses script execution until the user presses "OK".
|
||||
|
||||
For example:
|
||||
|
||||
|
@ -12,7 +20,7 @@ For example:
|
|||
alert("Hello");
|
||||
```
|
||||
|
||||
The mini-window with the message is called a *modal window*. The word "modal" means that the visitor can't interact with the rest of the page, press other buttons, etc, until they have dealt with the window. In this case -- until they press "OK".
|
||||
The mini-window with the message is called a *modal window*. The word "modal" means that the visitor can't interact with the rest of the page, press other buttons, etc. until they have dealt with the window. In this case -- until they press "OK".
|
||||
|
||||
## prompt
|
||||
|
||||
|
@ -30,11 +38,7 @@ It shows a modal window with a text message, an input field for the visitor, and
|
|||
`default`
|
||||
: An optional second parameter, the initial value for the input field.
|
||||
|
||||
```smart header="The square brackets in syntax `[...]`"
|
||||
The square brackets around `default` in the syntax above denote that the parameter is optional, not required.
|
||||
```
|
||||
|
||||
The visitor can type something in the prompt input field and press OK. Then we get that text in the `result`. Or they can cancel the input by pressing Cancel or hitting the `key:Esc` key, then we get `null` as the `result`.
|
||||
The visitor may type something in the prompt input field and press OK. Or they can cancel the input by pressing Cancel or hitting the `key:Esc` key.
|
||||
|
||||
The call to `prompt` returns the text from the input field or `null` if the input was canceled.
|
||||
|
|
@ -1 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="500" height="264" viewBox="0 0 500 264"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="combined" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="ifelse_task2.svg"><path id="Path-1218-Copy" fill="#C06334" fill-rule="nonzero" d="M425.5 133.5v54h8l-9.5 19-9.5-19h8v-51h-82v-3h85z"/><g id="Rectangle-1-+-Корень" transform="translate(213 4)"><rect id="Rectangle-1" width="78" height="28" x="1" y="1" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" rx="14"/><text id="Begin" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="21" y="19">Begin</tspan></text></g><g id="Rectangle-1-+-Корень-Copy-2" transform="translate(8 206)"><rect id="Rectangle-1" width="131" height="49" x="1" y="1" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" rx="24.5"/><text id="You-don't-know?-“ECM" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="12.937" y="20">You don't know?</tspan> <tspan x="22.107" y="39">“ECMAScript”!</tspan></text></g><g id="Rectangle-1-+-Корень-Copy-3" transform="translate(354 206)"><rect id="Rectangle-1" width="131" height="49" x="1" y="1" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" rx="24.5"/><text id="Right!" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="47.93" y="28">Right!</tspan></text></g><path id="Line" fill="#C06334" fill-rule="nonzero" d="M255 34v23.049l8 .001-9.5 19-9.5-19 8-.001V34h3z"/><path id="Path-1218" fill="#C06334" fill-rule="nonzero" d="M139.5 133.5v3h-59v51h8l-9.5 19-9.5-19h8v-54h62z"/><path id="Rectangle-356" fill="#FFF" d="M47 152h60v20H47z"/><g id="Rectangle-354-+-Каково-“официальное”" transform="translate(137 76)"><path id="Rectangle-354" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M116.5 1.123L230.801 59.5 116.5 117.877 2.199 59.5 116.5 1.123z"/><text id="What's-the-“official" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="80.958" y="43">What's the</tspan> <tspan x="60.214" y="62">“official” name of</tspan> <tspan x="80.77" y="81">JavaScript?</tspan></text></g><text id="Other" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="57" y="166">Other</tspan></text><path id="Rectangle-356-Copy" fill="#FFF" d="M387 152h60v20h-60z"/><text id="ECMAScript" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="383" y="165">ECMAScript</tspan></text></g></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="500" height="264" viewBox="0 0 500 264"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="combined" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="ifelse_task2.svg"><path id="Path-1218-Copy" fill="#EE6B47" fill-rule="nonzero" d="M425.5 133.5v54h8l-9.5 19-9.5-19h8v-51h-82v-3h85z"/><g id="Rectangle-1-+-Корень" transform="translate(213 4)"><rect id="Rectangle-1" width="78" height="28" x="1" y="1" fill="#FFF9EB" stroke="#E8C48E" stroke-width="2" rx="14"/><text id="Begin" fill="#8A704D" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="21" y="19">Begin</tspan></text></g><g id="Rectangle-1-+-Корень-Copy-2" transform="translate(8 206)"><rect id="Rectangle-1" width="131" height="49" x="1" y="1" fill="#FFF9EB" stroke="#E8C48E" stroke-width="2" rx="24.5"/><text id="You-don't-know?-“ECM" fill="#8A704D" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="13.354" y="20">You don't know?</tspan> <tspan x="21.967" y="39">“ECMAScript”!</tspan></text></g><g id="Rectangle-1-+-Корень-Copy-3" transform="translate(354 206)"><rect id="Rectangle-1" width="131" height="49" x="1" y="1" fill="#FFF9EB" stroke="#E8C48E" stroke-width="2" rx="24.5"/><text id="Right!" fill="#8A704D" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="47.93" y="28">Right!</tspan></text></g><path id="Line" fill="#EE6B47" fill-rule="nonzero" d="M255 34v23.049l8 .001-9.5 19-9.5-19 8-.001V34h3z"/><path id="Path-1218" fill="#EE6B47" fill-rule="nonzero" d="M139.5 133.5v3h-59v51h8l-9.5 19-9.5-19h8v-54h62z"/><path id="Rectangle-356" fill="#FFF" d="M47 152h60v20H47z"/><g id="Rectangle-354-+-Каково-“официальное”" transform="translate(137 76)"><path id="Rectangle-354" fill="#FFF9EB" stroke="#E8C48E" stroke-width="2" d="M2.199 59.5L116.5 117.877 230.801 59.5 116.5 1.123 2.199 59.5z"/><text id="What's-the-“official" fill="#8A704D" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="81.026" y="43">What's the</tspan> <tspan x="60.635" y="62">“official” name of</tspan> <tspan x="80.77" y="81">JavaScript?</tspan></text></g><text id="Other" fill="#8A704D" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="57" y="166">Other</tspan></text><path id="Rectangle-356-Copy" fill="#FFF" d="M387 152h60v20h-60z"/><text id="ECMAScript" fill="#8A704D" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="383" y="165">ECMAScript</tspan></text></g></g></svg>
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
|
@ -6,7 +6,7 @@ importance: 2
|
|||
|
||||
Using the `if..else` construct, write the code which asks: 'What is the "official" name of JavaScript?'
|
||||
|
||||
If the visitor enters "ECMAScript", then output "Right!", otherwise -- output: "You don't know? ECMAScript!"
|
||||
If the visitor enters "ECMAScript", then output "Right!", otherwise -- output: "Didn't know? ECMAScript!"
|
||||
|
||||

|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Conditional branching: if, '?'
|
||||
# Conditional operators: if, '?'
|
||||
|
||||
Sometimes, we need to perform different actions based on different conditions.
|
||||
|
||||
|
@ -68,7 +68,7 @@ if (cond) {
|
|||
|
||||
## The "else" clause
|
||||
|
||||
The `if` statement may contain an optional "else" block. It executes when the condition is falsy.
|
||||
The `if` statement may contain an optional "else" block. It executes when the condition is false.
|
||||
|
||||
For example:
|
||||
```js run
|
||||
|
|
|
@ -6,7 +6,7 @@ alert( alert(1) || 2 || alert(3) );
|
|||
|
||||
The call to `alert` does not return a value. Or, in other words, it returns `undefined`.
|
||||
|
||||
1. The first OR `||` evaluates its left operand `alert(1)`. That shows the first message with `1`.
|
||||
1. The first OR `||` evaluates it's left operand `alert(1)`. That shows the first message with `1`.
|
||||
2. The `alert` returns `undefined`, so OR goes on to the second operand searching for a truthy value.
|
||||
3. The second operand `2` is truthy, so the execution is halted, `2` is returned and then shown by the outer alert.
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
The answer: `null`, because it's the first falsy value from the list.
|
||||
|
||||
```js run
|
||||
alert(1 && null && 2);
|
||||
alert( 1 && null && 2 );
|
||||
```
|
||||
|
||||
|
|
|
@ -4,6 +4,6 @@ importance: 3
|
|||
|
||||
# Check the range between
|
||||
|
||||
Write an `if` condition to check that `age` is between `14` and `90` inclusively.
|
||||
Write an "if" condition to check that `age` is between `14` and `90` inclusively.
|
||||
|
||||
"Inclusively" means that `age` can reach the edges `14` or `90`.
|
||||
|
|
|
@ -4,6 +4,6 @@ importance: 3
|
|||
|
||||
# Check the range outside
|
||||
|
||||
Write an `if` condition to check that `age` is NOT between `14` and `90` inclusively.
|
||||
Write an `if` condition to check that `age` is NOT between 14 and 90 inclusively.
|
||||
|
||||
Create two variants: the first one using NOT `!`, the second one -- without it.
|
||||
|
|
Before Width: | Height: | Size: 6 KiB After Width: | Height: | Size: 6 KiB |
|
@ -3,19 +3,19 @@
|
|||
```js run demo
|
||||
let userName = prompt("Who's there?", '');
|
||||
|
||||
if (userName === 'Admin') {
|
||||
if (userName == 'Admin') {
|
||||
|
||||
let pass = prompt('Password?', '');
|
||||
|
||||
if (pass === 'TheMaster') {
|
||||
if (pass == 'TheMaster') {
|
||||
alert( 'Welcome!' );
|
||||
} else if (pass === '' || pass === null) {
|
||||
} else if (pass == '' || pass == null) {
|
||||
alert( 'Canceled' );
|
||||
} else {
|
||||
alert( 'Wrong password' );
|
||||
}
|
||||
|
||||
} else if (userName === '' || userName === null) {
|
||||
} else if (userName == '' || userName == null) {
|
||||
alert( 'Canceled' );
|
||||
} else {
|
||||
alert( "I don't know you" );
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Logical operators
|
||||
|
||||
There are four logical operators in JavaScript: `||` (OR), `&&` (AND), `!` (NOT), `??` (Nullish Coalescing). Here we cover the first three, the `??` operator is in the next article.
|
||||
There are three logical operators in JavaScript: `||` (OR), `&&` (AND), `!` (NOT).
|
||||
|
||||
Although they are called "logical", they can be applied to values of any type, not only boolean. Their result can also be of any type.
|
||||
|
||||
|
@ -64,7 +64,7 @@ if (hour < 10 || hour > 18 || isWeekend) {
|
|||
}
|
||||
```
|
||||
|
||||
## OR "||" finds the first truthy value [#or-finds-the-first-truthy-value]
|
||||
## OR "||" finds the first truthy value
|
||||
|
||||
The logic described above is somewhat classical. Now, let's bring in the "extra" features of JavaScript.
|
||||
|
||||
|
@ -84,16 +84,16 @@ The OR `||` operator does the following:
|
|||
|
||||
A value is returned in its original form, without the conversion.
|
||||
|
||||
In other words, a chain of OR `||` returns the first truthy value or the last one if no truthy value is found.
|
||||
In other words, a chain of OR `"||"` returns the first truthy value or the last one if no truthy value is found.
|
||||
|
||||
For instance:
|
||||
|
||||
```js run
|
||||
alert( 1 || 0 ); // 1 (1 is truthy)
|
||||
alert( true || 'no matter what' ); // (true is truthy)
|
||||
|
||||
alert( null || 1 ); // 1 (1 is the first truthy value)
|
||||
alert( null || 0 || 1 ); // 1 (the first truthy value)
|
||||
|
||||
alert( undefined || null || 0 ); // 0 (all falsy, returns the last value)
|
||||
```
|
||||
|
||||
|
@ -101,40 +101,53 @@ This leads to some interesting usage compared to a "pure, classical, boolean-onl
|
|||
|
||||
1. **Getting the first truthy value from a list of variables or expressions.**
|
||||
|
||||
For instance, we have `firstName`, `lastName` and `nickName` variables, all optional (i.e. can be undefined or have falsy values).
|
||||
Imagine we have a list of variables which can either contain data or be `null/undefined`. How can we find the first one with data?
|
||||
|
||||
Let's use OR `||` to choose the one that has the data and show it (or `"Anonymous"` if nothing set):
|
||||
We can use OR `||`:
|
||||
|
||||
```js run
|
||||
let firstName = "";
|
||||
let lastName = "";
|
||||
let nickName = "SuperCoder";
|
||||
let currentUser = null;
|
||||
let defaultUser = "John";
|
||||
|
||||
*!*
|
||||
alert( firstName || lastName || nickName || "Anonymous"); // SuperCoder
|
||||
let name = currentUser || defaultUser || "unnamed";
|
||||
*/!*
|
||||
|
||||
alert( name ); // selects "John" – the first truthy value
|
||||
```
|
||||
|
||||
If all variables were falsy, `"Anonymous"` would show up.
|
||||
|
||||
If both `currentUser` and `defaultUser` were falsy, `"unnamed"` would be the result.
|
||||
2. **Short-circuit evaluation.**
|
||||
|
||||
Another feature of OR `||` operator is the so-called "short-circuit" evaluation.
|
||||
Operands can be not only values, but arbitrary expressions. OR evaluates and tests them from left to right. The evaluation stops when a truthy value is reached, and the value is returned. This process is called "a short-circuit evaluation" because it goes as short as possible from left to right.
|
||||
|
||||
It means that `||` processes its arguments until the first truthy value is reached, and then the value is returned immediately, without even touching the other argument.
|
||||
This is clearly seen when the expression given as the second argument has a side effect like a variable assignment.
|
||||
|
||||
The importance of this feature becomes obvious if an operand isn't just a value, but an expression with a side effect, such as a variable assignment or a function call.
|
||||
|
||||
In the example below, only the second message is printed:
|
||||
In the example below, `x` does not get assigned:
|
||||
|
||||
```js run no-beautify
|
||||
*!*true*/!* || alert("not printed");
|
||||
*!*false*/!* || alert("printed");
|
||||
let x;
|
||||
|
||||
*!*true*/!* || (x = 1);
|
||||
|
||||
alert(x); // undefined, because (x = 1) not evaluated
|
||||
```
|
||||
|
||||
In the first line, the OR `||` operator stops the evaluation immediately upon seeing `true`, so the `alert` isn't run.
|
||||
If, instead, the first argument is `false`, `||` evaluates the second one, thus running the assignment:
|
||||
|
||||
Sometimes, people use this feature to execute commands only if the condition on the left part is falsy.
|
||||
```js run no-beautify
|
||||
let x;
|
||||
|
||||
*!*false*/!* || (x = 1);
|
||||
|
||||
alert(x); // 1
|
||||
```
|
||||
|
||||
An assignment is a simple case. There may be side effects, that won't show up if the evaluation doesn't reach them.
|
||||
|
||||
As we can see, such a use case is a "shorter way of doing `if`". The first operand is converted to boolean. If it's false, the second one is evaluated.
|
||||
|
||||
Most of time, it's better to use a "regular" `if` to keep the code easy to understand, but sometimes this can be handy.
|
||||
|
||||
## && (AND)
|
||||
|
||||
|
@ -223,8 +236,7 @@ The precedence of AND `&&` operator is higher than OR `||`.
|
|||
So the code `a && b || c && d` is essentially the same as if the `&&` expressions were in parentheses: `(a && b) || (c && d)`.
|
||||
````
|
||||
|
||||
````warn header="Don't replace `if` with `||` or `&&`"
|
||||
Sometimes, people use the AND `&&` operator as a "shorter way to write `if`".
|
||||
Just like OR, the AND `&&` operator can sometimes replace `if`.
|
||||
|
||||
For instance:
|
||||
|
||||
|
@ -241,12 +253,14 @@ So we basically have an analogue for:
|
|||
```js run
|
||||
let x = 1;
|
||||
|
||||
if (x > 0) alert( 'Greater than zero!' );
|
||||
if (x > 0) {
|
||||
alert( 'Greater than zero!' );
|
||||
}
|
||||
```
|
||||
|
||||
Although, the variant with `&&` appears shorter, `if` is more obvious and tends to be a little bit more readable. So we recommend using every construct for its purpose: use `if` if we want `if` and use `&&` if we want AND.
|
||||
````
|
||||
The variant with `&&` appears shorter. But `if` is more obvious and tends to be a little bit more readable.
|
||||
|
||||
So we recommend using every construct for its purpose: use `if` if we want if and use `&&` if we want AND.
|
||||
|
||||
## ! (NOT)
|
||||
|
||||
|
|
|
@ -1,169 +0,0 @@
|
|||
# Nullish coalescing operator '??'
|
||||
|
||||
[recent browser="new"]
|
||||
|
||||
The nullish coalescing operator is written as two question marks `??`.
|
||||
|
||||
As it treats `null` and `undefined` similarly, we'll use a special term here, in this article. For brevity, we'll say that a value is "defined" when it's neither `null` nor `undefined`.
|
||||
|
||||
The result of `a ?? b` is:
|
||||
- if `a` is defined, then `a`,
|
||||
- if `a` isn't defined, then `b`.
|
||||
|
||||
In other words, `??` returns the first argument if it's not `null/undefined`. Otherwise, the second one.
|
||||
|
||||
The nullish coalescing operator isn't anything completely new. It's just a nice syntax to get the first "defined" value of the two.
|
||||
|
||||
We can rewrite `result = a ?? b` using the operators that we already know, like this:
|
||||
|
||||
```js
|
||||
result = (a !== null && a !== undefined) ? a : b;
|
||||
```
|
||||
|
||||
Now it should be absolutely clear what `??` does. Let's see where it helps.
|
||||
|
||||
The common use case for `??` is to provide a default value.
|
||||
|
||||
For example, here we show `user` if its value isn't `null/undefined`, otherwise `Anonymous`:
|
||||
|
||||
```js run
|
||||
let user;
|
||||
|
||||
alert(user ?? "Anonymous"); // Anonymous (user not defined)
|
||||
```
|
||||
|
||||
Here's the example with `user` assigned to a name:
|
||||
|
||||
```js run
|
||||
let user = "John";
|
||||
|
||||
alert(user ?? "Anonymous"); // John (user defined)
|
||||
```
|
||||
|
||||
We can also use a sequence of `??` to select the first value from a list that isn't `null/undefined`.
|
||||
|
||||
Let's say we have a user's data in variables `firstName`, `lastName` or `nickName`. All of them may be not defined, if the user decided not to fill in the corresponding values.
|
||||
|
||||
We'd like to display the user name using one of these variables, or show "Anonymous" if all of them are `null/undefined`.
|
||||
|
||||
Let's use the `??` operator for that:
|
||||
|
||||
```js run
|
||||
let firstName = null;
|
||||
let lastName = null;
|
||||
let nickName = "Supercoder";
|
||||
|
||||
// shows the first defined value:
|
||||
*!*
|
||||
alert(firstName ?? lastName ?? nickName ?? "Anonymous"); // Supercoder
|
||||
*/!*
|
||||
```
|
||||
|
||||
## Comparison with ||
|
||||
|
||||
The OR `||` operator can be used in the same way as `??`, as it was described in the [previous chapter](info:logical-operators#or-finds-the-first-truthy-value).
|
||||
|
||||
For example, in the code above we could replace `??` with `||` and still get the same result:
|
||||
|
||||
```js run
|
||||
let firstName = null;
|
||||
let lastName = null;
|
||||
let nickName = "Supercoder";
|
||||
|
||||
// shows the first truthy value:
|
||||
*!*
|
||||
alert(firstName || lastName || nickName || "Anonymous"); // Supercoder
|
||||
*/!*
|
||||
```
|
||||
|
||||
Historically, the OR `||` operator was there first. It exists since the beginning of JavaScript, so developers were using it for such purposes for a long time.
|
||||
|
||||
On the other hand, the nullish coalescing operator `??` was added to JavaScript only recently, and the reason for that was that people weren't quite happy with `||`.
|
||||
|
||||
The important difference between them is that:
|
||||
- `||` returns the first *truthy* value.
|
||||
- `??` returns the first *defined* value.
|
||||
|
||||
In other words, `||` doesn't distinguish between `false`, `0`, an empty string `""` and `null/undefined`. They are all the same -- falsy values. If any of these is the first argument of `||`, then we'll get the second argument as the result.
|
||||
|
||||
In practice though, we may want to use default value only when the variable is `null/undefined`. That is, when the value is really unknown/not set.
|
||||
|
||||
For example, consider this:
|
||||
|
||||
```js run
|
||||
let height = 0;
|
||||
|
||||
alert(height || 100); // 100
|
||||
alert(height ?? 100); // 0
|
||||
```
|
||||
|
||||
- The `height || 100` checks `height` for being a falsy value, and it's `0`, falsy indeed.
|
||||
- so the result of `||` is the second argument, `100`.
|
||||
- The `height ?? 100` checks `height` for being `null/undefined`, and it's not,
|
||||
- so the result is `height` "as is", that is `0`.
|
||||
|
||||
In practice, the zero height is often a valid value, that shouldn't be replaced with the default. So `??` does just the right thing.
|
||||
|
||||
## Precedence
|
||||
|
||||
The precedence of the `??` operator is the same as `||`. They both equal `3` in the [MDN table](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence#Table).
|
||||
|
||||
That means that, just like `||`, the nullish coalescing operator `??` is evaluated before `=` and `?`, but after most other operations, such as `+`, `*`.
|
||||
|
||||
So we may need to add parentheses in expressions like this:
|
||||
|
||||
```js run
|
||||
let height = null;
|
||||
let width = null;
|
||||
|
||||
// important: use parentheses
|
||||
let area = (height ?? 100) * (width ?? 50);
|
||||
|
||||
alert(area); // 5000
|
||||
```
|
||||
|
||||
Otherwise, if we omit parentheses, then as `*` has the higher precedence than `??`, it would execute first, leading to incorrect results.
|
||||
|
||||
```js
|
||||
// without parentheses
|
||||
let area = height ?? 100 * width ?? 50;
|
||||
|
||||
// ...works this way (not what we want):
|
||||
let area = height ?? (100 * width) ?? 50;
|
||||
```
|
||||
|
||||
### Using ?? with && or ||
|
||||
|
||||
Due to safety reasons, JavaScript forbids using `??` together with `&&` and `||` operators, unless the precedence is explicitly specified with parentheses.
|
||||
|
||||
The code below triggers a syntax error:
|
||||
|
||||
```js run
|
||||
let x = 1 && 2 ?? 3; // Syntax error
|
||||
```
|
||||
|
||||
The limitation is surely debatable, it was added to the language specification with the purpose to avoid programming mistakes, when people start to switch from `||` to `??`.
|
||||
|
||||
Use explicit parentheses to work around it:
|
||||
|
||||
```js run
|
||||
*!*
|
||||
let x = (1 && 2) ?? 3; // Works
|
||||
*/!*
|
||||
|
||||
alert(x); // 2
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
||||
- The nullish coalescing operator `??` provides a short way to choose the first "defined" value from a list.
|
||||
|
||||
It's used to assign default values to variables:
|
||||
|
||||
```js
|
||||
// set height=100, if height is null or undefined
|
||||
height = height ?? 100;
|
||||
```
|
||||
|
||||
- The operator `??` has a very low precedence, only a bit higher than `?` and `=`, so consider adding parentheses when using it in an expression.
|
||||
- It's forbidden to use it with `||` or `&&` without explicit parentheses.
|
|
@ -10,6 +10,6 @@ do {
|
|||
The loop `do..while` repeats while both checks are truthy:
|
||||
|
||||
1. The check for `num <= 100` -- that is, the entered value is still not greater than `100`.
|
||||
2. The check `&& num` is false when `num` is `null` or an empty string. Then the `while` loop stops too.
|
||||
2. The check `&& num` is false when `num` is `null` or a empty string. Then the `while` loop stops too.
|
||||
|
||||
P.S. If `num` is `null` then `num <= 100` is `true`, so without the 2nd check the loop wouldn't stop if the user clicks CANCEL. Both checks are required.
|
|
@ -6,19 +6,6 @@ For example, outputting goods from a list one after another or just running the
|
|||
|
||||
*Loops* are a way to repeat the same code multiple times.
|
||||
|
||||
```smart header="The for..of and for..in loops"
|
||||
A small announcement for advanced readers.
|
||||
|
||||
This article covers only basic loops: `while`, `do..while` and `for(..;..;..)`.
|
||||
|
||||
If you came to this article searching for other types of loops, here are the pointers:
|
||||
|
||||
- See [for..in](info:object#forin) to loop over object properties.
|
||||
- See [for..of](info:array#loops) and [iterables](info:iterable) for looping over arrays and iterable objects.
|
||||
|
||||
Otherwise, please read on.
|
||||
```
|
||||
|
||||
## The "while" loop
|
||||
|
||||
The `while` loop has the following syntax:
|
||||
|
@ -119,7 +106,7 @@ Let's examine the `for` statement part-by-part:
|
|||
|
||||
| part | | |
|
||||
|-------|----------|----------------------------------------------------------------------------|
|
||||
| begin | `let i = 0` | Executes once upon entering the loop. |
|
||||
| begin | `i = 0` | Executes once upon entering the loop. |
|
||||
| condition | `i < 3`| Checked before every loop iteration. If false, the loop stops. |
|
||||
| body | `alert(i)`| Runs again and again while the condition is truthy. |
|
||||
| step| `i++` | Executes after the body on each iteration. |
|
||||
|
@ -175,8 +162,10 @@ for (i = 0; i < 3; i++) { // use an existing variable
|
|||
|
||||
alert(i); // 3, visible, because declared outside of the loop
|
||||
```
|
||||
|
||||
````
|
||||
|
||||
|
||||
### Skipping parts
|
||||
|
||||
Any part of `for` can be skipped.
|
||||
|
@ -279,7 +268,7 @@ for (let i = 0; i < 10; i++) {
|
|||
|
||||
From a technical point of view, this is identical to the example above. Surely, we can just wrap the code in an `if` block instead of using `continue`.
|
||||
|
||||
But as a side effect, this created one more level of nesting (the `alert` call inside the curly braces). If the code inside of `if` is longer than a few lines, that may decrease the overall readability.
|
||||
But as a side-effect, this created one more level of nesting (the `alert` call inside the curly braces). If the code inside of `if` is longer than a few lines, that may decrease the overall readability.
|
||||
````
|
||||
|
||||
````warn header="No `break/continue` to the right side of '?'"
|
||||
|
@ -297,6 +286,7 @@ if (i > 5) {
|
|||
|
||||
...and rewrite it using a question mark:
|
||||
|
||||
|
||||
```js no-beautify
|
||||
(i > 5) ? alert(i) : *!*continue*/!*; // continue isn't allowed here
|
||||
```
|
||||
|
@ -328,10 +318,9 @@ alert('Done!');
|
|||
|
||||
We need a way to stop the process if the user cancels the input.
|
||||
|
||||
The ordinary `break` after `input` would only break the inner loop. That's not sufficient -- labels, come to the rescue!
|
||||
The ordinary `break` after `input` would only break the inner loop. That's not sufficient--labels, come to the rescue!
|
||||
|
||||
A *label* is an identifier with a colon before a loop:
|
||||
|
||||
```js
|
||||
labelName: for (...) {
|
||||
...
|
||||
|
@ -353,7 +342,6 @@ The `break <labelName>` statement in the loop below breaks out to the label:
|
|||
// do something with the value...
|
||||
}
|
||||
}
|
||||
|
||||
alert('Done!');
|
||||
```
|
||||
|
||||
|
@ -374,26 +362,13 @@ The `continue` directive can also be used with a label. In this case, code execu
|
|||
Labels do not allow us to jump into an arbitrary place in the code.
|
||||
|
||||
For example, it is impossible to do this:
|
||||
|
||||
```js
|
||||
break label; // jump to the label below (doesn't work)
|
||||
break label; // doesn't jumps to the label below
|
||||
|
||||
label: for (...)
|
||||
```
|
||||
|
||||
A `break` directive must be inside a code block. Technically, any labelled code block will do, e.g.:
|
||||
|
||||
```js
|
||||
label: {
|
||||
// ...
|
||||
break label; // works
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
...Although, 99.9% of the time `break` is used inside loops, as we've seen in the examples above.
|
||||
|
||||
A `continue` is only possible from inside a loop.
|
||||
A call to `break/continue` is only possible from inside a loop and the label must be somewhere above the directive.
|
||||
````
|
||||
|
||||
## Summary
|
|
@ -47,7 +47,7 @@ switch (a) {
|
|||
break;
|
||||
*/!*
|
||||
case 5:
|
||||
alert( 'Too big' );
|
||||
alert( 'Too large' );
|
||||
break;
|
||||
default:
|
||||
alert( "I don't know such values" );
|
||||
|
@ -117,7 +117,7 @@ Several variants of `case` which share the same code can be grouped.
|
|||
For example, if we want the same code to run for `case 3` and `case 5`:
|
||||
|
||||
```js run no-beautify
|
||||
let a = 3;
|
||||
let a = 2 + 2;
|
||||
|
||||
switch (a) {
|
||||
case 4:
|
||||
|
@ -139,7 +139,7 @@ switch (a) {
|
|||
|
||||
Now both `3` and `5` show the same message.
|
||||
|
||||
The ability to "group" cases is a side effect of how `switch/case` works without `break`. Here the execution of `case 3` starts from the line `(*)` and goes through `case 5`, because there's no `break`.
|
||||
The ability to "group" cases is 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`.
|
||||
|
||||
## Type matters
|
||||
|
|
@ -0,0 +1 @@
|
|||
No difference.
|
|
@ -14,4 +14,4 @@ function checkAge(age) {
|
|||
}
|
||||
```
|
||||
|
||||
Note that the parentheses around `age > 18` are not required here. They exist for better readability.
|
||||
Note that the parentheses around `age > 18` are not required here. They exist for better readabilty.
|
|
@ -20,11 +20,11 @@ function showMessage() {
|
|||
}
|
||||
```
|
||||
|
||||
The `function` keyword goes first, then goes the *name of the function*, then a list of *parameters* between the parentheses (comma-separated, empty in the example above, we'll see examples later) and finally the code of the function, also named "the function body", between curly braces.
|
||||
The `function` keyword goes first, then goes the *name of the function*, then a list of *parameters* between the parentheses (comma-separated, empty in the example above) and finally the code of the function, also named "the function body", between curly braces.
|
||||
|
||||
```js
|
||||
function name(parameter1, parameter2, ... parameterN) {
|
||||
// body
|
||||
function name(parameters) {
|
||||
...body...
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -137,23 +137,26 @@ It's a good practice to minimize the use of global variables. Modern code has fe
|
|||
|
||||
## Parameters
|
||||
|
||||
We can pass arbitrary data to functions using parameters.
|
||||
We can pass arbitrary data to functions using parameters (also called *function arguments*) .
|
||||
|
||||
In the example below, the function has two parameters: `from` and `text`.
|
||||
|
||||
```js run
|
||||
function showMessage(*!*from, text*/!*) { // parameters: from, text
|
||||
function showMessage(*!*from, text*/!*) { // arguments: from, text
|
||||
alert(from + ': ' + text);
|
||||
}
|
||||
|
||||
*!*showMessage('Ann', 'Hello!');*/!* // Ann: Hello! (*)
|
||||
*!*showMessage('Ann', "What's up?");*/!* // Ann: What's up? (**)
|
||||
*!*
|
||||
showMessage('Ann', 'Hello!'); // Ann: Hello! (*)
|
||||
showMessage('Ann', "What's up?"); // Ann: What's up? (**)
|
||||
*/!*
|
||||
```
|
||||
|
||||
When the function is called in lines `(*)` and `(**)`, the given values are copied to local variables `from` and `text`. Then the function uses them.
|
||||
|
||||
Here's one more example: we have a variable `from` and pass it to the function. Please note: the function changes `from`, but the change is not seen outside, because a function always gets a copy of the value:
|
||||
|
||||
|
||||
```js run
|
||||
function showMessage(from, text) {
|
||||
|
||||
|
@ -172,21 +175,9 @@ showMessage(from, "Hello"); // *Ann*: Hello
|
|||
alert( from ); // Ann
|
||||
```
|
||||
|
||||
When a value is passed as a function parameter, it's also called an *argument*.
|
||||
|
||||
In other words, to put these terms straight:
|
||||
|
||||
- A parameter is the variable listed inside the parentheses in the function declaration (it's a declaration time term).
|
||||
- An argument is the value that is passed to the function when it is called (it's a call time term).
|
||||
|
||||
We declare functions listing their parameters, then call them passing arguments.
|
||||
|
||||
In the example above, one might say: "the function `showMessage` is declared with two parameters, then called with two arguments: `from` and `"Hello"`".
|
||||
|
||||
|
||||
## Default values
|
||||
|
||||
If a function is called, but an argument is not provided, then the corresponding value becomes `undefined`.
|
||||
If a parameter is not provided, then its value becomes `undefined`.
|
||||
|
||||
For instance, the aforementioned function `showMessage(from, text)` can be called with a single argument:
|
||||
|
||||
|
@ -194,9 +185,9 @@ For instance, the aforementioned function `showMessage(from, text)` can be calle
|
|||
showMessage("Ann");
|
||||
```
|
||||
|
||||
That's not an error. Such a call would output `"*Ann*: undefined"`. As the value for `text` isn't passed, it becomes `undefined`.
|
||||
That's not an error. Such a call would output `"Ann: undefined"`. There's no `text`, so it's assumed that `text === undefined`.
|
||||
|
||||
We can specify the so-called "default" (to use if omitted) value for a parameter in the function declaration, using `=`:
|
||||
If we want to use a "default" `text` in this case, then we can specify it after `=`:
|
||||
|
||||
```js run
|
||||
function showMessage(from, *!*text = "no text given"*/!*) {
|
||||
|
@ -206,13 +197,7 @@ function showMessage(from, *!*text = "no text given"*/!*) {
|
|||
showMessage("Ann"); // Ann: no text given
|
||||
```
|
||||
|
||||
Now if the `text` parameter is not passed, it will get the value `"no text given"`.
|
||||
|
||||
The default value also jumps in if the parameter exists, but strictly equals `undefined`, like this:
|
||||
|
||||
```js
|
||||
showMessage("Ann", undefined); // Ann: no text given
|
||||
```
|
||||
Now if the `text` parameter is not passed, it will get the value `"no text given"`
|
||||
|
||||
Here `"no text given"` is a string, but it can be a more complex expression, which is only evaluated and assigned if the parameter is missing. So, this is also possible:
|
||||
|
||||
|
@ -226,17 +211,13 @@ function showMessage(from, text = anotherFunction()) {
|
|||
```smart header="Evaluation of default parameters"
|
||||
In JavaScript, a default parameter is evaluated every time the function is called without the respective parameter.
|
||||
|
||||
In the example above, `anotherFunction()` isn't called at all, if the `text` parameter is provided.
|
||||
|
||||
On the other hand, it's independently called every time when `text` is missing.
|
||||
In the example above, `anotherFunction()` is called every time `showMessage()` is called without the `text` parameter.
|
||||
```
|
||||
|
||||
````smart header="Default parameters in old JavaScript code"
|
||||
Several years ago, JavaScript didn't support the syntax for default parameters. So people used other ways to specify them.
|
||||
````smart header="Default parameters old-style"
|
||||
Old editions of JavaScript did not support default parameters. So there are alternative ways to support them, that you can find mostly in the old scripts.
|
||||
|
||||
Nowadays, we can come across them in old scripts.
|
||||
|
||||
For example, an explicit check for `undefined`:
|
||||
For instance, an explicit check for being `undefined`:
|
||||
|
||||
```js
|
||||
function showMessage(from, text) {
|
||||
|
@ -250,64 +231,20 @@ function showMessage(from, text) {
|
|||
}
|
||||
```
|
||||
|
||||
...Or using the `||` operator:
|
||||
...Or the `||` operator:
|
||||
|
||||
```js
|
||||
function showMessage(from, text) {
|
||||
// If the value of text is falsy, assign the default value
|
||||
// this assumes that text == "" is the same as no text at all
|
||||
// if text is falsy then text gets the "default" value
|
||||
text = text || 'no text given';
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
````
|
||||
|
||||
|
||||
### Alternative default parameters
|
||||
|
||||
Sometimes it makes sense to assign default values for parameters at a later stage after the function declaration.
|
||||
|
||||
We can check if the parameter is passed during the function execution, by comparing it with `undefined`:
|
||||
|
||||
```js run
|
||||
function showMessage(text) {
|
||||
// ...
|
||||
|
||||
*!*
|
||||
if (text === undefined) { // if the parameter is missing
|
||||
text = 'empty message';
|
||||
}
|
||||
*/!*
|
||||
|
||||
alert(text);
|
||||
}
|
||||
|
||||
showMessage(); // empty message
|
||||
```
|
||||
|
||||
...Or we could use the `||` operator:
|
||||
|
||||
```js
|
||||
function showMessage(text) {
|
||||
// if text is undefined or otherwise falsy, set it to 'empty'
|
||||
text = text || 'empty';
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Modern JavaScript engines support the [nullish coalescing operator](info:nullish-coalescing-operator) `??`, it's better when most falsy values, such as `0`, should be considered "normal":
|
||||
|
||||
```js run
|
||||
function showCount(count) {
|
||||
// if count is undefined or null, show "unknown"
|
||||
alert(count ?? "unknown");
|
||||
}
|
||||
|
||||
showCount(0); // 0
|
||||
showCount(null); // unknown
|
||||
showCount(); // unknown
|
||||
```
|
||||
|
||||
## Returning a value
|
||||
|
||||
A function can return a value back into the calling code as the result.
|
||||
|
@ -462,7 +399,7 @@ Functions that are used *very often* sometimes have ultrashort names.
|
|||
|
||||
For example, the [jQuery](http://jquery.com) framework defines a function with `$`. The [Lodash](http://lodash.com/) library has its core function named `_`.
|
||||
|
||||
These are exceptions. Generally function names should be concise and descriptive.
|
||||
These are exceptions. Generally functions names should be concise and descriptive.
|
||||
```
|
||||
|
||||
## Functions == Comments
|
||||
|
@ -528,7 +465,7 @@ function name(parameters, delimited, by, comma) {
|
|||
|
||||
To make the code clean and easy to understand, it's recommended to use mainly local variables and parameters in the function, not outer variables.
|
||||
|
||||
It is always easier to understand a function which gets parameters, works with them and returns a result than a function which gets no parameters, but modifies outer variables as a side effect.
|
||||
It is always easier to understand a function which gets parameters, works with them and returns a result than a function which gets no parameters, but modifies outer variables as a side-effect.
|
||||
|
||||
Function naming:
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
No difference!
|
||||
|
||||
In both cases, `return confirm('Did parents allow you?')` executes exactly when the `if` condition is falsy.
|
|
@ -12,9 +12,7 @@ function sayHi() {
|
|||
|
||||
There is another syntax for creating a function that is called a *Function Expression*.
|
||||
|
||||
It allows us to create a new function in the middle of any expression.
|
||||
|
||||
For example:
|
||||
It looks like this:
|
||||
|
||||
```js
|
||||
let sayHi = function() {
|
||||
|
@ -22,19 +20,9 @@ let sayHi = function() {
|
|||
};
|
||||
```
|
||||
|
||||
Here we can see a variable `sayHi` getting a value, the new function, created as `function() { alert("Hello"); }`.
|
||||
Here, the function is created and assigned to the variable explicitly, like any other value. No matter how the function is defined, it's just a value stored in the variable `sayHi`.
|
||||
|
||||
As the function creation happens in the context of the assignment expression (to the right side of `=`), this is a *Function Expression*.
|
||||
|
||||
Please note, there's no name after the `function` keyword. Omitting a name is allowed for Function Expressions.
|
||||
|
||||
Here we immediately assign it to the variable, so the meaning of these code samples is the same: "create a function and put it into the variable `sayHi`".
|
||||
|
||||
In more advanced situations, that we'll come across later, a function may be created and immediately called or scheduled for a later execution, not stored anywhere, thus remaining anonymous.
|
||||
|
||||
## Function is a value
|
||||
|
||||
Let's reiterate: no matter how the function is created, a function is a value. Both examples above store a function in the `sayHi` variable.
|
||||
The meaning of these code samples is the same: "create a function and put it into the variable `sayHi`".
|
||||
|
||||
We can even print out that value using `alert`:
|
||||
|
||||
|
@ -75,10 +63,10 @@ Here's what happens above in detail:
|
|||
2. Line `(2)` copies it into the variable `func`. Please note again: there are no parentheses after `sayHi`. If there 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 as both `sayHi()` and `func()`.
|
||||
|
||||
We could also have used a Function Expression to declare `sayHi`, in the first line:
|
||||
Note that we could also have used a Function Expression to declare `sayHi`, in the first line:
|
||||
|
||||
```js
|
||||
let sayHi = function() { // (1) create
|
||||
let sayHi = function() {
|
||||
alert( "Hello" );
|
||||
};
|
||||
|
||||
|
@ -90,7 +78,7 @@ Everything would work the same.
|
|||
|
||||
|
||||
````smart header="Why is there a semicolon at the end?"
|
||||
You might wonder, why do Function Expressions have a semicolon `;` at the end, but Function Declarations do not:
|
||||
You might wonder, why does Function Expression have a semicolon `;` at the end, but Function Declaration does not:
|
||||
|
||||
```js
|
||||
function sayHi() {
|
||||
|
@ -102,9 +90,9 @@ let sayHi = function() {
|
|||
}*!*;*/!*
|
||||
```
|
||||
|
||||
The answer is simple: a Function Expression is created here as `function(…) {…}` inside the assignment statement: `let sayHi = …;`. The semicolon `;` is recommended at the end of the statement, it's not a part of the function syntax.
|
||||
|
||||
The semicolon would be there for a simpler assignment, such as `let sayHi = 5;`, and it's also there for a function assignment.
|
||||
The answer is simple:
|
||||
- There's no need for `;` at the end of code blocks and syntax structures that use them like `if { ... }`, `for { }`, `function f { }` etc.
|
||||
- A Function Expression is used inside the statement: `let sayHi = ...;`, as a value. It's not a code block, but rather an assignment. The semicolon `;` is recommended at the end of statements, no matter what the value is. So the semicolon here is not related to the Function Expression itself, it just terminates the statement.
|
||||
````
|
||||
|
||||
## Callback functions
|
||||
|
@ -144,13 +132,13 @@ function showCancel() {
|
|||
ask("Do you agree?", showOk, showCancel);
|
||||
```
|
||||
|
||||
In practice, such functions are quite useful. The major difference between a real-life `ask` and the example above is that real-life functions use more complex ways to interact with the user than a simple `confirm`. In the browser, such functions usually draw a nice-looking question window. But that's another story.
|
||||
In practice, such functions are quite useful. The major difference between a real-life `ask` and the example above is that real-life functions use more complex ways to interact with the user than a simple `confirm`. In the browser, such function usually draws a nice-looking question window. But that's another story.
|
||||
|
||||
**The arguments `showOk` and `showCancel` of `ask` are called *callback functions* or just *callbacks*.**
|
||||
|
||||
The idea is that we pass a function and expect it to be "called back" later if necessary. In our case, `showOk` becomes the callback for "yes" answer, and `showCancel` for "no" answer.
|
||||
|
||||
We can use Function Expressions to write an equivalent, shorter function:
|
||||
We can use Function Expressions to write the same function much shorter:
|
||||
|
||||
```js run no-beautify
|
||||
function ask(question, yes, no) {
|
||||
|
@ -186,7 +174,7 @@ Let's formulate the key differences between Function Declarations and Expression
|
|||
|
||||
First, the syntax: how to differentiate between them in the code.
|
||||
|
||||
- *Function Declaration:* a function, declared as a separate statement, in the main code flow:
|
||||
- *Function Declaration:* a function, declared as a separate statement, in the main code flow.
|
||||
|
||||
```js
|
||||
// Function Declaration
|
||||
|
@ -194,7 +182,7 @@ First, the syntax: how to differentiate between them in the code.
|
|||
return a + b;
|
||||
}
|
||||
```
|
||||
- *Function Expression:* a function, created inside an expression or inside another syntax construct. Here, the function is created on the right side of the "assignment expression" `=`:
|
||||
- *Function Expression:* a function, created inside an expression or inside another syntax construct. Here, the function is created at the right side of the "assignment expression" `=`:
|
||||
|
||||
```js
|
||||
// Function Expression
|
||||
|
@ -291,7 +279,7 @@ if (age < 18) {
|
|||
welcome(); // \ (runs)
|
||||
*/!*
|
||||
// |
|
||||
function welcome() { // |
|
||||
function welcome() { // |
|
||||
alert("Hello!"); // | Function Declaration is available
|
||||
} // | everywhere in the block where it's declared
|
||||
// |
|
||||
|
@ -301,7 +289,7 @@ if (age < 18) {
|
|||
|
||||
} else {
|
||||
|
||||
function welcome() {
|
||||
function welcome() {
|
||||
alert("Greetings!");
|
||||
}
|
||||
}
|
||||
|
@ -360,7 +348,7 @@ welcome(); // ok now
|
|||
|
||||
|
||||
```smart header="When to choose Function Declaration versus Function Expression?"
|
||||
As a rule of thumb, when we need to declare a function, the first thing to consider is Function Declaration syntax. It gives more freedom in how to organize our code, because we can call such functions before they are declared.
|
||||
As a rule of thumb, when we need to declare a function, the first to consider is Function Declaration syntax. It gives more freedom in how to organize our code, because we can call such functions before they are declared.
|
||||
|
||||
That's also better for readability, as it's easier to look up `function f(…) {…}` in the code than `let f = function(…) {…};`. Function Declarations are more "eye-catching".
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
```js run
|
||||
function ask(question, yes, no) {
|
||||
if (confirm(question)) yes();
|
||||
if (confirm(question)) yes()
|
||||
else no();
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ Replace Function Expressions with arrow functions in the code below:
|
|||
|
||||
```js run
|
||||
function ask(question, yes, no) {
|
||||
if (confirm(question)) yes();
|
||||
if (confirm(question)) yes()
|
||||
else no();
|
||||
}
|
||||
|
|
@ -5,15 +5,15 @@ There's another very simple and concise syntax for creating functions, that's of
|
|||
It's called "arrow functions", because it looks like this:
|
||||
|
||||
```js
|
||||
let func = (arg1, arg2, ..., argN) => expression;
|
||||
let func = (arg1, arg2, ...argN) => expression
|
||||
```
|
||||
|
||||
This creates a function `func` that accepts arguments `arg1..argN`, then evaluates the `expression` on the right side with their use and returns its result.
|
||||
...This creates a function `func` that accepts arguments `arg1..argN`, then evaluates the `expression` on the right side with their use and returns its result.
|
||||
|
||||
In other words, it's the shorter version of:
|
||||
|
||||
```js
|
||||
let func = function(arg1, arg2, ..., argN) {
|
||||
let func = function(arg1, arg2, ...argN) {
|
||||
return expression;
|
||||
};
|
||||
```
|
||||
|
@ -33,7 +33,7 @@ let sum = function(a, b) {
|
|||
alert( sum(1, 2) ); // 3
|
||||
```
|
||||
|
||||
As you can see, `(a, b) => a + b` means a function that accepts two arguments named `a` and `b`. Upon the execution, it evaluates the expression `a + b` and returns the result.
|
||||
As you can, see `(a, b) => a + b` means a function that accepts two arguments named `a` and `b`. Upon the execution, it evaluates the expression `a + b` and returns the result.
|
||||
|
||||
- If we have only one argument, then parentheses around parameters can be omitted, making that even shorter.
|
||||
|
||||
|
@ -48,7 +48,7 @@ As you can see, `(a, b) => a + b` means a function that accepts two arguments na
|
|||
alert( double(3) ); // 6
|
||||
```
|
||||
|
||||
- If there are no arguments, parentheses are empty, but they must be present:
|
||||
- If there are no arguments, parentheses will be empty (but they should be present):
|
||||
|
||||
```js run
|
||||
let sayHi = () => alert("Hello!");
|
||||
|
@ -64,10 +64,10 @@ For instance, to dynamically create a function:
|
|||
let age = prompt("What is your age?", 18);
|
||||
|
||||
let welcome = (age < 18) ?
|
||||
() => alert('Hello!') :
|
||||
() => alert('Hello') :
|
||||
() => alert("Greetings!");
|
||||
|
||||
welcome();
|
||||
welcome(); // ok now
|
||||
```
|
||||
|
||||
Arrow functions may appear unfamiliar and not very readable at first, but that quickly changes as the eyes get used to the structure.
|
||||
|
@ -76,9 +76,9 @@ They are very convenient for simple one-line actions, when we're just too lazy t
|
|||
|
||||
## Multiline arrow functions
|
||||
|
||||
The arrow functions that we've seen so far were very simple. They took arguments from the left of `=>`, evaluated and returned the right-side expression with them.
|
||||
The examples above took arguments from the left of `=>` and evaluated the right-side expression with them.
|
||||
|
||||
Sometimes we need a more complex function, with multiple expressions and statements. In that case, we can enclose them in curly braces. The major difference is that curly braces require a `return` within them to return a value (just like a regular function does).
|
||||
Sometimes we need something a little bit more complex, like multiple expressions or statements. It is also possible, but we should enclose them in curly braces. Then use a normal `return` within them.
|
||||
|
||||
Like this:
|
||||
|
||||
|
@ -86,7 +86,7 @@ Like this:
|
|||
let sum = (a, b) => { // the curly brace opens a multiline function
|
||||
let result = a + b;
|
||||
*!*
|
||||
return result; // if we use curly braces, then we need an explicit "return"
|
||||
return result; // if we use curly braces, then we need an explicit "return"
|
||||
*/!*
|
||||
};
|
||||
|
||||
|
@ -105,7 +105,7 @@ For now, we can already use arrow functions for one-line actions and callbacks.
|
|||
|
||||
## Summary
|
||||
|
||||
Arrow functions are handy for simple actions, especially for one-liners. They come in two flavors:
|
||||
Arrow functions are handy for one-liners. They come in two flavors:
|
||||
|
||||
1. Without curly braces: `(...args) => expression` -- the right side is an expression: the function evaluates it and returns the result. Parentheses can be omitted, if there's only a single argument, e.g. `n => n*2`.
|
||||
1. Without curly braces: `(...args) => expression` -- the right side is an expression: the function evaluates it and returns the result.
|
||||
2. With curly braces: `(...args) => { body }` -- brackets allow us to write multiple statements inside the function, but we need an explicit `return` to return something.
|
|
@ -55,7 +55,7 @@ To fully enable all features of modern JavaScript, we should start scripts with
|
|||
|
||||
The directive must be at the top of a script or at the beginning of a function body.
|
||||
|
||||
Without `"use strict"`, everything still works, but some features behave in the old-fashioned, "compatible" way. We'd generally prefer the modern behavior.
|
||||
Without `"use strict"`, everything still works, but some features behave in the old-fashion, "compatible" way. We'd generally prefer the modern behavior.
|
||||
|
||||
Some modern features of the language (like classes that we'll study in the future) enable strict mode implicitly.
|
||||
|
||||
|
@ -81,10 +81,9 @@ let x = 5;
|
|||
x = "John";
|
||||
```
|
||||
|
||||
There are 8 data types:
|
||||
There are 7 data types:
|
||||
|
||||
- `number` for both floating-point and integer numbers,
|
||||
- `bigint` for integer numbers of arbitrary length,
|
||||
- `string` for strings,
|
||||
- `boolean` for logical values: `true/false`,
|
||||
- `null` -- a type with a single value `null`, meaning "empty" or "does not exist",
|
||||
|
@ -144,7 +143,7 @@ Assignments
|
|||
: There is a simple assignment: `a = b` and combined ones like `a *= 2`.
|
||||
|
||||
Bitwise
|
||||
: Bitwise operators work with 32-bit integers at the lowest, bit-level: see the [docs](mdn:/JavaScript/Guide/Expressions_and_Operators#bitwise_operators) when they are needed.
|
||||
: Bitwise operators work with 32-bit integers at the lowest, bit-level: see the [docs](mdn:/JavaScript/Reference/Operators/Bitwise_Operators) when they are needed.
|
||||
|
||||
Conditional
|
||||
: The only operator with three parameters: `cond ? resultA : resultB`. If `cond` is truthy, returns `resultA`, otherwise `resultB`.
|
||||
|
@ -152,9 +151,6 @@ Conditional
|
|||
Logical operators
|
||||
: Logical AND `&&` and OR `||` perform short-circuit evaluation and then return the value where it stopped (not necessary `true`/`false`). Logical NOT `!` converts the operand to boolean type and returns the inverse value.
|
||||
|
||||
Nullish coalescing operator
|
||||
: The `??` operator provides a way to choose a defined value from a list of variables. The result of `a ?? b` is `a` unless it's `null/undefined`, then `b`.
|
||||
|
||||
Comparisons
|
||||
: Equality check `==` for values of different types converts them to a number (except `null` and `undefined` that equal each other and nothing else), so these are equal:
|
||||
|
||||
|
@ -174,7 +170,7 @@ Comparisons
|
|||
Other operators
|
||||
: There are few others, like a comma operator.
|
||||
|
||||
More in: <info:operators>, <info:comparison>, <info:logical-operators>, <info:nullish-coalescing-operator>.
|
||||
More in: <info:operators>, <info:comparison>, <info:logical-operators>.
|
||||
|
||||
## Loops
|
||||
|
||||
|
@ -216,7 +212,6 @@ let age = prompt('Your age?', 18);
|
|||
switch (age) {
|
||||
case 18:
|
||||
alert("Won't work"); // the result of prompt is a string, not a number
|
||||
break;
|
||||
|
||||
case "18":
|
||||
alert("This works!");
|
||||
|
@ -256,7 +251,7 @@ We covered three ways to create a function in JavaScript:
|
|||
3. Arrow functions:
|
||||
|
||||
```js
|
||||
// expression on the right side
|
||||
// expression at the right side
|
||||
let sum = (a, b) => a + b;
|
||||
|
||||
// or multi-line syntax with { ... }, need return here:
|
||||
|
@ -273,7 +268,7 @@ We covered three ways to create a function in JavaScript:
|
|||
```
|
||||
|
||||
|
||||
- Functions may have local variables: those declared inside its body or its parameter list. Such variables are only visible inside the function.
|
||||
- Functions may have local variables: those declared inside its body. Such variables are only visible inside the function.
|
||||
- Parameters can have default values: `function sum(a = 1, b = 2) {...}`.
|
||||
- Functions always return something. If there's no `return` statement, then the result is `undefined`.
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
# Debugging in the browser
|
||||
# Debugging in Chrome
|
||||
|
||||
Before writing more complex code, let's talk about debugging.
|
||||
|
||||
|
@ -38,7 +38,7 @@ If we press `key:Esc`, then a console opens below. We can type commands there an
|
|||
|
||||
After a statement is executed, its result is shown below.
|
||||
|
||||
For example, here `1+2` results in `3`, while the function call `hello("debugger")` returns nothing, so the result is `undefined`:
|
||||
For example, here `1+2` results in `3`, and `hello("debugger")` returns nothing, so the result is `undefined`:
|
||||
|
||||

|
||||
|
||||
|
@ -63,12 +63,12 @@ We can always find a list of breakpoints in the right panel. That's useful when
|
|||
- ...And so on.
|
||||
|
||||
```smart header="Conditional breakpoints"
|
||||
*Right click* on the line number allows to create a *conditional* breakpoint. It only triggers when the given expression, that you should provide when you create it, is truthy.
|
||||
*Right click* on the line number allows to create a *conditional* breakpoint. It only triggers when the given expression is truthy.
|
||||
|
||||
That's handy when we need to stop only for a certain variable value or for certain function parameters.
|
||||
```
|
||||
|
||||
## The command "debugger"
|
||||
## Debugger command
|
||||
|
||||
We can also pause the code by using the `debugger` command in it, like this:
|
||||
|
||||
|
@ -84,7 +84,8 @@ function hello(name) {
|
|||
}
|
||||
```
|
||||
|
||||
Such command works only when the development tools are open, otherwise the browser ignores it.
|
||||
That's very convenient when we are in a code editor and don't want to switch to the browser and look up the script in developer tools to set the breakpoint.
|
||||
|
||||
|
||||
## Pause and look around
|
||||
|
||||
|
@ -98,7 +99,7 @@ Please open the informational dropdowns to the right (labeled with arrows). They
|
|||
|
||||
1. **`Watch` -- shows current values for any expressions.**
|
||||
|
||||
You can click the plus `+` and input an expression. The debugger will show its value, automatically recalculating it in the process of execution.
|
||||
You can click the plus `+` and input an expression. The debugger will show its value at any moment, automatically recalculating it in the process of execution.
|
||||
|
||||
2. **`Call Stack` -- shows the nested calls chain.**
|
||||
|
||||
|
@ -134,11 +135,11 @@ There are buttons for it at the top of the right panel. Let's engage them.
|
|||
Clicking this again and again will step through all script statements one by one.
|
||||
|
||||
<span class="devtools" style="background-position:-62px -192px"></span> -- "Step over": run the next command, but *don't go into a function*, hotkey `key:F10`.
|
||||
: Similar to the previous "Step" command, but behaves differently if the next statement is a function call (not a built-in, like `alert`, but a function of our own).
|
||||
: Similar to the previous the "Step" command, but behaves differently if the next statement is a function call. That is: not a built-in, like `alert`, but a function of our own.
|
||||
|
||||
If we compare them, the "Step" command goes into a nested function call and pauses the execution at its first line, while "Step over" executes the nested function call invisibly to us, skipping the function internals.
|
||||
The "Step" command goes into it and pauses the execution at its first line, while "Step over" executes the nested function call invisibly, skipping the function internals.
|
||||
|
||||
The execution is then paused immediately after that function call.
|
||||
The execution is then paused immediately after that function.
|
||||
|
||||
That's good if we're not interested to see what happens inside the function call.
|
||||
|
||||
|
@ -154,7 +155,7 @@ There are buttons for it at the top of the right panel. Let's engage them.
|
|||
: That button does not move the execution. Just a mass on/off for breakpoints.
|
||||
|
||||
<span class="devtools" style="background-position:-90px -146px"></span> -- enable/disable automatic pause in case of an error.
|
||||
: When enabled, if the developer tools is open, an error during the script execution automatically pauses it. Then we can analyze variables in the debugger to see what went wrong. So if our script dies with an error, we can open debugger, enable this option and reload the page to see where it dies and what's the context at that moment.
|
||||
: When enabled, and the developer tools is open, a script error automatically pauses the execution. Then we can analyze variables to see what went wrong. So if our script dies with an error, we can open debugger, enable this option and reload the page to see where it dies and what's the context at that moment.
|
||||
|
||||
```smart header="Continue to here"
|
||||
Right click on a line of code opens the context menu with a great option called "Continue to here".
|
||||
|
@ -186,7 +187,7 @@ As we can see, there are three main ways to pause a script:
|
|||
2. The `debugger` statements.
|
||||
3. An error (if dev tools are open and the button <span class="devtools" style="background-position:-90px -146px"></span> is "on").
|
||||
|
||||
When paused, we can debug: examine variables and trace the code to see where the execution goes wrong.
|
||||
When paused, we can debug - examine variables and trace the code to see where the execution goes wrong.
|
||||
|
||||
There are many more options in developer tools than covered here. The full manual is at <https://developers.google.com/web/tools/chrome-devtools>.
|
||||
|
||||
|
|
Before Width: | Height: | Size: 118 KiB After Width: | Height: | Size: 120 KiB |
Before Width: | Height: | Size: 138 KiB After Width: | Height: | Size: 140 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 170 KiB After Width: | Height: | Size: 172 KiB |
Before Width: | Height: | Size: 164 KiB After Width: | Height: | Size: 166 KiB |
Before Width: | Height: | Size: 161 KiB After Width: | Height: | Size: 162 KiB |
|
@ -12,7 +12,7 @@ function pow(x,n) // <- no space between arguments
|
|||
|
||||
let x=prompt("x?",''), n=prompt("n?",'') // <-- technically possible,
|
||||
// but better make it 2 lines, also there's no spaces and missing ;
|
||||
if (n<=0) // <- no spaces inside (n <= 0), and should be extra line above it
|
||||
if (n<0) // <- no spaces inside (n < 0), and should be extra line above it
|
||||
{ // <- figure bracket on a separate line
|
||||
// below - long lines can be split into multiple lines for improved readability
|
||||
alert(`Power ${n} is not supported, please enter an integer number greater than zero`);
|
||||
|
@ -39,7 +39,7 @@ function pow(x, n) {
|
|||
let x = prompt("x?", "");
|
||||
let n = prompt("n?", "");
|
||||
|
||||
if (n <= 0) {
|
||||
if (n < 0) {
|
||||
alert(`Power ${n} is not supported,
|
||||
please enter an integer number greater than zero`);
|
||||
} else {
|
||||
|
|
|
@ -116,7 +116,7 @@ There are two types of indents:
|
|||
|
||||
One advantage of spaces over tabs is that spaces allow more flexible configurations of indents than the tab symbol.
|
||||
|
||||
For instance, we can align the parameters with the opening bracket, like this:
|
||||
For instance, we can align the arguments with the opening bracket, like this:
|
||||
|
||||
```js no-beautify
|
||||
show(parameters,
|
||||
|
@ -285,7 +285,7 @@ Of course, a team can always write their own style guide, but usually there's no
|
|||
|
||||
Some popular choices:
|
||||
|
||||
- [Google JavaScript Style Guide](https://google.github.io/styleguide/jsguide.html)
|
||||
- [Google JavaScript Style Guide](https://google.github.io/styleguide/javascriptguide.xml)
|
||||
- [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript)
|
||||
- [Idiomatic.JS](https://github.com/rwaldron/idiomatic.js)
|
||||
- [StandardJS](https://standardjs.com/)
|
||||
|
@ -301,11 +301,11 @@ The great thing about them is that style-checking can also find some bugs, like
|
|||
|
||||
Here are some well-known linting tools:
|
||||
|
||||
- [JSLint](https://www.jslint.com/) -- one of the first linters.
|
||||
- [JSHint](https://jshint.com/) -- more settings than JSLint.
|
||||
- [ESLint](https://eslint.org/) -- probably the newest one.
|
||||
- [JSLint](http://www.jslint.com/) -- one of the first linters.
|
||||
- [JSHint](http://www.jshint.com/) -- more settings than JSLint.
|
||||
- [ESLint](http://eslint.org/) -- probably the newest one.
|
||||
|
||||
All of them can do the job. The author uses [ESLint](https://eslint.org/).
|
||||
All of them can do the job. The author uses [ESLint](http://eslint.org/).
|
||||
|
||||
Most linters are integrated with many popular editors: just enable the plugin in the editor and configure the style.
|
||||
|
||||
|
@ -328,14 +328,14 @@ Here's an example of an `.eslintrc` file:
|
|||
},
|
||||
"rules": {
|
||||
"no-console": 0,
|
||||
"indent": 2
|
||||
"indent": ["warning", 2]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Here the directive `"extends"` denotes that the configuration is based on the "eslint:recommended" set of settings. After that, we specify our own.
|
||||
|
||||
It is also possible to download style rule sets from the web and extend them instead. See <https://eslint.org/docs/user-guide/getting-started> for more details about installation.
|
||||
It is also possible to download style rule sets from the web and extend them instead. See <http://eslint.org/docs/user-guide/getting-started> for more details about installation.
|
||||
|
||||
Also certain IDEs have built-in linting, which is convenient but not as customizable as ESLint.
|
||||
|
||||
|
|
Before Width: | Height: | Size: 132 KiB After Width: | Height: | Size: 134 KiB |
|
@ -125,25 +125,25 @@ Describe the architecture
|
|||
Document function parameters and usage
|
||||
: There's a special syntax [JSDoc](http://en.wikipedia.org/wiki/JSDoc) to document a function: usage, parameters, returned value.
|
||||
|
||||
For instance:
|
||||
```js
|
||||
/**
|
||||
* Returns x raised to the n-th power.
|
||||
*
|
||||
* @param {number} x The number to raise.
|
||||
* @param {number} n The power, must be a natural number.
|
||||
* @return {number} x raised to the n-th power.
|
||||
*/
|
||||
function pow(x, n) {
|
||||
...
|
||||
}
|
||||
```
|
||||
For instance:
|
||||
```js
|
||||
/**
|
||||
* Returns x raised to the n-th power.
|
||||
*
|
||||
* @param {number} x The number to raise.
|
||||
* @param {number} n The power, must be a natural number.
|
||||
* @return {number} x raised to the n-th power.
|
||||
*/
|
||||
function pow(x, n) {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Such comments allow us to understand the purpose of the function and use it the right way without looking in its code.
|
||||
Such comments allow us to understand the purpose of the function and use it the right way without looking in its code.
|
||||
|
||||
By the way, many editors like [WebStorm](https://www.jetbrains.com/webstorm/) can understand them as well and use them to provide autocomplete and some automatic code-checking.
|
||||
By the way, many editors like [WebStorm](https://www.jetbrains.com/webstorm/) can understand them as well and use them to provide autocomplete and some automatic code-checking.
|
||||
|
||||
Also, there are tools like [JSDoc 3](https://github.com/jsdoc/jsdoc) that can generate HTML-documentation from the comments. You can read more information about JSDoc at <https://jsdoc.app>.
|
||||
Also, there are tools like [JSDoc 3](https://github.com/jsdoc3/jsdoc) that can generate HTML-documentation from the comments. You can read more information about JSDoc at <http://usejsdoc.org/>.
|
||||
|
||||
Why is the task solved this way?
|
||||
: What's written is important. But what's *not* written may be even more important to understand what's going on. Why is the task solved exactly this way? The code gives no answer.
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# Ninja code
|
||||
|
||||
|
||||
```quote author="Confucius (Analects)"
|
||||
```quote author="Confucius"
|
||||
Learning without thought is labor lost; thought without learning is perilous.
|
||||
```
|
||||
|
||||
|
@ -43,7 +43,7 @@ The Dao hides in wordlessness. Only the Dao is well begun and well
|
|||
completed.
|
||||
```
|
||||
|
||||
Another way to code shorter is to use single-letter variable names everywhere. Like `a`, `b` or `c`.
|
||||
Another way to code faster is to use single-letter variable names everywhere. Like `a`, `b` or `c`.
|
||||
|
||||
A short variable disappears in the code like a real ninja in the forest. No one will be able to find it using "search" of the editor. And even if someone does, they won't be able to "decipher" what the name `a` or `b` means.
|
||||
|
||||
|
@ -104,8 +104,8 @@ A quick read of such code becomes impossible. And when there's a typo... Ummm...
|
|||
|
||||
## Smart synonyms
|
||||
|
||||
```quote author="Laozi (Tao Te Ching)"
|
||||
The Tao that can be told is not the eternal Tao. The name that can be named is not the eternal name.
|
||||
```quote author="Confucius"
|
||||
The hardest thing of all is to find a black cat in a dark room, especially if there is no cat.
|
||||
```
|
||||
|
||||
Using *similar* names for *same* things makes life more interesting and shows your creativity to the public.
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Automated testing will be used in further tasks, and it's also widely used in real projects.
|
||||
|
||||
## Why do we need tests?
|
||||
## Why we need tests?
|
||||
|
||||
When we write a function, we can usually imagine what it should do: which parameters give which results.
|
||||
|
||||
|
@ -51,7 +51,7 @@ describe("pow", function() {
|
|||
A spec has three main building blocks that you can see above:
|
||||
|
||||
`describe("title", function() { ... })`
|
||||
: What functionality we're describing? In our case we're describing the function `pow`. Used to group "workers" -- the `it` blocks.
|
||||
: What functionality we're describing. In our case we're describing the function `pow`. Used to group "workers" -- the `it` blocks.
|
||||
|
||||
`it("use case description", function() { ... })`
|
||||
: In the title of `it` we *in a human-readable way* describe the particular use case, and the second argument is a function that tests it.
|
||||
|
@ -79,7 +79,7 @@ So, the development is *iterative*. We write the spec, implement it, make sure t
|
|||
|
||||
Let's see this development flow in our practical case.
|
||||
|
||||
The first step is already complete: we have an initial spec for `pow`. Now, before making the implementation, let's use a few JavaScript libraries to run the tests, just to see that they are working (they will all fail).
|
||||
The first step is already complete: we have an initial spec for `pow`. Now, before making the implementation, let's use few JavaScript libraries to run the tests, just to see that they are working (they will all fail).
|
||||
|
||||
## The spec in action
|
||||
|
||||
|
|
|
@ -1,11 +1,5 @@
|
|||
describe("test", function() {
|
||||
|
||||
// Mocha usually waits for the tests for 2 seconds before considering them wrong
|
||||
|
||||
this.timeout(200000); // With this code we increase this - in this case to 200,000 milliseconds
|
||||
|
||||
// This is because of the "alert" function, because if you delay pressing the "OK" button the tests will not pass!
|
||||
|
||||
before(() => alert("Testing started – before all tests"));
|
||||
after(() => alert("Testing finished – after all tests"));
|
||||
|
||||
|
|
|
@ -1,92 +1,54 @@
|
|||
|
||||
# Polyfills and transpilers
|
||||
# Polyfills
|
||||
|
||||
The JavaScript language steadily evolves. New proposals to the language appear regularly, they are analyzed and, if considered worthy, are appended to the list at <https://tc39.github.io/ecma262/> and then progress to the [specification](http://www.ecma-international.org/publications/standards/Ecma-262.htm).
|
||||
|
||||
Teams behind JavaScript engines have their own ideas about what to implement first. They may decide to implement proposals that are in draft and postpone things that are already in the spec, because they are less interesting or just harder to do.
|
||||
|
||||
So it's quite common for an engine to implement only part of the standard.
|
||||
So it's quite common for an engine to implement only the part of the standard.
|
||||
|
||||
A good page to see the current state of support for language features is <https://kangax.github.io/compat-table/es6/> (it's big, we have a lot to study yet).
|
||||
|
||||
As programmers, we'd like to use most recent features. The more good stuff - the better!
|
||||
## Babel
|
||||
|
||||
On the other hand, how to make our modern code work on older engines that don't understand recent features yet?
|
||||
When we use modern features of the language, some engines may fail to support such code. Just as said, not all features are implemented everywhere.
|
||||
|
||||
There are two tools for that:
|
||||
Here Babel comes to the rescue.
|
||||
|
||||
1. Transpilers.
|
||||
2. Polyfills.
|
||||
[Babel](https://babeljs.io) is a [transpiler](https://en.wikipedia.org/wiki/Source-to-source_compiler). It rewrites modern JavaScript code into the previous standard.
|
||||
|
||||
Here, in this chapter, our purpose is to get the gist of how they work, and their place in web development.
|
||||
Actually, there are two parts in Babel:
|
||||
|
||||
## Transpilers
|
||||
1. First, the transpiler program, which rewrites the code. The developer runs it on their own computer. It rewrites the code into the older standard. And then the code is delivered to the website for users. Modern project build systems like [webpack](http://webpack.github.io/) provide means to run transpiler automatically on every code change, so that it's very easy to integrate into development process.
|
||||
|
||||
A [transpiler](https://en.wikipedia.org/wiki/Source-to-source_compiler) is a special piece of software that translates source code to another source code. It can parse ("read and understand") modern code and rewrite it using older syntax constructs, so that it'll also work in outdated engines.
|
||||
2. Second, the polyfill.
|
||||
|
||||
E.g. JavaScript before year 2020 didn't have the "nullish coalescing operator" `??`. So, if a visitor uses an outdated browser, it may fail to understand the code like `height = height ?? 100`.
|
||||
New language features may include new built-in functions and syntax constructs.
|
||||
The transpiler rewrites the code, transforming syntax constructs into older ones. But as for new built-in functions, we need to implement them. JavaScript is a highly dynamic language, scripts may add/modify any functions, so that they behave according to the modern standard.
|
||||
|
||||
A transpiler would analyze our code and rewrite `height ?? 100` into `(height !== undefined && height !== null) ? height : 100`.
|
||||
A script that updates/adds new functions is called "polyfill". It "fills in" the gap and adds missing implementations.
|
||||
|
||||
```js
|
||||
// before running the transpiler
|
||||
height = height ?? 100;
|
||||
Two interesting polyfills are:
|
||||
- [core js](https://github.com/zloirock/core-js) that supports a lot, allows to include only needed features.
|
||||
- [polyfill.io](http://polyfill.io) service that provides a script with polyfills, depending on the features and user's browser.
|
||||
|
||||
// after running the transpiler
|
||||
height = (height !== undefined && height !== null) ? height : 100;
|
||||
So, if we're going to use modern language features, a transpiler and a polyfill are necessary.
|
||||
|
||||
## Examples in the tutorial
|
||||
|
||||
|
||||
````online
|
||||
Most examples are runnable at-place, like this:
|
||||
|
||||
```js run
|
||||
alert('Press the "Play" button in the upper-right corner to run');
|
||||
```
|
||||
|
||||
Now the rewritten code is suitable for older JavaScript engines.
|
||||
Examples that use modern JS will work only if your browser supports it.
|
||||
````
|
||||
|
||||
Usually, a developer runs the transpiler on their own computer, and then deploys the transpiled code to the server.
|
||||
|
||||
Speaking of names, [Babel](https://babeljs.io) is one of the most prominent transpilers out there.
|
||||
|
||||
Modern project build systems, such as [webpack](https://webpack.js.org/), provide a means to run a transpiler automatically on every code change, so it's very easy to integrate into the development process.
|
||||
|
||||
## Polyfills
|
||||
|
||||
New language features may include not only syntax constructs and operators, but also built-in functions.
|
||||
|
||||
For example, `Math.trunc(n)` is a function that "cuts off" the decimal part of a number, e.g `Math.trunc(1.23)` returns `1`.
|
||||
|
||||
In some (very outdated) JavaScript engines, there's no `Math.trunc`, so such code will fail.
|
||||
|
||||
As we're talking about new functions, not syntax changes, there's no need to transpile anything here. We just need to declare the missing function.
|
||||
|
||||
A script that updates/adds new functions is called "polyfill". It "fills in" the gap and adds missing implementations.
|
||||
|
||||
For this particular case, the polyfill for `Math.trunc` is a script that implements it, like this:
|
||||
|
||||
```js
|
||||
if (!Math.trunc) { // if no such function
|
||||
// implement it
|
||||
Math.trunc = function(number) {
|
||||
// Math.ceil and Math.floor exist even in ancient JavaScript engines
|
||||
// they are covered later in the tutorial
|
||||
return number < 0 ? Math.ceil(number) : Math.floor(number);
|
||||
};
|
||||
}
|
||||
```offline
|
||||
As you're reading the offline version, in PDF examples are not runnable. In EPUB some of them can run.
|
||||
```
|
||||
|
||||
JavaScript is a highly dynamic language. Scripts may add/modify any function, even built-in ones.
|
||||
|
||||
Two interesting polyfill libraries are:
|
||||
- [core js](https://github.com/zloirock/core-js) that supports a lot, allows to include only needed features.
|
||||
- [polyfill.io](http://polyfill.io) service that provides a script with polyfills, depending on the features and user's browser.
|
||||
|
||||
|
||||
## Summary
|
||||
|
||||
In this chapter we'd like to motivate you to study modern and even "bleeding-edge" language features, even if they aren't yet well-supported by JavaScript engines.
|
||||
|
||||
Just don't forget to use a transpiler (if using modern syntax or operators) and polyfills (to add functions that may be missing). They'll ensure that the code works.
|
||||
|
||||
For example, later when you're familiar with JavaScript, you can setup a code build system based on [webpack](https://webpack.js.org/) with the [babel-loader](https://github.com/babel/babel-loader) plugin.
|
||||
|
||||
Good resources that show the current state of support for various features:
|
||||
- <https://kangax.github.io/compat-table/es6/> - for pure JavaScript.
|
||||
- <https://caniuse.com/> - for browser-related functions.
|
||||
|
||||
P.S. Google Chrome is usually the most up-to-date with language features, try it if a tutorial demo fails. Most tutorial demos work with any modern browser though.
|
||||
|
||||
Google Chrome is usually the most up-to-date with language features, good to run bleeding-edge demos without any transpilers, but other modern browsers also work fine.
|
||||
|
|
19
1-js/04-object-basics/01-object/4-const-object/solution.md
Normal file
|
@ -0,0 +1,19 @@
|
|||
Sure, it works, no problem.
|
||||
|
||||
The `const` only protects the variable itself from changing.
|
||||
|
||||
In other words, `user` stores a reference to the object. And it can't be changed. But the content of the object can.
|
||||
|
||||
```js run
|
||||
const user = {
|
||||
name: "John"
|
||||
};
|
||||
|
||||
*!*
|
||||
// works
|
||||
user.name = "Pete";
|
||||
*/!*
|
||||
|
||||
// error
|
||||
user = 123;
|
||||
```
|
18
1-js/04-object-basics/01-object/4-const-object/task.md
Normal file
|
@ -0,0 +1,18 @@
|
|||
importance: 5
|
||||
|
||||
---
|
||||
|
||||
# Constant objects?
|
||||
|
||||
Is it possible to change an object declared with `const`? What do you think?
|
||||
|
||||
```js
|
||||
const user = {
|
||||
name: "John"
|
||||
};
|
||||
|
||||
*!*
|
||||
// does it work?
|
||||
user.name = "Pete";
|
||||
*/!*
|
||||
```
|
|
@ -2,9 +2,9 @@ importance: 3
|
|||
|
||||
---
|
||||
|
||||
# Multiply numeric property values by 2
|
||||
# Multiply numeric properties by 2
|
||||
|
||||
Create a function `multiplyNumeric(obj)` that multiplies all numeric property values of `obj` by `2`.
|
||||
Create a function `multiplyNumeric(obj)` that multiplies all numeric properties of `obj` by `2`.
|
||||
|
||||
For instance:
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
# Objects
|
||||
|
||||
As we know from the chapter <info:types>, there are eight data types in JavaScript. Seven of them are called "primitive", because their values contain only a single thing (be it a string or a number or whatever).
|
||||
As we know from the chapter <info:types>, there are seven data types in JavaScript. Six of them are called "primitive", because their values contain only a single thing (be it a string or a number or whatever).
|
||||
|
||||
In contrast, objects are used to store keyed collections of various data and more complex entities. In JavaScript, objects penetrate almost every aspect of the language. So we must understand them first before going in-depth anywhere else.
|
||||
|
||||
|
@ -44,7 +44,7 @@ The resulting `user` object can be imagined as a cabinet with two signed files l
|
|||
|
||||

|
||||
|
||||
We can add, remove and read files from it at any time.
|
||||
We can add, remove and read files from it any time.
|
||||
|
||||
Property values are accessible using the dot notation:
|
||||
|
||||
|
@ -62,7 +62,7 @@ user.isAdmin = true;
|
|||
|
||||

|
||||
|
||||
To remove a property, we can use the `delete` operator:
|
||||
To remove a property, we can use `delete` operator:
|
||||
|
||||
```js
|
||||
delete user.age;
|
||||
|
@ -101,9 +101,7 @@ For multiword properties, the dot access doesn't work:
|
|||
user.likes birds = true
|
||||
```
|
||||
|
||||
JavaScript doesn't understand that. It thinks that we address `user.likes`, and then gives a syntax error when comes across unexpected `birds`.
|
||||
|
||||
The dot requires the key to be a valid variable identifier. That implies: contains no spaces, doesn't start with a digit and doesn't include special characters (`$` and `_` are allowed).
|
||||
That's because the dot requires the key to be a valid variable identifier. That is: no spaces and other limitations.
|
||||
|
||||
There's an alternative "square bracket notation" that works with any string:
|
||||
|
||||
|
@ -161,7 +159,7 @@ alert( user.key ) // undefined
|
|||
|
||||
### Computed properties
|
||||
|
||||
We can use square brackets in an object literal, when creating an object. That's called *computed properties*.
|
||||
We can use square brackets in an object literal. That's called *computed properties*.
|
||||
|
||||
For instance:
|
||||
|
||||
|
@ -201,13 +199,50 @@ let bag = {
|
|||
};
|
||||
```
|
||||
|
||||
Square brackets are much more powerful than dot notation. They allow any property names and variables. But they are also more cumbersome to write.
|
||||
Square brackets are much more powerful than the dot notation. They allow any property names and variables. But they are also more cumbersome to write.
|
||||
|
||||
So most of the time, when property names are known and simple, the dot is used. And if we need something more complex, then we switch to square brackets.
|
||||
|
||||
|
||||
|
||||
````smart header="Reserved words are allowed as property names"
|
||||
A variable cannot have a name equal to one of language-reserved words like "for", "let", "return" etc.
|
||||
|
||||
But for an object property, there's no such restriction. Any name is fine:
|
||||
|
||||
```js run
|
||||
let obj = {
|
||||
for: 1,
|
||||
let: 2,
|
||||
return: 3
|
||||
};
|
||||
|
||||
alert( obj.for + obj.let + obj.return ); // 6
|
||||
```
|
||||
|
||||
Basically, any name is allowed, but there's a special one: `"__proto__"` that gets special treatment for historical reasons. For instance, we can't set it to a non-object value:
|
||||
|
||||
```js run
|
||||
let obj = {};
|
||||
obj.__proto__ = 5;
|
||||
alert(obj.__proto__); // [object Object], didn't work as intended
|
||||
```
|
||||
|
||||
As we see from the code, the assignment to a primitive `5` is ignored.
|
||||
|
||||
That can become a source of bugs and even vulnerabilities if we intend to store arbitrary key-value pairs in an object, and allow a visitor to specify the keys.
|
||||
|
||||
In that case the visitor may choose `__proto__` as the key, and the assignment logic will be ruined (as shown above).
|
||||
|
||||
There is a way to make objects treat `__proto__` as a regular property, which we'll cover later, but first we need to know more about objects.
|
||||
|
||||
There's also another data structure [Map](info:map-set), that we'll learn in the chapter <info:map-set>, which supports arbitrary keys.
|
||||
````
|
||||
|
||||
|
||||
## Property value shorthand
|
||||
|
||||
In real code, we often use existing variables as values for property names.
|
||||
In real code we often use existing variables as values for property names.
|
||||
|
||||
For instance:
|
||||
|
||||
|
@ -215,7 +250,7 @@ For instance:
|
|||
function makeUser(name, age) {
|
||||
return {
|
||||
name: name,
|
||||
age: age,
|
||||
age: age
|
||||
// ...other properties
|
||||
};
|
||||
}
|
||||
|
@ -233,7 +268,7 @@ function makeUser(name, age) {
|
|||
*!*
|
||||
return {
|
||||
name, // same as name: name
|
||||
age, // same as age: age
|
||||
age // same as age: age
|
||||
// ...
|
||||
};
|
||||
*/!*
|
||||
|
@ -249,57 +284,9 @@ let user = {
|
|||
};
|
||||
```
|
||||
|
||||
## Existence check
|
||||
|
||||
## Property names limitations
|
||||
|
||||
As we already know, a variable cannot have a name equal to one of the language-reserved words like "for", "let", "return" etc.
|
||||
|
||||
But for an object property, there's no such restriction:
|
||||
|
||||
```js run
|
||||
// these properties are all right
|
||||
let obj = {
|
||||
for: 1,
|
||||
let: 2,
|
||||
return: 3
|
||||
};
|
||||
|
||||
alert( obj.for + obj.let + obj.return ); // 6
|
||||
```
|
||||
|
||||
In short, there are no limitations on property names. They can be any strings or symbols (a special type for identifiers, to be covered later).
|
||||
|
||||
Other types are automatically converted to strings.
|
||||
|
||||
For instance, a number `0` becomes a string `"0"` when used as a property key:
|
||||
|
||||
```js run
|
||||
let obj = {
|
||||
0: "test" // same as "0": "test"
|
||||
};
|
||||
|
||||
// both alerts access the same property (the number 0 is converted to string "0")
|
||||
alert( obj["0"] ); // test
|
||||
alert( obj[0] ); // test (same property)
|
||||
```
|
||||
|
||||
There's a minor gotcha with a special property named `__proto__`. We can't set it to a non-object value:
|
||||
|
||||
```js run
|
||||
let obj = {};
|
||||
obj.__proto__ = 5; // assign a number
|
||||
alert(obj.__proto__); // [object Object] - the value is an object, didn't work as intended
|
||||
```
|
||||
|
||||
As we see from the code, the assignment to a primitive `5` is ignored.
|
||||
|
||||
We'll cover the special nature of `__proto__` in [subsequent chapters](info:prototype-inheritance), and suggest the [ways to fix](info:prototype-methods) such behavior.
|
||||
|
||||
## Property existence test, "in" operator
|
||||
|
||||
A notable feature of objects in JavaScript, compared to many other languages, is that it's possible to access any property. There will be no error if the property doesn't exist!
|
||||
|
||||
Reading a non-existing property just returns `undefined`. So we can easily test whether the property exists:
|
||||
A notable objects feature is that it's possible to access any property. There will be no error if the property doesn't exist! Accessing a non-existing property just returns `undefined`. It provides a very common way to test whether the property exists -- to get it and compare vs undefined:
|
||||
|
||||
```js run
|
||||
let user = {};
|
||||
|
@ -307,7 +294,7 @@ let user = {};
|
|||
alert( user.noSuchProperty === undefined ); // true means "no such property"
|
||||
```
|
||||
|
||||
There's also a special operator `"in"` for that.
|
||||
There also exists a special operator `"in"` to check for the existence of a property.
|
||||
|
||||
The syntax is:
|
||||
```js
|
||||
|
@ -325,18 +312,17 @@ alert( "blabla" in user ); // false, user.blabla doesn't exist
|
|||
|
||||
Please note that on the left side of `in` there must be a *property name*. That's usually a quoted string.
|
||||
|
||||
If we omit quotes, that means a variable should contain the actual name to be tested. For instance:
|
||||
If we omit quotes, that would mean a variable containing the actual name will be tested. For instance:
|
||||
|
||||
```js run
|
||||
let user = { age: 30 };
|
||||
|
||||
let key = "age";
|
||||
alert( *!*key*/!* in user ); // true, property "age" exists
|
||||
alert( *!*key*/!* in user ); // true, takes the name from key and checks for such property
|
||||
```
|
||||
|
||||
Why does the `in` operator exist? Isn't it enough to compare against `undefined`?
|
||||
|
||||
Well, most of the time the comparison with `undefined` works fine. But there's a special case when it fails, but `"in"` works correctly.
|
||||
````smart header="Using \"in\" for properties that store `undefined`"
|
||||
Usually, the strict comparison `"=== undefined"` check the property existence just fine. But there's a special case when it fails, but `"in"` works correctly.
|
||||
|
||||
It's when an object property exists, but stores `undefined`:
|
||||
|
||||
|
@ -350,12 +336,13 @@ alert( obj.test ); // it's undefined, so - no such property?
|
|||
alert( "test" in obj ); // true, the property does exist!
|
||||
```
|
||||
|
||||
|
||||
In the code above, the property `obj.test` technically exists. So the `in` operator works right.
|
||||
|
||||
Situations like this happen very rarely, because `undefined` should not be explicitly assigned. We mostly use `null` for "unknown" or "empty" values. So the `in` operator is an exotic guest in the code.
|
||||
Situations like this happen very rarely, because `undefined` is usually not assigned. We mostly use `null` for "unknown" or "empty" values. So the `in` operator is an exotic guest in the code.
|
||||
````
|
||||
|
||||
|
||||
## The "for..in" loop [#forin]
|
||||
## The "for..in" loop
|
||||
|
||||
To walk over all keys of an object, there exists a special form of the loop: `for..in`. This is a completely different thing from the `for(;;)` construct that we studied before.
|
||||
|
||||
|
@ -388,6 +375,7 @@ Note that all "for" constructs allow us to declare the looping variable inside t
|
|||
|
||||
Also, we could use another variable name here instead of `key`. For instance, `"for (let prop in obj)"` is also widely used.
|
||||
|
||||
|
||||
### Ordered like an object
|
||||
|
||||
Are objects ordered? In other words, if we loop over an object, do we get all properties in the same order they were added? Can we rely on this?
|
||||
|
@ -412,7 +400,7 @@ for (let code in codes) {
|
|||
*/!*
|
||||
```
|
||||
|
||||
The object may be used to suggest a list of options to the user. If we're making a site mainly for a German audience then we probably want `49` to be the first.
|
||||
The object may be used to suggest a list of options to the user. If we're making a site mainly for German audience then we probably want `49` to be the first.
|
||||
|
||||
But if we run the code, we see a totally different picture:
|
||||
|
||||
|
@ -424,10 +412,9 @@ The phone codes go in the ascending sorted order, because they are integers. So
|
|||
````smart header="Integer properties? What's that?"
|
||||
The "integer property" term here means a string that can be converted to-and-from an integer without a change.
|
||||
|
||||
So, `"49"` is an integer property name, because when it's transformed to an integer number and back, it's still the same. But `"+49"` and `"1.2"` are not:
|
||||
So, "49" is an integer property name, because when it's transformed to an integer number and back, it's still the same. But "+49" and "1.2" are not:
|
||||
|
||||
```js run
|
||||
// Number(...) explicitly converts to a number
|
||||
// Math.trunc is a built-in function that removes the decimal part
|
||||
alert( String(Math.trunc(Number("49"))) ); // "49", same, integer property
|
||||
alert( String(Math.trunc(Number("+49"))) ); // "49", not same "+49" ⇒ not integer property
|
||||
|
@ -472,6 +459,262 @@ for (let code in codes) {
|
|||
|
||||
Now it works as intended.
|
||||
|
||||
## Copying by reference
|
||||
|
||||
One of the fundamental differences of objects vs primitives is that they are stored and copied "by reference".
|
||||
|
||||
Primitive values: strings, numbers, booleans -- are assigned/copied "as a whole value".
|
||||
|
||||
For instance:
|
||||
|
||||
```js
|
||||
let message = "Hello!";
|
||||
let phrase = message;
|
||||
```
|
||||
|
||||
As a result we have two independent variables, each one is storing the string `"Hello!"`.
|
||||
|
||||

|
||||
|
||||
Objects are not like that.
|
||||
|
||||
**A variable stores not the object itself, but its "address in memory", in other words "a reference" to it.**
|
||||
|
||||
Here's the picture for the object:
|
||||
|
||||
```js
|
||||
let user = {
|
||||
name: "John"
|
||||
};
|
||||
```
|
||||
|
||||

|
||||
|
||||
Here, the object is stored somewhere in memory. And the variable `user` has a "reference" to it.
|
||||
|
||||
**When an object variable is copied -- the reference is copied, the object is not duplicated.**
|
||||
|
||||
If we imagine an object as a cabinet, then a variable is a key to it. Copying a variable duplicates the key, but not the cabinet itself.
|
||||
|
||||
For instance:
|
||||
|
||||
```js no-beautify
|
||||
let user = { name: "John" };
|
||||
|
||||
let admin = user; // copy the reference
|
||||
```
|
||||
|
||||
Now we have two variables, each one with the reference to the same object:
|
||||
|
||||

|
||||
|
||||
We can use any variable to access the cabinet and modify its contents:
|
||||
|
||||
```js run
|
||||
let user = { name: 'John' };
|
||||
|
||||
let admin = user;
|
||||
|
||||
*!*
|
||||
admin.name = 'Pete'; // changed by the "admin" reference
|
||||
*/!*
|
||||
|
||||
alert(*!*user.name*/!*); // 'Pete', changes are seen from the "user" reference
|
||||
```
|
||||
|
||||
The example above demonstrates that there is only one object. As if we had a cabinet with two keys and used one of them (`admin`) to get into it. Then, if we later use the other key (`user`) we would see changes.
|
||||
|
||||
### Comparison by reference
|
||||
|
||||
The equality `==` and strict equality `===` operators for objects work exactly the same.
|
||||
|
||||
**Two objects are equal only if they are the same object.**
|
||||
|
||||
For instance, if two variables reference the same object, they are equal:
|
||||
|
||||
```js run
|
||||
let a = {};
|
||||
let b = a; // copy the reference
|
||||
|
||||
alert( a == b ); // true, both variables reference the same object
|
||||
alert( a === b ); // true
|
||||
```
|
||||
|
||||
And here two independent objects are not equal, even though both are empty:
|
||||
|
||||
```js run
|
||||
let a = {};
|
||||
let b = {}; // two independent objects
|
||||
|
||||
alert( a == b ); // false
|
||||
```
|
||||
|
||||
For comparisons like `obj1 > obj2` or for a comparison against a primitive `obj == 5`, objects are converted to primitives. We'll study how object conversions work very soon, but to tell the truth, such comparisons are necessary very rarely and usually are a result of a coding mistake.
|
||||
|
||||
### Const object
|
||||
|
||||
An object declared as `const` *can* be changed.
|
||||
|
||||
For instance:
|
||||
|
||||
```js run
|
||||
const user = {
|
||||
name: "John"
|
||||
};
|
||||
|
||||
*!*
|
||||
user.age = 25; // (*)
|
||||
*/!*
|
||||
|
||||
alert(user.age); // 25
|
||||
```
|
||||
|
||||
It might seem that the line `(*)` would cause an error, but no, there's totally no problem. That's because `const` fixes only value of `user` itself. And here `user` stores the reference to the same object all the time. The line `(*)` goes *inside* the object, it doesn't reassign `user`.
|
||||
|
||||
The `const` would give an error if we try to set `user` to something else, for instance:
|
||||
|
||||
```js run
|
||||
const user = {
|
||||
name: "John"
|
||||
};
|
||||
|
||||
*!*
|
||||
// Error (can't reassign user)
|
||||
*/!*
|
||||
user = {
|
||||
name: "Pete"
|
||||
};
|
||||
```
|
||||
|
||||
...But what if we want to make constant object properties? So that `user.age = 25` would give an error. That's possible too. We'll cover it in the chapter <info:property-descriptors>.
|
||||
|
||||
## Cloning and merging, Object.assign
|
||||
|
||||
So, copying an object variable creates one more reference to the same object.
|
||||
|
||||
But what if we need to duplicate an object? Create an independent copy, a clone?
|
||||
|
||||
That's also doable, but a little bit more difficult, because there's no built-in method for that in JavaScript. Actually, that's rarely needed. Copying by reference is good most of the time.
|
||||
|
||||
But if we really want that, then we need to create a new object and replicate the structure of the existing one by iterating over its properties and copying them on the primitive level.
|
||||
|
||||
Like this:
|
||||
|
||||
```js run
|
||||
let user = {
|
||||
name: "John",
|
||||
age: 30
|
||||
};
|
||||
|
||||
*!*
|
||||
let clone = {}; // the new empty object
|
||||
|
||||
// let's copy all user properties into it
|
||||
for (let key in user) {
|
||||
clone[key] = user[key];
|
||||
}
|
||||
*/!*
|
||||
|
||||
// now clone is a fully independent clone
|
||||
clone.name = "Pete"; // changed the data in it
|
||||
|
||||
alert( user.name ); // still John in the original object
|
||||
```
|
||||
|
||||
Also we can use the method [Object.assign](mdn:js/Object/assign) for that.
|
||||
|
||||
The syntax is:
|
||||
|
||||
```js
|
||||
Object.assign(dest, [src1, src2, src3...])
|
||||
```
|
||||
|
||||
- Arguments `dest`, and `src1, ..., srcN` (can be as many as needed) are objects.
|
||||
- It copies the properties of all objects `src1, ..., srcN` into `dest`. In other words, properties of all arguments starting from the 2nd are copied into the 1st. Then it returns `dest`.
|
||||
|
||||
For instance, we can use it to merge several objects into one:
|
||||
```js
|
||||
let user = { name: "John" };
|
||||
|
||||
let permissions1 = { canView: true };
|
||||
let permissions2 = { canEdit: true };
|
||||
|
||||
*!*
|
||||
// copies all properties from permissions1 and permissions2 into user
|
||||
Object.assign(user, permissions1, permissions2);
|
||||
*/!*
|
||||
|
||||
// now user = { name: "John", canView: true, canEdit: true }
|
||||
```
|
||||
|
||||
If the receiving object (`user`) already has the same named property, it will be overwritten:
|
||||
|
||||
```js
|
||||
let user = { name: "John" };
|
||||
|
||||
// overwrite name, add isAdmin
|
||||
Object.assign(user, { name: "Pete", isAdmin: true });
|
||||
|
||||
// now user = { name: "Pete", isAdmin: true }
|
||||
```
|
||||
|
||||
We also can use `Object.assign` to replace the loop for simple cloning:
|
||||
|
||||
```js
|
||||
let user = {
|
||||
name: "John",
|
||||
age: 30
|
||||
};
|
||||
|
||||
*!*
|
||||
let clone = Object.assign({}, user);
|
||||
*/!*
|
||||
```
|
||||
|
||||
It copies all properties of `user` into the empty object and returns it. Actually, the same as the loop, but shorter.
|
||||
|
||||
Until now we assumed that all properties of `user` are primitive. But properties can be references to other objects. What to do with them?
|
||||
|
||||
Like this:
|
||||
```js run
|
||||
let user = {
|
||||
name: "John",
|
||||
sizes: {
|
||||
height: 182,
|
||||
width: 50
|
||||
}
|
||||
};
|
||||
|
||||
alert( user.sizes.height ); // 182
|
||||
```
|
||||
|
||||
Now it's not enough to copy `clone.sizes = user.sizes`, because the `user.sizes` is an object, it will be copied by reference. So `clone` and `user` will share the same sizes:
|
||||
|
||||
Like this:
|
||||
```js run
|
||||
let user = {
|
||||
name: "John",
|
||||
sizes: {
|
||||
height: 182,
|
||||
width: 50
|
||||
}
|
||||
};
|
||||
|
||||
let clone = Object.assign({}, user);
|
||||
|
||||
alert( user.sizes === clone.sizes ); // true, same object
|
||||
|
||||
// user and clone share sizes
|
||||
user.sizes.width++; // change a property from one place
|
||||
alert(clone.sizes.width); // 51, see the result from the other one
|
||||
```
|
||||
|
||||
To fix that, we should use the cloning loop that examines each value of `user[key]` and, if it's an object, then replicate its structure as well. That is called a "deep cloning".
|
||||
|
||||
There's a standard algorithm for deep cloning that handles the case above and more complex cases, called the [Structured cloning algorithm](https://html.spec.whatwg.org/multipage/structured-data.html#safe-passing-of-structured-data). In order not to reinvent the wheel, we can use a working implementation of it from the JavaScript library [lodash](https://lodash.com), the method is called [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep).
|
||||
|
||||
|
||||
|
||||
## Summary
|
||||
|
||||
Objects are associative arrays with several special features.
|
||||
|
@ -482,13 +725,17 @@ They store properties (key-value pairs), where:
|
|||
|
||||
To access a property, we can use:
|
||||
- The dot notation: `obj.property`.
|
||||
- Square brackets notation `obj["property"]`. Square brackets allow taking the key from a variable, like `obj[varWithKey]`.
|
||||
- Square brackets notation `obj["property"]`. Square brackets allow to take the key from a variable, like `obj[varWithKey]`.
|
||||
|
||||
Additional operators:
|
||||
- To delete a property: `delete obj.prop`.
|
||||
- To check if a property with the given key exists: `"key" in obj`.
|
||||
- To iterate over an object: `for (let key in obj)` loop.
|
||||
|
||||
Objects are assigned and copied by reference. In other words, a variable stores not the "object value", but a "reference" (address in memory) for the value. So copying such a variable or passing it as a function argument copies that reference, not the object. All operations via copied references (like adding/removing properties) are performed on the same single object.
|
||||
|
||||
To make a "real copy" (a clone) we can use `Object.assign` or [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep).
|
||||
|
||||
What we've studied in this chapter is called a "plain object", or just `Object`.
|
||||
|
||||
There are many other kinds of objects in JavaScript:
|
||||
|
|
|
@ -1 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="248" height="173" viewBox="0 0 248 173"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="combined" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="object-user-delete.svg"><path fill="#FFF" d="M0 0h248v173H0z"/><path id="Rectangle-4-Copy" fill="#DBAF88" stroke="#DBAF88" stroke-width="4" d="M225.063 101l16.667 20H111.27l16.667-20h97.126z" opacity=".5"/><g id="Group-2" transform="translate(141 8)"><g id="Group"><path id="Rectangle-7" fill="#FFF" d="M0 0h32v110H0z"/><path id="Combined-Shape" fill="#DBAF88" d="M32 0v110H0V0h32zM16.5 88.71c-4.142 0-7.5 2.978-7.5 6.653 0 3.674 3.358 6.653 7.5 6.653 4.142 0 7.5-2.979 7.5-6.653 0-3.675-3.358-6.653-7.5-6.653zM28 4.435H4v77.178h24V4.435z"/></g><text id="name" fill="#C06334" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold" transform="rotate(-90 15.5 42.859)"><tspan x="-1.3" y="47.359">name</tspan></text></g><g id="Group-2-Copy-2" transform="translate(179 8)"><g id="Group"><path id="Rectangle-7" fill="#FFF" d="M0 0h32v110H0z"/><path id="Combined-Shape" fill="#DBAF88" d="M32 0v110H0V0h32zM16.5 88.71c-4.142 0-7.5 2.978-7.5 6.653 0 3.674 3.358 6.653 7.5 6.653 4.142 0 7.5-2.979 7.5-6.653 0-3.675-3.358-6.653-7.5-6.653zM28 4.435H4v77.178h24V4.435z"/></g><text id="isAdmin" fill="#C06334" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold" transform="rotate(-90 14.5 41.653)"><tspan x="-14.9" y="46.153">isAdmin</tspan></text></g><path id="Rectangle-4" fill="#FBF2EC" stroke="#DBAF88" stroke-width="4" d="M109 115h135v50H109z"/><path id="Rectangle-8" stroke="#DBAF88" stroke-width="3" d="M164.5 135.5h25v10h-25z"/><text id="user" fill="#C06334" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="9" y="145">user</tspan></text><path id="Line-8" fill="#C06334" fill-rule="nonzero" d="M75.5 131l19 9.5-19 9.5v-8H49v-3h26.5v-8z"/></g></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="248" height="173" viewBox="0 0 248 173"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="combined" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="object-user-delete.svg"><path fill="#FFF" d="M0 0h248v173H0z"/><path id="Rectangle-4-Copy" fill="#D1C4B1" stroke="#D1C4B1" stroke-width="4" d="M127.937 101l-16.667 20h130.46l-16.667-20h-97.126z" opacity=".5"/><g id="Group-2" transform="translate(141 8)"><g id="Group"><path id="Rectangle-7" fill="#FFF" d="M0 0h32v110H0z"/><path id="Combined-Shape" fill="#E8C48F" d="M32 0v110H0V0h32zM16.5 88.71c-4.142 0-7.5 2.978-7.5 6.653 0 3.674 3.358 6.653 7.5 6.653 4.142 0 7.5-2.979 7.5-6.653 0-3.675-3.358-6.653-7.5-6.653zM28 4.435H4v77.178h24V4.435z"/></g><text id="name" fill="#EE6B47" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold" transform="rotate(-90 15.5 42.859)"><tspan x="-1.3" y="47.359">name</tspan></text></g><g id="Group-2-Copy-2" transform="translate(179 8)"><g id="Group"><path id="Rectangle-7" fill="#FFF" d="M0 0h32v110H0z"/><path id="Combined-Shape" fill="#E8C48F" d="M32 0v110H0V0h32zM16.5 88.71c-4.142 0-7.5 2.978-7.5 6.653 0 3.674 3.358 6.653 7.5 6.653 4.142 0 7.5-2.979 7.5-6.653 0-3.675-3.358-6.653-7.5-6.653zM28 4.435H4v77.178h24V4.435z"/></g><text id="isAdmin" fill="#EE6B47" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold" transform="rotate(-90 14.5 41.653)"><tspan x="-14.9" y="46.153">isAdmin</tspan></text></g><path id="Rectangle-4" fill="#FFF9EB" stroke="#BCA68E" stroke-width="4" d="M109 115h135v50H109z"/><path id="Rectangle-8" stroke="#BCA68E" stroke-width="3" d="M164.5 135.5h25v10h-25z"/><text id="user" fill="#EE6B47" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="9" y="145">user</tspan></text><path id="Line-8" fill="#EE6B47" fill-rule="nonzero" d="M75.5 131l19 9.5-19 9.5v-8H49v-3h26.5v-8z"/></g></g></svg>
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
|
@ -1 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="248" height="92" viewBox="0 0 248 92"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="combined" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="object-user-empty.svg"><path fill="#FFF" d="M0 0h248v92H0z"/><path id="Rectangle-4-Copy" fill="#DBAF88" stroke="#DBAF88" stroke-width="4" d="M221.063 12l16.667 20H107.27l16.667-20h97.126z" opacity=".5"/><path id="Rectangle-4" fill="#FBF2EC" stroke="#DBAF88" stroke-width="4" d="M105 26h135v50H105z"/><path id="Rectangle-8" stroke="#DBAF88" stroke-width="3" d="M160.5 46.5h25v10h-25z"/><text id="empty" fill="#7E7C7B" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="151" y="22">empty</tspan></text><text id="user" fill="#C06334" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="4" y="56">user</tspan></text><path id="Line-8" fill="#C06334" fill-rule="nonzero" d="M70.5 42l19 9.5-19 9.5v-8H44v-3h26.5v-8z"/></g></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="248" height="92" viewBox="0 0 248 92"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="combined" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="object-user-empty.svg"><path fill="#FFF" d="M0 0h248v92H0z"/><path id="Rectangle-4-Copy" fill="#D1C4B1" stroke="#D1C4B1" stroke-width="4" d="M123.937 12L107.27 32h130.46l-16.667-20h-97.126z" opacity=".5"/><path id="Rectangle-4" fill="#FFF9EB" stroke="#BCA68E" stroke-width="4" d="M105 26h135v50H105z"/><path id="Rectangle-8" stroke="#BCA68E" stroke-width="3" d="M160.5 46.5h25v10h-25z"/><text id="empty" fill="#9B9B9B" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="151" y="22">empty</tspan></text><text id="user" fill="#EE6B47" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="4" y="56">user</tspan></text><path id="Line-8" fill="#EE6B47" fill-rule="nonzero" d="M70.5 42l19 9.5-19 9.5v-8H44v-3h26.5v-8z"/></g></g></svg>
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
@ -1 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="248" height="173" viewBox="0 0 248 173"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="combined" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="object-user-isadmin.svg"><path fill="#FFF" d="M0 0h248v173H0z"/><path id="Rectangle-4-Copy" fill="#DBAF88" stroke="#DBAF88" stroke-width="4" d="M225.063 101l16.667 20H111.27l16.667-20h97.126z" opacity=".5"/><g id="Group-2" transform="translate(125 8)"><g id="Group"><path id="Rectangle-7" fill="#FFF" d="M0 0h32v110H0z"/><path id="Combined-Shape" fill="#DBAF88" d="M32 0v110H0V0h32zM16.5 88.71c-4.142 0-7.5 2.978-7.5 6.653 0 3.674 3.358 6.653 7.5 6.653 4.142 0 7.5-2.979 7.5-6.653 0-3.675-3.358-6.653-7.5-6.653zM28 4.435H4v77.178h24V4.435z"/></g><text id="name" fill="#C06334" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold" transform="rotate(-90 15.5 42.859)"><tspan x="-1.3" y="47.359">name</tspan></text></g><g id="Group-2-Copy" transform="translate(162 8)"><g id="Group"><path id="Rectangle-7" fill="#FFF" d="M0 0h32v110H0z"/><path id="Combined-Shape" fill="#DBAF88" d="M32 0v110H0V0h32zM16.5 88.71c-4.142 0-7.5 2.978-7.5 6.653 0 3.674 3.358 6.653 7.5 6.653 4.142 0 7.5-2.979 7.5-6.653 0-3.675-3.358-6.653-7.5-6.653zM28 4.435H4v77.178h24V4.435z"/></g><text id="age" fill="#C06334" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold" transform="rotate(-90 14.5 41.653)"><tspan x="1.9" y="46.153">age</tspan></text></g><g id="Group-2-Copy-2" transform="translate(199 8)"><g id="Group"><path id="Rectangle-7" fill="#FFF" d="M0 0h32v110H0z"/><path id="Combined-Shape" fill="#DBAF88" d="M32 0v110H0V0h32zM16.5 88.71c-4.142 0-7.5 2.978-7.5 6.653 0 3.674 3.358 6.653 7.5 6.653 4.142 0 7.5-2.979 7.5-6.653 0-3.675-3.358-6.653-7.5-6.653zM28 4.435H4v77.178h24V4.435z"/></g><text id="isAdmin" fill="#C06334" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold" transform="rotate(-90 14.5 41.653)"><tspan x="-14.9" y="46.153">isAdmin</tspan></text></g><path id="Rectangle-4" fill="#FBF2EC" stroke="#DBAF88" stroke-width="4" d="M109 115h135v50H109z"/><path id="Rectangle-8" stroke="#DBAF88" stroke-width="3" d="M164.5 135.5h25v10h-25z"/><text id="user" fill="#C06334" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="12" y="145">user</tspan></text><path id="Line-8" fill="#C06334" fill-rule="nonzero" d="M78.5 131l19 9.5-19 9.5v-8H52v-3h26.5v-8z"/></g></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="248" height="173" viewBox="0 0 248 173"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="combined" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="object-user-isadmin.svg"><path fill="#FFF" d="M0 0h248v173H0z"/><path id="Rectangle-4-Copy" fill="#D1C4B1" stroke="#D1C4B1" stroke-width="4" d="M127.937 101l-16.667 20h130.46l-16.667-20h-97.126z" opacity=".5"/><g id="Group-2" transform="translate(125 8)"><g id="Group"><path id="Rectangle-7" fill="#FFF" d="M0 0h32v110H0z"/><path id="Combined-Shape" fill="#E8C48F" d="M32 0v110H0V0h32zM16.5 88.71c-4.142 0-7.5 2.978-7.5 6.653 0 3.674 3.358 6.653 7.5 6.653 4.142 0 7.5-2.979 7.5-6.653 0-3.675-3.358-6.653-7.5-6.653zM28 4.435H4v77.178h24V4.435z"/></g><text id="name" fill="#EE6B47" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold" transform="rotate(-90 15.5 42.859)"><tspan x="-1.3" y="47.359">name</tspan></text></g><g id="Group-2-Copy" transform="translate(162 8)"><g id="Group"><path id="Rectangle-7" fill="#FFF" d="M0 0h32v110H0z"/><path id="Combined-Shape" fill="#E8C48F" d="M32 0v110H0V0h32zM16.5 88.71c-4.142 0-7.5 2.978-7.5 6.653 0 3.674 3.358 6.653 7.5 6.653 4.142 0 7.5-2.979 7.5-6.653 0-3.675-3.358-6.653-7.5-6.653zM28 4.435H4v77.178h24V4.435z"/></g><text id="age" fill="#EE6B47" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold" transform="rotate(-90 14.5 41.653)"><tspan x="1.9" y="46.153">age</tspan></text></g><g id="Group-2-Copy-2" transform="translate(199 8)"><g id="Group"><path id="Rectangle-7" fill="#FFF" d="M0 0h32v110H0z"/><path id="Combined-Shape" fill="#E8C48F" d="M32 0v110H0V0h32zM16.5 88.71c-4.142 0-7.5 2.978-7.5 6.653 0 3.674 3.358 6.653 7.5 6.653 4.142 0 7.5-2.979 7.5-6.653 0-3.675-3.358-6.653-7.5-6.653zM28 4.435H4v77.178h24V4.435z"/></g><text id="isAdmin" fill="#EE6B47" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold" transform="rotate(-90 14.5 41.653)"><tspan x="-14.9" y="46.153">isAdmin</tspan></text></g><path id="Rectangle-4" fill="#FFF9EB" stroke="#BCA68E" stroke-width="4" d="M109 115h135v50H109z"/><path id="Rectangle-8" stroke="#BCA68E" stroke-width="3" d="M164.5 135.5h25v10h-25z"/><text id="user" fill="#EE6B47" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="12" y="145">user</tspan></text><path id="Line-8" fill="#EE6B47" fill-rule="nonzero" d="M78.5 131l19 9.5-19 9.5v-8H52v-3h26.5v-8z"/></g></g></svg>
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |