move objects (before?)

This commit is contained in:
Ilya Kantor 2016-07-19 23:36:27 +03:00
parent 480e69b843
commit 31b5af1580
50 changed files with 445 additions and 362 deletions

View file

@ -8,17 +8,8 @@ So we'll start with attaching a script to the webpage. For other environments li
[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:
@ -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.
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.
![Rendering order](hello-world-render.png)
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
@ -82,11 +62,76 @@ Comments before and after scripts.
//--></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
- 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.
- 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.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 83 KiB

Before After
Before After

View file

@ -1,5 +0,0 @@
Answers:
1. The first is `big.js`, that's a normal sequence for external `<script>` tags.
2. The first is `small.js`, because `async` makes script behave independently of each other and the page. The first to loads runs first.
3. The first is `big.js`, because "deferred" scripts keep relative execution order.

View file

@ -1,29 +0,0 @@
importance: 4
---
# Which script executes first?
In the questions below, there are two scripts: `small.js` and `big.js`.
If we assume that `small.js` loads much faster compared to `big.js` -- which script executes first?
```html
<script src="big.js"></script>
<script src="small.js"></script>
```
What if we add `async`?
```html
<script async src="big.js"></script>
<script async src="small.js"></script>
```
What if we switch to `defer`?
```html
<script defer src="big.js"></script>
<script defer src="small.js"></script>
```

View file

@ -1,251 +0,0 @@
# 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>
```
````
## Asynchronous scripts: defer/async
Browser loads and shows HTML gradually as it comes. That's clearly noticeable on the slow internet connection. The browser doesn't wait for the page to load fully. It shows the part that has been loaded already, and then adds content to it as it loads.
As we noted before, when the browser meets a `<script>` tag, it must execute it first and after that show the rest of the page.
For example, in the code below -- until all rabbits are counted, the bottom `<p>` is not shown:
```html run height=100
<!DOCTYPE HTML>
<html>
<body>
<p>Let's count:</p>
*!*
<script>
alert( 'The 1st rabbit!' );
alert( 'The 2nd rabbit!' );
alert( 'The 3rd rabbit!' );
</script>
*/!*
<p>Rabbits counted!</p>
</body>
</html>
```
The behavior is called "synchronous". Usually it causes no problems, but there's an important consequence.
**If a script is external, then until the browser downloads and executes it, a visitor has to wait.**
So, in this document, until `big.js` loads and executes, the `<body>` content is hidden:
```html
<html>
<head>
*!*
<script src="big.js"></script>
*/!*
</head>
<body>
This text is not shown until the browser executes big.js.
</body>
</html>
```
The question is -- do we really want the visitor to wait until the script finishes?
Most of time, we don't.
Sometimes, a script may contain a very important code that really must be loaded before the rest of the page is parsed (and the scripts below executed). But that's only sometimes.
Usually a visitor should be able to see the page while the script is loading. That's especially true for sites with an important content (like the tutorial) -- even if some interfaces are yet inactive (scripts not loaded yet), the visitor can read the text and navigate.
There are situations when such blocking is even dangerous. For example, when we attach a script from the banner system, or a 3rd-party integration code.
Like this:
```html run height=100
Wait. The text below will shown up only after the script executes.
<!-- this banner script takes time to load -->
<script src="/article/external-script/banner.js?speed=0"></script>
<p>…Important information!</p>
```
It's just wrong that the rest of the page is not shown until the banner is loaded. And what if their server is overloaded and responds slowly? Our visitors will have wait even more, maybe even leave for another, faster site.
So, how to "fix" the blocking behavior?
Our first attempt could be to put all such scripts to the bottom of the `<body>`, after all content.
But the solution is not perfect:
1. The script won't start loading until the HTML loads. If the HTML is large, then the delay may be significant. We'd like the browser to start loading a script early, but still do not block the page.
2. If there are many scripts at the bottom of the page, then they queue up. Browser executes only one `<script>` tag in one moment. Please note that it tries to downloads scripts in parallel (for better network utilization), but the execution order is still strictly one after another. So, the first script can block the rest if it's slow. That's not always welcome: some scripts, like ads and web analytics scripts should run independantly and immediately as they load, not block each other.
And here come the attributes `async` and `defer`.
The `async` attribute.
: The script is executed asynchronously. In other words, when the browser meets `<script async src="...">`, it does not stop showing the page. It just initiates script loading and goes on. When the script loads -- it runs.
The `defer` attribute.
: The script with `defer` also executes asynchronously, like async. But there are two essential differences:
1. The browser guarantees to keep the relative order of "deferred" scripts.
2. A "deferred" script always executes after the HTML-document is fully loaded.
Let's modify the "blocking script" example that we've seen before, adding `async`:
```html run height=100
Wait. The text belown will shown up only after the script executes.
<!-- This banner script take some time to load... But nobody cares. -->
<script *!*async*/!* src="/article/external-script/banner.js?speed=0"></script>
<p>…Important information!</p>
```
Now if we run it, we'll see that the whole document is displayed immediately, and the external script runs when it loads.
Let's see more examples with `defer` and `async` to clearly understand the similarities and the differences.
Both attributes allow the browser to show the page without waiting for the script to load. But...
1. Deferred scripts keep the relative order, while async scripts do not.
For example, in the code below (with `async`) there are two scripts. The one which loads first will run first.
```html
<script src="1.js" async></script>
<script src="2.js" async></script>
```
If `2.js` is bigger than `1.js`, it may happen that `2.js` will run before `1.js`. That's normal. Async scripts are totally independent.
And in the code below `defer` is used, which forces browser to keeps execution order. Even if `2.js` loads first, it waits and executes after `1.js`:
```html
<script src="1.js" defer></script>
<script src="2.js" defer></script>
```
This feature of "deferred" scripts is important when `2.js` relies on the result of `1.js` and we must be sure that the order is determined.
2. A script with `defer` always waits for the HTML-document to fully load. The `async` script runs immediately as it loads.
For instance, when the document is large, like:
```html
<script src="async.js" async></script>
<script src="defer.js" defer></script>
A long long text. Many words.
...
```
...Here `async.js` executes when it loads -- possibly, before the document is fully loaded. In contrast, `defer.js` always waits for the full document to be ready.
So, if a script doesn't need the rest of the document (like a web counter), then `async` is superb. And in another case a script may need the whole document to do some work with it. Then `defer` is preferable.
```smart header="Either `async` or `defer`"
We can't use both `defer` and `async` on a single script. If we do that, `async` takes precedence, `defer` is ignored.
```
```warn header="Attributes `async/defer` are only for external scripts"
The attributes `async/defer` work only when set on a script with `src`.
On a script without `src` like <code>&lt;script&gt;...&lt;/script&gt;</code>, they will be ignored.
```
## Summary
- Scripts in an external file can be inserted on the page via `<script src="path"></script>`.
- The browser doesn't show the content below the script until it executes. Unless the script has `async` or `defer` attributes.
- Both `async` and `defer` allow the browser to start script loading and then continue to parse/show the page. They only work on external scripts.
- The difference is that `defer` keeps the relative script order and always executes after the document is fully loaded. In contrast, `async` script executes when it loads, without any conditions.
Before inserting an external `<script src="…">` tag, we should always consider the side-effect of blocking the page rendering. Especially if it's a 3rd-party script. And if we don't want that, then `defer/async` can come in handy.
````smart header="Running ahead..."
For an advanced reader who already knows how to add new tags on the page using DOM: dynamic `<script>` tags behave as `async`. In other words, they run as they load, independantly.
But we can adjust that. If we set `script.async = false`, then such scripts are run in the load of insertion. That's a way to keep the relative order for dynamically added tags.
For example:
```js
function addScript(src){
let script = document.createElement('script');
script.src = src;
*!*
script.async = false;
*/!*
document.head.appendChild(script);
}
addScript('1.js'); // all these scripts will start loading immediately
addScript('2.js'); // but execute in the order of insertion
addScript('3.js'); // that is: 1 -> 2 -> 3
```
We'll surely cover dynamic tags and page manipulation in detail later, in the second part of the tutorial.
````

View file

@ -1 +0,0 @@
alert("Banner loaded!");

View file

@ -25,9 +25,9 @@ alert( 'Hello' );
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:
@ -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)
```
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

View file

@ -48,9 +48,9 @@ Only comments may appear above `"use strict"`.
```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.
```

View file

@ -10,7 +10,7 @@ Variables are used to store the information.
## 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.
@ -72,12 +72,12 @@ let user = 'John',
message = 'Hello';
```
...Or even in comma-first style:
...Or even in the "comma-first" style:
```js no-beautify
let user = 'John'
,age = 25
,message = 'Hello';
, age = 25
, message = 'Hello';
```
Technically, all these variants do the same. So, it's a matter of personal taste and aestetics.

View file

@ -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.
## Internal encoding
## Internals, Unicode
```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.

View file

@ -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.

View file

@ -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).
## 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?
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:

View 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"
};
```
![](memory-user-john.png)
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;
```
![](memory-user-john-lost.png)
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;
*/!*
```
![](memory-user-john-admin.png)
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:
![](family.png)
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:
![](family-no-father.png)
**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:
![](family-no-father-2.png)
## 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:
![](family-no-family.png)
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).

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB