Merge pull request #1 from iliakan/master

Repo Update 31-Jul-2018
This commit is contained in:
Aniket Kudale 2018-07-31 22:54:02 +05:30 committed by GitHub
commit 720b328969
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
211 changed files with 1322 additions and 1355 deletions

View file

@ -25,12 +25,12 @@ The browser has an embedded engine, sometimes it's also called a "JavaScript vir
Different engines have different "codenames", for example:
- [V8](https://en.wikipedia.org/wiki/V8_(JavaScript_engine)) -- in Chrome and Opera.
- [Gecko](https://en.wikipedia.org/wiki/Gecko_(software)) -- in Firefox.
- [SpiderMonkey](https://en.wikipedia.org/wiki/SpiderMonkey) -- in Firefox.
- ...There are other codenames like "Trident", "Chakra" for different versions of IE, "ChakraCore" for Microsoft Edge, "Nitro" and "SquirrelFish" for Safari etc.
The terms above are good to remember, because they are used in developer articles on the internet. We'll use them too. For instance, if "a feature X is supported by V8", then it probably works in Chrome and Opera.
```smart header="How engines work?"
```smart header="How do engines work?"
Engines are complicated. But the basics are easy.
@ -108,7 +108,7 @@ Modern tools make the transpilation very fast and transparent, actually allowing
Examples of such languages:
- [CoffeeScript](http://coffeescript.org/) is a "syntax sugar" for JavaScript, it introduces shorter syntax, allowing to write more precise and clear code. Usually Ruby devs like it.
- [CoffeeScript](http://coffeescript.org/) is a "syntactic sugar" for JavaScript, it introduces shorter syntax, allowing to write more precise and clear code. Usually Ruby devs like it.
- [TypeScript](http://www.typescriptlang.org/) is concentrated on adding "strict data typing", to simplify development and support of complex systems. It is developed by Microsoft.
- [Dart](https://www.dartlang.org/) is a standalone language that has its own engine that runs in non-browser environments (like mobile apps). It was initially offered by Google as a replacement for JavaScript, but as of now, browsers require it to be transpiled to JavaScript just like the ones above.

View file

@ -1,26 +1,22 @@
# Code editors
A code editor is the place where a programmer spends most of his time.
A code editor is the place where programmers spend most of their time.
There are two archetypes: IDE and lightweight editors. Many people feel comfortable choosing one tool of each type.
[cut]
## IDE
The term [IDE](https://en.wikipedia.org/wiki/Integrated_development_environment) (Integrated Development Environment) means a powerful editor with many features that usually operates on a "whole project". As said, that's not just an editor, but a full-scale "development environment".
The term [IDE](https://en.wikipedia.org/wiki/Integrated_development_environment) (Integrated Development Environment) means a powerful editor with many features that usually operates on a "whole project". As the name suggests, that's not just an editor, but a full-scale "development environment".
An IDE loads the project (can be many files), and then allows navigation between files, provides autocompletion based on the whole project, integrates with a version management system (like [git](https://git-scm.com/)), a testing environment and other "project-level" stuff.
An IDE loads the project (can be many files), allows navigation between files, provides autocompletion based on the whole project (not just the open file), integrates with a version management system (like [git](https://git-scm.com/)), a testing environment and other "project-level" stuff.
If you haven't considered selecting an IDE yet, look at the following variants:
- IntelliJ editors: [WebStorm](http://www.jetbrains.com/webstorm/) for frontend development and [PHPStorm (PHP)](http://www.jetbrains.com/phpstorm/), [IDEA (Java)](http://www.jetbrains.com/idea/), [RubyMine (Ruby)](http://www.jetbrains.com/ruby/) and others if you need additional languages.
- [WebStorm](http://www.jetbrains.com/webstorm/) for frontend development and other editors of the same company if you need additional languages.
- Visual Studio is fine if you're a .NET developer, and a free version is available ([Visual Studio Community](https://www.visualstudio.com/vs/community/))
- Eclipse-based products, like [Aptana](http://www.aptana.com/) and Zend Studio.
- [Komodo IDE](http://www.activestate.com/komodo-ide) and its lightweight free version [Komodo Edit](http://www.activestate.com/komodo-edit).
- [Netbeans](http://netbeans.org/).
All of the IDEs listed above are available on both Windows and Mac, and the IDEs other than Visual Studio are also available on Linux.
All of the IDEs except Visual Studio are available on Windows, MacOs and Linux. Visual Studio doesn't work on Linux.
Most IDEs are paid, but have a trial period. Their cost is usually negligible compared to a qualified developer's salary, so just choose the best one for you.
@ -40,7 +36,7 @@ The following options deserve your attention:
- [Atom](https://atom.io/) (cross-platform, free).
- [Sublime Text](http://www.sublimetext.com) (cross-platform, shareware).
- [Notepad++](https://notepad-plus-plus.org/) (Windows, free).
- Vim and Emacs are also cool, if you know how to use them.
- [Vim](http://www.vim.org/) and [Emacs](https://www.gnu.org/software/emacs/) are also cool, if you know how to use them.
## My favorites
@ -48,11 +44,9 @@ The personal preference of the author is to have both an IDE for projects and a
I'm using:
- [WebStorm](http://www.jetbrains.com/webstorm/) for JS, and if there is one more language in the project, then I switch to other Jetbrains editors like [PHPStorm](http://www.jetbrains.com/phpstorm/) (PHP), [IDEA](http://www.jetbrains.com/idea/) (Java), [RubyMine](http://www.jetbrains.com/ruby/) (Ruby). There are editors for other languages too, but I haven't used them.
- [WebStorm](http://www.jetbrains.com/webstorm/) for JS, and if there is one more language in the project, then I switch to one of the other Jetbrains offerings listed above.
- As a lightweight editor -- [Sublime Text](http://www.sublimetext.com) or [Atom](https://atom.io/).
If you don't know what to choose, you can consider these ones.
## Let's not argue
The editors in the lists above are those that either I or my friends who I consider good developers have been using for a long time and are happy with.

View file

@ -10,8 +10,6 @@ Most often developers lean towards Chrome or Firefox for development, because th
Developer tools are really powerful, there are many features. To start, we'll learn how to open them, look at errors and run JavaScript commands.
[cut]
## Google Chrome
Open the page [bug.html](bug.html).

View file

@ -7,8 +7,6 @@ But, we need a working environment to run our scripts, and, just because this bo
So first, let's see how to attach a script to a webpage. For server-side environments, you can just execute it with a command like `"node my.js"` for Node.JS.
[cut]
## The "script" tag
JavaScript programs can be inserted in any part of an HTML document with the help of the `<script>` tag.

View file

@ -2,8 +2,6 @@
The first thing to study is the building blocks of the code.
[cut]
## Statements
Statements are syntax constructs and commands that perform actions.
@ -102,7 +100,7 @@ As time goes on, the program becomes more and more complex. It becomes necessary
Comments can be put into any place of the script. They don't affect the execution, because the engine simply ignores them.
**One-line comments start with the two forward slash characters `//`.**
**One-line comments start with two forward slash characters `//`.**
The rest of the line is a comment. It may occupy a full line of its own or follow a statement.

View file

@ -6,8 +6,6 @@ That had the benefit of never breaking existing code. But the downside was that
It had been so until 2009 when ECMAScript 5 (ES5) appeared. It added new features to the language and modified some of the existing ones. To keep the old code working, most modifications are off by default. One needs to enable them explicitly with a special directive `"use strict"`.
[cut]
## "use strict"
The directive looks like a string: `"use strict"` or `'use strict'`. When it is located on the top of the script, then the whole script works the "modern" way.

View file

@ -1,10 +1,10 @@
# Variables
Most of the time, a script needs to work with information. 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.
Most of the time, a JavaScript application needs to work with information. Here are 2 examples:
1. An online-shop -- the information might include goods being sold and a shopping cart.
2. A chat application -- the information might include users, messages, and much more.
Variables are used to store the information.
[cut]
Variables are used to store this information.
## A variable
@ -97,7 +97,7 @@ There are subtle differences between `let` and `var`, but they do not matter for
We can easily grasp the concept of a "variable" if we imagine it as a "box" for data, with a uniquely-named sticker on it.
For instance, the variable `message` can be imagined as a box labelled `"message"` with the value `"Hello!"` in it:
For instance, the variable `message` can be imagined as a box labeled `"message"` with the value `"Hello!"` in it:
![](variable.png)
@ -296,23 +296,23 @@ Please name the variables sensibly. Take time to think if needed.
Variable naming is one of the most important and complex skills in programming. A quick glance at variable names can reveal which code is written by a beginner and which by an experienced developer.
In a real project, most of the time is spent on modifying and extending the existing code base, rather than writing something completely separate from the scratch. And when we return to the code after some time of doing something else, it's much easier to find information that is well-labelled. Or, in other words, when the variables have good names.
In a real project, most of the time is spent on modifying and extending the existing code base, rather than writing something completely separate from scratch. And when we return to the code after some time of doing something else, it's much easier to find information that is well-labeled. Or, in other words, when the variables have good names.
Please spend some time thinking about the right name for a variable before declaring it. That will repay you a lot.
Please spend some time thinking about the right name for a variable before declaring it. This will repay you a lot.
Some good-to-follow rules are:
- Use human-readable names like `userName` or `shoppingCart`.
- Stay away from abbreviations or short names like `a`, `b`, `c`, unless you really know what you're doing.
- Make the name maximally descriptive and concise. Examples of bad names are `data` and `value`. Such a name says nothing. It is only ok to use them if it's exceptionally obvious from the context which data or value is meant.
- Agree on terms within the team and in your own mind. If a site visitor is called a "user" then we should name related variables like `currentUser` or `newUser`, but not `currentVisitor` or a `newManInTown`.
- Agree on terms within your team and in your own mind. If a site visitor is called a "user" then we should name related variables like `currentUser` or `newUser`, but not `currentVisitor` or a `newManInTown`.
Sounds simple? Indeed it is, but creating good descriptive-and-concise names in practice is not. Go for it.
```smart header="Reuse or create?"
And the last note. There are some lazy programmers who, instead of declaring a new variable, tend to reuse the existing ones.
As the result, the variable is like a box where people throw different things without changing the sticker. What is inside it now? Who knows... We need to come closer and check.
As a result, the variable is like a box where people throw different things without changing the sticker. What is inside it now? Who knows... We need to come closer and check.
Such a programmer saves a little bit on variable declaration, but loses ten times more on debugging the code.

View file

@ -12,8 +12,6 @@ Programming languages that allow such things are called "dynamically typed", mea
There are seven basic data types in JavaScript. Here we'll study the basics, and in the next chapters we'll talk about each of them in detail.
[cut]
## A number
```js
@ -162,7 +160,7 @@ let x;
alert(x); // shows "undefined"
```
Technically, it is possible to assign any variable to `undefined`:
Technically, it is possible to assign `undefined` to any variable:
```js run
let x = 123;
@ -191,7 +189,7 @@ 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. The result is the same.
In other words, it works both with parentheses or without them. The result is the same.
The call to `typeof x` returns a string with the type name:

View file

@ -17,6 +17,6 @@ undefined + 1 = NaN // (4)
```
1. The addition with a string `"" + 1` converts `1` to a string: `"" + 1 = "1"`, and then we have `"1" + 0`, the same rule is applied.
2. The substruction `"-"` (like most math operations) only works with numbers, it converts an empty string `""` to `0`.
2. The subtraction `-` (like most math operations) only works with numbers, it converts an empty string `""` to `0`.
3. `null` becomes `0` after the numeric conversion.
4. `undefined` becomes `NaN` after the numeric conversion.

View file

@ -6,8 +6,6 @@ For example, `alert` automatically converts any value to a string to show it. Ma
There are also cases when we need to explicitly convert a value to put things right.
[cut]
```smart header="Not talking about objects yet"
In this chapter we don't cover objects yet. Here we study primitives first. Later, after we learn objects, we'll see how object conversion works in the chapter <info:object-toprimitive>.
```

View file

@ -4,14 +4,12 @@ Many operators are known to us from school. They are addition `+`, a multiplicat
In this chapter we concentrate on aspects that are not covered by school arithmetic.
[cut]
## Terms: "unary", "binary", "operand"
Before we move on, let's grasp the common terminology.
- *An operand* -- is what operators are applied to. For instance in multiplication `5 * 2` there are two operands: the left operand is `5`, and the right operand is `2`. Sometimes people say "arguments" instead of "operands".
- An operator is *unary* if it has a single operand. For example, the unary negation `"-"` reverses the sign of the number:
- An operator is *unary* if it has a single operand. For example, the unary negation `-` reverses the sign of the number:
```js run
let x = 1;
@ -34,7 +32,7 @@ Before we move on, let's grasp the common terminology.
Now let's see special features of JavaScript operators that are beyond school arithmetics.
Usually the plus operator `'+'` sums numbers.
Usually the plus operator `+` sums numbers.
But if the binary `+` is applied to strings, it merges (concatenates) them:
@ -43,7 +41,7 @@ let s = "my" + "string";
alert(s); // mystring
```
Note that if any of operands is a string, then the other one is converted to a string too.
Note that if any of the operands is a string, then the other one is converted to a string too.
For example:
@ -54,7 +52,14 @@ alert( 2 + '1' ); // "21"
See, it doesn't matter whether the first operand is a string or the second one. The rule is simple: if either operand is a string, then convert the other one into a string as well.
String concatenation and conversion is a special feature of the binary plus `"+"`. Other arithmetic operators work only with numbers. They always convert their operands to numbers.
However, note that operations run from left to right. If there are two numbers followed by a string, the numbers will be added before being converted to a string:
```js run
alert(2 + 2 + '1' ); // "41" and not "221"
```
String concatenation and conversion is a special feature of the binary plus `+`. Other arithmetic operators work only with numbers. They always convert their operands to numbers.
For instance, subtraction and division:
@ -65,7 +70,7 @@ alert( '6' / '2' ); // 3
## Numeric conversion, unary +
The plus `+` exist in two forms. The binary form that we used above and the unary form.
The plus `+` exists in two forms. The binary form that we used above and the unary form.
The unary plus or, in other words, the plus operator `+` applied to a single value, doesn't do anything with numbers, but if the operand is not a number, then it is converted into it.
@ -86,9 +91,9 @@ alert( +"" ); // 0
*/!*
```
It actually does the same as `Number(...)`, but shorter.
It actually does the same as `Number(...)`, but is shorter.
A need to convert string to number arises very often. For example, if we are getting values from HTML form fields, then they are usually strings.
A need to convert strings to numbers arises very often. For example, if we are getting values from HTML form fields, then they are usually strings.
What if we want to sum them?
@ -173,7 +178,7 @@ alert( b ); // 4
alert( c ); // 4
```
Chained assignments evaluate from right to left. First the rightmost expression `2+2` is evaluated then assigned to the variables on the left: `c`, `b` and `a`. At the end, all variables share a single value.
Chained assignments evaluate from right to left. First the rightmost expression `2 + 2` is evaluated then assigned to the variables on the left: `c`, `b` and `a`. At the end, all variables share a single value.
````smart header="The assignment operator `\"=\"` returns a value"
An operator always returns a value. That's obvious for most of them like an addition `+` or a multiplication `*`. But the assignment operator follows that rule too.
@ -246,14 +251,14 @@ So, there are special operators for that:
```js run no-beautify
let counter = 2;
counter++; // works same as counter = counter + 1, but shorter
counter++; // works the same as counter = counter + 1, but is shorter
alert( counter ); // 3
```
- **Decrement** `--` decreases a variable by 1:
```js run no-beautify
let counter = 2;
counter--; // works same as counter = counter - 1, but shorter
counter--; // works the same as counter = counter - 1, but is shorter
alert( counter ); // 1
```
@ -281,7 +286,7 @@ let a = ++counter; // (*)
alert(a); // *!*2*/!*
```
Here in the line `(*)` the prefix call `++counter` increments `i` and returns the new value that is `2`. So the `alert` shows `2`.
Here in the line `(*)` the prefix call `++counter` increments `counter` and returns the new value that is `2`. So the `alert` shows `2`.
Now let's use the postfix form:
@ -292,7 +297,7 @@ let a = counter++; // (*) changed ++counter to counter++
alert(a); // *!*1*/!*
```
In the line `(*)` the *postfix* form `counter++` also increments `i`, but returns the *old* value (prior to increment). So the `alert` shows `1`.
In the line `(*)` the *postfix* form `counter++` also increments `counter`, but returns the *old* value (prior to increment). So the `alert` shows `1`.
To summarize:
@ -381,8 +386,8 @@ This notation can be shortened using operators `+=` and `*=`:
```js run
let n = 2;
n += 5; // now n=7 (same as n = n + 5)
n *= 2; // now n=14 (same as n = n * 2)
n += 5; // now n = 7 (same as n = n + 5)
n *= 2; // now n = 14 (same as n = n * 2)
alert( n ); // 14
```
@ -401,26 +406,26 @@ alert( n ); // 16 (right part evaluated first, same as n *= 8)
## Comma
The comma operator `','` is one of most rare and unusual operators. Sometimes it's used to write shorter code, so we need to know it in order to understand what's going on.
The comma operator `,` is one of most rare and unusual operators. Sometimes it's used to write shorter code, so we need to know it in order to understand what's going on.
The comma operator allows us to evaluate several expressions, dividing them with a comma `','`. Each of them is evaluated, but the result of only the last one is returned.
The comma operator allows us to evaluate several expressions, dividing them with a comma `,`. Each of them is evaluated, but the result of only the last one is returned.
For example:
```js run
*!*
let a = (1+2, 3+4);
let a = (1 + 2, 3 + 4);
*/!*
alert( a ); // 7 (the result of 3+4)
alert( a ); // 7 (the result of 3 + 4)
```
Here, the first expression `1+2` is evaluated, and its result is thrown away, then `3+4` is evaluated and returned as the result.
Here, the first expression `1 + 2` is evaluated, and its result is thrown away, then `3 + 4` is evaluated and returned as the result.
```smart header="Comma has a very low precedence"
Please note that the comma operator has very low precedence, lower than `=`, so parentheses are important in the example above.
Without them: `a=1+2,3+4` evaluates `+` first, summing the numbers into `a=3,7`, then the assignment operator `=` assigns `a=3`, and then the number after the comma `7` is not processed anyhow, so it's ignored.
Without them: `a = 1 + 2, 3 + 4` evaluates `+` first, summing the numbers into `a = 3, 7`, then the assignment operator `=` assigns `a = 3`, and then the number after the comma `7` is not processed anyhow, so it's ignored.
```
Why do we need such an operator which throws away everything except the last part?

View file

@ -13,7 +13,7 @@ null === +"\n0\n" → false
Some of the reasons:
1. Obviously, true.
2. Dictionary comparison, hence true.
2. Dictionary comparison, hence false.
3. Again, dictionary comparison, first char of `"2"` is greater than the first char of `"1"`.
4. Values `null` and `undefined` equal each other only.
5. Strict equality is strict. Different types from both sides lead to false.

View file

@ -4,11 +4,9 @@ Many comparison operators we know from maths:
- Greater/less than: <code>a &gt; b</code>, <code>a &lt; b</code>.
- Greater/less than or equals: <code>a &gt;= b</code>, <code>a &lt;= b</code>.
- Equality check is written as `a == b` (please note the double equation sign `'='`. A single symbol `a = b` would mean an assignment).
- Equality check is written as `a == b` (please note the double equation sign `=`. A single symbol `a = b` would mean an assignment).
- Not equals. In maths the notation is <code>&ne;</code>, in JavaScript it's written as an assignment with an exclamation sign before it: <code>a != b</code>.
[cut]
## Boolean is the result
Just as all other operators, a comparison returns a value. The value is of the boolean type.
@ -108,7 +106,7 @@ From JavaScript's standpoint that's quite normal. An equality check converts usi
## Strict equality
A regular equality check `"=="` has a problem. It cannot differ `0` from `false`:
A regular equality check `==` has a problem. It cannot differ `0` from `false`:
```js run
alert( 0 == false ); // true
@ -146,7 +144,7 @@ There's a non-intuitive behavior when `null` or `undefined` are compared with ot
For a strict equality check `===`
: These values are different, because each of them belong to a separate type of it's own.
: These values are different, because each of them belongs to a separate type of its own.
```js run
alert( null === undefined ); // false

View file

@ -4,8 +4,6 @@ This part of the tutorial aims to cover JavaScript "as is", without environment-
But still we use a browser as the demo environment. So we should know at least a few user-interface functions. In this chapter we'll get familiar with the browser functions `alert`, `prompt` and `confirm`.
[cut]
## alert
Syntax:

View file

@ -9,13 +9,13 @@ if (userName == 'Admin') {
if (pass == 'TheMaster') {
alert( 'Welcome!' );
} else if (pass == null) {
} else if (pass == '' || pass == null) {
alert( 'Canceled.' );
} else {
alert( 'Wrong password' );
}
} else if (userName == null) {
} else if (userName == '' || userName == null) {
alert( 'Canceled' );
} else {
alert( "I don't know you" );

View file

@ -20,4 +20,6 @@ The schema:
Please use nested `if` blocks. Mind the overall readability of the code.
Hint: passing an empty input to a prompt returns an empty string `''`. Pressing `key:ESC` during a prompt returns `null`.
[demo]

View file

@ -2,13 +2,11 @@
Sometimes we need to perform different actions based on a condition.
There is the `if` statement for that and also the conditional (ternary) operator for conditional evaluation which we will be referring as the “question mark” operator: `"?"` for simplicity.
[cut]
There is the `if` statement for that and also the conditional (ternary) operator for conditional evaluation which we will be referring as the “question mark” operator `?` for simplicity.
## The "if" statement
The "if" statement gets a condition, evaluates it and, if the result is `true`, executes the code.
The `if` statement gets a condition, evaluates it and, if the result is `true`, executes the code.
For example:
@ -22,7 +20,7 @@ if (year == 2015) alert( 'You are right!' );
In the example above, the condition is a simple equality check: `year == 2015`, but it can be much more complex.
If there is more than one command to execute, we can use a code block in figure brackets:
If there is more than one statement to be executed, we have to wrap our code block inside curly braces:
```js
if (year == 2015) {
@ -31,7 +29,7 @@ if (year == 2015) {
}
```
It is recommended to use figure brackets every time with `if`, even if there is only one command. That improves readability.
It is recommended to wrap your code block with curly braces `{}` every time with `if`, even if there is only one statement. That improves readability.
## Boolean conversion
@ -128,7 +126,7 @@ alert(accessAllowed);
The so-called "ternary" or "question mark" operator lets us do that shorter and simpler.
The operator is represented by a question mark `"?"`. The formal term "ternary" means that the operator has three operands. It is actually the one and only operator in JavaScript which has that many.
The operator is represented by a question mark `?`. The formal term "ternary" means that the operator has three operands. It is actually the one and only operator in JavaScript which has that many.
The syntax is:
```js
@ -151,7 +149,7 @@ Technically, we can omit parentheses around `age > 18`. The question mark operat
let accessAllowed = age > 18 ? true : false;
```
...But parentheses make the code more readable. So it's recommended to use them.
But parentheses make the code more readable, so it's recommended to use them.
````smart
In the example above it's possible to evade the question mark operator, because the comparison by itself returns `true/false`:
@ -164,7 +162,7 @@ let accessAllowed = age > 18;
## Multiple '?'
A sequence of question mark `"?"` operators allows returning a value that depends on more than one condition.
A sequence of question mark `?` operators allows returning a value that depends on more than one condition.
For instance:
```js run
@ -190,7 +188,7 @@ The same logic using `if..else`:
```js
if (age < 3) {
message = 'Hi, baby!';
} else if (a < 18) {
} else if (age < 18) {
message = 'Hello!';
} else if (age < 100) {
message = 'Greetings!';
@ -201,7 +199,7 @@ if (age < 3) {
## Non-traditional use of '?'
Sometimes the question mark `'?'` is used as a replacement for `if`:
Sometimes the question mark `?` is used as a replacement for `if`:
```js run no-beautify
let company = prompt('Which company created JavaScript?', '');
@ -212,7 +210,7 @@ let company = prompt('Which company created JavaScript?', '');
*/!*
```
Depending on the condition `company == 'Netscape'`, either the first or the second part after `"?"` gets executed and shows the alert.
Depending on the condition `company == 'Netscape'`, either the first or the second part after `?` gets executed and shows the alert.
We don't assign a result to a variable here. The idea is to execute different code depending on the condition.
@ -236,4 +234,4 @@ if (company == 'Netscape') {
Our eyes scan the code vertically. The constructs which span several lines are easier to understand than a long horizontal instruction set.
The idea of a question mark `'?'` is to return one or another value depending on the condition. Please use it for exactly that. There is `if` to execute different branches of the code.
The idea of a question mark `?` is to return one or another value depending on the condition. Please use it for exactly that. There is `if` to execute different branches of the code.

View file

@ -6,8 +6,6 @@ Although they are called "logical", they can be applied to values of any type, n
Let's see the details.
[cut]
## || (OR)
The "OR" operator is represented with two vertical line symbols:
@ -68,7 +66,7 @@ if (hour < 10 || hour > 18 || isWeekend) {
## OR seeks the first truthy value
The logic described above is somewhat classical. Now let's bring in the "extra" features of JavaScipt.
The logic described above is somewhat classical. Now let's bring in the "extra" features of JavaScript.
The extended algorithm works as follows.
@ -78,11 +76,11 @@ Given multiple OR'ed values:
result = value1 || value2 || value3;
```
The OR `"||"` operator does the following:
The OR `||` operator does the following:
- Evaluate operands from left to right.
- For each operand, convert it to boolean. If the result is `true`, then stop and return the original value of that operand.
- If all other operands have been assessed (i.e. all were `falsy`), return the last operand.
- If all other operands have been assessed (i.e. all were `false`), return the last operand.
A value is returned in its original form, without the conversion.
@ -196,7 +194,7 @@ Given multiple AND'ed values:
result = value1 && value2 && value3;
```
The AND `"&&"` operator does the following:
The AND `&&` operator does the following:
- Evaluate operands from left to right.
- For each operand, convert it to a boolean. If the result is `false`, stop and return the original value of that operand.
@ -232,14 +230,10 @@ When all values are truthy, the last value is returned:
alert( 1 && 2 && 3 ); // 3, the last one
```
````smart header="AND `&&` executes before OR `||`"
The precedence of the AND `&&` operator is higher than OR `||`, so it executes before OR.
````smart header="Precedence of AND `&&` is higher than OR `||`"
The precedence of AND `&&` operator is higher than OR `||`.
In the code below `1 && 0` is calculated first:
```js run
alert( 5 || 1 && 0 ); // 5
```
So the code `a && b || c && d` is essentially the same as if `&&` were in parentheses: `(a && b) || (c && d)`.
````
Just like OR, the AND `&&` operator can sometimes replace `if`.
@ -270,7 +264,7 @@ So it is recommended to use every construct for its purpose. Use `if` if we want
## ! (NOT)
The boolean NOT operator is represented with an exclamation sign `"!"`.
The boolean NOT operator is represented with an exclamation sign `!`.
The syntax is pretty simple:
@ -305,3 +299,5 @@ There's a little more verbose way to do the same thing -- a built-in `Boolean` f
alert( Boolean("non-empty string") ); // true
alert( Boolean(null) ); // false
```
The precedence of NOT `!` is the highest of all bitwise operators, so it always executes first, before any `&&`, `||`.

View file

@ -8,8 +8,8 @@ for (let i = 0; i < 5; i++) alert( i );
That can be easily deducted from the algorithm of `for`:
1. Execute once `i=0` before everything (begin).
2. Check the condition `i<5`
1. Execute once `i = 0` before everything (begin).
2. Check the condition `i < 5`
3. If `true` -- execute the loop body `alert(i)`, and then `i++`
The increment `i++` is separated from the condition check (2). That's just another statement.

View file

@ -10,6 +10,6 @@ do {
The loop `do..while` repeats while both checks are truthy:
1. The check for `num <= 100` -- that is, the entered value is still not greater than `100`.
2. The check `&& num` is false when `num` is `null` or a empty strig. Then the `while` loop stops too.
2. The check `&& num` is false when `num` is `null` or a empty string. Then the `while` loop stops too.
P.S. If `num` is `null` then `num <= 100` is `true`, so without the 2nd check the loop wouldn't stop if the user clicks CANCEL. Both checks are required.

View file

@ -6,12 +6,12 @@ importance: 3
An integer number greater than `1` is called a [prime](https://en.wikipedia.org/wiki/Prime_number) if it cannot be divided without a remainder by anything except `1` and itself.
In other words, `n>1` is a prime if it can't be evenly divided by anything except `1` and `n`.
In other words, `n > 1` is a prime if it can't be evenly divided by anything except `1` and `n`.
For example, `5` is a prime, because it cannot be divided without a remainder by `2`, `3` and `4`.
**Write the code which outputs prime numbers in the interval from `2` to `n`.**
For `n=10` the result will be `2,3,5,7`.
For `n = 10` the result will be `2,3,5,7`.
P.S. The code should work for any `n`, not be hard-tuned for any fixed value.

View file

@ -6,8 +6,6 @@ For example, when we need to output goods from a list one after another. Or just
*Loops* are a way to repeat the same part of code multiple times.
[cut]
## The "while" loop
The `while` loop has the following syntax:
@ -21,7 +19,7 @@ while (condition) {
While the `condition` is `true`, the `code` from the loop body is executed.
For instance, the loop below outputs `i` while `i<3`:
For instance, the loop below outputs `i` while `i < 3`:
```js run
let i = 0;
@ -35,9 +33,9 @@ A single execution of the loop body is called *an iteration*. The loop in the ex
If there were no `i++` in the example above, the loop would repeat (in theory) forever. In practice, the browser provides ways to stop such loops, and for server-side JavaScript we can kill the process.
Any expression or a variable can be a loop condition, not just a comparison. They are evaluated and converted to boolean by `while`.
Any expression or a variable can be a loop condition, not just a comparison. They are evaluated and converted to a boolean by `while`.
For instance, the shorter way to write `while (i!=0)` could be `while (i)`:
For instance, the shorter way to write `while (i != 0)` could be `while (i)`:
```js run
let i = 3;
@ -108,8 +106,8 @@ Let's examine the `for` statement part by part:
| part | | |
|-------|----------|----------------------------------------------------------------------------|
| begin | `i=0` | Executes once upon entering the loop. |
| condition | `i<3`| Checked before every loop iteration, if fails the loop stops. |
| begin | `i = 0` | Executes once upon entering the loop. |
| condition | `i < 3`| Checked before every loop iteration, if fails the loop stops. |
| step| `i++` | Executes after the body on each iteration, but before the condition check. |
| body | `alert(i)`| Runs again and again while the condition is truthy |
@ -188,11 +186,11 @@ We can also remove the `step` part:
let i = 0;
for (; i < 3;) {
alert( i );
alert( i++ );
}
```
The loop became identical to `while (i<3)`.
The loop became identical to `while (i < 3)`.
We can actually remove everything, thus creating an infinite loop:
@ -229,7 +227,7 @@ while (true) {
alert( 'Sum: ' + sum );
```
The `break` directive is activated in the line `(*)` if the user enters an empty line or cancels the input. It stops the loop immediately, passing the control to the first line after the loop. Namely, `alert`.
The `break` directive is activated at the line `(*)` if the user enters an empty line or cancels the input. It stops the loop immediately, passing the control to the first line after the loop. Namely, `alert`.
The combination "infinite loop + `break` as needed" is great for situations when the condition must be checked not in the beginning/end of the loop, but in the middle, or even in several places of the body.
@ -239,7 +237,7 @@ The `continue` directive is a "lighter version" of `break`. It doesn't stop the
We can use it if we're done on the current iteration and would like to move on to the next.
The loop above uses `continue` to output only odd values:
The loop below uses `continue` to output only odd values:
```js run no-beautify
for (let i = 0; i < 10; i++) {
@ -268,11 +266,11 @@ for (let i = 0; i < 10; i++) {
From a technical point of view it's identical to the example above. Surely, we can just wrap the code in the `if` block instead of `continue`.
But as a side-effect we got one more figure brackets nesting level. If the code inside `if` is longer than a few lines, that may decrease the overall readability.
But as a side-effect we got one more nesting level (the `alert` call inside the curly braces). If the code inside `if` is longer than a few lines, that may decrease the overall readability.
````
````warn header="No `break/continue` to the right side of '?'"
Please note that syntax constructs that are not expressions cannot be used in `'?'`. In particular, directives `break/continue` are disallowed there.
Please note that syntax constructs that are not expressions cannot be used with the ternary operator `?`. In particular, directives such as `break/continue` are disallowed there.
For example, if we take this code:
@ -294,7 +292,7 @@ if (i > 5) {
...Then it stops working. The code like this will give a syntax error:
That's just another reason not to use a question mark operator `'?'` instead of `if`.
That's just another reason not to use a question mark operator `?` instead of `if`.
````
## Labels for break/continue
@ -324,7 +322,7 @@ The ordinary `break` after `input` would only break the inner loop. That's not s
A *label* is an identifier with a colon before a loop:
```js
labelName: for(...) {
labelName: for (...) {
...
}
```
@ -369,7 +367,7 @@ For example, it is impossible to do this:
```js
break label; // jumps to label? No.
label: for(...)
label: for (...)
```
The call to a `break/continue` is only possible from inside the loop, and the label must be somewhere upwards from the directive.
@ -381,10 +379,10 @@ We covered 3 types of loops:
- `while` -- The condition is checked before each iteration.
- `do..while` -- The condition is checked after each iteration.
- `for(;;)` -- The condition is checked before each iteration, additional settings available.
- `for (;;)` -- The condition is checked before each iteration, additional settings available.
To make an "infinite" loop, usually the `while(true)` construct is used. Such a loop, just like any other, can be stopped with the `break` directive.
If we don't want to do anything on the current iteration and would like to forward to the next one, the `continue` directive does it.
`Break/continue` support labels before the loop. A label is the only way for `break/continue` to escape the nesting and go to the outer loop.
`break/continue` support labels before the loop. A label is the only way for `break/continue` to escape the nesting and go to the outer loop.

View file

@ -4,8 +4,6 @@ A `switch` statement can replace multiple `if` checks.
It gives a more descriptive way to compare a value with multiple variants.
[cut]
## The syntax
The `switch` has one or more `case` blocks and an optional default.

View file

@ -35,4 +35,4 @@ function checkAge(age) {
}
```
Is there any difference in the bahavior of these two variants?
Is there any difference in the behavior of these two variants?

View file

@ -22,5 +22,5 @@ Rewrite it, to perform the same, but without `if`, in a single line.
Make two variants of `checkAge`:
1. Using a question mark operator `'?'`
1. Using a question mark operator `?`
2. Using OR `||`

View file

@ -13,7 +13,7 @@ function pow(x, n) {
let x = prompt("x?", '');
let n = prompt("n?", '');
if (n <= 1) {
if (n < 1) {
alert(`Power ${n} is not supported,
use an integer greater than 0`);
} else {

View file

@ -9,7 +9,7 @@ Write a function `pow(x,n)` that returns `x` in power `n`. Or, in other words, m
```js
pow(3, 2) = 3 * 3 = 9
pow(3, 3) = 3 * 3 * 3 = 27
pow(1, 100) = 1 * 1 * ...*1 = 1
pow(1, 100) = 1 * 1 * ...* 1 = 1
```
Create a web-page that prompts for `x` and `n`, and then shows the result of `pow(x,n)`.

View file

@ -6,8 +6,6 @@ For example, we need to show a nice-looking message when a visitor logs in, logs
Functions are the main "building blocks" of the program. They allow the code to be called many times without repetition.
[cut]
We've already seen examples of built-in functions, like `alert(message)`, `prompt(message, default)` and `confirm(question)`. But we can create functions of our own as well.
## Function Declaration
@ -22,7 +20,7 @@ function showMessage() {
}
```
The `function` keyword goes first, then goes the *name of the function*, then a list of *parameters* in the brackets (empty in the example above) and finally the code of the function, also named "the function body".
The `function` keyword goes first, then goes the *name of the function*, then a list of *parameters* between the parentheses (empty in the example above) and finally the code of the function, also named "the function body", between curly braces.
![](function_basics.png)
@ -43,7 +41,7 @@ showMessage();
The call `showMessage()` executes the code of the function. Here we will see the message two times.
This example clearly demonstrates one of the main purposes of functions: to evade code duplication.
This example clearly demonstrates one of the main purposes of functions: to avoid code duplication.
If we ever need to change the message or the way it is shown, it's enough to modify the code in one place: the function which outputs it.
@ -119,7 +117,7 @@ function showMessage() {
alert(message);
}
// the function will create and use it's own userName
// the function will create and use its own userName
showMessage();
alert( userName ); // *!*John*/!*, unchanged, the function did not access the outer variable
@ -130,7 +128,7 @@ Variables declared outside of any function, such as the outer `userName` in the
Global variables are visible from any function (unless shadowed by locals).
Usually, a function declares all variables specific to its task, and global variables only store project-level data, so important that it really must be seen from anywhere. Modern code has few or no globals. Most variables reside in their functions.
Usually, a function declares all variables specific to its task. Global variables only store project-level data, so when it's important that these variables are accesible from anywhere. Modern code has few or no globals. Most variables reside in their functions.
```
## Parameters
@ -150,7 +148,7 @@ showMessage('Ann', "What's up?"); // Ann: What's up? (**)
*/!*
```
When the function is called in lines `(*)` and `(**)`, the given values are copied to local variables `from` and `next`. Then the function uses them.
When the function is called in lines `(*)` and `(**)`, the given values are copied to local variables `from` and `text`. Then the function uses them.
Here's one more example: we have a variable `from` and pass it to the function. Please note: the function changes `from`, but the change is not seen outside, because a function always gets a copy of the value:
@ -378,7 +376,7 @@ These examples assume common meanings of prefixes. What they mean for you is det
```smart header="Ultrashort function names"
Functions that are used *very often* sometimes have ultrashort names.
For example, [jQuery](http://jquery.com) framework defines a function `$`, [LoDash](http://lodash.com/) library has it's core function named `_`.
For example, the [jQuery](http://jquery.com) framework defines a function `$`. The [LoDash](http://lodash.com/) library has its core function named `_`.
These are exceptions. Generally functions names should be concise, but descriptive.
```
@ -426,7 +424,7 @@ function isPrime(n) {
}
```
The second variant is easier to understand isn't it? Instead of the code piece we see a name of the action (`isPrime`). Sometimes people refer to such code as *self-describing*.
The second variant is easier to understand, isn't it? Instead of the code piece we see a name of the action (`isPrime`). Sometimes people refer to such code as *self-describing*.
So, functions can be created even if we don't intend to reuse them. They structure the code and make it readable.
@ -442,7 +440,7 @@ function name(parameters, delimited, by, comma) {
- Values passed to a function as parameters are copied to its local variables.
- A function may access outer variables. But it works only from inside out. The code outside of the function doesn't see its local variables.
- A function can return a value. If it doesn't then its result is `undefined`.
- A function can return a value. If it doesn't, then its result is `undefined`.
To make the code clean and easy to understand, it's recommended to use mainly local variables and parameters in the function, not outer variables.

View file

@ -2,8 +2,6 @@
In JavaScript, a function is not a "magical language structure", but a special kind of value.
[cut]
The syntax that we used before is called a *Function Declaration*:
```js
@ -135,13 +133,11 @@ function showCancel() {
ask("Do you agree?", showOk, showCancel);
```
Before we explore how we can write it in a much shorter way, let's note that in the browser (and on the server-side in some cases) such functions are quite popular.
Before we explore how we can write it in a much shorter way, let's note that in the browser (and on the server-side in some cases) such functions are quite popular. The major difference between a real-life implementation and the example above is that real-life functions use more complex ways to interact with the user than a simple `confirm`. In the browser, such a function usually draws a nice-looking question window. But that's another story.
The major difference between a real-life implementation and the example above is that real-life functions use more complex ways to interact with the user than a simple `confirm`. In the browser, such a function usually draws a nice-looking question window. But that's another story.
**The arguments of `ask` are called *callback functions* or just *callbacks*.**
**The arguments of `ask` are called *callback functions* or just *callbacks*. The idea is that we pass a function and expect it to be "called back" in certain circumstances.**
So, `showOk` becomes the callback for the "yes" answer, and `showCancel` for the "no" answer.
The idea is that we pass a function and expect it to be "called back" later if necessary. In our case, `showOk` becomes the callback for the "yes" answer, and `showCancel` for the "no" answer.
We can use Function Expressions to write the same function much shorter:
@ -160,6 +156,7 @@ ask(
*/!*
```
Here, functions are declared right inside the `ask(...)` call. They have no name, and so are called *anonymous*. Such functions are not accessible outside of `ask` (because they are not assigned to variables), but that's just what we want here.
Such code appears in our scripts very naturally, it's in the spirit of JavaScript.
@ -188,9 +185,8 @@ First, the syntax: how to see what is what in the code.
return a + b;
}
```
- *Function Expression:* a function, created inside an expression or inside another syntax construct.
Here, the function is created at the right side of the "assignment expression =":
- *Function Expression:* a function, created inside an expression or inside another syntax construct. Here, the function is created at the right side of the "assignment expression" `=`:
```js
// Function Expression
let sum = function(a, b) {
@ -242,20 +238,18 @@ let sayHi = function(name) { // (*) no magic any more
Function Expressions are created when the execution reaches them. That would happen only in the line `(*)`. Too late.
### Function Declaration in a block
When a Function Declaration is made within a code block, it is visible everywhere inside that block. But not outside of it.
**When a Function Declaration is made within a code block, it is visible everywhere inside that block. But not outside of it.**
Sometimes that's handy to declare a local function only needed in that block alone. But that feature may also cause problems.
For instance, let's imagine that we need to declare a function `welcome()` depending on the `age` variable that we get in run time. And then we plan to use it some time later.
For instance, let's imagine that we need to declare a function `welcome()` depending on the `age` variable that we get during runtime. And then we plan to use it some time later.
The code below doesn't work:
```js run
let age = prompt("What is your age?", 18);
// conditionally declare a function
if (age < 18) {
function welcome() {
@ -270,14 +264,15 @@ if (age < 18) {
}
// ...use it later
*!*
welcome(); // Error: welcome is not defined
*/!*
```
A Function Declaration is only visible inside the code block in which it resides.
That's because a Function Declaration is only visible inside the code block in which it resides.
We can call it from within the block, but not from outside:
Here's another example:
```js run
let age = 16; // take 16 as an example
@ -296,14 +291,13 @@ if (age < 18) {
*/!*
} else {
// \
function welcome() { // |
alert("Greetings!"); // | if age=16, the the execution does not go here,
} // | so this "welcome" is never created
// /
function welcome() { // for age = 16, this "welcome" is never created
alert("Greetings!");
}
}
// Here we're out of figure brackets,
// Here we're out of curly braces,
// so we can not see Function Declarations made inside of them.
*!*
@ -313,7 +307,9 @@ welcome(); // Error: welcome is not defined
What can we do to make `welcome` visible outside of `if`?
The correct approach would be to use a Function Expression and assign `welcome` to the variable that is declared outside of `if` and has the proper visibility:
The correct approach would be to use a Function Expression and assign `welcome` to the variable that is declared outside of `if` and has the proper visibility.
Now it works as intended:
```js run
let age = prompt("What is your age?", 18);
@ -354,7 +350,7 @@ welcome(); // ok now
```
```smart header="What to choose: Function Declaration or Function Expression?"
```smart header="When to choose Function Declaration versus Function Expression?"
As a rule of thumb, when we need to declare a function, the first to consider is Function Declaration syntax, the one we used before. It gives more freedom in how to organize our code, because we can call such functions before they are declared.
It's also a little bit easier to look up `function f(…) {…}` in the code than `let f = function(…) {…}`. Function Declarations are more "eye-catching".
@ -365,7 +361,7 @@ It's also a little bit easier to look up `function f(…) {…}` in the code tha
## Arrow functions [#arrow-functions]
There's one more very simple and concise syntax for creating functions. It's called "arrow functions", because it looks like this:
There's one more very simple and concise syntax for creating functions, that's often better than Function Expressions. It's called "arrow functions", because it looks like this:
```js
@ -382,37 +378,37 @@ let func = function(arg1, arg2, ...argN) {
}
```
...But much shorter.
...But much more concise.
Let's see an example:
```js run
let sum = (a, b) => a + b;
alert( sum(1, 2) ); // 3
```
/* The arrow function is a shorter form of:
Here the arrow function is a shorter form of:
```js
let sum = function(a, b) {
return a + b;
};
*/
alert( sum(1, 2) ); // 3
```
If we have only one argument, then parentheses can be omitted, making that even shorter:
```js run
// same as
// let double = function(n) { return n*2 }
// let double = function(n) { return n * 2 }
*!*
let double = n => n*2;
let double = n => n * 2;
*/!*
alert( double(3) ); // 6
```
If there are no arguments, we can insert empty parentheses:
If there are no arguments, parentheses should be empty (but they should be present):
```js run
let sayHi = () => alert("Hello!");
@ -434,23 +430,23 @@ let welcome = (age < 18) ?
welcome(); // ok now
```
The syntax may appear unfamiliar and not very readable at first, but that quickly changes as the eyes get used to the structure.
Arrow functions may appear unfamiliar and not very readable at first, but that quickly changes as the eyes get used to the structure.
Arrow functions are very convenient for simple one-line actions, when we're just too lazy to write many words.
They are very convenient for simple one-line actions, when we're just too lazy to write many words.
```smart header="Multiline arrow functions"
The examples above took arguments from the left of `=>` and evaluated the right-side expression with them.
Sometimes we need something a little bit more complex, like multiple expressions or statements. It is also possible, but we should enclose them in figure brackets. Then use a normal `return` within them.
Sometimes we need something a little bit more complex, like multiple expressions or statements. It is also possible, but we should enclose them in curly braces. Then use a normal `return` within them.
Like this:
```js run
let sum = (a, b) => { // the figure bracket opens a multiline function
let sum = (a, b) => { // the curly brace opens a multiline function
let result = a + b;
*!*
return result; // if we use figure brackets, use return to get results
return result; // if we use curly braces, use return to get results
*/!*
};
@ -478,5 +474,5 @@ So we should use a Function Expression only when a Function Declaration is not f
Arrow functions are handy for one-liners. They come in two flavors:
1. Without figure brackets: `(...args) => expression` -- the right side is an expression: the function evaluates it and returns the result.
2. With figure brackets: `(...args) => { body }` -- brackets allow us to write multiple statements inside the function, but we need an explicit `return` to return something.
1. Without curly braces: `(...args) => expression` -- the right side is an expression: the function evaluates it and returns the result.
2. With curly braces: `(...args) => { body }` -- brackets allow us to write multiple statements inside the function, but we need an explicit `return` to return something.

View file

@ -2,8 +2,6 @@
This chapter briefly recaps the features of JavaScript that we've learned by now, paying special attention to subtle moments.
[cut]
## Code structure
Statements are delimited with a semicolon:
@ -90,7 +88,7 @@ There are 7 data types:
- `boolean` for logical values: `true/false`,
- `null` -- a type with a single value `null`, meaning "empty" or "does not exist",
- `undefined` -- a type with a single value `undefined`, meaning "not assigned",
- `object` and `symbol` -- for complex data structures and unique identifiers, we didn't learn them yet.
- `object` and `symbol` -- for complex data structures and unique identifiers, we haven't learnt them yet.
The `typeof` operator returns the type for a value, with two exceptions:
```js
@ -134,7 +132,7 @@ JavaScript supports the following operators:
Arithmetical
: Regular: `* + - /`, also `%` for the remainder and `**` for power of a number.
Binary plus `+` concatenates strings. And if any of the operands is a string, the other one is converted to string too:
The binary plus `+` concatenates strings. And if any of the operands is a string, the other one is converted to string too:
```js run
alert( '1' + 2 ); // '12', string
@ -148,7 +146,7 @@ Bitwise
: Bitwise operators work with integers on bit-level: see the [docs](mdn:/JavaScript/Reference/Operators/Bitwise_Operators) when they are needed.
Ternary
: The only operator with three parameters: `cond ? resultA : result B`. If `cond` is truthy, returns `resultA`, otherwise `resultB`.
: The only operator with three parameters: `cond ? resultA : resultB`. If `cond` is truthy, returns `resultA`, otherwise `resultB`.
Logical operators
: Logical AND `&&` and OR `||` perform short-circuit evaluation and then return the value where it stopped.
@ -204,7 +202,7 @@ Later we'll study more types of loops to deal with objects.
## The "switch" construct
The "switch" construct can replace multiple `if` checks. It uses `===` for comparisons.
The "switch" construct can replace multiple `if` checks. It uses `===` (strict equality) for comparisons.
For instance:
@ -216,7 +214,7 @@ switch (age) {
alert("Won't work"); // the result of prompt is a string, not a number
case "18":
alert("This works!"");
alert("This works!");
break;
default:
@ -250,7 +248,7 @@ We covered three ways to create a function in JavaScript:
}
```
Function expression can have a name, like `sum = function name(a, b)`, but that `name` is only visible inside that function.
Function expressions can have a name, like `sum = function name(a, b)`, but that `name` is only visible inside that function.
3. Arrow functions:
@ -273,7 +271,7 @@ We covered three ways to create a function in JavaScript:
- Functions may have local variables: those declared inside its body. Such variables are only visible inside the function.
- Parameters can have default values: `function sum(a=1, b=2) {...}`.
- Parameters can have default values: `function sum(a = 1, b = 2) {...}`.
- Functions always return something. If there's no `return` statement, then the result is `undefined`.

View file

@ -6,8 +6,6 @@ All modern browsers and most other environments support "debugging" -- a special
We'll be using Chrome here, because it's probably the most feature-rich in this aspect.
[cut]
## The "sources" pane
Your Chrome version may look a little bit different, but it still should be obvious what's there.
@ -28,9 +26,9 @@ Let's click it and select `index.html` and then `hello.js` in the tree view. Her
Here we can see three zones:
1. **Resources zone** lists html, javascript, css and other files including images that are attached to the page. Chrome extensions may appear here too.
2. **Source zone** shows the source code.
3. **Information and control zone** is for debugging, we'll explore it soon.
1. The **Resources zone** lists HTML, JavaScript, CSS and other files, including images that are attached to the page. Chrome extensions may appear here too.
2. The **Source zone** shows the source code.
3. The **Information and control zone** is for debugging, we'll explore it soon.
Now you could click the same toggler <span class="devtools" style="background-position:-200px -76px"></span> again to hide the resources list and give the code some space.
@ -46,7 +44,7 @@ For example, here `1+2` results in `3`, and `hello("debugger")` returns nothing,
## Breakpoints
Let's examine what's going on within the code of the [example page](debugging/index.html). In `hello.js`, click at the line number `4`. Yes, right on the `"4"` digit, not on the code.
Let's examine what's going on within the code of the [example page](debugging/index.html). In `hello.js`, click at line number `4`. Yes, right on the `4` digit, not on the code.
Congratulations! You've set a breakpoint. Please also click on the number for line `8`.
@ -101,7 +99,7 @@ Please open the informational dropdowns to the right (labeled with arrows). They
1. **`Watch` -- shows current values for any expressions.**
You can click the plus `+` and input an expression. The debugger will show its value at any moment, automatically recalculating it in in the process of execution.
You can click the plus `+` and input an expression. The debugger will show its value at any moment, automatically recalculating it in the process of execution.
2. **`Call Stack` -- shows the nested calls chain.**
@ -123,7 +121,7 @@ Now it's time to *trace* the script.
There are buttons for it at the top of the right pane. Let's engage them.
<span class="devtools" style="background-position:-7px -76px"></span> -- continue the execution, hotkey `key:F8`.
: Resumes the execution. If there are no additional breakpoints, then the execution just continues and the debugger looses the control.
: Resumes the execution. If there are no additional breakpoints, then the execution just continues and the debugger loses control.
Here's what we can see after a click on it:
@ -178,7 +176,7 @@ As we can see, there are three main ways to pause a script:
Then we can examine variables and step on to see where the execution goes wrong.
There are many more options in developer tools than covered here. The full manual is at <https://developers.google.com/web/tools/chrome-devtools>
There are many more options in developer tools than covered here. The full manual is at <https://developers.google.com/web/tools/chrome-devtools>.
The information from this chapter is enough to begin debugging, but later, especially if you do a lot of browser stuff, please go there and look through more advanced capabilities of developer tools.

View file

@ -6,8 +6,6 @@ That is actually an art of programming -- to take a complex task and code it in
One thing to help is the good code style.
[cut]
## Syntax
A cheatsheet with the rules (more details below):
@ -42,9 +40,9 @@ Now let's discuss the rules and reasons for them in detail.
Nothing is "carved in stone" here. Everything is optional and can be changed: these are coding rules, not religious dogmas.
### Figure brackets
### Curly braces
In most JavaScript projects figure brackets are written on the same line, not on the new line. A so-called "egyptian" style. There's also a space before an opening bracket.
In most JavaScript projects curly braces are written on the same line as the corresponding keyword, not on the new line, a so-called "Egyptian" style. There's also a space before an opening bracket.
Like this:
@ -295,7 +293,7 @@ Most linters are integrated with editors: just enable the plugin in the editor a
For instance, for ESLint you should do the following:
1. Install [Node.JS](https://nodejs.org/).
2. Install ESLint with the command `npm install -g eslint` (npm is Node.JS package installer).
2. Install ESLint with the command `npm install -g eslint` (npm is a JavaScript package installer).
3. Create a config file named `.eslintrc` in the root of your JavaScript project (in the folder that contains all your files).
Here's an example of `.eslintrc`:
@ -331,6 +329,6 @@ Also certain IDEs support built-in linting, that also may be good, but not so tu
All syntax rules from this chapter and the style guides aim to increase readability, so all of them are debatable.
When we think about "how to write better?", the sole criterion is "what makes the code more readable and easier to understand? what helps to evade errors?" That's the main thing to keep in mind when choosing the style or discussing which one is better.
When we think about "how to write better?", the sole criterion is "what makes the code more readable and easier to understand? what helps to avoid errors?" That's the main thing to keep in mind when choosing the style or discussing which one is better.
Read style guides to see the latest ideas about that and follow those that you find the best.

View file

@ -18,7 +18,7 @@ complex;
code;
```
But in a good code the amount of such "explanatory" comments should be minimal. Seriously, a code should be easy to understand without them.
But in good code the amount of such "explanatory" comments should be minimal. Seriously, code should be easy to understand without them.
There's a great rule about that: "if the code is so unclear that it requires a comment, then maybe it should be rewritten instead".
@ -58,8 +58,9 @@ function showPrimes(n) {
function isPrime(n) {
for (let i = 2; i < n; i++) {
if ( n % i == 0) return false;
if (n % i == 0) return false;
}
return true;
}
```
@ -112,7 +113,7 @@ function addJuice(container) {
Once again, functions themselves tell what's going on. There's nothing to comment. And also the code structure is better when split. It's clear what every function does, what it takes and what it returns.
In reality, we can't totally evade "explanatory" comments. There are complex algorithms. And there are smart "tweaks" for purposes of optimization. But generally we should try to keep the code simple and self-descriptive.
In reality, we can't totally avoid "explanatory" comments. There are complex algorithms. And there are smart "tweaks" for purposes of optimization. But generally we should try to keep the code simple and self-descriptive.
## Good comments
@ -145,7 +146,7 @@ Document a function usage
Also, there are tools like [JSDoc 3](https://github.com/jsdoc3/jsdoc) that can generate HTML-documentation from the comments. You can read more information about JSDoc at <http://usejsdoc.org/>.
Why is the task solved this way?
: What's written is important. But what's *not* written maybe even more important to understand what's going on. Why is the task solved exactly this way? The code gives no answer.
: What's written is important. But what's *not* written may be even more important to understand what's going on. Why is the task solved exactly this way? The code gives no answer.
If there are many ways to solve the task, why this one? Especially when it's not the most obvious one.
@ -157,7 +158,7 @@ Why is the task solved this way?
Comments that explain the solution are very important. They help to continue development the right way.
Any subtle features of the code? Where they are used?
: If the code has anything subtle and counter-obvious, it's definitely worth commenting.
: If the code has anything subtle and counter-intuitive, it's definitely worth commenting.
## Summary
@ -171,7 +172,7 @@ Good comments allow us to maintain the code well, come back to it after a delay
- Function usage.
- Important solutions, especially when not immediately obvious.
**Evade comments:**
**Avoid comments:**
- That tell "how code works" and "what it does".
- Put them only if it's impossible to make the code so simple and self-descriptive that it doesn't require those.

View file

@ -1,15 +1,24 @@
# Ninja code
Programmer ninjas of the past used these tricks to make code maintainers cry. Code review gurus look for them in test tasks. Novice developers sometimes use them even better than programmer ninjas.
```quote author="Confucius"
Learning without thought is labor lost; thought without learning is perilous.
```
Programmer ninjas of the past used these tricks to make sharpen the mind of code maintainers.
Code review gurus look for them in test tasks.
Novice developers sometimes use them even better than programmer ninjas.
Read them carefully and find out who you are -- a ninja, a novice, or maybe a code reviewer?
[cut]
```warn header="Irony detected"
These are rules of writing bad code. Just... You know, some people miss the point.
Many try to follow ninja paths. Few succeed.
```
## Brevity is the soul of wit
Make the code as short as possible. Show how smart you are.
@ -34,7 +43,7 @@ The Dao hides in wordlessness. Only the Dao is well begun and well
completed.
```
Another way to code faster (and much worse!) is to use single-letter variable names everywhere. Like `a`, `b` or `c`.
Another way to code faster is to use single-letter variable names everywhere. Like `a`, `b` or `c`.
A short variable disappears in the code like a real ninja in the forest. No one will be able to find it using "search" of the editor. And even if someone does, he won't be able to "decipher" what the name `a` or `b` means.
@ -70,19 +79,21 @@ While choosing a name try to use the most abstract word. Like `obj`, `data`, `va
...But what to do if `data` is already taken? Try `value`, it's also universal. After all, a variable eventually gets a *value*.
- **Name the variable by its type: `str`, `num`...**
- **Name a variable by its type: `str`, `num`...**
Give them a try. A young ninja may wonder -- do such names make the code worse? Actually, yes!
Give them a try. A young initiate may wonder -- are such names really useful for a ninja? Indeed, they are!
From one hand, the variable name still means something. It says what's inside the variable: a string, a number or something else. But when an outsider tries to understand the code, he'll be surprised to see that there's actually no information at all!
Sure, the variable name still means something. It says what's inside the variable: a string, a number or something else. But when an outsider tries to understand the code, he'll be surprised to see that there's actually no information at all! And will ultimately fail to alter your well-thought code.
Indeed, the value type is easy to find out by debugging. But what's the meaning of the variable? Which string/number does it store? There's just no way to figure out without a good meditation!
The value type is easy to find out by debugging. But what's the meaning of the variable? Which string/number does it store?
- **...But what if there are no more such names?** Just add a letter: `item1, item2, elem5, data1`...
There's just no way to figure out without a good meditation!
- **...But what if there are no more such names?** Just add a number: `data1, item2, elem5`...
## Attention test
Only a truly attentive programmer should be able to understand the code. But how to check that?
Only a truly attentive programmer should be able to understand your code. But how to check that?
**One of the ways -- use similar variable names, like `date` and `data`.**

View file

@ -4,8 +4,6 @@ Automated testing will be used in further tasks.
It's actually a part of the "educational minimum" of a developer.
[cut]
## Why we need tests?
When we write a function, we can usually imagine what it should do: which parameters give which results.

View file

@ -46,7 +46,7 @@ alert('Press the "Play" button in the upper-right corner to run');
```
Examples that use modern JS will work only if your browser supports it.
```
````
```offline
As you're reading the offline version, examples are not runnable. But they usually work :)

View file

@ -2,7 +2,7 @@ Just loop over the object and `return false` immediately if there's at least one
```js
function isEmpty(obj) {
for(let key in obj) {
for (let key in obj) {
return false;
}
return true;

View file

@ -4,10 +4,10 @@ let salaries = {
John: 100,
Ann: 160,
Pete: 130
}
};
let sum = 0;
for(let key in salaries) {
for (let key in salaries) {
sum += salaries[key];
}

View file

@ -5,11 +5,9 @@ As we know from the chapter <info:types>, there are seven language types in Java
In contrast, objects are used to store keyed collections of various data and more complex entities. In JavaScript, objects penetrate almost every aspect of the language. So we must understand them first before going in-depth anywhere else.
[cut]
An object can be created with figure brackets `{…}` with an optional list of *properties*. A property is a "key: value" pair, where `key` is a string (also called a "property name"), and `value` can be anything.
We can imagine an object as a cabinet with signed files. Every piece of data is stored in it's file by the key. It's easy to find a file by it's name or add/remove a file.
We can imagine an object as a cabinet with signed files. Every piece of data is stored in its file by the key. It's easy to find a file by its name or add/remove a file.
![](object.png)
@ -85,7 +83,6 @@ let user = {
![](object-user-props.png)
````smart header="Trailing comma"
The last property in the list may end with a comma:
```js
let user = {
@ -94,7 +91,6 @@ let user = {
}
```
That is called a "trailing" or "hanging" comma. Makes it easier to add/remove/move around properties, because all lines become alike.
````
## Square brackets
@ -189,7 +185,7 @@ We can use more complex expressions inside square brackets:
```js
let fruit = 'apple';
let bag = {
['apple' + 'Computers']: 5 // bag.appleComputers = 5
[fruit + 'Computers']: 5 // bag.appleComputers = 5
};
```
@ -222,7 +218,14 @@ obj.__proto__ = 5;
alert(obj.__proto__); // [object Object], didn't work as intended
```
As we see from the code, the assignment to a primitive `5` 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.
As we see from the code, the assignment to a primitive `5` is ignored.
That can become a source of bugs and even vulnerabilies if we intent to store arbitrary key-value pairs in an object, and allow a visitor to specify the keys.
In that case the visitor may choose "__proto__" as the key, and the assignment logic will be ruined (as shown above).
There is a way to make objects treat `__proto__` as a regular property, which we'll cover later, but first we need to know more about objects.
There's also 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.
````
@ -365,7 +368,7 @@ Also, we could use another variable name here instead of `key`. For instance, `"
### 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? Can we rely on it?
Are objects ordered? In other words, if we loop over an object, do we get all properties in the same order they were added? Can we rely on this?
The short answer is: "ordered in a special fashion": integer properties are sorted, others appear in creation order. The details follow.
@ -404,8 +407,8 @@ So, "49" is an integer property name, because when it's transformed to an intege
```js run
// Math.trunc is a built-in function that removes the decimal part
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
alert( String(Math.trunc(Number("+49"))) ); // "49", not same "+49" ⇒ not integer property
alert( String(Math.trunc(Number("1.2"))) ); // "1", not same "1.2" ⇒ not integer property
```
````
@ -465,7 +468,7 @@ As a result we have two independent variables, each one is storing the string `"
Objects are not like that.
**A variable stores not the object itself, but it's "address in memory", in other words "a reference" to it.**
**A variable stores not the object itself, but its "address in memory", in other words "a reference" to it.**
Here's the picture for the object:
@ -509,7 +512,7 @@ admin.name = 'Pete'; // changed by the "admin" reference
alert(*!*user.name*/!*); // 'Pete', changes are seen from the "user" reference
```
The example above demonstrates that there is only one object. Like if we had a cabinet with two keys and used one of them (`admin`) to get into it. Then, if we later use the other key (`user`) we would see changes.
The example above demonstrates that there is only one object. As if we had a cabinet with two keys and used one of them (`admin`) to get into it. Then, if we later use the other key (`user`) we would see changes.
### Comparison by reference
@ -536,7 +539,7 @@ let b = {}; // two independent objects
alert( a == b ); // false
```
For comparisons like `obj1 > obj2` or for a comparison against a primitive `obj == 5`, objects are converted to primitives. We'll study how object conversions work very soon, but to say the truth, such comparisons are necessary very rarely and usually are a result of a coding mistake.
For comparisons like `obj1 > obj2` or for a comparison against a primitive `obj == 5`, objects are converted to primitives. We'll study how object conversions work very soon, but to tell the truth, such comparisons are necessary very rarely and usually are a result of a coding mistake.
### Const object
@ -696,9 +699,9 @@ user.sizes.width++; // change a property from one place
alert(clone.sizes.width); // 51, see the result from the other one
```
To fix that, we should use the cloning loop that examines each value of `user[key]` and, if it's an object, then replicate it's structure as well. That is called a "deep cloning".
To fix that, we should use the cloning loop that examines each value of `user[key]` and, if it's an object, then replicate its 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](https://w3c.github.io/html/infrastructure.html#internal-structured-cloning-algorithm). In order not to reinvent the wheel, we can use a working implementation of it from the JavaScript library [lodash](https://lodash.com), the method is called [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep).
There's a standard algorithm for deep cloning that handles the case above and more complex cases, called the [Structured cloning algorithm](http://w3c.github.io/html/infrastructure.html#safe-passing-of-structured-data). In order not to reinvent the wheel, we can use a working implementation of it from the JavaScript library [lodash](https://lodash.com), the method is called [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep).
@ -734,4 +737,4 @@ There are many other kinds of objects in JavaScript:
They have their special features that we'll study later. 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.
Objects in JavaScript are very powerful. Here we've just scratched the surface of a topic that is really huge. We'll be closely working with objects and learning more about them in further parts of the tutorial.

View file

@ -4,8 +4,6 @@ Memory management in JavaScript is performed automatically and invisibly to us.
What happens when something is not needed any more? How does the JavaScript engine discover it and clean it up?
[cut]
## Reachability
The main concept of memory management in JavaScript is *reachability*.

View file

@ -5,22 +5,27 @@ By specification, object property keys may be either of string type, or of symbo
Till now we've only seen strings. Now let's see the advantages that symbols can give us.
[cut]
## Symbols
"Symbol" value represents an unique identifier with a given name.
"Symbol" value represents a unique identifier.
A value of this type can be created using `Symbol(name)`:
A value of this type can be created using `Symbol()`:
```js
// id is a symbol with the name "id"
// id is a new symbol
let id = Symbol();
```
We can also give symbol a description (also called a symbol name), mostly useful for debugging purposes:
```js
// id is a symbol with the description "id"
let id = Symbol("id");
```
Symbols are guaranteed to be unique. Even if we create many symbols with the same name, they are different values.
Symbols are guaranteed to be unique. Even if we create many symbols with the same description, they are different values. The description is just a label that doesn't affect anything.
For instance, here are two symbols with the same name -- they are not equal:
For instance, here are two symbols with the same description -- they are not equal:
```js run
let id1 = Symbol("id");
@ -56,13 +61,11 @@ alert(id.toString()); // Symbol(id), now it works
That's a "language guard" against messing up, because strings and symbols are fundamentally different and should not occasionally convert one into another.
````
## "Hidden" properties
Symbols allow us to create "hidden" 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:
For instance, if we want to store an "identifier" for the object `user`, we can use a symbol as a key for it:
```js run
let user = { name: "John" };
@ -72,11 +75,13 @@ user[id] = "ID Value";
alert( user[id] ); // we can access the data using the symbol as the key
```
Now let's imagine that another script wants to have its own "id" property inside `user`, for its own purposes. That may be another JavaScript library, so the scripts are completely unaware of each other.
What's the benefit over using `Symbol("id")` over a string `"id"`?
No problem. It can create its own `Symbol("id")`.
Let's make the example a bit deeper to see that.
The script:
Imagine that another script wants to have its own "id" property inside `user`, for its own purposes. That may be another JavaScript library, so the scripts are completely unaware of each other.
Then that script can create its own `Symbol("id")`, like this:
```js
// ...
@ -87,7 +92,7 @@ user[id] = "Their id value";
There will be no conflict, because symbols are always different, even if they have the same name.
Please note that if we used a string `"id"` instead of a symbol for the same purpose, then there *would* be a conflict:
Now note that if we used a string `"id"` instead of a symbol for the same purpose, then there *would* be a conflict:
```js run
let user = { name: "John" };
@ -134,7 +139,7 @@ let user = {
};
*!*
for(let key in user) alert(key); // name, age (no symbols)
for (let key in user) alert(key); // name, age (no symbols)
*/!*
// the direct access by the symbol works
@ -176,25 +181,27 @@ alert( obj[0] ); // test (same property)
## Global symbols
As we've seen, usually all symbols are different, even if they have the same name. But sometimes we want same-named symbols to be the same entities.
As we've seen, usually all symbols are different, even if they have the same names. But sometimes we want same-named symbols to be same entities.
For instance, different parts of our application want to access symbol `"id"` meaning exactly the same property.
To achieve that, there exists a *global symbol registry*. We can create symbols in it and access them later, and it guarantees that repeated accesses by the same name return exactly the same symbol.
In order to create or read a symbol in the registry, use `Symbol.for(name)`.
In order to create or read a symbol in the registry, use `Symbol.for(key)`.
That call checks the global registry, and if there's a symbol described as `key`, then returns it, otherwise creates a new symbol `Symbol(key)` and stores it in the registry by the given `key`.
For instance:
```js run
// read from the global registry
let name = Symbol.for("name"); // if the symbol did not exist, it is created
let id = Symbol.for("id"); // if the symbol did not exist, it is created
// read it again
let nameAgain = Symbol.for("name");
let idAgain = Symbol.for("id");
// the same symbol
alert( name === nameAgain ); // true
alert( id === idAgain ); // true
```
Symbols inside the registry are called *global symbols*. If we want an application-wide symbol, accessible everywhere in the code -- that's what they are for.
@ -207,7 +214,7 @@ In JavaScript, as we can see, that's right for global symbols.
### Symbol.keyFor
For global symbols, not only `Symbol.for(name)` returns a symbol by name, but there's a reverse call: `Symbol.keyFor(sym)`, that does the reverse: returns a name by a global symbol.
For global symbols, not only `Symbol.for(key)` returns a symbol by name, but there's a reverse call: `Symbol.keyFor(sym)`, that does the reverse: returns a name by a global symbol.
For instance:
@ -220,20 +227,16 @@ alert( Symbol.keyFor(sym) ); // name
alert( Symbol.keyFor(sym2) ); // id
```
The `Symbol.keyFor` internally uses the global symbol registry to look up the name for the symbol. So it doesn't work for non-global symbols. If the symbol is not global, it won't be able to find it and return `undefined`.
The `Symbol.keyFor` internally uses the global symbol registry to look up the key for the symbol. So it doesn't work for non-global symbols. If the symbol is not global, it won't be able to find it and return `undefined`.
For instance:
```js run
alert( Symbol.keyFor(Symbol.for("name")) ); // name, global symbol
alert( Symbol.keyFor(Symbol("name2")) ); // undefined, non-global symbol
alert( Symbol.keyFor(Symbol("name2")) ); // undefined, the argument isn't a global symbol
```
So, for global symbols the name may be indeed helpful, as we can get a symbol by id.
And for non-global symbols the name is only used for debugging purposes, like printing out a symbol.
## System symbols
There exist many "system" symbols that JavaScript uses internally, and we can use them to fine-tune various aspects of our objects.
@ -254,9 +257,9 @@ Other symbols will also become familiar when we study the corresponding language
`Symbol` is a primitive type for unique identifiers.
Symbols are created with `Symbol(name)` call.
Symbols are created with `Symbol()` call with an optional description.
Symbols are always different values, even if they have the same name. If we want same-named symbols to be equal, then we should use the global registry: `Symbol.for(name)` returns (creates if needed) a global symbol with the given name. Multiple calls of `Symbol.for` return exactly the same symbol.
Symbols are always different values, even if they have the same name. If we want same-named symbols to be equal, then we should use the global registry: `Symbol.for(key)` returns (creates if needed) a global symbol with `key` as the name. Multiple calls of `Symbol.for` return exactly the same symbol.
Symbols have two main use cases:

View file

@ -13,8 +13,6 @@ And, in the real world, a user can *act*: select something from the shopping car
Actions are represented in JavaScript by functions in properties.
[cut]
## Method examples
For the start, let's teach the `user` to say hello:
@ -314,11 +312,11 @@ The result of a property access `user.hi` is not a function, but a value of Refe
(user, "hi", true)
```
When parentheses `()` are called on the Reference Type, they receive the full information about the object and it's method, and can set the right `this` (`=user` in this case).
When parentheses `()` are called on the Reference Type, they receive the full information about the object and its method, and can set the right `this` (`=user` in this case).
Any other operation like assignment `hi = user.hi` discards the reference type as a whole, takes the value of `user.hi` (a function) and passes it on. So any further operation "loses" `this`.
So, as the result, the value of `this` is only passed the right way if the function is called directly using a dot `obj.method()` or square brackets `obj[method]()` syntax (they do the same here).
So, as the result, the value of `this` is only passed the right way if the function is called directly using a dot `obj.method()` or square brackets `obj[method]()` syntax (they do the same here). Later in this tutorial, we will learn various ways to solve this problem such as [func.bind()](/bind#solution-2-bind).
## Arrow functions have no "this"

View file

@ -7,8 +7,6 @@ There are special methods in objects that do the conversion.
In the chapter <info:type-conversions> we've seen the rules for numeric, string and boolean conversions of primitives. But we left a gap for objects. Now, as we know about methods and symbols it becomes possible to close it.
[cut]
For objects, there's no to-boolean conversion, because all objects are `true` in a boolean context. So there are only string and numeric conversions.
The numeric conversion happens when we subtract objects or apply mathematical functions. For instance, `Date` objects (to be covered in the chapter <info:date>) can be subtracted, and the result of `date1 - date2` is the time difference between two dates.
@ -213,7 +211,7 @@ For instance:
```smart header="Historical notes"
For historical reasons, methods `toString` or `valueOf` *should* return a primitive: if any of them returns an object, then there's no error, but that object is ignored (like if the method didn't exist).
In contrast, `Symbol.toPrimitive` *must* return an primitive, otherwise there will be an error.
In contrast, `Symbol.toPrimitive` *must* return a primitive, otherwise, there will be an error.
```
## Summary

View file

@ -4,8 +4,6 @@ The regular `{...}` syntax allows to create one object. But often we need to cre
That can be done using constructor functions and the `"new"` operator.
[cut]
## Constructor function
Constructor functions technically are regular functions. There are two conventions though:
@ -87,6 +85,10 @@ The constructor can't be called again, because it is not saved anywhere, just cr
## Dual-syntax constructors: new.target
```smart header="Advanced stuff"
The syntax from this section is rarely used, skip it unless you want to know everything.
```
Inside a function, we can check whether it was called with `new` or without it, using a special `new.target` property.
It is empty for regular calls and equals the function if called with `new`:
@ -96,14 +98,18 @@ function User() {
alert(new.target);
}
// without new:
// without "new":
*!*
User(); // undefined
*/!*
// with new:
// with "new":
*!*
new User(); // function User { ... }
*/!*
```
That can be used to allow both `new` and regular syntax to work the same:
That can be used to allow both `new` and regular calls to work the same. That is, create the same object:
```js run
function User(name) {
@ -118,7 +124,9 @@ let john = User("John"); // redirects call to new User
alert(john.name); // John
```
This approach is sometimes used in libraries to make the syntax more flexible. Probably not a good thing to use everywhere though, because omitting `new` makes it a bit less obvious what's going on. With `new` we all know that the new object is being created, that's a good thing.
This approach is sometimes used in libraries to make the syntax more flexible. So that people may call the function with or without `new`, and it still works.
Probably not a good thing to use everywhere though, because omitting `new` makes it a bit less obvious what's going on. With `new` we all know that the new object is being created.
## Return from constructors

View file

@ -4,8 +4,6 @@ JavaScript allows us to work with primitives (strings, numbers etc) as if they w
They also provide methods to call and such. We will study those soon, but first we'll see how it works, because, of course, primitives are not objects (and here we will make it even more clear).
[cut]
Let's look at the key distinction between primitives and objects.
A primitive
@ -70,7 +68,7 @@ So primitives can provide methods, but they still remain lightweight.
The JavaScript engine highly optimizes this process. It may even skip the creation of the extra object at all. But it must still adhere to the specification and behave as if it creates one.
A number has methods of it's own, for instance, [toFixed(n)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed) rounds the number to the given precision:
A number has methods of its own, for instance, [toFixed(n)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed) rounds the number to the given precision:
```js run
let n = 1.23456;

View file

@ -28,6 +28,6 @@ Note that `63.5` has no precision loss at all. That's because the decimal part `
```js run
alert( Math.round(6.35 * 10) / 10); // 6.35 -> 63.5 -> 63(rounded) -> 6.3
alert( Math.round(6.35 * 10) / 10); // 6.35 -> 63.5 -> 64(rounded) -> 6.4
```

View file

@ -15,5 +15,3 @@ alert( random(1, 5) ); // 1.2345623452
alert( random(1, 5) ); // 3.7894332423
alert( random(1, 5) ); // 4.3435234525
```
You can use the solution of the [previous task](info:task/random-min-max) as the base.

View file

@ -27,7 +27,7 @@ Now we can clearly see that `1` gets twice less values than `2`. And the same wi
# The correct solution
There are many correct solutions to the task. One of them is to adjust interval borders. To ensure the same intervals, we can generate values from `0.5 to 2.5`, thus adding the required probabilities to the edges:
There are many correct solutions to the task. One of them is to adjust interval borders. To ensure the same intervals, we can generate values from `0.5 to 3.5`, thus adding the required probabilities to the edges:
```js run
*!*

View file

@ -15,4 +15,6 @@ Examples of its work:
alert( random(1, 5) ); // 1
alert( random(1, 5) ); // 3
alert( random(1, 5) ); // 5
```
```
You can use the solution of the [previous task](info:task/random-min-max) as the base.

View file

@ -249,10 +249,10 @@ Can we work around the problem? Sure, there're a number of ways:
2. We can temporarily turn numbers into integers for the maths and then revert it back. It works like this:
```js run
alert( (0.1*10 + 0.2*10) / 10 ); // 0.3
alert( (0.1 * 10 + 0.2 * 10) / 10 ); // 0.3
```
This works because when we do `0.1*10 = 1` and `0.2 * 10 = 2` then both numbers become integers, and there's no precision loss.
This works because when we do `0.1 * 10 = 1` and `0.2 * 10 = 2` then both numbers become integers, and there's no precision loss.
3. If we were dealing with a shop, then the most radical solution would be to store all prices in cents and use no fractions at all. But what if we apply a discount of 30%? In practice, totally evading fractions is rarely feasible, so the solutions above help avoid this pitfall.
@ -270,7 +270,7 @@ JavaScript doesn't trigger an error in such events. It does its best to fit the
````
```smart header="Two zeroes"
Another funny consequence of the internal representation of numbers is the existance of two zeroes: `0` and `-0`.
Another funny consequence of the internal representation of numbers is the existence of two zeroes: `0` and `-0`.
That's because a sign is represented by a single bit, so every number can be positive or negative, including a zero.

View file

@ -1,5 +1,5 @@
describe("truncate", function() {
it("truncate the long string to the given lenth (including the ellipsis)", function() {
it("truncate the long string to the given length (including the ellipsis)", function() {
assert.equal(
truncate("What I'd like to tell on this topic is:", 20),
"What I'd like to te…"
@ -13,4 +13,4 @@ describe("truncate", function() {
);
});
});
});

View file

@ -4,8 +4,6 @@ In JavaScript, the textual data is stored as strings. There is no separate type
The internal format for strings is always [UTF-16](https://en.wikipedia.org/wiki/UTF-16), it is not tied to the page encoding.
[cut]
## Quotes
Let's recall the kinds of quotes.
@ -19,7 +17,7 @@ let double = "double-quoted";
let backticks = `backticks`;
```
Single and double quotes are essentially the same. Backticks however allow us to embed any expression into the string, including function calls:
Single and double quotes are essentially the same. Backticks, however, allow us to embed any expression into the string, including function calls:
```js run
function sum(a, b) {
@ -49,7 +47,7 @@ let guestList = "Guests: // Error: Unexpected token ILLEGAL
Single and double quotes come from ancient times of language creation when the need for multiline strings was not taken into account. Backticks appeared much later and thus are more versatile.
Backticks also allow us to specify a "template function" before the first backtick. The syntax is: <code>func&#96;string&#96;</code>. The function `func` is called automatically, receives the string and embedded expressions and can process them. You can read more about it in the [docs](mdn:JavaScript/Reference/Template_literals#Tagged_template_literals). This is called "tagged templates". This feature makes it easier to wrap strings into custom templating or other functionality, but it is rarely used.
Backticks also allow us to specify a "template function" before the first backtick. The syntax is: <code>func&#96;string&#96;</code>. The function `func` is called automatically, receives the string and embedded expressions and can process them. You can read more about it in the [docs](mdn:/JavaScript/Reference/Template_literals#Tagged_template_literals). This is called "tagged templates". This feature makes it easier to wrap strings into custom templating or other functionality, but it is rarely used.
## Special characters
@ -88,8 +86,8 @@ Examples with unicode:
```js run
alert( "\u00A9" ); // ©
alert( "\u{20331}" ); // 𠌱, a rare chinese hieroglyph (long unicode)
alert( "\u{1F60D}"); // a smiling face sumbol (another long unicode)
alert( "\u{20331}" ); // , a rare chinese hieroglyph (long unicode)
alert( "\u{1F60D}" ); // 😍, a smiling face symbol (another long unicode)
```
All special characters start with a backslash character `\`. It is also called an "escape character".
@ -166,7 +164,7 @@ alert( str.charAt(1000) ); // '' (an empty string)
We can also iterate over characters using `for..of`:
```js run
for(let char of "Hello") {
for (let char of "Hello") {
alert(char); // H,e,l,l,o (char becomes "H", then "e", then "l" etc)
}
```
@ -236,7 +234,7 @@ alert( str.indexOf("id") ); // 1, "id" is found at the position 1 (..idget with
The optional second parameter allows us to search starting from the given position.
For instance, the first occurence of `"id"` is at position `1`. To look for the next occurence, let's start the search from position `2`:
For instance, the first occurrence of `"id"` is at position `1`. To look for the next occurrence, let's start the search from position `2`:
```js run
let str = 'Widget with id';
@ -280,7 +278,7 @@ while ((pos = str.indexOf(target, pos + 1)) != -1) {
```smart header="`str.lastIndexOf(pos)`"
There is also a similar method [str.lastIndexOf(pos)](mdn:js/String/lastIndexOf) that searches from the end of a string to its beginning.
It would list the occurences in the reverse order.
It would list the occurrences in the reverse order.
```
There is a slight inconvenience with `indexOf` in the `if` test. We can't put it in the `if` like this:
@ -379,8 +377,8 @@ There are 3 methods in JavaScript to get a substring: `substring`, `substr` and
```js run
let str = "stringify";
alert( str.slice(0,5) ); // 'strin', the substring from 0 to 5 (not including 5)
alert( str.slice(0,1) ); // 's', from 0 to 1, but not including 1, so only character at 0
alert( str.slice(0, 5) ); // 'strin', the substring from 0 to 5 (not including 5)
alert( str.slice(0, 1) ); // 's', from 0 to 1, but not including 1, so only character at 0
```
If there is no second argument, then `slice` goes till the end of the string:
@ -445,7 +443,7 @@ Let's recap these methods to avoid any confusion:
| method | selects... | negatives |
|--------|-----------|-----------|
| `slice(start, end)` | from `start` to `end` | allows negatives |
| `slice(start, end)` | from `start` to `end` (not including `end`) | allows negatives |
| `substring(start, end)` | between `start` and `end` | negative values mean `0` |
| `substr(start, length)` | from `start` get `length` characters | allows negative `start` |
@ -548,7 +546,7 @@ For instance:
alert( 'Österreich'.localeCompare('Zealand') ); // -1
```
This method actually has two additional arguments specified in [the documentation](mdn:js/String/localeCompare), which allow it 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.
This method actually has two additional arguments specified in [the documentation](mdn:js/String/localeCompare), which allows it to specify the language (by default taken from the environment) and setup additional rules like case sensitivity or should `"a"` and `"á"` be treated as the same etc.
## Internals, Unicode

View file

@ -3,7 +3,7 @@
```js run
let styles = ["Jazz", "Blues"];
styles.push("Rock-n-Roll");
styles[(styles.length + 1) / 2] = "Classics";
styles[Math.floor((styles.length - 1) / 2)] = "Classics";
alert( styles.shift() );
styles.unshift("Rap", "Reggie");
```

View file

@ -10,7 +10,7 @@ Let's try 5 array operations.
2. Append "Rock-n-Roll" to the end.
3. Replace the value in the middle by "Classics". Your code for finding the middle value should work for any arrays with odd length.
4. Strip off the first value of the array and show it.
5. Prepend `Rap` and `Reggie` to the array.
5. Prepend `Rap` and `Reggae` to the array.
The array in the process:
@ -19,6 +19,6 @@ Jazz, Blues
Jazz, Bues, Rock-n-Roll
Jazz, Classics, Rock-n-Roll
Classics, Rock-n-Roll
Rap, Reggie, Classics, Rock-n-Roll
Rap, Reggae, Classics, Rock-n-Roll
```

View file

@ -17,7 +17,7 @@ The task consists of two parts.
alert( calc.calculate("3 + 7") ); // 10
```
2. Then add the method `addOperator(name, func)` that teaches the calculator a new operation. It takes the operator `name` and the two-argument function `func(a,b)` that implements it.
2. Then add the method `addMethod(name, func)` that teaches the calculator a new operation. It takes the operator `name` and the two-argument function `func(a,b)` that implements it.
For instance, let's add the multiplication `*`, division `/` and power `**`:

View file

@ -8,8 +8,6 @@ It is not convenient to use an object here, because it provides no methods to ma
There exists a special data structure named `Array`, to store ordered collections.
[cut]
## Declaration
There are two syntaxes for creating an empty array:
@ -113,7 +111,7 @@ There's another use case for arrays -- the data structure named [stack](https://
It supports two operations:
- `push` adds an element to the end.
- `pop` takes an element to the end.
- `pop` takes an element from the end.
So new elements are added or taken always from the "end".
@ -297,7 +295,7 @@ But for arrays there is another form of loop, `for..of`:
let fruits = ["Apple", "Orange", "Plum"];
// iterates over array elements
for(let fruit of fruits) {
for (let fruit of fruits) {
alert( fruit );
}
```
@ -356,7 +354,7 @@ arr.length = 5; // return length back
alert( arr[3] ); // undefined: the values do not return
```
So, the simplest way to clear the array is: `arr.length=0`.
So, the simplest way to clear the array is: `arr.length = 0;`.
## new Array() [#new-array]
@ -385,9 +383,9 @@ In the code above, `new Array(number)` has all elements `undefined`.
To evade such surprises, we usually use square brackets, unless we really know what we're doing.
## Multidimentional arrays
## Multidimensional arrays
Arrays can have items that are also arrays. We can use it for multidimentional arrays, to store matrices:
Arrays can have items that are also arrays. We can use it for multidimensional arrays, to store matrices:
```js run
let matrix = [
@ -458,9 +456,9 @@ We can use an array as a deque with the following operations:
- `unshift(...items)` adds items to the beginning.
To loop over the elements of the array:
- `for(let i=0; i<arr.length; i++)` -- works fastest, old-browser-compatible.
- `for(let item of arr)` -- the modern syntax for items only,
- `for(let i in arr)` -- never use.
- `for (let i=0; i<arr.length; i++)` -- works fastest, old-browser-compatible.
- `for (let item of arr)` -- the modern syntax for items only,
- `for (let i in arr)` -- never use.
We will return to arrays and study more methods to add, remove, extract elements and sort arrays in the chapter <info:array-methods>.

View file

@ -3,9 +3,9 @@ function getAverageAge(users) {
return users.reduce((prev, user) => prev + user.age, 0) / users.length;
}
let john = { name: "John", age: 25 }
let pete = { name: "Pete", age: 30 }
let mary = { name: "Mary", age: 29 }
let john = { name: "John", age: 25 };
let pete = { name: "Pete", age: 30 };
let mary = { name: "Mary", age: 29 };
let arr = [ john, pete, mary ];

View file

@ -11,12 +11,12 @@ The formula for the average is `(age1 + age2 + ... + ageN) / N`.
For instance:
```js no-beautify
let john = { name: "John", age: 25 }
let pete = { name: "Pete", age: 30 }
let mary = { name: "Mary", age: 29 }
let john = { name: "John", age: 25 };
let pete = { name: "Pete", age: 30 };
let mary = { name: "Mary", age: 29 };
let arr = [ john, pete, mary ];
alert( getAverageAge(arr) ); // (25+30+29)/3 = 28
alert( getAverageAge(arr) ); // (25 + 30 + 29) / 3 = 28
```

View file

@ -7,7 +7,7 @@ function unique(arr) {
let result = [];
for (let str of arr) {
if (!result.includes(str) {
if (!result.includes(str)) {
result.push(str);
}
}

View file

@ -2,7 +2,7 @@ importance: 4
---
# Filter "in place"
# Filter range
Write a function `filterRange(arr, a, b)` that gets an array `arr`, looks for elements between `a` and `b` in it and returns an array of them.

View file

@ -4,7 +4,7 @@ importance: 4
# Filter range "in place"
Write a function `filterRangeInPlace(arr, a, b)` that gets an array `arr` and removes from it all values except those that are between `a` and `b. The test is: `a ≤ arr[i] ≤ b`.
Write a function `filterRangeInPlace(arr, a, b)` that gets an array `arr` and removes from it all values except those that are between `a` and `b`. The test is: `a ≤ arr[i] ≤ b`.
The function should only modify the array. It should not return anything.

View file

@ -3,7 +3,7 @@
```js run
let arr = [5, 2, 1, -10, 8];
arr.sort((a,b) => b - a);
arr.sort((a, b) => b - a);
alert( arr );
```

View file

@ -21,8 +21,8 @@ usersMapped = [
]
*/
alert( usersMapped[0].id ) // 1
alert( usersMapped[0].fullName ) // John Smith
alert( usersMapped[0].id ); // 1
alert( usersMapped[0].fullName ); // John Smith
```
Please note that in for the arrow functions we need to use additional brackets.
@ -43,7 +43,7 @@ Here JavaScript would treat `{` as the start of function body, not the start of
let usersMapped = users.map(user => *!*({*/!*
fullName: `${user.name} ${user.surname}`,
id: user.id
});
}));
```
Now fine.

View file

@ -12,6 +12,6 @@ let arr = [ john, pete, mary ];
sortByName(arr);
// now sorted is: [john, mary, pete]
alert(arr[1].name) // Mary
alert(arr[1].name); // Mary
```

View file

@ -9,15 +9,15 @@ Write the function `sortByName(users)` that gets an array of objects with proper
For instance:
```js no-beautify
let john = { name: "John", age: 25 }
let pete = { name: "Pete", age: 30 }
let mary = { name: "Mary", age: 28 }
let john = { name: "John", age: 25 };
let pete = { name: "Pete", age: 30 };
let mary = { name: "Mary", age: 28 };
let arr = [ john, pete, mary ];
lsortByName(arr);
sortByName(arr);
// now: [john, mary, pete]
alert(arr[1].name) // Mary
alert(arr[1].name); // Mary
```

View file

@ -12,7 +12,7 @@ shuffle(arr);
alert(arr);
```
That somewhat works, because `Math.random()-0.5` is a random number that may be positive or negative, so the sorting function reorders elements randomly.
That somewhat works, because `Math.random() - 0.5` is a random number that may be positive or negative, so the sorting function reorders elements randomly.
But because the sorting function is not meant to be used this way, not all permutations have the same probability.
@ -33,14 +33,14 @@ let count = {
'312': 0
};
for(let i = 0; i < 1000000; i++) {
for (let i = 0; i < 1000000; i++) {
let array = [1, 2, 3];
shuffle(array);
count[array.join('')]++;
}
// show counts of all possible permutations
for(let key in count) {
for (let key in count) {
alert(`${key}: ${count[key]}`);
}
```
@ -66,8 +66,8 @@ There are other good ways to do the task. For instance, there's a great algorith
```js
function shuffle(array) {
for(let i = array.length - 1; i > 0; i--) {
let j = Math.floor(Math.random() * (i+1)); // random index from 0 to i
for (let i = array.length - 1; i > 0; i--) {
let j = Math.floor(Math.random() * (i + 1)); // random index from 0 to i
[array[i], array[j]] = [array[j], array[i]]; // swap elements
}
}
@ -77,8 +77,8 @@ Let's test it the same way:
```js run
function shuffle(array) {
for(let i = array.length - 1; i > 0; i--) {
let j = Math.floor(Math.random() * (i+1));
for (let i = array.length - 1; i > 0; i--) {
let j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
}
@ -93,14 +93,14 @@ let count = {
'312': 0
};
for(let i = 0; i < 1000000; i++) {
for (let i = 0; i < 1000000; i++) {
let array = [1, 2, 3];
shuffle(array);
count[array.join('')]++;
}
// show counts of all possible permutations
for(let key in count) {
for (let key in count) {
alert(`${key}: ${count[key]}`);
}
```

View file

@ -2,8 +2,6 @@
Arrays provide a lot of methods. To make things easier, in this chapter they are split into groups.
[cut]
## Add/remove items
We already know methods that add and remove items from the beginning or the end:
@ -64,13 +62,13 @@ alert( arr ); // ["I", "JavaScript"]
Easy, right? Starting from the index `1` it removed `1` element.
In the next example we remove 3 elements and replace them by the other two:
In the next example we remove 3 elements and replace them with the other two:
```js run
let arr = [*!*"I", "study", "JavaScript",*/!* "right", "now"];
// remove 3 first elements and replace them by another
arr.splice(0, 3, "Let's", "dance")
// remove 3 first elements and replace them with another
arr.splice(0, 3, "Let's", "dance");
alert( arr ) // now [*!*"Let's", "dance"*/!*, "right", "now"]
```
@ -103,7 +101,7 @@ alert( arr ); // "I", "study", "complex", "language", "JavaScript"
Here and in other array methods, negative indexes are allowed. They specify the position from the end of the array, like here:
```js run
let arr = [1, 2, 5]
let arr = [1, 2, 5];
// from index -1 (one step from the end)
// delete 0 elements,
@ -155,7 +153,7 @@ It accepts any number of arguments -- either arrays or values.
The result is a new array containing items from `arr`, then `arg1`, `arg2` etc.
If an argument is an array or has `Symbol.isConcatSpreadable` property, then all its elements are copied. Otherwise the argument itself is copied.
If an argument is an array or has `Symbol.isConcatSpreadable` property, then all its elements are copied. Otherwise, the argument itself is copied.
For instance:
@ -172,7 +170,7 @@ alert( arr.concat([3, 4], [5, 6])); // 1,2,3,4,5,6
alert( arr.concat([3, 4], 5, 6)); // 1,2,3,4,5,6
```
Normally, it only copies elements from arrays ("spreads" them), other objects even if they look like arrays and added as a whole:
Normally, it only copies elements from arrays ("spreads" them). Other objects, even if they look like arrays, added as a whole:
```js run
let arr = [1, 2];
@ -211,7 +209,7 @@ These are methods to search for something in an array.
The methods [arr.indexOf](mdn:js/Array/indexOf), [arr.lastIndexOf](mdn:js/Array/lastIndexOf) and [arr.includes](mdn:js/Array/includes) have the same syntax and do essentially the same as their string counterparts, but operate on items instead of characters:
- `arr.indexOf(item, from)` looks for `item` starting from index `from`, and returns the index where it was found, otheriwse `-1`.
- `arr.indexOf(item, from)` looks for `item` starting from index `from`, and returns the index where it was found, otherwise `-1`.
- `arr.lastIndexOf(item, from)` -- same, but looks from right to left.
- `arr.includes(item, from)` -- looks for `item` starting from index `from`, returns `true` if found.
@ -231,6 +229,13 @@ Note that the methods use `===` comparison. So, if we look for `false`, it finds
If we want to check for inclusion, and don't want to know the exact index, then `arr.includes` is preferred.
Also, a very minor difference of `includes` is that it correctly handles `NaN`, unlike `indexOf/lastIndexOf`:
```js run
const arr = [NaN];
alert( arr.indexOf(NaN) ); // -1 (should be 0, but === equality doesn't work for NaN)
alert( arr.includes(NaN) );// true (correct)
```
### find and findIndex
@ -316,7 +321,7 @@ The syntax is:
```js
let result = arr.map(function(item, index, array) {
// returns the new value instead of item
}
})
```
It calls the function for each element of the array and returns the array of results.
@ -472,7 +477,7 @@ alert( str.split('') ); // t,e,s,t
```
````
The call [arr.join(str)](mdn:js/Array/join) does the reverse to `split`. It creates a string of `arr` items glued by `str` beween them.
The call [arr.join(str)](mdn:js/Array/join) does the reverse to `split`. It creates a string of `arr` items glued by `str` between them.
For instance:
@ -515,7 +520,7 @@ The easiest way to grasp that is by example.
Here we get a sum of array in one line:
```js run
let arr = [1, 2, 3, 4, 5]
let arr = [1, 2, 3, 4, 5];
let result = arr.reduce((sum, current) => sum + current, 0);
@ -528,7 +533,7 @@ Let's see the details of what's going on.
1. On the first run, `sum` is the initial value (the last argument of `reduce`), equals `0`, and `current` is the first array element, equals `1`. So the result is `1`.
2. On the second run, `sum = 1`, we add the second array element (`2`) to it and return.
3. On the 3rd run, `sum = 3` and we add one more element ot it, and so on...
3. On the 3rd run, `sum = 3` and we add one more element to it, and so on...
The calculation flow:
@ -687,7 +692,7 @@ A cheatsheet of array methods:
- To search among elements:
- `indexOf/lastIndexOf(item, pos)` -- look for `item` starting from position `pos`, return the index or `-1` if not found.
- `includes(value)` -- returns `true` if the array has `value`, otherwise `false`.
- `find/filter(func)` -- filter elements of through the function, return first/all values that make it return `true`.
- `find/filter(func)` -- filter elements through the function, return first/all values that make it return `true`.
- `findIndex` is like `find`, but returns the index instead of a value.
- To transform the array:
@ -703,7 +708,7 @@ A cheatsheet of array methods:
- Additionally:
- `Array.isArray(arr)` checks `arr` for being an array.
Of all these methods only `sort`, `reverse` and `splice` modify the array itself, the other ones only return a value.
Please note that methods `sort`, `reverse` and `splice` modify the array itself.
These methods are the most used ones, they cover 99% of use cases. But there are few others:

View file

@ -7,8 +7,6 @@ Arrays by themselves are iterable. But not only arrays. Strings are iterable too
Iterables are widely used by the core JavaScript. As we'll see many built-in operators and methods rely on them.
[cut]
## Symbol.iterator
We can easily grasp the concept of iterables by making one of our own.
@ -121,7 +119,7 @@ Arrays and strings are most widely used built-in iterables.
For a string, `for..of` loops over its characters:
```js run
for(let char of "test") {
for (let char of "test") {
alert( char ); // t, then e, then s, then t
}
```
@ -130,8 +128,8 @@ And it works right with surrogate pairs!
```js run
let str = '𝒳😂';
for(let char of str) {
alert(char); // 𝒳, and then 😂
for (let char of str) {
alert( char ); // 𝒳, and then 😂
}
```
@ -139,7 +137,7 @@ for(let char of str) {
Normally, internals of iterables are hidden from the external code. There's a `for..of` loop, that works, that's all it needs to know.
But to understand things a little bit more deeper let's see how to create an iterator explicitly.
But to understand things a little bit deeper let's see how to create an iterator explicitly.
We'll iterate over a string the same way as `for..of`, but with direct calls. This code gets a string iterator and calls it "manually":
@ -151,7 +149,7 @@ let str = "Hello";
let iterator = str[Symbol.iterator]();
while(true) {
while (true) {
let result = iterator.next();
if (result.done) break;
alert(result.value); // outputs characters one by one
@ -184,11 +182,11 @@ let arrayLike = { // has indexes and length => array-like
*!*
// Error (no Symbol.iterator)
for(let item of arrayLike) {}
for (let item of arrayLike) {}
*/!*
```
What they share in common -- both iterables and array-likes are usually *not arrays*, they don't have `push`, `pop` etc. That's rather inconvenient if we have such an object and want to work with it as with an array.
What do they have in common? Both iterables and array-likes are usually *not arrays*, they don't have `push`, `pop` etc. That's rather inconvenient if we have such an object and want to work with it as with an array.
## Array.from
@ -258,7 +256,7 @@ Technically here it does the same as:
let str = '𝒳😂';
let chars = []; // Array.from internally does the same loop
for(let char of str) {
for (let char of str) {
chars.push(char);
}
@ -295,8 +293,8 @@ Objects that can be used in `for..of` are called *iterable*.
- String iterator knows about surrogate pairs.
Objects that have indexed properties and `length` are called *array-like*. Such objects may also have other properties and methods, but lack built-in methods of arrays.
Objects that have indexed properties and `length` are called *array-like*. Such objects may also have other properties and methods, but lack the built-in methods of arrays.
If we look inside the specification -- we'll see that most built-in methods assume that they work with iterables or array-likes instead of "real" arrays, because that's more abstract.
`Array.from(obj[, mapFn, thisArg])` makes a real `Array` of an iterable or array-like `obj`, and then we can use array methods on it. The optional arguments `mapFn` and `thisArg` allow to apply a function to each item.
`Array.from(obj[, mapFn, thisArg])` makes a real `Array` of an iterable or array-like `obj`, and we can then use array methods on it. The optional arguments `mapFn` and `thisArg` allow us to apply a function to each item.

View file

@ -15,7 +15,7 @@ We'll use the letter-sorted variants as map keys to store only one value per eac
function aclean(arr) {
let map = new Map();
for(let word of arr) {
for (let word of arr) {
// split the word by letters, sort them and join back
*!*
let sorted = word.toLowerCase().split('').sort().join(''); // (*)

View file

@ -1,26 +1,26 @@
# Map, Set, WeakMap and WeakSet
Now we know the following complex data structures:
Now we've learned about the following complex data structures:
- Objects for storing keyed collections.
- Arrays for storing ordered collections.
But that's not enough for real life. That's why there also exist `Map` and `Set`.
But that's not enough for real life. That's why `Map` and `Set` also exist.
## Map
[Map](mdn:js/Map) is a collection of keyed data items. Just like an `Object`. But the main difference is that `Map` allows keys of any type.
[Map](mdn:js/Map) is a collection of keyed data items, just like an `Object`. But the main difference is that `Map` allows keys of any type.
The main methods are:
- `new Map()` -- creates the map.
- `map.set(key, value)` -- stores the value by the key.
- `map.get(key)` -- returns the value by the key.
- `map.get(key)` -- returns the value by the key, `undefined` if `key` doesn't exist in map.
- `map.has(key)` -- returns `true` if the `key` exists, `false` otherwise.
- `map.delete(key)` -- removes the value by the key.
- `map.clear()` -- clears the map
- `map.size` -- is the current elements count.
- `map.size` -- returns the current element count.
For instance:
@ -76,7 +76,7 @@ alert( visitsCounts[john.id] ); // 123
```smart header="How `Map` compares keys"
To test values for equivalence, `Map` uses the algorithm [SameValueZero](https://tc39.github.io/ecma262/#sec-samevaluezero). It is roughly the same as the strict equality `===`, but the difference is that `NaN` is considered equal to `NaN`. So `NaN` can be used as the key as well.
To test values for equivalence, `Map` uses the algorithm [SameValueZero](https://tc39.github.io/ecma262/#sec-samevaluezero). It is roughly the same as strict equality `===`, but the difference is that `NaN` is considered equal to `NaN`. So `NaN` can be used as the key as well.
This algorithm can't be changed or customized.
```
@ -106,7 +106,7 @@ let map = new Map([
]);
```
There is a built-in method [Object.entries(obj)](mdn:js/Object/entries) that returns the array of key/value pairs for an object exactly in that format.
There is a built-in method [Object.entries(obj)](mdn:js/Object/entries) that returns an array of key/value pairs for an object exactly in that format.
So we can initialize a map from an object like this:
@ -137,18 +137,18 @@ let recipeMap = new Map([
]);
// iterate over keys (vegetables)
for(let vegetable of recipeMap.keys()) {
alert(vegetable); // cucumber, tomateos, onion
for (let vegetable of recipeMap.keys()) {
alert(vegetable); // cucumber, tomatoes, onion
}
// iterate over values (amounts)
for(let amount of recipeMap.values()) {
for (let amount of recipeMap.values()) {
alert(amount); // 500, 350, 50
}
// iterate over [key, value] entries
for(let entry of recipeMap) { // the same as of recipeMap.entries()
alert(entry); // cucumber,50 (and so on)
for (let entry of recipeMap) { // the same as of recipeMap.entries()
alert(entry); // cucumber,500 (and so on)
}
```
@ -160,16 +160,16 @@ Besides that, `Map` has a built-in `forEach` method, similar to `Array`:
```js
recipeMap.forEach( (value, key, map) => {
alert(`${key}: ${value}`); // cucumber: 50 etc
alert(`${key}: ${value}`); // cucumber: 500 etc
});
```
## Set
`Set` -- is a collection of values, where each value may occur only once.
A `Set` is a collection of values, where each value may occur only once.
The main methods are:
Its main methods are:
- `new Set(iterable)` -- creates the set, optionally from an array of values (any iterable will do).
- `set.add(value)` -- adds a value, returns the set itself.
@ -199,7 +199,7 @@ set.add(mary);
// set keeps only unique values
alert( set.size ); // 3
for(let user of set) {
for (let user of set) {
alert(user.name); // John (then Pete and Mary)
}
```
@ -213,7 +213,7 @@ We can loop over a set either with `for..of` or using `forEach`:
```js run
let set = new Set(["oranges", "apples", "bananas"]);
for(let value of set) alert(value);
for (let value of set) alert(value);
// the same with forEach:
set.forEach((value, valueAgain, set) => {
@ -223,9 +223,9 @@ set.forEach((value, valueAgain, set) => {
Note the funny thing. The `forEach` function in the `Set` has 3 arguments: a value, then *again a value*, and then the target object. Indeed, the same value appears in the arguments twice.
That's made for compatibility with `Map` where `forEach` has three arguments.
That's for compatibility with `Map` where `forEach` has three arguments.
The same methods as `Map` has for iterators are also supported:
The same methods `Map` has for iterators are also supported:
- `set.keys()` -- returns an iterable object for values,
- `set.values()` -- same as `set.keys`, for compatibility with `Map`,
@ -330,9 +330,9 @@ weakMap.put(john, "secret documents");
That's useful 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.
Let's look at 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.
For instance, we have 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 anymore.
One way would be to keep track of leaving users and clean up the storage manually:
@ -345,7 +345,7 @@ let visitsCountMap = new Map();
// john is the key for the map
visitsCountMap.set(john, 123);
// now john leaves us, we don't need him any more
// now john leaves us, we don't need him anymore
john = null;
*!*
@ -364,7 +364,7 @@ let visitsCountMap = new WeakMap();
visitsCountMap.set(john, 123);
// now john leaves us, we don't need him any more
// now john leaves us, we don't need him anymore
john = null;
// there are no references except WeakMap,
@ -408,7 +408,7 @@ The most notable limitation of `WeakMap` and `WeakSet` is the absence of iterati
## Summary
- `Map` -- is a a collection of keyed values.
- `Map` -- is a collection of keyed values.
The differences from a regular `Object`:
@ -421,12 +421,12 @@ The most notable limitation of `WeakMap` and `WeakSet` is the absence of iterati
- 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.
- `WeakMap` -- a variant of `Map` that allows only objects as keys and removes them once they become inaccessible by other means.
- It does not support operations on the structure as a whole: no `size`, no `clear()`, no iterations.
- `WeakSet` -- is a variant of `Set` that only stores objects and removes them once they become unaccessible by other means.
- `WeakSet` -- is a variant of `Set` that only stores objects and removes them once they become inaccessible by other means.
- 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.
`WeakMap` and `WeakSet` are used as "secondary" data structures in addition to the "main" object storage. Once the object is removed from the main storage, so it only stays in `WeakMap/WeakSet`, they clean up automatically.

View file

@ -32,7 +32,7 @@ For plain objects, the following methods are available:
The first difference is that we have to call `Object.keys(obj)`, and not `obj.keys()`.
Why so? There main reason is flexibility. Remember, objects are a base of all complex structures in JavaScript. So we may have an object of our own like `order` that implements its own `order.values()` method. And we still can call `Object.values(order)` on it.
Why so? The main reason is flexibility. Remember, objects are a base of all complex structures in JavaScript. So we may have an object of our own like `order` that implements its own `order.values()` method. And we still can call `Object.values(order)` on it.
The second difference is that `Object.*` methods return "real" array objects, not just an iterable. That's mainly for historical reasons.
@ -58,7 +58,7 @@ let user = {
};
// loop over values
for(let value of Object.values(user)) {
for (let value of Object.values(user)) {
alert(value); // John, then 30
}
```

View file

@ -2,11 +2,9 @@
The two most used data structures in JavaScript are `Object` and `Array`.
Objects allow to pack many pieces of information into a single entity and arrays allow to store ordered collections. So we can make an object or an array and handle it as a single thing, maybe pass to a function call.
Objects allow us to pack many pieces of information into a single entity and arrays allow us to store ordered collections. So we can make an object or an array and handle it as a single entity, or maybe pass it to a function call.
*Destructuring assignment* is a special syntax that allows to "unpack" arrays or objects into a bunch of variables, as sometimes they are more convenient. Destructuring also works great with complex functions that have a lot of parameters, default values, and soon we'll see how these are handled too.
[cut]
*Destructuring assignment* is a special syntax that allows us to "unpack" arrays or objects into a bunch of variables, as sometimes they are more convenient. Destructuring also works great with complex functions that have a lot of parameters, default values, and soon we'll see how these are handled too.
## Array destructuring
@ -34,9 +32,9 @@ let [firstName, surname] = "Ilya Kantor".split(' ');
```
````smart header="\"Destructuring\" does not mean \"destructive\""
That's called "destructuring assignment", because it "destructurizes" by copying items into variables. But the array itself is not modified.
It's called "destructuring assignment", because it "destructurizes" by copying items into variables. But the array itself is not modified.
That's just a shorter way to write:
It's just a shorter way to write:
```js
// let [firstName, surname] = arr;
let firstName = arr[0];
@ -56,12 +54,12 @@ let [, , title] = ["Julius", "Caesar", "Consul", "of the Roman Republic"];
alert( title ); // Consul
```
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.
In the code above, although the first and second elements of the array are skipped, the third one is assigned to `title`, and the rest are also skipped.
````
````smart header="Works with any iterable on the right-side"
...Actually, we can use it with any iterable, not only an array:
...Actually, we can use it with any iterable, not only arrays:
```js
let [a, b, c] = "abc"; // ["a", "b", "c"]
@ -87,7 +85,7 @@ alert(user.name); // Ilya
````smart header="Looping with .entries()"
In the previous chapter we saw [Object.entries(obj)](mdn:js/Object/entries) method.
In the previous chapter we saw the [Object.entries(obj)](mdn:js/Object/entries) method.
We can use it with destructuring to loop over keys-and-values of an object:
@ -99,7 +97,7 @@ let user = {
// loop over keys-and-values
*!*
for(let [key, value] of Object.entries(user)) {
for (let [key, value] of Object.entries(user)) {
*/!*
alert(`${key}:${value}`); // name:John, then age:30
}
@ -113,7 +111,7 @@ user.set("name", "John");
user.set("age", "30");
*!*
for(let [key, value] of user.entries()) {
for (let [key, value] of user.entries()) {
*/!*
alert(`${key}:${value}`); // name:John, then age:30
}
@ -121,7 +119,7 @@ for(let [key, value] of user.entries()) {
````
### 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 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 three dots `"..."`:
```js run
let [name1, name2, *!*...rest*/!*] = ["Julius", "Caesar", *!*"Consul", "of the Roman Republic"*/!*];
@ -136,11 +134,11 @@ alert(rest.length); // 2
*/!*
```
The value of `rest` is the array of the remaining array elements. We can use any other variable name in place of `rest`, just make sure it has three dots before it and goes the last.
The value of `rest` is the array of the remaining array elements. We can use any other variable name in place of `rest`, just make sure it has three dots before it and goes last in the destructuring assignment.
### Default values
If there are fewer values in the array than variables in the assignment -- there will be no error, absent values are considered undefined:
If there are fewer values in the array than variables in the assignment, there will be no error. Absent values are considered undefined:
```js run
*!*
@ -155,7 +153,7 @@ If we want a "default" value to replace the missing one, we can provide it using
```js run
*!*
// default values
let [name="Guest", surname="Anonymous"] = ["Julius"];
let [name = "Guest", surname = "Anonymous"] = ["Julius"];
*/!*
alert(name); // Julius (from array)
@ -164,11 +162,11 @@ alert(surname); // Anonymous (default used)
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 two defaults. But it will only run only for the missing one:
For instance, here we use the `prompt` function for two defaults. But it will run only for the missing one:
```js run
// runs only prompt for surname
let [name=prompt('name?'), surname=prompt('surname?')] = ["Julius"];
let [name = prompt('name?'), surname = prompt('surname?')] = ["Julius"];
alert(name); // Julius (from array)
alert(surname); // whatever prompt gets
@ -186,7 +184,7 @@ The basic syntax is:
let {var1, var2} = {var1:…, var2…}
```
We have an existing object at the right side, that we want to split into variables. To the left side contains a "pattern" for corresponding properties. In the simple case that's a list of variable names in `{...}`.
We have an existing object at the right side, that we want to split into variables. The left side contains a "pattern" for corresponding properties. In the simple case, that's a list of variable names in `{...}`.
For instance:
@ -206,7 +204,7 @@ alert(width); // 100
alert(height); // 200
```
Properties `options.title`, `options.width` and `options.height` are assigned to the corresponding variables. The order does not matter, that works too:
Properties `options.title`, `options.width` and `options.height` are assigned to the corresponding variables. The order does not matter. This works too:
```js
// changed the order of properties in let {...}
@ -248,7 +246,7 @@ let options = {
};
*!*
let {width=100, height=200, title} = options;
let {width = 100, height = 200, title} = options;
*/!*
alert(title); // Menu
@ -258,7 +256,7 @@ alert(height); // 200
Just like with arrays or function parameters, default values can be any expressions or even function calls. They will be evaluated if the value is not provided.
The code below asks for width, but not about the title.
The code below asks for width, but not the title.
```js run
let options = {
@ -266,7 +264,7 @@ let options = {
};
*!*
let {width=prompt("width?"), title=prompt("title?")} = options;
let {width = prompt("width?"), title = prompt("title?")} = options;
*/!*
alert(title); // Menu
@ -281,7 +279,7 @@ let options = {
};
*!*
let {width:w=100, height:h=200, title} = options;
let {width: w = 100, height: h = 200, title} = options;
*/!*
alert(title); // Menu
@ -308,7 +306,7 @@ let options = {
let {title, ...rest} = options;
*/!*
// now title="Menu", rest={height: 200, widht: 100}
// now title="Menu", rest={height: 200, width: 100}
alert(rest.height); // 200
alert(rest.width); // 100
```
@ -389,9 +387,9 @@ The whole `options` object except `extra` that was not mentioned, is assigned to
Finally, we have `width`, `height`, `item1`, `item2` and `title` from the default value.
That often happens with destructuring assignments. We have a complex object with may properties and want extract only what we need.
That often happens with destructuring assignments. We have a complex object with many properties and want to extract only what we need.
Even like this:
Even here it happens:
```js
// take size as a whole into a variable, ignore the rest
let { size } = options;
@ -409,7 +407,7 @@ function showMenu(title = "Untitled", width = 200, height = 100, items = []) {
}
```
The real-life problem is how to remember the order of arguments. Usually IDEs try to help us, especially if the code is well-documented, but still... Another problem is how to call a function when most parameters are ok by default.
In real-life the problem is how to remember the order of arguments. Usually IDEs try to help us, especially if the code is well-documented, but still... Another problem is how to call a function when most parameters are ok by default.
Like this?
@ -434,7 +432,7 @@ let options = {
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( `${title} ${width} ${height}` ); // My Menu 200 100
alert( items ); // Item1, Item2
}
@ -452,12 +450,12 @@ let options = {
*!*
function showMenu({
title = "Untitled",
width:w = 100, // width goes to w
height:h = 200, // height goes to h
width: w = 100, // width goes to w
height: h = 200, // height goes to h
items: [item1, item2] // items first element goes to item1, second to item2
}) {
*/!*
alert( title + ' ' + w + ' ' + h ); // My Menu 100 200
alert( `${title} ${w} ${h}` ); // My Menu 100 200
alert( item1 ); // Item1
alert( item2 ); // Item2
}
@ -478,8 +476,8 @@ Please note that such destructuring assumes that `showMenu()` does have an argum
```js
showMenu({});
// that would give an error
showMenu();
showMenu(); // this would give an error
```
We can fix this by making `{}` the default value for the whole destructuring thing:
@ -487,8 +485,8 @@ We can fix this by making `{}` the default value for the whole destructuring thi
```js run
// simplified parameters a bit for clarity
function showMenu(*!*{ title="Menu", width=100, height=200 } = {}*/!*) {
alert( title + ' ' + width + ' ' + height );
function showMenu(*!*{ title = "Menu", width = 100, height = 200 } = {}*/!*) {
alert( `${title} ${width} ${height}` );
}
showMenu(); // Menu 100 200
@ -498,7 +496,7 @@ In the code above, the whole arguments object is `{}` by default, so there's alw
## Summary
- Destructuring assignment allows to instantly map an object or array into many variables.
- Destructuring assignment allows for instantly mapping an object or array onto many variables.
- The object syntax:
```js
let {prop : varName = default, ...} = object

View file

@ -4,7 +4,7 @@ importance: 5
# European weekday
European countries have days of week starting with monday (number 1), then tuesday (number 2) and till sunday (number 7). Write a function `getLocalDay(date)` that returns the "european" day of week for `date`.
European countries have days of week starting with Monday (number 1), then Tuesday (number 2) and till Sunday (number 7). Write a function `getLocalDay(date)` that returns the "European" day of week for `date`.
```js no-beautify
let date = new Date(2012, 0, 3); // 3 Jan 2012

View file

@ -13,3 +13,20 @@ function getSecondsToTomorrow() {
return Math.round(diff / 1000); // convert to seconds
}
```
Alternative solution:
```js run
function getSecondsToTomorrow() {
let now = new Date();
let hour = now.getHours();
let minutes = now.getMinutes();
let seconds = now.getSeconds();
let totalSecondsToday = (hour * 60 + minutes) * 60 + seconds;
let totalSecondsInADay = 86400;
return totalSecondsInADay - totalSecondsToday;
}
```
Please note that many countries have Daylight Savings Time (DST), so there may be days with 23 or 25 hours. We may want to treat such days separately.

View file

@ -43,3 +43,34 @@ alert( formatDate(new Date(new Date - 5 * 60 * 1000)) ); // "5 min. ago"
// yesterday's date like 31.12.2016, 20:00
alert( formatDate(new Date(new Date - 86400 * 1000)) );
```
Alternative solution:
```js run
function formatDate(date) {
let dayOfMonth = date.getDate();
let month = date.getMonth() + 1;
let year = date.getFullYear();
let hour = date.getHours();
let minutes = date.getMinutes();
let diffMs = new Date() - date;
let diffSec = Math.round(diffMs / 1000);
let diffMin = diffSec / 60;
let diffHour = diffMin / 60;
// formatting
year = year.toString().slice(-2);
month = month < 10 ? '0' + month : month;
dayOfMonth = dayOfMonth < 10 ? '0' + dayOfMonth : dayOfMonth;
if (diffSec < 1) {
return 'right now';
} else if (diffMin < 1) {
return `${diffSec} sec. ago`
} else if (diffHour < 1) {
return `${diffMin} min. ago`
} else {
return `${dayOfMonth}.${month}.${year} ${hour}:${minutes}`
}
}
```

View file

@ -4,8 +4,6 @@ Let's meet a new built-in object: [Date](mdn:js/Date). It stores the date, time
For instance, we can use it to store creation/modification times, or to measure time, or just to print out the current date.
[cut]
## Creation
To create a new `Date` object call `new Date()` with one of the following arguments:
@ -100,7 +98,7 @@ There are also their UTC-counterparts, that return day, month, year and so on fo
If your local time zone is shifted relative to UTC, then the code below shows different hours:
```js run
// currend date
// current date
let date = new Date();
// the hour in your current time zone
@ -208,7 +206,7 @@ let date = new Date();
alert(+date); // the number of milliseconds, same as date.getTime()
```
The important side effect: dates can be substracted, the result is their difference in ms.
The important side effect: dates can be subtracted, the result is their difference in ms.
That can be used for time measurements:
@ -251,7 +249,7 @@ for (let i = 0; i < 100000; i++) {
let end = Date.now(); // done
*/!*
alert( `The loop took ${end - start} ms` ); // substract numbers, not dates
alert( `The loop took ${end - start} ms` ); // subtract numbers, not dates
```
## Benchmarking
@ -262,7 +260,7 @@ For instance, let's measure two functions that calculate the difference between
```js
// we have date1 and date2, which function faster returns their difference in ms?
function diffSubstract(date1, date2) {
function diffSubtract(date1, date2) {
return date2 - date1;
}
@ -281,7 +279,7 @@ The first idea may be to run them many times in a row and measure the time diffe
Let's measure:
```js run
function diffSubstract(date1, date2) {
function diffSubtract(date1, date2) {
return date2 - date1;
}
@ -298,7 +296,7 @@ function bench(f) {
return Date.now() - start;
}
alert( 'Time of diffSubstract: ' + bench(diffSubstract) + 'ms' );
alert( 'Time of diffSubtract: ' + bench(diffSubtract) + 'ms' );
alert( 'Time of diffGetTime: ' + bench(diffGetTime) + 'ms' );
```
@ -306,7 +304,7 @@ Wow! Using `getTime()` is so much faster! That's because there's no type convers
Okay, we have something. But that's not a good benchmark yet.
Imagine that at the time of running `bench(diffSubstract)` CPU was doing something in parallel, and it was taking resources. And by the time of running `bench(diffGetTime)` the work has finished.
Imagine that at the time of running `bench(diffSubtract)` CPU was doing something in parallel, and it was taking resources. And by the time of running `bench(diffGetTime)` the work has finished.
A pretty real scenario for a modern multi-process OS.
@ -317,7 +315,7 @@ As a result, the first benchmark will have less CPU resources than the second. T
Here's the code example:
```js run
function diffSubstract(date1, date2) {
function diffSubtract(date1, date2) {
return date2 - date1;
}
@ -340,12 +338,12 @@ let time2 = 0;
*!*
// run bench(upperSlice) and bench(upperLoop) each 10 times alternating
for (let i = 0; i < 10; i++) {
time1 += bench(diffSubstract);
time1 += bench(diffSubtract);
time2 += bench(diffGetTime);
}
*/!*
alert( 'Total time for diffSubstract: ' + time1 );
alert( 'Total time for diffSubtract: ' + time1 );
alert( 'Total time for diffGetTime: ' + time2 );
```
@ -353,12 +351,12 @@ Modern JavaScript engines start applying advanced optimizations only to "hot cod
```js
// added for "heating up" prior to the main loop
bench(diffSubstract);
bench(diffSubtract);
bench(diffGetTime);
// now benchmark
for (let i = 0; i < 10; i++) {
time1 += bench(diffSubstract);
time1 += bench(diffSubtract);
time2 += bench(diffGetTime);
}
```
@ -405,8 +403,8 @@ alert(date);
- Date and time in JavaScript are represented with the [Date](mdn:js/Date) object. We can't create "only date" or "only time": `Date` objects always carry both.
- Months are counted from zero (yes, January is a zero month).
- Days of week in `getDay()` are also counted from zero (that's Sunday).
- `Date` auto-corrects itself when out-of-range components are set. Good for adding/substracting days/months/hours.
- Dates can be substracted, giving their difference in milliseconds. That's because a `Date` becomes the timestamp if converted to a number.
- `Date` auto-corrects itself when out-of-range components are set. Good for adding/subtracting days/months/hours.
- Dates can be subtracted, giving their difference in milliseconds. That's because a `Date` becomes the timestamp when converted to a number.
- Use `Date.now()` to get the current timestamp fast.
Note that unlike many other systems, timestamps in JavaScript are in milliseconds, not in seconds.
@ -417,7 +415,7 @@ Also, sometimes we need more precise time measurements. JavaScript itself does n
alert(`Loading started ${performance.now()}ms ago`);
// Something like: "Loading started 34731.26000000001ms ago"
// .26 is microseconds (260 microseconds)
// more than 3 digits after the decimal point are precision errors, but only 3 first are correct
// more than 3 digits after the decimal point are precision errors, but only the first 3 are correct
```
Node.JS has `microtime` module and other ways. Technically, any device and environment allows to get more precision, it's just not in `Date`.

View file

@ -4,7 +4,7 @@ importance: 5
# Turn the object into JSON and back
Turn the `leader` into JSON and then read it back into another variable.
Turn the `user` into JSON and then read it back into another variable.
```js
let user = {

View file

@ -25,8 +25,6 @@ alert(user); // {name: "John", age: 30}
Luckily, there's no need to write the code to handle all this. The task has been solved already.
[cut]
## JSON.stringify
The [JSON](http://en.wikipedia.org/wiki/JSON) (JavaScript Object Notation) is a general format to represent values and objects. It is described as in [RFC 4627](http://tools.ietf.org/html/rfc4627) standard. Initially it was made for JavaScript, but many other languages have libraries to handle it as well. So it's easy to use JSON for data exchange when the client uses JavaScript and the server is written on Ruby/PHP/Java/Whatever.
@ -36,7 +34,7 @@ JavaScript provides methods:
- `JSON.stringify` to convert objects into JSON.
- `JSON.parse` to convert JSON back into an object.
For instance, here's we `JSON.stringify` a student:
For instance, here we `JSON.stringify` a student:
```js run
let student = {
name: 'John',
@ -133,7 +131,7 @@ let meetup = {
title: "Conference",
*!*
room: {
number: 123,
number: 23,
participants: ["john", "ann"]
}
*/!*

View file

@ -10,8 +10,6 @@ Recursion is a programming pattern that is useful in situations when a task can
When 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 *recursion*.
[cut]
## Two ways of thinking
For something simple to start with -- let's write a function `pow(x, n)` that raises `x` to a natural power of `n`. In other words, multiplies `x` by itself `n` times.
@ -31,7 +29,7 @@ There are two ways to implement it.
let result = 1;
// multiply result by x n times in the loop
for(let i = 0; i < n; i++) {
for (let i = 0; i < n; i++) {
result *= x;
}
@ -67,8 +65,8 @@ pow(x, n) =
else = x * pow(x, n - 1)
```
1. If `n==1`, then everything is trivial. 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*: 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`.
1. If `n == 1`, then everything is trivial. 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*: 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 until `n` reaches `1`.
We can also say that `pow` *recursively calls itself* till `n == 1`.
@ -91,7 +89,7 @@ Here we can rewrite the same using the ternary `?` operator instead of `if` to m
```js run
function pow(x, n) {
return (n == 1) ? x : (x * pow(x, n-1));
return (n == 1) ? x : (x * pow(x, n - 1));
}
```
````
@ -134,7 +132,7 @@ We can sketch it as:
</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`:
That's when the function starts to execute. The condition `n == 1` is false, so the flow continues into the second branch of `if`:
```js run
function pow(x, n) {
@ -160,7 +158,7 @@ The variables are same, but the line changes, so the context is now:
</li>
</ul>
To calculate `x*pow(x, n-1)`, we need to make a subcall of `pow` with new arguments `pow(2, 2)`.
To calculate `x * pow(x, n - 1)`, we need to make a subcall of `pow` with new arguments `pow(2, 2)`.
### pow(2, 2)
@ -230,7 +228,7 @@ function pow(x, n) {
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 the top of the stack:
As the function finishes, its execution context is not needed anymore, so it's removed from the memory. The previous one is restored off the top of the stack:
<ul class="function-execution-context-list">
@ -244,7 +242,7 @@ As the function finishes, its execution context is not needed any more, so it's
</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`.
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:
@ -269,7 +267,7 @@ A loop-based algorithm is more memory-saving:
function pow(x, n) {
let result = 1;
for(let i = 0; i < n; i++) {
for (let i = 0; i < n; i++) {
result *= x;
}
@ -289,7 +287,7 @@ Recursion can give a shorter code, easier to understand and support. Optimizatio
Another great application of the recursion is a recursive traversal.
Imagine, we have an company. The staff structure can be presented as an object:
Imagine, we have a company. The staff structure can be presented as an object:
```js
let company = {
@ -360,7 +358,7 @@ function sumSalaries(department) {
return department.reduce((prev, current) => prev + current.salary, 0); // sum the array
} else { // case (2)
let sum = 0;
for(let subdep of Object.values(department)) {
for (let subdep of Object.values(department)) {
sum += sumSalaries(subdep); // recursively call for subdepartments, sum the results
}
return sum;
@ -395,7 +393,7 @@ A company *department* is:
- Either an array of people.
- Or an object with *departments*.
For web-developers there are much better known examples: HTML and XML documents.
For web-developers there are much better-known examples: HTML and XML documents.
In the HTML document, an *HTML-tag* may contain a list of:
- Text pieces.

View file

@ -1,6 +1,6 @@
# Rest parameters and spread operator
Many JavaScript built-in functions support on arbitrary number of arguments.
Many JavaScript built-in functions support an arbitrary number of arguments.
For instance:
@ -8,9 +8,7 @@ For instance:
- `Object.assign(dest, src1, ..., srcN)` -- copies properties from `src1..N` into `dest`.
- ...and so on.
In this chapter we'll see how to do the same. And, more important, how to feel comfortable working with such functions and arrays.
[cut]
In this chapter we'll learn how to do the same. And, more importantly, how to feel comfortable working with such functions and arrays.
## Rest parameters `...`
@ -27,7 +25,7 @@ alert( sum(1, 2, 3, 4, 5) );
There will be no error because of "excessive" arguments. But of course in the result only the first two will be counted.
The rest parameters can be mentioned in a function definition with three dots `...`. They literally mean: "gather the remaining parameters into an array".
The rest parameters can be mentioned in a function definition with three dots `...`. They literally mean "gather the remaining parameters into an array".
For instance, to gather all arguments into array `args`:
@ -35,7 +33,7 @@ For instance, to gather all arguments into array `args`:
function sumAll(...args) { // args is the name for the array
let sum = 0;
for(let arg of args) sum += arg;
for (let arg of args) sum += arg;
return sum;
}
@ -45,9 +43,9 @@ alert( sumAll(1, 2) ); // 3
alert( sumAll(1, 2, 3) ); // 6
```
We can choose to get first parameters as variables, and gather only the rest.
We can choose to get the first parameters as variables, and gather only the rest.
Here the first two arguments go into variables and the rest goes to `titles` array:
Here the first two arguments go into variables and the rest go into `titles` array:
```js run
function showName(firstName, lastName, ...titles) {
@ -72,7 +70,7 @@ function f(arg1, ...rest, arg2) { // arg2 after ...rest ?!
}
```
The `...rest` must always be the last.
The `...rest` must always be last.
````
## The "arguments" variable
@ -98,13 +96,13 @@ showName("Julius", "Caesar");
showName("Ilya");
```
In old times, rest parameters did not exist in the language, and `arguments` was the only way to get all arguments of the function no matter of their total number.
In old times, rest parameters did not exist in the language, and using `arguments` was the only way to get all arguments of the function, no matter their total number.
And it still works, we can use it.
And it still works, we can use it today.
But the downside is that although `arguments` is both array-like and iterable, it's not an array. It does not support array methods, so we can't say call `arguments.map(...)`.
But the downside is that although `arguments` is both array-like and iterable, it's not an array. It does not support array methods, so we can't call `arguments.map(...)` for example.
Also, it always has all arguments in it, we can't capture them partially, like we did with rest parameters.
Also, it always contains all arguments. We can't capture them partially, like we did with rest parameters.
So when we need these features, then rest parameters are preferred.
@ -123,23 +121,21 @@ f(1); // 1
```
As we remember, arrow functions don't have their own `this`. Now we know they don't have the special `arguments` object either.
````
## Spread operator [#spread-operator]
We've just seen how to get an array from the list of parameters.
But sometimes we need to do exactly the reverse.
For instance, there's a built-in function [Math.max](mdn:js/Math/max) that returns the greatest number from the list:
For instance, there's a built-in function [Math.max](mdn:js/Math/max) that returns the greatest number from a list:
```js run
alert( Math.max(3, 5, 1) ); // 5
```
Now let's say we have an array `[3, 5, 1]`. How to call `Math.max` with it?
Now let's say we have an array `[3, 5, 1]`. How do we call `Math.max` with it?
Passing it "as it" won't work, because `Math.max` expects a list of numeric arguments, not a single array:
Passing it "as is" won't work, because `Math.max` expects a list of numeric arguments, not a single array:
```js run
let arr = [3, 5, 1];
@ -149,9 +145,9 @@ alert( Math.max(arr) ); // NaN
*/!*
```
...And surely we can't manually list items in the code `Math.max(arg[0], arg[1], arg[2])`, because we may be unsure how much are there. As our script executes, there might be many, or there might be none. Also that would be ugly.
And surely we can't manually list items in the code `Math.max(arr[0], arr[1], arr[2])`, because we may be unsure how many there are. As our script executes, there could be a lot, or there could be none. And that would get ugly.
*Spread operator* to the rescue. It looks similar to rest parameters, also using `...`, but does quite the opposite.
*Spread operator* to the rescue! It looks similar to rest parameters, also using `...`, but does quite the opposite.
When `...arr` is used in the function call, it "expands" an iterable object `arr` into the list of arguments.
@ -172,7 +168,7 @@ let arr2 = [8, 3, -8, 1];
alert( Math.max(...arr1, ...arr2) ); // 8
```
...And even combine the spread operator with normal values:
We can even combine the spread operator with normal values:
```js run
@ -182,7 +178,7 @@ let arr2 = [8, 3, -8, 1];
alert( Math.max(1, ...arr1, 2, ...arr2, 25) ); // 25
```
Also spread operator can be used to merge arrays:
Also, the spread operator can be used to merge arrays:
```js run
let arr = [3, 5, 1];
@ -197,7 +193,7 @@ alert(merged); // 0,3,5,1,2,8,9,15 (0, then arr, then 2, then arr2)
In the examples above we used an array to demonstrate the spread operator, but any iterable will do.
For instance, here we use spread operator to turn the string into array of characters:
For instance, here we use the spread operator to turn the string into array of characters:
```js run
let str = "Hello";
@ -207,7 +203,7 @@ alert( [...str] ); // H,e,l,l,o
The spread operator internally uses iterators to gather elements, the same way as `for..of` does.
So, for a string, `for..of` returns characters and `...str` becomes `"h","e","l","l","o"`. The list of characters is passed to array initializer `[...str]`.
So, for a string, `for..of` returns characters and `...str` becomes `"H","e","l","l","o"`. The list of characters is passed to array initializer `[...str]`.
For this particular task we could also use `Array.from`, because it converts an iterable (like a string) into an array:
@ -225,7 +221,7 @@ But there's a subtle difference between `Array.from(obj)` and `[...obj]`:
- `Array.from` operates on both array-likes and iterables.
- The spread operator operates only on iterables.
So, for the task of turning something into an array, `Array.from` appears more universal.
So, for the task of turning something into an array, `Array.from` tends to be more universal.
## Summary
@ -234,8 +230,8 @@ When we see `"..."` in the code, it is either rest parameters or the spread oper
There's an easy way to distinguish between them:
- 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 or alike, 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 of arguments into an array.
- When `...` occurs in a function call or alike, it's called a "spread operator" and expands an array into a list.
Use patterns:

View file

@ -2,4 +2,4 @@ The answer: **0,1.**
Functions `counter` and `counter2` are created by different invocations of `makeCounter`.
So they have independent outer Lexical Environments, each one has it's own `count`.
So they have independent outer Lexical Environments, each one has its own `count`.

View file

@ -1,23 +1,21 @@
# Closure
JavaScript is a very function-oriented language. It gives a lot of freedom. A function can be created at one moment, then copied to another variable or passed as an argument to another function and called from a totally different place later.
JavaScript is a very function-oriented language. It gives us a lot of freedom. A function can be created at one moment, then copied to another variable or passed as an argument to another function and called from a totally different place later.
We know that a function can access variables outside of it. And this feature is used quite often.
We know that a function can access variables outside of it; this feature is used quite often.
But what happens when an outer variables changes? Does a function get a most recent value or the one that existed when the function was created?
But what happens when an outer variable changes? Does a function get the most recent value or the one that existed when the function was created?
Also, what happens when a function travels to another place of the code and is called from there -- does it get access to outer variables in the new place?
Also, what happens when a function travels to another place in the code and is called from there -- does it get access to the outer variables of the new place?
Different languages behave differently here, in this chapter we cover JavaScript.
[cut]
Different languages behave differently here, and in this chapter we cover the behaviour of JavaScript.
## A couple of questions
Let's formulate two questions for the seed, and then study the internal mechanics piece-by-piece, so that you'll be able to answer these questions and more complex ones in the future.
Let's consider two situations to begin with, and then study the internal mechanics piece-by-piece, so that you'll be able to answer the following questions and more complex ones in the future.
1. The function `sayHi` uses an external variable `name`. When the function runs, which value of these two it's going to use?
1. The function `sayHi` uses an external variable `name`. When the function runs, which value is it going to use?
```js
let name = "John";
@ -33,12 +31,12 @@ Let's formulate two questions for the seed, and then study the internal mechanic
*/!*
```
Such situations are common in both browser and server-side development. A function may be scheduled to execute later than it is created, for instance after a user action or a network request.
Such situations are common both in browser and server-side development. A function may be scheduled to execute later than it is created, for instance after a user action or a network request.
So, the question is: does it pick up latest changes?
So, the question is: does it pick up the latest changes?
2. The function `makeWorker` makes another function and returns it. That new function can be called from somewhere else. Will it have access to outer variables from its creation place or the invocation place or maybe both?
2. The function `makeWorker` makes another function and returns it. That new function can be called from somewhere else. Will it have access to the outer variables from its creation place, or the invocation place, or both?
```js
function makeWorker() {
@ -63,16 +61,16 @@ Let's formulate two questions for the seed, and then study the internal mechanic
## Lexical Environment
To understand what's going on, let's first discuss what a "variable" technically is.
To understand what's going on, let's first discuss what a "variable" actually is.
In JavaScript, every running function, code block and the script as a whole have an associated object named *Lexical Environment*.
In JavaScript, every running function, code block, and the script as a whole have an associated object known as the *Lexical Environment*.
The Lexical Environment object consists of two parts:
1. *Environment Record* -- an object that has all local variables as its properties (and some other information like the value of `this`).
2. A reference to the *outer lexical environment*, usually the one associated with the code lexically right outside of it (outside of the current figure brackets).
2. A reference to the *outer lexical environment*, usually the one associated with the code lexically right outside of it (outside of the current curly brackets).
So, a "variable" is just a property of the special internal object, Environment Record. "To get or change a variable" means "to get or change the property of that object".
So, a "variable" is just a property of the special internal object, Environment Record. "To get or change a variable" means "to get or change a property of the Lexical Environment".
For instance, in this simple code, there is only one Lexical Environment:
@ -80,7 +78,7 @@ For instance, in this simple code, there is only one Lexical Environment:
This is a so-called global Lexical Environment, associated with the whole script. For browsers, all `<script>` tags share the same global environment.
On the picture above, the rectangle means Environment Record (variable store) and the arrow means the outer reference. The global Lexical Environment has no outer one, so that's `null`.
On the picture above, the rectangle means Environment Record (variable store) and the arrow means the outer reference. The global Lexical Environment has no outer reference, so it points to `null`.
Here's the bigger picture of how `let` variables work:
@ -89,8 +87,8 @@ Here's the bigger picture of how `let` variables work:
Rectangles on the right-hand side demonstrate how the global Lexical Environment changes during the execution:
1. When the script starts, the Lexical Environment is empty.
2. The `let phrase` definition appears. Now it initially has no value, so `undefined` is stored.
3. `phrase` is assigned.
2. The `let phrase` definition appears. It has been assigned no value, so `undefined` is stored.
3. `phrase` is assigned a value.
4. `phrase` refers to a new value.
Everything looks simple for now, right?
@ -104,7 +102,7 @@ To summarize:
Function Declarations are special. Unlike `let` variables, they are processed not when the execution reaches them, but when a Lexical Environment is created. For the global Lexical Environment, it means the moment when the script is started.
...And that is why we can call a function declaration before it is defined.
That is why we can call a function declaration before it is defined.
The code below demonstrates that the Lexical Environment is non-empty from the beginning. It has `say`, because that's a Function Declaration. And later it gets `phrase`, declared with `let`:
@ -113,21 +111,20 @@ The code below demonstrates that the Lexical Environment is non-empty from the b
### Inner and outer Lexical Environment
During the call `say()` uses an outer variable, so let's see the details of what's going on.
During the call, `say()` uses an outer variable, so let's look at the details of what's going on.
First, when a function runs, a new function Lexical Environment is created automatically. That's a general rule for all functions. That Lexical Environment is used to store local variables and parameters of the call.
<!--
```js
let phrase = "Hello";
function say(name) {
alert( `${phrase}, ${name}` );
}
say("John"); // Hello, John
```
-->
```js
let phrase = "Hello";
function say(name) {
alert( `${phrase}, ${name}` );
}
say("John"); // Hello, John
```-->
Here's the picture of Lexical Environments when the execution is inside `say("John")`, at the line labelled with an arrow:
@ -140,7 +137,7 @@ During the function call we have two Lexical Environments: the inner one (for th
The inner Lexical Environment has the `outer` reference to the outer one.
**When a code wants to access a variable -- it is first searched in the inner Lexical Environment, then in the outer one, then the more outer one and so on until the end of the chain.**
**When code wants to access a variable -- it is first searched for in the inner Lexical Environment, then in the outer one, then the more outer one and so on until the end of the chain.**
If a variable is not found anywhere, that's an error in strict mode. Without `use strict`, an assignment to an undefined variable creates a new global variable, for backwards compatibility.
@ -151,9 +148,9 @@ Let's see how the search proceeds in our example:
![lexical environment lookup](lexical-environment-simple-lookup.png)
Now we can give the answer to the first seed question from the beginning of the chapter.
Now we can give the answer to the first question from the beginning of the chapter.
**A function gets outer variables as they are now, the most recent values.**
**A function gets outer variables as they are now; it uses the most recent values.**
That's because of the described mechanism. Old variable values are not saved anywhere. When a function wants them, it takes the current values from its own or an outer Lexical Environment.
@ -196,9 +193,9 @@ And if a function is called multiple times, then each invocation will have its o
A function is called "nested" when it is created inside another function.
Technically, that is easily possible.
It is easily possible to do this with JavaScript.
We can use it to organize the code, like this:
We can use it to organize our code, like this:
```js
function sayHiBye(firstName, lastName) {
@ -216,7 +213,7 @@ function sayHiBye(firstName, lastName) {
Here the *nested* function `getFullName()` is made for convenience. It can access the outer variables and so can return the full name.
What's more interesting, a nested function can be returned: as a property of a new object (if the outer function creates an object with methods) or as a result by itself. And then used somewhere else. No matter where, it still keeps the access to the same outer variables.
What's more interesting, a nested function can be returned: either as a property of a new object (if the outer function creates an object with methods) or as a result by itself. It can then be used somewhere else. No matter where, it still has access to the same outer variables.
An example with the constructor function (see the chapter <info:constructor-new>):
@ -252,7 +249,7 @@ alert( counter() ); // 1
alert( counter() ); // 2
```
Let's go on with the `makeCounter` example. It creates the "counter" function that returns the next number on each invocation. Despite being simple, slightly modified variants of that code have practical uses, for instance, as a [pseudorandom number generator](https://en.wikipedia.org/wiki/Pseudorandom_number_generator), and more. So the example is not quite artificial.
Let's go on with the `makeCounter` example. It creates the "counter" function that returns the next number on each invocation. Despite being simple, slightly modified variants of that code have practical uses, for instance, as a [pseudorandom number generator](https://en.wikipedia.org/wiki/Pseudorandom_number_generator), and more. So the example is not as artificial as it may appear.
How does the counter work internally?
@ -260,22 +257,24 @@ When the inner function runs, the variable in `count++` is searched from inside
![](lexical-search-order.png)
1. The locals of the nested function.
2. The variables of the outer function.
3. ...And further until it reaches globals.
1. The locals of the nested function...
2. The variables of the outer function...
3. And so on until it reaches global variables.
In that example `count` is found on the step `2`. When an outer variable is modified, it's changed where it's found. So `count++` finds the outer variable and increases it in the Lexical Environment where it belongs. Like if we had `let count = 1`.
In this example `count` is found on step `2`. When an outer variable is modified, it's changed where it's found. So `count++` finds the outer variable and increases it in the Lexical Environment where it belongs. Like if we had `let count = 1`.
Here are two questions for you:
Here are two questions to consider:
1. Can we somehow reset the `counter` from the code that doesn't belong to `makeCounter`? E.g. after `alert` calls in the example above.
2. If we call `makeCounter()` multiple times -- it returns many `counter` functions. Are they independent or do they share the same `count`?
Try to answer them before going on reading.
Try to answer them before you continue reading.
...Are you done?
...
Okay, here we go with the answers.
All done?
Okay, let's go over the answers.
1. There is no way. The `counter` is a local function variable, we can't access it from the outside.
2. For every call to `makeCounter()` a new function Lexical Environment is created, with its own `counter`. So the resulting `counter` functions are independent.
@ -296,15 +295,15 @@ let counter2 = makeCounter();
alert( counter1() ); // 0
alert( counter1() ); // 1
alert( counter2() ); // 0 (independant)
alert( counter2() ); // 0 (independent)
```
Probably, the situation with outer variables is quite clear for you as of now. But in more complex situations a deeper understanding of internals may be required. So let's go ahead.
Hopefully, the situation with outer variables is quite clear for you now. But in more complex situations a deeper understanding of internals may be required. So let's dive deeper.
## Environments in detail
Now as you understand how closures work generally, we may finally descend to the very nuts and bolts.
Now that you understand how closures work generally, we can descend to the very nuts and bolts.
Here's what's going on in the `makeCounter` example step-by-step, follow it to make sure that you understand everything. Please note the additional `[[Environment]]` property that we didn't cover yet.
@ -314,27 +313,27 @@ Here's what's going on in the `makeCounter` example step-by-step, follow it to m
At that starting moment there is only `makeCounter` function, because it's a Function Declaration. It did not run yet.
All functions "on birth" receive a hidden property `[[Environment]]` with the reference to the Lexical Environment of their creation. We didn't talk about it yet, but technically that's a way how the function knows where it was made.
All functions "on birth" receive a hidden property `[[Environment]]` with a reference to the Lexical Environment of their creation. We didn't talk about it yet, but that's how the function knows where it was made.
Here, `makeCounter` is created in the global Lexical Environment, so `[[Environment]]` keeps the reference to it.
Here, `makeCounter` is created in the global Lexical Environment, so `[[Environment]]` keeps a reference to it.
In other words, a function is "imprinted" with a reference to the Lexical Environment where it was born. And `[[Environment]]` is the hidden function property that has that reference.
2. Then the code runs on, and the call to `makeCounter()` is performed. Here's the picture for the moment when the execution is on the first line inside `makeCounter()`:
2. The code runs on, and the call to `makeCounter()` is performed. Here's a snapshot of the moment when the execution is on the first line inside `makeCounter()`:
![](lexenv-nested-makecounter-2.png)
At the moment of the call of `makeCounter()`, the Lexical Environment is created, to hold its variables and arguments.
As all Lexical Environments, it stores two things:
1. An Environment Record with local variables. In our case `count` is the only local variable (appears in it when the line with `let count` is executed).
1. An Environment Record with local variables. In our case `count` is the only local variable (appearing when the line with `let count` is executed).
2. The outer lexical reference, which is set to `[[Environment]]` of the function. Here `[[Environment]]` of `makeCounter` references the global Lexical Environment.
So, now we have two Lexical Environments: the first one is global, the second one is for the current `makeCounter` call, with the outer reference to global.
3. During the execution of `makeCounter()`, a tiny nested function is created.
It doesn't matter whether the function is created using Function Declaration or Function Expression. All functions get the `[[Environment]]` property that references the Lexical Environment where they were made. So that new tiny nested function gets it as well.
It doesn't matter whether the function is created using Function Declaration or Function Expression. All functions get the `[[Environment]]` property that references the Lexical Environment in which they were made. So our new tiny nested function gets it as well.
For our new nested function the value of `[[Environment]]` is the current Lexical Environment of `makeCounter()` (where it was born):
@ -348,7 +347,7 @@ Here's what's going on in the `makeCounter` example step-by-step, follow it to m
That function has only one line: `return count++`, that will be executed when we run it.
5. When the `counter()` is called, an "empty" Lexical Environment is created for it. It has no local variables by itself. But the `[[Environment]]` of `counter` is used as the outer reference for it, so it has access to the variables of the former `makeCounter()` call, where it was created:
5. When the `counter()` is called, an "empty" Lexical Environment is created for it. It has no local variables by itself. But the `[[Environment]]` of `counter` is used as the outer reference for it, so it has access to the variables of the former `makeCounter()` call where it was created:
![](lexenv-nested-makecounter-5.png)
@ -356,9 +355,9 @@ Here's what's going on in the `makeCounter` example step-by-step, follow it to m
When it looks for `count`, it finds it among the variables `makeCounter`, in the nearest outer Lexical Environment.
Please note how memory management works here. When `makeCounter()` call finished some time ago, its Lexical Environment was retained in memory, because there's a nested function with `[[Environment]]` referencing it.
Please note how memory management works here. Although `makeCounter()` call finished some time ago, its Lexical Environment was retained in memory, because there's a nested function with `[[Environment]]` referencing it.
Generally, a Lexical Environment object lives until there is a function which may use it. And when there are none, it is cleared.
Generally, a Lexical Environment object lives as long as there is a function which may use it. And only when there are none remaining, it is cleared.
6. The call to `counter()` not only returns the value of `count`, but also increases it. Note that the modification is done "in place". The value of `count` is modified exactly in the environment where it was found.
@ -368,7 +367,7 @@ Here's what's going on in the `makeCounter` example step-by-step, follow it to m
7. Next `counter()` invocations do the same.
The answer to the second seed question from the beginning of the chapter should now be obvious.
The answer to the second question from the beginning of the chapter should now be obvious.
The `work()` function in the code below uses the `name` from the place of its origin through the outer lexical environment reference:
@ -376,41 +375,40 @@ The `work()` function in the code below uses the `name` from the place of its or
So, the result is `"Pete"` here.
...But if there were no `let name` in `makeWorker()`, then the search would go outside and take the global variable as we can see from the chain above. In that case it would be `"John"`.
But if there were no `let name` in `makeWorker()`, then the search would go outside and take the global variable as we can see from the chain above. In that case it would be `"John"`.
```smart header="Closures"
There is a general programming term "closure", that developers generally should know.
A [closure](https://en.wikipedia.org/wiki/Closure_(computer_programming)) is a function that remembers its outer variables and can access them. In some languages, that's not possible, or a function should be written in a special way to make it happen. But as explained above, in JavaScript all functions are naturally closures (there is only one exclusion, to be covered in <info:new-function>).
That is: they automatically remember where they are created using a hidden `[[Environment]]` property, and all of them can access outer variables.
That is: they automatically remember where they were created using a hidden `[[Environment]]` property, and all of them can access outer variables.
When on an interview a frontend developer gets a question about "what's a closure?", a valid answer would be a definition of the closure and an explanation that all functions in JavaScript are closures, and maybe few more words about technical details: the `[[Environment]]` property and how Lexical Environments work.
When on an interview, a frontend developer gets a question about "what's a closure?", a valid answer would be a definition of the closure and an explanation that all functions in JavaScript are closures, and maybe few more words about technical details: the `[[Environment]]` property and how Lexical Environments work.
```
## Code blocks and loops, IIFE
The examples above concentrated on functions. But Lexical Environments also exist for code blocks `{...}`.
They are created when a code block runs and contain block-local variables. Here's a couple of examples.
They are created when a code block runs and contain block-local variables. Here are a couple of examples.
## If
In the example below, when the execution goes into `if` block, the new "if-only" Lexical Environment is created for it:
<!--
```js run
let phrase = "Hello";
```js run
let phrase = "Hello";
if (true) {
let user = "John";
if (true) {
let user = "John";
alert(`${phrase}, ${user}`); // Hello, John
}
alert(`${phrase}, ${user}`); // Hello, John
}
alert(user); // Error, can't see such variable!
```
-->
alert(user); // Error, can't see such variable!
```-->
![](lexenv-if.png)
@ -420,10 +418,10 @@ For instance, after `if` finishes, the `alert` below won't see the `user`, hence
## For, while
For a loop, every run has a separate Lexical Environment. If the variable is declared in `for`, then it's also local to that Lexical Environment:
For a loop, every iteration has a separate Lexical Environment. If a variable is declared in `for`, then it's also local to that Lexical Environment:
```js run
for(let i = 0; i < 10; i++) {
for (let i = 0; i < 10; i++) {
// Each loop has its own Lexical Environment
// {i: value}
}
@ -443,7 +441,7 @@ For instance, in a web browser all scripts share the same global area. So if we
That may happen if the variable name is a widespread word, and script authors are unaware of each other.
If we'd like to evade that, we can use a code block to isolate the whole script or an area in it:
If we'd like to avoid that, we can use a code block to isolate the whole script or a part of it:
```js run
{
@ -457,7 +455,7 @@ If we'd like to evade that, we can use a code block to isolate the whole script
alert(message); // Error: message is not defined
```
The code outside of the block (or inside another script) doesn't see variables in it, because a code block has its own Lexical Environment.
The code outside of the block (or inside another script) doesn't see variables inside the block, because the block has its own Lexical Environment.
### IIFE
@ -475,9 +473,9 @@ They look like this:
})();
```
Here a Function Expression is created and immediately called. So the code executes right now and has its own private variables.
Here a Function Expression is created and immediately called. So the code executes right away and has its own private variables.
The Function Expression is wrapped with brackets `(function {...})`, because when JavaScript meets `"function"` in the main code flow, it understands it as a start of Function Declaration. But a Function Declaration must have a name, so there will be an error:
The Function Expression is wrapped with brackets `(function {...})`, because when JavaScript meets `"function"` in the main code flow, it understands it as the start of a Function Declaration. But a Function Declaration must have a name, so there will be an error:
```js run
// Error: Unexpected token (
@ -490,7 +488,7 @@ function() { // <-- JavaScript cannot find function name, meets ( and gives erro
}();
```
We can say "okay, let it be Function Declaration, let's add a name", but it won't work. JavaScript does not allow Function Declarations to be called immediately:
We can say "okay, let it be so Function Declaration, let's add a name", but it won't work. JavaScript does not allow Function Declarations to be called immediately:
```js run
// syntax error because of brackets below
@ -499,7 +497,7 @@ function go() {
}(); // <-- can't call Function Declaration immediately
```
...So the brackets are needed to show JavaScript that the function is created in the context of another expression, and hence it's a Function Expression. Needs no name and can be called immediately.
So, round brackets are needed to show JavaScript that the function is created in the context of another expression, and hence it's a Function Expression. It needs no name and can be called immediately.
There are other ways to tell JavaScript that we mean Function Expression:
@ -523,11 +521,11 @@ There are other ways to tell JavaScript that we mean Function Expression:
}();
```
In all cases above we declare a Function Expression and run it immediately.
In all the above cases we declare a Function Expression and run it immediately.
## Garbage collection
Lexical Environment objects that we've been talking about are subjects to same memory management rules as regular values.
Lexical Environment objects that we've been talking about are subject to the same memory management rules as regular values.
- Usually, Lexical Environment is cleaned up after the function run. For instance:
@ -567,13 +565,13 @@ Lexical Environment objects that we've been talking about are subjects to same m
return function() { alert(value); };
}
// 3 functions in array, every of them links to Lexical Environment
// 3 functions in array, every one of them links to Lexical Environment
// from the corresponding f() run
// LE LE LE
let arr = [f(), f(), f()];
```
- A Lexical Environment object dies when it becomes unreachable. That is: when no nested functions remain that reference it. In the code below, after `g` becomes unreachable, the `value` is also cleaned from the memory;
- A Lexical Environment object dies when it becomes unreachable: when no nested functions remain that reference it. In the code below, after `g` becomes unreachable, the `value` is also cleaned from memory;
```js
function f() {
@ -598,9 +596,9 @@ But in practice, JavaScript engines try to optimize that. They analyze variable
**An important side effect in V8 (Chrome, Opera) is that such variable will become unavailable in debugging.**
Try running the example below with the open Developer Tools in Chrome.
Try running the example below in Chrome with the Developer Tools open.
When it pauses, in console type `alert(value)`.
When it pauses, in the console type `alert(value)`.
```js run
function f() {
@ -641,6 +639,6 @@ g();
```warn header="See ya!"
This feature of V8 is good to know. If you are debugging with Chrome/Opera, sooner or later you will meet it.
That is not a bug of debugger, but a special feature of V8. Maybe it will be changed some time.
You always can check for it by running examples on this page.
That is not a bug in the debugger, but rather a special feature of V8. Perhaps it will be changed sometime.
You always can check for it by running the examples on this page.
```

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Before After
Before After

View file

@ -13,8 +13,6 @@ But `var` is a very different beast, that originates from very old times. It's g
If you don't plan meeting such scripts you may even skip this chapter or postpone it, but then there's a chance that it bites you later.
[cut]
From the first sight, `var` behaves similar to `let`. That is, declares a variable:
```js run
@ -52,7 +50,7 @@ If we used `let test` on the 2nd line, then it wouldn't be visible to `alert`. B
The same thing for loops: `var` cannot be block- or loop-local:
```js
for(var i = 0; i < 10; i++) {
for (var i = 0; i < 10; i++) {
// ...
}
@ -61,7 +59,7 @@ alert(i); // 10, "i" is visible after loop, it's a global variable
*/!*
```
If a code block in inside a function, then `var` becomes a function-level variable:
If a code block is inside a function, then `var` becomes a function-level variable:
```js
function sayHi() {
@ -76,7 +74,7 @@ sayHi();
alert(phrase); // Error: phrase is not defined
```
As we can see, `var` pierces through `if`, `for` or other code blocks. That's because long time ago in JavaScript blocks had no Lexical Environments. And `var` is a reminiscence of that.
As we can see, `var` pierces through `if`, `for` or other code blocks. That's because a long time ago in JavaScript blocks had no Lexical Environments. And `var` is a reminiscence of that.
## "var" are processed at the function start
@ -184,4 +182,4 @@ There are two main differences of `var`:
There's one more minor difference related to the global object, we'll cover that in the next chapter.
These differences are actually a bad thing most of time. First, we can't create block-local variables. And hoisting just creates more space for errors. So, for new scripts `var` is used exceptionally rarely.
These differences are actually a bad thing most of the time. First, we can't create block-local variables. And hoisting just creates more space for errors. So, for new scripts `var` is used exceptionally rarely.

View file

@ -3,18 +3,18 @@
As we already know, functions in JavaScript are values.
Every value in JavaScript has the type. What type of value is a function?
Every value in JavaScript has a type. What type is a function?
In JavaScript, a function is an object.
In JavaScript, functions are objects.
A good way to imagine functions is as callable "action objects". We can not only call them, but also treat them as objects: add/remove properties, pass by reference etc.
## The "name" property
Function objects contain few sometimes-useable properties.
Function objects contain a few useable properties.
For instance, a function name is accessible as the "name" property:
For instance, a function's name is accessible as the "name" property:
```js run
function sayHi() {
@ -24,7 +24,7 @@ function sayHi() {
alert(sayHi.name); // sayHi
```
What's more funny, the name-assigning logic is smart. It also sticks the right name to function that are used in assignments:
What's more funny, the name-assigning logic is smart. It also assigns the correct name to functions that are used in assignments:
```js run
let sayHi = function() {
@ -34,7 +34,7 @@ let sayHi = function() {
alert(sayHi.name); // sayHi (works!)
```
Also works if the assignment is done via a default value:
It also works if the assignment is done via a default value:
```js run
function f(sayHi = function() {}) {
@ -65,9 +65,7 @@ alert(user.sayHi.name); // sayHi
alert(user.sayBye.name); // sayBye
```
There's no magic though. There are cases when there's no way to figure out the right name.
Then it's empty, like here:
There's no magic though. There are cases when there's no way to figure out the right name. In that case, the name property empty, like here:
```js
// function created inside array
@ -77,7 +75,7 @@ alert( arr[0].name ); // <empty string>
// the engine has no way to set up the right name, so there is none
```
In practice, most functions do have a name.
In practice, however, most functions do have a name.
## The "length" property
@ -97,14 +95,14 @@ Here we can see that rest parameters are not counted.
The `length` property is sometimes used for introspection in functions that operate on other functions.
For instance, in the code below `ask` function accepts a `question` to ask and an arbitrary number of `handler` functions to call.
For instance, in the code below the `ask` function accepts a `question` to ask and an arbitrary number of `handler` functions to call.
When a user answers, it calls the handlers. We can pass two kinds of handlers:
Once a user provides their answer, the function calls the handlers. We can pass two kinds of handlers:
- A zero-argument function, then it is only called for a positive answer.
- A function with arguments, then it is called in any case and gets the answer.
- A zero-argument function, which is only called when the user gives a positive answer.
- A function with arguments, which is called in either case and returns an answer.
The idea is that we have a simple no-arguments handler syntax for positive cases (most frequent variant), but allow to provide universal handlers as well.
The idea is that we have a simple, no-arguments handler syntax for positive cases (most frequent variant), but are able to provide universal handlers as well.
To call `handlers` the right way, we examine the `length` property:
@ -158,7 +156,7 @@ A property assigned to a function like `sayHi.counter = 0` does *not* define a l
We can treat a function as an object, store properties in it, but that has no effect on its execution. Variables never use function properties and vice versa. These are just parallel words.
```
Function properties can replace the closure sometimes. For instance, we can rewrite the counter example from the chapter <info:closure> to use a function property:
Function properties can replace closures sometimes. For instance, we can rewrite the counter function example from the chapter <info:closure> to use a function property:
```js run
function makeCounter() {
@ -181,9 +179,9 @@ alert( counter() ); // 1
The `count` is now stored in the function directly, not in its outer Lexical Environment.
Is it worse or better than using the closure?
Is it better or worse than using a closure?
The main difference is that if the value of `count` lives in an outer variable, then an external code is unable to access it. Only nested functions may modify it. And if it's bound to function, then such thing is possible:
The main difference is that if the value of `count` lives in an outer variable, then external code is unable to access it. Only nested functions may modify it. And if it's bound to a function, then such a thing is possible:
```js run
function makeCounter() {
@ -205,11 +203,11 @@ alert( counter() ); // 10
*/!*
```
So it depends on our aims which variant to choose.
So the choice of implementation depends on our aims.
## Named Function Expression
Named Function Expression or, shortly, NFE, is a term for Function Expressions that have a name.
Named Function Expression, or NFE, is a term for Function Expressions that have a name.
For instance, let's take an ordinary Function Expression:
@ -219,7 +217,7 @@ let sayHi = function(who) {
};
```
...And add a name to it:
And add a name to it:
```js
let sayHi = function *!*func*/!*(who) {
@ -227,7 +225,7 @@ let sayHi = function *!*func*/!*(who) {
};
```
Did we do anything sane here? What's the role of that additional `"func"` name?
Did we achieve anything here? What's the purpose of that additional `"func"` name?
First let's note, that we still have a Function Expression. Adding the name `"func"` after `function` did not make it a Function Declaration, because it is still created as a part of an assignment expression.
@ -245,10 +243,10 @@ sayHi("John"); // Hello, John
There are two special things about the name `func`:
1. It allows to reference the function from inside itself.
1. It allows the function to reference itself internally.
2. It is not visible outside of the function.
For instance, the function `sayHi` below re-calls itself with `"Guest"` if no `who` is provided:
For instance, the function `sayHi` below calls itself again with `"Guest"` if no `who` is provided:
```js run
let sayHi = function *!*func*/!*(who) {
@ -305,9 +303,9 @@ welcome(); // Error, the nested sayHi call doesn't work any more!
That happens because the function takes `sayHi` from its outer lexical environment. There's no local `sayHi`, so the outer variable is used. And at the moment of the call that outer `sayHi` is `null`.
The optional name which we can put into the Function Expression is exactly meant to solve this kind of problems.
The optional name which we can put into the Function Expression is meant to solve exactly these kinds of problems.
Let's use it to fix the code:
Let's use it to fix our code:
```js run
let sayHi = function *!*func*/!*(who) {
@ -326,9 +324,9 @@ sayHi = null;
welcome(); // Hello, Guest (nested call works)
```
Now it works, because the name `"func"` is function-local. It is not taken from outside (and not visible there). The specification guarantees that it always references the current function.
Now it works, because the name `"func"` is function-local. It is not taken from outside (and not visible there). The specification guarantees that it will always reference the current function.
The outer code still has it's variable `sayHi` or `welcome` later. And `func` is an "internal function name", how it calls itself privately.
The outer code still has it's variable `sayHi` or `welcome`. And `func` is an "internal function name", how the function can call itself internally.
```smart header="There's no such thing for Function Declaration"
The "internal name" feature described here is only available for Function Expressions, not to Function Declarations. For Function Declarations, there's just no syntax possibility to add a one more "internal" name.
@ -345,10 +343,10 @@ Here we covered their properties:
- `name` -- the function name. Exists not only when given in the function definition, but also for assignments and object properties.
- `length` -- the number of arguments in the function definition. Rest parameters are not counted.
If the function is declared as a Function Expression (not in the main code flow), and it carries the name, then it is called Named Function Expression. The name can be used inside to reference itself, for recursive calls or such.
If the function is declared as a Function Expression (not in the main code flow), and it carries the name, then it is called a Named Function Expression. The name can be used inside to reference itself, for recursive calls or such.
Also, functions may carry additional properties. Many well-known JavaScript libraries make a great use of this feature.
Also, functions may carry additional properties. Many well-known JavaScript libraries make great use of this feature.
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). Actually, they do it to less pollute the global space, so that a single library gives only one global variable. That lowers the chance of possible naming conflicts.
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). Actually, they do it to lessen their pollution of the global space, so that a single library gives only one global variable. That reduces the possibility of naming conflicts.
So, a function can do a useful job by itself and also carry a bunch of other functionality in properties.

View file

@ -3,27 +3,25 @@
There's one more way to create a function. It's rarely used, but sometimes there's no alternative.
[cut]
## The syntax
## Syntax
The syntax for creating a function:
```js
let func = new Function('a', 'b', 'return a + b');
let func = new Function ([arg1[, arg2[, ...argN]],] functionBody)
```
All arguments of `new Function` are strings. Parameters go first, and the body is the last.
In other words, function parameters (or, more precisely, names for them) go first, and the body is last. All arguments are strings.
For instance:
It's easier to understand by looking at an example. Here's a function with two arguments:
```js run
let sum = new Function('arg1', 'arg2', 'return arg1 + arg2');
let sum = new Function('a', 'b', 'return a + b');
alert( sum(1, 2) ); // 3
```
If there are no arguments, then there will be only body:
If there are no arguments, then there's only a single argument, the function body:
```js run
let sayHi = new Function('alert("Hello")');
@ -31,22 +29,22 @@ let sayHi = new Function('alert("Hello")');
sayHi(); // Hello
```
The major difference from other ways we've seen -- the function is created literally from a string, that is passed at run time.
The major difference from other ways we've seen is that the function is created literally from a string, that is passed at run time.
All previous declarations required us, programmers, to write the function code in the script.
But `new Function` allows to turn any string into a function, for example we can receive a new function from the server and then execute it:
But `new Function` allows to turn any string into a function. For example, we can receive a new function from a server and then execute it:
```js
let str = ... receive the code from the server dynamically ...
let str = ... receive the code from a server dynamically ...
let func = new Function(str);
func();
```
It is used in very specific cases, like when we receive the code from the server, or to dynamically compile a function from a template. The need for that usually arises at advanced stages of development.
It is used in very specific cases, like when we receive code from a server, or to dynamically compile a function from a template. The need for that usually arises at advanced stages of development.
## The closure
## Closure
Usually, a function remembers where it was born in the special property `[[Environment]]`. It references the Lexical Environment from where it's created.
@ -85,29 +83,29 @@ getFunc()(); // *!*"test"*/!*, from the Lexical Environment of getFunc
This special feature of `new Function` looks strange, but appears very useful in practice.
Imagine that we really have to create a function from the string. The code of that function is not known at the time of writing the script (that's why we don't use regular functions), but will be known in the process of execution. We may receive it from the server or from another source.
Imagine that we must create a function from a string. The code of that function is not known at the time of writing the script (that's why we don't use regular functions), but will be known in the process of execution. We may receive it from the server or from another source.
Our new function needs to interact with the main script.
Maybe we want it to be able to access outer local variables?
Perhaps we want it to be able to access outer local variables?
But the problem is that before JavaScript is published to production, it's compressed using a *minifier* -- a special program that shrinks code by removing extra comments, spaces and -- what's important, renames local variables into shorter ones.
The problem is that before JavaScript is published to production, it's compressed using a *minifier* -- a special program that shrinks code by removing extra comments, spaces and -- what's important, renames local variables into shorter ones.
For instance, if a function has `let userName`, minifier replaces it `let a` (or another letter if this one is occupied), and does it everywhere. That's usually a safe thing to do, because the variable is local, nothing outside the function can access it. And inside the function minifier replaces every mention of it. Minifiers are smart, they analyze the code structure, not just find-and-replace, so that's ok.
For instance, if a function has `let userName`, minifier replaces it `let a` (or another letter if this one is occupied), and does it everywhere. That's usually a safe thing to do, because the variable is local, nothing outside the function can access it. And inside the function, minifier replaces every mention of it. Minifiers are smart, they analyze the code structure, so they don't break anything. They're not just a dumb find-and-replace.
...But if `new Function` could access outer variables, then it would be unable to find `userName`.
But, if `new Function` could access outer variables, then it would be unable to find `userName`, since this is passed in as a string *after* the code is minified.
**Even if we could access outer lexical environment in `new Function`, we would have problems with minifiers.**
The "special feature" of `new Function` saves us from mistakes.
And it enforces better code. If we need to pass something to a function, created by `new Function`, we should pass it explicitly as arguments.
And it enforces better code. If we need to pass something to a function created by `new Function`, we should pass it explicitly as an argument.
The "sum" function actually does that right:
Our "sum" function actually does that right:
```js run
*!*
let sum = new Function('a', 'b', ' return a + b; ');
let sum = new Function('a', 'b', 'return a + b');
*/!*
let a = 1, b = 2;
@ -131,9 +129,9 @@ For historical reasons, arguments can also be given as a comma-separated list.
These three mean the same:
```js
new Function('a', 'b', ' return a + b; '); // basic syntax
new Function('a,b', ' return a + b; '); // comma-separated
new Function('a , b', ' return a + b; '); // comma-separated with spaces
new Function('a', 'b', 'return a + b'); // basic syntax
new Function('a,b', 'return a + b'); // comma-separated
new Function('a , b', 'return a + b'); // comma-separated with spaces
```
Functions created with `new Function`, have `[[Environment]]` referencing the global Lexical Environment, not the outer one. Hence, they can not use outer variables. But that's actually good, because it saves us from errors. Explicit parameters passing is a much better thing architecturally and has no problems with minifiers.
Functions created with `new Function`, have `[[Environment]]` referencing the global Lexical Environment, not the outer one. Hence, they cannot use outer variables. But that's actually good, because it saves us from errors. Passing parameters explicitly is a much better method architecturally and causes no problems with minifiers.

View file

@ -15,7 +15,7 @@ function count() {
if (i == 1000000000) {
alert("Done in " + (Date.now() - start) + 'ms');
cancelInterval(timer);
clearInterval(timer);
}
}

View file

@ -6,14 +6,14 @@ importance: 5
In the code below there's a `setTimeout` call scheduled, then a heavy calculation is run, that takes more than 100ms to finish.
When the scheduled function will run?
When will the scheduled function run?
1. After the loop.
2. Before the loop.
3. In the beginning of the loop.
What `alert` is going to show?
What is `alert` going to show?
```js
let i = 0;

View file

@ -10,8 +10,6 @@ There are two methods for it:
These methods are not a part of JavaScript specification. But most environments have the internal scheduler and provide these methods. In particular, they are supported in all browsers and Node.JS.
[cut]
## setTimeout
The syntax:
@ -91,7 +89,7 @@ let timerId = setTimeout(...);
clearTimeout(timerId);
```
In the code below we schedule the function and then cancel it (changed our mind). As a result, nothing happens:
In the code below, we schedule the function and then cancel it (changed our mind). As a result, nothing happens:
```js run no-beautify
let timerId = setTimeout(() => alert("never happens"), 1000);
@ -101,7 +99,7 @@ clearTimeout(timerId);
alert(timerId); // same identifier (doesn't become null after canceling)
```
As we can see from `alert` output, in a browser the timer identifier is a number. In other environments, that can be something else. For instance, Node.JS returns a timer object with additional methods.
As we can see from `alert` output, in a browser the timer identifier is a number. In other environments, this can be something else. For instance, Node.JS returns a timer object with additional methods.
Again, there is no universal specification for these methods, so that's fine.
@ -109,7 +107,7 @@ For browsers, timers are described in the [timers section](https://www.w3.org/TR
## setInterval
Method `setInterval` has the same syntax as `setTimeout`:
The `setInterval` method has the same syntax as `setTimeout`:
```js
let timerId = setInterval(func|code, delay[, arg1, arg2...])
@ -154,11 +152,11 @@ let timerId = setTimeout(function tick() {
}, 2000);
```
The `setTimeout` above schedules next call right at the end of the current one `(*)`.
The `setTimeout` above schedules the next call right at the end of the current one `(*)`.
Recursive `setTimeout` is more flexible method than `setInterval`. This way the next call may be scheduled differently, depending on the results of the current one.
The recursive `setTimeout` is a more flexible method than `setInterval`. This way the next call may be scheduled differently, depending on the results of the current one.
For instance, we need to write a service that each 5 seconds sends a request to server asking for data, but in case the server is overloaded, it should increase the interval to 10, 20, 40 seconds...
For instance, we need to write a service that sends a request to the server every 5 seconds asking for data, but in case the server is overloaded, it should increase the interval to 10, 20, 40 seconds...
Here's the pseudocode:
```js
@ -178,7 +176,7 @@ let timerId = setTimeout(function request() {
```
And if we regulary have CPU-hungry tasks, then we can measure the time taken by the execution and plan the next call sooner or later.
And if we regularly have CPU-hungry tasks, then we can measure the time taken by the execution and plan the next call sooner or later.
**Recursive `setTimeout` guarantees a delay between the executions, `setInterval` -- does not.**
@ -205,23 +203,23 @@ For `setInterval` the internal scheduler will run `func(i)` every 100ms:
![](setinterval-interval.png)
Did you notice?...
Did you notice?
**The real delay between `func` calls for `setInterval` is less than in the code!**
That's natural, because the time taken by `func` execution "consumes" a part of the interval.
That's normal, because the time taken by `func`'s execution "consumes" a part of the interval.
It is possible that `func` execution turns out to be longer than we expected and takes more than 100ms.
It is possible that `func`'s execution turns out to be longer than we expected and takes more than 100ms.
In this case the engine waits for `func` to complete, then checks the scheduler and if the time is up, then runs it again *immediately*.
In this case the engine waits for `func` to complete, then checks the scheduler and if the time is up, runs it again *immediately*.
In the edge case, if the function always executes longer than `delay` ms, then the calls will happen without pause at all.
In the edge case, if the function always executes longer than `delay` ms, then the calls will happen without a pause at all.
And here is the picture for recursive `setTimeout`:
And here is the picture for the recursive `setTimeout`:
![](settimeout-interval.png)
**Recursive `setTimeout` guarantees the fixed delay (here 100ms).**
**The recursive `setTimeout` guarantees the fixed delay (here 100ms).**
That's because a new call is planned at the end of the previous one.
@ -233,9 +231,9 @@ When a function is passed in `setInterval/setTimeout`, an internal reference is
setTimeout(function() {...}, 100);
```
For `setInterval` the function stays in memory until `cancelInterval` is called.
For `setInterval` the function stays in memory until `clearInterval` is called.
There's a side-effect. A function references the outer lexical environment, so, while it lives, outer variables live too. They may take much more memory than the function itself. So when we don't need the scheduled function any more, it's better to cancel it, even if it's very small.
There's a side-effect. A function references the outer lexical environment, so, while it lives, outer variables live too. They may take much more memory than the function itself. So when we don't need the scheduled function anymore, it's better to cancel it, even if it's very small.
````
## setTimeout(...,0)
@ -258,11 +256,11 @@ The first line "puts the call into calendar after 0ms". But the scheduler will o
### Splitting CPU-hungry tasks
There's a trick to split CPU-hungry task using `setTimeout`.
There's a trick to split CPU-hungry tasks using `setTimeout`.
For instance, syntax highlighting script (used to colorize code examples on this page) is quite CPU-heavy. To hightlight the code, it performs the analysis, creates many colored elements, adds them to the document -- for a big text that takes a lot. It may even cause the browser to "hang", that's unacceptable.
For instance, a syntax-highlighting script (used to colorize code examples on this page) is quite CPU-heavy. To highlight the code, it performs the analysis, creates many colored elements, adds them to the document -- for a big text that takes a lot. It may even cause the browser to "hang", which is unacceptable.
So we can split the long text to pieces. First 100 lines, then plan another 100 lines using `setTimeout(...,0)`, and so on.
So we can split the long text into pieces. First 100 lines, then plan another 100 lines using `setTimeout(...,0)`, and so on.
For clarity, let's take a simpler example for consideration. We have a function to count from `1` to `1000000000`.
@ -276,7 +274,7 @@ let start = Date.now();
function count() {
// do a heavy job
for(let j = 0; j < 1e9; j++) {
for (let j = 0; j < 1e9; j++) {
i++;
}
@ -286,7 +284,7 @@ function count() {
count();
```
The browser may even show "the script takes too long" warning (but hopefully won't, the number is not very big).
The browser may even show "the script takes too long" warning (but hopefully it won't, because the number is not very big).
Let's split the job using the nested `setTimeout`:
@ -319,15 +317,15 @@ We do a part of the job `(*)`:
1. First run: `i=1...1000000`.
2. Second run: `i=1000001..2000000`.
3. ...and so on, the `while` checks if `i` is evenly divided by `100000`.
3. ...and so on, the `while` checks if `i` is evenly divided by `1000000`.
Then the next call is scheduled in `(*)` if we're not done yet.
Pauses between `count` executions provide just enough "breath" for the JavaScript engine to do something else, to react on other user actions.
Pauses between `count` executions provide just enough "breath" for the JavaScript engine to do something else, to react to other user actions.
The notable thing is that both variants: with and without splitting the job by `setInterval` -- are comparable in speed. There's no much difference in the overall counting time.
The notable thing is that both variants -- with and without splitting the job by `setTimeout` -- are comparable in speed. There's no much difference in the overall counting time.
To make them closer let's make an improvement.
To make them closer, let's make an improvement.
We'll move the scheduling in the beginning of the `count()`:
@ -356,14 +354,14 @@ function count() {
count();
```
Now when we start to `count()` and know that we'll need to `count()` more -- we schedule that immediately, before doing the job.
Now when we start to `count()` and know that we'll need to `count()` more, we schedule that immediately, before doing the job.
If you run it, easy to notice that it takes significantly less time.
If you run it, it's easy to notice that it takes significantly less time.
````smart header="Minimal delay of nested timers in-browser"
In the browser, there's a limitation of how often nested timers can run. The [HTML5 standard](https://www.w3.org/TR/html5/webappapis.html#timers) says: "after five nested timers..., the interval is forced to be at least four milliseconds.".
In the browser, there's a limitation of how often nested timers can run. The [HTML5 standard](https://www.w3.org/TR/html5/webappapis.html#timers) says: "after five nested timers, the interval is forced to be at least four milliseconds.".
Let's demonstrate what it means by the example below. The `setTimeout` call in it re-schedules itself after `0ms`. Each call remembers the real time from the previous one in the `times` array. What the real delays look like? Let's see:
Let's demonstrate what it means with the example below. The `setTimeout` call in it re-schedules itself after `0ms`. Each call remembers the real time from the previous one in the `times` array. What do the real delays look like? Let's see:
```js run
let start = Date.now();
@ -401,7 +399,7 @@ Here's the demo:
let i = 0;
function count() {
for(let j = 0; j < 1e6; j++) {
for (let j = 0; j < 1e6; j++) {
i++;
// put the current i into the <div>
// (we'll talk more about innerHTML in the specific chapter, should be obvious here)
@ -461,4 +459,4 @@ For example, the in-browser timer may slow down for a lot of reasons:
- The browser tab is in the background mode.
- The laptop is on battery.
All that may decrease the minimal timer resolution (the minimal delay) to 300ms or even 1000ms depending on the browser and settings.
All that may increase the minimal timer resolution (the minimal delay) to 300ms or even 1000ms depending on the browser and settings.

View file

@ -24,7 +24,7 @@ describe("spy", function() {
let wrappedSum = spy(sum);
assert.equal(wrappedSum(1, 2), 3);
assert(spy.calledWith(1, 2));
assert(sum.calledWith(1, 2));
});
@ -36,9 +36,9 @@ describe("spy", function() {
calc.wrappedSum = spy(calc.sum);
assert.equal(calculator.wrappedSum(1, 2), 3);
assert(spy.calledWith(1, 2));
assert(spy.calledOn(calculator));
assert.equal(calc.wrappedSum(1, 2), 3);
assert(calc.sum.calledWith(1, 2));
assert(calc.sum.calledOn(calc));
});
});
});

View file

@ -1 +1 @@
Here we can use `log.push(args)` to store all arguments in the log and `f.apply(this, args)` to forward the call.
Here we can use `calls.push(args)` to store all arguments in the log and `f.apply(this, args)` to forward the call.

View file

@ -22,7 +22,7 @@ work = spy(work);
work(1, 2); // 3
work(4, 5); // 9
for(let args of work.calls) {
for (let args of work.calls) {
alert( 'call:' + args.join() ); // "call:1,2", "call:4,5"
}
```

View file

@ -2,8 +2,6 @@
JavaScript gives exceptional flexibility when dealing with functions. They can be passed around, used as objects, and now we'll see how to *forward* calls between them and *decorate* them.
[cut]
## Transparent caching
Let's say we have a function `slow(x)` which is CPU-heavy, but its results are stable. In other words, for the same `x` it always returns the same result.
@ -70,7 +68,7 @@ To summarize, there are several benefits of using a separate `cachingDecorator`
The caching decorator mentioned above is not suited to work with object methods.
For instance, in the code below `user.format()` stops working after the decoration:
For instance, in the code below `worker.slow()` stops working after the decoration:
```js run
// we'll make worker.slow caching
@ -135,7 +133,7 @@ func.call(context, arg1, arg2, ...)
It runs `func` providing the first argument as `this`, and the next as the arguments.
To put it simple, these two calls do almost the same:
To put it simply, these two calls do almost the same:
```js
func(1, 2, 3);
func.call(obj, 1, 2, 3)
@ -154,8 +152,8 @@ let user = { name: "John" };
let admin = { name: "Admin" };
// use call to pass different objects as "this"
sayHi.call( user ); // John
sayHi.call( admin ); // Admin
sayHi.call( user ); // this = John
sayHi.call( admin ); // this = Admin
```
And here we use `call` to call `say` with the given context and phrase:
@ -241,7 +239,7 @@ There are many solutions possible:
1. Implement a new (or use a third-party) map-like data structure that is more versatile and allows multi-keys.
2. Use nested maps: `cache.set(min)` will be a `Map` that stores the pair `(max, result)`. So we can get `result` as `cache.get(min).get(max)`.
3. Join two values into one. In our particular case we can just use a string `"min,max"` as the `Map` key. For flexibility, we can allow to provide a *hashing function* for the decorator, that knows how to make a one value from many.
3. Join two values into one. In our particular case we can just use a string `"min,max"` as the `Map` key. For flexibility, we can allow to provide a *hashing function* for the decorator, that knows how to make one value from many.
For many practical applications, the 3rd variant is good enough, so we'll stick to it.
@ -319,7 +317,7 @@ let wrapper = function() {
That's called *call forwarding*. The `wrapper` passes everything it gets: the context `this` and arguments to `anotherFunction` and returns back its result.
When an external code calls such `wrapper`, it is undistinguishable from the call of the original function.
When an external code calls such `wrapper`, it is indistinguishable from the call of the original function.
Now let's bake it all into the more powerful `cachingDecorator`:
@ -438,7 +436,7 @@ So, technically it takes `this` and joins `this[0]`, `this[1]` ...etc together.
*Decorator* is a wrapper around a function that alters its behavior. The main job is still carried out by the function.
It is generally safe to replace a function or a method with a decorated one, except for one little thing. If the original function had properties on it, like `func.calledCount` or whatever, then the decorated one will not provide them. Because that is a wrapper. So one need to be careful if one uses them. Some decorators provide their own properties.
It is generally safe to replace a function or a method with a decorated one, except for one little thing. If the original function had properties on it, like `func.calledCount` or whatever, then the decorated one will not provide them. Because that is a wrapper. So one needs to be careful if one uses them. Some decorators provide their own properties.
Decorators can be seen as "features" or "aspects" that can be added to a function. We can add one or add many. And all this without changing its code!

View file

@ -9,8 +9,6 @@ When using `setTimeout` with object methods or passing object methods along, the
Suddenly, `this` just stops working right. The situation is typical for novice developers, but happens with experienced ones as well.
[cut]
## Losing "this"
We already know that in JavaScript it's easy to lose `this`. Once a method is passed somewhere separately from the object -- `this` is lost.
@ -72,7 +70,7 @@ setTimeout(() => user.sayHi(), 1000); // Hello, John!
Looks fine, but a slight vulnerability appears in our code structure.
What if before `setTimeout` triggers (there's one second delay!) `user` changes value? Then, suddenly, the it will call the wrong object!
What if before `setTimeout` triggers (there's one second delay!) `user` changes value? Then, suddenly, it will call the wrong object!
```js run

Some files were not shown because too many files have changed in this diff Show more