move objects (before?)
|
@ -8,17 +8,8 @@ So we'll start with attaching a script to the webpage. For other environments li
|
||||||
|
|
||||||
[cut]
|
[cut]
|
||||||
|
|
||||||
[todo remove defer/async from here and move to 2nd part?]
|
|
||||||
|
|
||||||
## The "script" tag
|
## The "script" tag
|
||||||
|
|
||||||
[todo need this? and special (need it too?)]
|
|
||||||
```smart header="What if I want to move faster?"
|
|
||||||
In the case if you've developed with JavaScript already or have a lot of experience in another language, you can skip detailed explanatins and jump to <info:javascript-specials>. There you can find an essense of important features.
|
|
||||||
|
|
||||||
If you have enough time and want to learn things in details then read on :)
|
|
||||||
```
|
|
||||||
|
|
||||||
JavaScript programs can be inserted in any place of HTML with the help of the `<script>` tag.
|
JavaScript programs can be inserted in any place of HTML with the help of the `<script>` tag.
|
||||||
|
|
||||||
For instance:
|
For instance:
|
||||||
|
@ -50,17 +41,6 @@ You can run the example clicking on a "Play" button in it's right-top corner.
|
||||||
|
|
||||||
The `<script>` tag contains JavaScript code which is automatically executed when the browser meets the tag.
|
The `<script>` tag contains JavaScript code which is automatically executed when the browser meets the tag.
|
||||||
|
|
||||||
Please note the execution sequence:
|
|
||||||
|
|
||||||
1. Browser starts to parse the document and display the page.
|
|
||||||
2. When the browser meets `<script>`, it switches to the JavaScript execution mode. In this mode it executes the script.
|
|
||||||
3. The `alert` command shows a message and pauses the execution.
|
|
||||||
4. Note that at this moment a part of the page before the script is shown already.
|
|
||||||
5. When the script is finished, it gets back to the HTML-mode, and *only then* it shows the rest of the document.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
A visitor won't see the content after the script until it is executed. In other words, a `<script>` tag blocks rendering.
|
|
||||||
|
|
||||||
## The modern markup
|
## The modern markup
|
||||||
|
|
||||||
|
@ -82,11 +62,76 @@ Comments before and after scripts.
|
||||||
//--></script>
|
//--></script>
|
||||||
```
|
```
|
||||||
|
|
||||||
These comments were supposed to hide the code from an old browser that did't understand a `<script>` tag. But for all browsers born in the past 15+ years that's not an issue. I only mention it here, because it serves as a pointer. If you see that in a code somewhere -- it's probably really old and probably not worth looking into.
|
These comments were supposed to hide the code from an old browser that did't know about a `<script>` tag. But all browsers born in the past 15+ years don't have any issues. It is only mentioned here, because it serves as a sign. If you see that somewhere -- that code is probably really old and not worth looking into.
|
||||||
|
|
||||||
|
|
||||||
|
## External scripts
|
||||||
|
|
||||||
|
If we have a lot of JavaScript code, we can it put it into a separate file.
|
||||||
|
|
||||||
|
The script file is attached to HTML like this:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script src="/path/to/script.js"></script>
|
||||||
|
```
|
||||||
|
|
||||||
|
Here `/path/to/script.js` is an absolute path to the file with the script (from the site root).
|
||||||
|
|
||||||
|
It is also possible to provide a path relative to the current page. For instance, `src="script.js"` would mean a file `"script.js"` from the current folder.
|
||||||
|
|
||||||
|
We can give a full URL al well, for instance:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.2.0/lodash.js"></script>
|
||||||
|
```
|
||||||
|
|
||||||
|
To attach several scripts, use multiple tags:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script src="/js/script1.js"></script>
|
||||||
|
<script src="/js/script2.js"></script>
|
||||||
|
…
|
||||||
|
```
|
||||||
|
|
||||||
|
```smart
|
||||||
|
As a rule, only simplest scripts are put into HTML. More complex ones reside in separate files.
|
||||||
|
|
||||||
|
The benefit of a separate file is that the browser will download it and then store in its [cache](https://en.wikipedia.org/wiki/Web_cache).
|
||||||
|
|
||||||
|
After it, other pages which want the same script will take it from the cache instead of downloading it. So the file is actually downloaded only once.
|
||||||
|
|
||||||
|
That saves traffic and makes pages faster.
|
||||||
|
```
|
||||||
|
|
||||||
|
````warn header="If `src` is set, the script content is ignored."
|
||||||
|
A single `<script>` tag may not have both an `src` and the code inside.
|
||||||
|
|
||||||
|
This won't work:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script *!*src*/!*="file.js">
|
||||||
|
alert(1); // the content is ignored, because src is set
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
We must choose: either it's an external `<script src="…">` or a regular `<script>` with code.
|
||||||
|
|
||||||
|
The example above can be split into two scripts to work:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script src="file.js"></script>
|
||||||
|
<script>
|
||||||
|
alert(1);
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
````
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|
||||||
- We can use a `<script>` tag to add JavaScript code to the page.
|
- We can use a `<script>` tag to add JavaScript code to the page.
|
||||||
- The `type` and `language` attributes are not required.
|
- The `type` and `language` attributes are not required.
|
||||||
- A `<script>` tag blocks page rendering. Later we'll see how to evade that where needed.
|
- Scripts in an external file can be inserted on the page via `<script src="path"></script>`.
|
||||||
|
|
||||||
|
|
||||||
|
There is much more about browser scripts and their interaction with the web-page. But let's keep in mind that this part of the tutorial is devoted to Javascript language. So we shouldn't distract ourselves from it. We'll be using a browser as a way to run Javascript, very convenient for online reading, but yet one of many.
|
||||||
|
|
||||||
|
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 83 KiB |
|
@ -25,9 +25,9 @@ alert( 'Hello' );
|
||||||
alert( 'World' );
|
alert( 'World' );
|
||||||
```
|
```
|
||||||
|
|
||||||
## The semicolon [#semicolon]
|
## Semicolons [#semicolon]
|
||||||
|
|
||||||
The semicolon may be omitted in most cases when a line break exists.
|
A semicolon may be omitted in most cases when a line break exists.
|
||||||
|
|
||||||
This would also work:
|
This would also work:
|
||||||
|
|
||||||
|
@ -86,11 +86,10 @@ The error in the no-semicolon variant occurs because JavaScript engine does not
|
||||||
alert( "There will be an error" )[1, 2].forEach(alert)
|
alert( "There will be an error" )[1, 2].forEach(alert)
|
||||||
```
|
```
|
||||||
|
|
||||||
And in this particular case, that's just wrong. There must be two independent statements. Hence the error.
|
And in this particular case, that's just wrong, hence the error. There are other situations when such thing happens.
|
||||||
|
|
||||||
````
|
````
|
||||||
|
|
||||||
It's recommended to put 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 time. But it's safer, especially for a beginner -- to put them.
|
It's recommended to put 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 time. But it's safer, especially for a beginner -- to put them.
|
||||||
|
|
||||||
## Comments
|
## Comments
|
||||||
|
|
||||||
|
|
|
@ -48,9 +48,9 @@ Only comments may appear above `"use strict"`.
|
||||||
|
|
||||||
|
|
||||||
```smart header="`use strict` for functions"
|
```smart header="`use strict` for functions"
|
||||||
We will learn [functions](/function-basics) very soon.
|
We will learn functions (a way to group commands) soon.
|
||||||
|
|
||||||
Looking ahead let's just note that `"use strict"` can be put at the start of a function instead of the whole script. Then the strict mode is enabled in this function only. But that limitation is exceptionally rarely needed.
|
Looking ahead let's just note that `"use strict"` can be put at the start of a function instead of the whole script. Then the strict mode is enabled in that function only. But usually people put it on top of scripts.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ Variables are used to store the information.
|
||||||
|
|
||||||
## A variable
|
## A variable
|
||||||
|
|
||||||
A [variable]("https://en.wikipedia.org/wiki/Variable_(computer_science)") is a basic "named storage" for the information. We can use variables to store the goods, visitors etc.
|
A [variable]("https://en.wikipedia.org/wiki/Variable_(computer_science)") is a "named storage" for the information. We can use variables to store goodies, visitors and other data.
|
||||||
|
|
||||||
To create a variable in JavaScript, we need to use the `let` keyword.
|
To create a variable in JavaScript, we need to use the `let` keyword.
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ let user = 'John',
|
||||||
message = 'Hello';
|
message = 'Hello';
|
||||||
```
|
```
|
||||||
|
|
||||||
...Or even in comma-first style:
|
...Or even in the "comma-first" style:
|
||||||
|
|
||||||
```js no-beautify
|
```js no-beautify
|
||||||
let user = 'John'
|
let user = 'John'
|
||||||
|
|
|
@ -548,7 +548,7 @@ alert( 'Österreich'.localeCompare('Zealand') ); // -1
|
||||||
|
|
||||||
The method actually has two additional arguments specified in [the documentation](mdn:js/String/localeCompare), that allow to specify the language (by default taken from the environment) and setup additional rules like case sensivity or should `"a"` and `"á"` be treated as the same etc.
|
The method actually has two additional arguments specified in [the documentation](mdn:js/String/localeCompare), that allow to specify the language (by default taken from the environment) and setup additional rules like case sensivity or should `"a"` and `"á"` be treated as the same etc.
|
||||||
|
|
||||||
## Internal encoding
|
## Internals, Unicode
|
||||||
|
|
||||||
```warn header="Advanced knowledge"
|
```warn header="Advanced knowledge"
|
||||||
The section goes deeper into string internals. The knowledge will be useful for you if you plan to deal with emoji, rare mathematical of hieroglyphs characters or other rare symbols.
|
The section goes deeper into string internals. The knowledge will be useful for you if you plan to deal with emoji, rare mathematical of hieroglyphs characters or other rare symbols.
|
||||||
|
|
|
@ -1 +1,20 @@
|
||||||
|
There are many ways to solve that.
|
||||||
|
|
||||||
|
Here are two of them:
|
||||||
|
|
||||||
|
```js
|
||||||
|
function isEmpty(obj)
|
||||||
|
return Object.keys(obj).length == 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```js
|
||||||
|
function isEmpty(obj)
|
||||||
|
for(let key in obj) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The second one is probably more performant.
|
|
@ -540,11 +540,11 @@ To fix that, we should examine the value of `user[key]` in the cloning loop and
|
||||||
There's a standard algorithm for deep cloning that handles the case above and more complex cases, called the [Structured cloning algorithm](w3c.github.io/html/infrastructure.html#internal-structured-cloning-algorithm). We can use a ready implementation of it from the Javascript library [lodash](https://lodash.com). The method is called [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep).
|
There's a standard algorithm for deep cloning that handles the case above and more complex cases, called the [Structured cloning algorithm](w3c.github.io/html/infrastructure.html#internal-structured-cloning-algorithm). We can use a ready implementation of it from the Javascript library [lodash](https://lodash.com). The method is called [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep).
|
||||||
|
|
||||||
|
|
||||||
## Ordering
|
## Ordered like an object
|
||||||
|
|
||||||
Are objects ordered? In other words, if we loop over an object, do we get all properties in the same order that they are added in it?
|
Are objects ordered? In other words, if we loop over an object, do we get all properties in the same order that they are added in it?
|
||||||
|
|
||||||
The short answer is: "no" for integer properties, "yes" for others. The details follow.
|
The short answer is: "ordered like an object": integer properties are sorted, others appear in creation order. The details follow.
|
||||||
|
|
||||||
As an example, let's consider an object with the phone codes:
|
As an example, let's consider an object with the phone codes:
|
||||||
|
|
||||||
|
|
202
1-js/4-data-structures/5-memory-management/article.md
Normal file
|
@ -0,0 +1,202 @@
|
||||||
|
# Garbage collection
|
||||||
|
|
||||||
|
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?
|
||||||
|
|
||||||
|
[cut]
|
||||||
|
|
||||||
|
## Reachability
|
||||||
|
|
||||||
|
The main concept of memory management in Javascript is *reachability*.
|
||||||
|
|
||||||
|
Simply put, "reachable" values are those that are accessible now or in the future. They are guaranteed to be stored in memory.
|
||||||
|
|
||||||
|
1. There's a base set of reachable values. For instance:
|
||||||
|
|
||||||
|
- Local variables and parameters of the current function.
|
||||||
|
- Variables and parameters for other functions in the current chain of nested calls.
|
||||||
|
- Global variables.
|
||||||
|
|
||||||
|
These variables are called *roots*.
|
||||||
|
|
||||||
|
2. Any other value is retained in memory only while it's reachable from a root by a reference of by a chain of references.
|
||||||
|
|
||||||
|
There's a background process that runs by the engine itself called [garbage collector](https://en.wikipedia.org/wiki/Garbage_collection_(computer_science)). It monitors all objects and removes those that became unreachable.
|
||||||
|
|
||||||
|
Note that only objects need reachability tracking, not primitives. For primitives things are very simple. They are copied as a whole on assignment, a single primitive can only be stored in one place, there can be no references for them. So if there was a string in a variable, and it was replaced with another string, then the old one can safely be junked.
|
||||||
|
|
||||||
|
Objects from the other hand can be referenced from multiple variables. If two variables store a reference to the same object, then even if one of them is overwritten, the object is still accessible through the second one. That's why a special "garbage collector" is needed that watches the references.
|
||||||
|
|
||||||
|
The basic garbage collection algorithm is called "mark-and-sweep".
|
||||||
|
|
||||||
|
Regularly the following "garbage collection" steps are performed:
|
||||||
|
|
||||||
|
- The garbage collector takes roots and follows all references from them.
|
||||||
|
- It remembers all objects it meets and follows their references.
|
||||||
|
- ...And so on until there are unvisited references.
|
||||||
|
- All objects except those are removed.
|
||||||
|
|
||||||
|
Let's see examples to get the better picture.
|
||||||
|
|
||||||
|
## A simple example
|
||||||
|
|
||||||
|
Here's the simplest example:
|
||||||
|
|
||||||
|
```js
|
||||||
|
// user has a reference to the object
|
||||||
|
let user = {
|
||||||
|
name: "John"
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Here an on further pictures, arrows depict references. For instance, the global variable `"user"` references John (the object `{name: "John"}`). The `"name"` property is not a reference, it stores a primitive, so it's painted inside the object.
|
||||||
|
|
||||||
|
If the `user` is overwritten, the reference is lost:
|
||||||
|
|
||||||
|
```js
|
||||||
|
user = null;
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Now John becomes unreachable. There's no way to access it, no references to it. Garbage collector will junk the data and free the memory.
|
||||||
|
|
||||||
|
## Two references
|
||||||
|
|
||||||
|
Now let's imagine we copied the reference from `user` to `admin`:
|
||||||
|
|
||||||
|
```js
|
||||||
|
// user has a reference to the object
|
||||||
|
let user = {
|
||||||
|
name: "John"
|
||||||
|
};
|
||||||
|
|
||||||
|
*!*
|
||||||
|
let admin = user;
|
||||||
|
*/!*
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Now if we do the same:
|
||||||
|
```js
|
||||||
|
user = null;
|
||||||
|
```
|
||||||
|
|
||||||
|
...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
|
||||||
|
|
||||||
|
Now a more complex example. The family:
|
||||||
|
|
||||||
|
```js
|
||||||
|
function marry(man, woman) {
|
||||||
|
woman.husband = man;
|
||||||
|
man.wife = woman;
|
||||||
|
|
||||||
|
return {
|
||||||
|
father: man,
|
||||||
|
mother: woman
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let family = marry({
|
||||||
|
name: "John"
|
||||||
|
}, {
|
||||||
|
name: "Ann"
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Function `marry` "marries" two objects by giving them references to each other and returns a new object that contains them both.
|
||||||
|
|
||||||
|
The resulting memory structure:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
To activate garbage collection, let's remove two references:
|
||||||
|
|
||||||
|
```js
|
||||||
|
delete family.father;
|
||||||
|
delete family.mother.husband;
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that if the deletion of any single one of them would not lead to anything, because all objects would still be reachable.
|
||||||
|
|
||||||
|
But if we delete both, then we can see that John has no incoming references any more:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
**Outgoing references do not matter. Only incoming ones can make the object reachable.**
|
||||||
|
|
||||||
|
John, the former `family.father` is now unreachable and will be removed from the memory with all its data that also became unaccessible.
|
||||||
|
|
||||||
|
After garbage collection:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Unreachable island
|
||||||
|
|
||||||
|
It is possible that the whole island of interlinked objects becomes unreachable and is removed from the memory.
|
||||||
|
|
||||||
|
The source object is the same as above. Then:
|
||||||
|
|
||||||
|
```js
|
||||||
|
family = null;
|
||||||
|
```
|
||||||
|
|
||||||
|
The result:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
This example demonstrates how important the concept of reachability is.
|
||||||
|
|
||||||
|
It is clearly seen that John and Ann are still linked, both have incoming references. But it's not enough.
|
||||||
|
|
||||||
|
The former `"family"` object has been unlinked from the root, there's no reference to it any more, so the whole island became unreachable and will be removed.
|
||||||
|
|
||||||
|
## Real-life algorithms
|
||||||
|
|
||||||
|
```smart header="Advanced knowledge"
|
||||||
|
The subsection contains advanced information about how things work internally. This knowledge is not required to continue learning Javascript.
|
||||||
|
```
|
||||||
|
|
||||||
|
The simple garbage collection algorithm that was described earlier in this chapter is not the best.
|
||||||
|
|
||||||
|
One of the main problems is that the collection process must be atomic: no new links should appear and no existing ones should be modified while it analyzes all references of existing objects and figures out which are unreachable.
|
||||||
|
|
||||||
|
Essentially that means that the script execution must be paused to ensure that. Pauses may be small for simple scripts, but become noticeable (like 1000 ms or more) for big ones with many of objects. Such "hiccups" can be unpleasant and even disruptive for program systems that operate on real-time financial or medical data.
|
||||||
|
|
||||||
|
So there are many optimizations to it.
|
||||||
|
|
||||||
|
In this section we use
|
||||||
|
|
||||||
|
### Generational collection
|
||||||
|
|
||||||
|
Most objects die very fast. For instance, local variables of functions usually don't exist for long. So a special memory area is created for them.
|
||||||
|
|
||||||
|
This memory area is garbage-collected in a special way. Instead of
|
||||||
|
|
||||||
|
|
||||||
|
So many optimizations and variations of the algorithm exist.
|
||||||
|
|
||||||
|
One of most widely used optimizations in JS engines is called "generational" garbage collection.
|
||||||
|
|
||||||
|
Objects are split into two sets: "old ones" and "new ones". Each set has its own memory area.
|
||||||
|
|
||||||
|
A new object is created in the "new" memory area and, if survived long enough, migrates to the "old" one. The "new" area is usually small and is garbage collected often. The "old" area is big and rarely cleaned up.
|
||||||
|
|
||||||
|
In practice that helps a lot, because many small objects are created and destroyed almost immediately. For instance when they are local variables of a function. And few objects survive for a long time, like the object with the current visitor data.
|
||||||
|
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
- 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.
|
||||||
|
- Modern engines implement advanced algorithms of garbage collector that try to evade stop-the-world problem of the old ones.
|
||||||
|
|
||||||
|
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).
|
||||||
|
|
BIN
1-js/4-data-structures/5-memory-management/family-no-family.png
Normal file
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 40 KiB |
After Width: | Height: | Size: 7.7 KiB |
After Width: | Height: | Size: 18 KiB |
BIN
1-js/4-data-structures/5-memory-management/family-no-father.png
Normal file
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
After Width: | Height: | Size: 5.7 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 6.6 KiB |
After Width: | Height: | Size: 16 KiB |
BIN
1-js/4-data-structures/5-memory-management/memory-user-john.png
Normal file
After Width: | Height: | Size: 5 KiB |
After Width: | Height: | Size: 12 KiB |
0
archive/01-hello-world/1-hello-alert/solution.md
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
alert( "I'm JavaScript!" );
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
12
archive/01-hello-world/1-hello-alert/task.md
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
importance: 5
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Show an alert
|
||||||
|
|
||||||
|
Create a page that shows a message "I'm JavaScript!".
|
||||||
|
|
||||||
|
Do it in a sandbox, or on your hard drive, doesn't matter, just ensure that it works.
|
||||||
|
|
||||||
|
[demo src="solution"]
|
||||||
|
|
92
archive/01-hello-world/article.md
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
# Hello, world!
|
||||||
|
|
||||||
|
About 98% of the tutorial is core Javascript, that is platform-independant. So you'll be able to learn how to use Node.JS and other things based on that knowledge.
|
||||||
|
|
||||||
|
But we need something like a "base environment" to run our scripts, and browser is probably a good choice.
|
||||||
|
|
||||||
|
So we'll start with attaching a script to the webpage. For other environments like Node.JS there are other ways to run it.
|
||||||
|
|
||||||
|
[cut]
|
||||||
|
|
||||||
|
[todo remove defer/async from here and move to 2nd part?]
|
||||||
|
|
||||||
|
## The "script" tag
|
||||||
|
|
||||||
|
[todo need this? and special (need it too?)]
|
||||||
|
```smart header="What if I want to move faster?"
|
||||||
|
In the case if you've developed with JavaScript already or have a lot of experience in another language, you can skip detailed explanatins and jump to <info:javascript-specials>. There you can find an essense of important features.
|
||||||
|
|
||||||
|
If you have enough time and want to learn things in details then read on :)
|
||||||
|
```
|
||||||
|
|
||||||
|
JavaScript programs can be inserted in any place of HTML with the help of the `<script>` tag.
|
||||||
|
|
||||||
|
For instance:
|
||||||
|
|
||||||
|
```html run height=100
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<p>Before the script...</p>
|
||||||
|
|
||||||
|
*!*
|
||||||
|
<script>
|
||||||
|
alert( 'Hello, world!' );
|
||||||
|
</script>
|
||||||
|
*/!*
|
||||||
|
|
||||||
|
<p>...After the script.</p>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
```online
|
||||||
|
You can run the example clicking on a "Play" button in it's right-top corner.
|
||||||
|
```
|
||||||
|
|
||||||
|
The `<script>` tag contains JavaScript code which is automatically executed when the browser meets the tag.
|
||||||
|
|
||||||
|
Please note the execution sequence:
|
||||||
|
|
||||||
|
1. Browser starts to parse the document and display the page.
|
||||||
|
2. When the browser meets `<script>`, it switches to the JavaScript execution mode. In this mode it executes the script.
|
||||||
|
3. The `alert` command shows a message and pauses the execution.
|
||||||
|
4. Note that at this moment a part of the page before the script is shown already.
|
||||||
|
5. When the script is finished, it gets back to the HTML-mode, and *only then* it shows the rest of the document.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
A visitor won't see the content after the script until it is executed. In other words, a `<script>` tag blocks rendering.
|
||||||
|
|
||||||
|
## The modern markup
|
||||||
|
|
||||||
|
The `<script>` tag has a few attributes that are rarely used nowadays, but we can find them in the old code:
|
||||||
|
|
||||||
|
The `type` attribute: <code><script <u>type</u>=...></code>
|
||||||
|
|
||||||
|
: The old standard HTML4 required a script to have the type. Usually it was `type="text/javascript"`. The modern HTML standard assumes this `type` by default, no attribute is required.
|
||||||
|
|
||||||
|
The `language` attribute: <code><script <u>language</u>=...></code>
|
||||||
|
: This attribute was meant to show the language of the script. As of now, this attribute makes no sense, the language is JavaScript by default. No need to use it.
|
||||||
|
|
||||||
|
Comments before and after scripts.
|
||||||
|
: In really ancient books and guides, one may find comments inside `<script>`, like this:
|
||||||
|
|
||||||
|
```html no-beautify
|
||||||
|
<script type="text/javascript"><!--
|
||||||
|
...
|
||||||
|
//--></script>
|
||||||
|
```
|
||||||
|
|
||||||
|
These comments were supposed to hide the code from an old browser that did't understand a `<script>` tag. But for all browsers born in the past 15+ years that's not an issue. I only mention it here, because it serves as a pointer. If you see that in a code somewhere -- it's probably really old and probably not worth looking into.
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
- We can use a `<script>` tag to add JavaScript code to the page.
|
||||||
|
- The `type` and `language` attributes are not required.
|
||||||
|
- A `<script>` tag blocks page rendering. Later we'll see how to evade that where needed.
|
||||||
|
|
BIN
archive/01-hello-world/hello-world-render.png
Normal file
After Width: | Height: | Size: 44 KiB |
BIN
archive/01-hello-world/hello-world-render@2x.png
Normal file
After Width: | Height: | Size: 83 KiB |
1
archive/02-external-script/1-hello-alert-ext/alert.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
alert("I'm JavaScript!");
|
10
archive/02-external-script/1-hello-alert-ext/index.html
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<script src="alert.js"></script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
8
archive/02-external-script/1-hello-alert-ext/solution.md
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
The HTML code:
|
||||||
|
|
||||||
|
[html src="index.html"]
|
||||||
|
|
||||||
|
For the file `alert.js` in the same folder:
|
||||||
|
|
||||||
|
[js src="alert.js"]
|
||||||
|
|
9
archive/02-external-script/1-hello-alert-ext/task.md
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
importance: 5
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Show an alert with an external script
|
||||||
|
|
||||||
|
Take the solution of the previous task <info:task/hello-alert>. Modify it by extracting the script content into an external file `alert.js`, residing in the same folder.
|
||||||
|
|
||||||
|
Open the page, ensures that the alert works.
|
|
@ -1,326 +0,0 @@
|
||||||
# Memory Management
|
|
||||||
|
|
||||||
Memory management in Javascript is performed automatically and invisibly to us. We create primitives, objects, functions... All that takes memory.
|
|
||||||
|
|
||||||
What happens with an object when it's not needed any more? Can we occasionally create problems for memory management and get performance problems because of that? To answer that -- let's get under the hood of the Javascript engine.
|
|
||||||
|
|
||||||
[cut]
|
|
||||||
|
|
||||||
## Reachability
|
|
||||||
|
|
||||||
The main concept of memory management in Javascript is *reachability*.
|
|
||||||
|
|
||||||
Simply put, "reachable" values are those that are accessible now or in the future. They are guaranteed to be stored in memory.
|
|
||||||
|
|
||||||
1. There's a base set of reachable values. For instance:
|
|
||||||
|
|
||||||
- Local variables and parameters of the current function.
|
|
||||||
- Variables and parameters for other functions in the current chain of nested calls.
|
|
||||||
- Global variables.
|
|
||||||
|
|
||||||
These variables are called *roots*.
|
|
||||||
|
|
||||||
2. **Any other value is retained in memory only while it's reachable from a root by a reference of by a chain of references.**
|
|
||||||
|
|
||||||
There's a background process that runs by the engine itself called [Garbage collector](https://en.wikipedia.org/wiki/Garbage_collection_(computer_science)), it monitors all objects and removes those that became unreachable.
|
|
||||||
|
|
||||||
|
|
||||||
Things are very simple for primitive values. They are copied as a whole on assignment. A single primitive can only be stored in one place, there are no references for them. So if there was a string in a variable, and it was replaced with another string, then the old one can safely be junked.
|
|
||||||
|
|
||||||
Objects from the other hand can be referenced from multiple variables. If two variables store a reference to the same object, then even if one of them is overwritten, the object is still accessible through the second one. That's why a special "garbage collector" is needed that watches the references.
|
|
||||||
|
|
||||||
The basic garbage collection algorithm is called "mark-and-sweep".
|
|
||||||
|
|
||||||
Regularly the following "garbage collection" steps are performed:
|
|
||||||
|
|
||||||
- The collector follows all references from roots and remembers the objects.
|
|
||||||
- All objects except those are removed.
|
|
||||||
|
|
||||||
Let's see examples to get the better picture.
|
|
||||||
|
|
||||||
## A simple example
|
|
||||||
|
|
||||||
Here's the simplest example:
|
|
||||||
|
|
||||||
```js
|
|
||||||
// user has a reference to the object
|
|
||||||
let user = {
|
|
||||||
name: "John"
|
|
||||||
};
|
|
||||||
|
|
||||||
// user is overwritten, the reference is lost
|
|
||||||
user = null;
|
|
||||||
```
|
|
||||||
|
|
||||||
After the variable is overwritten, the object `{name: "John"}` becomes unreachable. There's no way to access it, no references to it. Garbage collector will junk the data and free the memory.
|
|
||||||
|
|
||||||
## Two references
|
|
||||||
|
|
||||||
Now let's imagine we copied the reference:
|
|
||||||
|
|
||||||
```js
|
|
||||||
// user has a reference to the object
|
|
||||||
let user = {
|
|
||||||
name: "John"
|
|
||||||
};
|
|
||||||
|
|
||||||
*!*
|
|
||||||
let admin = user;
|
|
||||||
*/!*
|
|
||||||
|
|
||||||
user = null;
|
|
||||||
```
|
|
||||||
|
|
||||||
Here we still can access the object via `admin` global variable, so it's in memory. If we overwrite `admin` too, then it can be removed.
|
|
||||||
|
|
||||||
|
|
||||||
## Mutual links
|
|
||||||
|
|
||||||
Now a more complex example. The family:
|
|
||||||
|
|
||||||
```js
|
|
||||||
function marry(man, woman) {
|
|
||||||
woman.husband = man;
|
|
||||||
man.wife = woman;
|
|
||||||
|
|
||||||
return {
|
|
||||||
father: man,
|
|
||||||
mother: woman
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let family = marry({
|
|
||||||
name: "John"
|
|
||||||
}, {
|
|
||||||
name: "Ann"
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
Function `marry` "marries" two objects by giving them references to each other and returns a new object that contains them both.
|
|
||||||
|
|
||||||
The resulting memory structure:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Here arrows depict references. The `"name"` property is not a reference, it stores a primitive, so it's drawn inside the object.
|
|
||||||
|
|
||||||
To activate garbage collection, let's remove two references:
|
|
||||||
|
|
||||||
```js
|
|
||||||
delete family.father;
|
|
||||||
delete family.mother.husband;
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that if the deletion of any one of them would not lead to anything, because all objects are still reachable.
|
|
||||||
|
|
||||||
But if we delete the two, then we can see that the object `"John"` has no incoming references any more:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
**Outgoing references do not matter. Only incoming ones can make the object reachable.**
|
|
||||||
|
|
||||||
The former `family.father` is now unreachable and will be removed from the memory with all its data that also became unaccessible.
|
|
||||||
|
|
||||||
After garbage collection:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## Unreachable island
|
|
||||||
|
|
||||||
It is possible that the whole island of interlinked objects becomes unreachable and is removed from the memory.
|
|
||||||
|
|
||||||
The source object is the same as above. Then:
|
|
||||||
|
|
||||||
```js
|
|
||||||
family = null;
|
|
||||||
```
|
|
||||||
|
|
||||||
The result:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
This example demonstrates how important the concept of reachability is.
|
|
||||||
|
|
||||||
It is clearly seen that `"John"` and `"Ann"` objects are still linked, both have incoming references. But it's not enough.
|
|
||||||
|
|
||||||
The former `"family"` object has been unlinked from the root, there's no reference to it any more, so the whole island became unreachable and will be removed.
|
|
||||||
|
|
||||||
## Generational optimization
|
|
||||||
|
|
||||||
The simple garbage collection algorithm has a problem.
|
|
||||||
|
|
||||||
If there are many objects, then it may take time to walk all paths from the roots and find all unreachables. And that process must be atomic: no new links should appear and no existing ones should be modified until it completes.
|
|
||||||
|
|
||||||
Essentially that means that the script execution is paused while the search is in progress. Pauses may be small like few milliseconds for simple scripts or noticeable like `100+ms` for big programs with tons of objects. Such hiccups can be unpleasant and even disruptive for program systems that operate on real-time financial or medical data.
|
|
||||||
|
|
||||||
So various optimizations are applied.
|
|
||||||
|
|
||||||
One of most widely used in JS engines is so-called "generational" garbage collection.
|
|
||||||
|
|
||||||
Objects are split into two sets: "old ones" and "new ones". Each set has its own memory area.
|
|
||||||
|
|
||||||
A new object is created in the "new" memory area and, if survived long enough, migrates to the "old" one. The "new" area is usually small and is garbage collected often. The "old" area is big and rarely cleaned up.
|
|
||||||
|
|
||||||
In practice, that's very effective, because most objects are created and destroyed almost immediately. For instance when they are local variables of a function.
|
|
||||||
|
|
||||||
And few objects survive for a long time, like the object with the current visitor data.
|
|
||||||
|
|
||||||
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).
|
|
||||||
|
|
||||||
## Замыкания
|
|
||||||
|
|
||||||
Объекты переменных, о которых шла речь ранее, в главе про замыкания, также подвержены сборке мусора. Они следуют тем же правилам, что и обычные объекты.
|
|
||||||
|
|
||||||
Объект переменных внешней функции существует в памяти до тех пор, пока существует хоть одна внутренняя функция, ссылающаяся на него через свойство `[[Scope]]`.
|
|
||||||
|
|
||||||
Например:
|
|
||||||
|
|
||||||
- Обычно объект переменных удаляется по завершении работы функции. Даже если в нём есть объявление внутренней функции:
|
|
||||||
|
|
||||||
```js
|
|
||||||
function f() {
|
|
||||||
var value = 123;
|
|
||||||
|
|
||||||
function g() {} // g видна только изнутри
|
|
||||||
}
|
|
||||||
|
|
||||||
f();
|
|
||||||
```
|
|
||||||
|
|
||||||
В коде выше `value` и `g` являются свойствами объекта переменных. Во время выполнения `f()` её объект переменных находится в текущем стеке выполнения, поэтому жив. По окончанию, он станет недостижимым и будет убран из памяти вместе с остальными локальными переменными.
|
|
||||||
- ...А вот в этом случае лексическое окружение, включая переменную `value`, будет сохранено:
|
|
||||||
|
|
||||||
```js
|
|
||||||
function f() {
|
|
||||||
var value = 123;
|
|
||||||
|
|
||||||
function g() {}
|
|
||||||
|
|
||||||
*!*
|
|
||||||
return g;
|
|
||||||
*/!*
|
|
||||||
}
|
|
||||||
|
|
||||||
var g = f(); // функция g будет жить и сохранит ссылку на объект переменных
|
|
||||||
```
|
|
||||||
|
|
||||||
В скрытом свойстве `g.[[Scope]]` находится ссылка на объект переменных, в котором была создана `g`. Поэтому этот объект переменных останется в памяти, а в нём -- и `value`.
|
|
||||||
- Если `f()` будет вызываться много раз, а полученные функции будут сохраняться, например, складываться в массив, то будут сохраняться и объекты `LexicalEnvironment` с соответствующими значениями `value`:
|
|
||||||
|
|
||||||
```js
|
|
||||||
function f() {
|
|
||||||
var value = Math.random();
|
|
||||||
|
|
||||||
return function() {};
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3 функции, каждая ссылается на свой объект переменных,
|
|
||||||
// каждый со своим значением value
|
|
||||||
var arr = [f(), f(), f()];
|
|
||||||
```
|
|
||||||
- Объект `LexicalEnvironment` живёт ровно до тех пор, пока на него существуют ссылки. В коде ниже после удаления ссылки на `g` умирает:
|
|
||||||
|
|
||||||
```js
|
|
||||||
function f() {
|
|
||||||
var value = 123;
|
|
||||||
|
|
||||||
function g() {}
|
|
||||||
|
|
||||||
return g;
|
|
||||||
}
|
|
||||||
|
|
||||||
var g = f(); // функция g жива
|
|
||||||
// а значит в памяти остается соответствующий объект переменных f()
|
|
||||||
|
|
||||||
g = null; // ..а вот теперь память будет очищена
|
|
||||||
```
|
|
||||||
|
|
||||||
### Оптимизация в V8 и её последствия
|
|
||||||
|
|
||||||
Современные JS-движки делают оптимизации замыканий по памяти. Они анализируют использование переменных и в случае, когда переменная из замыкания абсолютно точно не используется, удаляют её.
|
|
||||||
|
|
||||||
В коде выше переменная `value` никак не используется. Поэтому она будет удалена из памяти.
|
|
||||||
|
|
||||||
**Важный побочный эффект в V8 (Chrome, Opera) состоит в том, что удалённая переменная станет недоступна и при отладке!**
|
|
||||||
|
|
||||||
Попробуйте запустить пример ниже с открытой консолью Chrome. Когда он остановится, в консоли наберите `alert(value)`.
|
|
||||||
|
|
||||||
```js run
|
|
||||||
function f() {
|
|
||||||
var value = Math.random();
|
|
||||||
|
|
||||||
function g() {
|
|
||||||
debugger; // выполните в консоли alert( value ); Нет такой переменной!
|
|
||||||
}
|
|
||||||
|
|
||||||
return g;
|
|
||||||
}
|
|
||||||
|
|
||||||
var g = f();
|
|
||||||
g();
|
|
||||||
```
|
|
||||||
|
|
||||||
Как вы могли увидеть -- нет такой переменной! Недоступна она изнутри `g`. Интерпретатор решил, что она нам не понадобится и удалил.
|
|
||||||
|
|
||||||
Это может привести к забавным казусам при отладке, вплоть до того что вместо этой переменной будет другая, внешняя:
|
|
||||||
|
|
||||||
```js run
|
|
||||||
var value = "Сюрприз";
|
|
||||||
|
|
||||||
function f() {
|
|
||||||
var value = "самое близкое значение";
|
|
||||||
|
|
||||||
function g() {
|
|
||||||
debugger; // выполните в консоли alert( value ); Сюрприз!
|
|
||||||
}
|
|
||||||
|
|
||||||
return g;
|
|
||||||
}
|
|
||||||
|
|
||||||
var g = f();
|
|
||||||
g();
|
|
||||||
```
|
|
||||||
|
|
||||||
```warn header="Ещё увидимся"
|
|
||||||
Об этой особенности важно знать. Если вы отлаживаете под Chrome/Opera, то наверняка рано или поздно с ней встретитесь!
|
|
||||||
|
|
||||||
Это не глюк отладчика, а особенность работы V8, которая, возможно, будет когда-нибудь изменена. Вы всегда сможете проверить, не изменилось ли чего, запустив примеры на этой странице.
|
|
||||||
```
|
|
||||||
|
|
||||||
## Влияние управления памятью на скорость
|
|
||||||
|
|
||||||
На создание новых объектов и их удаление тратится время. Это важно иметь в виду в случае, когда важна производительность.
|
|
||||||
|
|
||||||
В качестве примера рассмотрим рекурсию. При вложенных вызовах каждый раз создаётся новый объект с переменными и помещается в стек. Потом память из-под него нужно очистить. Поэтому рекурсивный код будет всегда медленнее использующего цикл, но насколько?
|
|
||||||
|
|
||||||
Пример ниже тестирует сложение чисел до данного через рекурсию по сравнению с обычным циклом:
|
|
||||||
|
|
||||||
```js run
|
|
||||||
function sumTo(n) { // обычный цикл 1+2+...+n
|
|
||||||
var result = 0;
|
|
||||||
for (var i = 1; i <= n; i++) {
|
|
||||||
result += i;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function sumToRec(n) { // рекурсия sumToRec(n) = n+SumToRec(n-1)
|
|
||||||
return n == 1 ? 1 : n + sumToRec(n - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
var timeLoop = performance.now();
|
|
||||||
for (var i = 1; i < 1000; i++) sumTo(1000); // цикл
|
|
||||||
timeLoop = performance.now() - timeLoop;
|
|
||||||
|
|
||||||
var timeRecursion = performance.now();
|
|
||||||
for (var i = 1; i < 1000; i++) sumToRec(1000); // рекурсия
|
|
||||||
timeRecursion = performance.now() - timeRecursion;
|
|
||||||
|
|
||||||
alert( "Разница в " + (timeRecursion / timeLoop) + " раз" );
|
|
||||||
```
|
|
||||||
|
|
||||||
Различие в скорости на таком примере может составлять, в зависимости от интерпретатора, 2-10 раз.
|
|
||||||
|
|
||||||
Вообще, этот пример -- не показателен. Ещё раз обращаю ваше внимание на то, что такие искусственные "микротесты" часто врут. Правильно их делать -- отдельная наука, которая выходит за рамки этой главы. Но и на практике ускорение в 2-10 раз оптимизацией по количеству объектов (и вообще, любых значений) -- отнюдь не миф, а вполне достижимо.
|
|
||||||
|
|
||||||
В реальной жизни в большинстве ситуаций такая оптимизация несущественна, просто потому что "JavaScript и так достаточно быстр". Но она может быть эффективной для "узких мест" кода.
|
|
Before Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 8.7 KiB |
Before Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 34 KiB |