Compare commits
2 commits
master
...
paroche-pa
Author | SHA1 | Date | |
---|---|---|---|
![]() |
30369a8f8e | ||
![]() |
5b14645cd8 |
1
.gitignore
vendored
|
@ -21,4 +21,3 @@ sftp-config.json
|
|||
Thumbs.db
|
||||
|
||||
|
||||
/svgs
|
|
@ -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 "Chakra" for 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,7 +59,7 @@ 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:
|
||||
|
||||
|
@ -67,17 +67,17 @@ Examples of such restrictions include:
|
|||
|
||||
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,23 @@ 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.
|
||||
- Today, JavaScript has a unique position as the most widely-adopted browser language with full integration in 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,7 +9,7 @@ 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>.
|
||||
|
||||
|
@ -19,9 +19,9 @@ Also, if you're developing for the browser, then there are other specifications
|
|||
|
||||
- **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.
|
||||
|
||||
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.
|
||||
|
||||
## Compatibility tables
|
||||
|
||||
|
@ -29,9 +29,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.
|
||||
|
|
|
@ -8,7 +8,7 @@ To see errors and get a lot of other useful information about scripts, "develope
|
|||
|
||||
Most developers lean towards Chrome or Firefox for development because those browsers have the best developer tools. Other browsers also provide developer tools, sometimes with special features, but are usually playing "catch-up" to Chrome or Firefox. So most developers have a "favorite" browser and switch to others if a problem is browser-specific.
|
||||
|
||||
Developer tools are potent; they have many features. To start, we'll learn how to open them, look at errors, and run JavaScript commands.
|
||||
Developer tools are potent, they have many features. To start, we'll learn how to open them, look at errors, and run JavaScript commands.
|
||||
|
||||
## Google Chrome
|
||||
|
||||
|
|
|
@ -73,7 +73,7 @@ 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:
|
||||
|
||||
|
|
|
@ -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,36 +56,40 @@ 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.
|
||||
|
|
|
@ -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,
|
||||
|
@ -104,7 +103,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;
|
||||
|
||||
|
@ -194,7 +192,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"
|
||||
|
@ -262,6 +260,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 +291,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 |
|
@ -46,15 +46,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,22 +64,11 @@ 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.
|
||||
In JavaScript, the "number" type cannot 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. It's a technical limitation caused by their internal representation.
|
||||
|
||||
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.
|
||||
For most purposes that's quite enough, 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.
|
||||
|
||||
|
@ -226,7 +213,14 @@ The `symbol` type is used to create unique identifiers for objects. We have to m
|
|||
|
||||
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 +251,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.
|
||||
2. The result of `typeof null` is `"object"`. That's an officially recognized error in `typeof` behavior, coming from the 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.
|
||||
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.
|
||||
```
|
||||
|
||||
## 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 <code>±(2<sup>53</sup>-1)</code>.
|
||||
- `bigint` is 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`.
|
||||
- `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,7 +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.
|
||||
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.
|
||||
```
|
||||
|
@ -70,7 +70,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 +130,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)`.
|
||||
|
||||
|
|
|
@ -22,4 +22,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`.
|
||||
|
|
|
@ -63,8 +63,8 @@ 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 ** 2 ); // 2² = 4
|
||||
alert( 2 ** 3 ); // 2³ = 8
|
||||
alert( 2 ** 4 ); // 2⁴ = 16
|
||||
```
|
||||
|
||||
|
@ -194,22 +194,22 @@ Here's an extract from the [precedence table](https://developer.mozilla.org/en-U
|
|||
| Precedence | Name | Sign |
|
||||
|------------|------|------|
|
||||
| ... | ... | ... |
|
||||
| 14 | unary plus | `+` |
|
||||
| 14 | unary negation | `-` |
|
||||
| 13 | exponentiation | `**` |
|
||||
| 12 | multiplication | `*` |
|
||||
| 12 | division | `/` |
|
||||
| 11 | addition | `+` |
|
||||
| 11 | subtraction | `-` |
|
||||
| 17 | unary plus | `+` |
|
||||
| 17 | unary negation | `-` |
|
||||
| 16 | exponentiation | `**` |
|
||||
| 15 | multiplication | `*` |
|
||||
| 15 | 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 `17` 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`.
|
||||
|
||||
|
@ -303,9 +303,9 @@ Such operators have the same precedence as a normal assignment, so they run afte
|
|||
```js run
|
||||
let n = 2;
|
||||
|
||||
n *= 3 + 5; // right part evaluated first, same as n *= 8
|
||||
n *= 3 + 5;
|
||||
|
||||
alert( n ); // 16
|
||||
alert( n ); // 16 (right part evaluated first, same as n *= 8)
|
||||
```
|
||||
|
||||
## Increment/decrement
|
||||
|
@ -437,7 +437,7 @@ 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, 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) chapter on MDN when a need arises.
|
||||
|
||||
## Comma
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ In JavaScript they are written like this:
|
|||
- 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>.
|
||||
- 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.
|
||||
|
||||
|
|
|
@ -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 |
|
@ -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 );
|
||||
```
|
||||
|
||||
|
|
Before Width: | Height: | Size: 6 KiB After Width: | Height: | Size: 6 KiB |
|
@ -123,7 +123,7 @@ This leads to some interesting usage compared to a "pure, classical, boolean-onl
|
|||
|
||||
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.
|
||||
|
||||
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.
|
||||
That 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:
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
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`.
|
||||
As it treats `null` and `undefined` similarly, we'll use a special term here, in this article. We'll say that an expression is "defined" when it's neither `null` nor `undefined`.
|
||||
|
||||
The result of `a ?? b` is:
|
||||
- if `a` is defined, then `a`,
|
||||
|
@ -22,9 +22,9 @@ 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.
|
||||
The common use case for `??` is to provide a default value for a potentially undefined variable.
|
||||
|
||||
For example, here we show `user` if its value isn't `null/undefined`, otherwise `Anonymous`:
|
||||
For example, here we show `user` if defined, otherwise `Anonymous`:
|
||||
|
||||
```js run
|
||||
let user;
|
||||
|
@ -42,9 +42,9 @@ 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.
|
||||
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 enter a value.
|
||||
|
||||
We'd like to display the user name using one of these variables, or show "Anonymous" if all of them are `null/undefined`.
|
||||
We'd like to display the user name using one of these variables, or show "Anonymous" if all of them aren't defined.
|
||||
|
||||
Let's use the `??` operator for that:
|
||||
|
||||
|
@ -106,11 +106,11 @@ In practice, the zero height is often a valid value, that shouldn't be replaced
|
|||
|
||||
## 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).
|
||||
The precedence of the `??` operator is about the same as `||`, just a bit lower. It equals `5` in the [MDN table](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence#Table), while `||` is `6`.
|
||||
|
||||
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:
|
||||
So if we'd like to choose a value with `??` in an expression with other operators, consider adding parentheses:
|
||||
|
||||
```js run
|
||||
let height = null;
|
||||
|
@ -128,7 +128,7 @@ Otherwise, if we omit parentheses, then as `*` has the higher precedence than `?
|
|||
// without parentheses
|
||||
let area = height ?? 100 * width ?? 50;
|
||||
|
||||
// ...works this way (not what we want):
|
||||
// ...works the same as this (probably not what we want):
|
||||
let area = height ?? (100 * width) ?? 50;
|
||||
```
|
||||
|
||||
|
|
|
@ -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
|
||||
```
|
||||
|
@ -331,7 +321,6 @@ 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!
|
||||
|
||||
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,7 +362,6 @@ 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)
|
||||
|
||||
|
@ -382,7 +369,6 @@ label: for (...)
|
|||
```
|
||||
|
||||
A `break` directive must be inside a code block. Technically, any labelled code block will do, e.g.:
|
||||
|
||||
```js
|
||||
label: {
|
||||
// ...
|
||||
|
@ -391,7 +377,7 @@ label: {
|
|||
}
|
||||
```
|
||||
|
||||
...Although, 99.9% of the time `break` is used inside loops, as we've seen in the examples above.
|
||||
...Although, 99.9% of the time `break` used is inside loops, as we've seen in the examples above.
|
||||
|
||||
A `continue` is only possible from inside a loop.
|
||||
````
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -1,3 +1 @@
|
|||
No difference!
|
||||
|
||||
In both cases, `return confirm('Did parents allow you?')` executes exactly when the `if` condition is falsy.
|
||||
No difference.
|
|
@ -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,55 +211,19 @@ 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.
|
||||
|
||||
Nowadays, we can come across them in old scripts.
|
||||
|
||||
For example, an explicit check for `undefined`:
|
||||
|
||||
```js
|
||||
function showMessage(from, text) {
|
||||
*!*
|
||||
if (text === undefined) {
|
||||
text = 'no text given';
|
||||
}
|
||||
*/!*
|
||||
|
||||
alert( from + ": " + text );
|
||||
}
|
||||
```
|
||||
|
||||
...Or using 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
|
||||
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.
|
||||
Sometimes it makes sense to set default values for parameters not in the function declaration, but at a later stage, during its execution.
|
||||
|
||||
We can check if the parameter is passed during the function execution, by comparing it with `undefined`:
|
||||
To check for an omitted parameter, we can compare it with `undefined`:
|
||||
|
||||
```js run
|
||||
function showMessage(text) {
|
||||
// ...
|
||||
|
||||
*!*
|
||||
if (text === undefined) { // if the parameter is missing
|
||||
if (text === undefined) {
|
||||
text = 'empty message';
|
||||
}
|
||||
*/!*
|
||||
|
@ -288,18 +237,18 @@ showMessage(); // empty message
|
|||
...Or we could use the `||` operator:
|
||||
|
||||
```js
|
||||
// if text parameter is omitted or "" is passed, set it to 'empty'
|
||||
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":
|
||||
Modern JavaScript engines support the [nullish coalescing operator](info:nullish-coalescing-operator) `??`, it's better when falsy values, such as `0`, are considered regular:
|
||||
|
||||
```js run
|
||||
// if there's no "count" parameter, show "unknown"
|
||||
function showCount(count) {
|
||||
// if count is undefined or null, show "unknown"
|
||||
alert(count ?? "unknown");
|
||||
}
|
||||
|
||||
|
@ -462,7 +411,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 +477,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:
|
||||
|
||||
|
|
|
@ -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".
|
||||
|
||||
|
|
|
@ -5,10 +5,10 @@ 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:
|
||||
|
||||
|
@ -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,7 +64,7 @@ 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();
|
||||
|
@ -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.
|
||||
|
||||
|
@ -144,7 +144,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/Guide/Expressions_and_Operators#Bitwise) when they are needed.
|
||||
|
||||
Conditional
|
||||
: The only operator with three parameters: `cond ? resultA : resultB`. If `cond` is truthy, returns `resultA`, otherwise `resultB`.
|
||||
|
@ -256,7 +256,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:
|
||||
|
|
|
@ -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 "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 |
|
@ -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.
|
||||
|
||||
|
@ -335,7 +335,7 @@ Here's an example of an `.eslintrc` file:
|
|||
|
||||
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 |
|
@ -143,7 +143,7 @@ Such comments allow us to understand the purpose of the function and use it the
|
|||
|
||||
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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ The JavaScript language steadily evolves. New proposals to the language appear r
|
|||
|
||||
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).
|
||||
|
||||
|
@ -22,7 +22,7 @@ Here, in this chapter, our purpose is to get the gist of how they work, and thei
|
|||
|
||||
## Transpilers
|
||||
|
||||
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.
|
||||
A [transpiler](https://en.wikipedia.org/wiki/Source-to-source_compiler) is a special piece of software that can parse ("read and understand") modern code, and rewrite it using older syntax constructs, so that the result would be the same.
|
||||
|
||||
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`.
|
||||
|
||||
|
@ -40,15 +40,15 @@ Now the rewritten code is suitable for older JavaScript engines.
|
|||
|
||||
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.
|
||||
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.
|
||||
Modern project build systems, such as [webpack](http://webpack.github.io/), provide means to run transpiler automatically on every code change, so it's very easy to integrate into 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`.
|
||||
For example, `Math.trunc(n)` is a function that "cuts off" the decimal part of a number, e.g `Math.trunc(1.23) = 1`.
|
||||
|
||||
In some (very outdated) JavaScript engines, there's no `Math.trunc`, so such code will fail.
|
||||
|
||||
|
@ -69,9 +69,9 @@ if (!Math.trunc) { // if no such function
|
|||
}
|
||||
```
|
||||
|
||||
JavaScript is a highly dynamic language. Scripts may add/modify any function, even built-in ones.
|
||||
JavaScript is a highly dynamic language, scripts may add/modify any functions, even including built-in ones.
|
||||
|
||||
Two interesting polyfill libraries are:
|
||||
Two interesting libraries of 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.
|
||||
|
||||
|
@ -80,9 +80,9 @@ Two interesting polyfill libraries are:
|
|||
|
||||
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.
|
||||
Just don't forget to use transpiler (if using modern syntax or operators) and polyfills (to add functions that may be missing). And 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.
|
||||
For example, later when you're familiar with JavaScript, you can setup a code build system based on [webpack](http://webpack.github.io/) with [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.
|
||||
|
|
|
@ -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;
|
||||
|
@ -201,13 +201,13 @@ 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.
|
||||
|
||||
## 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:
|
||||
|
||||
|
@ -252,7 +252,7 @@ let user = {
|
|||
|
||||
## 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.
|
||||
As we already know, 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:
|
||||
|
||||
|
@ -325,7 +325,7 @@ 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 means a variable, it should contain the actual name to be tested. For instance:
|
||||
|
||||
```js run
|
||||
let user = { age: 30 };
|
||||
|
@ -355,7 +355,7 @@ In the code above, the property `obj.test` technically exists. So the `in` opera
|
|||
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.
|
||||
|
||||
|
||||
## 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.
|
||||
|
||||
|
@ -412,7 +412,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 +424,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
|
||||
|
@ -482,7 +481,7 @@ 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`.
|
||||
|
|
|
@ -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 |
|
@ -1 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="248" height="215" viewBox="0 0 248 215"><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-props.svg"><path fill="#FFF" d="M0 0h248v215H0z"/><path id="Rectangle-4-Copy" fill="#DBAF88" stroke="#DBAF88" stroke-width="4" d="M225.063 143l16.667 20H111.27l16.667-20h97.126z" opacity=".5"/><g id="Group-2" transform="translate(124 3)"><g id="Group"><path id="Rectangle-7" fill="#FFF" d="M0 0h32v157H0z"/><path id="Combined-Shape" fill="#DBAF88" d="M32 0v157H0V0h32zM16.5 126.613c-4.142 0-7.5 4.251-7.5 9.496 0 5.244 3.358 9.496 7.5 9.496 4.142 0 7.5-4.252 7.5-9.496 0-5.245-3.358-9.496-7.5-9.496zM28 6.33H4v110.153h24V6.33z"/></g><text id="name" fill="#C06334" font-family="PTMono-Bold, PT Mono" font-size="16" font-weight="bold" transform="rotate(-90 13.105 60.141)"><tspan x="-6.095" y="65.141">name</tspan></text></g><g id="Group-2-Copy" transform="translate(161 3)"><g id="Group"><path id="Rectangle-7" fill="#FFF" d="M0 0h32v157H0z"/><path id="Combined-Shape" fill="#DBAF88" d="M32 0v157H0V0h32zM16.5 126.613c-4.142 0-7.5 4.251-7.5 9.496 0 5.244 3.358 9.496 7.5 9.496 4.142 0 7.5-4.252 7.5-9.496 0-5.245-3.358-9.496-7.5-9.496zM28 6.33H4v110.153h24V6.33z"/></g><text id="age" fill="#C06334" font-family="PTMono-Bold, PT Mono" font-size="16" font-weight="bold" transform="rotate(-90 13.105 60.141)"><tspan x="-1.295" y="65.141">age</tspan></text></g><g id="Group-2-Copy-2" transform="translate(198 3)"><g id="Group"><path id="Rectangle-7" fill="#FFF" d="M0 0h32v157H0z"/><path id="Combined-Shape" fill="#DBAF88" d="M32 0v157H0V0h32zM16.5 126.613c-4.142 0-7.5 4.251-7.5 9.496 0 5.244 3.358 9.496 7.5 9.496 4.142 0 7.5-4.252 7.5-9.496 0-5.245-3.358-9.496-7.5-9.496zM28 6.33H4v110.153h24V6.33z"/></g><text id="likes-birds" fill="#C06334" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold" transform="rotate(-90 15.504 60.774)"><tspan x="-30.696" y="65.274">likes birds</tspan></text></g><path id="Rectangle-4" fill="#FBF2EC" stroke="#DBAF88" stroke-width="4" d="M109 157h135v50H109z"/><path id="Rectangle-8" stroke="#DBAF88" stroke-width="3" d="M164.5 177.5h25v10h-25z"/><text id="user" fill="#C06334" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="5" y="187">user</tspan></text><path id="Line-8" fill="#C06334" fill-rule="nonzero" d="M71.5 173l19 9.5-19 9.5v-8H45v-3h26.5v-8z"/></g></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="248" height="215" viewBox="0 0 248 215"><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-props.svg"><path fill="#FFF" d="M0 0h248v215H0z"/><path id="Rectangle-4-Copy" fill="#D1C4B1" stroke="#D1C4B1" stroke-width="4" d="M127.937 143l-16.667 20h130.46l-16.667-20h-97.126z" opacity=".5"/><g id="Group-2" transform="translate(124 3)"><g id="Group"><path id="Rectangle-7" fill="#FFF" d="M0 0h32v157H0z"/><path id="Combined-Shape" fill="#E8C48F" d="M32 0v157H0V0h32zM16.5 126.613c-4.142 0-7.5 4.251-7.5 9.496 0 5.244 3.358 9.496 7.5 9.496 4.142 0 7.5-4.252 7.5-9.496 0-5.245-3.358-9.496-7.5-9.496zM28 6.33H4v110.153h24V6.33z"/></g><text id="name" fill="#EE6B47" font-family="PTMono-Bold, PT Mono" font-size="16" font-weight="bold" transform="rotate(-90 15.5 60.141)"><tspan x="-3.7" y="62.746">name</tspan></text></g><g id="Group-2-Copy" transform="translate(161 3)"><g id="Group"><path id="Rectangle-7" fill="#FFF" d="M0 0h32v157H0z"/><path id="Combined-Shape" fill="#E8C48F" d="M32 0v157H0V0h32zM16.5 126.613c-4.142 0-7.5 4.251-7.5 9.496 0 5.244 3.358 9.496 7.5 9.496 4.142 0 7.5-4.252 7.5-9.496 0-5.245-3.358-9.496-7.5-9.496zM28 6.33H4v110.153h24V6.33z"/></g><text id="age" fill="#EE6B47" font-family="PTMono-Bold, PT Mono" font-size="16" font-weight="bold" transform="rotate(-90 15.5 60.141)"><tspan x="1.1" y="62.746">age</tspan></text></g><g id="Group-2-Copy-2" transform="translate(198 3)"><g id="Group"><path id="Rectangle-7" fill="#FFF" d="M0 0h32v157H0z"/><path id="Combined-Shape" fill="#E8C48F" d="M32 0v157H0V0h32zM16.5 126.613c-4.142 0-7.5 4.251-7.5 9.496 0 5.244 3.358 9.496 7.5 9.496 4.142 0 7.5-4.252 7.5-9.496 0-5.245-3.358-9.496-7.5-9.496zM28 6.33H4v110.153h24V6.33z"/></g><text id="likes-birds" fill="#EE6B47" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold" transform="rotate(-90 17.5 60.774)"><tspan x="-28.7" y="63.278">likes birds</tspan></text></g><path id="Rectangle-4" fill="#FFF9EB" stroke="#BCA68E" stroke-width="4" d="M109 157h135v50H109z"/><path id="Rectangle-8" stroke="#BCA68E" stroke-width="3" d="M164.5 177.5h25v10h-25z"/><text id="user" fill="#EE6B47" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="5" y="187">user</tspan></text><path id="Line-8" fill="#EE6B47" fill-rule="nonzero" d="M71.5 173l19 9.5-19 9.5v-8H45v-3h26.5v-8z"/></g></g></svg>
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 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.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(142 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(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="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><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="11" y="145">user</tspan></text><path id="Line-8" fill="#C06334" fill-rule="nonzero" d="M77.5 131l19 9.5-19 9.5v-8H51v-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.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(142 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(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="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><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="11" y="145">user</tspan></text><path id="Line-8" fill="#EE6B47" fill-rule="nonzero" d="M77.5 131l19 9.5-19 9.5v-8H51v-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="176" height="183" viewBox="0 0 176 183"><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.svg"><path fill="#FFF" d="M0 0h176v183H0z"/><path id="Rectangle-4-Copy" fill="#DBAF88" stroke="#DBAF88" stroke-width="4" d="M134.063 113l16.667 20H20.27l16.667-20h97.126z" opacity=".5"/><g id="Group-2" transform="translate(33 6)"><g id="Group"><path id="Rectangle-7" fill="#FFF" d="M0 0h32v124H0z"/><path id="Combined-Shape" fill="#DBAF88" d="M32 0v124H0V0h32zM16.5 100a7.5 7.5 0 100 15 7.5 7.5 0 000-15zM28 5H4v87h24V5z"/></g><text id="key1" fill="#C06334" font-family="PTMono-Bold, PT Mono" font-size="16" font-weight="bold" transform="rotate(-90 15.5 47.5)"><tspan x="-3.7" y="52.5">key1</tspan></text></g><g id="Group-2-Copy" transform="translate(70 6)"><g id="Group"><path id="Rectangle-7" fill="#FFF" d="M0 0h32v124H0z"/><path id="Combined-Shape" fill="#DBAF88" d="M32 0v124H0V0h32zM16.5 100a7.5 7.5 0 100 15 7.5 7.5 0 000-15zM28 5H4v87h24V5z"/></g><text id="key2" fill="#C06334" font-family="PTMono-Bold, PT Mono" font-size="16" font-weight="bold" transform="rotate(-90 15.5 47.5)"><tspan x="-3.7" y="52.5">key2</tspan></text></g><g id="Group-2-Copy-2" transform="translate(107 6)"><g id="Group"><path id="Rectangle-7" fill="#FFF" d="M0 0h32v124H0z"/><path id="Combined-Shape" fill="#DBAF88" d="M32 0v124H0V0h32zM16.5 100a7.5 7.5 0 100 15 7.5 7.5 0 000-15zM28 5H4v87h24V5z"/></g><text id="key3" fill="#C06334" font-family="PTMono-Bold, PT Mono" font-size="16" font-weight="bold" transform="rotate(-90 15.5 47.5)"><tspan x="-3.7" y="52.5">key3</tspan></text></g><path id="Rectangle-4" fill="#FBF2EC" stroke="#DBAF88" stroke-width="4" d="M18 127h135v50H18z"/><path id="Rectangle-8" stroke="#DBAF88" stroke-width="3" d="M73.5 147.5h25v10h-25z"/></g></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="176" height="183" viewBox="0 0 176 183"><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.svg"><path fill="#FFF" d="M0 0h176v183H0z"/><path id="Rectangle-4-Copy" fill="#D1C4B1" stroke="#D1C4B1" stroke-width="4" d="M36.937 113L20.27 133h130.46l-16.667-20H36.937z" opacity=".5"/><g id="Group-2" transform="translate(33 6)"><g id="Group"><path id="Rectangle-7" fill="#FFF" d="M0 0h32v124H0z"/><path id="Combined-Shape" fill="#E8C48F" d="M32 0v124H0V0h32zM16.5 100a7.5 7.5 0 100 15 7.5 7.5 0 000-15zM28 5H4v87h24V5z"/></g><text id="key1" fill="#EE6B47" font-family="PTMono-Bold, PT Mono" font-size="16" font-weight="bold" transform="rotate(-90 15.5 47.5)"><tspan x="-3.7" y="52.5">key1</tspan></text></g><g id="Group-2-Copy" transform="translate(70 6)"><g id="Group"><path id="Rectangle-7" fill="#FFF" d="M0 0h32v124H0z"/><path id="Combined-Shape" fill="#E8C48F" d="M32 0v124H0V0h32zM16.5 100a7.5 7.5 0 100 15 7.5 7.5 0 000-15zM28 5H4v87h24V5z"/></g><text id="key2" fill="#EE6B47" font-family="PTMono-Bold, PT Mono" font-size="16" font-weight="bold" transform="rotate(-90 15.5 47.5)"><tspan x="-3.7" y="52.5">key2</tspan></text></g><g id="Group-2-Copy-2" transform="translate(107 6)"><g id="Group"><path id="Rectangle-7" fill="#FFF" d="M0 0h32v124H0z"/><path id="Combined-Shape" fill="#E8C48F" d="M32 0v124H0V0h32zM16.5 100a7.5 7.5 0 100 15 7.5 7.5 0 000-15zM28 5H4v87h24V5z"/></g><text id="key3" fill="#EE6B47" font-family="PTMono-Bold, PT Mono" font-size="16" font-weight="bold" transform="rotate(-90 15.5 47.5)"><tspan x="-3.7" y="52.5">key3</tspan></text></g><path id="Rectangle-4" fill="#FFF9EB" stroke="#BCA68E" stroke-width="4" d="M18 127h135v50H18z"/><path id="Rectangle-8" stroke="#BCA68E" stroke-width="3" d="M73.5 147.5h25v10h-25z"/></g></g></svg>
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
|
@ -37,7 +37,7 @@ And here's how it's actually stored in memory:
|
|||
|
||||
The object is stored somewhere in memory (at the right of the picture), while the `user` variable (at the left) has a "reference" to it.
|
||||
|
||||
We may think of an object variable, such as `user`, like a sheet of paper with the address of the object on it.
|
||||
We may think of an object variable, such as `user`, as like a sheet of paper with the address of the object on it.
|
||||
|
||||
When we perform actions with the object, e.g. take a property `user.name`, the JavaScript engine looks at what's at that address and performs the operation on the actual object.
|
||||
|
||||
|
@ -100,37 +100,15 @@ 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 needed very rarely -- usually they appear as a result of a programming mistake.
|
||||
|
||||
````smart header="Const objects can be modified"
|
||||
An important side effect of storing objects as references is that an object declared as `const` *can* be modified.
|
||||
|
||||
For instance:
|
||||
|
||||
```js run
|
||||
const user = {
|
||||
name: "John"
|
||||
};
|
||||
|
||||
*!*
|
||||
user.name = "Pete"; // (*)
|
||||
*/!*
|
||||
|
||||
alert(user.name); // Pete
|
||||
```
|
||||
|
||||
It might seem that the line `(*)` would cause an error, but it does not. The value of `user` is constant, it must always reference the same object, but properties of that object are free to change.
|
||||
|
||||
In other words, the `const user` gives an error only if we try to set `user=...` as a whole.
|
||||
|
||||
That said, if we really need to make constant object properties, it's also possible, but using totally different methods. We'll mention that in the chapter <info:property-descriptors>.
|
||||
````
|
||||
|
||||
## Cloning and merging, Object.assign [#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?
|
||||
But what if we need to duplicate an object? Create an independent copy, a clone?
|
||||
|
||||
We can create a new object and replicate the structure of the existing one, by iterating over its properties and copying them on the primitive level.
|
||||
That's also doable, but a little bit more difficult, because there's no built-in method for that in JavaScript. But there is rarely a need -- 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:
|
||||
|
||||
|
@ -155,22 +133,21 @@ clone.name = "Pete"; // changed the data in it
|
|||
alert( user.name ); // still John in the original object
|
||||
```
|
||||
|
||||
We can also use the method [Object.assign](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign).
|
||||
Also we can use the method [Object.assign](mdn:js/Object/assign) for that.
|
||||
|
||||
The syntax is:
|
||||
|
||||
```js
|
||||
Object.assign(dest, ...sources)
|
||||
Object.assign(dest, [src1, src2, src3...])
|
||||
```
|
||||
|
||||
- The first argument `dest` is a target object.
|
||||
- Further arguments is a list of source objects.
|
||||
- Further arguments `src1, ..., srcN` (can be as many as needed) are source objects.
|
||||
- It copies the properties of all source objects `src1, ..., srcN` into the target `dest`. In other words, properties of all arguments starting from the second are copied into the first object.
|
||||
- The call returns `dest`.
|
||||
|
||||
It copies the properties of all source objects into the target `dest`, and then returns it as the result.
|
||||
|
||||
For example, we have `user` object, let's add a couple of permissions to it:
|
||||
|
||||
```js run
|
||||
For instance, we can use it to merge several objects into one:
|
||||
```js
|
||||
let user = { name: "John" };
|
||||
|
||||
let permissions1 = { canView: true };
|
||||
|
@ -182,9 +159,6 @@ Object.assign(user, permissions1, permissions2);
|
|||
*/!*
|
||||
|
||||
// now user = { name: "John", canView: true, canEdit: true }
|
||||
alert(user.name); // John
|
||||
alert(user.canView); // true
|
||||
alert(user.canEdit); // true
|
||||
```
|
||||
|
||||
If the copied property name already exists, it gets overwritten:
|
||||
|
@ -197,9 +171,9 @@ Object.assign(user, { name: "Pete" });
|
|||
alert(user.name); // now user = { name: "Pete" }
|
||||
```
|
||||
|
||||
We also can use `Object.assign` to perform a simple object cloning:
|
||||
We also can use `Object.assign` to replace `for..in` loop for simple cloning:
|
||||
|
||||
```js run
|
||||
```js
|
||||
let user = {
|
||||
name: "John",
|
||||
age: 30
|
||||
|
@ -208,18 +182,15 @@ let user = {
|
|||
*!*
|
||||
let clone = Object.assign({}, user);
|
||||
*/!*
|
||||
|
||||
alert(clone.name); // John
|
||||
alert(clone.age); // 30
|
||||
```
|
||||
|
||||
Here it copies all properties of `user` into the empty object and returns it.
|
||||
It copies all properties of `user` into the empty object and returns it.
|
||||
|
||||
There are also other methods of cloning an object, e.g. using the [spread syntax](info:rest-parameters-spread) `clone = {...user}`, covered later in the tutorial.
|
||||
|
||||
## Nested cloning
|
||||
|
||||
Until now we assumed that all properties of `user` are primitive. But properties can be references to other objects.
|
||||
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
|
||||
|
@ -234,7 +205,9 @@ let user = {
|
|||
alert( user.sizes.height ); // 182
|
||||
```
|
||||
|
||||
Now it's not enough to copy `clone.sizes = user.sizes`, because `user.sizes` is an object, and will be copied by reference, so `clone` and `user` will share the same sizes:
|
||||
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 = {
|
||||
|
@ -250,71 +223,37 @@ let clone = Object.assign({}, user);
|
|||
alert( user.sizes === clone.sizes ); // true, same object
|
||||
|
||||
// user and clone share sizes
|
||||
user.sizes.width = 60; // change a property from one place
|
||||
alert(clone.sizes.width); // 60, get the result from the other one
|
||||
user.sizes.width++; // change a property from one place
|
||||
alert(clone.sizes.width); // 51, see the result from the other one
|
||||
```
|
||||
|
||||
To fix that and make `user` and `clone` truly separate objects, we should use a 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" or "structured cloning". There's [structuredClone](https://developer.mozilla.org/en-US/docs/Web/API/structuredClone) method that implements deep cloning.
|
||||
To fix that, we should use a 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".
|
||||
|
||||
We can use recursion to implement it. Or, to not reinvent the wheel, take an existing implementation, for instance [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep) from the JavaScript library [lodash](https://lodash.com).
|
||||
|
||||
### structuredClone
|
||||
|
||||
The call `structuredClone(object)` clones the `object` with all nested properties.
|
||||
|
||||
Here's how we can use it in our example:
|
||||
|
||||
```js run
|
||||
let user = {
|
||||
name: "John",
|
||||
sizes: {
|
||||
height: 182,
|
||||
width: 50
|
||||
}
|
||||
};
|
||||
|
||||
*!*
|
||||
let clone = structuredClone(user);
|
||||
*/!*
|
||||
|
||||
alert( user.sizes === clone.sizes ); // false, different objects
|
||||
|
||||
// user and clone are totally unrelated now
|
||||
user.sizes.width = 60; // change a property from one place
|
||||
alert(clone.sizes.width); // 50, not related
|
||||
```
|
||||
|
||||
The `structuredClone` method can clone most data types, such as objects, arrays, primitive values.
|
||||
|
||||
It also supports circular references, when an object property references the object itself (directly or via a chain or references).
|
||||
````smart header="Const objects can be modified"
|
||||
An important side effect of storing objects as references is that an object declared as `const` *can* be modified.
|
||||
|
||||
For instance:
|
||||
|
||||
```js run
|
||||
let user = {};
|
||||
// let's create a circular reference:
|
||||
// user.me references the user itself
|
||||
user.me = user;
|
||||
const user = {
|
||||
name: "John"
|
||||
};
|
||||
|
||||
let clone = structuredClone(user);
|
||||
alert(clone.me === clone); // true
|
||||
*!*
|
||||
user.name = "Pete"; // (*)
|
||||
*/!*
|
||||
|
||||
alert(user.name); // Pete
|
||||
```
|
||||
|
||||
As you can see, `clone.me` references the `clone`, not the `user`! So the circular reference was cloned correctly as well.
|
||||
It might seem that the line `(*)` would cause an error, but it does not. The value of `user` is constant, it must always reference the same object, but properties of that object are free to change.
|
||||
|
||||
Although, there are cases when `structuredClone` fails.
|
||||
In other words, the `const user` gives an error only if we try to set `user=...` as a whole.
|
||||
|
||||
For instance, when an object has a function property:
|
||||
|
||||
```js run
|
||||
// error
|
||||
structuredClone({
|
||||
f: function() {}
|
||||
});
|
||||
```
|
||||
|
||||
Function properties aren't supported.
|
||||
|
||||
To handle such complex cases we may need to use a combination of cloning methods, write custom code or, to not reinvent the wheel, take an existing implementation, for instance [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep) from the JavaScript library [lodash](https://lodash.com).
|
||||
That said, if we really need to make constant object properties, it's also possible, but using totally different methods. We'll mention that in the chapter <info:property-descriptors>.
|
||||
````
|
||||
|
||||
## Summary
|
||||
|
||||
|
@ -322,4 +261,4 @@ Objects are assigned and copied by reference. In other words, a variable stores
|
|||
|
||||
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` for the so-called "shallow copy" (nested objects are copied by reference) or a "deep cloning" function `structuredClone` or use a custom cloning implementation, such as [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep).
|
||||
To make a "real copy" (a clone) we can use `Object.assign` for the so-called "shallow copy" (nested objects are copied by reference) or a "deep cloning" function, such as [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep).
|
||||
|
|
|
@ -1 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="370" height="249" viewBox="0 0 370 249"><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-contains-reference.svg"><g id="noun_1211_cc" fill="#DBAF88" transform="translate(12 119)"><path id="Shape" d="M17 28.196h112.558v42.95c0 .373-.079.862-.279 1.294-.2.433-16.574 35.56-16.574 35.56V53.64l16.854-25.444L148 0H35.44L17 28.196zM17 57V29L2 57"/><path id="Shape" d="M0 59v54.73c0 3.42 1.484 5.27 4.387 5.27h100.086c3.122 0 5.527-2.548 5.527-3.476V59H0z"/></g><text id="user" fill="#FFF" font-family="OpenSans-Bold, Open Sans" font-size="18" font-weight="bold"><tspan x="47" y="215">user</tspan></text><path id="Rectangle-4" fill="#FFF" d="M116 153h44v6h-44z"/><path id="Shape-3" fill="#C06334" d="M123.854 154.322l13.779-13.476c1.613-1.274 5.185-4.427 3.572-5.702-1.613-1.275-2.664-.472-4.275.803l-18.499 18.375c-1.613 1.275-1.613 2.845 0 4.12 0 0 21.483 20.067 22.774 20.067 3.005 0 2.677-1.821 1.064-3.096l-18.415-17.826H201.028c2.281 0 3.75.279 3.75-1.524 0-1.804-1.469-1.741-3.75-1.741h-77.174z" transform="matrix(-1 0 0 1 322 0)"/><g id="Rectangle-3-+-Shape" transform="translate(58 135)"><path id="Rectangle-3" fill="#FFF" d="M0 0h47v37H0z"/><path id="Shape" fill="#C06334" d="M44 33.772H4V4h40v29.772zM8.733 31.018h30.533L29.22 20.868l-1.902 1.889c-.824.812-2.003 1.277-3.242 1.277l-.021-.001c-1.244-.005-2.427-.48-3.246-1.304l-1.934-1.933-10.14 10.222zM6.791 8.726V29.04l10.122-10.202L6.79 8.726zm24.395 10.187L41.209 29.04V8.954l-10.023 9.96zM8.74 6.755l14.057 14.042c.304.305.766.48 1.27.482h.008c.496 0 .968-.173 1.264-.467L39.49 6.755H8.739z"/></g><path id="Rectangle-4-Copy" fill="#DBAF88" stroke="#DBAF88" stroke-width="4" d="M339.063 117l16.667 20H225.27l16.667-20h97.126z" opacity=".5"/><g id="Group-2" transform="translate(271 10)"><g id="Group"><path id="Rectangle-7" fill="#FFF" d="M0 0h32v124H0z"/><path id="Combined-Shape" fill="#DBAF88" d="M32 0v124H0V0h32zM16.5 100a7.5 7.5 0 100 15 7.5 7.5 0 000-15zM28 5H4v87h24V5z"/></g><text id="name" fill="#C06334" font-family="PTMono-Bold, PT Mono" font-size="16" font-weight="bold" transform="rotate(-90 15.5 47.5)"><tspan x="-3.7" y="52.5">name</tspan></text></g><path id="Rectangle-4" fill="#FBF2EC" stroke="#DBAF88" stroke-width="4" d="M223 131h135v50H223z"/><path id="Rectangle-8" stroke="#DBAF88" stroke-width="3" d="M278.5 151.5h25v10h-25z"/></g></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="370" height="249" viewBox="0 0 370 249"><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-contains-reference.svg"><g id="noun_1211_cc" fill="#E8C48F" transform="translate(12 119)"><path id="Shape" d="M17 28.196h112.558v42.95c0 .373-.079.862-.279 1.294-.2.433-16.574 35.56-16.574 35.56V53.64l16.854-25.444L148 0H35.44L17 28.196zM17 57V29L2 57"/><path id="Shape" d="M0 59v54.73c0 3.42 1.484 5.27 4.387 5.27h100.086c3.122 0 5.527-2.548 5.527-3.476V59H0z"/></g><text id="user" fill="#FFF" font-family="OpenSans-Bold, Open Sans" font-size="18" font-weight="bold"><tspan x="47" y="215">user</tspan></text><path id="Rectangle-4" fill="#FFF" d="M116 153h44v6h-44z"/><path id="Shape-3" fill="#EE6B47" d="M123.854 154.322l13.779-13.476c1.613-1.274 5.185-4.427 3.572-5.702-1.613-1.275-2.664-.472-4.275.803l-18.499 18.375c-1.613 1.275-1.613 2.845 0 4.12 0 0 21.483 20.067 22.774 20.067 3.005 0 2.677-1.821 1.064-3.096l-18.415-17.826H201.028c2.281 0 3.75.279 3.75-1.524 0-1.804-1.469-1.741-3.75-1.741h-77.174z" transform="matrix(-1 0 0 1 322 0)"/><g id="Rectangle-3-+-Shape" transform="translate(58 135)"><path id="Rectangle-3" fill="#FFF" d="M0 0h47v37H0z"/><path id="Shape" fill="#EE6B47" d="M44 33.772H4V4h40v29.772zM8.733 31.018h30.533L29.22 20.868l-1.902 1.889c-.824.812-2.003 1.277-3.242 1.277l-.021-.001c-1.244-.005-2.427-.48-3.246-1.304l-1.934-1.933-10.14 10.222zM6.791 8.726V29.04l10.122-10.202L6.79 8.726zm24.395 10.187L41.209 29.04V8.954l-10.023 9.96zM8.74 6.755l14.057 14.042c.304.305.766.48 1.27.482h.008c.496 0 .968-.173 1.264-.467L39.49 6.755H8.739z"/></g><path id="Rectangle-4-Copy" fill="#D1C4B1" stroke="#D1C4B1" stroke-width="4" d="M241.937 117l-16.667 20h130.46l-16.667-20h-97.126z" opacity=".5"/><g id="Group-2" transform="translate(271 10)"><g id="Group"><path id="Rectangle-7" fill="#FFF" d="M0 0h32v124H0z"/><path id="Combined-Shape" fill="#E8C48F" d="M32 0v124H0V0h32zM16.5 100a7.5 7.5 0 100 15 7.5 7.5 0 000-15zM28 5H4v87h24V5z"/></g><text id="name" fill="#EE6B47" font-family="PTMono-Bold, PT Mono" font-size="16" font-weight="bold" transform="rotate(-90 15.5 47.5)"><tspan x="-3.7" y="52.5">name</tspan></text></g><path id="Rectangle-4" fill="#FFF9EB" stroke="#BCA68E" stroke-width="4" d="M223 131h135v50H223z"/><path id="Rectangle-8" stroke="#BCA68E" stroke-width="3" d="M278.5 151.5h25v10h-25z"/></g></g></svg>
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.8 KiB |
|
@ -1 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="599" height="260" viewBox="0 0 599 260"><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-copy-reference.svg"><g id="Group" transform="translate(11 125)"><g id="noun_1211_cc" fill="#DBAF88"><path id="Shape" d="M17 28.196h112.558v42.95c0 .373-.079.862-.279 1.294-.2.433-16.574 35.56-16.574 35.56V53.64l16.854-25.444L148 0H35.44L17 28.196zM17 57V29L2 57"/><path id="Shape" d="M0 59v54.73c0 3.42 1.484 5.27 4.387 5.27h100.086c3.122 0 5.527-2.548 5.527-3.476V59H0z"/></g><text id="user" fill="#FFF" font-family="OpenSans-Bold, Open Sans" font-size="18" font-weight="bold"><tspan x="35" y="96">user</tspan></text><g id="Rectangle-4-+-Shape-3" transform="translate(104 15)"><path id="Rectangle-4" fill="#FFF" d="M0 19h44v6H0z"/><path id="Shape-3" fill="#C06334" d="M7.854 20.322L21.633 6.846c1.613-1.274 5.185-4.427 3.572-5.702-1.613-1.275-2.664-.472-4.275.803L2.43 20.322c-1.613 1.275-1.613 2.845 0 4.12 0 0 21.483 20.067 22.774 20.067 3.005 0 2.677-1.821 1.064-3.096L7.854 23.587H85.028c2.281 0 3.75.279 3.75-1.524 0-1.804-1.469-1.741-3.75-1.741H7.854z" transform="matrix(-1 0 0 1 90 0)"/></g><g id="Rectangle-3-+-Shape" transform="translate(46 16)"><path id="Rectangle-3" fill="#FFF" d="M0 0h47v37H0z"/><path id="Shape" fill="#C06334" d="M44 33.772H4V4h40v29.772zM8.733 31.018h30.533L29.22 20.868l-1.902 1.889c-.824.812-2.003 1.277-3.242 1.277l-.021-.001c-1.244-.005-2.427-.48-3.246-1.304l-1.934-1.933-10.14 10.222zM6.791 8.726V29.04l10.122-10.202L6.79 8.726zm24.395 10.187L41.209 29.04V8.954l-10.023 9.96zM8.74 6.755l14.057 14.042c.304.305.766.48 1.27.482h.008c.496 0 .968-.173 1.264-.467L39.49 6.755H8.739z"/></g></g><g id="noun_1211_cc" fill="#DBAF88" transform="translate(418 125)"><path id="Shape" d="M17 28.196h112.558v42.95c0 .373-.079.862-.279 1.294-.2.433-16.574 35.56-16.574 35.56V53.64l16.854-25.444L148 0H35.44L17 28.196zM17 57V29L2 57"/><path id="Shape" d="M0 59v54.73c0 3.42 1.484 5.27 4.387 5.27h100.086c3.122 0 5.527-2.548 5.527-3.476V59H0z"/></g><text id="admin" fill="#FFF" font-family="OpenSans-Bold, Open Sans" font-size="18" font-weight="bold"><tspan x="446" y="221">admin</tspan></text><g id="Rectangle-4-+-Shape-3" transform="matrix(-1 0 0 1 451 140)"><path id="Rectangle-4" fill="#FFF" d="M0 19h44v6H0z"/><path id="Shape-3" fill="#C06334" d="M7.854 20.322L21.633 6.846c1.613-1.274 5.185-4.427 3.572-5.702-1.613-1.275-2.664-.472-4.275.803L2.43 20.322c-1.613 1.275-1.613 2.845 0 4.12 0 0 21.483 20.067 22.774 20.067 3.005 0 2.677-1.821 1.064-3.096L7.854 23.587H85.028c2.281 0 3.75.279 3.75-1.524 0-1.804-1.469-1.741-3.75-1.741H7.854z" transform="matrix(-1 0 0 1 90 0)"/></g><g id="Rectangle-3-+-Shape" transform="translate(464 141)"><path id="Rectangle-3" fill="#FFF" d="M0 0h47v37H0z"/><path id="Shape" fill="#C06334" d="M44 33.772H4V4h40v29.772zM8.733 31.018h30.533L29.22 20.868l-1.902 1.889c-.824.812-2.003 1.277-3.242 1.277l-.021-.001c-1.244-.005-2.427-.48-3.246-1.304l-1.934-1.933-10.14 10.222zM6.791 8.726V29.04l10.122-10.202L6.79 8.726zm24.395 10.187L41.209 29.04V8.954l-10.023 9.96zM8.74 6.755l14.057 14.042c.304.305.766.48 1.27.482h.008c.496 0 .968-.173 1.264-.467L39.49 6.755H8.739z"/></g><path id="Rectangle-4-Copy" fill="#DBAF88" stroke="#DBAF88" stroke-width="4" d="M333.063 125l16.667 20H219.27l16.667-20h97.126z" opacity=".5"/><g id="Group-2" transform="translate(265 18)"><g id="Group"><path id="Rectangle-7" fill="#FFF" d="M0 0h32v124H0z"/><path id="Combined-Shape" fill="#DBAF88" d="M32 0v124H0V0h32zM16.5 100a7.5 7.5 0 100 15 7.5 7.5 0 000-15zM28 5H4v87h24V5z"/></g><text id="name" fill="#C06334" font-family="PTMono-Bold, PT Mono" font-size="16" font-weight="bold" transform="rotate(-90 15.5 47.5)"><tspan x="-3.7" y="52.5">name</tspan></text></g><path id="Rectangle-4" fill="#FBF2EC" stroke="#DBAF88" stroke-width="4" d="M217 139h135v50H217z"/><path id="Rectangle-8" stroke="#DBAF88" stroke-width="3" d="M272.5 159.5h25v10h-25z"/></g></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="599" height="260" viewBox="0 0 599 260"><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-copy-reference.svg"><g id="Group" transform="translate(11 125)"><g id="noun_1211_cc" fill="#E8C48F"><path id="Shape" d="M17 28.196h112.558v42.95c0 .373-.079.862-.279 1.294-.2.433-16.574 35.56-16.574 35.56V53.64l16.854-25.444L148 0H35.44L17 28.196zM17 57V29L2 57"/><path id="Shape" d="M0 59v54.73c0 3.42 1.484 5.27 4.387 5.27h100.086c3.122 0 5.527-2.548 5.527-3.476V59H0z"/></g><text id="user" fill="#FFF" font-family="OpenSans-Bold, Open Sans" font-size="18" font-weight="bold"><tspan x="35" y="96">user</tspan></text><g id="Rectangle-4-+-Shape-3" transform="translate(104 15)"><path id="Rectangle-4" fill="#FFF" d="M0 19h44v6H0z"/><path id="Shape-3" fill="#EE6B47" d="M7.854 20.322L21.633 6.846c1.613-1.274 5.185-4.427 3.572-5.702-1.613-1.275-2.664-.472-4.275.803L2.43 20.322c-1.613 1.275-1.613 2.845 0 4.12 0 0 21.483 20.067 22.774 20.067 3.005 0 2.677-1.821 1.064-3.096L7.854 23.587H85.028c2.281 0 3.75.279 3.75-1.524 0-1.804-1.469-1.741-3.75-1.741H7.854z" transform="matrix(-1 0 0 1 90 0)"/></g><g id="Rectangle-3-+-Shape" transform="translate(46 16)"><path id="Rectangle-3" fill="#FFF" d="M0 0h47v37H0z"/><path id="Shape" fill="#EE6B47" d="M44 33.772H4V4h40v29.772zM8.733 31.018h30.533L29.22 20.868l-1.902 1.889c-.824.812-2.003 1.277-3.242 1.277l-.021-.001c-1.244-.005-2.427-.48-3.246-1.304l-1.934-1.933-10.14 10.222zM6.791 8.726V29.04l10.122-10.202L6.79 8.726zm24.395 10.187L41.209 29.04V8.954l-10.023 9.96zM8.74 6.755l14.057 14.042c.304.305.766.48 1.27.482h.008c.496 0 .968-.173 1.264-.467L39.49 6.755H8.739z"/></g></g><g id="noun_1211_cc" fill="#E8C48F" transform="translate(418 125)"><path id="Shape" d="M17 28.196h112.558v42.95c0 .373-.079.862-.279 1.294-.2.433-16.574 35.56-16.574 35.56V53.64l16.854-25.444L148 0H35.44L17 28.196zM17 57V29L2 57"/><path id="Shape" d="M0 59v54.73c0 3.42 1.484 5.27 4.387 5.27h100.086c3.122 0 5.527-2.548 5.527-3.476V59H0z"/></g><text id="admin" fill="#FFF" font-family="OpenSans-Bold, Open Sans" font-size="18" font-weight="bold"><tspan x="446" y="221">admin</tspan></text><g id="Rectangle-4-+-Shape-3" transform="matrix(-1 0 0 1 451 140)"><path id="Rectangle-4" fill="#FFF" d="M0 19h44v6H0z"/><path id="Shape-3" fill="#EE6B47" d="M7.854 20.322L21.633 6.846c1.613-1.274 5.185-4.427 3.572-5.702-1.613-1.275-2.664-.472-4.275.803L2.43 20.322c-1.613 1.275-1.613 2.845 0 4.12 0 0 21.483 20.067 22.774 20.067 3.005 0 2.677-1.821 1.064-3.096L7.854 23.587H85.028c2.281 0 3.75.279 3.75-1.524 0-1.804-1.469-1.741-3.75-1.741H7.854z" transform="matrix(-1 0 0 1 90 0)"/></g><g id="Rectangle-3-+-Shape" transform="translate(464 141)"><path id="Rectangle-3" fill="#FFF" d="M0 0h47v37H0z"/><path id="Shape" fill="#EE6B47" d="M44 33.772H4V4h40v29.772zM8.733 31.018h30.533L29.22 20.868l-1.902 1.889c-.824.812-2.003 1.277-3.242 1.277l-.021-.001c-1.244-.005-2.427-.48-3.246-1.304l-1.934-1.933-10.14 10.222zM6.791 8.726V29.04l10.122-10.202L6.79 8.726zm24.395 10.187L41.209 29.04V8.954l-10.023 9.96zM8.74 6.755l14.057 14.042c.304.305.766.48 1.27.482h.008c.496 0 .968-.173 1.264-.467L39.49 6.755H8.739z"/></g><path id="Rectangle-4-Copy" fill="#D1C4B1" stroke="#D1C4B1" stroke-width="4" d="M235.937 125l-16.667 20h130.46l-16.667-20h-97.126z" opacity=".5"/><g id="Group-2" transform="translate(265 18)"><g id="Group"><path id="Rectangle-7" fill="#FFF" d="M0 0h32v124H0z"/><path id="Combined-Shape" fill="#E8C48F" d="M32 0v124H0V0h32zM16.5 100a7.5 7.5 0 100 15 7.5 7.5 0 000-15zM28 5H4v87h24V5z"/></g><text id="name" fill="#EE6B47" font-family="PTMono-Bold, PT Mono" font-size="16" font-weight="bold" transform="rotate(-90 15.5 47.5)"><tspan x="-3.7" y="52.5">name</tspan></text></g><path id="Rectangle-4" fill="#FFF9EB" stroke="#BCA68E" stroke-width="4" d="M217 139h135v50H217z"/><path id="Rectangle-8" stroke="#BCA68E" stroke-width="3" d="M272.5 159.5h25v10h-25z"/></g></g></svg>
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
|
@ -1 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="359" height="143" viewBox="0 0 359 143"><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-copy-value.svg"><g id="noun_1211_cc-+-Message" transform="translate(11 6)"><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 38.202 38.946)"><tspan x="2.822" y="43.482">"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 id="Group" transform="translate(210 6)"><path id="Shape" fill="#DBAF88" d="M17 37.196h113.417v42.95c0 .373-.08.862-.28 1.294-.202.433-16.702 35.56-16.702 35.56V62.64l16.983-25.444L149 9H35.582L17 37.196z"/><path id="Shape" fill="#DBAF88" d="M18 66V38L2 66"/><g id="Rectangle-5-+-"World!"" transform="translate(15)"><path id="Rectangle-5" fill="#FBF2EC" stroke="#AF6E24" stroke-width="2" d="M19.117 1.8l53.867 56.994-17.101 15.734L2.016 17.534 19.117 1.799z"/><text id=""Hello!"" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold" transform="rotate(47 38.162 37.693)"><tspan x="2.782" y="42.23">"Hello!"</tspan></text></g><path id="Shape" fill="#DBAF88" d="M0 68v54.73c0 3.42 1.497 5.27 4.427 5.27h100.996c3.15 0 5.577-2.548 5.577-3.476V68H0z"/><text id="phrase" fill="#FFF" font-family="OpenSans-Bold, Open Sans" font-size="18" font-weight="bold"><tspan x="25" y="105">phrase</tspan></text></g></g></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="359" height="143" viewBox="0 0 359 143"><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-copy-value.svg"><g id="noun_1211_cc-+-Message" transform="translate(11 6)"><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 38.202 38.946)"><tspan x="2.822" y="43.482">"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 id="Group" transform="translate(210 6)"><path id="Shape" fill="#E8C48F" d="M17 37.196h113.417v42.95c0 .373-.08.862-.28 1.294-.202.433-16.702 35.56-16.702 35.56V62.64l16.983-25.444L149 9H35.582L17 37.196z"/><path id="Shape" fill="#E8C48F" d="M18 66V38L2 66"/><g id="Rectangle-5-+-"World!"" transform="translate(15)"><path id="Rectangle-5" fill="#FFF9EB" stroke="#8A704D" stroke-width="2" d="M19.117 1.8l-17.1 15.734 53.866 56.994 17.1-15.734L19.118 1.799z"/><text id=""Hello!"" fill="#8A704D" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold" transform="rotate(47 38.162 37.693)"><tspan x="2.782" y="42.23">"Hello!"</tspan></text></g><path id="Shape" fill="#E8C48F" d="M0 68v54.73c0 3.42 1.497 5.27 4.427 5.27h100.996c3.15 0 5.577-2.548 5.577-3.476V68H0z"/><text id="phrase" fill="#FFF" font-family="OpenSans-Bold, Open Sans" font-size="18" font-weight="bold"><tspan x="25" y="105">phrase</tspan></text></g></g></g></svg>
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
|
@ -74,7 +74,7 @@ Now if we do the same:
|
|||
user = null;
|
||||
```
|
||||
|
||||
...Then the object is still reachable via `admin` global variable, so it must stay in memory. If we overwrite `admin` too, then it can be removed.
|
||||
...Then the object is still reachable via `admin` global variable, so it's in memory. If we overwrite `admin` too, then it can be removed.
|
||||
|
||||
## Interlinked objects
|
||||
|
||||
|
@ -169,11 +169,11 @@ The first step marks the roots:
|
|||
|
||||

|
||||
|
||||
Then we follow their references and mark referenced objects:
|
||||
Then their references are marked:
|
||||
|
||||

|
||||
|
||||
...And continue to follow further references, while possible:
|
||||
...And their references, while possible:
|
||||
|
||||

|
||||
|
||||
|
@ -183,12 +183,12 @@ Now the objects that could not be visited in the process are considered unreacha
|
|||
|
||||
We can also imagine the process as spilling a huge bucket of paint from the roots, that flows through all references and marks all reachable objects. The unmarked ones are then removed.
|
||||
|
||||
That's the concept of how garbage collection works. JavaScript engines apply many optimizations to make it run faster and not introduce any delays into the code execution.
|
||||
That's the concept of how garbage collection works. JavaScript engines apply many optimizations to make it run faster and not affect the execution.
|
||||
|
||||
Some of the optimizations:
|
||||
|
||||
- **Generational collection** -- objects are split into two sets: "new ones" and "old ones". In typical code, many objects have a short life span: they appear, do their job and die fast, so it makes sense to track new objects and clear the memory from them if that's the case. Those that survive for long enough, become "old" and are examined less often.
|
||||
- **Incremental collection** -- if there are many objects, and we try to walk and mark the whole object set at once, it may take some time and introduce visible delays in the execution. So the engine splits the whole set of existing objects into multiple parts. And then clear these parts one after another. There are many small garbage collections instead of a total one. That requires some extra bookkeeping between them to track changes, but we get many tiny delays instead of a big one.
|
||||
- **Generational collection** -- objects are split into two sets: "new ones" and "old ones". Many objects appear, do their job and die fast, they can be cleaned up aggressively. Those that survive for long enough, become "old" and are examined less often.
|
||||
- **Incremental collection** -- if there are many objects, and we try to walk and mark the whole object set at once, it may take some time and introduce visible delays in the execution. So the engine tries to split the garbage collection into pieces. Then the pieces are executed one by one, separately. That requires some extra bookkeeping between them to track changes, but we have many tiny delays instead of a big one.
|
||||
- **Idle-time collection** -- the garbage collector tries to run only while the CPU is idle, to reduce the possible effect on the execution.
|
||||
|
||||
There exist other optimizations and flavours of garbage collection algorithms. As much as I'd like to describe them here, I have to hold off, because different engines implement different tweaks and techniques. And, what's even more important, things change as engines develop, so studying deeper "in advance", without a real need is probably not worth that. Unless, of course, it is a matter of pure interest, then there will be some links for you below.
|
||||
|
@ -199,14 +199,14 @@ The main things to know:
|
|||
|
||||
- Garbage collection is performed automatically. We cannot force or prevent it.
|
||||
- Objects are retained in memory while they are reachable.
|
||||
- Being referenced is not the same as being reachable (from a root): a pack of interlinked objects can become unreachable as a whole, as we've seen in the example above.
|
||||
- Being referenced is not the same as being reachable (from a root): a pack of interlinked objects can become unreachable as a whole.
|
||||
|
||||
Modern engines implement advanced algorithms of garbage collection.
|
||||
|
||||
A general book "The Garbage Collection Handbook: The Art of Automatic Memory Management" (R. Jones et al) covers some of them.
|
||||
|
||||
If you are familiar with low-level programming, more detailed information about V8's garbage collector is in the article [A tour of V8: Garbage Collection](http://jayconrod.com/posts/55/a-tour-of-v8-garbage-collection).
|
||||
If you are familiar with low-level programming, the more detailed information about V8 garbage collector is in the article [A tour of V8: Garbage Collection](http://jayconrod.com/posts/55/a-tour-of-v8-garbage-collection).
|
||||
|
||||
The [V8 blog](https://v8.dev/) also publishes articles about changes in memory management from time to time. Naturally, to learn more about garbage collection, you'd better prepare by learning about V8 internals in general and read the blog of [Vyacheslav Egorov](http://mrale.ph) who worked as one of the V8 engineers. I'm saying: "V8", because it is best covered by articles on the internet. For other engines, many approaches are similar, but garbage collection differs in many aspects.
|
||||
[V8 blog](https://v8.dev/) also publishes articles about changes in memory management from time to time. Naturally, to learn the garbage collection, you'd better prepare by learning about V8 internals in general and read the blog of [Vyacheslav Egorov](http://mrale.ph) who worked as one of V8 engineers. I'm saying: "V8", because it is best covered with articles in the internet. For other engines, many approaches are similar, but garbage collection differs in many aspects.
|
||||
|
||||
In-depth knowledge of engines is good when you need low-level optimizations. It would be wise to plan that as the next step after you're familiar with the language.
|
||||
In-depth knowledge of engines is good when you need low-level optimizations. It would be wise to plan that as the next step after you're familiar with the language.
|
||||
|
|
|
@ -1 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="337" height="204" viewBox="0 0 337 204"><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="garbage-collection" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="family-delete-refs.svg"><path id="Line" fill="#7E7C7B" fill-rule="nonzero" d="M147.5 183.5v6h46v2h-46v6l-14-7 14-7z" opacity=".6"/><path id="Rectangle-1" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M88 13h151v26H88z"/><text id="<global-variable>" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="92" y="30"><global variable></tspan></text><path id="Rectangle-2" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M123 80h78v26h-78z"/><text id="Object" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="139" y="96">Object</tspan></text><path id="Rectangle-3" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M5 148h118v48H5z"/><text id="Object-2" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="39" y="165">Object</tspan></text><text id="wife" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="147" y="148">wife</tspan></text><text id="family" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="106" y="63">family</tspan></text><text id="name:-"John"" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="19" y="185">name: "John"</tspan></text><path id="Rectangle-4" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M214 148h118v48H214z"/><text id="name:-"Ann"" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="230" y="185">name: "Ann"</tspan></text><text id="mother" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="242" y="127">mother</tspan></text><text id="Object-3" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="244" y="165">Object</tspan></text><path id="Line" fill="#C06334" fill-rule="nonzero" d="M164.5 43.5v18h6l-7 14-7-14h6v-18h2z"/><path id="Line-2" fill="#7E7C7B" fill-rule="nonzero" d="M114.75 111.108l1.142 1.642-.82.57-34.508 24.005 3.426 4.926L68.5 144.5l7.495-13.741 3.426 4.924 34.508-24.004.82-.571z" opacity=".6"/><path id="Line" fill="#C06334" fill-rule="nonzero" d="M180.5 149.5l14 7-14 7v-6h-48v-2h48v-6zM208.236 111.11l.827.563 35.427 24.121 3.377-4.96L255.5 144.5l-15.512-2.093 3.377-4.96-35.428-24.12-.826-.563 1.125-1.653z"/><path id="Line-Copy" stroke="#C06334" stroke-linecap="square" stroke-width="2" d="M100.5 118.5l-11 14"/><path id="Line-Copy-2" stroke="#C06334" stroke-linecap="square" stroke-width="2" d="M89.5 118.5l11 14"/><path id="Line-Copy-4" stroke="#C06334" stroke-linecap="square" stroke-width="2" d="M172.5 183.5l-11 14"/><path id="Line-Copy-3" stroke="#C06334" stroke-linecap="square" stroke-width="2" d="M161.5 183.5l11 14"/><text id="father" fill="#7E7C7B" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="35" y="127">father</tspan></text><text id="husband" fill="#7E7C7B" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="136" y="181">husband</tspan></text></g></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="337" height="204" viewBox="0 0 337 204"><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="garbage-collection" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="family-delete-refs.svg"><path id="Line" fill="#9B9B9B" fill-rule="nonzero" d="M147.5 183.5v6h46v2h-46v6l-14-7 14-7z" opacity=".6"/><path id="Rectangle-1" fill="#FFF9EB" stroke="#E8C48E" stroke-width="2" d="M88 13h151v26H88z"/><text id="<global-variable>" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="92" y="30"><global variable></tspan></text><path id="Rectangle-2" fill="#FFF9EB" stroke="#BCA68E" stroke-width="2" d="M123 80h78v26h-78z"/><text id="Object" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="139" y="96">Object</tspan></text><path id="Rectangle-3" fill="#FFF9EB" stroke="#BCA68E" stroke-width="2" d="M5 148h118v48H5z"/><text id="Object-2" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="39" y="165">Object</tspan></text><text id="wife" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="147" y="148">wife</tspan></text><text id="family" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="106" y="63">family</tspan></text><text id="name:-"John"" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="19" y="185">name: "John"</tspan></text><path id="Rectangle-4" fill="#FFF9EB" stroke="#BCA68E" stroke-width="2" d="M214 148h118v48H214z"/><text id="name:-"Ann"" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="230" y="185">name: "Ann"</tspan></text><text id="mother" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="242" y="127">mother</tspan></text><text id="Object-3" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="244" y="165">Object</tspan></text><path id="Line" fill="#EE6B47" fill-rule="nonzero" d="M164.5 43.5v18h6l-7 14-7-14h6v-18h2z"/><path id="Line-2" fill="#9B9B9B" fill-rule="nonzero" d="M114.75 111.108l1.142 1.642-.82.57-34.508 24.005 3.426 4.926L68.5 144.5l7.495-13.741 3.426 4.924 34.508-24.004.82-.571z" opacity=".6"/><path id="Line" fill="#EE6B47" fill-rule="nonzero" d="M180.5 149.5l14 7-14 7v-6h-48v-2h48v-6zM208.236 111.11l.827.563 35.427 24.121 3.377-4.96L255.5 144.5l-15.512-2.093 3.377-4.96-35.428-24.12-.826-.563 1.125-1.653z"/><path id="Line-Copy" stroke="#EE6B47" stroke-linecap="square" stroke-width="2" d="M100.5 118.5l-11 14"/><path id="Line-Copy-2" stroke="#EE6B47" stroke-linecap="square" stroke-width="2" d="M89.5 118.5l11 14"/><path id="Line-Copy-4" stroke="#EE6B47" stroke-linecap="square" stroke-width="2" d="M172.5 183.5l-11 14"/><path id="Line-Copy-3" stroke="#EE6B47" stroke-linecap="square" stroke-width="2" d="M161.5 183.5l11 14"/><text id="father" fill="#9B9B9B" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="35" y="127">father</tspan></text><text id="husband" fill="#9B9B9B" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="136" y="181">husband</tspan></text></g></g></svg>
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
|
@ -1 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="420" height="279" viewBox="0 0 420 279"><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="garbage-collection" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="family-no-family.svg"><path id="Line" fill="#C06334" fill-rule="nonzero" d="M211.5 228.5v6h46v2h-46v6l-14-7 14-7z"/><path id="Rectangle-1" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M153 25h153v48H153z"/><text id="<global>" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="199" y="43"><global></tspan></text><path id="Rectangle-2" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M187 123h78v26h-78z"/><text id="Object" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="203" y="139">Object</tspan></text><path id="Rectangle-3" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M69 193h118v48H69z"/><text id="Object-2" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="103" y="210">Object</tspan></text><text id="father" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="99" y="170">father</tspan></text><text id="wife" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="211" y="193">wife</tspan></text><text id="name:-"John"" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="83" y="230">name: "John"</tspan></text><path id="Rectangle-4" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M278 193h118v48H278z"/><text id="name:-"Ann"" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="294" y="230">name: "Ann"</tspan></text><text id="mother" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="306" y="170">mother</tspan></text><text id="Object-3" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="308" y="210">Object</tspan></text><path id="Line-2" fill="#C06334" fill-rule="nonzero" d="M178.75 154.108l1.142 1.642-.82.57-34.508 24.005 3.426 4.926-15.49 2.249 7.495-13.741 3.426 4.924 34.508-24.004.82-.571z"/><path id="Line" fill="#C06334" fill-rule="nonzero" d="M244.5 194.5l14 7-14 7v-6h-48v-2h48v-6z"/><text id="husband" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="200" y="226">husband</tspan></text><path id="Line" fill="#C06334" fill-rule="nonzero" d="M272.236 154.11l.827.563 35.427 24.121 3.377-4.96L319.5 187.5l-15.512-2.093 3.377-4.96-35.428-24.12-.826-.563 1.125-1.653z"/><path id="Rectangle-5" stroke="#A7333A" stroke-width="2" d="M48 112h364v150H48z"/><text id="family:-null" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="183" y="66">family: null</tspan></text><g id="noun_48910_cc" transform="translate(8 110)"><path id="Shape" d="M17.503 1.75h-5.006a.341.341 0 00-.34.342v1.125h5.686V2.092a.341.341 0 00-.34-.341z"/><path id="Shape" fill="#A7333A" d="M28.364 3.217H19.59V2.092A2.09 2.09 0 0017.503 0h-5.006c-1.15 0-2.087.938-2.087 2.092v1.125H1.637c-.7 0-1.266.568-1.266 1.269v.09c0 .7.567 1.267 1.266 1.267h26.727c.699 0 1.266-.567 1.266-1.268v-.09c0-.7-.567-1.268-1.266-1.268zm-10.52 0h-5.687V2.092c0-.188.153-.341.34-.341h5.006a.34.34 0 01.34.34v1.126zM26.054 6.281H3.728c-1.298 0-2.35-.224-2.35 1.077L3.14 33.196c0 1.3 1.052 2.409 2.35 2.409h18.802c1.298 0 2.35-1.11 2.35-2.409l1.763-25.838c0-1.301-1.053-1.077-2.35-1.077zM9.637 32.193c-.377.012-.691-.261-.704-.612l-.694-19.917c-.012-.351.283-.647.66-.66.376-.013.69.261.703.613l.694 19.916c.013.351-.283.647-.659.66zm6.044-.63c0 .352-.306.637-.682.637-.377 0-.682-.286-.682-.637V11.634c0-.351.305-.636.682-.636.376 0 .682.285.682.636v19.93zm5.384.018c-.012.351-.327.625-.704.612-.376-.013-.672-.308-.66-.66l.695-19.916c.012-.352.326-.626.703-.613.377.014.672.309.66.66l-.694 19.917z"/></g></g></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="420" height="279" viewBox="0 0 420 279"><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="garbage-collection" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="family-no-family.svg"><path id="Line" fill="#EE6B47" fill-rule="nonzero" d="M211.5 228.5v6h46v2h-46v6l-14-7 14-7z"/><path id="Rectangle-1" fill="#FFF9EB" stroke="#E8C48E" stroke-width="2" d="M153 25h153v48H153z"/><text id="<global>" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="199" y="43"><global></tspan></text><path id="Rectangle-2" fill="#FFF9EB" stroke="#BCA68E" stroke-width="2" d="M187 123h78v26h-78z"/><text id="Object" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="203" y="139">Object</tspan></text><path id="Rectangle-3" fill="#FFF9EB" stroke="#BCA68E" stroke-width="2" d="M69 193h118v48H69z"/><text id="Object-2" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="103" y="210">Object</tspan></text><text id="father" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="99" y="170">father</tspan></text><text id="wife" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="211" y="193">wife</tspan></text><text id="name:-"John"" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="83" y="230">name: "John"</tspan></text><path id="Rectangle-4" fill="#FFF9EB" stroke="#BCA68E" stroke-width="2" d="M278 193h118v48H278z"/><text id="name:-"Ann"" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="294" y="230">name: "Ann"</tspan></text><text id="mother" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="306" y="170">mother</tspan></text><text id="Object-3" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="308" y="210">Object</tspan></text><path id="Line-2" fill="#EE6B47" fill-rule="nonzero" d="M178.75 154.108l1.142 1.642-.82.57-34.508 24.005 3.426 4.926-15.49 2.249 7.495-13.741 3.426 4.924 34.508-24.004.82-.571z"/><path id="Line" fill="#EE6B47" fill-rule="nonzero" d="M244.5 194.5l14 7-14 7v-6h-48v-2h48v-6z"/><text id="husband" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="200" y="226">husband</tspan></text><path id="Line" fill="#EE6B47" fill-rule="nonzero" d="M272.236 154.11l.827.563 35.427 24.121 3.377-4.96L319.5 187.5l-15.512-2.093 3.377-4.96-35.428-24.12-.826-.563 1.125-1.653z"/><path id="Rectangle-5" stroke="#D0021B" stroke-width="2" d="M48 112h364v150H48z"/><text id="family:-null" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="183" y="66">family: null</tspan></text><g id="noun_48910_cc" transform="translate(8 110)"><path id="Shape" d="M17.503 1.75h-5.006a.341.341 0 00-.34.342v1.125h5.686V2.092a.341.341 0 00-.34-.341z"/><path id="Shape" fill="#D0021B" d="M28.364 3.217H19.59V2.092A2.09 2.09 0 0017.503 0h-5.006c-1.15 0-2.087.938-2.087 2.092v1.125H1.637c-.7 0-1.266.568-1.266 1.269v.09c0 .7.567 1.267 1.266 1.267h26.727c.699 0 1.266-.567 1.266-1.268v-.09c0-.7-.567-1.268-1.266-1.268zm-10.52 0h-5.687V2.092c0-.188.153-.341.34-.341h5.006a.34.34 0 01.34.34v1.126zM26.054 6.281H3.728c-1.298 0-2.35-.224-2.35 1.077L3.14 33.196c0 1.3 1.052 2.409 2.35 2.409h18.802c1.298 0 2.35-1.11 2.35-2.409l1.763-25.838c0-1.301-1.053-1.077-2.35-1.077zM9.637 32.193c-.377.012-.691-.261-.704-.612l-.694-19.917c-.012-.351.283-.647.66-.66.376-.013.69.261.703.613l.694 19.916c.013.351-.283.647-.659.66zm6.044-.63c0 .352-.306.637-.682.637-.377 0-.682-.286-.682-.637V11.634c0-.351.305-.636.682-.636.376 0 .682.285.682.636v19.93zm5.384.018c-.012.351-.327.625-.704.612-.376-.013-.672-.308-.66-.66l.695-19.916c.012-.352.326-.626.703-.613.377.014.672.309.66.66l-.694 19.917z"/></g></g></g></svg>
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
|
@ -1 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="144" height="225" viewBox="0 0 144 225"><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="garbage-collection" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="family-no-father-2.svg"><path id="Rectangle-2" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M34 91h78v26H34z"/><text id="Object" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="50" y="107">Object</tspan></text><text id="family" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="15" y="67">family</tspan></text><path id="Rectangle-4" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M16 166h118v48H16z"/><text id="name:-"Ann"" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="34" y="203">name: "Ann"</tspan></text><text id="mother" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="16" y="142">mother</tspan></text><text id="Object-3" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="48" y="183">Object</tspan></text><path id="Line" fill="#C06334" fill-rule="nonzero" d="M75.5 47.5v18h6l-7 14-7-14h6v-18h2zM74.5 122.5v19h6l-7 14-7-14h6v-19h2z"/><path id="Rectangle-1" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M27 16h93v26H27z"/><text id="<global>" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="43" y="33"><global></tspan></text></g></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="144" height="225" viewBox="0 0 144 225"><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="garbage-collection" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="family-no-father-2.svg"><path id="Rectangle-2" fill="#FFF9EB" stroke="#BCA68E" stroke-width="2" d="M34 91h78v26H34z"/><text id="Object" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="50" y="107">Object</tspan></text><text id="family" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="15" y="67">family</tspan></text><path id="Rectangle-4" fill="#FFF9EB" stroke="#BCA68E" stroke-width="2" d="M16 166h118v48H16z"/><text id="name:-"Ann"" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="34" y="203">name: "Ann"</tspan></text><text id="mother" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="16" y="142">mother</tspan></text><text id="Object-3" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="48" y="183">Object</tspan></text><path id="Line" fill="#EE6B47" fill-rule="nonzero" d="M75.5 47.5v18h6l-7 14-7-14h6v-18h2zM74.5 122.5v19h6l-7 14-7-14h6v-19h2z"/><path id="Rectangle-1" fill="#FFF9EB" stroke="#E8C48E" stroke-width="2" d="M27 16h93v26H27z"/><text id="<global>" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="43" y="33"><global></tspan></text></g></g></svg>
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
|
@ -1 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="399" height="225" viewBox="0 0 399 225"><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="garbage-collection" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="family-no-father.svg"><path id="Rectangle-2" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M187 78h78v26h-78z"/><text id="Object" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="203" y="94">Object</tspan></text><path id="Rectangle-3" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M69 146h118v48H69z"/><text id="Object-2" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="103" y="163">Object</tspan></text><text id="wife" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="211" y="146">wife</tspan></text><text id="family" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="171" y="61">family</tspan></text><text id="name:-"John"" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="83" y="183">name: "John"</tspan></text><path id="Rectangle-4" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M278 146h118v48H278z"/><text id="name:-"Ann"" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="299" y="183">name: "Ann"</tspan></text><text id="mother" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="306" y="125">mother</tspan></text><text id="Object-3" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="308" y="163">Object</tspan></text><path id="Line" fill="#C06334" fill-rule="nonzero" d="M228.5 41.5v18h6l-7 14-7-14h6v-18h2zM244.5 147.5l14 7-14 7v-6h-48v-2h48v-6zM272.236 109.11l.827.563 35.427 24.121 3.377-4.96L319.5 142.5l-15.512-2.093 3.377-4.96-35.428-24.12-.826-.563 1.125-1.653z"/><path id="Rectangle-5" stroke="#A7333A" stroke-width="2" d="M48 117h217v99H48z"/><g id="noun_48910_cc" transform="translate(7 114)"><path id="Shape" d="M17.503 1.75h-5.006a.341.341 0 00-.34.342v1.125h5.686V2.092a.341.341 0 00-.34-.341z"/><path id="Shape" fill="#A7333A" d="M28.364 3.217H19.59V2.092A2.09 2.09 0 0017.503 0h-5.006c-1.15 0-2.087.938-2.087 2.092v1.125H1.637c-.7 0-1.266.568-1.266 1.269v.09c0 .7.567 1.267 1.266 1.267h26.727c.699 0 1.266-.567 1.266-1.268v-.09c0-.7-.567-1.268-1.266-1.268zm-10.52 0h-5.687V2.092c0-.188.153-.341.34-.341h5.006a.34.34 0 01.34.34v1.126zM26.054 6.281H3.728c-1.298 0-2.35-.224-2.35 1.077L3.14 33.196c0 1.3 1.052 2.409 2.35 2.409h18.802c1.298 0 2.35-1.11 2.35-2.409l1.763-25.838c0-1.301-1.053-1.077-2.35-1.077zM9.637 32.193c-.377.012-.691-.261-.704-.612l-.694-19.917c-.012-.351.283-.647.66-.66.376-.013.69.261.703.613l.694 19.916c.013.351-.283.647-.659.66zm6.044-.63c0 .352-.306.637-.682.637-.377 0-.682-.286-.682-.637V11.634c0-.351.305-.636.682-.636.376 0 .682.285.682.636v19.93zm5.384.018c-.012.351-.327.625-.704.612-.376-.013-.672-.308-.66-.66l.695-19.916c.012-.352.326-.626.703-.613.377.014.672.309.66.66l-.694 19.917z"/></g><path id="Rectangle-1" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M181 13h93v26h-93z"/><text id="<global>" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="197" y="30"><global></tspan></text></g></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="399" height="225" viewBox="0 0 399 225"><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="garbage-collection" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="family-no-father.svg"><path id="Rectangle-2" fill="#FFF9EB" stroke="#BCA68E" stroke-width="2" d="M187 78h78v26h-78z"/><text id="Object" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="203" y="94">Object</tspan></text><path id="Rectangle-3" fill="#FFF9EB" stroke="#BCA68E" stroke-width="2" d="M69 146h118v48H69z"/><text id="Object-2" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="103" y="163">Object</tspan></text><text id="wife" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="211" y="146">wife</tspan></text><text id="family" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="171" y="61">family</tspan></text><text id="name:-"John"" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="83" y="183">name: "John"</tspan></text><path id="Rectangle-4" fill="#FFF9EB" stroke="#BCA68E" stroke-width="2" d="M278 146h118v48H278z"/><text id="name:-"Ann"" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="299" y="183">name: "Ann"</tspan></text><text id="mother" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="306" y="125">mother</tspan></text><text id="Object-3" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="308" y="163">Object</tspan></text><path id="Line" fill="#EE6B47" fill-rule="nonzero" d="M228.5 41.5v18h6l-7 14-7-14h6v-18h2zM244.5 147.5l14 7-14 7v-6h-48v-2h48v-6zM272.236 109.11l.827.563 35.427 24.121 3.377-4.96L319.5 142.5l-15.512-2.093 3.377-4.96-35.428-24.12-.826-.563 1.125-1.653z"/><path id="Rectangle-5" stroke="#D0021B" stroke-width="2" d="M48 117h217v99H48z"/><g id="noun_48910_cc" transform="translate(7 114)"><path id="Shape" d="M17.503 1.75h-5.006a.341.341 0 00-.34.342v1.125h5.686V2.092a.341.341 0 00-.34-.341z"/><path id="Shape" fill="#D0021B" d="M28.364 3.217H19.59V2.092A2.09 2.09 0 0017.503 0h-5.006c-1.15 0-2.087.938-2.087 2.092v1.125H1.637c-.7 0-1.266.568-1.266 1.269v.09c0 .7.567 1.267 1.266 1.267h26.727c.699 0 1.266-.567 1.266-1.268v-.09c0-.7-.567-1.268-1.266-1.268zm-10.52 0h-5.687V2.092c0-.188.153-.341.34-.341h5.006a.34.34 0 01.34.34v1.126zM26.054 6.281H3.728c-1.298 0-2.35-.224-2.35 1.077L3.14 33.196c0 1.3 1.052 2.409 2.35 2.409h18.802c1.298 0 2.35-1.11 2.35-2.409l1.763-25.838c0-1.301-1.053-1.077-2.35-1.077zM9.637 32.193c-.377.012-.691-.261-.704-.612l-.694-19.917c-.012-.351.283-.647.66-.66.376-.013.69.261.703.613l.694 19.916c.013.351-.283.647-.659.66zm6.044-.63c0 .352-.306.637-.682.637-.377 0-.682-.286-.682-.637V11.634c0-.351.305-.636.682-.636.376 0 .682.285.682.636v19.93zm5.384.018c-.012.351-.327.625-.704.612-.376-.013-.672-.308-.66-.66l.695-19.916c.012-.352.326-.626.703-.613.377.014.672.309.66.66l-.694 19.917z"/></g><path id="Rectangle-1" fill="#FFF9EB" stroke="#E8C48E" stroke-width="2" d="M181 13h93v26h-93z"/><text id="<global>" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="197" y="30"><global></tspan></text></g></g></svg>
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
|
@ -1 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="337" height="204" viewBox="0 0 337 204"><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="garbage-collection" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="family.svg"><path id="Line" fill="#C06334" fill-rule="nonzero" d="M147.5 183.5v6h46v2h-46v6l-14-7 14-7z"/><path id="Rectangle-2" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M123 80h78v26h-78z"/><text id="Object" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="139" y="96">Object</tspan></text><path id="Rectangle-3" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M5 148h118v48H5z"/><text id="Object-2" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="39" y="165">Object</tspan></text><text id="father" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="35" y="127">father</tspan></text><text id="wife" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="147" y="148">wife</tspan></text><text id="family" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="107" y="63">family</tspan></text><text id="name:-"John"" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="19" y="185">name: "John"</tspan></text><path id="Rectangle-4" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M214 148h118v48H214z"/><text id="name:-"Ann"" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="230" y="185">name: "Ann"</tspan></text><text id="mother" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="242" y="127">mother</tspan></text><text id="Object-3" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="244" y="165">Object</tspan></text><path id="Line" fill="#C06334" fill-rule="nonzero" d="M164.5 43.5v18h6l-7 14-7-14h6v-18h2z"/><path id="Line-2" fill="#C06334" fill-rule="nonzero" d="M114.75 111.108l1.142 1.642-.82.57-34.508 24.005 3.426 4.926L68.5 144.5l7.495-13.741 3.426 4.924 34.508-24.004.82-.571z"/><path id="Line" fill="#C06334" fill-rule="nonzero" d="M180.5 149.5l14 7-14 7v-6h-48v-2h48v-6z"/><text id="husband" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="136" y="181">husband</tspan></text><path id="Line" fill="#C06334" fill-rule="nonzero" d="M208.236 111.11l.827.563 35.427 24.121 3.377-4.96L255.5 144.5l-15.512-2.093 3.377-4.96-35.428-24.12-.826-.563 1.125-1.653z"/><path id="Rectangle-1" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M88 13h151v26H88z"/><text id="<global-variable>" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="92" y="30"><global variable></tspan></text></g></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="337" height="204" viewBox="0 0 337 204"><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="garbage-collection" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="family.svg"><path id="Line" fill="#EE6B47" fill-rule="nonzero" d="M147.5 183.5v6h46v2h-46v6l-14-7 14-7z"/><path id="Rectangle-2" fill="#FFF9EB" stroke="#BCA68E" stroke-width="2" d="M123 80h78v26h-78z"/><text id="Object" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="139" y="96">Object</tspan></text><path id="Rectangle-3" fill="#FFF9EB" stroke="#BCA68E" stroke-width="2" d="M5 148h118v48H5z"/><text id="Object-2" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="39" y="165">Object</tspan></text><text id="father" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="35" y="127">father</tspan></text><text id="wife" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="147" y="148">wife</tspan></text><text id="family" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="107" y="63">family</tspan></text><text id="name:-"John"" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="19" y="185">name: "John"</tspan></text><path id="Rectangle-4" fill="#FFF9EB" stroke="#BCA68E" stroke-width="2" d="M214 148h118v48H214z"/><text id="name:-"Ann"" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="230" y="185">name: "Ann"</tspan></text><text id="mother" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="242" y="127">mother</tspan></text><text id="Object-3" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="244" y="165">Object</tspan></text><path id="Line" fill="#EE6B47" fill-rule="nonzero" d="M164.5 43.5v18h6l-7 14-7-14h6v-18h2z"/><path id="Line-2" fill="#EE6B47" fill-rule="nonzero" d="M114.75 111.108l1.142 1.642-.82.57-34.508 24.005 3.426 4.926L68.5 144.5l7.495-13.741 3.426 4.924 34.508-24.004.82-.571z"/><path id="Line" fill="#EE6B47" fill-rule="nonzero" d="M180.5 149.5l14 7-14 7v-6h-48v-2h48v-6z"/><text id="husband" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="136" y="181">husband</tspan></text><path id="Line" fill="#EE6B47" fill-rule="nonzero" d="M208.236 111.11l.827.563 35.427 24.121 3.377-4.96L255.5 144.5l-15.512-2.093 3.377-4.96-35.428-24.12-.826-.563 1.125-1.653z"/><path id="Rectangle-1" fill="#FFF9EB" stroke="#E8C48E" stroke-width="2" d="M88 13h151v26H88z"/><text id="<global-variable>" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="92" y="30"><global variable></tspan></text></g></g></svg>
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 7.4 KiB |
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 9 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
@ -1 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="144" height="159" viewBox="0 0 144 159"><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="garbage-collection" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="memory-user-john-admin.svg"><text id="user" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="12" y="67">user</tspan></text><path id="Rectangle-4" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M16 93h118v48H16z"/><text id="name:-"John"" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="30" y="130">name: "John"</tspan></text><text id="Object" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="44" y="110">Object</tspan></text><path id="Line" fill="#C06334" fill-rule="nonzero" d="M55.5 47.5v18h6l-7 14-7-14h6v-18h2z"/><text id="admin" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="92" y="67">admin</tspan></text><path id="Line-Copy" fill="#C06334" fill-rule="nonzero" d="M83.5 47.5v18h6l-7 14-7-14h6v-18h2z"/><path id="Rectangle-1" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M27 16h93v26H27z"/><text id="<global>" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="43" y="33"><global></tspan></text></g></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="144" height="159" viewBox="0 0 144 159"><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="garbage-collection" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="memory-user-john-admin.svg"><text id="user" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="12" y="67">user</tspan></text><path id="Rectangle-4" fill="#FFF9EB" stroke="#BCA68E" stroke-width="2" d="M16 93h118v48H16z"/><text id="name:-"John"" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="30" y="130">name: "John"</tspan></text><text id="Object" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="44" y="110">Object</tspan></text><path id="Line" fill="#EE6B47" fill-rule="nonzero" d="M55.5 47.5v18h6l-7 14-7-14h6v-18h2z"/><text id="admin" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="92" y="67">admin</tspan></text><path id="Line-Copy" fill="#EE6B47" fill-rule="nonzero" d="M83.5 47.5v18h6l-7 14-7-14h6v-18h2z"/><path id="Rectangle-1" fill="#FFF9EB" stroke="#E8C48E" stroke-width="2" d="M27 16h93v26H27z"/><text id="<global>" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="43" y="33"><global></tspan></text></g></g></svg>
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
@ -1 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="225" height="159" viewBox="0 0 225 159"><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="garbage-collection" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="memory-user-john-lost.svg"><path id="Rectangle-4" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M61 89h118v48H61z"/><text id="name:-"John"" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="83" y="126">name: "John"</tspan></text><text id="Object-3" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="97" y="106">Object</tspan></text><path id="Rectangle-1" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M65 4h104v42H65z"/><text id="user:-null" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="79" y="40">user: null</tspan></text><text id="<global>" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="86" y="23"><global></tspan></text><path id="Rectangle-5" stroke="#A7333A" stroke-width="2" d="M47 69h154v86H47z"/><g id="noun_48910_cc" transform="translate(8 65)"><path id="Shape" d="M17.503 1.75h-5.006a.341.341 0 00-.34.342v1.125h5.686V2.092a.341.341 0 00-.34-.341z"/><path id="Shape" fill="#A7333A" d="M28.364 3.217H19.59V2.092A2.09 2.09 0 0017.503 0h-5.006c-1.15 0-2.087.938-2.087 2.092v1.125H1.637c-.7 0-1.266.568-1.266 1.269v.09c0 .7.567 1.267 1.266 1.267h26.727c.699 0 1.266-.567 1.266-1.268v-.09c0-.7-.567-1.268-1.266-1.268zm-10.52 0h-5.687V2.092c0-.188.153-.341.34-.341h5.006a.34.34 0 01.34.34v1.126zM26.054 6.281H3.728c-1.298 0-2.35-.224-2.35 1.077L3.14 33.196c0 1.3 1.052 2.409 2.35 2.409h18.802c1.298 0 2.35-1.11 2.35-2.409l1.763-25.838c0-1.301-1.053-1.077-2.35-1.077zM9.637 32.193c-.377.012-.691-.261-.704-.612l-.694-19.917c-.012-.351.283-.647.66-.66.376-.013.69.261.703.613l.694 19.916c.013.351-.283.647-.659.66zm6.044-.63c0 .352-.306.637-.682.637-.377 0-.682-.286-.682-.637V11.634c0-.351.305-.636.682-.636.376 0 .682.285.682.636v19.93zm5.384.018c-.012.351-.327.625-.704.612-.376-.013-.672-.308-.66-.66l.695-19.916c.012-.352.326-.626.703-.613.377.014.672.309.66.66l-.694 19.917z"/></g></g></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="225" height="159" viewBox="0 0 225 159"><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="garbage-collection" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="memory-user-john-lost.svg"><path id="Rectangle-4" fill="#FFF9EB" stroke="#BCA68E" stroke-width="2" d="M61 89h118v48H61z"/><text id="name:-"John"" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="83" y="126">name: "John"</tspan></text><text id="Object-3" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="97" y="106">Object</tspan></text><path id="Rectangle-1" fill="#FFF9EB" stroke="#E8C48E" stroke-width="2" d="M65 4h104v42H65z"/><text id="user:-null" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="79" y="40">user: null</tspan></text><text id="<global>" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="86" y="23"><global></tspan></text><path id="Rectangle-5" stroke="#D0021B" stroke-width="2" d="M47 69h154v86H47z"/><g id="noun_48910_cc" transform="translate(8 65)"><path id="Shape" d="M17.503 1.75h-5.006a.341.341 0 00-.34.342v1.125h5.686V2.092a.341.341 0 00-.34-.341z"/><path id="Shape" fill="#D0021B" d="M28.364 3.217H19.59V2.092A2.09 2.09 0 0017.503 0h-5.006c-1.15 0-2.087.938-2.087 2.092v1.125H1.637c-.7 0-1.266.568-1.266 1.269v.09c0 .7.567 1.267 1.266 1.267h26.727c.699 0 1.266-.567 1.266-1.268v-.09c0-.7-.567-1.268-1.266-1.268zm-10.52 0h-5.687V2.092c0-.188.153-.341.34-.341h5.006a.34.34 0 01.34.34v1.126zM26.054 6.281H3.728c-1.298 0-2.35-.224-2.35 1.077L3.14 33.196c0 1.3 1.052 2.409 2.35 2.409h18.802c1.298 0 2.35-1.11 2.35-2.409l1.763-25.838c0-1.301-1.053-1.077-2.35-1.077zM9.637 32.193c-.377.012-.691-.261-.704-.612l-.694-19.917c-.012-.351.283-.647.66-.66.376-.013.69.261.703.613l.694 19.916c.013.351-.283.647-.659.66zm6.044-.63c0 .352-.306.637-.682.637-.377 0-.682-.286-.682-.637V11.634c0-.351.305-.636.682-.636.376 0 .682.285.682.636v19.93zm5.384.018c-.012.351-.327.625-.704.612-.376-.013-.672-.308-.66-.66l.695-19.916c.012-.352.326-.626.703-.613.377.014.672.309.66.66l-.694 19.917z"/></g></g></g></svg>
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
|
@ -1 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="144" height="150" viewBox="0 0 144 150"><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="garbage-collection" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="memory-user-john.svg"><text id="user" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="31" y="69">user</tspan></text><path id="Rectangle-4" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M16 95h118v48H16z"/><text id="name:-"John"" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="32" y="132">name: "John"</tspan></text><text id="Object-3" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="46" y="112">Object</tspan></text><path id="Line" fill="#C06334" fill-rule="nonzero" d="M75.5 49.5v18h6l-7 14-7-14h6v-18h2z"/><path id="Rectangle-1" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M27 12h93v26H27z"/><text id="<global>" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="43" y="29"><global></tspan></text></g></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="144" height="150" viewBox="0 0 144 150"><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="garbage-collection" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="memory-user-john.svg"><text id="user" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="31" y="69">user</tspan></text><path id="Rectangle-4" fill="#FFF9EB" stroke="#BCA68E" stroke-width="2" d="M16 95h118v48H16z"/><text id="name:-"John"" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="32" y="132">name: "John"</tspan></text><text id="Object-3" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="46" y="112">Object</tspan></text><path id="Line" fill="#EE6B47" fill-rule="nonzero" d="M75.5 49.5v18h6l-7 14-7-14h6v-18h2z"/><path id="Rectangle-1" fill="#FFF9EB" stroke="#E8C48E" stroke-width="2" d="M27 12h93v26H27z"/><text id="<global>" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="43" y="29"><global></tspan></text></g></g></svg>
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
@ -6,7 +6,7 @@ importance: 5
|
|||
|
||||
Create an object `calculator` with three methods:
|
||||
|
||||
- `read()` prompts for two values and saves them as object properties with names `a` and `b` respectively.
|
||||
- `read()` prompts for two values and saves them as object properties.
|
||||
- `sum()` returns the sum of saved values.
|
||||
- `mul()` multiplies saved values and returns the result.
|
||||
|
||||
|
@ -21,3 +21,4 @@ alert( calculator.mul() );
|
|||
```
|
||||
|
||||
[demo]
|
||||
|
||||
|
|
|
@ -11,6 +11,5 @@ let ladder = {
|
|||
},
|
||||
showStep: function() {
|
||||
alert(this.step);
|
||||
return this;
|
||||
}
|
||||
};
|
|
@ -32,14 +32,6 @@ describe('Ladder', function() {
|
|||
it('down().up().up().up() ', function() {
|
||||
assert.equal(ladder.down().up().up().up().step, 2);
|
||||
});
|
||||
|
||||
it('showStep() should return this', function() {
|
||||
assert.equal(ladder.showStep(), ladder);
|
||||
});
|
||||
|
||||
it('up().up().down().showStep().down().showStep()', function () {
|
||||
assert.equal(ladder.up().up().down().showStep().down().showStep().step, 0)
|
||||
});
|
||||
|
||||
after(function() {
|
||||
ladder.step = 0;
|
||||
|
|
|
@ -23,7 +23,7 @@ let ladder = {
|
|||
}
|
||||
};
|
||||
|
||||
ladder.up().up().down().showStep().down().showStep(); // shows 1 then 0
|
||||
ladder.up().up().down().up().down().showStep(); // 1
|
||||
```
|
||||
|
||||
We also can write a single call per line. For long chains it's more readable:
|
||||
|
@ -33,7 +33,7 @@ ladder
|
|||
.up()
|
||||
.up()
|
||||
.down()
|
||||
.showStep() // 1
|
||||
.up()
|
||||
.down()
|
||||
.showStep(); // 0
|
||||
.showStep(); // 1
|
||||
```
|
||||
|
|
|
@ -28,14 +28,12 @@ ladder.up();
|
|||
ladder.up();
|
||||
ladder.down();
|
||||
ladder.showStep(); // 1
|
||||
ladder.down();
|
||||
ladder.showStep(); // 0
|
||||
```
|
||||
|
||||
Modify the code of `up`, `down` and `showStep` to make the calls chainable, like this:
|
||||
|
||||
```js
|
||||
ladder.up().up().down().showStep().down().showStep(); // shows 1 then 0
|
||||
ladder.up().up().down().showStep(); // 1
|
||||
```
|
||||
|
||||
Such approach is widely used across JavaScript libraries.
|
||||
|
|
|
@ -51,7 +51,7 @@ let user = {
|
|||
// first, declare
|
||||
function sayHi() {
|
||||
alert("Hello!");
|
||||
}
|
||||
};
|
||||
|
||||
// then add as a method
|
||||
user.sayHi = sayHi;
|
||||
|
@ -81,7 +81,7 @@ user = {
|
|||
// method shorthand looks better, right?
|
||||
user = {
|
||||
*!*
|
||||
sayHi() { // same as "sayHi: function(){...}"
|
||||
sayHi() { // same as "sayHi: function()"
|
||||
*/!*
|
||||
alert("Hello");
|
||||
}
|
||||
|
@ -90,7 +90,7 @@ user = {
|
|||
|
||||
As demonstrated, we can omit `"function"` and just write `sayHi()`.
|
||||
|
||||
To tell the truth, the notations are not fully identical. There are subtle differences related to object inheritance (to be covered later), but for now they do not matter. In almost all cases, the shorter syntax is preferred.
|
||||
To tell the truth, the notations are not fully identical. There are subtle differences related to object inheritance (to be covered later), but for now they do not matter. In almost all cases the shorter syntax is preferred.
|
||||
|
||||
## "this" in methods
|
||||
|
||||
|
|
|
@ -4,14 +4,14 @@ importance: 2
|
|||
|
||||
# Two functions – one object
|
||||
|
||||
Is it possible to create functions `A` and `B` so that `new A() == new B()`?
|
||||
Is it possible to create functions `A` and `B` such as `new A()==new B()`?
|
||||
|
||||
```js no-beautify
|
||||
function A() { ... }
|
||||
function B() { ... }
|
||||
|
||||
let a = new A();
|
||||
let b = new B();
|
||||
let a = new A;
|
||||
let b = new B;
|
||||
|
||||
alert( a == b ); // true
|
||||
```
|
||||
|
|
|
@ -6,7 +6,7 @@ importance: 5
|
|||
|
||||
Create a constructor function `Calculator` that creates objects with 3 methods:
|
||||
|
||||
- `read()` prompts for two values and saves them as object properties with names `a` and `b` respectively.
|
||||
- `read()` asks for two values using `prompt` and remembers them in object properties.
|
||||
- `sum()` returns the sum of these properties.
|
||||
- `mul()` returns the multiplication product of these properties.
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Constructor, operator "new"
|
||||
|
||||
The regular `{...}` syntax allows us to create one object. But often we need to create many similar objects, like multiple users or menu items and so on.
|
||||
The regular `{...}` syntax allows to create one object. But often we need to create many similar objects, like multiple users or menu items and so on.
|
||||
|
||||
That can be done using constructor functions and the `"new"` operator.
|
||||
|
||||
|
@ -64,14 +64,13 @@ Now if we want to create other users, we can call `new User("Ann")`, `new User("
|
|||
|
||||
That's the main purpose of constructors -- to implement reusable object creation code.
|
||||
|
||||
Let's note once again -- technically, any function (except arrow functions, as they don't have `this`) can be used as a constructor. It can be run with `new`, and it will execute the algorithm above. The "capital letter first" is a common agreement, to make it clear that a function is to be run with `new`.
|
||||
Let's note once again -- technically, any function can be used as a constructor. That is: any function can be run with `new`, and it will execute the algorithm above. The "capital letter first" is a common agreement, to make it clear that a function is to be run with `new`.
|
||||
|
||||
````smart header="new function() { ... }"
|
||||
If we have many lines of code all about creation of a single complex object, we can wrap them in an immediately called constructor function, like this:
|
||||
If we have many lines of code all about creation of a single complex object, we can wrap them in constructor function, like this:
|
||||
|
||||
```js
|
||||
// create a function and immediately call it with new
|
||||
let user = new function() {
|
||||
let user = new function() {
|
||||
this.name = "John";
|
||||
this.isAdmin = false;
|
||||
|
||||
|
@ -81,7 +80,7 @@ let user = new function() {
|
|||
};
|
||||
```
|
||||
|
||||
This constructor can't be called again, because it is not saved anywhere, just created and called. So this trick aims to encapsulate the code that constructs the single object, without future reuse.
|
||||
The constructor can't be called again, because it is not saved anywhere, just created and called. So this trick aims to encapsulate the code that constructs the single object, without future reuse.
|
||||
````
|
||||
|
||||
## Constructor mode test: new.target
|
||||
|
@ -171,7 +170,7 @@ alert( new SmallUser().name ); // John
|
|||
Usually constructors don't have a `return` statement. Here we mention the special behavior with returning objects mainly for the sake of completeness.
|
||||
|
||||
````smart header="Omitting parentheses"
|
||||
By the way, we can omit parentheses after `new`:
|
||||
By the way, we can omit parentheses after `new`, if it has no arguments:
|
||||
|
||||
```js
|
||||
let user = new User; // <-- no parentheses
|
||||
|
|
|
@ -25,14 +25,14 @@ That's the expected result. JavaScript works like this. As `user.address` is `un
|
|||
|
||||
In many practical cases we'd prefer to get `undefined` instead of an error here (meaning "no street").
|
||||
|
||||
...and another example. In Web development, we can get an object that corresponds to a web page element using a special method call, such as `document.querySelector('.elem')`, and it returns `null` when there's no such element.
|
||||
...And another example. In the web development, we can get an object that corresponds to a web page element using a special method call, such as `document.querySelector('.elem')`, and it returns `null` when there's no such element.
|
||||
|
||||
```js run
|
||||
// document.querySelector('.elem') is null if there's no element
|
||||
let html = document.querySelector('.elem').innerHTML; // error if it's null
|
||||
```
|
||||
|
||||
Once again, if the element doesn't exist, we'll get an error accessing `.innerHTML` property of `null`. And in some cases, when the absence of the element is normal, we'd like to avoid the error and just accept `html = null` as the result.
|
||||
Once again, if the element doesn't exist, we'll get an error accessing `.innerHTML` of `null`. And in some cases, when the absence of the element is normal, we'd like to avoid the error and just accept `html = null` as the result.
|
||||
|
||||
How can we do this?
|
||||
|
||||
|
@ -44,19 +44,11 @@ let user = {};
|
|||
alert(user.address ? user.address.street : undefined);
|
||||
```
|
||||
|
||||
It works, there's no error... But it's quite inelegant. As you can see, the `"user.address"` appears twice in the code.
|
||||
It works, there's no error... But it's quite inelegant. As you can see, the `"user.address"` appears twice in the code. For more deeply nested properties, that becomes a problem as more repetitions are required.
|
||||
|
||||
Here's how the same would look for `document.querySelector`:
|
||||
E.g. let's try getting `user.address.street.name`.
|
||||
|
||||
```js run
|
||||
let html = document.querySelector('.elem') ? document.querySelector('.elem').innerHTML : null;
|
||||
```
|
||||
|
||||
We can see that the element search `document.querySelector('.elem')` is actually called twice here. Not good.
|
||||
|
||||
For more deeply nested properties, it becomes even uglier, as more repetitions are required.
|
||||
|
||||
E.g. let's get `user.address.street.name` in a similar fashion.
|
||||
We need to check both `user.address` and `user.address.street`:
|
||||
|
||||
```js
|
||||
let user = {}; // user has no address
|
||||
|
@ -66,7 +58,7 @@ alert(user.address ? user.address.street ? user.address.street.name : null : nul
|
|||
|
||||
That's just awful, one may even have problems understanding such code.
|
||||
|
||||
There's a little better way to write it, using the `&&` operator:
|
||||
Don't even care to, as there's a better way to write it, using the `&&` operator:
|
||||
|
||||
```js run
|
||||
let user = {}; // user has no address
|
||||
|
@ -100,12 +92,6 @@ alert( user?.address?.street ); // undefined (no error)
|
|||
|
||||
The code is short and clean, there's no duplication at all.
|
||||
|
||||
Here's an example with `document.querySelector`:
|
||||
|
||||
```js run
|
||||
let html = document.querySelector('.elem')?.innerHTML; // will be undefined, if there's no element
|
||||
```
|
||||
|
||||
Reading the address with `user?.address` works even if `user` object doesn't exist:
|
||||
|
||||
```js run
|
||||
|
@ -122,9 +108,9 @@ E.g. in `user?.address.street.name` the `?.` allows `user` to safely be `null/un
|
|||
```warn header="Don't overuse the optional chaining"
|
||||
We should use `?.` only where it's ok that something doesn't exist.
|
||||
|
||||
For example, if according to our code logic `user` object must exist, but `address` is optional, then we should write `user.address?.street`, but not `user?.address?.street`.
|
||||
For example, if according to our coding logic `user` object must exist, but `address` is optional, then we should write `user.address?.street`, but not `user?.address?.street`.
|
||||
|
||||
Then, if `user` happens to be undefined, we'll see a programming error about it and fix it. Otherwise, if we overuse `?.`, coding errors can be silenced where not appropriate, and become more difficult to debug.
|
||||
So, if `user` happens to be undefined due to a mistake, we'll see a programming error about it and fix it. Otherwise, coding errors can be silenced where not appropriate, and become more difficult to debug.
|
||||
```
|
||||
|
||||
````warn header="The variable before `?.` must be declared"
|
||||
|
@ -141,7 +127,7 @@ The variable must be declared (e.g. `let/const/var user` or as a function parame
|
|||
|
||||
As it was said before, the `?.` immediately stops ("short-circuits") the evaluation if the left part doesn't exist.
|
||||
|
||||
So, if there are any further function calls or operations to the right of `?.`, they won't be made.
|
||||
So, if there are any further function calls or side effects, they don't occur.
|
||||
|
||||
For instance:
|
||||
|
||||
|
@ -149,7 +135,7 @@ For instance:
|
|||
let user = null;
|
||||
let x = 0;
|
||||
|
||||
user?.sayHi(x++); // no "user", so the execution doesn't reach sayHi call and x++
|
||||
user?.sayHi(x++); // no "sayHi", so the execution doesn't reach x++
|
||||
|
||||
alert(x); // 0, value not incremented
|
||||
```
|
||||
|
@ -176,13 +162,13 @@ userAdmin.admin?.(); // I am admin
|
|||
*/!*
|
||||
|
||||
*!*
|
||||
userGuest.admin?.(); // nothing happens (no such method)
|
||||
userGuest.admin?.(); // nothing (no such method)
|
||||
*/!*
|
||||
```
|
||||
|
||||
Here, in both lines we first use the dot (`userAdmin.admin`) to get `admin` property, because we assume that the `user` object exists, so it's safe read from it.
|
||||
Here, in both lines we first use the dot (`userAdmin.admin`) to get `admin` property, because we assume that the user object exists, so it's safe read from it.
|
||||
|
||||
Then `?.()` checks the left part: if the `admin` function exists, then it runs (that's so for `userAdmin`). Otherwise (for `userGuest`) the evaluation stops without errors.
|
||||
Then `?.()` checks the left part: if the admin function exists, then it runs (that's so for `userAdmin`). Otherwise (for `userGuest`) the evaluation stops without errors.
|
||||
|
||||
The `?.[]` syntax also works, if we'd like to use brackets `[]` to access properties instead of dot `.`. Similar to previous cases, it allows to safely read a property from an object that may not exist.
|
||||
|
||||
|
@ -193,7 +179,7 @@ let user1 = {
|
|||
firstName: "John"
|
||||
};
|
||||
|
||||
let user2 = null;
|
||||
let user2 = null;
|
||||
|
||||
alert( user1?.[key] ); // John
|
||||
alert( user2?.[key] ); // undefined
|
||||
|
@ -206,16 +192,17 @@ delete user?.name; // delete user.name if user exists
|
|||
```
|
||||
|
||||
````warn header="We can use `?.` for safe reading and deleting, but not writing"
|
||||
The optional chaining `?.` has no use on the left side of an assignment.
|
||||
The optional chaining `?.` has no use at the left side of an assignment.
|
||||
|
||||
For example:
|
||||
```js run
|
||||
let user = null;
|
||||
|
||||
user?.name = "John"; // Error, doesn't work
|
||||
// because it evaluates to: undefined = "John"
|
||||
// because it evaluates to undefined = "John"
|
||||
```
|
||||
|
||||
It's just not that smart.
|
||||
````
|
||||
|
||||
## Summary
|
||||
|
@ -230,4 +217,4 @@ As we can see, all of them are straightforward and simple to use. The `?.` check
|
|||
|
||||
A chain of `?.` allows to safely access nested properties.
|
||||
|
||||
Still, we should apply `?.` carefully, only where it's acceptable, according to our code logic, that the left part doesn't exist. So that it won't hide programming errors from us, if they occur.
|
||||
Still, we should apply `?.` carefully, only where it's acceptable that the left part doesn't exist. So that it won't hide programming errors from us, if they occur.
|
||||
|
|
|
@ -1,16 +1,9 @@
|
|||
|
||||
# Symbol type
|
||||
|
||||
By specification, only two primitive types may serve as object property keys:
|
||||
By specification, object property keys may be either of string type, or of symbol type. Not numbers, not booleans, only strings or symbols, these two types.
|
||||
|
||||
- string type, or
|
||||
- symbol type.
|
||||
|
||||
Otherwise, if one uses another type, such as number, it's autoconverted to string. So that `obj[1]` is the same as `obj["1"]`, and `obj[true]` is the same as `obj["true"]`.
|
||||
|
||||
Until now we've been using only strings.
|
||||
|
||||
Now let's explore symbols, see what they can do for us.
|
||||
Till now we've been using only strings. Now let's see the benefits that symbols can give us.
|
||||
|
||||
## Symbols
|
||||
|
||||
|
@ -19,17 +12,18 @@ A "symbol" represents a unique identifier.
|
|||
A value of this type can be created using `Symbol()`:
|
||||
|
||||
```js
|
||||
// id is a new symbol
|
||||
let id = Symbol();
|
||||
```
|
||||
|
||||
Upon creation, we can give symbols a description (also called a symbol name), mostly useful for debugging purposes:
|
||||
Upon creation, we can give symbol a description (also called a symbol name), mostly useful for debugging purposes:
|
||||
|
||||
```js
|
||||
// id is a symbol with the description "id"
|
||||
let id = Symbol("id");
|
||||
```
|
||||
|
||||
Symbols are guaranteed to be unique. Even if we create many symbols with exactly the same description, they are different values. The description is just a label that doesn't affect anything.
|
||||
Symbols are guaranteed to be unique. Even if we create many symbols with the same description, they are different values. The description is just a label that doesn't affect anything.
|
||||
|
||||
For instance, here are two symbols with the same description -- they are not equal:
|
||||
|
||||
|
@ -44,8 +38,6 @@ alert(id1 == id2); // false
|
|||
|
||||
If you are familiar with Ruby or another language that also has some sort of "symbols" -- please don't be misguided. JavaScript symbols are different.
|
||||
|
||||
So, to summarize, a symbol is a "primitive unique value" with an optional description. Let's see where we can use them.
|
||||
|
||||
````warn header="Symbols don't auto-convert to a string"
|
||||
Most values in JavaScript support implicit conversion to a string. For instance, we can `alert` almost any value, and it will work. Symbols are special. They don't auto-convert.
|
||||
|
||||
|
@ -61,7 +53,6 @@ alert(id); // TypeError: Cannot convert a Symbol value to a string
|
|||
That's a "language guard" against messing up, because strings and symbols are fundamentally different and should not accidentally convert one into another.
|
||||
|
||||
If we really want to show a symbol, we need to explicitly call `.toString()` on it, like here:
|
||||
|
||||
```js run
|
||||
let id = Symbol("id");
|
||||
*!*
|
||||
|
@ -70,7 +61,6 @@ alert(id.toString()); // Symbol(id), now it works
|
|||
```
|
||||
|
||||
Or get `symbol.description` property to show the description only:
|
||||
|
||||
```js run
|
||||
let id = Symbol("id");
|
||||
*!*
|
||||
|
@ -82,7 +72,6 @@ alert(id.description); // id
|
|||
|
||||
## "Hidden" properties
|
||||
|
||||
|
||||
Symbols allow us to create "hidden" properties of an object, that no other part of code can accidentally access or overwrite.
|
||||
|
||||
For instance, if we're working with `user` objects, that belong to a third-party code. We'd like to add identifiers to them.
|
||||
|
@ -103,9 +92,9 @@ alert( user[id] ); // we can access the data using the symbol as the key
|
|||
|
||||
What's the benefit of using `Symbol("id")` over a string `"id"`?
|
||||
|
||||
As `user` objects belong to another codebase, it's unsafe to add fields to them, since we might affect pre-defined behavior in that other codebase. However, symbols cannot be accessed accidentally. The third-party code won't be aware of newly defined symbols, so it's safe to add symbols to the `user` objects.
|
||||
As `user` objects belongs to another code, and that code also works with them, we shouldn't just add any fields to it. That's unsafe. But a symbol cannot be accessed accidentally, the third-party code probably won't even see it, so it's probably all right to do.
|
||||
|
||||
Also, imagine that another script wants to have its own identifier inside `user`, for its own purposes.
|
||||
Also, imagine that another script wants to have its own identifier inside `user`, for its own purposes. That may be another JavaScript library, so that the scripts are completely unaware of each other.
|
||||
|
||||
Then that script can create its own `Symbol("id")`, like this:
|
||||
|
||||
|
@ -169,10 +158,10 @@ for (let key in user) alert(key); // name, age (no symbols)
|
|||
*/!*
|
||||
|
||||
// the direct access by the symbol works
|
||||
alert( "Direct: " + user[id] ); // Direct: 123
|
||||
alert( "Direct: " + user[id] );
|
||||
```
|
||||
|
||||
[Object.keys(user)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys) also ignores them. That's a part of the general "hiding symbolic properties" principle. If another script or a library loops over our object, it won't unexpectedly access a symbolic property.
|
||||
`Object.keys(user)` also ignores them. That's a part of the general "hiding symbolic properties" principle. If another script or a library loops over our object, it won't unexpectedly access a symbolic property.
|
||||
|
||||
In contrast, [Object.assign](mdn:js/Object/assign) copies both string and symbol properties:
|
||||
|
||||
|
@ -217,12 +206,12 @@ Symbols inside the registry are called *global symbols*. If we want an applicati
|
|||
```smart header="That sounds like Ruby"
|
||||
In some programming languages, like Ruby, there's a single symbol per name.
|
||||
|
||||
In JavaScript, as we can see, that's true for global symbols.
|
||||
In JavaScript, as we can see, that's right for global symbols.
|
||||
```
|
||||
|
||||
### Symbol.keyFor
|
||||
|
||||
We have seen that for global symbols, `Symbol.for(key)` returns a symbol by name. To do the opposite -- return a name by global symbol -- we can use: `Symbol.keyFor(sym)`:
|
||||
For global symbols, not only `Symbol.for(key)` returns a symbol by name, but there's a reverse call: `Symbol.keyFor(sym)`, that does the reverse: returns a name by a global symbol.
|
||||
|
||||
For instance:
|
||||
|
||||
|
@ -238,7 +227,7 @@ alert( Symbol.keyFor(sym2) ); // id
|
|||
|
||||
The `Symbol.keyFor` internally uses the global symbol registry to look up the key for the symbol. So it doesn't work for non-global symbols. If the symbol is not global, it won't be able to find it and returns `undefined`.
|
||||
|
||||
That said, all symbols have the `description` property.
|
||||
That said, any symbols have `description` property.
|
||||
|
||||
For instance:
|
||||
|
||||
|
@ -279,11 +268,10 @@ Symbols are always different values, even if they have the same name. If we want
|
|||
Symbols have two main use cases:
|
||||
|
||||
1. "Hidden" object properties.
|
||||
|
||||
If we want to add a property into an object that "belongs" to another script or a library, we can create a symbol and use it as a property key. A symbolic property does not appear in `for..in`, so it won't be accidentally processed together with other properties. Also it won't be accessed directly, because another script does not have our symbol. So the property will be protected from accidental use or overwrite.
|
||||
|
||||
So we can "covertly" hide something into objects that we need, but others should not see, using symbolic properties.
|
||||
|
||||
2. There are many system symbols used by JavaScript which are accessible as `Symbol.*`. We can use them to alter some built-in behaviors. For instance, later in the tutorial we'll use `Symbol.iterator` for [iterables](info:iterable), `Symbol.toPrimitive` to setup [object-to-primitive conversion](info:object-toprimitive) and so on.
|
||||
|
||||
Technically, symbols are not 100% hidden. There is a built-in method [Object.getOwnPropertySymbols(obj)](mdn:js/Object/getOwnPropertySymbols) that allows us to get all symbols. Also there is a method named [Reflect.ownKeys(obj)](mdn:js/Reflect/ownKeys) that returns *all* keys of an object including symbolic ones. But most libraries, built-in functions and syntax constructs don't use these methods.
|
||||
Technically, symbols are not 100% hidden. There is a built-in method [Object.getOwnPropertySymbols(obj)](mdn:js/Object/getOwnPropertySymbols) that allows us to get all symbols. Also there is a method named [Reflect.ownKeys(obj)](mdn:js/Reflect/ownKeys) that returns *all* keys of an object including symbolic ones. So they are not really hidden. But most libraries, built-in functions and syntax constructs don't use these methods.
|
||||
|
|
|
@ -3,40 +3,19 @@
|
|||
|
||||
What happens when objects are added `obj1 + obj2`, subtracted `obj1 - obj2` or printed using `alert(obj)`?
|
||||
|
||||
JavaScript doesn't allow you to customize how operators work on objects. Unlike some other programming languages, such as Ruby or C++, we can't implement a special object method to handle addition (or other operators).
|
||||
|
||||
In case of such operations, objects are auto-converted to primitives, and then the operation is carried out over these primitives and results in a primitive value.
|
||||
|
||||
That's an important limitation: the result of `obj1 + obj2` (or another math operation) can't be another object!
|
||||
|
||||
E.g. we can't make objects representing vectors or matrices (or achievements or whatever), add them and expect a "summed" object as the result. Such architectural feats are automatically "off the board".
|
||||
|
||||
So, because we can't technically do much here, there's no maths with objects in real projects. When it happens, with rare exceptions, it's because of a coding mistake.
|
||||
|
||||
In this chapter we'll cover how an object converts to primitive and how to customize it.
|
||||
|
||||
We have two purposes:
|
||||
|
||||
1. It will allow us to understand what's going on in case of coding mistakes, when such an operation happened accidentally.
|
||||
2. There are exceptions, where such operations are possible and look good. E.g. subtracting or comparing dates (`Date` objects). We'll come across them later.
|
||||
|
||||
## Conversion rules
|
||||
In that case, objects are auto-converted to primitives, and then the operation is carried out.
|
||||
|
||||
In the chapter <info:type-conversions> we've seen the rules for numeric, string and boolean conversions of primitives. But we left a gap for objects. Now, as we know about methods and symbols it becomes possible to fill it.
|
||||
|
||||
1. There's no conversion to boolean. All objects are `true` in a boolean context, as simple as that. There exist only numeric and string conversions.
|
||||
1. All objects are `true` in a boolean context. There are only numeric and string conversions.
|
||||
2. The numeric conversion happens when we subtract objects or apply mathematical functions. For instance, `Date` objects (to be covered in the chapter <info:date>) can be subtracted, and the result of `date1 - date2` is the time difference between two dates.
|
||||
3. As for the string conversion -- it usually happens when we output an object with `alert(obj)` and in similar contexts.
|
||||
3. As for the string conversion -- it usually happens when we output an object like `alert(obj)` and in similar contexts.
|
||||
|
||||
We can implement string and numeric conversion by ourselves, using special object methods.
|
||||
## ToPrimitive
|
||||
|
||||
Now let's get into technical details, because it's the only way to cover the topic in-depth.
|
||||
We can fine-tune string and numeric conversion, using special object methods.
|
||||
|
||||
## Hints
|
||||
|
||||
How does JavaScript decide which conversion to apply?
|
||||
|
||||
There are three variants of type conversion, that happen in various situations. They're called "hints", as described in the [specification](https://tc39.github.io/ecma262/#sec-toprimitive):
|
||||
There are three variants of type conversion, so-called "hints", described in the [specification](https://tc39.github.io/ecma262/#sec-toprimitive):
|
||||
|
||||
`"string"`
|
||||
: For an object-to-string conversion, when we're doing an operation on an object that expects a string, like `alert`:
|
||||
|
@ -64,12 +43,10 @@ There are three variants of type conversion, that happen in various situations.
|
|||
let greater = user1 > user2;
|
||||
```
|
||||
|
||||
Most built-in mathematical functions also include such conversion.
|
||||
|
||||
`"default"`
|
||||
: Occurs in rare cases when the operator is "not sure" what type to expect.
|
||||
|
||||
For instance, binary plus `+` can work both with strings (concatenates them) and numbers (adds them). So if a binary plus gets an object as an argument, it uses the `"default"` hint to convert it.
|
||||
For instance, binary plus `+` can work both with strings (concatenates them) and numbers (adds them), so both strings and numbers would do. So if a binary plus gets an object as an argument, it uses the `"default"` hint to convert it.
|
||||
|
||||
Also, if an object is compared using `==` with a string, number or a symbol, it's also unclear which conversion should be done, so the `"default"` hint is used.
|
||||
|
||||
|
@ -83,19 +60,21 @@ There are three variants of type conversion, that happen in various situations.
|
|||
|
||||
The greater and less comparison operators, such as `<` `>`, can work with both strings and numbers too. Still, they use the `"number"` hint, not `"default"`. That's for historical reasons.
|
||||
|
||||
In practice though, things are a bit simpler.
|
||||
In practice though, we don't need to remember these peculiar details, because all built-in objects except for one case (`Date` object, we'll learn it later) implement `"default"` conversion the same way as `"number"`. And we can do the same.
|
||||
|
||||
All built-in objects except for one case (`Date` object, we'll learn it later) implement `"default"` conversion the same way as `"number"`. And we probably should do the same.
|
||||
```smart header="No `\"boolean\"` hint"
|
||||
Please note -- there are only three hints. It's that simple.
|
||||
|
||||
Still, it's important to know about all 3 hints, soon we'll see why.
|
||||
There is no "boolean" hint (all objects are `true` in boolean context) or anything else. And if we treat `"default"` and `"number"` the same, like most built-ins do, then there are only two conversions.
|
||||
```
|
||||
|
||||
**To do the conversion, JavaScript tries to find and call three object methods:**
|
||||
|
||||
1. Call `obj[Symbol.toPrimitive](hint)` - the method with the symbolic key `Symbol.toPrimitive` (system symbol), if such method exists,
|
||||
2. Otherwise if hint is `"string"`
|
||||
- try calling `obj.toString()` or `obj.valueOf()`, whatever exists.
|
||||
- try `obj.toString()` and `obj.valueOf()`, whatever exists.
|
||||
3. Otherwise if hint is `"number"` or `"default"`
|
||||
- try calling `obj.valueOf()` or `obj.toString()`, whatever exists.
|
||||
- try `obj.valueOf()` and `obj.toString()`, whatever exists.
|
||||
|
||||
## Symbol.toPrimitive
|
||||
|
||||
|
@ -103,14 +82,11 @@ Let's start from the first method. There's a built-in symbol named `Symbol.toPri
|
|||
|
||||
```js
|
||||
obj[Symbol.toPrimitive] = function(hint) {
|
||||
// here goes the code to convert this object to a primitive
|
||||
// it must return a primitive value
|
||||
// must return a primitive value
|
||||
// hint = one of "string", "number", "default"
|
||||
};
|
||||
```
|
||||
|
||||
If the method `Symbol.toPrimitive` exists, it's used for all hints, and no more methods are needed.
|
||||
|
||||
For instance, here `user` object implements it:
|
||||
|
||||
```js run
|
||||
|
@ -130,17 +106,18 @@ alert(+user); // hint: number -> 1000
|
|||
alert(user + 500); // hint: default -> 1500
|
||||
```
|
||||
|
||||
As we can see from the code, `user` becomes a self-descriptive string or a money amount, depending on the conversion. The single method `user[Symbol.toPrimitive]` handles all conversion cases.
|
||||
As we can see from the code, `user` becomes a self-descriptive string or a money amount depending on the conversion. The single method `user[Symbol.toPrimitive]` handles all conversion cases.
|
||||
|
||||
|
||||
## toString/valueOf
|
||||
|
||||
If there's no `Symbol.toPrimitive` then JavaScript tries to find methods `toString` and `valueOf`:
|
||||
|
||||
- For the `"string"` hint: call `toString` method, and if it doesn't exist or if it returns an object instead of a primitive value, then call `valueOf` (so `toString` has the priority for string conversions).
|
||||
- For other hints: call `valueOf`, and if it doesn't exist or if it returns an object instead of a primitive value, then call `toString` (so `valueOf` has the priority for maths).
|
||||
|
||||
Methods `toString` and `valueOf` come from ancient times. They are not symbols (symbols did not exist that long ago), but rather "regular" string-named methods. They provide an alternative "old-style" way to implement the conversion.
|
||||
|
||||
If there's no `Symbol.toPrimitive` then JavaScript tries to find them and try in the order:
|
||||
|
||||
- `toString -> valueOf` for "string" hint.
|
||||
- `valueOf -> toString` otherwise.
|
||||
|
||||
These methods must return a primitive value. If `toString` or `valueOf` returns an object, then it's ignored (same as if there were no method).
|
||||
|
||||
By default, a plain object has following `toString` and `valueOf` methods:
|
||||
|
@ -159,9 +136,9 @@ alert(user.valueOf() === user); // true
|
|||
|
||||
So if we try to use an object as a string, like in an `alert` or so, then by default we see `[object Object]`.
|
||||
|
||||
The default `valueOf` is mentioned here only for the sake of completeness, to avoid any confusion. As you can see, it returns the object itself, and so is ignored. Don't ask me why, that's for historical reasons. So we can assume it doesn't exist.
|
||||
And the default `valueOf` is mentioned here only for the sake of completeness, to avoid any confusion. As you can see, it returns the object itself, and so is ignored. Don't ask me why, that's for historical reasons. So we can assume it doesn't exist.
|
||||
|
||||
Let's implement these methods to customize the conversion.
|
||||
Let's implement these methods.
|
||||
|
||||
For instance, here `user` does the same as above using a combination of `toString` and `valueOf` instead of `Symbol.toPrimitive`:
|
||||
|
||||
|
@ -206,27 +183,27 @@ alert(user + 500); // toString -> John500
|
|||
|
||||
In the absence of `Symbol.toPrimitive` and `valueOf`, `toString` will handle all primitive conversions.
|
||||
|
||||
### A conversion can return any primitive type
|
||||
## Return types
|
||||
|
||||
The important thing to know about all primitive-conversion methods is that they do not necessarily return the "hinted" primitive.
|
||||
|
||||
There is no control whether `toString` returns exactly a string, or whether `Symbol.toPrimitive` method returns a number for the hint `"number"`.
|
||||
There is no control whether `toString` returns exactly a string, or whether `Symbol.toPrimitive` method returns a number for a hint `"number"`.
|
||||
|
||||
The only mandatory thing: these methods must return a primitive, not an object.
|
||||
|
||||
```smart header="Historical notes"
|
||||
For historical reasons, if `toString` or `valueOf` returns an object, there's no error, but such value is ignored (like if the method didn't exist). That's because in ancient times there was no good "error" concept in JavaScript.
|
||||
|
||||
In contrast, `Symbol.toPrimitive` is stricter, it *must* return a primitive, otherwise there will be an error.
|
||||
In contrast, `Symbol.toPrimitive` *must* return a primitive, otherwise there will be an error.
|
||||
```
|
||||
|
||||
## Further conversions
|
||||
|
||||
As we know already, many operators and functions perform type conversions, e.g. multiplication `*` converts operands to numbers.
|
||||
|
||||
If we pass an object as an argument, then there are two stages of calculations:
|
||||
If we pass an object as an argument, then there are two stages:
|
||||
1. The object is converted to a primitive (using the rules described above).
|
||||
2. If the necessary for further calculations, the resulting primitive is also converted.
|
||||
2. If the resulting primitive isn't of the right type, it's converted.
|
||||
|
||||
For instance:
|
||||
|
||||
|
@ -263,18 +240,16 @@ The object-to-primitive conversion is called automatically by many built-in func
|
|||
There are 3 types (hints) of it:
|
||||
- `"string"` (for `alert` and other operations that need a string)
|
||||
- `"number"` (for maths)
|
||||
- `"default"` (few operators, usually objects implement it the same way as `"number"`)
|
||||
- `"default"` (few operators)
|
||||
|
||||
The specification describes explicitly which operator uses which hint.
|
||||
The specification describes explicitly which operator uses which hint. There are very few operators that "don't know what to expect" and use the `"default"` hint. Usually for built-in objects `"default"` hint is handled the same way as `"number"`, so in practice the last two are often merged together.
|
||||
|
||||
The conversion algorithm is:
|
||||
|
||||
1. Call `obj[Symbol.toPrimitive](hint)` if the method exists,
|
||||
2. Otherwise if hint is `"string"`
|
||||
- try calling `obj.toString()` or `obj.valueOf()`, whatever exists.
|
||||
- try `obj.toString()` and `obj.valueOf()`, whatever exists.
|
||||
3. Otherwise if hint is `"number"` or `"default"`
|
||||
- try calling `obj.valueOf()` or `obj.toString()`, whatever exists.
|
||||
- try `obj.valueOf()` and `obj.toString()`, whatever exists.
|
||||
|
||||
All these methods must return a primitive to work (if defined).
|
||||
|
||||
In practice, it's often enough to implement only `obj.toString()` as a "catch-all" method for string conversions that should return a "human-readable" representation of an object, for logging or debugging purposes.
|
||||
In practice, it's often enough to implement only `obj.toString()` as a "catch-all" method for all conversions that return a "human-readable" representation of an object, for logging or debugging purposes.
|
||||
|
|
|
@ -15,4 +15,4 @@ str.test = 5;
|
|||
alert(str.test);
|
||||
```
|
||||
|
||||
What do you think, will it work? What will be shown?
|
||||
How do you think, will it work? What will be shown?
|
||||
|
|
|
@ -39,7 +39,7 @@ Objects are "heavier" than primitives. They require additional resources to supp
|
|||
|
||||
Here's the paradox faced by the creator of JavaScript:
|
||||
|
||||
- There are many things one would want to do with a primitive, like a string or a number. It would be great to access them using methods.
|
||||
- There are many things one would want to do with a primitive like a string or a number. It would be great to access them as methods.
|
||||
- Primitives must be as fast and lightweight as possible.
|
||||
|
||||
The solution looks a little bit awkward, but here it is:
|
||||
|
@ -48,7 +48,7 @@ The solution looks a little bit awkward, but here it is:
|
|||
2. The language allows access to methods and properties of strings, numbers, booleans and symbols.
|
||||
3. In order for that to work, a special "object wrapper" that provides the extra functionality is created, and then is destroyed.
|
||||
|
||||
The "object wrappers" are different for each primitive type and are called: `String`, `Number`, `Boolean`, `Symbol` and `BigInt`. Thus, they provide different sets of methods.
|
||||
The "object wrappers" are different for each primitive type and are called: `String`, `Number`, `Boolean` and `Symbol`. Thus, they provide different sets of methods.
|
||||
|
||||
For instance, there exists a string method [str.toUpperCase()](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/String/toUpperCase) that returns a capitalized `str`.
|
||||
|
||||
|
@ -104,10 +104,9 @@ if (zero) { // zero is true, because it's an object
|
|||
}
|
||||
```
|
||||
|
||||
On the other hand, using the same functions `String/Number/Boolean` without `new` is totally fine and useful thing. They convert a value to the corresponding type: to a string, a number, or a boolean (primitive).
|
||||
On the other hand, using the same functions `String/Number/Boolean` without `new` is a totally sane and useful thing. They convert a value to the corresponding type: to a string, a number, or a boolean (primitive).
|
||||
|
||||
For example, this is entirely valid:
|
||||
|
||||
```js
|
||||
let num = Number("123"); // convert a string to number
|
||||
```
|
||||
|
|
|
@ -28,6 +28,6 @@ Note that `63.5` has no precision loss at all. That's because the decimal part `
|
|||
|
||||
|
||||
```js run
|
||||
alert( Math.round(6.35 * 10) / 10 ); // 6.35 -> 63.5 -> 64(rounded) -> 6.4
|
||||
alert( Math.round(6.35 * 10) / 10); // 6.35 -> 63.5 -> 64(rounded) -> 6.4
|
||||
```
|
||||
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
In modern JavaScript, there are two types of numbers:
|
||||
|
||||
1. Regular numbers in JavaScript are stored in 64-bit format [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754), also known as "double precision floating point numbers". These are numbers that we're using most of the time, and we'll talk about them in this chapter.
|
||||
1. Regular numbers in JavaScript are stored in 64-bit format [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754-2008_revision), also known as "double precision floating point numbers". These are numbers that we're using most of the time, and we'll talk about them in this chapter.
|
||||
|
||||
2. BigInt numbers represent integers of arbitrary length. They are sometimes needed because a regular integer number can't safely exceed <code>(2<sup>53</sup>-1)</code> or be less than <code>-(2<sup>53</sup>-1)</code>, as we mentioned earlier in the chapter <info:types>. As bigints are used in few special areas, we devote them a special chapter <info:bigint>.
|
||||
2. BigInt numbers, to represent integers of arbitrary length. They are sometimes needed, because a regular number can't exceed <code>2<sup>53</sup></code> or be less than <code>-2<sup>53</sup></code>. As bigints are used in few special areas, we devote them a special chapter <info:bigint>.
|
||||
|
||||
So here we'll talk about regular numbers. Let's expand our knowledge of them.
|
||||
|
||||
|
@ -22,7 +22,7 @@ We also can use underscore `_` as the separator:
|
|||
let billion = 1_000_000_000;
|
||||
```
|
||||
|
||||
Here the underscore `_` plays the role of the "[syntactic sugar](https://en.wikipedia.org/wiki/Syntactic_sugar)", it makes the number more readable. The JavaScript engine simply ignores `_` between digits, so it's exactly the same one billion as above.
|
||||
Here the underscore `_` plays the role of the "syntactic sugar", it makes the number more readable. The JavaScript engine simply ignores `_` between digits, so it's exactly the same one billion as above.
|
||||
|
||||
In real life though, we try to avoid writing long sequences of zeroes. We're too lazy for that. We'll try to write something like `"1bn"` for a billion or `"7.3bn"` for 7 billion 300 million. The same is true for most large numbers.
|
||||
|
||||
|
@ -37,35 +37,32 @@ alert( 7.3e9 ); // 7.3 billions (same as 7300000000 or 7_300_000_000)
|
|||
In other words, `e` multiplies the number by `1` with the given zeroes count.
|
||||
|
||||
```js
|
||||
1e3 === 1 * 1000; // e3 means *1000
|
||||
1.23e6 === 1.23 * 1000000; // e6 means *1000000
|
||||
1e3 = 1 * 1000 // e3 means *1000
|
||||
1.23e6 = 1.23 * 1000000 // e6 means *1000000
|
||||
```
|
||||
|
||||
Now let's write something very small. Say, 1 microsecond (one millionth of a second):
|
||||
|
||||
```js
|
||||
let mсs = 0.000001;
|
||||
let ms = 0.000001;
|
||||
```
|
||||
|
||||
Just like before, using `"e"` can help. If we'd like to avoid writing the zeroes explicitly, we could write the same as:
|
||||
Just like before, using `"e"` can help. If we'd like to avoid writing the zeroes explicitly, we could say the same as:
|
||||
|
||||
```js
|
||||
let mcs = 1e-6; // five zeroes to the left from 1
|
||||
let ms = 1e-6; // six zeroes to the left from 1
|
||||
```
|
||||
|
||||
If we count the zeroes in `0.000001`, there are 6 of them. So naturally it's `1e-6`.
|
||||
If we count the zeroes in `0.000001`, there are 6 of them. So naturally it's `1e-6`.
|
||||
|
||||
In other words, a negative number after `"e"` means a division by 1 with the given number of zeroes:
|
||||
|
||||
```js
|
||||
// -3 divides by 1 with 3 zeroes
|
||||
1e-3 === 1 / 1000; // 0.001
|
||||
1e-3 = 1 / 1000 (=0.001)
|
||||
|
||||
// -6 divides by 1 with 6 zeroes
|
||||
1.23e-6 === 1.23 / 1000000; // 0.00000123
|
||||
|
||||
// an example with a bigger number
|
||||
1234e-2 === 1234 / 100; // 12.34, decimal point moves 2 times
|
||||
1.23e-6 = 1.23 / 1000000 (=0.00000123)
|
||||
```
|
||||
|
||||
### Hex, binary and octal numbers
|
||||
|
@ -121,7 +118,6 @@ Please note that two dots in `123456..toString(36)` is not a typo. If we want to
|
|||
If we placed a single dot: `123456.toString(36)`, then there would be an error, because JavaScript syntax implies the decimal part after the first dot. And if we place one more dot, then JavaScript knows that the decimal part is empty and now goes the method.
|
||||
|
||||
Also could write `(123456).toString(36)`.
|
||||
|
||||
```
|
||||
|
||||
## Rounding
|
||||
|
@ -160,7 +156,7 @@ There are two ways to do so:
|
|||
|
||||
1. Multiply-and-divide.
|
||||
|
||||
For example, to round the number to the 2nd digit after the decimal, we can multiply the number by `100`, call the rounding function and then divide it back.
|
||||
For example, to round the number to the 2nd digit after the decimal, we can multiply the number by `100` (or a bigger power of 10), call the rounding function and then divide it back.
|
||||
```js run
|
||||
let num = 1.23456;
|
||||
|
||||
|
@ -181,20 +177,20 @@ There are two ways to do so:
|
|||
alert( num.toFixed(1) ); // "12.4"
|
||||
```
|
||||
|
||||
Please note that the result of `toFixed` is a string. If the decimal part is shorter than required, zeroes are appended to the end:
|
||||
Please note that result of `toFixed` is a string. If the decimal part is shorter than required, zeroes are appended to the end:
|
||||
|
||||
```js run
|
||||
let num = 12.34;
|
||||
alert( num.toFixed(5) ); // "12.34000", added zeroes to make exactly 5 digits
|
||||
```
|
||||
|
||||
We can convert it to a number using the unary plus or a `Number()` call, e.g write `+num.toFixed(5)`.
|
||||
We can convert it to a number using the unary plus or a `Number()` call: `+num.toFixed(5)`.
|
||||
|
||||
## Imprecise calculations
|
||||
|
||||
Internally, a number is represented in 64-bit format [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754), so there are exactly 64 bits to store a number: 52 of them are used to store the digits, 11 of them store the position of the decimal point, and 1 bit is for the sign.
|
||||
Internally, a number is represented in 64-bit format [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754-2008_revision), so there are exactly 64 bits to store a number: 52 of them are used to store the digits, 11 of them store the position of the decimal point (they are zero for integer numbers), and 1 bit is for the sign.
|
||||
|
||||
If a number is really huge, it may overflow the 64-bit storage and become a special numeric value `Infinity`:
|
||||
If a number is too big, it would overflow the 64-bit storage, potentially giving an infinity:
|
||||
|
||||
```js run
|
||||
alert( 1e500 ); // Infinity
|
||||
|
@ -202,7 +198,7 @@ alert( 1e500 ); // Infinity
|
|||
|
||||
What may be a little less obvious, but happens quite often, is the loss of precision.
|
||||
|
||||
Consider this (falsy!) equality test:
|
||||
Consider this (falsy!) test:
|
||||
|
||||
```js run
|
||||
alert( 0.1 + 0.2 == 0.3 ); // *!*false*/!*
|
||||
|
@ -216,13 +212,13 @@ Strange! What is it then if not `0.3`?
|
|||
alert( 0.1 + 0.2 ); // 0.30000000000000004
|
||||
```
|
||||
|
||||
Ouch! Imagine you're making an e-shopping site and the visitor puts `$0.10` and `$0.20` goods into their cart. The order total will be `$0.30000000000000004`. That would surprise anyone.
|
||||
Ouch! There are more consequences than an incorrect comparison here. Imagine you're making an e-shopping site and the visitor puts `$0.10` and `$0.20` goods into their cart. The order total will be `$0.30000000000000004`. That would surprise anyone.
|
||||
|
||||
But why does this happen?
|
||||
|
||||
A number is stored in memory in its binary form, a sequence of bits - ones and zeroes. But fractions like `0.1`, `0.2` that look simple in the decimal numeric system are actually unending fractions in their binary form.
|
||||
|
||||
What is `0.1`? It is one divided by ten `1/10`, one-tenth. In decimal numeral system such numbers are easily representable. Compare it to one-third: `1/3`. It becomes an endless fraction `0.33333(3)`.
|
||||
In other words, what is `0.1`? It is one divided by ten `1/10`, one-tenth. In decimal numeral system such numbers are easily representable. Compare it to one-third: `1/3`. It becomes an endless fraction `0.33333(3)`.
|
||||
|
||||
So, division by powers `10` is guaranteed to work well in the decimal system, but division by `3` is not. For the same reason, in the binary numeral system, the division by powers of `2` is guaranteed to work, but `1/10` becomes an endless binary fraction.
|
||||
|
||||
|
@ -249,7 +245,7 @@ Can we work around the problem? Sure, the most reliable method is to round the r
|
|||
|
||||
```js run
|
||||
let sum = 0.1 + 0.2;
|
||||
alert( sum.toFixed(2) ); // "0.30"
|
||||
alert( sum.toFixed(2) ); // 0.30
|
||||
```
|
||||
|
||||
Please note that `toFixed` always returns a string. It ensures that it has 2 digits after the decimal point. That's actually convenient if we have an e-shopping and need to show `$0.30`. For other cases, we can use the unary plus to coerce it into a number:
|
||||
|
@ -308,7 +304,7 @@ They belong to the type `number`, but are not "normal" numbers, so there are spe
|
|||
alert( isNaN("str") ); // true
|
||||
```
|
||||
|
||||
But do we need this function? Can't we just use the comparison `=== NaN`? Unfortunately not. The value `NaN` is unique in that it does not equal anything, including itself:
|
||||
But do we need this function? Can't we just use the comparison `=== NaN`? Sorry, but the answer is no. The value `NaN` is unique in that it does not equal anything, including itself:
|
||||
|
||||
```js run
|
||||
alert( NaN === NaN ); // false
|
||||
|
@ -332,46 +328,18 @@ let num = +prompt("Enter a number", '');
|
|||
alert( isFinite(num) );
|
||||
```
|
||||
|
||||
Please note that an empty or a space-only string is treated as `0` in all numeric functions including `isFinite`.
|
||||
Please note that an empty or a space-only string is treated as `0` in all numeric functions including `isFinite`.
|
||||
|
||||
````smart header="`Number.isNaN` and `Number.isFinite`"
|
||||
[Number.isNaN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isNaN) and [Number.isFinite](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isFinite) methods are the more "strict" versions of `isNaN` and `isFinite` functions. They do not autoconvert their argument into a number, but check if it belongs to the `number` type instead.
|
||||
```smart header="Compare with `Object.is`"
|
||||
|
||||
- `Number.isNaN(value)` returns `true` if the argument belongs to the `number` type and it is `NaN`. In any other case it returns `false`.
|
||||
|
||||
```js run
|
||||
alert( Number.isNaN(NaN) ); // true
|
||||
alert( Number.isNaN("str" / 2) ); // true
|
||||
|
||||
// Note the difference:
|
||||
alert( Number.isNaN("str") ); // false, because "str" belongs to the string type, not the number type
|
||||
alert( isNaN("str") ); // true, because isNaN converts string "str" into a number and gets NaN as a result of this conversion
|
||||
```
|
||||
|
||||
- `Number.isFinite(value)` returns `true` if the argument belongs to the `number` type and it is not `NaN/Infinity/-Infinity`. In any other case it returns `false`.
|
||||
|
||||
```js run
|
||||
alert( Number.isFinite(123) ); // true
|
||||
alert( Number.isFinite(Infinity) ); // false
|
||||
alert( Number.isFinite(2 / 0) ); // false
|
||||
|
||||
// Note the difference:
|
||||
alert( Number.isFinite("123") ); // false, because "123" belongs to the string type, not the number type
|
||||
alert( isFinite("123") ); // true, because isFinite converts string "123" into a number 123
|
||||
```
|
||||
|
||||
In a way, `Number.isNaN` and `Number.isFinite` are simpler and more straightforward than `isNaN` and `isFinite` functions. In practice though, `isNaN` and `isFinite` are mostly used, as they're shorter to write.
|
||||
````
|
||||
|
||||
```smart header="Comparison with `Object.is`"
|
||||
There is a special built-in method `Object.is` that compares values like `===`, but is more reliable for two edge cases:
|
||||
There is a special built-in method [`Object.is`](mdn:js/Object/is) that compares values like `===`, but is more reliable for two edge cases:
|
||||
|
||||
1. It works with `NaN`: `Object.is(NaN, NaN) === true`, that's a good thing.
|
||||
2. Values `0` and `-0` are different: `Object.is(0, -0) === false`, technically that's correct, because internally the number has a sign bit that may be different even if all other bits are zeroes.
|
||||
2. Values `0` and `-0` are different: `Object.is(0, -0) === false`, technically that's true, because internally the number has a sign bit that may be different even if all other bits are zeroes.
|
||||
|
||||
In all other cases, `Object.is(a, b)` is the same as `a === b`.
|
||||
|
||||
We mention `Object.is` here, because it's often used in JavaScript specification. When an internal algorithm needs to compare two values for being exactly the same, it uses `Object.is` (internally called [SameValue](https://tc39.github.io/ecma262/#sec-samevalue)).
|
||||
This way of comparison is often used in JavaScript specification. When an internal algorithm needs to compare two values for being exactly the same, it uses `Object.is` (internally called [SameValue](https://tc39.github.io/ecma262/#sec-samevalue)).
|
||||
```
|
||||
|
||||
|
||||
|
@ -431,8 +399,8 @@ A few examples:
|
|||
alert( Math.random() ); // ... (any random numbers)
|
||||
```
|
||||
|
||||
`Math.max(a, b, c...)` and `Math.min(a, b, c...)`
|
||||
: Returns the greatest and smallest from the arbitrary number of arguments.
|
||||
`Math.max(a, b, c...)` / `Math.min(a, b, c...)`
|
||||
: Returns the greatest/smallest from the arbitrary number of arguments.
|
||||
|
||||
```js run
|
||||
alert( Math.max(3, 5, -10, 0, 1) ); // 5
|
||||
|
@ -461,13 +429,6 @@ For different numeral systems:
|
|||
- `parseInt(str, base)` parses the string `str` into an integer in numeral system with given `base`, `2 ≤ base ≤ 36`.
|
||||
- `num.toString(base)` converts a number to a string in the numeral system with the given `base`.
|
||||
|
||||
For regular number tests:
|
||||
|
||||
- `isNaN(value)` converts its argument to a number and then tests it for being `NaN`
|
||||
- `Number.isNaN(value)` checks whether its argument belongs to the `number` type, and if so, tests it for being `NaN`
|
||||
- `isFinite(value)` converts its argument to a number and then tests it for not being `NaN/Infinity/-Infinity`
|
||||
- `Number.isFinite(value)` checks whether its argument belongs to the `number` type, and if so, tests it for not being `NaN/Infinity/-Infinity`
|
||||
|
||||
For converting values like `12pt` and `100px` to a number:
|
||||
|
||||
- Use `parseInt/parseFloat` for the "soft" conversion, which reads a number from a string and then returns the value they could read before the error.
|
||||
|
|
|
@ -8,7 +8,12 @@ let newStr = str[0].toUpperCase() + str.slice(1);
|
|||
|
||||
There's a small problem though. If `str` is empty, then `str[0]` is `undefined`, and as `undefined` doesn't have the `toUpperCase()` method, we'll get an error.
|
||||
|
||||
The easiest way out is to add a test for an empty string, like this:
|
||||
There are two variants here:
|
||||
|
||||
1. Use `str.charAt(0)`, as it always returns a string (maybe empty).
|
||||
2. Add a test for an empty string.
|
||||
|
||||
Here's the 2nd variant:
|
||||
|
||||
```js run demo
|
||||
function ucFirst(str) {
|
||||
|
@ -19,3 +24,4 @@ function ucFirst(str) {
|
|||
|
||||
alert( ucFirst("john") ); // John
|
||||
```
|
||||
|
||||
|
|
|
@ -48,9 +48,9 @@ let guestList = "Guests: // Error: Unexpected token ILLEGAL
|
|||
* John";
|
||||
```
|
||||
|
||||
Single and double quotes come from ancient times of language creation, when the need for multiline strings was not taken into account. Backticks appeared much later and thus are more versatile.
|
||||
Single and double quotes come from ancient times of language creation when the need for multiline strings was not taken into account. Backticks appeared much later and thus are more versatile.
|
||||
|
||||
Backticks also allow us to specify a "template function" before the first backtick. The syntax is: <code>func`string`</code>. The function `func` is called automatically, receives the string and embedded expressions and can process them. This feature is called "tagged templates", it's rarely seen, but you can read about it in the MDN: [Template literals](mdn:/JavaScript/Reference/Template_literals#Tagged_templates).
|
||||
Backticks also allow us to specify a "template function" before the first backtick. The syntax is: <code>func`string`</code>. The function `func` is called automatically, receives the string and embedded expressions and can process them. This is called "tagged templates". This feature makes it easier to implement custom templating, but is rarely used in practice. You can read more about it in the [manual](mdn:/JavaScript/Reference/Template_literals#Tagged_templates).
|
||||
|
||||
## Special characters
|
||||
|
||||
|
@ -59,10 +59,10 @@ It is still possible to create multiline strings with single and double quotes b
|
|||
```js run
|
||||
let guestList = "Guests:\n * John\n * Pete\n * Mary";
|
||||
|
||||
alert(guestList); // a multiline list of guests, same as above
|
||||
alert(guestList); // a multiline list of guests
|
||||
```
|
||||
|
||||
As a simpler example, these two lines are equal, just written differently:
|
||||
For example, these two lines are equal, just written differently:
|
||||
|
||||
```js run
|
||||
let str1 = "Hello\nWorld"; // two lines using a "newline symbol"
|
||||
|
@ -74,26 +74,33 @@ World`;
|
|||
alert(str1 == str2); // true
|
||||
```
|
||||
|
||||
There are other, less common special characters:
|
||||
There are other, less common "special" characters.
|
||||
|
||||
Here's the full list:
|
||||
|
||||
| Character | Description |
|
||||
|-----------|-------------|
|
||||
|`\n`|New line|
|
||||
|`\r`|In Windows text files a combination of two characters `\r\n` represents a new break, while on non-Windows OS it's just `\n`. That's for historical reasons, most Windows software also understands `\n`. |
|
||||
|`\'`, `\"`, <code>\\`</code>|Quotes|
|
||||
|`\r`|Carriage return: not used alone. Windows text files use a combination of two characters `\r\n` to represent a line break. |
|
||||
|`\'`, `\"`|Quotes|
|
||||
|`\\`|Backslash|
|
||||
|`\t`|Tab|
|
||||
|`\b`, `\f`, `\v`| Backspace, Form Feed, Vertical Tab -- mentioned for completeness, coming from old times, not used nowadays (you can forget them right now). |
|
||||
|`\b`, `\f`, `\v`| Backspace, Form Feed, Vertical Tab -- kept for compatibility, not used nowadays. |
|
||||
|`\xXX`|Unicode character with the given hexadecimal Unicode `XX`, e.g. `'\x7A'` is the same as `'z'`.|
|
||||
|`\uXXXX`|A Unicode symbol with the hex code `XXXX` in UTF-16 encoding, for instance `\u00A9` -- is a Unicode for the copyright symbol `©`. It must be exactly 4 hex digits. |
|
||||
|`\u{X…XXXXXX}` (1 to 6 hex characters)|A Unicode symbol with the given UTF-32 encoding. Some rare characters are encoded with two Unicode symbols, taking 4 bytes. This way we can insert long codes. |
|
||||
|
||||
As you can see, all special characters start with a backslash character `\`. It is also called an "escape character".
|
||||
|
||||
Because it's so special, if we need to show an actual backslash `\` within the string, we need to double it:
|
||||
Examples with Unicode:
|
||||
|
||||
```js run
|
||||
alert( `The backslash: \\` ); // The backslash: \
|
||||
alert( "\u00A9" ); // ©
|
||||
alert( "\u{20331}" ); // 佫, a rare Chinese hieroglyph (long Unicode)
|
||||
alert( "\u{1F60D}" ); // 😍, a smiling face symbol (another long Unicode)
|
||||
```
|
||||
|
||||
So-called "escaped" quotes `\'`, `\"`, <code>\\`</code> are used to insert a quote into the same-quoted string.
|
||||
All special characters start with a backslash character `\`. It is also called an "escape character".
|
||||
|
||||
We might also use it if we wanted to insert a quote into the string.
|
||||
|
||||
For instance:
|
||||
|
||||
|
@ -106,10 +113,18 @@ As you can see, we have to prepend the inner quote by the backslash `\'`, becaus
|
|||
Of course, only the quotes that are the same as the enclosing ones need to be escaped. So, as a more elegant solution, we could switch to double quotes or backticks instead:
|
||||
|
||||
```js run
|
||||
alert( "I'm the Walrus!" ); // I'm the Walrus!
|
||||
alert( `I'm the Walrus!` ); // I'm the Walrus!
|
||||
```
|
||||
|
||||
Besides these special characters, there's also a special notation for Unicode codes `\u…`, it's rarely used and is covered in the optional chapter about [Unicode](info:unicode).
|
||||
Note that the backslash `\` serves for the correct reading of the string by JavaScript, then disappears. The in-memory string has no `\`. You can clearly see that in `alert` from the examples above.
|
||||
|
||||
But what if we need to show an actual backslash `\` within the string?
|
||||
|
||||
That's possible, but we need to double it like `\\`:
|
||||
|
||||
```js run
|
||||
alert( `The backslash: \\` ); // The backslash: \
|
||||
```
|
||||
|
||||
## String length
|
||||
|
||||
|
@ -124,36 +139,33 @@ Note that `\n` is a single "special" character, so the length is indeed `3`.
|
|||
```warn header="`length` is a property"
|
||||
People with a background in some other languages sometimes mistype by calling `str.length()` instead of just `str.length`. That doesn't work.
|
||||
|
||||
Please note that `str.length` is a numeric property, not a function. There is no need to add parenthesis after it. Not `.length()`, but `.length`.
|
||||
Please note that `str.length` is a numeric property, not a function. There is no need to add parenthesis after it.
|
||||
```
|
||||
|
||||
## Accessing characters
|
||||
|
||||
To get a character at position `pos`, use square brackets `[pos]` or call the method [str.at(pos)](mdn:js/String/at). The first character starts from the zero position:
|
||||
To get a character at position `pos`, use square brackets `[pos]` or call the method [str.charAt(pos)](mdn:js/String/charAt). The first character starts from the zero position:
|
||||
|
||||
```js run
|
||||
let str = `Hello`;
|
||||
|
||||
// the first character
|
||||
alert( str[0] ); // H
|
||||
alert( str.at(0) ); // H
|
||||
alert( str.charAt(0) ); // H
|
||||
|
||||
// the last character
|
||||
alert( str[str.length - 1] ); // o
|
||||
alert( str.at(-1) );
|
||||
```
|
||||
|
||||
As you can see, the `.at(pos)` method has a benefit of allowing negative position. If `pos` is negative, then it's counted from the end of the string.
|
||||
The square brackets are a modern way of getting a character, while `charAt` exists mostly for historical reasons.
|
||||
|
||||
So `.at(-1)` means the last character, and `.at(-2)` is the one before it, etc.
|
||||
|
||||
The square brackets always return `undefined` for negative indexes, for instance:
|
||||
The only difference between them is that if no character is found, `[]` returns `undefined`, and `charAt` returns an empty string:
|
||||
|
||||
```js run
|
||||
let str = `Hello`;
|
||||
|
||||
alert( str[-2] ); // undefined
|
||||
alert( str.at(-2) ); // l
|
||||
alert( str[1000] ); // undefined
|
||||
alert( str.charAt(1000) ); // '' (an empty string)
|
||||
```
|
||||
|
||||
We can also iterate over characters using `for..of`:
|
||||
|
@ -202,7 +214,7 @@ alert( 'Interface'.toLowerCase() ); // interface
|
|||
|
||||
Or, if we want a single character lowercased:
|
||||
|
||||
```js run
|
||||
```js
|
||||
alert( 'Interface'[0].toLowerCase() ); // 'i'
|
||||
```
|
||||
|
||||
|
@ -298,6 +310,45 @@ if (str.indexOf("Widget") != -1) {
|
|||
}
|
||||
```
|
||||
|
||||
#### The bitwise NOT trick
|
||||
|
||||
One of the old tricks used here is the [bitwise NOT](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_NOT) `~` operator. It converts the number to a 32-bit integer (removes the decimal part if exists) and then reverses all bits in its binary representation.
|
||||
|
||||
In practice, that means a simple thing: for 32-bit integers `~n` equals `-(n+1)`.
|
||||
|
||||
For instance:
|
||||
|
||||
```js run
|
||||
alert( ~2 ); // -3, the same as -(2+1)
|
||||
alert( ~1 ); // -2, the same as -(1+1)
|
||||
alert( ~0 ); // -1, the same as -(0+1)
|
||||
*!*
|
||||
alert( ~-1 ); // 0, the same as -(-1+1)
|
||||
*/!*
|
||||
```
|
||||
|
||||
As we can see, `~n` is zero only if `n == -1` (that's for any 32-bit signed integer `n`).
|
||||
|
||||
So, the test `if ( ~str.indexOf("...") )` is truthy only if the result of `indexOf` is not `-1`. In other words, when there is a match.
|
||||
|
||||
People use it to shorten `indexOf` checks:
|
||||
|
||||
```js run
|
||||
let str = "Widget";
|
||||
|
||||
if (~str.indexOf("Widget")) {
|
||||
alert( 'Found it!' ); // works
|
||||
}
|
||||
```
|
||||
|
||||
It is usually not recommended to use language features in a non-obvious way, but this particular trick is widely used in old code, so we should understand it.
|
||||
|
||||
Just remember: `if (~str.indexOf(...))` reads as "if found".
|
||||
|
||||
To be precise though, as big numbers are truncated to 32 bits by `~` operator, there exist other numbers that give `0`, the smallest is `~4294967295=0`. That makes such check correct only if a string is not that long.
|
||||
|
||||
Right now we can see this trick only in the old code, as modern JavaScript provides `.includes` method (see below).
|
||||
|
||||
### includes, startsWith, endsWith
|
||||
|
||||
The more modern method [str.includes(substr, pos)](mdn:js/String/includes) returns `true/false` depending on whether `str` contains `substr` within.
|
||||
|
@ -320,8 +371,8 @@ alert( "Widget".includes("id", 3) ); // false, from position 3 there is no "id"
|
|||
The methods [str.startsWith](mdn:js/String/startsWith) and [str.endsWith](mdn:js/String/endsWith) do exactly what they say:
|
||||
|
||||
```js run
|
||||
alert( "*!*Wid*/!*get".startsWith("Wid") ); // true, "Widget" starts with "Wid"
|
||||
alert( "Wid*!*get*/!*".endsWith("get") ); // true, "Widget" ends with "get"
|
||||
alert( "Widget".startsWith("Wid") ); // true, "Widget" starts with "Wid"
|
||||
alert( "Widget".endsWith("get") ); // true, "Widget" ends with "get"
|
||||
```
|
||||
|
||||
## Getting a substring
|
||||
|
@ -356,9 +407,9 @@ There are 3 methods in JavaScript to get a substring: `substring`, `substr` and
|
|||
```
|
||||
|
||||
`str.substring(start [, end])`
|
||||
: Returns the part of the string *between* `start` and `end` (not including `end`).
|
||||
: Returns the part of the string *between* `start` and `end`.
|
||||
|
||||
This is almost the same as `slice`, but it allows `start` to be greater than `end` (in this case it simply swaps `start` and `end` values).
|
||||
This is almost the same as `slice`, but it allows `start` to be greater than `end`.
|
||||
|
||||
For instance:
|
||||
|
||||
|
@ -394,22 +445,18 @@ There are 3 methods in JavaScript to get a substring: `substring`, `substr` and
|
|||
alert( str.substr(-4, 2) ); // 'gi', from the 4th position get 2 characters
|
||||
```
|
||||
|
||||
This method resides in the [Annex B](https://tc39.es/ecma262/#sec-string.prototype.substr) of the language specification. It means that only browser-hosted Javascript engines should support it, and it's not recommended to use it. In practice, it's supported everywhere.
|
||||
|
||||
Let's recap these methods to avoid any confusion:
|
||||
|
||||
| method | selects... | negatives |
|
||||
|--------|-----------|-----------|
|
||||
| `slice(start, end)` | from `start` to `end` (not including `end`) | allows negatives |
|
||||
| `substring(start, end)` | between `start` and `end` (not including `end`)| negative values mean `0` |
|
||||
| `substring(start, end)` | between `start` and `end` | negative values mean `0` |
|
||||
| `substr(start, length)` | from `start` get `length` characters | allows negative `start` |
|
||||
|
||||
```smart header="Which one to choose?"
|
||||
All of them can do the job. Formally, `substr` has a minor drawback: it is described not in the core JavaScript specification, but in Annex B, which covers browser-only features that exist mainly for historical reasons. So, non-browser environments may fail to support it. But in practice it works everywhere.
|
||||
|
||||
Of the other two variants, `slice` is a little bit more flexible, it allows negative arguments and shorter to write.
|
||||
|
||||
So, for practical use it's enough to remember only `slice`.
|
||||
Of the other two variants, `slice` is a little bit more flexible, it allows negative arguments and shorter to write. So, it's enough to remember solely `slice` of these three methods.
|
||||
```
|
||||
|
||||
## Comparing strings
|
||||
|
@ -432,18 +479,17 @@ Although, there are some oddities.
|
|||
|
||||
This may lead to strange results if we sort these country names. Usually people would expect `Zealand` to come after `Österreich` in the list.
|
||||
|
||||
To understand what happens, we should be aware that strings in Javascript are encoded using [UTF-16](https://en.wikipedia.org/wiki/UTF-16). That is: each character has a corresponding numeric code.
|
||||
To understand what happens, let's review the internal representation of strings in JavaScript.
|
||||
|
||||
There are special methods that allow to get the character for the code and back:
|
||||
All strings are encoded using [UTF-16](https://en.wikipedia.org/wiki/UTF-16). That is: each character has a corresponding numeric code. There are special methods that allow to get the character for the code and back.
|
||||
|
||||
`str.codePointAt(pos)`
|
||||
: Returns a decimal number representing the code for the character at position `pos`:
|
||||
: Returns the code for the character at position `pos`:
|
||||
|
||||
```js run
|
||||
// different case letters have different codes
|
||||
alert( "Z".codePointAt(0) ); // 90
|
||||
alert( "z".codePointAt(0) ); // 122
|
||||
alert( "z".codePointAt(0).toString(16) ); // 7a (if we need a hexadecimal value)
|
||||
alert( "Z".codePointAt(0) ); // 90
|
||||
```
|
||||
|
||||
`String.fromCodePoint(code)`
|
||||
|
@ -451,7 +497,13 @@ There are special methods that allow to get the character for the code and back:
|
|||
|
||||
```js run
|
||||
alert( String.fromCodePoint(90) ); // Z
|
||||
alert( String.fromCodePoint(0x5a) ); // Z (we can also use a hex value as an argument)
|
||||
```
|
||||
|
||||
We can also add Unicode characters by their codes using `\u` followed by the hex code:
|
||||
|
||||
```js run
|
||||
// 90 is 5a in hexadecimal system
|
||||
alert( '\u005a' ); // Z
|
||||
```
|
||||
|
||||
Now let's see the characters with codes `65..220` (the latin alphabet and a little bit extra) by making a string of them:
|
||||
|
@ -463,7 +515,6 @@ for (let i = 65; i <= 220; i++) {
|
|||
str += String.fromCodePoint(i);
|
||||
}
|
||||
alert( str );
|
||||
// Output:
|
||||
// ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
|
||||
// ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜ
|
||||
```
|
||||
|
@ -475,7 +526,7 @@ Now it becomes obvious why `a > Z`.
|
|||
The characters are compared by their numeric code. The greater code means that the character is greater. The code for `a` (97) is greater than the code for `Z` (90).
|
||||
|
||||
- All lowercase letters go after uppercase letters because their codes are greater.
|
||||
- Some letters like `Ö` stand apart from the main alphabet. Here, its code is greater than anything from `a` to `z`.
|
||||
- Some letters like `Ö` stand apart from the main alphabet. Here, it's code is greater than anything from `a` to `z`.
|
||||
|
||||
### Correct comparisons [#correct-comparisons]
|
||||
|
||||
|
@ -483,7 +534,7 @@ The "right" algorithm to do string comparisons is more complex than it may seem,
|
|||
|
||||
So, the browser needs to know the language to compare.
|
||||
|
||||
Luckily, modern browsers support the internationalization standard [ECMA-402](https://www.ecma-international.org/publications-and-standards/standards/ecma-402/).
|
||||
Luckily, all modern browsers (IE10- requires the additional library [Intl.js](https://github.com/andyearnshaw/Intl.js/)) support the internationalization standard [ECMA-402](http://www.ecma-international.org/ecma-402/1.0/ECMA-402.pdf).
|
||||
|
||||
It provides a special method to compare strings in different languages, following their rules.
|
||||
|
||||
|
@ -501,10 +552,118 @@ alert( 'Österreich'.localeCompare('Zealand') ); // -1
|
|||
|
||||
This method actually has two additional arguments specified in [the documentation](mdn:js/String/localeCompare), which allows it to specify the language (by default taken from the environment, letter order depends on the language) and setup additional rules like case sensitivity or should `"a"` and `"á"` be treated as the same etc.
|
||||
|
||||
## Internals, Unicode
|
||||
|
||||
```warn header="Advanced knowledge"
|
||||
The section goes deeper into string internals. This knowledge will be useful for you if you plan to deal with emoji, rare mathematical or hieroglyphic characters or other rare symbols.
|
||||
|
||||
You can skip the section if you don't plan to support them.
|
||||
```
|
||||
|
||||
### Surrogate pairs
|
||||
|
||||
All frequently used characters have 2-byte codes. Letters in most european languages, numbers, and even most hieroglyphs, have a 2-byte representation.
|
||||
|
||||
But 2 bytes only allow 65536 combinations and that's not enough for every possible symbol. So rare symbols are encoded with a pair of 2-byte characters called "a surrogate pair".
|
||||
|
||||
The length of such symbols is `2`:
|
||||
|
||||
```js run
|
||||
alert( '𝒳'.length ); // 2, MATHEMATICAL SCRIPT CAPITAL X
|
||||
alert( '😂'.length ); // 2, FACE WITH TEARS OF JOY
|
||||
alert( '𩷶'.length ); // 2, a rare Chinese hieroglyph
|
||||
```
|
||||
|
||||
Note that surrogate pairs did not exist at the time when JavaScript was created, and thus are not correctly processed by the language!
|
||||
|
||||
We actually have a single symbol in each of the strings above, but the `length` shows a length of `2`.
|
||||
|
||||
`String.fromCodePoint` and `str.codePointAt` are few rare methods that deal with surrogate pairs right. They recently appeared in the language. Before them, there were only [String.fromCharCode](mdn:js/String/fromCharCode) and [str.charCodeAt](mdn:js/String/charCodeAt). These methods are actually the same as `fromCodePoint/codePointAt`, but don't work with surrogate pairs.
|
||||
|
||||
Getting a symbol can be tricky, because surrogate pairs are treated as two characters:
|
||||
|
||||
```js run
|
||||
alert( '𝒳'[0] ); // strange symbols...
|
||||
alert( '𝒳'[1] ); // ...pieces of the surrogate pair
|
||||
```
|
||||
|
||||
Note that pieces of the surrogate pair have no meaning without each other. So the alerts in the example above actually display garbage.
|
||||
|
||||
Technically, surrogate pairs are also detectable by their codes: if a character has the code in the interval of `0xd800..0xdbff`, then it is the first part of the surrogate pair. The next character (second part) must have the code in interval `0xdc00..0xdfff`. These intervals are reserved exclusively for surrogate pairs by the standard.
|
||||
|
||||
In the case above:
|
||||
|
||||
```js run
|
||||
// charCodeAt is not surrogate-pair aware, so it gives codes for parts
|
||||
|
||||
alert( '𝒳'.charCodeAt(0).toString(16) ); // d835, between 0xd800 and 0xdbff
|
||||
alert( '𝒳'.charCodeAt(1).toString(16) ); // dcb3, between 0xdc00 and 0xdfff
|
||||
```
|
||||
|
||||
You will find more ways to deal with surrogate pairs later in the chapter <info:iterable>. There are probably special libraries for that too, but nothing famous enough to suggest here.
|
||||
|
||||
### Diacritical marks and normalization
|
||||
|
||||
In many languages there are symbols that are composed of the base character with a mark above/under it.
|
||||
|
||||
For instance, the letter `a` can be the base character for: `àáâäãåā`. Most common "composite" character have their own code in the UTF-16 table. But not all of them, because there are too many possible combinations.
|
||||
|
||||
To support arbitrary compositions, UTF-16 allows us to use several Unicode characters: the base character followed by one or many "mark" characters that "decorate" it.
|
||||
|
||||
For instance, if we have `S` followed by the special "dot above" character (code `\u0307`), it is shown as Ṡ.
|
||||
|
||||
```js run
|
||||
alert( 'S\u0307' ); // Ṡ
|
||||
```
|
||||
|
||||
If we need an additional mark above the letter (or below it) -- no problem, just add the necessary mark character.
|
||||
|
||||
For instance, if we append a character "dot below" (code `\u0323`), then we'll have "S with dots above and below": `Ṩ`.
|
||||
|
||||
For example:
|
||||
|
||||
```js run
|
||||
alert( 'S\u0307\u0323' ); // Ṩ
|
||||
```
|
||||
|
||||
This provides great flexibility, but also an interesting problem: two characters may visually look the same, but be represented with different Unicode compositions.
|
||||
|
||||
For instance:
|
||||
|
||||
```js run
|
||||
let s1 = 'S\u0307\u0323'; // Ṩ, S + dot above + dot below
|
||||
let s2 = 'S\u0323\u0307'; // Ṩ, S + dot below + dot above
|
||||
|
||||
alert( `s1: ${s1}, s2: ${s2}` );
|
||||
|
||||
alert( s1 == s2 ); // false though the characters look identical (?!)
|
||||
```
|
||||
|
||||
To solve this, there exists a "Unicode normalization" algorithm that brings each string to the single "normal" form.
|
||||
|
||||
It is implemented by [str.normalize()](mdn:js/String/normalize).
|
||||
|
||||
```js run
|
||||
alert( "S\u0307\u0323".normalize() == "S\u0323\u0307".normalize() ); // true
|
||||
```
|
||||
|
||||
It's funny that in our situation `normalize()` actually brings together a sequence of 3 characters to one: `\u1e68` (S with two dots).
|
||||
|
||||
```js run
|
||||
alert( "S\u0307\u0323".normalize().length ); // 1
|
||||
|
||||
alert( "S\u0307\u0323".normalize() == "\u1e68" ); // true
|
||||
```
|
||||
|
||||
In reality, this is not always the case. The reason being that the symbol `Ṩ` is "common enough", so UTF-16 creators included it in the main table and gave it the code.
|
||||
|
||||
If you want to learn more about normalization rules and variants -- they are described in the appendix of the Unicode standard: [Unicode Normalization Forms](http://www.unicode.org/reports/tr15/), but for most practical purposes the information from this section is enough.
|
||||
|
||||
## Summary
|
||||
|
||||
- There are 3 types of quotes. Backticks allow a string to span multiple lines and embed expressions `${…}`.
|
||||
- We can use special characters, such as a line break `\n`.
|
||||
- Strings in JavaScript are encoded using UTF-16.
|
||||
- We can use special characters like `\n` and insert letters by their Unicode using `\u...`.
|
||||
- To get a character, use: `[]`.
|
||||
- To get a substring, use: `slice` or `substring`.
|
||||
- To lowercase/uppercase a string, use: `toLowerCase/toUpperCase`.
|
||||
|
@ -518,5 +677,3 @@ There are several other helpful methods in strings:
|
|||
- ...and more to be found in the [manual](mdn:js/String).
|
||||
|
||||
Strings also have methods for doing search/replace with regular expressions. But that's big topic, so it's explained in a separate tutorial section <info:regular-expressions>.
|
||||
|
||||
Also, as of now it's important to know that strings are based on Unicode encoding, and hence there're issues with comparisons. There's more about Unicode in the chapter <info:unicode>.
|
|
@ -59,7 +59,7 @@ alert( getMaxSubSum([100, -9, 2, -3, 5]) ); // 100
|
|||
|
||||
The solution has a time complexity of [O(n<sup>2</sup>)](https://en.wikipedia.org/wiki/Big_O_notation). In other words, if we increase the array size 2 times, the algorithm will work 4 times longer.
|
||||
|
||||
For big arrays (1000, 10000 or more items) such algorithms can lead to serious sluggishness.
|
||||
For big arrays (1000, 10000 or more items) such algorithms can lead to a serious sluggishness.
|
||||
|
||||
# Fast solution
|
||||
|
||||
|
@ -91,4 +91,4 @@ alert( getMaxSubSum([-1, -2, -3]) ); // 0
|
|||
|
||||
The algorithm requires exactly 1 array pass, so the time complexity is O(n).
|
||||
|
||||
You can find more detailed information about the algorithm here: [Maximum subarray problem](http://en.wikipedia.org/wiki/Maximum_subarray_problem). If it's still not obvious why that works, then please trace the algorithm on the examples above, see how it works, that's better than any words.
|
||||
You can find more detail information about the algorithm here: [Maximum subarray problem](http://en.wikipedia.org/wiki/Maximum_subarray_problem). If it's still not obvious why that works, then please trace the algorithm on the examples above, see how it works, that's better than any words.
|
||||
|
|
|
@ -8,7 +8,7 @@ Let's try 5 array operations.
|
|||
|
||||
1. Create an array `styles` with items "Jazz" and "Blues".
|
||||
2. Append "Rock-n-Roll" to the end.
|
||||
3. Replace the value in the middle with "Classics". Your code for finding the middle value should work for any arrays with odd length.
|
||||
3. Replace the value in the middle by "Classics". Your code for finding the middle value should work for any arrays with odd length.
|
||||
4. Strip off the first value of the array and show it.
|
||||
5. Prepend `Rap` and `Reggae` to the array.
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ let arr = ["a", "b"];
|
|||
|
||||
arr.push(function() {
|
||||
alert( this );
|
||||
});
|
||||
})
|
||||
|
||||
arr[2](); // ?
|
||||
```
|
||||
|
|
|
@ -1 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="479" height="148" viewBox="0 0 479 148"><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="array" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="array-pop.svg"><path id="Rectangle-1" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M103 82h35v52h-35z"/><path id="Rectangle-2" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M139 82h35v52h-35z"/><text id="0" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="117" y="112">0</tspan></text><text id="1" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="154" y="112">1</tspan></text><path id="Rectangle-3" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M175 82h35v52h-35z"/><text id="2" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="190" y="112">2</tspan></text><path id="Rectangle-4" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M211 82h35v52h-35z"/><text id="3" fill="#C06334" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="226" y="112">3</tspan></text><text id=""Apple"" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal" transform="rotate(-89 114.044 48.948)"><tspan x="84.544" y="53.448">"Apple"</tspan></text><text id=""Orange"" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal" transform="rotate(-89 151.053 44.448)"><tspan x="117.053" y="48.948">"Orange"</tspan></text><text id=""Pear"" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal" transform="rotate(-89 187.535 52.448)"><tspan x="162.035" y="56.948">"Pear"</tspan></text><text id=""Lemon"" fill="#C06334" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal" transform="rotate(-89 229.044 48.948)"><tspan x="199.544" y="53.448">"Lemon"</tspan></text><text id="length-=-4" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="8" y="59">length = 4</tspan></text><path id="Shape" fill="#C06334" d="M306 137l9-16.606h-6V78h-6v42.394h-6z" transform="rotate(-90 306 107.5)"/><text id="clear" fill="#C06334" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="287" y="96">clear</tspan></text><path id="Rectangle-7" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M363 82h35v52h-35z"/><path id="Rectangle-5" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M399 82h35v52h-35z"/><text id="-1" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="377" y="112">0</tspan></text><text id="-2" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="414" y="112">1</tspan></text><path id="Rectangle-6" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M435 82h35v52h-35z"/><text id="-3" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="450" y="112">2</tspan></text><text id=""Apple"" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal" transform="rotate(-89 374.044 48.948)"><tspan x="344.544" y="53.448">"Apple"</tspan></text><text id=""Orange"" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal" transform="rotate(-89 411.053 44.448)"><tspan x="377.053" y="48.948">"Orange"</tspan></text><text id=""Pear"" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal" transform="rotate(-89 447.535 52.448)"><tspan x="422.035" y="56.948">"Pear"</tspan></text><text id="length-=-3" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="263" y="59">length = 3</tspan></text></g></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="479" height="148" viewBox="0 0 479 148"><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="array" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="array-pop.svg"><path id="Rectangle-1" fill="#FFF9EB" stroke="#E8C48E" stroke-width="2" d="M103 82h35v52h-35z"/><path id="Rectangle-2" fill="#FFF9EB" stroke="#E8C48E" stroke-width="2" d="M139 82h35v52h-35z"/><text id="0" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="117" y="112">0</tspan></text><text id="1" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="154" y="112">1</tspan></text><path id="Rectangle-3" fill="#FFF9EB" stroke="#E8C48E" stroke-width="2" d="M175 82h35v52h-35z"/><text id="2" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="190" y="112">2</tspan></text><path id="Rectangle-4" fill="#FFF9EB" stroke="#E8C48E" stroke-width="2" d="M211 82h35v52h-35z"/><text id="3" fill="#EE6B47" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="226" y="112">3</tspan></text><text id=""Apple"" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal" transform="rotate(-89 114.044 48.948)"><tspan x="84.544" y="53.448">"Apple"</tspan></text><text id=""Orange"" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal" transform="rotate(-89 151.053 44.448)"><tspan x="117.053" y="48.948">"Orange"</tspan></text><text id=""Pear"" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal" transform="rotate(-89 187.535 52.448)"><tspan x="162.035" y="56.948">"Pear"</tspan></text><text id=""Lemon"" fill="#EE6B47" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal" transform="rotate(-89 229.044 48.948)"><tspan x="199.544" y="53.448">"Lemon"</tspan></text><text id="length-=-4" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="8" y="59">length = 4</tspan></text><path id="Shape" fill="#EE6B47" d="M306 137l9-16.606h-6V78h-6v42.394h-6z" transform="rotate(-90 306 107.5)"/><text id="clear" fill="#EE6B47" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="287" y="96">clear</tspan></text><path id="Rectangle-7" fill="#FFF9EB" stroke="#E8C48E" stroke-width="2" d="M363 82h35v52h-35z"/><path id="Rectangle-5" fill="#FFF9EB" stroke="#E8C48E" stroke-width="2" d="M399 82h35v52h-35z"/><text id="-1" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="377" y="112">0</tspan></text><text id="-2" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="414" y="112">1</tspan></text><path id="Rectangle-6" fill="#FFF9EB" stroke="#E8C48E" stroke-width="2" d="M435 82h35v52h-35z"/><text id="-3" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="450" y="112">2</tspan></text><text id=""Apple"" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal" transform="rotate(-89 374.044 48.948)"><tspan x="344.544" y="53.448">"Apple"</tspan></text><text id=""Orange"" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal" transform="rotate(-89 411.053 44.448)"><tspan x="377.053" y="48.948">"Orange"</tspan></text><text id=""Pear"" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal" transform="rotate(-89 447.535 52.448)"><tspan x="422.035" y="56.948">"Pear"</tspan></text><text id="length-=-3" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="263" y="59">length = 3</tspan></text></g></g></svg>
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 6.3 KiB |
|
@ -1 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="354" height="173" viewBox="0 0 354 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="array" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="array-speed.svg"><path id="Rectangle-1" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M109 93h35v62h-35z"/><path id="Rectangle-2" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M145 93h35v62h-35z"/><text id="0" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="122.5" y="128.5">0</tspan></text><text id="1" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="159.5" y="128.5">1</tspan></text><path id="Rectangle-3" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M181 93h35v62h-35z"/><text id="2" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="195.5" y="128.5">2</tspan></text><path id="Rectangle-4" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M217 93h35v62h-35z"/><text id="3" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="231.5" y="128.5">3</tspan></text><path id="Shape-5" fill="#C06334" d="M326.5 117.5l4.5-6.192h-3V95.5h-3v15.808h-3z" transform="rotate(-90 326.5 106.5)"/><path id="Rectangle-5" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M264 95h46v22h-46z"/><text id="pop" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="273.807" y="110">pop</tspan></text><path id="Shape-2" fill="#C06334" d="M94.5 117.5l4.5-6.192h-3V95.5h-3v15.808h-3z" transform="rotate(-90 94.5 106.5)"/><path id="Rectangle-6" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M19 96h61v22H19z"/><text id="unshift" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="23.833" y="112">unshift</tspan></text><path id="Shape-3" fill="#C06334" d="M326.5 152.5l4.5-6.192h-3V130.5h-3v15.808h-3z" transform="matrix(0 -1 -1 0 468 468)"/><path id="Rectangle-7" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M310 130h-46v22h46z"/><text id="push" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="269.89" y="146">push</tspan></text><path id="Shape-4" fill="#C06334" d="M93.5 152.5l4.5-6.192h-3V130.5h-3v15.808h-3z" transform="matrix(0 -1 -1 0 235 235)"/><path id="Rectangle-8" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M79 131H18v22h61z"/><text id="shift" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="32.535" y="147">shift</tspan></text><path id="Shape" fill="#DBAF88" d="M329.693 35.92c-.952-2.478-7.313-10.643-8.63-11.08-1.315-.437-4.095-.292-4.095-.292-1.243-13.925-4.314-15.456-6.434-16.987-2.12-1.53-1.902 4.521-1.902 4.521-.731-2.551-4.461-6.707-7.02-6.051-2.56.656-1.17 9.04-.804 11.372.367 2.334 3.656 5.686 4.898 7.51 1.243 1.821.586 3.425.586 3.425-1.024 1.46-.878 5.128-.878 5.128-2.558-.414-5.044 2.528-5.044 2.528-5.778-2.478-9.085-2.528-9.085-2.528-17.535.175-19.767 16.815-20.025 22.334-.626-.271-1.496-.264-2.619.618-2.581 2.027-.74 4.511-.74 4.511-.727-.2-1.447-3.54-1.447-3.54-1.65 3.517 1.35 5.92 1.35 5.92-1.118.239-2.968-1.01-2.968-1.01.175.235 1.333 2.033 3.581 4.38 2.838 2.96 7.023 2.322 9.087 2.243a.4.4 0 00.196-.009l.06-.001.01-.026c.324-.144.581-.602 1.929-.152l1.731 1.086s15.302.142 17.697 0c1.3-.077-1.316-3.79-1.316-3.79 4.753-.293 7.312-2.043 7.312-2.043l7.343 5.975h7.703c1.514 0 .586-1.618.586-1.618l-7.223-6.544.803-1.385c2.78-.583 4.754-3.28 5.045-3.79.294-.51.074-4.301.074-4.301s.294.365 1.535-1.167c1.244-1.532-.145-3.937-.51-4.448-.365-.509-1.097-3.72-1.097-3.72s4.898-.29 6.07-.874c1.167-.58 5.19-3.716 4.24-6.194zM85.866 34.289c-4.561 2.635-4.417 9.072-15.582 17.687-2.718 2.097 2.877 13.103.454 15.556-2.468 2.499-12.35 2.623-14.739 1.26-3.517-2.007-.76-7.243.027-14.886-7.206.735-9.541.615-16.783.313 1.104 5.799 2.719 12.246.377 14.37-1.909 1.731-11.198 1.919-14.103.022-3.635-2.374 2.634-15.875.509-16.093-2.445-.25-17.353.334-14.716-15.842 2.789 4.076 7.56 7.912 13.032 6.188.746-13.723 9.071-26.068 22.358-26.068s22.684 10.307 22.754 23.079c7.867-5.908 2.084-20.426 11.514-25.857 6.712-3.866 14.563-1.869 18.307 4.257 1.955 3.199 2.565 8.84.141 11.938-3.607 4.61-10.421 2.268-13.55 4.076z"/></g></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="354" height="173" viewBox="0 0 354 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="array" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="array-speed.svg"><path id="Rectangle-1" fill="#FFF9EB" stroke="#E8C48E" stroke-width="2" d="M109 93h35v62h-35z"/><path id="Rectangle-2" fill="#FFF9EB" stroke="#E8C48E" stroke-width="2" d="M145 93h35v62h-35z"/><text id="0" fill="#8A704D" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="122.5" y="128.5">0</tspan></text><text id="1" fill="#8A704D" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="159.5" y="128.5">1</tspan></text><path id="Rectangle-3" fill="#FFF9EB" stroke="#E8C48E" stroke-width="2" d="M181 93h35v62h-35z"/><text id="2" fill="#8A704D" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="195.5" y="128.5">2</tspan></text><path id="Rectangle-4" fill="#FFF9EB" stroke="#E8C48E" stroke-width="2" d="M217 93h35v62h-35z"/><text id="3" fill="#8A704D" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="231.5" y="128.5">3</tspan></text><path id="Shape-5" fill="#EE6B47" d="M326.5 117.5l4.5-6.192h-3V95.5h-3v15.808h-3z" transform="rotate(-90 326.5 106.5)"/><path id="Rectangle-5" fill="#FFF9EB" stroke="#E8C48E" stroke-width="2" d="M264 95h46v22h-46z"/><text id="pop" fill="#8A704D" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="273.807" y="110">pop</tspan></text><path id="Shape-2" fill="#EE6B47" d="M94.5 117.5l4.5-6.192h-3V95.5h-3v15.808h-3z" transform="rotate(-90 94.5 106.5)"/><path id="Rectangle-6" fill="#FFF9EB" stroke="#E8C48E" stroke-width="2" d="M19 96h61v22H19z"/><text id="unshift" fill="#8A704D" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="23.833" y="112">unshift</tspan></text><path id="Shape-3" fill="#EE6B47" d="M326.5 152.5l4.5-6.192h-3V130.5h-3v15.808h-3z" transform="matrix(0 -1 -1 0 468 468)"/><path id="Rectangle-7" fill="#FFF9EB" stroke="#E8C48E" stroke-width="2" d="M310 130h-46v22h46z"/><text id="push" fill="#8A704D" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="269.89" y="146">push</tspan></text><path id="Shape-4" fill="#EE6B47" d="M93.5 152.5l4.5-6.192h-3V130.5h-3v15.808h-3z" transform="matrix(0 -1 -1 0 235 235)"/><path id="Rectangle-8" fill="#FFF9EB" stroke="#E8C48E" stroke-width="2" d="M79 131H18v22h61z"/><text id="shift" fill="#8A704D" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="32.535" y="147">shift</tspan></text><path id="Shape" fill="#E8C48F" d="M329.693 35.92c-.952-2.478-7.313-10.643-8.63-11.08-1.315-.437-4.095-.292-4.095-.292-1.243-13.925-4.314-15.456-6.434-16.987-2.12-1.53-1.902 4.521-1.902 4.521-.731-2.551-4.461-6.707-7.02-6.051-2.56.656-1.17 9.04-.804 11.372.367 2.334 3.656 5.686 4.898 7.51 1.243 1.821.586 3.425.586 3.425-1.024 1.46-.878 5.128-.878 5.128-2.558-.414-5.044 2.528-5.044 2.528-5.778-2.478-9.085-2.528-9.085-2.528-17.535.175-19.767 16.815-20.025 22.334-.626-.271-1.496-.264-2.619.618-2.581 2.027-.74 4.511-.74 4.511-.727-.2-1.447-3.54-1.447-3.54-1.65 3.517 1.35 5.92 1.35 5.92-1.118.239-2.968-1.01-2.968-1.01.175.235 1.333 2.033 3.581 4.38 2.838 2.96 7.023 2.322 9.087 2.243a.4.4 0 00.196-.009l.06-.001.01-.026c.324-.144.581-.602 1.929-.152l1.731 1.086s15.302.142 17.697 0c1.3-.077-1.316-3.79-1.316-3.79 4.753-.293 7.312-2.043 7.312-2.043l7.343 5.975h7.703c1.514 0 .586-1.618.586-1.618l-7.223-6.544.803-1.385c2.78-.583 4.754-3.28 5.045-3.79.294-.51.074-4.301.074-4.301s.294.365 1.535-1.167c1.244-1.532-.145-3.937-.51-4.448-.365-.509-1.097-3.72-1.097-3.72s4.898-.29 6.07-.874c1.167-.58 5.19-3.716 4.24-6.194zM85.866 34.289c-4.561 2.635-4.417 9.072-15.582 17.687-2.718 2.097 2.877 13.103.454 15.556-2.468 2.499-12.35 2.623-14.739 1.26-3.517-2.007-.76-7.243.027-14.886-7.206.735-9.541.615-16.783.313 1.104 5.799 2.719 12.246.377 14.37-1.909 1.731-11.198 1.919-14.103.022-3.635-2.374 2.634-15.875.509-16.093-2.445-.25-17.353.334-14.716-15.842 2.789 4.076 7.56 7.912 13.032 6.188.746-13.723 9.071-26.068 22.358-26.068s22.684 10.307 22.754 23.079c7.867-5.908 2.084-20.426 11.514-25.857 6.712-3.866 14.563-1.869 18.307 4.257 1.955 3.199 2.565 8.84.141 11.938-3.607 4.61-10.421 2.268-13.55 4.076z"/></g></g></svg>
|
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 4.6 KiB |
|
@ -92,38 +92,6 @@ let fruits = [
|
|||
The "trailing comma" style makes it easier to insert/remove items, because all lines become alike.
|
||||
````
|
||||
|
||||
## Get last elements with "at"
|
||||
|
||||
[recent browser="new"]
|
||||
|
||||
Let's say we want the last element of the array.
|
||||
|
||||
Some programming languages allow to use negative indexes for the same purpose, like `fruits[-1]`.
|
||||
|
||||
Although, in JavaScript it won't work. The result will be `undefined`, because the index in square brackets is treated literally.
|
||||
|
||||
We can explicitly calculate the last element index and then access it: `fruits[fruits.length - 1]`.
|
||||
|
||||
```js run
|
||||
let fruits = ["Apple", "Orange", "Plum"];
|
||||
|
||||
alert( fruits[fruits.length-1] ); // Plum
|
||||
```
|
||||
|
||||
A bit cumbersome, isn't it? We need to write the variable name twice.
|
||||
|
||||
Luckily, there's a shorter syntax: `fruits.at(-1)`:
|
||||
|
||||
```js run
|
||||
let fruits = ["Apple", "Orange", "Plum"];
|
||||
|
||||
// same as fruits[fruits.length-1]
|
||||
alert( fruits.at(-1) ); // Plum
|
||||
```
|
||||
|
||||
In other words, `arr.at(i)`:
|
||||
- is exactly the same as `arr[i]`, if `i >= 0`.
|
||||
- for negative values of `i`, it steps back from the end of the array.
|
||||
|
||||
## Methods pop/push, shift/unshift
|
||||
|
||||
|
@ -153,9 +121,9 @@ A stack is usually illustrated as a pack of cards: new cards are added to the to
|
|||
|
||||
For stacks, the latest pushed item is received first, that's also called LIFO (Last-In-First-Out) principle. For queues, we have FIFO (First-In-First-Out).
|
||||
|
||||
Arrays in JavaScript can work both as a queue and as a stack. They allow you to add/remove elements, both to/from the beginning or the end.
|
||||
Arrays in JavaScript can work both as a queue and as a stack. They allow you to add/remove elements both to/from the beginning or the end.
|
||||
|
||||
In computer science, the data structure that allows this, is called [deque](https://en.wikipedia.org/wiki/Double-ended_queue).
|
||||
In computer science the data structure that allows this, is called [deque](https://en.wikipedia.org/wiki/Double-ended_queue).
|
||||
|
||||
**Methods that work with the end of the array:**
|
||||
|
||||
|
@ -170,8 +138,6 @@ In computer science, the data structure that allows this, is called [deque](http
|
|||
alert( fruits ); // Apple, Orange
|
||||
```
|
||||
|
||||
Both `fruits.pop()` and `fruits.at(-1)` return the last element of the array, but `fruits.pop()` also modifies the array by removing it.
|
||||
|
||||
`push`
|
||||
: Append the element to the end of the array:
|
||||
|
||||
|
@ -281,7 +247,7 @@ Why is it faster to work with the end of an array than with its beginning? Let's
|
|||
fruits.shift(); // take 1 element from the start
|
||||
```
|
||||
|
||||
It's not enough to take and remove the element with the index `0`. Other elements need to be renumbered as well.
|
||||
It's not enough to take and remove the element with the number `0`. Other elements need to be renumbered as well.
|
||||
|
||||
The `shift` operation must do 3 things:
|
||||
|
||||
|
@ -399,11 +365,11 @@ There is one more syntax to create an array:
|
|||
let arr = *!*new Array*/!*("Apple", "Pear", "etc");
|
||||
```
|
||||
|
||||
It's rarely used, because square brackets `[]` are shorter. Also, there's a tricky feature with it.
|
||||
It's rarely used, because square brackets `[]` are shorter. Also there's a tricky feature with it.
|
||||
|
||||
If `new Array` is called with a single argument which is a number, then it creates an array *without items, but with the given length*.
|
||||
|
||||
Let's see how one can shoot themselves in the foot:
|
||||
Let's see how one can shoot themself in the foot:
|
||||
|
||||
```js run
|
||||
let arr = new Array(2); // will it create an array of [2] ?
|
||||
|
@ -413,7 +379,9 @@ alert( arr[0] ); // undefined! no elements.
|
|||
alert( arr.length ); // length 2
|
||||
```
|
||||
|
||||
To avoid such surprises, we usually use square brackets, unless we really know what we're doing.
|
||||
In the code above, `new Array(number)` has all elements `undefined`.
|
||||
|
||||
To evade such surprises, we usually use square brackets, unless we really know what we're doing.
|
||||
|
||||
## Multidimensional arrays
|
||||
|
||||
|
@ -473,7 +441,7 @@ Let's recall the rules:
|
|||
- If one of the arguments of `==` is an object, and the other one is a primitive, then the object gets converted to primitive, as explained in the chapter <info:object-toprimitive>.
|
||||
- ...With an exception of `null` and `undefined` that equal `==` each other and nothing else.
|
||||
|
||||
The strict comparison `===` is even simpler, as it doesn't convert types.
|
||||
The strict comparison `===` is even simpler, as it doesn't convert types.
|
||||
|
||||
So, if we compare arrays with `==`, they are never the same, unless we compare two variables that reference exactly the same array.
|
||||
|
||||
|
@ -493,7 +461,7 @@ alert( 0 == [] ); // true
|
|||
alert('0' == [] ); // false
|
||||
```
|
||||
|
||||
Here, in both cases, we compare a primitive with an array object. So the array `[]` gets converted to primitive for the purpose of comparison and becomes an empty string `''`.
|
||||
Here, in both cases, we compare a primitive with an array object. So the array `[]` gets converted to primitive for the purpose of comparison and becomes an empty string `''`.
|
||||
|
||||
Then the comparison process goes on with the primitives, as described in the chapter <info:type-conversions>:
|
||||
|
||||
|
@ -512,26 +480,21 @@ That's simple: don't use the `==` operator. Instead, compare them item-by-item i
|
|||
|
||||
Array is a special kind of object, suited to storing and managing ordered data items.
|
||||
|
||||
The declaration:
|
||||
- The declaration:
|
||||
|
||||
```js
|
||||
// square brackets (usual)
|
||||
let arr = [item1, item2...];
|
||||
```js
|
||||
// square brackets (usual)
|
||||
let arr = [item1, item2...];
|
||||
|
||||
// new Array (exceptionally rare)
|
||||
let arr = new Array(item1, item2...);
|
||||
```
|
||||
// new Array (exceptionally rare)
|
||||
let arr = new Array(item1, item2...);
|
||||
```
|
||||
|
||||
The call to `new Array(number)` creates an array with the given length, but without elements.
|
||||
The call to `new Array(number)` creates an array with the given length, but without elements.
|
||||
|
||||
- The `length` property is the array length or, to be precise, its last numeric index plus one. It is auto-adjusted by array methods.
|
||||
- If we shorten `length` manually, the array is truncated.
|
||||
|
||||
Getting the elements:
|
||||
|
||||
- we can get element by its index, like `arr[0]`
|
||||
- also we can use `at(i)` method that allows negative indexes. For negative values of `i`, it steps back from the end of the array. If `i >= 0`, it works same as `arr[i]`.
|
||||
|
||||
We can use an array as a deque with the following operations:
|
||||
|
||||
- `push(...items)` adds `items` to the end.
|
||||
|
|
|
@ -1 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="187" height="108" viewBox="0 0 187 108"><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="array" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="queue.svg"><path id="Shape" fill="#DBAF88" d="M90.795 43.15a2.79 2.79 0 01-.748-5.476l22.392-6.253a2.789 2.789 0 111.5 5.372l-22.392 6.253a2.76 2.76 0 01-.752.104zM90.795 54.188a2.79 2.79 0 01-.748-5.476l22.392-6.253a2.789 2.789 0 111.5 5.372l-22.392 6.253a2.76 2.76 0 01-.752.104zM90.795 64.557a2.79 2.79 0 01-.748-5.476l22.392-6.253a2.789 2.789 0 111.5 5.372l-22.392 6.253a2.8 2.8 0 01-.752.104z"/><path id="Shape" fill="#DBAF88" d="M114.837 17.439v-1.784c0-3.613-3.021-7.324-6.551-6.551-9.412 2.06-30.718 8.959-41.853 11.353-3.531.759-6.552 3.736-6.552 7.349v60.952c0 3.613 2.976 7.061 6.552 6.552 1.999-.284 4.267-.747 6.728-1.34v1.428c0 3.613 2.975 7.061 6.551 6.552 4.176-.594 26.68-7.4 32.483-9.111v-5.374c-5.567 1.638-28.034 8.308-32.483 9.349-.761.179-1.416-.635-1.416-1.416V35.74c-.078-1.792.619-3.287 2.715-3.847 6.07-1.623 21.952-5.945 31.944-8.668l1.881-.512 6.728-1.834c.781 0 1.416.635 1.416 1.416v60.953c0 .781-.665 1.203-1.416 1.416-2.596.738-5.82 1.713-9.382 2.801l.013 5.374c3.405-1.119 6.602-2.17 9.369-3.039 3.446-1.083 6.552-2.369 6.552-6.552V22.294c.001-3.612-3.021-7.324-6.551-6.551-1.841.403-4.142.993-6.728 1.696zm-35.124 9.657c-3.531.76-6.551 3.737-6.551 7.349V88.46a201.44 201.44 0 01-6.728 1.715c-.761.178-1.416-.636-1.416-1.417V29.1c-.078-1.792.619-3.287 2.715-3.847 9.143-2.443 40.554-11.014 40.554-11.014.781 0 1.416.635 1.416 1.416v3.217c-10.059 2.86-22.391 6.59-29.99 8.224z"/><path id="Shape-3" fill="#C06334" d="M158 71.817l12-12h-8V29.183h-8v30.634h-8z" transform="matrix(0 -1 -1 0 208.5 208.5)"/><path id="Shape-2" fill="#C06334" d="M30 71.817l12-12h-8V29.183h-8v30.634h-8z" transform="matrix(0 -1 -1 0 80.5 80.5)"/><text id="push" fill="#C06334" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="146.39" y="32">push</tspan></text><text id="shift" fill="#C06334" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="17.535" y="32">shift</tspan></text></g></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="187" height="108" viewBox="0 0 187 108"><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="array" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="queue.svg"><path id="Shape" fill="#E8C48F" d="M90.795 43.15a2.79 2.79 0 01-.748-5.476l22.392-6.253a2.789 2.789 0 111.5 5.372l-22.392 6.253a2.76 2.76 0 01-.752.104zM90.795 54.188a2.79 2.79 0 01-.748-5.476l22.392-6.253a2.789 2.789 0 111.5 5.372l-22.392 6.253a2.76 2.76 0 01-.752.104zM90.795 64.557a2.79 2.79 0 01-.748-5.476l22.392-6.253a2.789 2.789 0 111.5 5.372l-22.392 6.253a2.8 2.8 0 01-.752.104z"/><path id="Shape" fill="#E8C48F" d="M114.837 17.439v-1.784c0-3.613-3.021-7.324-6.551-6.551-9.412 2.06-30.718 8.959-41.853 11.353-3.531.759-6.552 3.736-6.552 7.349v60.952c0 3.613 2.976 7.061 6.552 6.552 1.999-.284 4.267-.747 6.728-1.34v1.428c0 3.613 2.975 7.061 6.551 6.552 4.176-.594 26.68-7.4 32.483-9.111v-5.374c-5.567 1.638-28.034 8.308-32.483 9.349-.761.179-1.416-.635-1.416-1.416V35.74c-.078-1.792.619-3.287 2.715-3.847 6.07-1.623 21.952-5.945 31.944-8.668l1.881-.512 6.728-1.834c.781 0 1.416.635 1.416 1.416v60.953c0 .781-.665 1.203-1.416 1.416-2.596.738-5.82 1.713-9.382 2.801l.013 5.374c3.405-1.119 6.602-2.17 9.369-3.039 3.446-1.083 6.552-2.369 6.552-6.552V22.294c.001-3.612-3.021-7.324-6.551-6.551-1.841.403-4.142.993-6.728 1.696zm-35.124 9.657c-3.531.76-6.551 3.737-6.551 7.349V88.46a201.44 201.44 0 01-6.728 1.715c-.761.178-1.416-.636-1.416-1.417V29.1c-.078-1.792.619-3.287 2.715-3.847 9.143-2.443 40.554-11.014 40.554-11.014.781 0 1.416.635 1.416 1.416v3.217c-10.059 2.86-22.391 6.59-29.99 8.224z"/><path id="Shape-3" fill="#EE6B47" d="M158 71.817l12-12h-8V29.183h-8v30.634h-8z" transform="matrix(0 -1 -1 0 208.5 208.5)"/><path id="Shape-2" fill="#EE6B47" d="M30 71.817l12-12h-8V29.183h-8v30.634h-8z" transform="matrix(0 -1 -1 0 80.5 80.5)"/><text id="push" fill="#EE6B47" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="146.39" y="32">push</tspan></text><text id="shift" fill="#EE6B47" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="17.535" y="32">shift</tspan></text></g></g></svg>
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
|
@ -1 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="145" height="149" viewBox="0 0 145 149"><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="array" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="stack.svg"><path id="Shape-3" fill="#DBAF88" d="M40.39 83.617c-1.45.556-2.994.508-3.73-.188-.887-.837-.266-2.297 1.387-3.26L62.98 65.631c1.652-.962 3.709-1.064 4.597-.227.886.838.264 2.298-1.388 3.26L41.255 83.203c-.28.163-.57.302-.865.415z"/><path id="Shape-4" fill="#DBAF88" d="M49.861 90.382c-1.449.556-2.993.509-3.73-.188-.886-.837-.265-2.296 1.387-3.26l24.935-14.536c1.651-.963 3.708-1.064 4.596-.228.886.839.265 2.298-1.388 3.261L50.727 89.967c-.28.163-.571.302-.866.415z"/><path id="Shape-5" fill="#DBAF88" d="M59.333 95.795c-1.449.556-2.993.508-3.73-.188-.886-.837-.265-2.297 1.387-3.26L81.925 77.81c1.651-.962 3.708-1.064 4.596-.227.886.838.265 2.298-1.388 3.26L60.2 95.38c-.28.163-.571.301-.866.415z"/><path id="Shape-6" fill="#DBAF88" d="M135.388 102.356c0-.535-.286-1.027-.75-1.294l-4.934-2.848 4.935-2.851a1.492 1.492 0 000-2.587L65.055 52.598a1.498 1.498 0 00-1.495 0L14.357 81.006a1.493 1.493 0 000 2.589l4.936 2.85-4.936 2.85a1.496 1.496 0 000 2.59l4.938 2.85-4.938 2.85a1.493 1.493 0 000 2.588l69.586 40.175a1.487 1.487 0 001.496 0l49.201-28.407a1.492 1.492 0 000-2.588l-4.936-2.851 4.936-2.85c.462-.268.748-.762.748-1.296zM64.307 55.618l66.596 38.45L84.69 120.75l-66.597-38.45 46.214-26.683zm62.608 52.725l3.988 2.303L84.69 137.33 18.093 98.88l4.19-2.419 61.66 35.6a1.504 1.504 0 001.495 0l41.299-23.845c.055.046.113.092.178.128zM84.69 129.04L18.17 90.634l-.076-.045 4.19-2.418 61.66 35.6a1.487 1.487 0 001.496 0l41.406-23.906c.12.163.272.307.459.415l3.6 2.077-46.214 26.682z"/><path id="Shape" fill="#C06334" d="M63.046 64.937c-.295.332-.655.489-1.037.489-.763 0-1.618-.627-2.224-1.753l-9.247-17.146c-.808-1.502-.29-3.268.803-3.268.136 0 .283.027.436.085l4.49 1.736.067-34.628 12.308 8.087v31.348l4.493 1.738c1.375.533 1.78 1.781.896 2.775L63.046 64.937z"/><path id="Shape-2" fill="#C06334" d="M94.167 33.943c-.294-.332-.654-.489-1.037-.489-.763 0-1.617.627-2.223 1.753L81.66 52.353c-.809 1.502-.29 3.268.803 3.268.136 0 .282-.027.435-.085l4.49-1.736.067 34.628 12.309 7.567V48.993l4.492-1.738c1.376-.533 1.781-1.78.897-2.775L94.167 33.943z"/><text id="push" fill="#C06334" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="13.39" y="32">push</tspan></text><text id="pop" fill="#C06334" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="109.307" y="63">pop</tspan></text></g></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="145" height="149" viewBox="0 0 145 149"><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="array" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="stack.svg"><path id="Shape-3" fill="#E8C48F" d="M40.39 83.617c-1.45.556-2.994.508-3.73-.188-.887-.837-.266-2.297 1.387-3.26L62.98 65.631c1.652-.962 3.709-1.064 4.597-.227.886.838.264 2.298-1.388 3.26L41.255 83.203c-.28.163-.57.302-.865.415z"/><path id="Shape-4" fill="#E8C48F" d="M49.861 90.382c-1.449.556-2.993.509-3.73-.188-.886-.837-.265-2.296 1.387-3.26l24.935-14.536c1.651-.963 3.708-1.064 4.596-.228.886.839.265 2.298-1.388 3.261L50.727 89.967c-.28.163-.571.302-.866.415z"/><path id="Shape-5" fill="#E8C48F" d="M59.333 95.795c-1.449.556-2.993.508-3.73-.188-.886-.837-.265-2.297 1.387-3.26L81.925 77.81c1.651-.962 3.708-1.064 4.596-.227.886.838.265 2.298-1.388 3.26L60.2 95.38c-.28.163-.571.301-.866.415z"/><path id="Shape-6" fill="#E8C48F" d="M135.388 102.356c0-.535-.286-1.027-.75-1.294l-4.934-2.848 4.935-2.851a1.492 1.492 0 000-2.587L65.055 52.598a1.498 1.498 0 00-1.495 0L14.357 81.006a1.493 1.493 0 000 2.589l4.936 2.85-4.936 2.85a1.496 1.496 0 000 2.59l4.938 2.85-4.938 2.85a1.493 1.493 0 000 2.588l69.586 40.175a1.487 1.487 0 001.496 0l49.201-28.407a1.492 1.492 0 000-2.588l-4.936-2.851 4.936-2.85c.462-.268.748-.762.748-1.296zM64.307 55.618l66.596 38.45L84.69 120.75l-66.597-38.45 46.214-26.683zm62.608 52.725l3.988 2.303L84.69 137.33 18.093 98.88l4.19-2.419 61.66 35.6a1.504 1.504 0 001.495 0l41.299-23.845c.055.046.113.092.178.128zM84.69 129.04L18.17 90.634l-.076-.045 4.19-2.418 61.66 35.6a1.487 1.487 0 001.496 0l41.406-23.906c.12.163.272.307.459.415l3.6 2.077-46.214 26.682z"/><path id="Shape" fill="#EE6B47" d="M63.046 64.937c-.295.332-.655.489-1.037.489-.763 0-1.618-.627-2.224-1.753l-9.247-17.146c-.808-1.502-.29-3.268.803-3.268.136 0 .283.027.436.085l4.49 1.736.067-34.628 12.308 8.087v31.348l4.493 1.738c1.375.533 1.78 1.781.896 2.775L63.046 64.937z"/><path id="Shape-2" fill="#EE6B47" d="M94.167 33.943c-.294-.332-.654-.489-1.037-.489-.763 0-1.617.627-2.223 1.753L81.66 52.353c-.809 1.502-.29 3.268.803 3.268.136 0 .282-.027.435-.085l4.49-1.736.067 34.628 12.309 7.567V48.993l4.492-1.738c1.376-.533 1.781-1.78.897-2.775L94.167 33.943z"/><text id="push" fill="#EE6B47" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="13.39" y="32">push</tspan></text><text id="pop" fill="#EE6B47" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="109.307" y="63">pop</tspan></text></g></g></svg>
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
|
@ -4,7 +4,7 @@ importance: 4
|
|||
|
||||
# Create keyed object from array
|
||||
|
||||
Let's say we received an array of users in the form `{id:..., name:..., age:... }`.
|
||||
Let's say we received an array of users in the form `{id:..., name:..., age... }`.
|
||||
|
||||
Create a function `groupById(arr)` that creates an object from it, with `id` as the key, and array items as values.
|
||||
|
||||
|
|
|
@ -4,13 +4,13 @@ describe("filterRangeInPlace", function() {
|
|||
|
||||
let arr = [5, 3, 8, 1];
|
||||
|
||||
filterRangeInPlace(arr, 2, 5);
|
||||
filterRangeInPlace(arr, 1, 4);
|
||||
|
||||
assert.deepEqual(arr, [5, 3]);
|
||||
assert.deepEqual(arr, [3, 1]);
|
||||
});
|
||||
|
||||
it("doesn't return anything", function() {
|
||||
assert.isUndefined(filterRangeInPlace([1,2,3], 1, 4));
|
||||
});
|
||||
|
||||
});
|
||||
});
|
|
@ -234,13 +234,12 @@ Now let's cover methods that search in an array.
|
|||
|
||||
### indexOf/lastIndexOf and includes
|
||||
|
||||
The methods [arr.indexOf](mdn:js/Array/indexOf) and [arr.includes](mdn:js/Array/includes) have the similar syntax and do essentially the same as their string counterparts, but operate on items instead of characters:
|
||||
The methods [arr.indexOf](mdn:js/Array/indexOf), [arr.lastIndexOf](mdn:js/Array/lastIndexOf) and [arr.includes](mdn:js/Array/includes) have the same syntax and do essentially the same as their string counterparts, but operate on items instead of characters:
|
||||
|
||||
- `arr.indexOf(item, from)` -- looks for `item` starting from index `from`, and returns the index where it was found, otherwise `-1`.
|
||||
- `arr.lastIndexOf(item, from)` -- same, but looks for from right to left.
|
||||
- `arr.includes(item, from)` -- looks for `item` starting from index `from`, returns `true` if found.
|
||||
|
||||
Usually these methods are used with only one argument: the `item` to search. By default, the search is from the beginning.
|
||||
|
||||
For instance:
|
||||
|
||||
```js run
|
||||
|
@ -253,31 +252,19 @@ alert( arr.indexOf(null) ); // -1
|
|||
alert( arr.includes(1) ); // true
|
||||
```
|
||||
|
||||
Please note that `indexOf` uses the strict equality `===` for comparison. So, if we look for `false`, it finds exactly `false` and not the zero.
|
||||
Note that the methods use `===` comparison. So, if we look for `false`, it finds exactly `false` and not the zero.
|
||||
|
||||
If we want to check if `item` exists in the array, and don't need the exact index, then `arr.includes` is preferred.
|
||||
If we want to check for inclusion, and don't want to know the exact index, then `arr.includes` is preferred.
|
||||
|
||||
The method [arr.lastIndexOf](mdn:js/Array/lastIndexOf) is the same as `indexOf`, but looks for from right to left.
|
||||
|
||||
```js run
|
||||
let fruits = ['Apple', 'Orange', 'Apple']
|
||||
|
||||
alert( fruits.indexOf('Apple') ); // 0 (first Apple)
|
||||
alert( fruits.lastIndexOf('Apple') ); // 2 (last Apple)
|
||||
```
|
||||
|
||||
````smart header="The `includes` method handles `NaN` correctly"
|
||||
A minor, but noteworthy feature of `includes` is that it correctly handles `NaN`, unlike `indexOf`:
|
||||
Also, a very minor difference of `includes` is that it correctly handles `NaN`, unlike `indexOf/lastIndexOf`:
|
||||
|
||||
```js run
|
||||
const arr = [NaN];
|
||||
alert( arr.indexOf(NaN) ); // -1 (wrong, should be 0)
|
||||
alert( arr.indexOf(NaN) ); // -1 (should be 0, but === equality doesn't work for NaN)
|
||||
alert( arr.includes(NaN) );// true (correct)
|
||||
```
|
||||
That's because `includes` was added to JavaScript much later and uses the more up to date comparison algorithm internally.
|
||||
````
|
||||
|
||||
### find and findIndex/findLastIndex
|
||||
### find and findIndex
|
||||
|
||||
Imagine we have an array of objects. How do we find an object with the specific condition?
|
||||
|
||||
|
@ -317,28 +304,7 @@ In real life arrays of objects is a common thing, so the `find` method is very u
|
|||
|
||||
Note that in the example we provide to `find` the function `item => item.id == 1` with one argument. That's typical, other arguments of this function are rarely used.
|
||||
|
||||
The [arr.findIndex](mdn:js/Array/findIndex) method has the same syntax, but returns the index where the element was found instead of the element itself. The value of `-1` is returned if nothing is found.
|
||||
|
||||
The [arr.findLastIndex](mdn:js/Array/findLastIndex) method is like `findIndex`, but searches from right to left, similar to `lastIndexOf`.
|
||||
|
||||
Here's an example:
|
||||
|
||||
```js run
|
||||
let users = [
|
||||
{id: 1, name: "John"},
|
||||
{id: 2, name: "Pete"},
|
||||
{id: 3, name: "Mary"},
|
||||
{id: 4, name: "John"}
|
||||
];
|
||||
|
||||
// Find the index of the first John
|
||||
alert(users.findIndex(user => user.name == 'John')); // 0
|
||||
|
||||
// Find the index of the last John
|
||||
alert(users.findLastIndex(user => user.name == 'John')); // 3
|
||||
```
|
||||
|
||||
|
||||
The [arr.findIndex](mdn:js/Array/findIndex) method is essentially the same, but it returns the index where the element was found instead of the element itself and `-1` is returned when nothing is found.
|
||||
|
||||
### filter
|
||||
|
||||
|
@ -423,7 +389,6 @@ Literally, all elements are converted to strings for comparisons. For strings, l
|
|||
To use our own sorting order, we need to supply a function as the argument of `arr.sort()`.
|
||||
|
||||
The function should compare two arbitrary values and return:
|
||||
|
||||
```js
|
||||
function compare(a, b) {
|
||||
if (a > b) return 1; // if the first value is greater than the second
|
||||
|
@ -668,6 +633,7 @@ So it's advised to always specify the initial value.
|
|||
|
||||
The method [arr.reduceRight](mdn:js/Array/reduceRight) does the same, but goes from right to left.
|
||||
|
||||
|
||||
## Array.isArray
|
||||
|
||||
Arrays do not form a separate language type. They are based on objects.
|
||||
|
@ -676,7 +642,7 @@ So `typeof` does not help to distinguish a plain object from an array:
|
|||
|
||||
```js run
|
||||
alert(typeof {}); // object
|
||||
alert(typeof []); // object (same)
|
||||
alert(typeof []); // same
|
||||
```
|
||||
|
||||
...But arrays are used so often that there's a special method for that: [Array.isArray(value)](mdn:js/Array/isArray). It returns `true` if the `value` is an array, and `false` otherwise.
|
||||
|
@ -767,7 +733,7 @@ A cheat sheet of array methods:
|
|||
- `reduce/reduceRight(func, initial)` -- calculate a single value over the array by calling `func` for each element and passing an intermediate result between the calls.
|
||||
|
||||
- Additionally:
|
||||
- `Array.isArray(value)` checks `value` for being an array, if so returns `true`, otherwise `false`.
|
||||
- `Array.isArray(arr)` checks `arr` for being an array.
|
||||
|
||||
Please note that methods `sort`, `reverse` and `splice` modify the array itself.
|
||||
|
||||
|
@ -780,7 +746,6 @@ These methods are the most used ones, they cover 99% of use cases. But there are
|
|||
These methods behave sort of like `||` and `&&` operators: if `fn` returns a truthy value, `arr.some()` immediately returns `true` and stops iterating over the rest of items; if `fn` returns a falsy value, `arr.every()` immediately returns `false` and stops iterating over the rest of items as well.
|
||||
|
||||
We can use `every` to compare arrays:
|
||||
|
||||
```js run
|
||||
function arraysEqual(arr1, arr2) {
|
||||
return arr1.length === arr2.length && arr1.every((value, index) => value === arr2[index]);
|
||||
|
|
|
@ -1 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="620" height="129" viewBox="0 0 620 129"><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="array" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="reduce.svg"><path id="Rectangle-1" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M9 75h78v38H9z"/><text id="1" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="44" y="99">1</tspan></text><text id="sum" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="15" y="17">sum</tspan> <tspan x="15" y="32">0</tspan> <tspan x="15" y="47">current</tspan> <tspan x="15" y="62">1</tspan></text><path id="Rectangle-2" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M88 75h78v38H88z"/><text id="2" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="123" y="99">2</tspan></text><text id="sum" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="94" y="17">sum</tspan> <tspan x="94" y="32">0+1</tspan> <tspan x="94" y="47">current</tspan> <tspan x="94" y="62">2</tspan></text><path id="Rectangle-3" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M167 75h78v38h-78z"/><text id="3" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="202" y="99">3</tspan></text><text id="sum" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="173" y="17">sum</tspan> <tspan x="173" y="32">0+1+2</tspan> <tspan x="173" y="47">current</tspan> <tspan x="173" y="62">3</tspan></text><path id="Rectangle-5" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M246 75h78v38h-78z"/><text id="4" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="281" y="99">4</tspan></text><text id="sum" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="252" y="17">sum</tspan> <tspan x="252" y="32">0+1+2+3</tspan> <tspan x="252" y="47">current</tspan> <tspan x="252" y="62">4</tspan></text><path id="Rectangle-4" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M325 75h78v38h-78z"/><text id="5" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="360" y="99">5</tspan></text><text id="sum" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="331" y="17">sum</tspan> <tspan x="331" y="32">0+1+2+3+4</tspan> <tspan x="331" y="47">current</tspan> <tspan x="331" y="62">5</tspan></text><path id="Line" fill="#C06334" fill-rule="nonzero" d="M456.5 85l14 7-14 7v-6h-37v-2h37v-6z"/><text id="0+1+2+3+4+5-=-15" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="480" y="96">0+1+2+3+4+5 = 15</tspan></text></g></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="620" height="129" viewBox="0 0 620 129"><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="array" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="reduce.svg"><path id="Rectangle-1" fill="#FFF9EB" stroke="#E8C48E" stroke-width="2" d="M9 75h78v38H9z"/><text id="1" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="44" y="99">1</tspan></text><text id="sum" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="15" y="17">sum</tspan> <tspan x="15" y="32">0</tspan> <tspan x="15" y="47">current</tspan> <tspan x="15" y="62">1</tspan></text><path id="Rectangle-2" fill="#FFF9EB" stroke="#E8C48E" stroke-width="2" d="M88 75h78v38H88z"/><text id="2" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="123" y="99">2</tspan></text><text id="sum" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="94" y="17">sum</tspan> <tspan x="94" y="32">0+1</tspan> <tspan x="94" y="47">current</tspan> <tspan x="94" y="62">2</tspan></text><path id="Rectangle-3" fill="#FFF9EB" stroke="#E8C48E" stroke-width="2" d="M167 75h78v38h-78z"/><text id="3" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="202" y="99">3</tspan></text><text id="sum" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="173" y="17">sum</tspan> <tspan x="173" y="32">0+1+2</tspan> <tspan x="173" y="47">current</tspan> <tspan x="173" y="62">3</tspan></text><path id="Rectangle-5" fill="#FFF9EB" stroke="#E8C48E" stroke-width="2" d="M246 75h78v38h-78z"/><text id="4" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="281" y="99">4</tspan></text><text id="sum" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="252" y="17">sum</tspan> <tspan x="252" y="32">0+1+2+3</tspan> <tspan x="252" y="47">current</tspan> <tspan x="252" y="62">4</tspan></text><path id="Rectangle-4" fill="#FFF9EB" stroke="#E8C48E" stroke-width="2" d="M325 75h78v38h-78z"/><text id="5" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="360" y="99">5</tspan></text><text id="sum" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="331" y="17">sum</tspan> <tspan x="331" y="32">0+1+2+3+4</tspan> <tspan x="331" y="47">current</tspan> <tspan x="331" y="62">5</tspan></text><path id="Line" fill="#EE6B47" fill-rule="nonzero" d="M456.5 85l14 7-14 7v-6h-37v-2h37v-6z"/><text id="0+1+2+3+4+5-=-15" fill="#8A704D" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="480" y="96">0+1+2+3+4+5 = 15</tspan></text></g></g></svg>
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
|
@ -31,7 +31,7 @@ To make the `range` object iterable (and thus let `for..of` work) we need to add
|
|||
1. When `for..of` starts, it calls that method once (or errors if not found). The method must return an *iterator* -- an object with the method `next`.
|
||||
2. Onward, `for..of` works *only with that returned object*.
|
||||
3. When `for..of` wants the next value, it calls `next()` on that object.
|
||||
4. The result of `next()` must have the form `{done: Boolean, value: any}`, where `done=true` means that the loop is finished, otherwise `value` is the next value.
|
||||
4. The result of `next()` must have the form `{done: Boolean, value: any}`, where `done=true` means that the iteration is finished, otherwise `value` is the next value.
|
||||
|
||||
Here's the full implementation for `range` with remarks:
|
||||
|
||||
|
@ -45,10 +45,10 @@ let range = {
|
|||
range[Symbol.iterator] = function() {
|
||||
|
||||
// ...it returns the iterator object:
|
||||
// 2. Onward, for..of works only with the iterator object below, asking it for next values
|
||||
// 2. Onward, for..of works only with this iterator, asking it for next values
|
||||
return {
|
||||
current: this.from,
|
||||
last: this.to,
|
||||
last: this.to,
|
||||
|
||||
// 3. next() is called on each iteration by the for..of loop
|
||||
next() {
|
||||
|
@ -218,7 +218,7 @@ alert(arr.pop()); // World (method works)
|
|||
|
||||
The same happens for an iterable:
|
||||
|
||||
```js run
|
||||
```js
|
||||
// assuming that range is taken from the example above
|
||||
let arr = Array.from(range);
|
||||
alert(arr); // 1,2,3,4,5 (array toString conversion works)
|
||||
|
@ -233,7 +233,7 @@ The optional second argument `mapFn` can be a function that will be applied to e
|
|||
|
||||
For instance:
|
||||
|
||||
```js run
|
||||
```js
|
||||
// assuming that range is taken from the example above
|
||||
|
||||
// square each number
|
||||
|
@ -270,7 +270,7 @@ for (let char of str) {
|
|||
alert(chars);
|
||||
```
|
||||
|
||||
...But it is shorter.
|
||||
...But it is shorter.
|
||||
|
||||
We can even build surrogate-aware `slice` on it:
|
||||
|
||||
|
|
|
@ -15,12 +15,12 @@ But that's not enough for real life. That's why `Map` and `Set` also exist.
|
|||
Methods and properties are:
|
||||
|
||||
- `new Map()` -- creates the map.
|
||||
- [`map.set(key, value)`](mdn:js/Map/set) -- stores the value by the key.
|
||||
- [`map.get(key)`](mdn:js/Map/get) -- returns the value by the key, `undefined` if `key` doesn't exist in map.
|
||||
- [`map.has(key)`](mdn:js/Map/has) -- returns `true` if the `key` exists, `false` otherwise.
|
||||
- [`map.delete(key)`](mdn:js/Map/delete) -- removes the value by the key.
|
||||
- [`map.clear()`](mdn:js/Map/clear) -- removes everything from the map.
|
||||
- [`map.size`](mdn:js/Map/size) -- returns the current element count.
|
||||
- `map.set(key, value)` -- stores the value by the key.
|
||||
- `map.get(key)` -- returns the value by the key, `undefined` if `key` doesn't exist in map.
|
||||
- `map.has(key)` -- returns `true` if the `key` exists, `false` otherwise.
|
||||
- `map.delete(key)` -- removes the value by the key.
|
||||
- `map.clear()` -- removes everything from the map.
|
||||
- `map.size` -- returns the current element count.
|
||||
|
||||
For instance:
|
||||
|
||||
|
@ -105,9 +105,9 @@ map.set('1', 'str1')
|
|||
|
||||
For looping over a `map`, there are 3 methods:
|
||||
|
||||
- [`map.keys()`](mdn:js/Map/keys) -- returns an iterable for keys,
|
||||
- [`map.values()`](mdn:js/Map/values) -- returns an iterable for values,
|
||||
- [`map.entries()`](mdn:js/Map/entries) -- returns an iterable for entries `[key, value]`, it's used by default in `for..of`.
|
||||
- `map.keys()` -- returns an iterable for keys,
|
||||
- `map.values()` -- returns an iterable for values,
|
||||
- `map.entries()` -- returns an iterable for entries `[key, value]`, it's used by default in `for..of`.
|
||||
|
||||
For instance:
|
||||
|
||||
|
@ -238,11 +238,11 @@ A `Set` is a special type collection - "set of values" (without keys), where eac
|
|||
Its main methods are:
|
||||
|
||||
- `new Set(iterable)` -- creates the set, and if an `iterable` object is provided (usually an array), copies values from it into the set.
|
||||
- [`set.add(value)`](mdn:js/Set/add) -- adds a value, returns the set itself.
|
||||
- [`set.delete(value)`](mdn:js/Set/delete) -- removes the value, returns `true` if `value` existed at the moment of the call, otherwise `false`.
|
||||
- [`set.has(value)`](mdn:js/Set/has) -- returns `true` if the value exists in the set, otherwise `false`.
|
||||
- [`set.clear()`](mdn:js/Set/clear) -- removes everything from the set.
|
||||
- [`set.size`](mdn:js/Set/size) -- is the elements count.
|
||||
- `set.add(value)` -- adds a value, returns the set itself.
|
||||
- `set.delete(value)` -- removes the value, returns `true` if `value` existed at the moment of the call, otherwise `false`.
|
||||
- `set.has(value)` -- returns `true` if the value exists in the set, otherwise `false`.
|
||||
- `set.clear()` -- removes everything from the set.
|
||||
- `set.size` -- is the elements count.
|
||||
|
||||
The main feature is that repeated calls of `set.add(value)` with the same value don't do anything. That's the reason why each value appears in a `Set` only once.
|
||||
|
||||
|
@ -291,13 +291,13 @@ set.forEach((value, valueAgain, set) => {
|
|||
|
||||
Note the funny thing. The callback function passed in `forEach` has 3 arguments: a `value`, then *the same value* `valueAgain`, and then the target object. Indeed, the same value appears in the arguments twice.
|
||||
|
||||
That's for compatibility with `Map` where the callback passed `forEach` has three arguments. Looks a bit strange, for sure. But this may help to replace `Map` with `Set` in certain cases with ease, and vice versa.
|
||||
That's for compatibility with `Map` where the callback passed `forEach` has three arguments. Looks a bit strange, for sure. But may help to replace `Map` with `Set` in certain cases with ease, and vice versa.
|
||||
|
||||
The same methods `Map` has for iterators are also supported:
|
||||
|
||||
- [`set.keys()`](mdn:js/Set/keys) -- returns an iterable object for values,
|
||||
- [`set.values()`](mdn:js/Set/values) -- same as `set.keys()`, for compatibility with `Map`,
|
||||
- [`set.entries()`](mdn:js/Set/entries) -- returns an iterable object for entries `[value, value]`, exists for compatibility with `Map`.
|
||||
- `set.keys()` -- returns an iterable object for values,
|
||||
- `set.values()` -- same as `set.keys()`, for compatibility with `Map`,
|
||||
- `set.entries()` -- returns an iterable object for entries `[value, value]`, exists for compatibility with `Map`.
|
||||
|
||||
## Summary
|
||||
|
||||
|
@ -306,12 +306,12 @@ The same methods `Map` has for iterators are also supported:
|
|||
Methods and properties:
|
||||
|
||||
- `new Map([iterable])` -- creates the map, with optional `iterable` (e.g. array) of `[key,value]` pairs for initialization.
|
||||
- [`map.set(key, value)`](mdn:js/Map/set) -- stores the value by the key, returns the map itself.
|
||||
- [`map.get(key)`](mdn:js/Map/get) -- returns the value by the key, `undefined` if `key` doesn't exist in map.
|
||||
- [`map.has(key)`](mdn:js/Map/has) -- returns `true` if the `key` exists, `false` otherwise.
|
||||
- [`map.delete(key)`](mdn:js/Map/delete) -- removes the value by the key, returns `true` if `key` existed at the moment of the call, otherwise `false`.
|
||||
- [`map.clear()`](mdn:js/Map/clear) -- removes everything from the map.
|
||||
- [`map.size`](mdn:js/Map/size) -- returns the current element count.
|
||||
- `map.set(key, value)` -- stores the value by the key, returns the map itself.
|
||||
- `map.get(key)` -- returns the value by the key, `undefined` if `key` doesn't exist in map.
|
||||
- `map.has(key)` -- returns `true` if the `key` exists, `false` otherwise.
|
||||
- `map.delete(key)` -- removes the value by the key, returns `true` if `key` existed at the moment of the call, otherwise `false`.
|
||||
- `map.clear()` -- removes everything from the map.
|
||||
- `map.size` -- returns the current element count.
|
||||
|
||||
The differences from a regular `Object`:
|
||||
|
||||
|
@ -323,10 +323,10 @@ The differences from a regular `Object`:
|
|||
Methods and properties:
|
||||
|
||||
- `new Set([iterable])` -- creates the set, with optional `iterable` (e.g. array) of values for initialization.
|
||||
- [`set.add(value)`](mdn:js/Set/add) -- adds a value (does nothing if `value` exists), returns the set itself.
|
||||
- [`set.delete(value)`](mdn:js/Set/delete) -- removes the value, returns `true` if `value` existed at the moment of the call, otherwise `false`.
|
||||
- [`set.has(value)`](mdn:js/Set/has) -- returns `true` if the value exists in the set, otherwise `false`.
|
||||
- [`set.clear()`](mdn:js/Set/clear) -- removes everything from the set.
|
||||
- [`set.size`](mdn:js/Set/size) -- is the elements count.
|
||||
- `set.add(value)` -- adds a value (does nothing if `value` exists), returns the set itself.
|
||||
- `set.delete(value)` -- removes the value, returns `true` if `value` existed at the moment of the call, otherwise `false`.
|
||||
- `set.has(value)` -- returns `true` if the value exists in the set, otherwise `false`.
|
||||
- `set.clear()` -- removes everything from the set.
|
||||
- `set.size` -- is the elements count.
|
||||
|
||||
Iteration over `Map` and `Set` is always in the insertion order, so we can't say that these collections are unordered, but we can't reorder elements or directly get an element by its number.
|
||||
|
|
|
@ -77,7 +77,7 @@ Objects lack many methods that exist for arrays, e.g. `map`, `filter` and others
|
|||
If we'd like to apply them, then we can use `Object.entries` followed by `Object.fromEntries`:
|
||||
|
||||
1. Use `Object.entries(obj)` to get an array of key/value pairs from `obj`.
|
||||
2. Use array methods on that array, e.g. `map`, to transform these key/value pairs.
|
||||
2. Use array methods on that array, e.g. `map`.
|
||||
3. Use `Object.fromEntries(array)` on the resulting array to turn it back into an object.
|
||||
|
||||
For example, we have an object with prices, and would like to double them:
|
||||
|
@ -91,13 +91,12 @@ let prices = {
|
|||
|
||||
*!*
|
||||
let doublePrices = Object.fromEntries(
|
||||
// convert prices to array, map each key/value pair into another pair
|
||||
// and then fromEntries gives back the object
|
||||
Object.entries(prices).map(entry => [entry[0], entry[1] * 2])
|
||||
// convert to array, map, and then fromEntries gives back the object
|
||||
Object.entries(prices).map(([key, value]) => [key, value * 2])
|
||||
);
|
||||
*/!*
|
||||
|
||||
alert(doublePrices.meat); // 8
|
||||
```
|
||||
```
|
||||
|
||||
It may look difficult at first sight, but becomes easy to understand after you use it once or twice. We can make powerful chains of transforms this way.
|
||||
It may look difficult from the first sight, but becomes easy to understand after you use it once or twice. We can make powerful chains of transforms this way.
|
||||
|
|