This commit is contained in:
Ilya Kantor 2016-07-13 10:39:15 +03:00
parent 75cdcb406c
commit 4bdc4cd5aa
43 changed files with 1746 additions and 171 deletions

View file

@ -8,11 +8,11 @@ But in the browser, a user doesn't see the errors by default. So, if something g
To see errors and get a lot of other useful information about scripts, browsers have embedded "developer tools".
**It is recommended to use Chrome or Firefox for the development.**
**Most often developers lean towards Chrome or Firefox for the development.**
Other browsers also provide developer tools, but are usually in a "catching-up" position, compared to Chrome/Firefox which are the best.
If there is an error in the certain browser only, then we can use it's developer tools, but usually -- Chrome/Firefox.
If there is an error in the certain browser only, then we can always switch to it's developer tools for the concrete problem.
Developer tools are really powerful, there are many features. On this stage let's just look how to open them, look at errors and run JavaScript commands.

View file

@ -1,11 +1,18 @@
# Hello, world!
In this chapter we'll create a simple script and see it working.
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.

View file

@ -6,7 +6,7 @@ The first overall thing to know is the code structure.
## Statements
The code consists of [statements](https://en.wikipedia.org/wiki/Statement_(computer_science)) -- syntax constructs and commands to perform actions.
[Statements](https://en.wikipedia.org/wiki/Statement_(computer_science)) are syntax constructs and commands to perform actions.
We've already seen a statement `alert('Hello, world!')`, which shows the message.
@ -36,9 +36,9 @@ alert( 'Hello' )
alert( 'World' )
```
In this case JavaScript interprets the line break as a splitter. Just as if there were a semicolon between the lines.
Here JavaScript interprets the line break as an "implicit" semicolon. That's also called an [automatic semicolon insertion](https://tc39.github.io/ecma262/#sec-automatic-semicolon-insertion).
**But it's important that "in most cases" does not mean "always"!**
**In most cases a newline implies a simicolon. But "in most cases" does not mean "always"!**
There are cases when a newline does not mean a semicolon, for example:
@ -50,12 +50,12 @@ alert(3 +
The code outputs `6`, because JavaScript does not insert semicolons here. It is intuitively obvious that if the line ends with a plus `"+"`, then it is an "incomplete expression". And in this case that's actually fine and comfortable.
**But there are situations where JavaScript "fails" to assume a semicolon break where it is really needed.**
**But there are situations where JavaScript "fails" to assume a semicolon where it is really needed.**
Errors which come appear in such cases are quite hard to find and fix.
````smart header="An example of the error"
For a curious reader who might be interested in a concrete example, check this code out:
If you're curious to see a concrete example, check this code out:
```js run
[1, 2].forEach(alert)
@ -63,9 +63,9 @@ For a curious reader who might be interested in a concrete example, check this c
It shows `1` then `2`.
No need to think about the meaning of the brackets `[]` and `forEach` just for now -- it does not matter here. Let's just remember the result.
No need to think about the meaning of the brackets `[]` and `forEach`, for now -- it does not matter. Let's just remember the result.
Now we prepend an `alert` statement *not followed by a semicolon*:
Now we prepend the code with an `alert` statement *not followed by a semicolon*:
```js run no-beautify
alert( "There will be an error" ) // shown
@ -77,16 +77,16 @@ Now if we run it, only the first `alert` is shown, and then an error.
But everything's fine if we add a semicolon:
```js run
alert( "All fine now" ); // shown
[1, 2].forEach(alert) // then this too
[1, 2].forEach(alert) // this works too
```
The error in the former variant occurs because JavaScript engine does not assume a semicolon before square brackets `[...]`, so the code is actually treated as a one-line statement:
The error in the no-semicolon variant occurs because JavaScript engine does not assume a semicolon before square brackets `[...]`, so the code is actually treated as a one-line statement:
```js run no-beautify
alert( "There will be an error" )[1, 2].forEach(alert)
```
And in this particular case, that's just wrong. Hence the error.
And in this particular case, that's just wrong. There must be two independent statements. Hence the error.
````
@ -154,5 +154,5 @@ Please, don't hesitate to comment your code.
Comments increase the overall code footprint, but that's not a problem at all. There are many tools which minify the code before publishing to production server. They remove comments, so they do not appear in the working scripts. So, the comments do not have any negative effects on production at all.
Further in the tutorial we'll make more notes about how to write the code better, easier to read and maintain. We'll also talk more about comments.
Further in the tutorial we'll devote a special chapter to code style, also explaining how to write better comments.

View file

@ -58,10 +58,10 @@ Looking ahead let's just note that `"use strict"` can be put at the start of a f
It is recommended to always start a script with `"use strict"`, for the following reasons:
1. First, because all modern browsers support it, except Internet Explorer 9 and lower.
1. First, all modern browsers support it. Only outdated ones like Internet Explorer 9 and below do not.
2. Second, the modern JavaScript actually forces us into the strict mode. There are several modern language features like "classes" and "modules" that enable strict mode automatically. So, it's hard to evade it.
Here in the tutorial all code (except where said otherwise) works in `"use strict"`. but we'll still note the subtle differences of what happens if you forget it or if the visitor has an outdated browser. So you will also be able to write a code for IE8 and below if you'd like that.
Here in the tutorial all code (except where said otherwise) works in `"use strict"`. but we'll still note the subtle differences of what happens if you forget it or if the visitor has an outdated browser. So you will also be able to write a code that also works for old IE if you'd like that.
## Summary

View file

@ -1,5 +1,5 @@
We generally use upper case for constants that are "hard-coded" or, in other words, when the value is directly written into the code.
We generally use upper case for constants that are "hard-coded". Or, in other words, when the value is known prior to execution and directly written into the code.
In this code, `birthday` is just like that. So we could use the upper case for it.
In this code, `birthday` is exactly like that. So we could use the upper case for it.
In contrast, `age` is evaluated in run-time. Today we have one age, a year after we'll have another one. It is constant in a sense that it does not change through the code execution. But it is a bit "less of a constant" than `birthday`, so we should keep the lower case for it.
In contrast, `age` is evaluated in run-time. Today we have one age, a year after we'll have another one. It is constant in a sense that it does not change through the code execution. But it is a bit "less of a constant" than `birthday`, it is calculated, so we should keep the lower case for it.

View file

@ -9,18 +9,16 @@ Examine the following code:
```js
const birthday = '18.04.1982';
const age = calculateAgeBasedOn(birthday);
const age = someCode(birthday);
```
Here we have a constant `birthday` date and the `age` is calculated with the help of a function (its code is not provided cause it doesn't matter here).
Here we have a constant `birthday` date and the `age` is calculated from `birthday` with the help of some code (it is not provided for shortness, and because details don't matter here).
Would it be right to name both constants using the upper case? Like this:
Would it be right to use upper case for `birthday`? For `age`? Or even for both?
```js
const BIRTHDAY = '18.04.1982';
const BIRTHDAY = '18.04.1982'; // make uppercase?
const AGE = calculateAgeBasedOn(BIRTHDAY);
const AGE = someCode(BIRTHDAY); // make uppercase?
```
...Or we should use the upper case for only one of them? Or just use lower case everywhere?

View file

@ -1,8 +1,8 @@
# Variables
Most of the time, script needs to work with the information.
Most of the time, a script needs to work with the information.
If it's an online-shop -- that's going to be the goods and a shopping cart. If it's a chat -- visitors, messages and so on.
If it's an online-shop -- that's going to be the goods and a shopping cart. If it's a chat -- users, messages and so on.
Variables are used to store the information.
@ -10,7 +10,7 @@ Variables are used to store the information.
## A variable
A [variable]("https://en.wikipedia.org/wiki/Variable_(computer_science)") is defined as a "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 basic "named storage" for the information. We can use variables to store the goods, visitors etc.
To create a variable in JavaScript, we need to use the `let` keyword.
@ -30,7 +30,7 @@ message = 'Hello'; // store the string
*/!*
```
The string is now saved into the memory area assosiated with that variable. We can access it using the variable name:
The string is now saved into the memory area assosiated with the variable. We can access it using the variable name:
```js run
let message;
@ -44,8 +44,9 @@ alert( message ); // shows the variable content
To be concise we can merge the variable declaration and assignment into a single line:
```js run
let message = 'Hello!';
alert( message ); // same as above
let message = 'Hello!'; // define the variable and assign the value
alert( message ); // Hello!
```
We can also declare multiple variables in one line:
@ -54,7 +55,7 @@ We can also declare multiple variables in one line:
let user = 'John', age = 25, message = 'Hello';
```
That might seem shorter, but it's recommended, for the sake of beter readability, to use a single line per variable.
That might seem shorter, but it's not recommended. For the sake of beter readability, please use a single line per variable.
The rewritten code is a bit longer, but easier to read:
@ -64,6 +65,24 @@ let age = 25;
let message = 'Hello';
```
Some people also write many variables like that:
```js no-beautify
let user = 'John',
age = 25,
message = 'Hello';
```
...Or even in comma-first style:
```js no-beautify
let user = 'John'
,age = 25
,message = 'Hello';
```
Technically, all these variants do the same. So, it's a matter of personal taste and aestetics.
````smart header="`var` instead of `let`"
In older scripts you may also find another keyword: `var` instead of `let`:
@ -231,9 +250,9 @@ myBirthday = '01.01.2001'; // error, can't reassign the constant!
When a programmer is sure that the variable should never change, he can use `const` to guarantee it, and also to clearly show that fact to everyone.
### Uppercases constants
### Uppercase constants
There is a widespread practice to use constants as aliases for difficult-to-remember and hard-coded prior to execution values.
There is a widespread practice to use constants as aliases for difficult-to-remember values that are known prior to execution.
Such constants are named using capitals and underscores.
@ -250,10 +269,7 @@ let color = COLOR_ORANGE;
alert( color ); // #FF7F00
```
`COLOR_ORANGE` is much easier to understand and remember than `"#FF7F00"`. Also it is much easier to make a typo in `"#FF7F00"` than in `COLOR_ORANGE`.
The upper case is only used for constants that are "hard-coded" (written in the code before its execution).
`COLOR_ORANGE` is much easier to remember than `"#FF7F00"`. Also it is much easier to mistype in `"#FF7F00"` than in `COLOR_ORANGE`. And when reading the code -- `COLOR_ORANGE` is much more meaningful.
## Name things right

View file

@ -49,7 +49,7 @@ Besides regular numbers there are so-called "special numeric values" which also
```smart header="Mathematical operations are safe"
Doing maths is safe in JavaScript. We can do anything: divide by zero, treat non-numeric strings as numbers, etc.
The script will never die. At worst we'll get `NaN` as the result.
The script will never stop ("die") on that. At worst we'll get `NaN` as the result.
```
Special numeric values formally belong to the "number" type. Of course they are not numbers in a common sense of this word.
@ -175,9 +175,9 @@ A `key` is a string, `value` can be anything.
For instance, here we create a `user` object with two properties:
```js
let user = {
name: "John",
age: 30
let user = { // an object
name: "John", // key "name" has value "John"
age: 30 // key "age" has value 30
};
```
@ -209,21 +209,22 @@ delete user.age;
![user object 3](object-user-delete.png)
If the string which denotes the key (also called a "property name") has multiple words, then we should use square brackets notation to access it:
```js
user["likes to swim?"] = true;
```
See, the dot requires the property name to be a valid variable identifier. That is: no spaces and other limitations. Square brackets work with any string.
If the string which denotes the key (also called a "property name") has multiple words, then the dot notation won't work:
```js
// this would give a syntax error
user.likes to swim? = true;
```
Another powerful feature of square bracket notation is that they allow to access a property by the name from the variable:
That's because, the dot requires the property name to be a valid variable identifier. That is: no spaces and other limitations.
There's a "square bracket notation" that works with any string:
```js
user["likes to swim?"] = true;
```
Square brackets are also the way to access a property by the name from the variable:
```js
let key = "likes to swim?";
@ -234,10 +235,41 @@ Here we have a variable `key` which contains the property name, probably evaluat
Most of time, the dot is used to access object properties, but when we need a complex property name or to pass the name as a variable, then -- we go square brackets.
Javascript supports object inheritance. There are many types that are based on objects: `Date` for dates, `Array` for ordered data, `Error` for error-reporting and so on. So the word "object" is applicable to a variety of things. The term *plain objects* or just `Object` (capital first) is used to represent "basic" objects, the ones we create with `{ ... }`.
What we've just seen is called a "plain object", or just `Object`.
There are many other kinds of objects in Javascript:
- `Array` to store ordered data collections,
- `Date` to store the information about the date and time,
- `Error` to store the information about an error.
- ...And so on.
Sometimes people say something like "Array type" or "Date type", but formally they are not types of their own, but belong to a single "object" data type. And they extend it in various ways.
Objects in JavaScript are very powerful. Here we've just scratched the surface of the topic that is really huge. We'll be closely working with objects and learning more about them in further parts of the tutorial.
````smart header="A trailing comma"
[todo: move to 4-object ?]
Experienced developers sometimes add one more comma to the end of an object, like this:
```js
let user = {
name: "John",
age: 30,
isAdmin: true*!*,*/!* // extra comma
};
```
That's called a "trailing comma" and is allowed by the language.
Sometimes the reason is pure lazyness: when in the development process the last property becomes unneeded and is removed, the programmer forgets to delete the comma at the end of newly last one.
But from the other side that "lazyness" is justified, because the same line can be safely moved between objects -- from the first position to the middle or to the last -- without bookkeeping commas. That's a good thing.
Actual decision whether to add trailing commas or not depends on you. Some people like them, some find them ugly.
````
## Arrays
As weve just seen, objects in Javascript store arbitrary keyed values.
@ -271,7 +303,7 @@ Please note that arrays do not form a separate language type. They are based on
## Symbol type
The `symbol` type is used in conjunction with objects. Probably we won't need them any time soon, but it's the 7th and the last type of the language, so we must mention it here for the sake of completeness.
The `symbol` type is used in conjunction with objects. Probably we won't need them soon, but it's the 7th and the last type of the language, so we must mention it here for the sake of completeness.
A "symbol" represents an unique identifier with a given name.
@ -288,12 +320,14 @@ Symbols in JavaScript are different from symbols in Ruby language (if you are fa
let id1 = Symbol("id");
let id2 = Symbol("id");
*!*
alert(id1 == id2); // false
*/!*
```
Symbols is a special primitive type used for identifiers, which are guaranteed to be unique. So, even if we create many symbols with the same name, they are still unique.
The use case for symbols is to create "concealed" properties of an object, which only make sense locally, that no other part of code can occasionally access or overwrite.
The use case for symbols is to create "concealed" properties of an object, that no other part of code can occasionally access or overwrite.
For instance, if we want to store an "identifier" for the object `user`, we can create a symbol with the name `id` for it:
@ -309,7 +343,7 @@ Now let's imagine that another script wants to have his own "id" property inside
No problem. It can create its own `Symbol("id")`. There will be no conflict, because symbols are always different, even if they have the same name.
Please note that in the same case if we used a string `"id"` instead of a symbol here, then there would be a conflict:
Please note if we used a string `"id"` instead of a symbol for the same purpose, then there could be a conflict:
```js run
let user = { name: "John" };
@ -330,16 +364,16 @@ Symbols are widely used by the JavaScript language itself to store "system" prop
## The typeof operator [#type-typeof]
The `typeof` operator returns the type of the argument. It's handy in the case when we want to process values of different types differently, or just to make a quick check.
The `typeof` operator returns the type of the argument. It's useful when we want to process values of different types differently, or just want to make a quick check.
It allows two forms of syntax:
It supports two forms of syntax:
1. As an operator: `typeof x`.
2. Function style: `typeof(x)`.
In other words, it works both with the brackets or without them. They result is the same.
In other words, it works both with the brackets or without them. The result is the same.
The result of `typeof x` is a string, which has the type name:
The call to `typeof x` returns a string, which has the type name:
```js
typeof undefined // "undefined"
@ -367,10 +401,10 @@ typeof alert // "function" (3)
*/!*
```
Please note the last lines.
The last three lines may be a little unobvious so here's explanations:
1. The array is not a type of its own, but a subtype of object, that's why `typeof []` is `"object"`.
2. The result of `typeof null` equals to `"object"`. That's wrong. It is an officially recognized error in `typeof` implementation, kept for compatibility. Of course, `null` is not an object. It is a special value with a separate type of its own.
1. The array is not a type of its own, it is based on object, that's why `typeof []` is `"object"`.
2. The result of `typeof null` equals to `"object"`. That's wrong. It is an officially recognized error in `typeof`, kept for compatibility. Of course, `null` is not an object. It is a special value with a separate type of its own. So, again, that's an error in the language.
3. The result of `typeof alert` is `"function"`, because `alert` is a function of the language. We'll study functions in the near future and see that actually functions belong to the object type. But `typeof` treats them differently. That's very convenient in practice.

View file

@ -101,35 +101,45 @@ alert( '1' + 2 ); // '12' (string to the left)
alert( 1 + 2 ); // 3, numbers (for the contrast)
```
That only happens when one of arguments is a string, in other cases values are converted to numbers.
That only happens when one of arguments is a string. Otherwise values are converted to numbers.
````
## ToBoolean
Boolean conversion is the simplest one.
It happens in logical operations (later we'll meet `if` tests and other kinds), but also can be performed manually with the call of `Boolean(value)`.
It happens in logical operations (later we'll meet condition tests and other kinds), but also can be performed manually with the call of `Boolean(value)`.
The conversion rule:
- Values that are intuitively "empty", like `0`, an empty string, `null`, `undefined` and `NaN` become `false`.
- Other values become `true`.
For instance:
```js run
alert( Boolean(1) ); // true
alert( Boolean(0) ); // false
alert( Boolean("hello") ); // true
alert( Boolean("") ); // false
```
````warn header="Please note: the string with zero `\"0\"` is `true`"
Some languages (namely PHP) treat `"0"` as `false`. But in JavaScript a non-empty string is always `true`.
```js run
alert( Boolean("0") ); // true
alert( Boolean(" ") ); // any non-empty string, even whitespaces are true
alert( Boolean(" ") ); // also true (any non-empty string is true)
```
````
````warn header="Empty objects and arrays are truthy"
````warn header="Empty objects are truthy"
All objects become `true`:
```js run
alert( Boolean([]) ); // true
alert( Boolean({}) ); // true
alert( Boolean([]) ); // true, even if empty array
alert( Boolean({}) ); // true, even if empty object
```
````
@ -138,9 +148,9 @@ alert( Boolean({}) ); // true
A string or numeric conversion of an object is a two-stage process. The object is first converted to a primitive value, and then ToString/ToNumber rules are applied to it.
The conversion is customizable on a per-object basis, so we'd better deal with it later when we know more about objects.
The conversion is customizable on a per-object basis, so we'll study it later when we go deeper into objects. [todo in the chapter?]
For now, let's just see two common rules that we often meet when showing objects.
Examples:
- When a plain object is converted into a string, is becomes `[object Object]`:
@ -160,8 +170,8 @@ For now, let's just see two common rules that we often meet when showing objects
We'll return to it in the chapter [todo].
```smart header="It was only about ToString/ToNumber"
For ToBoolean, there is no complexity neither customizability.
```smart header="ToBoolean is always true"
Here `ToBoolean` was not mentioned, because it provides no customizability for objects
The rule is simple: all objects are truthy.
```

View file

@ -214,6 +214,28 @@ alert( 8 % 3 ); // 2 is a remainder of 8 divided by 3
alert( 6 % 3 ); // 0 is a remainder of 6 divided by 3
```
## Exponentiation **
The exponentiation operator `**` is a recent addition to the language. Not all browsers support it yet.
For a natural number `b`, the result of `a ** b` is `a` multiplied by itself `b` times.
For instance:
```js run
alert( 2 ** 2 ); // 4 (2 * 2)
alert( 2 ** 3 ); // 8 (2 * 2 * 2)
alert( 2 ** 4 ); // 16 (2 * 2 * 2 * 2)
```
The operator works with non-integer numbers of `a` and `b` as well, for instance:
```js run
alert( 4 ** (1/2) ); // 2 (square root of 4)
alert( 8 ** (1/3) ); // 2 (cubic root of 8)
```
## Increment/decrement: ++, --
Increasing or decreasing a number by one is among the most common numerical operations.
@ -341,7 +363,7 @@ The list of operators:
- RIGHT SHIFT ( `>>` )
- ZERO-FILL RIGHT SHIFT ( `>>>` )
These operators are used very rarely. To understand them, we should delve into low-level number representation, and it would not be optimal to do that right now. Especially because we won't need them any time soon. If you're curious, you can read the [Bitwise Operators](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators) article in MDN. But it would be more practical to return to this topic later when a real need arises.
These operators are used very rarely. To understand them, we should delve into low-level number representation, and it would not be optimal to do that right now. Especially because we won't need them any time soon. If you're curious, you can read the [Bitwise Operators](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators) article in MDN. But it would be more practical to do that when a real need arises.
## Modify-in-place

View file

@ -144,31 +144,33 @@ Let's see more corner cases.
There's a non-intuitive behavior when `null` or `undefined` is compared with other values.
- For a strict equality check `===` these values are different, because each of them belong to a separate type of it's own.
- For a non-strict check `null == undefined`, there's a special rule. These two are a "sweet couple". They equal each other (in the sense of `==`), but no any other value.
For a strict equality check `===`
: These values are different, because each of them belong to a separate type of it's own.
- For maths and evaluation of other comparisons including `<`, `>`, `<=`, `>=`, values `null/undefined` are converted to a number: `null` becomes `0`, while `undefined` becomes `NaN`.
For a non-strict check `null == undefined`
: There's a special rule. These two are a "sweet couple": they equal each other (in the sense of `==`), but no any other value.
Now let's see funny things that happen when we apply those rules. And then, later, how do not fall into a trap with unobvious language features.
For maths and evaluation of other comparisons `< > <= >=`
: Values `null/undefined` are converted to a number: `null` becomes `0`, while `undefined` becomes `NaN`.
Now let's see funny things that happen when we apply those rules. And, what's more important, how do not fall into a trap with unobvious language features.
### Strange result: null vs 0
Let's compare `null` with a zero:
```js run
alert( null > 0 ); // false
alert( null == 0 ); // false
alert( null >= 0 ); // *!*true*/!*
alert( null > 0 ); // (1) false
alert( null == 0 ); // (2) false
alert( null >= 0 ); // (3) *!*true*/!*
```
Yeah, mathematically that's strange. The last result states that "`null` is equal or greater than zero". Then one of the comparisons above must be correct, but they are both falsy.
Well, every programmer language has its own unobvious features. That was an example for Javascript.
The reason is that an equality check `==` and comparisons `> < >= <=` work differently. Comparisons convert `null` to a number, hence treat it as `0`. That's why (1) `null >= 0` is true and (3) `null > 0` is false.
The reason is that an equality check `==` and comparisons `> < >= <=` work differently. Comparisons convert `null` to a number, hence treat it as `0`. That's why `null >= 0` is true and `null > 0` is false.
From the other hand, equality `==` for `undefined` and `null` works without any conversions. There's just a rule that they equal each other and don't equal anything else. That's why `null == 0` is false.
From the other hand, the equality check `==` for `undefined` and `null` works by the rule, without any conversions. They equal each other and don't equal anything else. That's why (2) `null == 0` is false.
### An uncomparable undefined
@ -195,19 +197,6 @@ Just treat any comparison with `undefined/null` except the strict equality `===`
Don't use comparisons `>= > < <=` with a variable which may be `null/undefined`, unless you are really sure what you're doing. If a variable can have such values, then check it separately.
## Comparison with objects
For equality checks two objects are always treated as non-equal.
```js run
alert( {} == {} ); // false
alert( [] == [] ); // false
```
Note that Javascript does not try to compare the objects by content. The rule is simple: different objects are not equal.
For other comparisons like `< >` operators, or when an object is compared with a primitive, objects are first converted to primitives, and then the comparison runs as usual.
## Summary
- Comparison operators return a logical value.

View file

@ -2,6 +2,8 @@
This chapter covers basic UI operations: `alert`, `prompt` and `confirm`. They allow to ask a visitor for the input and show the results.
They are browser-specific. For other environments like Node.JS there are other ways of getting the information. Also they are very simple, so we can use them for the start.
[cut]
## alert

View file

@ -3,7 +3,7 @@
Objects in JavaScript combine two functionalities.
1. First -- they are "associative arrays": a structure for storing keyed data.
2. Second -- they provide features for object-oriented programming.
2. Second -- they provide features for object-oriented programming like inheritance.
Here we concentrate on the first part: using objects as a data store, and we will study it in-depth. That's the required base for studying the second part.
@ -41,14 +41,26 @@ let user = {
![](object-user-props.png)
````smart header="Trailing comma"
The last property may end with a comma:
```js
let user = {
name: "John",
age: 30*!*,*/!*
}
```
That is called a "trailing" or "hanging" comma. Makes it easier to add/move/remove properties, because all lines become alike.
````
## Accessing a property
To access a property, there are two syntaxes:
- The dot notation: `user.name`
- Square brackets: `user["name"]`
Square brackets are more powerful, because they allow to specify arbitrary string as a property name. In contrast, the dot notation requires the nae to be a valid variable identifier, that is: no spaces, special chracters etc.
But more than that, square brackets is the only choice when the name of the property is in a variable.
The dot notation requires the name to be a valid variable identifier, that is: no spaces, special chracters etc. Square brackets are more powerful, because they allow to specify an arbitrary string as a property name. Also, square brackets is the only choice when the name of the property is in a variable.
For instance:
@ -60,12 +72,13 @@ let user = {
let key = prompt("What do you want to know about the user?", "name");
alert( user[key] ); // John (if enter "name"), 30 for the "age"
// access by variable
alert( user[key] ); // John (if enter "name")
```
The square brackets literally say: "take the property name from the variable".
The square brackets mean: "take the property name from the variable".
Also it is handy to use square brackets in an object literal, when the property name is stored in a variable.
Square brackets also can be used in an object literal.
That's called a *computed property*:
@ -73,7 +86,7 @@ That's called a *computed property*:
let fruit = prompt("Which fruit to buy?", "apple");
let bag = {
[fruit]: 5,
[fruit]: 5, // the name of the property is taken from the variable fruit
};
alert( bag.apple ); // 5 if fruit="apple"
@ -107,21 +120,12 @@ let obj = {
0: "test" // same as "0": "test"
}
// bot alerts access the same property (the number 0 is converted to string "0")
alert( obj["0"] ); // test
alert( obj[0] ); // test (same property)
```
````
````smart header="Trailing comma"
The last property may end with a comma:
```js
let user = {
name: "John",
age: 30*!*,*/!*
}
```
That is called a "trailing" or "hanging" comma. Makes it easier to add/move/remove property, because all lines become alike.
````
````smart header="Reserved words are allowed as property names"
A variable cannot have a name equal to one of language-reserved words like "for", "let", "return" etc.
@ -138,7 +142,9 @@ let obj = {
alert( obj.for + obj.let + obj.return ); // 6
```
Basically, any name is allowed. With one exclusion. There's a built-in property named `__proto__` with a special functionality (we'll cover it later), which can't be set to a non-object value:
Basically, any name is allowed, with one exclusion: `__proto__`.
The built-in property named `__proto__` has a special functionality (we'll cover it later), and it can't be set to a non-object value:
```js run
let obj = {};
@ -146,7 +152,7 @@ obj.__proto__ = 5;
alert(obj.__proto__); // [object Object], didn't work as intended
```
If we want to store *arbitrary* (user-provided) keys, then this can be a source of bugs. There's another data structure [Map](info:map-set-weakmap-weakset), that we'll learn in a few chapters, it can support arbitrary keys.
As you we see from the code, an assignment to a primitive is ignored. If we want to store *arbitrary* (user-provided) keys, then such behavior can be the source of bugs and even vulnerabilities, because it's unexpected. There's another data structure [Map](info:map-set-weakmap-weakset), that we'll learn in the chapter <info:map-set-weakmap-weakset>, which supports arbitrary keys.
````
## Removing a property
@ -194,23 +200,25 @@ let key = "age";
alert( key in user ); // true, takes the value of key and checks for such property
```
````smart header="The property which equals `undefined`"
Thee is a case when `"=== undefined"` check fails, but the `"in"` operator works correctly.
````smart header="Using \"in\" for properties that store `undefined`"
There is a case when `"=== undefined"` check fails, but the `"in"` operator works correctly.
It's when an object property stores `undefined`:
```js run
let obj = { test: undefined };
let obj = {
test: undefined
};
alert( obj.test ); // undefined, no such property?
alert( "test" in obj ); // true, the property does exist!
```
In the code above, the property `obj.test` stores `undefined`, so the first check fails.
But the `in` operator puts things right.
Situations like this happen very rarely, because `undefined` is usually not assigned. We mostly use `null` for unknown values. So the `in` operator is an exotic guest in the code.
In the code above, the property `obj.test` technically exists. So the `in` operator works right.
Situations like this happen very rarely, because `undefined` is usually not assigned. We mostly use `null` for "unknown" or "empty" values. So the `in` operator is an exotic guest in the code.
````
## Property shorthands
@ -245,7 +253,6 @@ Methods definitions
};
```
To say the truth, these notations are not fully identical. There are subtle differences related to object inheritance (to be covered later), but for now they do not matter.
## Loops
@ -395,12 +402,37 @@ alert(*!*user.name*/!*); // 'Pete', changes are seen from the "user" reference
Quite obvious, if we used one of the keys (`admin`) and changed something inside the cabinet, then if we use another key later (`user`), we find things modified.
### Comparison with objects
## Cloning objects
Two objects are equal only when they are one object:
```js run
let a = {};
let b = a; // copy the reference
alert( a == b ); // true, both variables reference the same object
```
We can also think of it like: the variables are "papers with address" of the objects. We copied the address from `a` to `b`. Then when we compare `a == b`, we compare the adresses. If they match, the equality is truthy.
In all other cases objects are non-equal, even if their content is the same.
For instance:
```js run
let a = {};
let b = {}; // two independents object
alert( a == b ); // false
```
For unusual equality checks like: object vs a priimtive, or an object less/greater `< >` than another object, objects are converted to numbers. To say the truth, such comparisons occur very rarely in real code and usually are a result of a mistake.
## Cloning, Object.assign
What if we need to duplicate an object? Create an independant copy, a clone?
That's also doable, but a little bit more difficult, because there's no such method in Javascript. Frankly, that's very rarely needed.
That's also doable, but a little bit more difficult, because there's no such method in Javascript. Actually, copying by reference is good most of the time.
But if we really want that, then we need to create a new object and replicate the structure of the existing one by iterating over its properties and copying them on the primitive level.
@ -498,14 +530,14 @@ Now it's not enough to copy `clone.sizes = user.sizes`, because the `user.sizes`
To fix that, we should examine the value of `user[key]` in the cloning loop and if it's an object, then replicate it's structure as well. That is called a "deep cloning".
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 from the Javascript library [lodash](https://lodash.com). The method is [_.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
Are objects ordered? If we loop over an object, do we get all properties in the same order that they are in the `{...}` definition?
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 answer is "yes" for non-numeric properties, "no" for others.
The short answer is: "no" for integer properties, "yes" for others. The details follow.
As an example, let's consider an object with the phone codes:
@ -519,29 +551,40 @@ let codes = {
};
*!*
for(let code in codes) alert(code); // 1, 41, 44, 49
for(let code in codes) {
alert(code); // 1, 41, 44, 49
}
*/!*
```
The object is used to generate HTML `<select>` list. If we're making a site mainly for German audience then we probably want `49` to be the first.
The object may be used to suggest a list of options to the user. If we're making a site mainly for German audience then we probably want `49` to be the first.
But if we try to loop over the object, we see a totally different picture:
But if we run the code, we see a totally different picture:
- USA (1) goes first
- then Switzerland (41) and so on.
That's because according to the language stantard objects have no order. The loop is officially allowed to list properties randomly.
That's because the iteration order is:
But in practice, there's a de-facto agreement among modern JavaScript engines.
1. Integer properties in the ascending sort order go first.
2. String properties in the orders of their creation.
3. Symbol properties in the order of their creation.
- The numeric properties are sorted.
- Non-numeric properties are ordered as they appear in the object.
The phone codes were sorted, because they are integer. That's why we see `1, 41, 44, 49`.
That agreement is not enforced by a standard, but stands strong, because a lot of JavaScript code is already based on it.
````smart header="Integer properties? What's that?"
By specification object property names are either strings or symbols. So an "integer property" actually means a string that can be converted to-from integer without a change.
Now it's easy to see that the properties were iterated in the ascending order, because they are numeric... Of course, object property names are strings, but the Javascript engine detects that it's a number and applies internal optimizations to it, including sorting. That's why we see `1, 41, 44, 49`.
So, "49" is an integer string, because when it's transformed to an integer number and back, it's still the same. But "+49" and "1.2" are not:
On the other hand, if the keys are non-numeric, then they are listed as they appear, for instance:
```js run
alert( String(Math.trunc(Number("49"))) ); // "49", same, integer property
alert( String(Math.trunc(Number("+49"))) ); // "49", not same ⇒ not integer property
alert( String(Math.trunc(Number("1.2"))) ); // "1", not same ⇒ not integer property
```
````
On the other hand, if the keys are non-integer, then they are listed as they appear, for instance:
```js run
let user = {
@ -551,14 +594,14 @@ let user = {
user.age = 25; // add one more
*!*
// as they appear in the object
// non-integer properties are listed in the creation order
*/!*
for (let prop in user) {
alert( prop ); // name, surname, age
}
```
So, to fix the issue with the phone codes, we can "cheat" by making the codes non-numeric. Adding a plus `"+"` sign before each code is enough.
So, to fix the issue with the phone codes, we can "cheat" by making the codes non-integer. Adding a plus `"+"` sign before each code is enough.
Like this:
@ -572,8 +615,7 @@ let codes = {
};
for(let code in codes) {
// explicitly convert each code to a number if required
alert( +code ); // 49, 41, 44, 1
alert( +code ); // 49, 41, 44, 1
}
```
@ -599,8 +641,8 @@ Property access:
- `for([key,value] of Object.entries(obj))` for both.
- Ordering:
- Non-numeric properties keep the order.
- Numeric properties are sorted. To keep the order for numeric properties, we can prepend them with `+` to make them look like non-numeric.
- Integer properties in sorted order first, then strings in creation order, then symbols in creation order.
- To keep the order for numeric properties, we can prepend them with `+` to make them look like non-numeric.
- Objects are assigned and copied by reference.

View file

@ -22,10 +22,12 @@ readMessages.add(messages[0]);
alert("Read message 0: " + readMessages.has(messages[0]));
messages.shift();
// now readMessages has 1 element (or will be cleaned soon)
// now readMessages has 1 element (technically memory may be cleaned later)
```
The `WeakSet` allows to store a set of messages and easily check for the existance of a message in it.
It cleans up itself automatically. The tradeoff is that we can't iterate over it. That's not needed here though.
P.S. `WeakSet` stores one bit of information: "was it read" (yes/no - in set/out of set). To store a date, we can use `WeakMap`.

View file

@ -16,6 +16,8 @@ let messages = [
Your code can access it, but the messages are managed by someone else's code. New messages are added, old ones are removed regularly by that code, and you don't know the exact moments when it happens.
Now, which data structure you could use to store only messages that are "have been read"? The structure must be well-suited to give the answer "was it read?" for the given message.
Now, which data structure you could use to store only messages that are "have been read"? The structure must be well-suited to give the answer "was it read?" for the given message object.
P.S. When a message is removed from the collection, it should disappear from your structure as well.
P.P.S. What you'd use to store the information about "when it was read?"

View file

@ -246,7 +246,7 @@ For instance:
```js
let john = { name: "John" };
// the object can be accessed, john is the reference
// the object can be accessed, john is the reference to it
// overwrite the reference
john = null;
@ -254,62 +254,93 @@ john = null;
// the object will be removed from memory
```
We'll go into more details later, but the gist is somewhat obvious, right? If nothing references the object, it can be safely removed.
We'll go into more details later, but the example here is somewhat obvious, right? If nothing references the object, it can be safely removed.
Usually, if an object is in a set or an array or another data structure, and the data structure is in memory, then the object remains in memory too. With the exception of `WeakMap/WeakSet`.
**If an object only exists in `WeakMap/WeakSet`, then it is removed from the memory.**
**`WeakMap/WeakSet` does not prevent the object removal from the memory.**
That's handy for situations when we have a main storage for the objects somewhere and need to keep additional data for them that only exists while the object exists.
For instance, let's start with `WeakMap`. The first difference from `Map` is that its keys can only be objects, not primitive values:
For instance, we have a code that keeps a visit count for each user. And when the user leaves, we don't need to store his visit count any more.
One way would be to keep track of leaving users and cleaning up the storage manually.
```js run
let weakMap = new WeakMap();
Another way would be to use `WeakMap`:
let obj = {};
weakMap.set(obj, "ok"); // works fine (object key)
*!*
weakMap.set("test", "wops"); // Error, because "test" is a primitive
*/!*
```
So it stores data for objects. And the data only exists while the object exists. That's handy for situations when we have a main storage for the objects somewhere and need to keep additional information that is only relevant while the object lives.
Let's see an example.
For instance, we have a code that keeps a visit count for each user. The information is stored in a map: a user is the key and the visit count is the value. When a user leaves, we don't want to store his visit count any more.
One way would be to keep track of leaving users and clean up the storage manually:
```js run
let john = { name: "John" };
// user => visits count
let visitsCountMap = new WeakMap();
// map: user => visits count
let visitsCountMap = new Map();
// john is the key for the map
visitsCountMap.set(john, 123);
alert( visitsCountMap.get(john) ); // 123
// now john leaves us, we don't need him any more
john = null;
*!*
alert( visitsCountMap.get(john) ); // undefined
// but it's still in the map, we need to clean it!
*/!*
alert( visitsCountMap.size ); // 1
// it's also in the memory, because Map uses it as the key
```
Did you notice the difference versus a regular `Map`? If `visitorCountMap` were a `new Map()`, then the object `john` and the corresponding value would remain in it. If users keep coming and leaving, then there would be more and more values flooding the map.
Another way would be to use `WeakMap`:
So, with a regular `Map`, the user deletion becomes a more tedious task: we also need to clean up the additional stores. And it can become rather cumbersome in more complex cases when users are managed in one place of the code and the additional structure is at another place and is getting no information about removals.
```js
let john = { name: "John" };
`WeakMap` uses only objects as keys, not primitive values. It has the following methods:
let visitsCountMap = new WeakMap();
visitsCountMap.set(john, 123);
// now john leaves us, we don't need him any more
john = null;
// there are no references except WeakMap,
// so the object is removed both from the memory and from visitsCountMap automatically
```
With a regular `Map`, the user deletion becomes a tedious task: we not only need to remove the user from it's main storage (be it a variable or an array), but also need to clean up the additional stores like `visitsCountMap`. And it can become cumbersome in more complex cases when users are managed in one place of the code and the additional structure is at another place and is getting no information about removals.
`WeakMap` can make things simpler, because it is cleaned up automatically.
`WeakMap` has only the following methods:
- `weakMap.get(key)`
- `weakMap.set(key, value)`
- `weakMap.delete(key, value)`
- `weakMap.has(key)`
`WeakMap` does not support methods `clear()` and has no `size` property. Also we can not iterate over it.
Please note `WeakMap` does not support methods `clear()` and has no `size` property. Also we can not iterate over it.
That's for technical reasons. Once a key is no longer referenced from anywhere, it is removed from the `WeakMap` and from the memory. But technically it's not exactly specified *when the removal happens*.
That's for technical reasons. If the object is to be removed (like `john` in the code above), then it `WeakMap` will be cleaned up automatically. But technically it's not exactly specified *when the cleanup happens*.
The JavaScript engine decides that. It may choose to perform the memory cleanup immediately or wait and do the cleaning later when more deletions happen. So, technically the current element count of the `WeakMap` is not known. The engine may have cleaned it up or not, or did it partially. For that reason, the methods that access the `WeakMap` as a whole are not supported.
The JavaScript engine decides that. It may choose to perform the memory cleanup immediately or to wait and do the cleaning later when more deletions happen. So, technically the current element count of the `WeakMap` is not known. The engine may have cleaned it up or not, or did it partially. For that reason, the methods that access the `WeakMap` as a whole are not supported.
The same refers to `WeakSet`.
- It keeps a set of objects, an object exists while it is referenced from anywhere else.
- It only supports `add`, `has` and `delete`, no support for `size` and no iterations.
- Like `Set`, it supports `add`, `has` and `delete`, but not `size` and no iterations.
The limitations may appear inconvenient, but they actually do not prevent `WeakMap/WeakSet` from doing the main task -- be an "additional" storage of data for objects which are stored/managed at another place.
The limitations may appear inconvenient, but they actually do not prevent `WeakMap/WeakSet` from doing their main job -- be an "additional" storage of data for objects which are stored/managed at another place.
## Summary
@ -319,11 +350,12 @@ The limitations may appear inconvenient, but they actually do not prevent `WeakM
- Any keys, objects can be keys.
- Iterates in the insertion order.
- Additional convenient methods for iterating and cleaning, the `size` property.
- Additional convenient methods, the `size` property.
- `Set` -- is a collection of unique values.
- Unlike an array, does not allow to reorder elements. Keeps the insertion order.
- Unlike an array, does not allow to reorder elements.
- Keeps the insertion order.
- `WeakMap` -- a variant of `Map` that allows only objects as keys and removes them once they become unaccessible by other means.
@ -331,5 +363,7 @@ The limitations may appear inconvenient, but they actually do not prevent `WeakM
- `WeakSet` -- is a variant of `Set` that only stores objects and removes them once they become unaccessible by other means.
`WeakMap` and `WeakSet` are used as "secondary" data structures in additional to the "main" object storage. Once the object is removed from the main storage, they clean up aumatically.
- Also does not support `size/clear()` and iterations.
`WeakMap` and `WeakSet` are used as "secondary" data structures in additional to the "main" object storage. Once the object is removed from the main storage, so it only stays in `WeakMap/WeakSet`, they clean up aumatically.

View file

@ -0,0 +1,42 @@
Решение **с использованием цикла**:
```js run
function sumTo(n) {
var sum = 0;
for (var i = 1; i <= n; i++) {
sum += i;
}
return sum;
}
alert( sumTo(100) );
```
Решение через **рекурсию**:
```js run
function sumTo(n) {
if (n == 1) return 1;
return n + sumTo(n - 1);
}
alert( sumTo(100) );
```
Решение **по формуле**: `sumTo(n) = n*(n+1)/2`:
```js run
function sumTo(n) {
return n * (n + 1) / 2;
}
alert( sumTo(100) );
```
P.S. Надо ли говорить, что решение по формуле работает быстрее всех? Это очевидно. Оно использует всего три операции для любого `n`, а цикл и рекурсия требуют как минимум `n` операций сложения.
Вариант с циклом -- второй по скорости. Он быстрее рекурсии, так как операций сложения столько же, но нет дополнительных вычислительных затрат на организацию вложенных вызовов.
Рекурсия в данном случае работает медленнее всех.
P.P.S. Существует ограничение глубины вложенных вызовов, поэтому рекурсивный вызов `sumTo(100000)` выдаст ошибку.

View file

@ -0,0 +1,34 @@
importance: 5
---
# Вычислить сумму чисел до данного
Напишите функцию `sumTo(n)`, которая для данного `n` вычисляет сумму чисел от 1 до `n`, например:
```js no-beautify
sumTo(1) = 1
sumTo(2) = 2 + 1 = 3
sumTo(3) = 3 + 2 + 1 = 6
sumTo(4) = 4 + 3 + 2 + 1 = 10
...
sumTo(100) = 100 + 99 + ... + 2 + 1 = 5050
```
Сделайте три варианта решения:
1. С использованием цикла.
2. Через рекурсию, т.к. `sumTo(n) = n + sumTo(n-1)` для `n > 1`.
3. С использованием формулы для суммы [арифметической прогрессии](http://ru.wikipedia.org/wiki/%D0%90%D1%80%D0%B8%D1%84%D0%BC%D0%B5%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B0%D1%8F_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B5%D1%81%D1%81%D0%B8%D1%8F).
Пример работы вашей функции:
```js
function sumTo(n) { /*... ваш код ... */ }
alert( sumTo(100) ); // 5050
```
**Какой вариант решения самый быстрый? Самый медленный? Почему?**
**Можно ли при помощи рекурсии посчитать `sumTo(100000)`? Если нет, то почему?**

View file

@ -0,0 +1,23 @@
По свойствам факториала, как описано в условии, `n!` можно записать как `n * (n-1)!`.
То есть, результат функции для `n` можно получить как `n`, умноженное на результат функции для `n-1`, и так далее до `1!`:
```js run
function factorial(n) {
return (n != 1) ? n * factorial(n - 1) : 1;
}
alert( factorial(5) ); // 120
```
Базисом рекурсии является значение `1`. А можно было бы сделать базисом и `0`. Тогда код станет чуть короче:
```js run
function factorial(n) {
return n ? n * factorial(n - 1) : 1;
}
alert( factorial(5) ); // 120
```
В этом случае вызов `factorial(1)` сведётся к `1*factorial(0)`, будет дополнительный шаг рекурсии.

View file

@ -0,0 +1,31 @@
importance: 4
---
# Вычислить факториал
*Факториа́л числа* -- это число, умноженное на "себя минус один", затем на "себя минус два" и так далее, до единицы. Обозначается `n!`
Определение факториала можно записать как:
```js
n! = n * (n - 1) * (n - 2) * ...*1
```
Примеры значений для разных `n`:
```js
1! = 1
2! = 2 * 1 = 2
3! = 3 * 2 * 1 = 6
4! = 4 * 3 * 2 * 1 = 24
5! = 5 * 4 * 3 * 2 * 1 = 120
```
Задача -- написать функцию `factorial(n)`, которая возвращает факториал числа `n!`, используя рекурсивный вызов.
```js
alert( factorial(5) ); // 120
```
Подсказка: обратите внимание, что `n!` можно записать как `n * (n-1)!`. Например: `3! = 3*2! = 3*2*1! = 6`

View file

@ -0,0 +1,93 @@
# Вычисление рекурсией (медленное)
Решение по формуле, используя рекурсию:
```js run
function fib(n) {
return n <= 1 ? n : fib(n - 1) + fib(n - 2);
}
alert( fib(3) ); // 2
alert( fib(7) ); // 13
// fib(77); // не запускаем, подвесит браузер
```
При больших значениях `n` оно будет работать очень медленно. Например, `fib(77)` уже будет вычисляться очень долго.
Это потому, что функция порождает обширное дерево вложенных вызовов. При этом ряд значений вычисляется много раз. Например, посмотрим на отрывок вычислений:
```js no-beautify
...
fib(5) = fib(4) + fib(3)
fib(4) = fib(3) + fib(2)
...
```
Здесь видно, что значение `fib(3)` нужно одновременно и для `fib(5)` и для `fib(4)`. В коде оно будет вычислено два раза, совершенно независимо.
Можно это оптимизировать, запоминая уже вычисленные значения, получится гораздо быстрее. Альтернативный вариант -- вообще отказаться от рекурсии, а вместо этого в цикле начать с первых значений `1`, `2`, затем из них получить `fib(3)`, далее `fib(4)`, затем `fib(5)` и так далее, до нужного значения.
Это решение будет наиболее эффективным. Попробуйте его написать.
# Алгоритм вычисления в цикле
Будем идти по формуле слева-направо:
```js no-beautify
var a = 1, b = 1; // начальные значения
var c = a + b; // 2
/* переменные на начальном шаге:
a b c
1, 1, 2
*/
```
Теперь следующий шаг, присвоим `a` и `b` текущие 2 числа и получим новое следующее в `c`:
```js no-beautify
a = b, b = c;
c = a + b;
/* стало так (ещё число):
a b c
1, 1, 2, 3
*/
```
Следующий шаг даст нам ещё одно число последовательности:
```js no-beautify
a = b, b = c;
c = a + b;
/* стало так (ещё число):
a b c
1, 1, 2, 3, 5
*/
```
Повторять в цикле до тех пор, пока не получим нужное значение. Это гораздо быстрее, чем рекурсия, хотя бы потому что ни одно из чисел не вычисляется дважды.
P.S. Этот подход к вычислению называется [динамическое программирование снизу-вверх](http://ru.wikipedia.org/wiki/%D0%94%D0%B8%D0%BD%D0%B0%D0%BC%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5).
# Код для вычисления в цикле
```js run
function fib(n) {
var a = 1,
b = 1;
for (var i = 3; i <= n; i++) {
var c = a + b;
a = b;
b = c;
}
return b;
}
alert( fib(3) ); // 2
alert( fib(7) ); // 13
alert( fib(77) ); // 5527939700884757
```
Цикл здесь начинается с `i=3`, так как первое и второе числа Фибоначчи заранее записаны в переменные `a=1`, `b=1`.

View file

@ -0,0 +1,24 @@
importance: 5
---
# Числа Фибоначчи
Последовательность [чисел Фибоначчи](http://ru.wikipedia.org/wiki/%D0%A7%D0%B8%D1%81%D0%BB%D0%B0_%D0%A4%D0%B8%D0%B1%D0%BE%D0%BD%D0%B0%D1%87%D1%87%D0%B8) имеет формулу <code>F<sub>n</sub> = F<sub>n-1</sub> + F<sub>n-2</sub></code>. То есть, следующее число получается как сумма двух предыдущих.
Первые два числа равны `1`, затем `2(1+1)`, затем `3(1+2)`, `5(2+3)` и так далее: `1, 1, 2, 3, 5, 8, 13, 21...`.
Числа Фибоначчи тесно связаны с [золотым сечением](http://ru.wikipedia.org/wiki/%D0%97%D0%BE%D0%BB%D0%BE%D1%82%D0%BE%D0%B5_%D1%81%D0%B5%D1%87%D0%B5%D0%BD%D0%B8%D0%B5) и множеством природных явлений вокруг нас.
Напишите функцию `fib(n)`, которая возвращает `n-е` число Фибоначчи. Пример работы:
```js no-beautify
function fib(n) { /* ваш код */ }
alert( fib(3) ); // 2
alert( fib(7) ); // 13
alert( fib(77)); // 5527939700884757
```
**Все запуски функций из примера выше должны срабатывать быстро.**

View file

@ -0,0 +1,277 @@
# Recursion and stack
A function solves a task. In the process it can call many other functions. A partial case of this is when a function calls *itself*.
That's called *a recursion*.
Recursion is useful in situations when a task can be naturally split into several tasks of the same kind, but simpler. Or when a task can be simplified into an easy action plus a simpler variant of the same task.
Soon we'll see examples.
Recursion is a common programming topic, not specific to Javascript. If you developed in other languages or studied algorithms, then you probably already know what's this.
The chapter is mostly meant for those who are unfamiliar with the topic and want to study better how functions work.
[cut]
## Recursive thinking
For something simple to start with -- let's consider a task of raising `x` into a natural power of `n`. In other words, we need `x` multiplied by itself `n` times.
There are two ways to solve it.
1. Iterative thinking -- the `for` loop:
```js run
function pow(x, n) {
let result = 1;
// multiply result by x n times in the loop
for(let i = 0; i < n; i++) {
result *= x;
}
return result
}
alert( pow(2, 3) ); // 8
```
2. Recursive thinking -- simplify the task:
```js run
function pow(x, n) {
if (n == 1) {
return x;
} else {
return x * pow(x, n - 1);
}
}
alert( pow(2, 3) ); // 8
```
The recursive variant represents the function as a simple action plus a simplified task:
```js
if n==1 = x
/
pow(x, n) =
\ = x * pow(x, n - 1)
else
```
Here we have two branches.
1. The `n==1` is the trivial variant. It is called *the base* of recursion, because it immediately produces the obvious result: `pow(x, 1)` equals `x`.
2. Otherwise, we can represent `pow(x, n)` as `x * pow(x, n-1)`. In maths, one would write <code>x<sup>n</sup> = x * x<sup>n-1</sup></code>. This is called *a recursive step*, because we transform the task into a simpler action (multiplication by `x`) and a simpler call of the same task (`pow` with lower `n`). Next steps simplify it further and further untill `n` reaches `1`.
We can also say that `pow` *recursively calls itself* till `n == 1`.
For example, to calculate `pow(2, 4)` the recursive variant does these steps:
1. `pow(2, 4) = 2 * pow(2, 3)`
2. `pow(2, 3) = 2 * pow(2, 2)`
3. `pow(2, 2) = 2 * pow(2, 1)`
4. `pow(2, 1) = 2`
````smart header="Recursion is usually shorter"
A recursive solution is usually shorter.
Here we can write the same using the ternary `?` operator instead of `if` to make it even more terse and still very readable:
```js run
function pow(x, n) {
return (n == 1) ? x : (x * pow(x, n-1));
}
```
````
So, the recursion can be used when a function call can be reduced to a simpler call, and then -- to even more simpler, and so on, until the result becomes obvious.
The maximal number of nested calls (including the first one) is called *recursion depth*. In our case, it there will be exactly `n`.
The maximal recursion depth is limited by Javascript engine. We can make sure about 10000, some engines allow more, but 100000 is probably out of limit for the majority of them. There are automatic optimizations that help to alleviate this ("tail calls optimizations"), but they are not yet supported everywhere and work only in simple cases.
That limits the application of recursion, but it still remains very wide. There are many tasks where a recursive way of thinking gives simpler code, easier to maintain.
## The execution stack
Now let's examine how recursive calls work. For that we'll look under the hood of functions, how they work.
The information about a function run is stored in its *execution context*.
The [execution context](https://tc39.github.io/ecma262/#sec-execution-contexts) is an internal data structure that contains details about where the execution flow is now, current variables, the value of `this` (we don't use it here) and few other internal details.
### pow(2, 3)
For instance, for the beginning of the call `pow(2, 3)` the execution context will store variables: `x = 2, n = 3`, the execution flow is at line `1` of the function.
We can sketch it as:
<ul class="function-execution-context-list">
<li>
<span class="function-execution-context">Context: { x: 2, n: 3, at line 1 }</span>
<span class="function-execution-context-call">pow(2, 3)</span>
</li>
</ul>
That's when the function starts to execute. The condition `n == 1` is falsy, so the flow continues into the second branch of `if`:
```js run
function pow(x, n) {
if (n == 1) {
return x;
} else {
*!*
return x * pow(x, n - 1);
*/!*
}
}
alert( pow(2, 3) );
```
The line changes, so the context is now:
<ul class="function-execution-context-list">
<li>
<span class="function-execution-context">Context: { x: 2, n: 3, at line 5 }</span>
<span class="function-execution-context-call">pow(2, 3)</span>
</li>
</ul>
To calculate `x * pow(x, n-1)`, we need to run `pow` witn new arguments.
### pow(2, 2)
To do a nested call, Javascript remembers the current execution context in the *execution context stack*.
Here we call the same function `pow`, but it absolutely doesn't matter. The process is the same for all functions:
1. The current context is "laid away" on top of the stack.
2. The new context is created for the subcall.
3. When the subcall is finished -- the previous context is popped from the stack, and its execution continues.
The context stack as a list where the current execution context is always on-top, and previous remembered contexts are below:
<ul class="function-execution-context-list">
<li>
<span class="function-execution-context">Context: { x: 2, n: 2, at line 1 }</span>
<span class="function-execution-context-call">pow(2, 2)</span>
</li>
<li>
<span class="function-execution-context">Context: { x: 2, n: 3, at line 5 }</span>
<span class="function-execution-context-call">pow(2, 3)</span>
</li>
</ul>
Here, the new current execution context is also made bold for clarity.
Note that the context of the previous call not only includes the variables, but also the place in the code, so when the new call finishes -- it would be easy to resume. We use a word "line" in the illustrations for that, but of course in fact it's more precise.
### pow(2, 1)
The process repeats: a new subcall is made at line `5`, now with arguments `x=2`, `n=1`.
A new execution context is created, the previous one is pushed on top of the stack:
<ul class="function-execution-context-list">
<li>
<span class="function-execution-context">Context: { x: 2, n: 1, at line 1 }</span>
<span class="function-execution-context-call">pow(2, 1)</span>
</li>
<li>
<span class="function-execution-context">Context: { x: 2, n: 2, at line 5 }</span>
<span class="function-execution-context-call">pow(2, 2)</span>
</li>
<li>
<span class="function-execution-context">Context: { x: 2, n: 3, at line 5 }</span>
<span class="function-execution-context-call">pow(2, 3)</span>
</li>
</ul>
The stack has 2 old contexts now.
### The exit
During the execution of `pow(2, 1)`, unlike before, the condition `n == 1` is truthy, so the first branch of `if` works:
```js
function pow(x, n) {
if (n == 1) {
*!*
return x;
*/!*
} else {
return x * pow(x, n - 1);
}
}
```
There are no more nested calls, so the function finishes, returning `2`.
As the function finishes, its execution context is not needed any more, so it's removed from the memory. The previous one is restored off-top of the stack:
<ul class="function-execution-context-list">
<li>
<span class="function-execution-context">Context: { x: 2, n: 2, at line 5 }</span>
<span class="function-execution-context-call">pow(2, 2)</span>
</li>
<li>
<span class="function-execution-context">Context: { x: 2, n: 3, at line 5 }</span>
<span class="function-execution-context-call">pow(2, 3)</span>
</li>
</ul>
The execution of `pow(2, 2)` is resumed. It has the result of the subcall `pow(2, 1)`, so it also can finish the evaluation of `x * pow(x, n-1)`, returning `4`.
Then the previous context is restored:
<ul class="function-execution-context-list">
<li>
<span class="function-execution-context">Context: { x: 2, n: 3, at line 5 }</span>
<span class="function-execution-context-call">pow(2, 3)</span>
</li>
</ul>
When it finishes, we have a result of `pow(2, 3) = 8`.
The recursion depth in this case was: **3**.
As we can see from the illustrations above, recursion depth equals the maximal number of context in the stack.
Note the memory requirements. Contexts take memory. In our case, raising to the power of `n` actually requires the memory for `n` contexts, for all lower values of `n`.
A loop-based algorithm is much memory-saving:
```js
function pow(x, n) {
let result = 1;
for(let i = 0; i < n; i++) {
result *= x;
}
return result
}
```
The iterative `pow` uses a single context changing `i` and `result` in the process.
**Any recursion can be rewritten as a loop. The loop variant is usually more effective.**
...But sometimes the rewrite can be non-trivial, especially when function uses different recursive subcalls, when the branching is more intricate. And the optimization benefits may be unneeded and totally not worth that.
## Итого
Рекурсия -- это когда функция вызывает сама себя, как правило, с другими аргументами.
Существуют много областей применения рекурсивных вызовов. Здесь мы посмотрели на один из них -- решение задачи путём сведения её к более простой (с меньшими аргументами), но также рекурсия используется для работы с "естественно рекурсивными" структурами данных, такими как HTML-документы, для "глубокого" копирования сложных объектов.
Есть и другие применения, с которыми мы встретимся по мере изучения JavaScript.
Здесь мы постарались рассмотреть происходящее достаточно подробно, однако, если пожелаете, допустимо временно забежать вперёд и открыть главу <info:debugging-chrome>, с тем чтобы при помощи отладчика построчно пробежаться по коду и посмотреть стек на каждом шаге. Отладчик даёт к нему доступ.

View file

@ -0,0 +1,26 @@
<style>
.function-execution-context-list {
margin: 0;
padding: 0;
overflow: auto;
}
.function-execution-context {
border: 1px solid black;
font-family: "Consolas", monospace;
padding: 5px 8px;
}
.function-execution-context-call {
color: gray;
padding-left: 20px;
}
.function-execution-context-call::before {
content: '@';
}
.function-execution-context-list li:first-child {
font-weight: bold;
}
</style>

1
1-js/5-deeper/index.md Normal file
View file

@ -0,0 +1 @@
# Deeper