grammar, usage, punctuation edits, Part 1, sections 3.1 - 4.6
This commit is contained in:
parent
fa4a678d53
commit
374db1ebd4
11 changed files with 102 additions and 102 deletions
|
@ -22,7 +22,7 @@ Here's what you should see if you are doing it for the first time:
|
|||
|
||||
The toggler button <span class="devtools" style="background-position:-168px -76px"></span> opens the tab with files.
|
||||
|
||||
Let's click it and select `index.html` and then `hello.js` in the tree view. That's what should show up:
|
||||
Let's click it and select `index.html` and then `hello.js` in the tree view. Here's what should show up:
|
||||
|
||||

|
||||
|
||||
|
@ -36,7 +36,7 @@ Now you could click the same toggler <span class="devtools" style="background-po
|
|||
|
||||
## Console
|
||||
|
||||
If we press `Esc`, then a console opens below, we can type commands there and press `key:Enter` to execute.
|
||||
If we press `Esc`, then a console opens below. We can type commands there and press `key:Enter` to execute.
|
||||
|
||||
After a statement is executed, its result is shown below.
|
||||
|
||||
|
@ -50,7 +50,7 @@ Let's examine what's going on within the code of the [example page](debugging/in
|
|||
|
||||
Congratulations! You've set a breakpoint. Please also click on the number for line `8`.
|
||||
|
||||
Should look like this (blue is where you should click):
|
||||
It should look like this (blue is where you should click):
|
||||
|
||||

|
||||
|
||||
|
@ -67,7 +67,7 @@ We can always find a list of breakpoints in the right pane. That's useful when w
|
|||
```smart header="Conditional breakpoints"
|
||||
*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 a certain function parameters.
|
||||
That's handy when we need to stop only for a certain variable value or for certain function parameters.
|
||||
```
|
||||
|
||||
## Debugger command
|
||||
|
@ -91,13 +91,13 @@ That's very convenient when we are in a code editor and don't want to switch to
|
|||
|
||||
## Pause and look around
|
||||
|
||||
In our example, `hello()` is called during the page load, so the easiest way to activate debugger -- is to reload the page. So let's press `key:F5` (Windows, Linux) or `key:Cmd+R` (Mac).
|
||||
In our example, `hello()` is called during the page load, so the easiest way to activate the debugger is to reload the page. So let's press `key:F5` (Windows, Linux) or `key:Cmd+R` (Mac).
|
||||
|
||||
As the breakpoint is set, the execution pauses at the 4th line:
|
||||
|
||||

|
||||
|
||||
Please open the informational dropdowns to the right (labelled with arrows). They allow you to examine the current code state:
|
||||
Please open the informational dropdowns to the right (labeled with arrows). They allow you to examine the current code state:
|
||||
|
||||
1. **`Watch` -- shows current values for any expressions.**
|
||||
|
||||
|
@ -129,9 +129,9 @@ There are buttons for it at the top of the right pane. Let's engage them.
|
|||
|
||||

|
||||
|
||||
The execution has resumed, reached another breakpoint inside `say()` and paused there. Take a look at the "Call stack" at the right. It has increased by one more call. We're inside `say()` now.
|
||||
The execution has resumed, reached another breakpoint inside `say()` and paused there. Take a look at the "Call stack" at the right. It has increased by one more call. We're inside `say()` now.
|
||||
|
||||
<span class="devtools" style="background-position:-137px -76px"></span> -- make a step (run the next command), but *not go into the function*, hotkey `key:F10`.
|
||||
<span class="devtools" style="background-position:-137px -76px"></span> -- make a step (run the next command), but *don't go into the function*, hotkey `key:F10`.
|
||||
: If we click it now, `alert` will be shown. The important thing is that `alert` can be any function, the execution "steps over it", skipping the function internals.
|
||||
|
||||
<span class="devtools" style="background-position:-72px -76px"></span> -- make a step, hotkey `key:F11`.
|
||||
|
@ -149,7 +149,7 @@ There are buttons for it at the top of the right pane. Let's engage them.
|
|||
```smart header="Continue to here"
|
||||
Right click on a line of code opens the context menu with a great option called "Continue to here".
|
||||
|
||||
That's handy when we want to move multiple steps forward, but too lazy to set a breakpoint.
|
||||
That's handy when we want to move multiple steps forward, but we're too lazy to set a breakpoint.
|
||||
```
|
||||
|
||||
## Logging
|
||||
|
@ -165,20 +165,20 @@ for (let i = 0; i < 5; i++) {
|
|||
}
|
||||
```
|
||||
|
||||
Regular users don't see that output, it is in the console. To see it -- either open the Console tab of developer tools or press `key:Esc` while in another tab: that opens the console at the bottom.
|
||||
Regular users don't see that output, it is in the console. To see it, either open the Console tab of developer tools or press `key:Esc` while in another tab: that opens the console at the bottom.
|
||||
|
||||
If we have enough logging in our code, then we can see what's going on from the records, without the debugger.
|
||||
|
||||
## Summary
|
||||
|
||||
As we can see, there are 3 main ways to pause a script:
|
||||
As we can see, there are three main ways to pause a script:
|
||||
1. A breakpoint.
|
||||
2. The `debugger` statements.
|
||||
3. An error (if dev tools are open and the button <span class="devtools" style="background-position:-264px -4px"></span> is "on")
|
||||
|
||||
Then we can examine variables and step on to see where the execution goes the wrong way.
|
||||
Then we can examine variables and step on to see where the execution goes wrong.
|
||||
|
||||
There's much more options in developer tools than covered here. The full manual is at <https://developers.google.com/web/tools/chrome-devtools>
|
||||
There are many more options in developer tools than covered here. The full manual is at <https://developers.google.com/web/tools/chrome-devtools>
|
||||
|
||||
The information from this chapter is enough to begin debugging, but later, especially if you do a lot of browser stuff, please go there and look through more advanced capabilities of developer tools.
|
||||
|
||||
|
|
|
@ -77,7 +77,7 @@ if (n < 0) {
|
|||

|
||||
|
||||
As a summary:
|
||||
- For a really short code one line is acceptable: like `if (cond) return null`.
|
||||
- For a really short code, one line is acceptable: like `if (cond) return null`.
|
||||
- But a separate line for each statement in brackets is usually better.
|
||||
|
||||
### Line length
|
||||
|
@ -94,7 +94,7 @@ There are two types of indents:
|
|||
|
||||
A horizontal indentation is made using either 2 or 4 spaces or the "Tab" symbol. Which one to choose is an old holy war. Spaces are more common nowadays.
|
||||
|
||||
One of advantages of spaces over tabs is that spaces allow more flexible configurations of indents than the "Tab" symbol.
|
||||
One advantage of spaces over tabs is that spaces allow more flexible configurations of indents than the "Tab" symbol.
|
||||
|
||||
For instance, we can align the arguments with the opening bracket, like this:
|
||||
|
||||
|
@ -125,11 +125,11 @@ There are two types of indents:
|
|||
}
|
||||
```
|
||||
|
||||
Insert an extra newline where it helps to make the code more readable. There should not be more than 9 lines of code without a vertical indentation.
|
||||
Insert an extra newline where it helps to make the code more readable. There should not be more than nine lines of code without a vertical indentation.
|
||||
|
||||
### A semicolon
|
||||
|
||||
A semicolon should be present after each statement. Even if it could be possibly be skipped.
|
||||
A semicolon should be present after each statement. Even if it could possibly be skipped.
|
||||
|
||||
There are languages where a semicolon is truly optional. It's rarely used there. But in JavaScript there are few cases when a line break is sometimes not interpreted as a semicolon. That leaves a place for programming errors.
|
||||
|
||||
|
@ -160,7 +160,7 @@ for (let i = 0; i < 10; i++) {
|
|||
}
|
||||
```
|
||||
|
||||
The similar thing can be done with `if/else` and `return`.
|
||||
A similar thing can be done with `if/else` and `return`.
|
||||
|
||||
For example, two constructs below are identical.
|
||||
|
||||
|
@ -258,11 +258,11 @@ That's because when reading a code, we first want to know "what it does". If the
|
|||
|
||||
## Style guides
|
||||
|
||||
A style guide contains general rules about "how to write": which quotes to use, how many spaces to indent, where to put line breaks etc. A lot of minor things.
|
||||
A style guide contains general rules about "how to write": which quotes to use, how many spaces to indent, where to put line breaks, etc. A lot of minor things.
|
||||
|
||||
In total, when all members of a team use the same style guide, the code looks uniform. No matter who of the team wrote it, still the same style.
|
||||
In total, when all members of a team use the same style guide, the code looks uniform. No matter who of the team wrote it, it's still the same style.
|
||||
|
||||
Surely, a team may think out a style guide themselves. But as of now, there's no need to. There are many tried, worked out style guides, easy to adopt.
|
||||
Surely, a team may think out a style guide themselves. But as of now, there's no need to. There are many tried, worked-out style guides, which are easy to adopt.
|
||||
|
||||
For instance:
|
||||
|
||||
|
@ -321,7 +321,7 @@ Then install/enable the plugin for your editor that integrates with ESLint. The
|
|||
|
||||
It is 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.
|
||||
|
||||
Using a linter has the great side-effect. Linters catch typos. For instance, when an undefined variable is accessed, a linter detects it and (if integrated with an editor) highlights. In most cases that's a mistype. So we can fix it right ahead.
|
||||
Using a linter has a great side-effect: linters catch typos. For instance, when an undefined variable is accessed, a linter detects it and (if integrated with an editor) highlights it. In most cases that's a mistype. So we can fix it right ahead.
|
||||
|
||||
For that reason even if you're not concerned about styles, using a linter is highly recommended.
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ Comments are generally a good thing. But novices in programming generally get th
|
|||
|
||||
But the amount of such "explanatory" comments should be minimal.
|
||||
|
||||
Seriously, a good code should be easy to understand without them.
|
||||
Seriously, good code should be easy to understand without them.
|
||||
|
||||
There's a great rule about that: "if the code is so unclear that it requires a comment, then maybe it should be rewritten instead".
|
||||
|
||||
|
@ -122,14 +122,14 @@ Document a function usage
|
|||
}
|
||||
```
|
||||
|
||||
Such comments allow to understand the purpose of the function and use it the right way without looking in its code.
|
||||
Such comments allow us to understand the purpose of the function and use it the right way without looking in its code.
|
||||
|
||||
By the way, many editors like [WebStorm](https://www.jetbrains.com/webstorm/) can understand them as well and use them to provide autocomplete and some automatic code-checking.
|
||||
|
||||
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 the task is solved this way?
|
||||
: What's written is important. But what's *not* written maybe even more important to understand what's going on. Why the task is solved exactly this way? The code gives no answer.
|
||||
Why is the task solved this way?
|
||||
: What's written is important. But what's *not* written maybe even more important to understand what's going on. Why is the task solved exactly this way? The code gives no answer.
|
||||
|
||||
If there are many ways to solve the task, why this one? Especially when it's not the most obvious one.
|
||||
|
||||
|
@ -145,6 +145,6 @@ Any subtle features of the code? Where they are used?
|
|||
|
||||
## Summary
|
||||
|
||||
One of signs of a good developer is his comments. Good comments allow to maintain the code well, return to it after a long delay and use features more effectively.
|
||||
One sign of a good developer is his comments. Good comments allow us to maintain the code well, return to it after a long delay and use features more effectively.
|
||||
|
||||
Comments are also used for auto-documenting tools: they read them and generate HTML-docs (or in another format).
|
||||
|
|
|
@ -38,13 +38,13 @@ Another way to code faster (and much worse!) is to use single-letter variable na
|
|||
|
||||
A short variable disappears in the code like a real ninja in the forest. No one will be able to find it using "search" of the editor. And even if someone does, he won't be able to "decipher" what the name `a` or `b` means.
|
||||
|
||||
...But there's an exception. A real ninja will never use `i` as the counter in a `"for"` loop. Anywhere, but not here. Look around, there are so much more exotic letters. For instance, `x` or `y`.
|
||||
...But there's an exception. A real ninja will never use `i` as the counter in a `"for"` loop. Anywhere, but not here. Look around, there are many more exotic letters. For instance, `x` or `y`.
|
||||
|
||||
An exotic variable as a loop counter is especially cool if the loop body takes 1-2 pages (make it longer if you can). Then if someone looks deep inside the loop, he won't be able to figure out fast that the variable named `x` is the loop counter.
|
||||
An exotic variable as a loop counter is especially cool if the loop body takes 1-2 pages (make it longer if you can). Then if someone looks deep inside the loop, he won't be able to quickly figure out that the variable named `x` is the loop counter.
|
||||
|
||||
## Use abbreviations
|
||||
|
||||
If the team rules forbid to use one-letter and vague names -- shorten them, make abbreviations.
|
||||
If the team rules forbid the use of one-letter and vague names -- shorten them, make abbreviations.
|
||||
|
||||
Like this:
|
||||
|
||||
|
@ -53,7 +53,7 @@ Like this:
|
|||
- `browser` -> `brsr`.
|
||||
- ...etc
|
||||
|
||||
Only the one with a truly good intuition will be able to understand such names. Try to shorten everything. Only a worthy person should be able to uphold the development of your code.
|
||||
Only the one with truly good intuition will be able to understand such names. Try to shorten everything. Only a worthy person should be able to uphold the development of your code.
|
||||
|
||||
## Soar high. Be abstract.
|
||||
|
||||
|
@ -66,7 +66,7 @@ The great image has no form.
|
|||
|
||||
While choosing a name try to use the most abstract word. Like `obj`, `data`, `value`, `item`, `elem` and so on.
|
||||
|
||||
- **The ideal name for a variable is `data`.** Use it everywhere where you can. Indeed, every variable holds *data*, right?
|
||||
- **The ideal name for a variable is `data`.** Use it everywhere you can. Indeed, every variable holds *data*, right?
|
||||
|
||||
...But what to do if `data` is already taken? Try `value`, it's also universal. After all, a variable eventually gets a *value*.
|
||||
|
||||
|
@ -74,9 +74,9 @@ While choosing a name try to use the most abstract word. Like `obj`, `data`, `va
|
|||
|
||||
Give them a try. A young ninja may wonder -- do such names make the code worse? Actually, yes!
|
||||
|
||||
From one hand, the variable name still means something. It says what's inside the variable: a string, a number or something else. But when an outsider tries to understand the code -- he'll be surprised to see that there's actually no information at all!
|
||||
From one hand, the variable name still means something. It says what's inside the variable: a string, a number or something else. But when an outsider tries to understand the code, he'll be surprised to see that there's actually no information at all!
|
||||
|
||||
Indeed, the value type is easy to find out by debugging. But what's the meaning of the variable? Which string/number it stores? There's just no way to figure out without a good meditation!
|
||||
Indeed, the value type is easy to find out by debugging. But what's the meaning of the variable? Which string/number does it store? There's just no way to figure out without a good meditation!
|
||||
|
||||
- **...But what if there are no more such names?** Just add a letter: `item1, item2, elem5, data1`...
|
||||
|
||||
|
@ -182,9 +182,9 @@ function render() {
|
|||
}
|
||||
```
|
||||
|
||||
A programmer who jumps inside the `render` will probably miss to notice that there's a local `user` shadowing the outer one.
|
||||
A programmer who jumps inside the `render` will probably fail to notice that there's a local `user` shadowing the outer one.
|
||||
|
||||
Then he'll try to work with `user` it assuming that it's the external variable, the result of `authenticateUser()`... The trap is sprung! Hello, debugger...
|
||||
Then he'll try to work with `user` assuming that it's the external variable, the result of `authenticateUser()`... The trap is sprung! Hello, debugger...
|
||||
|
||||
|
||||
## Side-effects everywhere!
|
||||
|
@ -223,6 +223,6 @@ Imagine, another developer wants only to check the email, and not output any mes
|
|||
|
||||
All "pieces of advice" above are from the real code... Sometimes, written by experienced developers. Maybe even more experienced than you are ;)
|
||||
|
||||
- Follow some of them -- and your code will become full of surprises.
|
||||
- Follow many of them -- and your code will become truly yours, no one would want to change it.
|
||||
- Follow all -- and your code will become a valuable lesson for young developers looking for enlightment.
|
||||
- Follow some of them, and your code will become full of surprises.
|
||||
- Follow many of them, and your code will become truly yours, no one would want to change it.
|
||||
- Follow all, and your code will become a valuable lesson for young developers looking for enlightenment.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Automated testing with mocha
|
||||
|
||||
Automated testing will be used further in tasks.
|
||||
Automated testing will be used in further tasks.
|
||||
|
||||
It's actually a part of the "educational minimum" of a developer.
|
||||
|
||||
|
@ -8,19 +8,19 @@ It's actually a part of the "educational minimum" of a developer.
|
|||
|
||||
## Why we need tests?
|
||||
|
||||
When we write a function, we usually can imagine what it should do: which parameters give which results.
|
||||
When we write a function, we can usually imagine what it should do: which parameters give which results.
|
||||
|
||||
During the development, we can check the function by running it and comparing the outcome with the expected one. For instance, we can do it in the console.
|
||||
During development, we can check the function by running it and comparing the outcome with the expected one. For instance, we can do it in the console.
|
||||
|
||||
If something's wrong -- then we fix the code, run again, check the result -- and so on till it works.
|
||||
If something is wrong -- then we fix the code, run again, check the result -- and so on till it works.
|
||||
|
||||
But such manual "re-runs" are imperfect.
|
||||
|
||||
**When testing a code by manual re-runs -- it's easy to miss something.**
|
||||
**When testing a code by manual re-runs, it's easy to miss something.**
|
||||
|
||||
For instance, we're creating a function `f`. Wrote some code, testing: `f(1)` works, but `f(2)` doesn't work. We fix the code and now `f(2)` works. Looks complete? But we forgot to re-test `f(1)`. That may lead to an error.
|
||||
|
||||
That's very typical. When we develop something, we keep a lot of possible use cases and mind. But it's hard to expect from programmer to check all of them manually after every change. So it becomes easy to fix one thing and break another one.
|
||||
That's very typical. When we develop something, we keep a lot of possible use cases in mind. But it's hard to expect a programmer to check all of them manually after every change. So it becomes easy to fix one thing and break another one.
|
||||
|
||||
**Automated testing means that tests are written separately, in addition to the code. They can be executed easily and check all the main use cases.**
|
||||
|
||||
|
@ -36,7 +36,7 @@ Enough words. Let's see the example.
|
|||
|
||||
Let's say we want to make a function `pow(x, n)` that raises `x` to an integer power `n`. We assume that `n≥0`.
|
||||
|
||||
That task is just an example: there's `**` operator in JavaScript that can do that, but here we concentrate on the development flow that can be applied to more complex tasks as well.
|
||||
That task is just an example: there's the `**` operator in JavaScript that can do that, but here we concentrate on the development flow that can be applied to more complex tasks as well.
|
||||
|
||||
Before creating the code of `pow`, we can imagine what the function should do and describe it.
|
||||
|
||||
|
@ -73,7 +73,7 @@ The flow of development usually looks like this:
|
|||
|
||||
1. An initial spec is written, with tests for the most basic functionality.
|
||||
2. An initial implementation is created.
|
||||
3. To check whether it works, we run the testing framework [Mocha](http://mochajs.org/) (more details soon) that runs the spec. Errors are displayed. We make corrections till everything works.
|
||||
3. To check whether it works, we run the testing framework [Mocha](http://mochajs.org/) (more details soon) that runs the spec. Errors are displayed. We make corrections until everything works.
|
||||
4. Now we have a working initial implementation with tests.
|
||||
5. We add more use cases to the spec, probably not yet supported by the implementations. Tests start to fail.
|
||||
6. Go to 3, update the implementation till tests give no errors.
|
||||
|
@ -112,7 +112,7 @@ The result:
|
|||
|
||||
As of now, the test fails, there's an error. That's logical: we have an empty function code in `pow`, so `pow(2,3)` returns `undefined` instead of `8`.
|
||||
|
||||
For future, let's note that for there are advanced test-runners, like [karma](https://karma-runner.github.io/) and others. So it's generally not a problem to setup many different tests.
|
||||
For the future, let's note that there are advanced test-runners, like [karma](https://karma-runner.github.io/) and others. So it's generally not a problem to setup many different tests.
|
||||
|
||||
## Initial implementation
|
||||
|
||||
|
@ -130,7 +130,7 @@ Wow, now it works!
|
|||
|
||||
## Improving the spec
|
||||
|
||||
What we've done -- is definitely a cheat. The function does not work: an attempt to calculate `pow(3,4)` would give an incorrect result, but tests pass.
|
||||
What we've done is definitely a cheat. The function does not work: an attempt to calculate `pow(3,4)` would give an incorrect result, but tests pass.
|
||||
|
||||
...But the situation is quite typical, it happens in practice. Tests pass, but the function works wrong. Our spec is imperfect. We need to add more use cases to it.
|
||||
|
||||
|
@ -172,11 +172,11 @@ The principal difference is that when `assert` triggers an error, the `it` block
|
|||
|
||||
Making tests separate is useful to get more information about what's going on, so the second variant is better.
|
||||
|
||||
And besides that, there's one more rule good to follow.
|
||||
And besides that, there's one more rule that's good to follow.
|
||||
|
||||
**One test checks one thing.**
|
||||
|
||||
If we look at the test and see two independent checks in it -- better to split it into two simpler ones.
|
||||
If we look at the test and see two independent checks in it, it's better to split it into two simpler ones.
|
||||
|
||||
So let's continue with the second variant.
|
||||
|
||||
|
@ -388,7 +388,7 @@ The spec can be used in three ways:
|
|||
|
||||
With the spec, we can safely improve, change, even rewrite the function from scratch and make sure it still works right.
|
||||
|
||||
That's especially important in large projects when a function is used in many places. When we change such a function -- there's just no way to manually check if every place that uses them still works right.
|
||||
That's especially important in large projects when a function is used in many places. When we change such a function, there's just no way to manually check if every place that uses it still works right.
|
||||
|
||||
Without tests, people have two ways:
|
||||
|
||||
|
@ -397,7 +397,7 @@ Without tests, people have two ways:
|
|||
|
||||
**Automatically tested code is contrary to that!**
|
||||
|
||||
If the project is covered with tests -- there's just no such problem. We can run tests and see a lot of checks made in a matter of seconds.
|
||||
If the project is covered with tests, there's just no such problem. We can run tests and see a lot of checks made in a matter of seconds.
|
||||
|
||||
**Besides, a well-tested code has better architecture.**
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
# Objects
|
||||
|
||||
As we know from the chapter <info:types>, there are 7 language types in JavaScript. Six of them are called "primitive", because their values contain only a single thing (be it a string or a number or whatever).
|
||||
As we know from the chapter <info:types>, there are seven language types in JavaScript. Six of them are called "primitive", because their values contain only a single thing (be it a string or a number or whatever).
|
||||
|
||||
In contrast, objects are used to store keyed collections of various data and more complex entities. In JavaScript, objects penetrate almost every aspect of the language. So we must understand them first before going in-depth anywhere else.
|
||||
|
||||
|
@ -42,7 +42,7 @@ In the `user` object, there are two properties:
|
|||
1. The first property has the name `"name"` and the value `"John"`.
|
||||
2. The second one has the name `"age"` and the value `30`.
|
||||
|
||||
The resulting `user` object can be imagined as a cabinet with two signed files labelled "name" and "age".
|
||||
The resulting `user` object can be imagined as a cabinet with two signed files labeled "name" and "age".
|
||||
|
||||

|
||||
|
||||
|
@ -125,7 +125,7 @@ delete user["likes birds"];
|
|||
|
||||
Now everything is fine. Please note that the string inside the brackets is properly quoted (any type of quotes will do).
|
||||
|
||||
Square brackets also provide a way to obtain the property name as the result of any expression - as opposed to a literal string - like from a variable as follows:
|
||||
Square brackets also provide a way to obtain the property name as the result of any expression -- as opposed to a literal string -- like from a variable as follows:
|
||||
|
||||
```js
|
||||
let key = "likes birds";
|
||||
|
@ -246,7 +246,7 @@ let user = makeUser("John", 30);
|
|||
alert(user.name); // John
|
||||
```
|
||||
|
||||
In the example above, properties have same names as variables. The use-case of making a property from a variable is so common, that there's a special *property value shorthand* to make it shorter.
|
||||
In the example above, properties have the same names as variables. The use-case of making a property from a variable is so common, that there's a special *property value shorthand* to make it shorter.
|
||||
|
||||
Instead of `name:name` we can just write `name`, like this:
|
||||
|
||||
|
@ -280,7 +280,7 @@ let user = {};
|
|||
alert( user.noSuchProperty === undefined ); // true means "no such property"
|
||||
```
|
||||
|
||||
There also exists a special operator `"in"` to check for the existance of a property.
|
||||
There also exists a special operator `"in"` to check for the existence of a property.
|
||||
|
||||
The syntax is:
|
||||
```js
|
||||
|
@ -358,7 +358,7 @@ for(let key in user) {
|
|||
}
|
||||
```
|
||||
|
||||
Note that all "for" constructs allow to declare the looping variable inside the loop, like `let key` here.
|
||||
Note that all "for" constructs allow us to declare the looping variable inside the loop, like `let key` here.
|
||||
|
||||
Also, we could use another variable name here instead of `key`. For instance, `"for(let prop in obj)"` is also widely used.
|
||||
|
||||
|
@ -394,10 +394,10 @@ But if we run the code, we see a totally different picture:
|
|||
- USA (1) goes first
|
||||
- then Switzerland (41) and so on.
|
||||
|
||||
The phone codes go in the ascending sorted order, because they are integer. So we see `1, 41, 44, 49`.
|
||||
The phone codes go in the ascending sorted order, because they are integers. So we see `1, 41, 44, 49`.
|
||||
|
||||
````smart header="Integer properties? What's that?"
|
||||
The "integer property" term here means a string that can be converted to-and-from integer without a change.
|
||||
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:
|
||||
|
||||
|
@ -448,7 +448,7 @@ Now it works as intended.
|
|||
|
||||
## Copying by reference
|
||||
|
||||
One of fundamental differences of objects vs primitives is that they are stored and copied "by reference".
|
||||
One of the fundamental differences of objects vs primitives is that they are stored and copied "by reference".
|
||||
|
||||
Primitive values: strings, numbers, booleans -- are assigned/copied "as a whole value".
|
||||
|
||||
|
@ -509,7 +509,7 @@ admin.name = 'Pete'; // changed by the "admin" reference
|
|||
alert(*!*user.name*/!*); // 'Pete', changes are seen from the "user" reference
|
||||
```
|
||||
|
||||
The example above demonstrates that there is only one object. Like if we had a cabinet with two keys and used one of them (`admin`) to get into it. Then, if we later use the other key (`user`) we see changes.
|
||||
The example above demonstrates that there is only one object. Like if we had a cabinet with two keys and used one of them (`admin`) to get into it. Then, if we later use the other key (`user`) we would see changes.
|
||||
|
||||
### Comparison by reference
|
||||
|
||||
|
@ -579,9 +579,9 @@ user = {
|
|||
|
||||
So, copying an object variable creates one more reference to the same object.
|
||||
|
||||
But what if we need to duplicate an object? Create an independant copy, a clone?
|
||||
But what if we need to duplicate an object? Create an independent copy, a clone?
|
||||
|
||||
That's also doable, but a little bit more difficult, because there's no built-in method for that in JavaScript. Actually, that's rarely needed, copying by reference is good most of the time.
|
||||
That's also doable, but a little bit more difficult, because there's no built-in method for that in JavaScript. Actually, that's rarely needed. Copying by reference is good most of the time.
|
||||
|
||||
But if we really want that, then we need to create a new object and replicate the structure of the existing one by iterating over its properties and copying them on the primitive level.
|
||||
|
||||
|
@ -660,7 +660,7 @@ let clone = Object.assign({}, user);
|
|||
|
||||
It copies all properties of `user` into the empty object and returns it. Actually, the same as the loop, but shorter.
|
||||
|
||||
Till now we assumed that all properties of `user` are primitive. But properties can be references to other objects. What to do with them?
|
||||
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
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Memory management in JavaScript is performed automatically and invisibly to us. We create primitives, objects, functions... All that takes memory.
|
||||
|
||||
What happens when something is not needed any more? How JavaScript engine discovers that and cleans up?
|
||||
What happens when something is not needed any more? How does the JavaScript engine discover it and clean it up?
|
||||
|
||||
[cut]
|
||||
|
||||
|
@ -10,7 +10,7 @@ What happens when something is not needed any more? How JavaScript engine discov
|
|||
|
||||
The main concept of memory management in JavaScript is *reachability*.
|
||||
|
||||
Simply put, "reachable" values are those that are accessible or useable somehow. They are guaranteed to be stored in memory.
|
||||
Simply put, "reachable" values are those that are accessible or usable somehow. They are guaranteed to be stored in memory.
|
||||
|
||||
1. There's a base set of inherently reachable values, that cannot be deleted for obvious reasons.
|
||||
|
||||
|
@ -25,9 +25,9 @@ Simply put, "reachable" values are those that are accessible or useable somehow.
|
|||
|
||||
2. Any other value is considered reachable if it's reachable from a root by a reference or by a chain of references.
|
||||
|
||||
For instance, if there's an object in a local variable, and that object has a property referencing another object, that object is considered reachable. And those that it references -- are also reachable. Detailed examples to follow.
|
||||
For instance, if there's an object in a local variable, and that object has a property referencing another object, that object is considered reachable. And those that it references are also reachable. Detailed examples to follow.
|
||||
|
||||
There's a background process in the JavaScript engine that is called [garbage collector](https://en.wikipedia.org/wiki/Garbage_collection_(computer_science)). It monitors all objects and removes those that became unreachable.
|
||||
There's a background process in the JavaScript engine that is called [garbage collector](https://en.wikipedia.org/wiki/Garbage_collection_(computer_science)). It monitors all objects and removes those that have become unreachable.
|
||||
|
||||
## A simple example
|
||||
|
||||
|
@ -153,11 +153,11 @@ The former `"family"` object has been unlinked from the root, there's no referen
|
|||
|
||||
The basic garbage collection algorithm is called "mark-and-sweep".
|
||||
|
||||
Regularly the following "garbage collection" steps are performed:
|
||||
The following "garbage collection" steps are regularly performed:
|
||||
|
||||
- The garbage collector takes roots and "marks" (remembers) them.
|
||||
- Then it visits and "marks" all references from them.
|
||||
- Then it visits marked objects and marks *their* references. All visited objects are remembered, not to visit the same object twice in the future.
|
||||
- Then it visits marked objects and marks *their* references. All visited objects are remembered, so as not to visit the same object twice in the future.
|
||||
- ...And so on until there are unvisited references (reachable from the roots).
|
||||
- All objects except marked ones are removed.
|
||||
|
||||
|
@ -183,7 +183,7 @@ Now the objects that could not be visited in the process are considered unreacha
|
|||
|
||||

|
||||
|
||||
That's the concept how garbage collection works.
|
||||
That's the concept of how garbage collection works.
|
||||
|
||||
JavaScript engines apply many optimizations to make it run faster and not affect the execution.
|
||||
|
||||
|
@ -193,7 +193,7 @@ Some of the optimizations:
|
|||
- **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 are 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 going 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.
|
||||
There are 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 going 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.
|
||||
|
||||
## Summary
|
||||
|
||||
|
@ -205,7 +205,7 @@ The main things to know:
|
|||
|
||||
Modern engines implement advanced algorithms of garbage collection.
|
||||
|
||||
A general book "The Garbage Collection Handbook: The Art of Automatic Memory Management" (R. Jones at al) covers some of them.
|
||||
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, 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).
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
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.
|
||||
|
||||
Till now we only saw strings. Now let's see the advantages that symbols can give us.
|
||||
Till now we've only seen strings. Now let's see the advantages that symbols can give us.
|
||||
|
||||
[cut]
|
||||
|
||||
|
@ -60,7 +60,7 @@ That's a "language guard" against messing up, because strings and symbols are fu
|
|||
|
||||
## "Hidden" properties
|
||||
|
||||
Symbols allow to create "hidden" properties of an object, that no other part of code can occasionally access or overwrite.
|
||||
Symbols allow us to create "hidden" properties of an object, that no other part of code can occasionally access or overwrite.
|
||||
|
||||
For instance, if we want to store an "identifier" for the object `user`, we can create a symbol with the name `id` for it:
|
||||
|
||||
|
@ -72,11 +72,11 @@ user[id] = "ID Value";
|
|||
alert( user[id] ); // we can access the data using the symbol as the key
|
||||
```
|
||||
|
||||
Now let's imagine that another script wants to have his own "id" property inside `user`, for his own purposes. That may be another JavaScript library, so the scripts are completely unaware for each other.
|
||||
Now let's imagine that another script wants to have its own "id" property inside `user`, for its own purposes. That may be another JavaScript library, so the scripts are completely unaware of each other.
|
||||
|
||||
No problem. It can create its own `Symbol("id")`.
|
||||
|
||||
Their script:
|
||||
The script:
|
||||
|
||||
```js
|
||||
// ...
|
||||
|
@ -176,7 +176,7 @@ alert( obj[0] ); // test (same property)
|
|||
|
||||
## Global symbols
|
||||
|
||||
As we've seen, usually all symbols are different, even if they have the same name. But sometimes we want same-named symbols to be same entities.
|
||||
As we've seen, usually all symbols are different, even if they have the same name. But sometimes we want same-named symbols to be the same entities.
|
||||
|
||||
For instance, different parts of our application want to access symbol `"id"` meaning exactly the same property.
|
||||
|
||||
|
@ -246,7 +246,7 @@ They are listed in the specification in the [Well-known symbols](https://tc39.gi
|
|||
- `Symbol.toPrimitive`
|
||||
- ...and so on.
|
||||
|
||||
For instance, `Symbol.toPrimitive` allows to describe object to primitive conversion. We'll see its use very soon.
|
||||
For instance, `Symbol.toPrimitive` allows us to describe object to primitive conversion. We'll see its use very soon.
|
||||
|
||||
Other symbols will also become familiar when we study the corresponding language features.
|
||||
|
||||
|
@ -265,6 +265,6 @@ Symbols have two main use cases:
|
|||
|
||||
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 and 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.
|
||||
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 build-in method [Object.getOwnPropertySymbols(obj)](mdn:js/Object/getOwnPropertySymbols) that allows 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 methods and syntax constructs adhere to a common agreement that they are. And the one who explicitly calls the aforementioned methods probably understands well what he's doing.
|
||||
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 methods and syntax constructs adhere to a common agreement that they are. And the one who explicitly calls the aforementioned methods probably understands well what he's doing.
|
||||
|
|
|
@ -36,7 +36,7 @@ user.sayHi(); // Hello!
|
|||
|
||||
Here we've just used a Function Expression to create the function and assign it to the property `user.sayHi` of the object.
|
||||
|
||||
Then we can call it. The user now can speak!
|
||||
Then we can call it. The user can now speak!
|
||||
|
||||
A function that is the property of an object is called its *method*.
|
||||
|
||||
|
@ -92,7 +92,7 @@ let user = {
|
|||
|
||||
As demonstrated, we can omit `"function"` and just write `sayHi()`.
|
||||
|
||||
To say 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
|
||||
|
||||
|
@ -271,7 +271,7 @@ This doesn't (evaluated method):
|
|||
(user.name == "John" ? user.hi : user.bye)(); // Error!
|
||||
```
|
||||
|
||||
Why? If we want to understand why it happens -- let's get under the hood of how `obj.method()` call works.
|
||||
Why? If we want to understand why it happens, let's get under the hood of how `obj.method()` call works.
|
||||
|
||||
Looking closely, we may notice two operations in `obj.method()` statement:
|
||||
|
||||
|
@ -352,4 +352,4 @@ The value of `this` is defined at run-time.
|
|||
- That function can be copied between objects.
|
||||
- When a function is called in the "method" syntax: `object.method()`, the value of `this` during the call is `object`.
|
||||
|
||||
Please note that arrow functions are special: they have no `this`. When `this` is accessed inside an arrow function -- it is taken from outside.
|
||||
Please note that arrow functions are special: they have no `this`. When `this` is accessed inside an arrow function, it is taken from outside.
|
||||
|
|
|
@ -19,11 +19,11 @@ As for the string conversion -- it usually happens when we output an object like
|
|||
|
||||
When an object is used in the context where a primitive is required, for instance, in an `alert` or mathematical operations, it's converted to a primitive value using the `ToPrimitive` algorithm ([specification](https://tc39.github.io/ecma262/#sec-toprimitive)).
|
||||
|
||||
That algorithm allows to customize the conversion using in a special object method.
|
||||
That algorithm allows us to customize the conversion using a special object method.
|
||||
|
||||
Depending on the context, the conversion has a so-called "hint".
|
||||
|
||||
There are 3 variants:
|
||||
There are three variants:
|
||||
|
||||
`"string"`
|
||||
: When an operation expects a string, for object-to-string conversions, like `alert`:
|
||||
|
@ -68,7 +68,7 @@ There are 3 variants:
|
|||
|
||||
In practice, all built-in objects except for one case (`Date` object, we'll learn it later) implement `"default"` conversion the same way as `"number"`. And probably we should do the same.
|
||||
|
||||
Please note -- there are only three hints. That simple. 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.
|
||||
Please note -- there are only three hints. It's that simple. 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:**
|
||||
|
||||
|
@ -159,12 +159,12 @@ alert(user); // toString -> John
|
|||
alert(user + 500); // toString -> John500
|
||||
```
|
||||
|
||||
In the absense of `Symbol.toPrimitive` and `valueOf`, `toString` will handle all primitive conversions.
|
||||
In the absence of `Symbol.toPrimitive` and `valueOf`, `toString` will handle all primitive conversions.
|
||||
|
||||
|
||||
## ToPrimitive and ToString/ToNumber
|
||||
|
||||
The important thing to know about all primitive-conversion methods is that they not necessarily return the "hinted" primitive.
|
||||
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 a hint "number".
|
||||
|
||||
|
@ -178,7 +178,7 @@ For instance:
|
|||
|
||||
```js run
|
||||
let obj = {
|
||||
toString() { // toString handles all conversions in the absense of other methods
|
||||
toString() { // toString handles all conversions in the absence of other methods
|
||||
return "2";
|
||||
}
|
||||
};
|
||||
|
@ -186,7 +186,7 @@ For instance:
|
|||
alert(obj * 2); // 4, ToPrimitive gives "2", then it becomes 2
|
||||
```
|
||||
|
||||
- Binary plus performs checks the primitive -- if it's a string, then it does concatenation, otherwise performs `ToNumber` and works with numbers.
|
||||
- Binary plus checks the primitive -- if it's a string, then it does concatenation, otherwise it performs `ToNumber` and works with numbers.
|
||||
|
||||
String example:
|
||||
```js run
|
||||
|
@ -235,4 +235,4 @@ The conversion algorithm is:
|
|||
3. Otherwise if hint is `"number"` or `"default"`
|
||||
- try `obj.valueOf()` and `obj.toString()`, whatever exists.
|
||||
|
||||
In practice, it's often enough to implement only `obj.toString()` as a "catch-all" method for all conversions that returns 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.
|
||||
|
|
|
@ -103,7 +103,7 @@ User(); // undefined
|
|||
new User(); // function User { ... }
|
||||
```
|
||||
|
||||
That can be used to allow both `new` and regular syntax work the same:
|
||||
That can be used to allow both `new` and regular syntax to work the same:
|
||||
|
||||
```js run
|
||||
function User(name) {
|
||||
|
@ -118,7 +118,7 @@ let john = User("John"); // redirects call to new User
|
|||
alert(john.name); // John
|
||||
```
|
||||
|
||||
This approach is sometimes used in libraries to make the syntax more flexible. Probably not a good thing to use everywhere though, because omitting `new` makes a bit less obvious what's going on. With `new` we all know that the new object is being created, that's a good thing.
|
||||
This approach is sometimes used in libraries to make the syntax more flexible. Probably not a good thing to use everywhere though, because omitting `new` makes it a bit less obvious what's going on. With `new` we all know that the new object is being created, that's a good thing.
|
||||
|
||||
## Return from constructors
|
||||
|
||||
|
@ -176,7 +176,7 @@ Omitting parentheses here is not considered a "good style", but the syntax is pe
|
|||
|
||||
## Methods in constructor
|
||||
|
||||
Using constuctor functions to create objects gives a great deal of flexibility. The constructor function may have parameters that define how to construct the object, what to put in it.
|
||||
Using constructor functions to create objects gives a great deal of flexibility. The constructor function may have parameters that define how to construct the object, and what to put in it.
|
||||
|
||||
Of course, we can add to `this` not only properties, but methods as well.
|
||||
|
||||
|
@ -207,8 +207,8 @@ john = {
|
|||
|
||||
## Summary
|
||||
|
||||
- Constructor functions or, shortly, constructors, are regular functions, but there's a common agreement to name them with capital letter first.
|
||||
- Constructor functions should only be called using `new`. Such call implies a creation of empty `this` at the start and returning the populated one at the end.
|
||||
- Constructor functions or, briefly, constructors, are regular functions, but there's a common agreement to name them with capital letter first.
|
||||
- Constructor functions should only be called using `new`. Such a call implies a creation of empty `this` at the start and returning the populated one at the end.
|
||||
|
||||
We can use constructor functions to make multiple similar objects.
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue