ok
This commit is contained in:
parent
438e7aef2e
commit
185efef6cc
9 changed files with 163 additions and 33 deletions
|
@ -151,11 +151,11 @@ Regularly the following "garbage collection" steps are performed:
|
|||
|
||||
- The garbage collector takes roots and "marks" them.
|
||||
- Then it visits and "marks" all references from them.
|
||||
- Then it visits marked objects and marks their references (the same object is not visited twice).
|
||||
- ...And so on until there are unvisited references.
|
||||
- Then it visits marked objects and marks *their* references, but the same object is not visited twice.
|
||||
- ...And so on until there are unvisited references (reachable from the roots).
|
||||
- All objects except marked ones are removed.
|
||||
|
||||
For instance, if our object structure might look like this:
|
||||
For instance, if our object structure looks like this:
|
||||
|
||||

|
||||
|
||||
|
@ -182,11 +182,10 @@ Javascript engines apply many optimizations to it, to make it run faster and be
|
|||
Some of the optimizations:
|
||||
|
||||
- **Generational collection** -- objects are split into two sets: "new ones" and "old ones". Many objects appear, do their job and die fast, so they can be cleaned up more aggressively. Those "new" that survive for long enough, become "old".
|
||||
- **Incremental collection** -- there may be many objects, if we try to clean up the whole object tree at once, it may take some time and introduce visible delays. So the engine tries to split the job into pieces. Then pieces are executed one at a time. That requires some extra bookkeeping in-between to stay consistent.
|
||||
- **Incremental collection** -- if there are many objects, and we try to walk and mark the whole object set at once, it may take some time and introduce visible delays. So the engine tries to split the job into pieces. Then pieces are executed one at a time. That requires some extra bookkeeping in-between them to stay consistent.
|
||||
- **Idle-time collection** -- the garbage collector tries to run only while the CPU is idle, to reduce the possible effect on the execution.
|
||||
|
||||
In-depth understanding of these optimization is also achievable, there's no magic, but it requires a lot of under-the-hood digging. Javascript engines implement them differently. And -- what's even more important, things change, so going that deep is recommended when you already know the language well and really need low-level optimizations for your code.
|
||||
|
||||
In-depth understanding of these optimization is also possible, there's no magic, but it requires a lot of under-the-hood digging. Javascript engines implement garbage collection differently. And -- what's even more important, things change, so going really deep "in advance" is probably not worth that. Unless, of course, it is a matter of pure interest.
|
||||
|
||||
## Summary
|
||||
|
||||
|
@ -197,8 +196,8 @@ The main things to know:
|
|||
|
||||
Modern engines implement advanced algorithms of garbage collection.
|
||||
|
||||
If you are familiar with low-level programming, the more detailed information about V8 garbage collector is in the article [A tour of V8: Garbage Collection](http://jayconrod.com/posts/55/a-tour-of-v8-garbage-collection). Also you'd better prepare yourself by learning how values are stored in V8. I'm saying: "V8", because it is best covered with articles in the internet. For other engines, things are partially similar, but not the same.
|
||||
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). Also you'd better prepare yourself by learning how values are stored in V8. I'm saying: "V8", because it is best covered with articles in the internet. For other engines, things are somewhat similar, but not the same.
|
||||
|
||||
In-depth knowledge of engines is good when you need low-level optimizations. It would be wise to plan that as the next step after you're familiar with the language. And when you feel that you need that of course.
|
||||
In-depth knowledge of engines is good when you need low-level optimizations. It would be wise to plan that as the next step after you're familiar with the language.
|
||||
|
||||
|
||||
|
|
|
@ -136,7 +136,7 @@ alert(+user); // 30
|
|||
alert(user + 1); // 31 (default like number calls valueOf)
|
||||
```
|
||||
|
||||
If we only want a nice debugging output of our object, then we can implement `toString` only. It the absence of `valueOf` it will be used for both conversions.
|
||||
If we want a single "catch-all" place for primitive conversions, or only want a nice debugging output of our object, then we can implement `toString` only. It the absence of `valueOf` it will be used for all conversions.
|
||||
|
||||
For instance:
|
||||
|
||||
|
|
115
1-js/3-object-basics/9-function-object/article.md
Normal file
115
1-js/3-object-basics/9-function-object/article.md
Normal file
|
@ -0,0 +1,115 @@
|
|||
|
||||
# Function object
|
||||
|
||||
Functions in Javascript are two-faced. From one side they are callable "actions". From the other side, they are objects.
|
||||
|
||||
We can add/remove properties to them, and they have some useful properties of their own.
|
||||
|
||||
## Properties "name" and "length"
|
||||
|
||||
For Function Declarations, it's obvious:
|
||||
|
||||
```js run
|
||||
function sayHi() {
|
||||
alert("Hi");
|
||||
}
|
||||
|
||||
alert(sayHi.name); // sayHi
|
||||
```
|
||||
|
||||
But what's more funny, the "name"-assigning logic is smart. It also recognizes functions, assigned to variables or provided as object methods:
|
||||
|
||||
```js run
|
||||
let sayHi = function() {
|
||||
alert("Hi");
|
||||
}
|
||||
|
||||
alert(sayHi.name); // sayHi (works!)
|
||||
```
|
||||
|
||||
```js run
|
||||
let user = {
|
||||
|
||||
sayHi() { // works
|
||||
// ...
|
||||
},
|
||||
|
||||
sayBye: function() { // works too
|
||||
// ...
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
alert(user.sayHi.name); // sayHi
|
||||
alert(user.sayBye.name); // sayBye
|
||||
```
|
||||
|
||||
In other cases, the name is empty, like here:
|
||||
|
||||
```js
|
||||
let arr = [function() {}];
|
||||
|
||||
alert( arr[0].name ); // ""
|
||||
```
|
||||
|
||||
In practice, most functions do have a name. It's mostly used for debugging, or, sometimes,
|
||||
|
||||
## Builtin "name" and "length"
|
||||
|
||||
Function objects have
|
||||
|
||||
We already know that functions in Javascript are objects
|
||||
Every value in Javascript has the type. What type of value is a function?
|
||||
|
||||
In Javascript, a function is an object.
|
||||
|
||||
NOT ALL !!! (some inherit?)
|
||||
|
||||
For example, all functions have property `name` (function name) and `length` (number of arguments):
|
||||
|
||||
```js run
|
||||
function sayHi() {
|
||||
alert("Hi");
|
||||
}
|
||||
|
||||
alert( sayHi.name ); // sayHi
|
||||
alert( sayHi.length ); // 0
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Custom properties
|
||||
|
||||
Using a function as an object is nothing special.
|
||||
|
||||
Here we add the `counter` property to track the total calls count:
|
||||
|
||||
```js run
|
||||
function sayHi() {
|
||||
alert("Hi");
|
||||
|
||||
*!*
|
||||
// let's count how many times we run
|
||||
sayHi.counter++;
|
||||
*/!*
|
||||
}
|
||||
sayHi.counter = 0; // initial value
|
||||
|
||||
sayHi(); // Hi
|
||||
sayHi(); // Hi
|
||||
|
||||
alert( `Called ${sayHi.counter} times` ); // Called 2 times
|
||||
```
|
||||
|
||||
|
||||
```warn header="A property is not a variable"
|
||||
A property assigned to a function like `sayHi.counter = 0` does *not* define a local variable `counter` inside it. In other words, a property `sayHi.counter` and `let counter` inside the function (if we have it) are two unrelated things.
|
||||
|
||||
We can treat a function as an object for convenience, store properties in it, that has no effect on its execution.
|
||||
```
|
||||
|
||||
There are many well-known Javascript libraries that make a great use of custom function properties.
|
||||
|
||||
They create a "main" function and attach many other "helper" functions to it. For instance, the [jquery](https://jquery.com) library creates a function named `$`. The [lodash](https://lodash.com) library creates a function `_`. And then adds `_.clone`, `_.keyBy` and other properties to (see the [docs](https://lodash.com/docs) when you want learn more about them).
|
||||
|
||||
So, a function can do a useful job by itself and also carry a bunch of other functionality in properties.
|
|
@ -155,7 +155,7 @@ alert( Math.max(0, ...arr, 2, ...arr2) ); // 15
|
|||
When you see `"..."`, there's an easy way to differ spread operator from rest parameters:
|
||||
|
||||
- Rest parameters, if it's in the function definition (gathers into array).
|
||||
- Spread operator, if anywhere else (expands an array).
|
||||
- Spread operator, if in the function call (expands an array).
|
||||
```
|
||||
|
||||
Also we can use spread operator to merge arrays:
|
||||
|
@ -195,8 +195,8 @@ alert( Array.from(str) ); // H,e,l,l,o
|
|||
|
||||
## Summary
|
||||
|
||||
- When `...` occurs in function parameters, it's "rest parameters" and gathers the rest of the list into the array.
|
||||
- When `...` occurs anywhere else, it's called a "spread operator" and expands an array into the list.
|
||||
- When `...` is at the end of function parameters, it's "rest parameters" and gathers the rest of the list into the array.
|
||||
- When `...` occurs in a function call, it's called a "spread operator" and expands an array into the list.
|
||||
|
||||
Together they help to travel between a list and an array of parameters with ease.
|
||||
|
||||
|
|
|
@ -40,14 +40,12 @@ alert( title ); // Imperator
|
|||
In the code above, the first and second elements of the array are skipped, the third one is assigned to `title`, and the rest is also skipped.
|
||||
````
|
||||
|
||||
### The rest operator
|
||||
### The rest '...'
|
||||
|
||||
If we want not just to get first values, but also to gather all that follows -- we can add one more parameter that gets "the rest" using the rest operator `"..."` (three dots):
|
||||
If we want not just to get first values, but also to gather all that follows -- we can add one more parameter that gets "the rest" using the three dots `"..."`:
|
||||
|
||||
```js run
|
||||
*!*
|
||||
let [name1, name2, ...rest] = ["Julius", "Caesar", "Consul", "of the Roman Republic"];
|
||||
*/!*
|
||||
let [name1, name2, *!*...rest*/!*] = ["Julius", "Caesar", *!*"Consul", "of the Roman Republic"*/!*];
|
||||
|
||||
alert(name1); // Julius
|
||||
alert(name2); // Caesar
|
||||
|
@ -55,6 +53,7 @@ alert(name2); // Caesar
|
|||
*!*
|
||||
alert(rest[0]); // Consul
|
||||
alert(rest[1]); // of the Roman Republic
|
||||
alert(rest.length); // 2
|
||||
*/!*
|
||||
```
|
||||
|
||||
|
@ -77,14 +76,25 @@ If we want a "default" value to take place of the absent one, we can provide it
|
|||
```js run
|
||||
*!*
|
||||
// default values
|
||||
let [name="Guest", surname="Anonymous"] = [];
|
||||
let [name="Guest", surname="Anonymous"] = ["Julius"];
|
||||
*/!*
|
||||
|
||||
alert(name); // Guest
|
||||
alert(surname); // Anonymous
|
||||
alert(name); // Julius (from array)
|
||||
alert(surname); // Anonymous (default used)
|
||||
```
|
||||
|
||||
Note that default values can be more complex expressions. They are evaluated only if the value is not provided.
|
||||
Default values can be more complex expressions or even function calls. They are evaluated only if the value is not provided.
|
||||
|
||||
For instance, here we use `prompt` function for defaults. But it will run only for the second one, as it's not provided:
|
||||
|
||||
```js run
|
||||
let [name=prompt('name?'), surname=prompt('surname?')] = ["Julius"];
|
||||
|
||||
alert(name); // Julius (from array)
|
||||
alert(surname); // whatever prompt gets
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Object destructuring
|
||||
|
||||
|
@ -185,7 +195,7 @@ What if the object has more properties than we have variables? Can we assign the
|
|||
Unfortunately, the current specification does not support that feature. There is a proposal, but it's not in the standard yet.
|
||||
|
||||
````smart header="Destructuring without `let`"
|
||||
In the examples above variables were declared right before the assignment: `let {…} = {…}`. Of course, we could use the existing variables too. But there's a catch.
|
||||
In the examples above variables were declared right before the assignment: `let {…} = {…}`. Of course, we could use existing variables as well. But there's a catch.
|
||||
|
||||
This won't work:
|
||||
```js run
|
||||
|
@ -206,7 +216,7 @@ The problem is that Javascript treats `{...}` in the main code flow (not inside
|
|||
}
|
||||
```
|
||||
|
||||
To show Javascript that it's not a code block, but a something else, we can wrap the whole assignment in brackets `(...)`:
|
||||
Of course, we want destructuring assignment, but by syntax rules of Javascript blocks are checked first. To show Javascript that it's not a code block, we can wrap the whole assignment in brackets `(...)`:
|
||||
|
||||
|
||||
```js run
|
||||
|
@ -218,6 +228,7 @@ let title, width, height;
|
|||
|
||||
alert( title ); // Menu
|
||||
```
|
||||
Brackets do not affect the result, but now the `{...}` thing is not in the main code flow, so code block syntax does not apply, and the engine finally understands it right.
|
||||
````
|
||||
|
||||
## Nested destructuring
|
||||
|
@ -232,7 +243,8 @@ let options = {
|
|||
width: 100,
|
||||
height: 200
|
||||
},
|
||||
items: ["Cake", "Donut"]
|
||||
items: ["Cake", "Donut"],
|
||||
extra: true // something extra that we will not destruct
|
||||
}
|
||||
|
||||
// destructuring assignment on multiple lines for clarity
|
||||
|
@ -242,7 +254,7 @@ let {
|
|||
height
|
||||
},
|
||||
items: [item1, item2], // assign items here
|
||||
title = "Menu" // an extra property (default value is used)
|
||||
title = "Menu" // not present in the object (default value is used)
|
||||
} = options;
|
||||
|
||||
alert(title); // Menu
|
||||
|
@ -252,11 +264,13 @@ alert(item1); // Cake
|
|||
alert(item2); // Donut
|
||||
```
|
||||
|
||||
As we can see, the whole `options` object is correctly assigned to variables.
|
||||
The whole `options` object except `extra` that was not mentioned, is assigned to corresponding variables.
|
||||
|
||||
The left part of the destructuring assignment can combine and nest things as needed.
|
||||

|
||||
|
||||
## Destructuring function parameters
|
||||
As we can see, the left part of the destructuring assignment can combine and nest things as needed.
|
||||
|
||||
## Smart function parameters
|
||||
|
||||
There are times when a function may have many parameters, most of which are optional. That's especially true for user interfaces. Imagine a function that creates a menu. It may have a width, a height, a title, items list and so on.
|
||||
|
||||
|
@ -283,14 +297,16 @@ Destructuring comes to the rescue!
|
|||
We can pass parameters as an object, and the function immediately destructurizes them into variables:
|
||||
|
||||
```js run
|
||||
// we pass object to function
|
||||
let options = {
|
||||
title: "My menu",
|
||||
items: ["Item1", "Item2"]
|
||||
};
|
||||
|
||||
*!*
|
||||
function showMenu({title = "Untitled", width = 200, height = 100, items = []}) {
|
||||
*/!*
|
||||
// ...and it immediately expands it to variables
|
||||
function showMenu(*!*{title = "Untitled", width = 200, height = 100, items = []}*/!*) {
|
||||
// title, items – taken from options,
|
||||
// width, height – defaults used
|
||||
alert( title + ' ' + width + ' ' + height ); // My Menu 100 200
|
||||
alert( items ); // Item1, Item2
|
||||
}
|
||||
|
@ -339,7 +355,7 @@ showMenu({});
|
|||
showMenu();
|
||||
```
|
||||
|
||||
We can fix this by making `{}` a value by default for the whole destructuring thing:
|
||||
We can fix this by making `{}` the default value for the whole destructuring thing:
|
||||
|
||||
|
||||
```js run
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
Binary file not shown.
After Width: | Height: | Size: 38 KiB |
|
@ -65,7 +65,7 @@ recursion (
|
|||
task: traverse list back
|
||||
)
|
||||
|
||||
<< function is object
|
||||
<< function name
|
||||
name property (obj props, methods, assignments - set it)
|
||||
length property
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue