diff --git a/1-js/2-first-steps/02-external-script/1-hello-alert-ext/alert.js b/1-js/2-first-steps/01-hello-world/2-hello-alert-ext/alert.js similarity index 100% rename from 1-js/2-first-steps/02-external-script/1-hello-alert-ext/alert.js rename to 1-js/2-first-steps/01-hello-world/2-hello-alert-ext/alert.js diff --git a/1-js/2-first-steps/02-external-script/1-hello-alert-ext/index.html b/1-js/2-first-steps/01-hello-world/2-hello-alert-ext/index.html similarity index 100% rename from 1-js/2-first-steps/02-external-script/1-hello-alert-ext/index.html rename to 1-js/2-first-steps/01-hello-world/2-hello-alert-ext/index.html diff --git a/1-js/2-first-steps/02-external-script/1-hello-alert-ext/solution.md b/1-js/2-first-steps/01-hello-world/2-hello-alert-ext/solution.md similarity index 100% rename from 1-js/2-first-steps/02-external-script/1-hello-alert-ext/solution.md rename to 1-js/2-first-steps/01-hello-world/2-hello-alert-ext/solution.md diff --git a/1-js/2-first-steps/02-external-script/1-hello-alert-ext/task.md b/1-js/2-first-steps/01-hello-world/2-hello-alert-ext/task.md similarity index 100% rename from 1-js/2-first-steps/02-external-script/1-hello-alert-ext/task.md rename to 1-js/2-first-steps/01-hello-world/2-hello-alert-ext/task.md diff --git a/1-js/2-first-steps/01-hello-world/article.md b/1-js/2-first-steps/01-hello-world/article.md index bfab9428..ba840a88 100644 --- a/1-js/2-first-steps/01-hello-world/article.md +++ b/1-js/2-first-steps/01-hello-world/article.md @@ -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 . 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 ` ``` - These comments were supposed to hide the code from an old browser that did't understand a ` +``` + +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 + +``` + +To attach several scripts, use multiple tags: + +```html + + +… +``` + +```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 ` +``` + +We must choose: either it's an external ` + +``` +```` ## Summary - We can use a ``. + + +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. diff --git a/1-js/2-first-steps/01-hello-world/hello-world-render.png b/1-js/2-first-steps/01-hello-world/hello-world-render.png index c845cabd..ffe81069 100644 Binary files a/1-js/2-first-steps/01-hello-world/hello-world-render.png and b/1-js/2-first-steps/01-hello-world/hello-world-render.png differ diff --git a/1-js/2-first-steps/01-hello-world/hello-world-render@2x.png b/1-js/2-first-steps/01-hello-world/hello-world-render@2x.png index 8c0be22e..c4411027 100644 Binary files a/1-js/2-first-steps/01-hello-world/hello-world-render@2x.png and b/1-js/2-first-steps/01-hello-world/hello-world-render@2x.png differ diff --git a/1-js/2-first-steps/03-structure/article.md b/1-js/2-first-steps/03-structure/article.md index 6a1bb0c1..90d8cbad 100644 --- a/1-js/2-first-steps/03-structure/article.md +++ b/1-js/2-first-steps/03-structure/article.md @@ -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 diff --git a/1-js/2-first-steps/04-strict-mode/article.md b/1-js/2-first-steps/04-strict-mode/article.md index 4a0740d9..b194e88e 100644 --- a/1-js/2-first-steps/04-strict-mode/article.md +++ b/1-js/2-first-steps/04-strict-mode/article.md @@ -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. ``` diff --git a/1-js/2-first-steps/05-variables/article.md b/1-js/2-first-steps/05-variables/article.md index 477e17fe..4593da57 100644 --- a/1-js/2-first-steps/05-variables/article.md +++ b/1-js/2-first-steps/05-variables/article.md @@ -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. diff --git a/1-js/4-data-structures/3-string/article.md b/1-js/4-data-structures/3-string/article.md index c6c31046..8fc5c4fa 100644 --- a/1-js/4-data-structures/3-string/article.md +++ b/1-js/4-data-structures/3-string/article.md @@ -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. diff --git a/1-js/4-data-structures/4-object/2-is-empty/solution.md b/1-js/4-data-structures/4-object/2-is-empty/solution.md index 8b137891..9b78efa6 100644 --- a/1-js/4-data-structures/4-object/2-is-empty/solution.md +++ b/1-js/4-data-structures/4-object/2-is-empty/solution.md @@ -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. \ No newline at end of file diff --git a/1-js/4-data-structures/4-object/article.md b/1-js/4-data-structures/4-object/article.md index 569a3b78..3774b8c9 100644 --- a/1-js/4-data-structures/4-object/article.md +++ b/1-js/4-data-structures/4-object/article.md @@ -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: diff --git a/1-js/4-data-structures/5-memory-management/article.md b/1-js/4-data-structures/5-memory-management/article.md new file mode 100644 index 00000000..2717988a --- /dev/null +++ b/1-js/4-data-structures/5-memory-management/article.md @@ -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). + diff --git a/1-js/4-data-structures/5-memory-management/family-no-family.png b/1-js/4-data-structures/5-memory-management/family-no-family.png new file mode 100644 index 00000000..a4ce30a3 Binary files /dev/null and b/1-js/4-data-structures/5-memory-management/family-no-family.png differ diff --git a/1-js/4-data-structures/5-memory-management/family-no-family@2x.png b/1-js/4-data-structures/5-memory-management/family-no-family@2x.png new file mode 100644 index 00000000..0d994983 Binary files /dev/null and b/1-js/4-data-structures/5-memory-management/family-no-family@2x.png differ diff --git a/1-js/4-data-structures/5-memory-management/family-no-father-2.png b/1-js/4-data-structures/5-memory-management/family-no-father-2.png new file mode 100644 index 00000000..e24dba5b Binary files /dev/null and b/1-js/4-data-structures/5-memory-management/family-no-father-2.png differ diff --git a/1-js/4-data-structures/5-memory-management/family-no-father-2@2x.png b/1-js/4-data-structures/5-memory-management/family-no-father-2@2x.png new file mode 100644 index 00000000..a6c4ee36 Binary files /dev/null and b/1-js/4-data-structures/5-memory-management/family-no-father-2@2x.png differ diff --git a/1-js/4-data-structures/5-memory-management/family-no-father.png b/1-js/4-data-structures/5-memory-management/family-no-father.png new file mode 100644 index 00000000..df14624b Binary files /dev/null and b/1-js/4-data-structures/5-memory-management/family-no-father.png differ diff --git a/1-js/4-data-structures/5-memory-management/family-no-father@2x.png b/1-js/4-data-structures/5-memory-management/family-no-father@2x.png new file mode 100644 index 00000000..5ab4b379 Binary files /dev/null and b/1-js/4-data-structures/5-memory-management/family-no-father@2x.png differ diff --git a/archive/5-memory-management/family.png b/1-js/4-data-structures/5-memory-management/family.png similarity index 100% rename from archive/5-memory-management/family.png rename to 1-js/4-data-structures/5-memory-management/family.png diff --git a/archive/5-memory-management/family@2x.png b/1-js/4-data-structures/5-memory-management/family@2x.png similarity index 100% rename from archive/5-memory-management/family@2x.png rename to 1-js/4-data-structures/5-memory-management/family@2x.png diff --git a/1-js/4-data-structures/5-memory-management/memory-user-john-admin.png b/1-js/4-data-structures/5-memory-management/memory-user-john-admin.png new file mode 100644 index 00000000..29c4fcbe Binary files /dev/null and b/1-js/4-data-structures/5-memory-management/memory-user-john-admin.png differ diff --git a/1-js/4-data-structures/5-memory-management/memory-user-john-admin@2x.png b/1-js/4-data-structures/5-memory-management/memory-user-john-admin@2x.png new file mode 100644 index 00000000..2f80f19a Binary files /dev/null and b/1-js/4-data-structures/5-memory-management/memory-user-john-admin@2x.png differ diff --git a/1-js/4-data-structures/5-memory-management/memory-user-john-lost.png b/1-js/4-data-structures/5-memory-management/memory-user-john-lost.png new file mode 100644 index 00000000..cdc1d490 Binary files /dev/null and b/1-js/4-data-structures/5-memory-management/memory-user-john-lost.png differ diff --git a/1-js/4-data-structures/5-memory-management/memory-user-john-lost@2x.png b/1-js/4-data-structures/5-memory-management/memory-user-john-lost@2x.png new file mode 100644 index 00000000..d58afdb5 Binary files /dev/null and b/1-js/4-data-structures/5-memory-management/memory-user-john-lost@2x.png differ diff --git a/1-js/4-data-structures/5-memory-management/memory-user-john.png b/1-js/4-data-structures/5-memory-management/memory-user-john.png new file mode 100644 index 00000000..3ba5730d Binary files /dev/null and b/1-js/4-data-structures/5-memory-management/memory-user-john.png differ diff --git a/1-js/4-data-structures/5-memory-management/memory-user-john@2x.png b/1-js/4-data-structures/5-memory-management/memory-user-john@2x.png new file mode 100644 index 00000000..5aa81bb0 Binary files /dev/null and b/1-js/4-data-structures/5-memory-management/memory-user-john@2x.png differ diff --git a/archive/01-hello-world/1-hello-alert/solution.md b/archive/01-hello-world/1-hello-alert/solution.md new file mode 100644 index 00000000..e69de29b diff --git a/archive/01-hello-world/1-hello-alert/solution.view/index.html b/archive/01-hello-world/1-hello-alert/solution.view/index.html new file mode 100644 index 00000000..45e6744b --- /dev/null +++ b/archive/01-hello-world/1-hello-alert/solution.view/index.html @@ -0,0 +1,12 @@ + + + + + + + + + + \ No newline at end of file diff --git a/archive/01-hello-world/1-hello-alert/task.md b/archive/01-hello-world/1-hello-alert/task.md new file mode 100644 index 00000000..afed6a91 --- /dev/null +++ b/archive/01-hello-world/1-hello-alert/task.md @@ -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"] + diff --git a/archive/01-hello-world/article.md b/archive/01-hello-world/article.md new file mode 100644 index 00000000..bfab9428 --- /dev/null +++ b/archive/01-hello-world/article.md @@ -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 . 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 ` +*/!* + +

...After the script.

+ + + + +``` + +```online +You can run the example clicking on a "Play" button in it's right-top corner. +``` + +The ` + ``` + + These comments were supposed to hide the code from an old browser that did't understand a ` + + + + \ No newline at end of file diff --git a/archive/02-external-script/1-hello-alert-ext/solution.md b/archive/02-external-script/1-hello-alert-ext/solution.md new file mode 100644 index 00000000..f42c41e6 --- /dev/null +++ b/archive/02-external-script/1-hello-alert-ext/solution.md @@ -0,0 +1,8 @@ +The HTML code: + +[html src="index.html"] + +For the file `alert.js` in the same folder: + +[js src="alert.js"] + diff --git a/archive/02-external-script/1-hello-alert-ext/task.md b/archive/02-external-script/1-hello-alert-ext/task.md new file mode 100644 index 00000000..fe38de40 --- /dev/null +++ b/archive/02-external-script/1-hello-alert-ext/task.md @@ -0,0 +1,9 @@ +importance: 5 + +--- + +# Show an alert with an external script + +Take the solution of the previous task . 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. diff --git a/1-js/2-first-steps/02-external-script/2-async-defer-first/solution.md b/archive/02-external-script/2-async-defer-first/solution.md similarity index 100% rename from 1-js/2-first-steps/02-external-script/2-async-defer-first/solution.md rename to archive/02-external-script/2-async-defer-first/solution.md diff --git a/1-js/2-first-steps/02-external-script/2-async-defer-first/task.md b/archive/02-external-script/2-async-defer-first/task.md similarity index 100% rename from 1-js/2-first-steps/02-external-script/2-async-defer-first/task.md rename to archive/02-external-script/2-async-defer-first/task.md diff --git a/1-js/2-first-steps/02-external-script/article.md b/archive/02-external-script/article.md similarity index 100% rename from 1-js/2-first-steps/02-external-script/article.md rename to archive/02-external-script/article.md diff --git a/1-js/2-first-steps/02-external-script/banner.js b/archive/02-external-script/banner.js similarity index 100% rename from 1-js/2-first-steps/02-external-script/banner.js rename to archive/02-external-script/banner.js diff --git a/archive/5-memory-management/article.md b/archive/5-memory-management/article.md deleted file mode 100644 index f65433ad..00000000 --- a/archive/5-memory-management/article.md +++ /dev/null @@ -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: - -![](family.png) - -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: - -![](family-no-father.png) - -**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: - -![](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"` 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 и так достаточно быстр". Но она может быть эффективной для "узких мест" кода. diff --git a/archive/5-memory-management/family-no-family.png b/archive/5-memory-management/family-no-family.png deleted file mode 100644 index 9ca2bbad..00000000 Binary files a/archive/5-memory-management/family-no-family.png and /dev/null differ diff --git a/archive/5-memory-management/family-no-family@2x.png b/archive/5-memory-management/family-no-family@2x.png deleted file mode 100644 index cfdf63b3..00000000 Binary files a/archive/5-memory-management/family-no-family@2x.png and /dev/null differ diff --git a/archive/5-memory-management/family-no-father-2.png b/archive/5-memory-management/family-no-father-2.png deleted file mode 100644 index 448f8d28..00000000 Binary files a/archive/5-memory-management/family-no-father-2.png and /dev/null differ diff --git a/archive/5-memory-management/family-no-father-2@2x.png b/archive/5-memory-management/family-no-father-2@2x.png deleted file mode 100644 index 03e30711..00000000 Binary files a/archive/5-memory-management/family-no-father-2@2x.png and /dev/null differ diff --git a/archive/5-memory-management/family-no-father.png b/archive/5-memory-management/family-no-father.png deleted file mode 100644 index d2119848..00000000 Binary files a/archive/5-memory-management/family-no-father.png and /dev/null differ diff --git a/archive/5-memory-management/family-no-father@2x.png b/archive/5-memory-management/family-no-father@2x.png deleted file mode 100644 index 84604247..00000000 Binary files a/archive/5-memory-management/family-no-father@2x.png and /dev/null differ diff --git a/figures.sketch b/figures.sketch index cea3b4b2..dde9a827 100644 Binary files a/figures.sketch and b/figures.sketch differ