Merge branch 'master' into patch-1

This commit is contained in:
Miguel N. Galace 2020-09-23 18:44:06 +08:00 committed by GitHub
commit 482ca750a4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
327 changed files with 3983 additions and 2566 deletions

View file

@ -4,7 +4,7 @@ Let's see what's so special about JavaScript, what we can achieve with it, and w
## What is JavaScript?
*JavaScript* was initially created to *"make web pages alive"*.
*JavaScript* was initially created to "make web pages alive".
The programs in this language are called *scripts*. They can be written right in a web page's HTML and run automatically as the page loads.
@ -26,7 +26,7 @@ Different engines have different "codenames". For example:
- [V8](https://en.wikipedia.org/wiki/V8_(JavaScript_engine)) -- in Chrome and Opera.
- [SpiderMonkey](https://en.wikipedia.org/wiki/SpiderMonkey) -- in Firefox.
- ...There are other codenames like "Trident" and "Chakra" for different versions of IE, "ChakraCore" for Microsoft Edge, "Nitro" and "SquirrelFish" for Safari, etc.
- ...There are other codenames like "Chakra" for 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.
@ -63,7 +63,7 @@ JavaScript's abilities in the browser are limited for the sake of the user's saf
Examples of such restrictions include:
- JavaScript on a webpage may not read/write arbitrary files on the hard disk, copy them or execute programs. It has no direct access to OS system functions.
- JavaScript on a webpage may not read/write arbitrary files on the hard disk, copy them or execute programs. It has no direct access to OS functions.
Modern browsers allow it to work with files, but the access is limited and only provided if the user does certain actions, like "dropping" a file into a browser window or selecting it via an `<input>` tag.
@ -110,6 +110,7 @@ Examples of such languages:
- [TypeScript](http://www.typescriptlang.org/) is concentrated on adding "strict data typing" to simplify the development and support of complex systems. It is developed by Microsoft.
- [Flow](http://flow.org/) also adds data typing, but in a different way. Developed by Facebook.
- [Dart](https://www.dartlang.org/) is a standalone language that has its own engine that runs in non-browser environments (like mobile apps), but also can be transpiled to JavaScript. Developed by Google.
- [Brython](https://brython.info/) is a Python transpiler to JavaScript that allow to write application in pure Python without JavaScript.
There are more. Of course, even if we use one of transpiled languages, we should also know JavaScript to really understand what we're doing.

View file

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<body>
<script>
alert( "I'm JavaScript!" );
</script>
</body>
</html>

View file

@ -0,0 +1,2 @@
[html src="index.html"]

View file

@ -46,7 +46,7 @@ The `<script>` tag contains JavaScript code which is automatically executed when
The `<script>` tag has a few attributes that are rarely used nowadays but can still be found in old code:
The `type` attribute: <code>&lt;script <u>type</u>=...&gt;</code>
: The old HTML standard, HTML4, required a script to have a `type`. Usually it was `type="text/javascript"`. It's not required anymore. Also, the modern HTML standard totally changed the meaning of this attribute. Now, it can be used for JavaScript modules. But that's an advanced topic; we'll talk about modules in another part of the tutorial.
: The old HTML standard, HTML4, required a script to have a `type`. Usually it was `type="text/javascript"`. It's not required anymore. Also, the modern HTML standard totally changed the meaning of this attribute. Now, it can be used for JavaScript modules. But that's an advanced topic, we'll talk about modules in another part of the tutorial.
The `language` attribute: <code>&lt;script <u>language</u>=...&gt;</code>
: This attribute was meant to show the language of the script. This attribute no longer makes sense because JavaScript is the default language. There is no need to use it.
@ -78,7 +78,7 @@ Here, `/path/to/script.js` is an absolute path to the script from the site root.
We can give a full URL as well. For instance:
```html
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.2.0/lodash.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>
```
To attach several scripts, use multiple tags:

View file

@ -94,7 +94,7 @@ But it should be two separate statements, not one. Such a merging in this case i
We recommend putting semicolons between statements even if they are separated by newlines. This rule is widely adopted by the community. Let's note once again -- *it is possible* to leave out semicolons most of the time. But it's safer -- especially for a beginner -- to use them.
## Comments
## Comments [#code-comments]
As time goes on, programs become more and more complex. It becomes necessary to add *comments* which describe what the code does and why.
@ -136,7 +136,7 @@ alert('World');
```
```smart header="Use hotkeys!"
In most editors, a line of code can be commented out by pressing the `key:Ctrl+/` hotkey for a single-line comment and something like `key:Ctrl+Shift+/` -- for multiline comments (select a piece of code and press the hotkey). For Mac, try `key:Cmd` instead of `key:Ctrl`.
In most editors, a line of code can be commented out by pressing the `key:Ctrl+/` hotkey for a single-line comment and something like `key:Ctrl+Shift+/` -- for multiline comments (select a piece of code and press the hotkey). For Mac, try `key:Cmd` instead of `key:Ctrl` and `key:Option` instead of `key:Shift`.
```
````warn header="Nested comments are not supported!"

View file

@ -19,8 +19,7 @@ For example:
...
```
We will learn functions (a way to group commands) soon. Looking ahead, let's note that `"use strict"` can be put at the beginning of the function body instead of the whole script. Doing that enables strict mode in that function only. But usually, people use it for the whole script.
Quite soon we're going to learn functions (a way to group commands), so let's note in advance that `"use strict"` can be put at the beginning of a function. Doing that enables strict mode in that function only. But usually people use it for the whole script.
````warn header="Ensure that \"use strict\" is at the top"
Please make sure that `"use strict"` is at the top of your scripts, otherwise strict mode may not be enabled.
@ -47,11 +46,13 @@ Once we enter strict mode, there's no going back.
## Browser console
For the future, when you use a browser console to test features, please note that it doesn't `use strict` by default.
When you use a [developer console](info:devtools) to run code, please note that it doesn't `use strict` by default.
Sometimes, when `use strict` makes a difference, you'll get incorrect results.
You can try to press `key:Shift+Enter` to input multiple lines, and put `use strict` on top, like this:
So, how to actually `use strict` in the console?
First, you can try to press `key:Shift+Enter` to input multiple lines, and put `use strict` on top, like this:
```js
'use strict'; <Shift+Enter for a newline>
@ -61,25 +62,28 @@ You can try to press `key:Shift+Enter` to input multiple lines, and put `use str
It works in most browsers, namely Firefox and Chrome.
If it doesn't, the most reliable way to ensure `use strict` would be to input the code into console like this:
If it doesn't, e.g. in an old browser, there's an ugly, but reliable way to ensure `use strict`. Put it inside this kind of wrapper:
```js
(function() {
'use strict';
// ...your code...
// ...your code here...
})()
```
## Always "use strict"
## Should we "use strict"?
We have yet to cover the differences between strict mode and the "default" mode.
The question may sound obvious, but it's not so.
In the next chapters, as we learn language features, we'll note the differences between the strict and default modes. Luckily, there aren't many and they actually make our lives better.
One could recommend to start scripts with `"use strict"`... But you know what's cool?
For now, it's enough to know about it in general:
Modern JavaScript supports "classes" and "modules" - advanced language structures (we'll surely get to them), that enable `use strict` automatically. So we don't need to add the `"use strict"` directive, if we use them.
1. The `"use strict"` directive switches the engine to the "modern" mode, changing the behavior of some built-in features. We'll see the details later in the tutorial.
2. Strict mode is enabled by placing `"use strict"` at the top of a script or function. Several language features, like "classes" and "modules", enable strict mode automatically.
3. Strict mode is supported by all modern browsers.
4. We recommended always starting scripts with `"use strict"`. All examples in this tutorial assume strict mode unless (very rarely) specified otherwise.
**So, for now `"use strict";` is a welcome guest at the top of your scripts. Later, when your code is all in classes and modules, you may omit it.**
As of now, we've got to know about `use strict` in general.
In the next chapters, as we learn language features, we'll see the differences between the strict and old modes. Luckily, there aren't many and they actually make our lives better.
All examples in this tutorial assume strict mode unless (very rarely) specified otherwise.

View file

@ -80,7 +80,6 @@ let user = 'John'
Technically, all these variants do the same thing. So, it's a matter of personal taste and aesthetics.
````smart header="`var` instead of `let`"
In older scripts, you may also find another keyword: `var` instead of `let`:
@ -135,6 +134,20 @@ alert(hello); // Hello world!
alert(message); // Hello world!
```
````warn header="Declaring twice triggers an error"
A variable should be declared only once.
A repeated declaration of the same variable is an error:
```js run
let message = "This";
// repeated 'let' leads to an error
let message = "That"; // SyntaxError: 'message' has already been declared
```
So, we should declare a variable once and then refer to it without `let`.
````
```smart header="Functional languages"
It's interesting to note that there exist [functional](https://en.wikipedia.org/wiki/Functional_programming) programming languages, like [Scala](http://www.scala-lang.org/) or [Erlang](http://www.erlang.org/) that forbid changing variable values.
@ -190,7 +203,7 @@ let имя = '...';
let 我 = '...';
```
Technically, there is no error here, such names are allowed, but there is an international tradition to use English in variable names. Even if we're writing a small script, it may have a long life ahead. People from other countries may need to read it some time.
Technically, there is no error here. Such names are allowed, but there is an international convention to use English in variable names. Even if we're writing a small script, it may have a long life ahead. People from other countries may need to read it some time.
````
````warn header="Reserved names"

View file

@ -1,6 +1,10 @@
# Data types
A variable in JavaScript can contain any data. A variable can at one moment be a string and at another be a number:
A value in JavaScript is always of a certain type. For example, a string or a number.
There are eight basic data types in JavaScript. Here, we'll cover them in general and in the next chapters we'll talk about each of them in detail.
We can put any type in a variable. For example, a variable can at one moment be a string and then store a number:
```js
// no error
@ -8,9 +12,7 @@ let message = "hello";
message = 123456;
```
Programming languages that allow such things are called "dynamically typed", meaning that there are data types, but variables are not bound to any of them.
There are eight basic data types in JavaScript. Here, we'll cover them in general and in the next chapters we'll talk about each of them in detail.
Programming languages that allow such things, such as JavaScript, are called "dynamically typed", meaning that there exist data types, but variables are not bound to any of them.
## Number
@ -64,21 +66,23 @@ We'll see more about working with numbers in the chapter <info:number>.
## BigInt
In JavaScript, the "number" type cannot represent integer values larger than <code>2<sup>53</sup></code> (or less than <code>-2<sup>53</sup></code> for negatives), that's a technical limitation caused by their internal representation. That's about 16 decimal digits, so for most purposes the limitation isn't a problem, but sometimes we need really big numbers, e.g. for cryptography or microsecond-precision timestamps.
In JavaScript, the "number" type cannot represent integer values larger than <code>(2<sup>53</sup>-1)</code> (that's `9007199254740991`), or less than <code>-(2<sup>53</sup>-1)</code> for negatives. It's a technical limitation caused by their internal representation.
For most purposes that's quite enough, but sometimes we need really big numbers, e.g. for cryptography or microsecond-precision timestamps.
`BigInt` type was recently added to the language to represent integers of arbitrary length.
A `BigInt` is created by appending `n` to the end of an integer literal:
A `BigInt` value is created by appending `n` to the end of an integer:
```js
// the "n" at the end means it's a BigInt
const bigInt = 1234567890123456789012345678901234567890n;
```
As `BigInt` numbers are rarely needed, we devoted them a separate chapter <info:bigint>.
As `BigInt` numbers are rarely needed, we don't cover them here, but devoted them a separate chapter <info:bigint>. Read it when you need such big numbers.
```smart header="Compatability issues"
Right now `BigInt` is supported in Firefox and Chrome, but not in Safari/IE/Edge.
```smart header="Compatibility issues"
Right now `BigInt` is supported in Firefox/Chrome/Edge, but not in Safari/IE.
```
## String
@ -123,7 +127,7 @@ We'll cover strings more thoroughly in the chapter <info:string>.
```smart header="There is no *character* type."
In some languages, there is a special "character" type for a single character. For example, in the C language and in Java it is called "char".
In JavaScript, there is no such type. There's only one type: `string`. A string may consist of only one character or many of them.
In JavaScript, there is no such type. There's only one type: `string`. A string may consist of zero characters (be empty), one character or many of them.
```
## Boolean (logical type)
@ -163,7 +167,7 @@ In JavaScript, `null` is not a "reference to a non-existing object" or a "null p
It's just a special value which represents "nothing", "empty" or "value unknown".
The code above states that `age` is unknown or empty for some reason.
The code above states that `age` is unknown.
## The "undefined" value
@ -174,30 +178,33 @@ The meaning of `undefined` is "value is not assigned".
If a variable is declared, but not assigned, then its value is `undefined`:
```js run
let x;
let age;
alert(x); // shows "undefined"
alert(age); // shows "undefined"
```
Technically, it is possible to assign `undefined` to any variable:
Technically, it is possible to explicitly assign `undefined` to a variable:
```js run
let x = 123;
let age = 100;
x = undefined;
// change the value to undefined
age = undefined;
alert(x); // "undefined"
alert(age); // "undefined"
```
...But we don't recommend doing that. Normally, we use `null` to assign an "empty" or "unknown" value to a variable, and we use `undefined` for checks like seeing if a variable has been assigned.
...But we don't recommend doing that. Normally, one uses `null` to assign an "empty" or "unknown" value to a variable, while `undefined` is reserved as a default initial value for unassigned things.
## Objects and Symbols
The `object` type is special.
All other types are called "primitive" because their values can contain only a single thing (be it a string or a number or whatever). In contrast, objects are used to store collections of data and more complex entities. We'll deal with them later in the chapter <info:object> after we learn more about primitives.
All other types are called "primitive" because their values can contain only a single thing (be it a string or a number or whatever). In contrast, objects are used to store collections of data and more complex entities.
The `symbol` type is used to create unique identifiers for objects. We mention it here for completeness, but we'll study it after objects.
Being that important, objects deserve a special treatment. We'll deal with them later in the chapter <info:object>, after we learn more about primitives.
The `symbol` type is used to create unique identifiers for objects. We have to mention it here for the sake of completeness, but also postpone the details till we know objects.
## The typeof operator [#type-typeof]
@ -241,16 +248,16 @@ typeof alert // "function" (3)
The last three lines may need additional explanation:
1. `Math` is a built-in object that provides mathematical operations. We will learn it in the chapter <info:number>. Here, it serves just as an example of an object.
2. The result of `typeof null` is `"object"`. That's wrong. It is an officially recognized error in `typeof`, kept for compatibility. Of course, `null` is not an object. It is a special value with a separate type of its own. So, again, this is an error in the language.
3. The result of `typeof alert` is `"function"`, because `alert` is a function. We'll study functions in the next chapters where we'll also see that there's no special "function" type in JavaScript. Functions belong to the object type. But `typeof` treats them differently, returning `"function"`. That's not quite correct, but very convenient in practice.
2. The result of `typeof null` is `"object"`. That's an officially recognized error in `typeof` behavior, coming from the early days of JavaScript and kept for compatibility. Definitely, `null` is not an object. It is a special value with a separate type of its own.
3. The result of `typeof alert` is `"function"`, because `alert` is a function. We'll study functions in the next chapters where we'll also see that there's no special "function" type in JavaScript. Functions belong to the object type. But `typeof` treats them differently, returning `"function"`. That also comes from the early days of JavaScript. Technically, such behavior isn't correct, but can be convenient in practice.
## Summary
There are 8 basic data types in JavaScript.
- `number` for numbers of any kind: integer or floating-point, integers are limited by ±2<sup>53</sup>.
- `number` for numbers of any kind: integer or floating-point, integers are limited by <code>±(2<sup>53</sup>-1)</code>.
- `bigint` is for integer numbers of arbitrary length.
- `string` for strings. A string may have one or more characters, there's no separate single-character type.
- `string` for strings. A string may have zero or more characters, there's no separate single-character type.
- `boolean` for `true`/`false`.
- `null` for unknown values -- a standalone type that has a single value `null`.
- `undefined` for unassigned values -- a standalone type that has a single value `undefined`.

View file

@ -1,18 +1,10 @@
# Interaction: alert, prompt, confirm
In this part of the tutorial we cover JavaScript language "as is", without environment-specific tweaks.
But we'll still be using the browser as our demo environment, so we should know at least a few of its user-interface functions. In this chapter, we'll get familiar with the browser functions `alert`, `prompt` and `confirm`.
As we'll be using the browser as our demo environment, let's see a couple of functions to interact with the user: `alert`, `prompt` and `confirm`.
## alert
Syntax:
```js
alert(message);
```
This shows a message and pauses script execution until the user presses "OK".
This one we've seen already. It shows a message and waits for the user to press "OK".
For example:
@ -20,7 +12,7 @@ For example:
alert("Hello");
```
The mini-window with the message is called a *modal window*. The word "modal" means that the visitor can't interact with the rest of the page, press other buttons, etc. until they have dealt with the window. In this case -- until they press "OK".
The mini-window with the message is called a *modal window*. The word "modal" means that the visitor can't interact with the rest of the page, press other buttons, etc, until they have dealt with the window. In this case -- until they press "OK".
## prompt
@ -38,7 +30,11 @@ It shows a modal window with a text message, an input field for the visitor, and
`default`
: An optional second parameter, the initial value for the input field.
The visitor may type something in the prompt input field and press OK. Or they can cancel the input by pressing Cancel or hitting the `key:Esc` key.
```smart header="The square brackets in syntax `[...]`"
The square brackets around `default` in the syntax above denote that the parameter is optional, not required.
```
The visitor can type something in the prompt input field and press OK. Then we get that text in the `result`. Or they can cancel the input by pressing Cancel or hitting the `key:Esc` key, then we get `null` as the `result`.
The call to `prompt` returns the text from the input field or `null` if the input was canceled.

View file

@ -7,7 +7,9 @@ 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 the expected type.
```smart header="Not talking about objects yet"
In this chapter, we won't cover objects. Instead, we'll study primitives first. Later, after we learn about objects, we'll see how object conversion works in the chapter <info:object-toprimitive>.
In this chapter, we won't cover objects. For now we'll just be talking about primitives.
Later, after we learn about objects, in the chapter <info:object-toprimitive> we'll see how objects fit in.
```
## String Conversion

View file

@ -10,8 +10,8 @@ true + false = 1
"4" - 2 = 2
"4px" - 2 = NaN
7 / 0 = Infinity
" -9 " + 5 = " -9 5" // (3)
" -9 " - 5 = -14 // (4)
" -9 " + 5 = " -9 5" // (3)
" -9 " - 5 = -14 // (4)
null + 1 = 1 // (5)
undefined + 1 = NaN // (6)
" \t \n" - 2 = -2 // (7)

View file

@ -0,0 +1,32 @@
The reason is that prompt returns user input as a string.
So variables have values `"1"` and `"2"` respectively.
```js run
let a = "1"; // prompt("First number?", 1);
let b = "2"; // prompt("Second number?", 2);
alert(a + b); // 12
```
What we should do is to convert strings to numbers before `+`. For example, using `Number()` or prepending them with `+`.
For example, right before `prompt`:
```js run
let a = +prompt("First number?", 1);
let b = +prompt("Second number?", 2);
alert(a + b); // 3
```
Or in the `alert`:
```js run
let a = prompt("First number?", 1);
let b = prompt("Second number?", 2);
alert(+a + +b); // 3
```
Using both unary and binary `+` in the latest code. Looks funny, doesn't it?

View file

@ -0,0 +1,18 @@
importance: 5
---
# Fix the addition
Here's a code that asks the user for two numbers and shows their sum.
It works incorrectly. The output in the example below is `12` (for default prompt values).
Why? Fix it. The result should be `3`.
```js run
let a = prompt("First number?", 1);
let b = prompt("Second number?", 2);
alert(a + b); // 12
```

View file

@ -1,8 +1,8 @@
# Operators
# Basic operators, maths
We know many operators from school. They are things like addition `+`, multiplication `*`, subtraction `-`, and so on.
In this chapter, we'll concentrate on aspects of operators that are not covered by school arithmetic.
In this chapter, well start with simple operators, then concentrate on JavaScript-specific aspects, not covered by school arithmetic.
## Terms: "unary", "binary", "operand"
@ -28,9 +28,55 @@ Before we move on, let's grasp some common terminology.
Formally, in the examples above we have two different operators that share the same symbol: the negation operator, a unary operator that reverses the sign, and the subtraction operator, a binary operator that subtracts one number from another.
## String concatenation, binary +
## Maths
Now, let's see special features of JavaScript operators that are beyond school arithmetics.
The following math operations are supported:
- Addition `+`,
- Subtraction `-`,
- Multiplication `*`,
- Division `/`,
- Remainder `%`,
- Exponentiation `**`.
The first four are straightforward, while `%` and `**` need a few words about them.
### Remainder %
The remainder operator `%`, despite its appearance, is not related to percents.
The result of `a % b` is the [remainder](https://en.wikipedia.org/wiki/Remainder) of the integer division of `a` by `b`.
For instance:
```js run
alert( 5 % 2 ); // 1, a remainder of 5 divided by 2
alert( 8 % 3 ); // 2, a remainder of 8 divided by 3
```
### Exponentiation **
The exponentiation operator `a ** b` multiplies `a` by itself `b` times.
For instance:
```js run
alert( 2 ** 2 ); // 4 (2 multiplied by itself 2 times)
alert( 2 ** 3 ); // 8 (2 * 2 * 2, 3 times)
alert( 2 ** 4 ); // 16 (2 * 2 * 2 * 2, 4 times)
```
Mathematically, the exponentiation is defined for non-integer numbers as well. For example, a square root is an exponentiation by `1/2`:
```js run
alert( 4 ** (1/2) ); // 2 (power of 1/2 is the same as a square root)
alert( 8 ** (1/3) ); // 2 (power of 1/3 is the same as a cubic root)
```
## String concatenation with binary +
Let's meet features of JavaScript operators that are beyond school arithmetics.
Usually, the plus operator `+` sums numbers.
@ -41,7 +87,7 @@ let s = "my" + "string";
alert(s); // mystring
```
Note that if one of the operands is a string, 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:
@ -50,22 +96,23 @@ alert( '1' + 2 ); // "12"
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, the other one is converted into a string as well.
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:
See, it doesn't matter whether the first operand is a string or the second one.
Here's a more complex example:
```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 and always convert their operands to numbers.
Here, operators work one after another. The first `+` sums two numbers, so it returns `4`, then the next `+` adds the string `1` to it, so it's like `4 + '1' = 41`.
For instance, subtraction and division:
The binary `+` is the only operator that supports strings in such a way. Other arithmetic operators work only with numbers and always convert their operands to numbers.
Here's the demo for subtraction and division:
```js run
alert( 2 - '1' ); // 1
alert( '6' / '2' ); // 3
alert( 6 - '2' ); // 4, converts '2' to a number
alert( '6' / '2' ); // 3, converts both operands to numbers
```
## Numeric conversion, unary +
@ -133,22 +180,23 @@ Parentheses override any precedence, so if we're not satisfied with the default
There are many operators in JavaScript. Every operator has a corresponding precedence number. The one with the larger number executes first. If the precedence is the same, the execution order is from left to right.
Here's an extract from the [precedence table](https://developer.mozilla.org/en/JavaScript/Reference/operators/operator_precedence) (you don't need to remember this, but note that unary operators are higher than corresponding binary ones):
Here's an extract from the [precedence table](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence) (you don't need to remember this, but note that unary operators are higher than corresponding binary ones):
| Precedence | Name | Sign |
|------------|------|------|
| ... | ... | ... |
| 16 | unary plus | `+` |
| 16 | unary negation | `-` |
| 14 | multiplication | `*` |
| 14 | division | `/` |
| 17 | unary plus | `+` |
| 17 | unary negation | `-` |
| 16 | exponentiation | `**` |
| 15 | multiplication | `*` |
| 15 | division | `/` |
| 13 | addition | `+` |
| 13 | subtraction | `-` |
| ... | ... | ... |
| 3 | assignment | `=` |
| ... | ... | ... |
As we can see, the "unary plus" has a priority of `16` which is higher than the `13` of "addition" (binary plus). That's why, in the expression `"+apples + +oranges"`, unary pluses work before the addition.
As we can see, the "unary plus" has a priority of `17` which is higher than the `13` of "addition" (binary plus). That's why, in the expression `"+apples + +oranges"`, unary pluses work before the addition.
## Assignment
@ -162,24 +210,11 @@ let x = 2 * 2 + 1;
alert( x ); // 5
```
It is possible to chain assignments:
### Assignment = returns a value
```js run
let a, b, c;
The fact of `=` being an operator, not a "magical" language construct has an interesting implication.
*!*
a = b = c = 2 + 2;
*/!*
alert( a ); // 4
alert( b ); // 4
alert( c ); // 4
```
Chained assignments evaluate from right to left. First, the rightmost expression `2 + 2` is evaluated and then assigned to the variables on the left: `c`, `b` and `a`. At the end, all the 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 addition `+` or multiplication `*`. But the assignment operator follows this rule too.
Most operators in JavaScript return a value. That's obvious for `+` and `-`, but also true for `=`.
The call `x = value` writes the `value` into `x` *and then returns it*.
@ -199,49 +234,74 @@ alert( c ); // 0
In the example above, the result of expression `(a = b + 1)` is the value which was assigned to `a` (that is `3`). It is then used for further evaluations.
Funny code, isn't it? We should understand how it works, because sometimes we see it in JavaScript libraries, but shouldn't write anything like that ourselves. Such tricks definitely don't make code clearer or readable.
````
Funny code, isn't it? We should understand how it works, because sometimes we see it in JavaScript libraries.
## Remainder %
Although, please don't write the code like that. Such tricks definitely don't make code clearer or readable.
The remainder operator `%`, despite its appearance, is not related to percents.
### Chaining assignments
The result of `a % b` is the remainder of the integer division of `a` by `b`.
For instance:
Another interesting feature is the ability to chain assignments:
```js run
alert( 5 % 2 ); // 1 is a remainder of 5 divided by 2
alert( 8 % 3 ); // 2 is a remainder of 8 divided by 3
alert( 6 % 3 ); // 0 is a remainder of 6 divided by 3
let a, b, c;
*!*
a = b = c = 2 + 2;
*/!*
alert( a ); // 4
alert( b ); // 4
alert( c ); // 4
```
## Exponentiation **
Chained assignments evaluate from right to left. First, the rightmost expression `2 + 2` is evaluated and then assigned to the variables on the left: `c`, `b` and `a`. At the end, all the variables share a single value.
The exponentiation operator `**` is a recent addition to the language.
Once again, for the purposes of readability it's better to split such code into few lines:
For a natural number `b`, the result of `a ** b` is `a` multiplied by itself `b` times.
```js
c = 2 + 2;
b = c;
a = c;
```
That's easier to read, especially when eye-scanning the code fast.
For instance:
## Modify-in-place
```js run
alert( 2 ** 2 ); // 4 (2 * 2)
alert( 2 ** 3 ); // 8 (2 * 2 * 2)
alert( 2 ** 4 ); // 16 (2 * 2 * 2 * 2)
We often need to apply an operator to a variable and store the new result in that same variable.
For example:
```js
let n = 2;
n = n + 5;
n = n * 2;
```
The operator works for non-integer numbers as well.
For instance:
This notation can be shortened using the operators `+=` and `*=`:
```js run
alert( 4 ** (1/2) ); // 2 (power of 1/2 is the same as a square root, that's maths)
alert( 8 ** (1/3) ); // 2 (power of 1/3 is the same as a cubic root)
let 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
```
Short "modify-and-assign" operators exist for all arithmetical and bitwise operators: `/=`, `-=`, etc.
Such operators have the same precedence as a normal assignment, so they run after most other calculations:
```js run
let n = 2;
n *= 3 + 5;
alert( n ); // 16 (right part evaluated first, same as n *= 8)
```
## Increment/decrement
<!-- Can't use -- in title, because built-in parse turns it into -->
<!-- Can't use -- in title, because the built-in parser turns it into a 'long dash' -->
Increasing or decreasing a number by one is among the most common numerical operations.
@ -368,41 +428,7 @@ The list of operators:
- RIGHT SHIFT ( `>>` )
- ZERO-FILL RIGHT SHIFT ( `>>>` )
These operators are used very rarely. To understand them, we need to delve into low-level number representation and it would not be optimal to do that right now, especially since we won't need them any time soon. If you're curious, you can read the [Bitwise Operators](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators) article on MDN. It would be more practical to do that when a real need arises.
## Modify-in-place
We often need to apply an operator to a variable and store the new result in that same variable.
For example:
```js
let n = 2;
n = n + 5;
n = n * 2;
```
This notation can be shortened using the 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)
alert( n ); // 14
```
Short "modify-and-assign" operators exist for all arithmetical and bitwise operators: `/=`, `-=`, etc.
Such operators have the same precedence as a normal assignment, so they run after most other calculations:
```js run
let n = 2;
n *= 3 + 5;
alert( n ); // 16 (right part evaluated first, same as n *= 8)
```
These operators are used very rarely, when we need to fiddle with numbers on the very lowest (bitwise) level. We won't need these operators any time soon, as web development has little use of them, but in some special areas, such as cryptography, they are useful. You can read the [Bitwise Operators](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Bitwise) chapter on MDN when a need arises.
## Comma

View file

@ -1,15 +1,21 @@
# Comparisons
We know many comparison operators from maths:
We know many comparison operators from maths.
In JavaScript they are written like this:
- 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>.
- Equals: `a == b` (please note the double equals sign `=`. A single symbol `a = b` would mean an assignment).
- Not equals. In maths the notation is <code>&ne;</code>, but in JavaScript it's written as an assignment with an exclamation sign before it: <code>a != b</code>.
- Equals: `a == b`, please note the double equality sign `==` means the equality test, while a single one `a = b` means an assignment.
- Not equals. In maths the notation is <code>&ne;</code>, but in JavaScript it's written as <code>a != b</code>.
In this article we'll learn more about different types of comparisons, how JavaScript makes them, including important peculiarities.
At the end you'll find a good recipe to avoid "javascript quirks"-related issues.
## Boolean is the result
Like all other operators, a comparison returns a value. In this case, the value is a boolean.
All comparison operators return a boolean value:
- `true` -- means "yes", "correct" or "the truth".
- `false` -- means "no", "wrong" or "not the truth".
@ -192,13 +198,12 @@ We get these results because:
- Comparisons `(1)` and `(2)` return `false` because `undefined` gets converted to `NaN` and `NaN` is a special numeric value which returns `false` for all comparisons.
- The equality check `(3)` returns `false` because `undefined` only equals `null`, `undefined`, and no other value.
### Evade problems
### Avoid problems
Why did we go over these examples? Should we remember these peculiarities all the time? Well, not really. Actually, these tricky things will gradually become familiar over time, but there's a solid way to evade problems with them:
Why did we go over these examples? Should we remember these peculiarities all the time? Well, not really. Actually, these tricky things will gradually become familiar over time, but there's a solid way to avoid problems with them:
Just treat any comparison with `undefined/null` except the strict equality `===` with exceptional care.
Don't use comparisons `>= > < <=` with a variable which may be `null/undefined`, unless you're really sure of what you're doing. If a variable can have these values, check for them separately.
- Treat any comparison with `undefined/null` except the strict equality `===` with exceptional care.
- Don't use comparisons `>= > < <=` with a variable which may be `null/undefined`, unless you're really sure of what you're doing. If a variable can have these values, check for them separately.
## Summary

View file

@ -1,4 +1,4 @@
# Conditional operators: if, '?'
# Conditional branching: if, '?'
Sometimes, we need to perform different actions based on different conditions.

View file

@ -6,7 +6,7 @@ alert( alert(1) || 2 || alert(3) );
The call to `alert` does not return a value. Or, in other words, it returns `undefined`.
1. The first OR `||` evaluates it's left operand `alert(1)`. That shows the first message with `1`.
1. The first OR `||` evaluates its left operand `alert(1)`. That shows the first message with `1`.
2. The `alert` returns `undefined`, so OR goes on to the second operand searching for a truthy value.
3. The second operand `2` is truthy, so the execution is halted, `2` is returned and then shown by the outer alert.

View file

@ -4,6 +4,6 @@ importance: 3
# Check the range between
Write an "if" condition to check that `age` is between `14` and `90` inclusively.
Write an `if` condition to check that `age` is between `14` and `90` inclusively.
"Inclusively" means that `age` can reach the edges `14` or `90`.

View file

@ -4,6 +4,6 @@ importance: 3
# Check the range outside
Write an `if` condition to check that `age` is NOT between 14 and 90 inclusively.
Write an `if` condition to check that `age` is NOT between `14` and `90` inclusively.
Create two variants: the first one using NOT `!`, the second one -- without it.

View file

@ -3,19 +3,19 @@
```js run demo
let userName = prompt("Who's there?", '');
if (userName == 'Admin') {
if (userName === 'Admin') {
let pass = prompt('Password?', '');
if (pass == 'TheMaster') {
if (pass === 'TheMaster') {
alert( 'Welcome!' );
} else if (pass == '' || pass == null) {
} else if (pass === '' || pass === null) {
alert( 'Canceled' );
} else {
alert( 'Wrong password' );
}
} else if (userName == '' || userName == null) {
} else if (userName === '' || userName === null) {
alert( 'Canceled' );
} else {
alert( "I don't know you" );

View file

@ -84,16 +84,16 @@ The OR `||` operator does the following:
A value is returned in its original form, without the conversion.
In other words, a chain of OR `"||"` returns the first truthy value or the last one if no truthy value is found.
In other words, a chain of OR `||` returns the first truthy value or the last one if no truthy value is found.
For instance:
```js run
alert( 1 || 0 ); // 1 (1 is truthy)
alert( true || 'no matter what' ); // (true is truthy)
alert( null || 1 ); // 1 (1 is the first truthy value)
alert( null || 0 || 1 ); // 1 (the first truthy value)
alert( undefined || null || 0 ); // 0 (all falsy, returns the last value)
```
@ -101,53 +101,40 @@ This leads to some interesting usage compared to a "pure, classical, boolean-onl
1. **Getting the first truthy value from a list of variables or expressions.**
Imagine we have a list of variables which can either contain data or be `null/undefined`. How can we find the first one with data?
For instance, we have `firstName`, `lastName` and `nickName` variables, all optional (i.e. can be undefined or have falsy values).
We can use OR `||`:
Let's use OR `||` to choose the one that has the data and show it (or `"Anonymous"` if nothing set):
```js run
let currentUser = null;
let defaultUser = "John";
let firstName = "";
let lastName = "";
let nickName = "SuperCoder";
*!*
let name = currentUser || defaultUser || "unnamed";
alert( firstName || lastName || nickName || "Anonymous"); // SuperCoder
*/!*
alert( name ); // selects "John" the first truthy value
```
If both `currentUser` and `defaultUser` were falsy, `"unnamed"` would be the result.
If all variables were falsy, `"Anonymous"` would show up.
2. **Short-circuit evaluation.**
Operands can be not only values, but arbitrary expressions. OR evaluates and tests them from left to right. The evaluation stops when a truthy value is reached, and the value is returned. This process is called "a short-circuit evaluation" because it goes as short as possible from left to right.
Another feature of OR `||` operator is the so-called "short-circuit" evaluation.
This is clearly seen when the expression given as the second argument has a side effect like a variable assignment.
It means that `||` processes its arguments until the first truthy value is reached, and then the value is returned immediately, without even touching the other argument.
In the example below, `x` does not get assigned:
That importance of this feature becomes obvious if an operand isn't just a value, but an expression with a side effect, such as a variable assignment or a function call.
In the example below, only the second message is printed:
```js run no-beautify
let x;
*!*true*/!* || (x = 1);
alert(x); // undefined, because (x = 1) not evaluated
*!*true*/!* || alert("not printed");
*!*false*/!* || alert("printed");
```
If, instead, the first argument is `false`, `||` evaluates the second one, thus running the assignment:
In the first line, the OR `||` operator stops the evaluation immediately upon seeing `true`, so the `alert` isn't run.
```js run no-beautify
let x;
*!*false*/!* || (x = 1);
alert(x); // 1
```
An assignment is a simple case. There may be side effects, that won't show up if the evaluation doesn't reach them.
As we can see, such a use case is a "shorter way of doing `if`". The first operand is converted to boolean. If it's false, the second one is evaluated.
Most of time, it's better to use a "regular" `if` to keep the code easy to understand, but sometimes this can be handy.
Sometimes, people use this feature to execute commands only if the condition on the left part is falsy.
## && (AND)
@ -236,7 +223,8 @@ The precedence of AND `&&` operator is higher than OR `||`.
So the code `a && b || c && d` is essentially the same as if the `&&` expressions were in parentheses: `(a && b) || (c && d)`.
````
Just like OR, the AND `&&` operator can sometimes replace `if`.
````warn header="Don't replace `if` with `||` or `&&`"
Sometimes, people use the AND `&&` operator as a "shorter way to write `if`".
For instance:
@ -253,14 +241,12 @@ So we basically have an analogue for:
```js run
let x = 1;
if (x > 0) {
alert( 'Greater than zero!' );
}
if (x > 0) alert( 'Greater than zero!' );
```
The variant with `&&` appears shorter. But `if` is more obvious and tends to be a little bit more readable.
Although, the variant with `&&` appears shorter, `if` is more obvious and tends to be a little bit more readable. So we recommend using every construct for its purpose: use `if` if we want `if` and use `&&` if we want AND.
````
So we recommend using every construct for its purpose: use `if` if we want if and use `&&` if we want AND.
## ! (NOT)

View file

@ -0,0 +1,168 @@
# Nullish coalescing operator '??'
[recent browser="new"]
Here, in this article, we'll say that an expression is "defined" when it's neither `null` nor `undefined`.
The nullish coalescing operator is written as two question marks `??`.
The result of `a ?? b` is:
- if `a` is defined, then `a`,
- if `a` isn't defined, then `b`.
In other words, `??` returns the first argument if it's defined. Otherwise, the second one.
The nullish coalescing operator isn't anything completely new. It's just a nice syntax to get the first "defined" value of the two.
We can rewrite `result = a ?? b` using the operators that we already know, like this:
```js
result = (a !== null && a !== undefined) ? a : b;
```
The common use case for `??` is to provide a default value for a potentially undefined variable.
For example, here we show `Anonymous` if `user` isn't defined:
```js run
let user;
alert(user ?? "Anonymous"); // Anonymous
```
Of course, if `user` had any value except `null/undefined`, then we would see it instead:
```js run
let user = "John";
alert(user ?? "Anonymous"); // John
```
We can also use a sequence of `??` to select the first defined value from a list.
Let's say we a user's data in variables `firstName`, `lastName` or `nickName`. All of them may be undefined, if the user decided not to enter a value.
We'd like to display the user name using one of these variables, or show "Anonymous" if all of them are undefined.
Let's use the `??` operator for that:
```js run
let firstName = null;
let lastName = null;
let nickName = "Supercoder";
// shows the first defined value:
*!*
alert(firstName ?? lastName ?? nickName ?? "Anonymous"); // Supercoder
*/!*
```
## Comparison with ||
The OR `||` operator can be used in the same way as `??`, as it was described in the [previous chapter](info:logical-operators#or-finds-the-first-truthy-value).
For example, in the code above we could replace `??` with `||` and still get the same result:
```js run
let firstName = null;
let lastName = null;
let nickName = "Supercoder";
// shows the first truthy value:
*!*
alert(firstName || lastName || nickName || "Anonymous"); // Supercoder
*/!*
```
The OR `||` operator exists since the beginning of JavaScript, so developers were using it for such purposes for a long time.
On the other hand, the nullish coalescing operator `??` was added only recently, and the reason for that was that people weren't quite happy with `||`.
The subtle, yet important difference is that:
- `||` returns the first *truthy* value.
- `??` returns the first *defined* value.
In other words, `||` doesn't distinguish between `false`, `0`, an empty string `""` and `null/undefined`. They are all the same -- falsy values. If any of these is the first argument of `||`, then we'll get the second argument as the result.
In practice though, we may want to use default value only when the variable is `null/undefined`. That is, when the value is really unknown/not set.
For example, consider this:
```js run
let height = 0;
alert(height || 100); // 100
alert(height ?? 100); // 0
```
Here, we have a zero height.
- The `height || 100` checks `height` for being a falsy value, and it really is.
- so the result is the second argument, `100`.
- The `height ?? 100` checks `height` for being `null/undefined`, and it's not,
- so the result is `height` "as is", that is `0`.
If we assume that zero height is a valid value, that shouldn't be replaced with the default, then `??` does just the right thing.
## Precedence
The precedence of the `??` operator is rather low: `5` in the [MDN table](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence#Table). So `??` is evaluated before `=` and `?`, but after most other operations, such as `+`, `*`.
So if we'd like to choose a value with `??` an expression with other operators, consider adding parentheses:
```js run
let height = null;
let width = null;
// important: use parentheses
let area = (height ?? 100) * (width ?? 50);
alert(area); // 5000
```
Otherwise, if we omit parentheses, then as `*` has the higher precedence than `??`, it would execute first, leading to incorrect results.
```js
// without parentheses
let area = height ?? 100 * width ?? 50;
// ...works the same as this (probably not what we want):
let area = height ?? (100 * width) ?? 50;
```
### Using ?? with && or ||
Due to safety reasons, JavaScript forbids using `??` together with `&&` and `||` operators, unless the precedence is explicitly specified with parentheses.
The code below triggers a syntax error:
```js run
let x = 1 && 2 ?? 3; // Syntax error
```
The limitation is surely debatable, but it was added to the language specification with the purpose to avoid programming mistakes, when people start to switch to `??` from `||`.
Use explicit parentheses to work around it:
```js run
*!*
let x = (1 && 2) ?? 3; // Works
*/!*
alert(x); // 2
```
## Summary
- The nullish coalescing operator `??` provides a short way to choose a "defined" value from the list.
It's used to assign default values to variables:
```js
// set height=100, if height is null or undefined
height = height ?? 100;
```
- The operator `??` has a very low precedence, a bit higher than `?` and `=`, so consider adding parentheses when using it in an expression.
- It's forbidden to use it with `||` or `&&` without explicit parentheses.

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 string. Then the `while` loop stops too.
2. The check `&& num` is false when `num` is `null` or an 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

@ -256,7 +256,7 @@ For even values of `i`, the `continue` directive stops executing the body and pa
````smart header="The `continue` directive helps decrease nesting"
A loop that shows odd values could look like this:
```js
```js run
for (let i = 0; i < 10; i++) {
if (i % 2) {
@ -268,7 +268,7 @@ for (let i = 0; i < 10; i++) {
From a technical point of view, this is identical to the example above. Surely, we can just wrap the code in an `if` block instead of using `continue`.
But as a side-effect, this created one more level of nesting (the `alert` call inside the curly braces). If the code inside of`if` is longer than a few lines, that may decrease the overall readability.
But as a side-effect, this created one more level of nesting (the `alert` call inside the curly braces). If the code inside of `if` is longer than a few lines, that may decrease the overall readability.
````
````warn header="No `break/continue` to the right side of '?'"

View file

@ -117,7 +117,7 @@ Several variants of `case` which share the same code can be grouped.
For example, if we want the same code to run for `case 3` and `case 5`:
```js run no-beautify
let a = 2 + 2;
let a = 3;
switch (a) {
case 4:

View file

@ -185,7 +185,7 @@ For instance, the aforementioned function `showMessage(from, text)` can be calle
showMessage("Ann");
```
That's not an error. Such a call would output `"Ann: undefined"`. There's no `text`, so it's assumed that `text === undefined`.
That's not an error. Such a call would output `"*Ann*: undefined"`. There's no `text`, so it's assumed that `text === undefined`.
If we want to use a "default" `text` in this case, then we can specify it after `=`:
@ -214,36 +214,48 @@ In JavaScript, a default parameter is evaluated every time the function is calle
In the example above, `anotherFunction()` is called every time `showMessage()` is called without the `text` parameter.
```
````smart header="Default parameters old-style"
Old editions of JavaScript did not support default parameters. So there are alternative ways to support them, that you can find mostly in the old scripts.
### Alternative default parameters
For instance, an explicit check for being `undefined`:
Sometimes it makes sense to set default values for parameters not in the function declaration, but at a later stage, during its execution.
```js
function showMessage(from, text) {
To check for an omitted parameter, we can compare it with `undefined`:
```js run
function showMessage(text) {
*!*
if (text === undefined) {
text = 'no text given';
text = 'empty message';
}
*/!*
alert( from + ": " + text );
alert(text);
}
showMessage(); // empty message
```
...Or the `||` operator:
...Or we could use the `||` operator:
```js
function showMessage(from, text) {
// if text is falsy then text gets the "default" value
text = text || 'no text given';
// if text parameter is omitted or "" is passed, set it to 'empty'
function showMessage(text) {
text = text || 'empty';
...
}
```
Modern JavaScript engines support the [nullish coalescing operator](info:nullish-coalescing-operator) `??`, it's better when falsy values, such as `0`, are considered regular:
````
```js run
// if there's no "count" parameter, show "unknown"
function showCount(count) {
alert(count ?? "unknown");
}
showCount(0); // 0
showCount(null); // unknown
showCount(); // unknown
```
## Returning a value
@ -266,7 +278,7 @@ There may be many occurrences of `return` in a single function. For instance:
```js run
function checkAge(age) {
if (age > 18) {
if (age >= 18) {
*!*
return true;
*/!*

View file

@ -67,7 +67,7 @@ let welcome = (age < 18) ?
() => alert('Hello') :
() => alert("Greetings!");
welcome(); // ok now
welcome();
```
Arrow functions may appear unfamiliar and not very readable at first, but that quickly changes as the eyes get used to the structure.

View file

@ -81,9 +81,10 @@ let x = 5;
x = "John";
```
There are 7 data types:
There are 8 data types:
- `number` for both floating-point and integer numbers,
- `bigint` for integer numbers of arbitrary length,
- `string` for strings,
- `boolean` for logical values: `true/false`,
- `null` -- a type with a single value `null`, meaning "empty" or "does not exist",
@ -151,6 +152,9 @@ Conditional
Logical operators
: Logical AND `&&` and OR `||` perform short-circuit evaluation and then return the value where it stopped (not necessary `true`/`false`). Logical NOT `!` converts the operand to boolean type and returns the inverse value.
Nullish coalescing operator
: The `??` operator provides a way to choose a defined value from a list of variables. The result of `a ?? b` is `a` unless it's `null/undefined`, then `b`.
Comparisons
: Equality check `==` for values of different types converts them to a number (except `null` and `undefined` that equal each other and nothing else), so these are equal:
@ -170,7 +174,7 @@ Comparisons
Other operators
: There are few others, like a comma operator.
More in: <info:operators>, <info:comparison>, <info:logical-operators>.
More in: <info:operators>, <info:comparison>, <info:logical-operators>, <info:nullish-coalescing-operator>.
## Loops
@ -212,6 +216,7 @@ let age = prompt('Your age?', 18);
switch (age) {
case 18:
alert("Won't work"); // the result of prompt is a string, not a number
break;
case "18":
alert("This works!");

View file

@ -4,7 +4,7 @@ Before writing more complex code, let's talk about debugging.
[Debugging](https://en.wikipedia.org/wiki/Debugging) is the process of finding and fixing errors within a script. All modern browsers and most other environments support debugging tools -- a special UI in developer tools that makes debugging much easier. It also allows to trace the code step by step to see what exactly is going on.
We'll be using Chrome here, because it has enough features, most other browsers have a similar process`.
We'll be using Chrome here, because it has enough features, most other browsers have a similar process.
## The "Sources" panel
@ -24,11 +24,11 @@ Let's click it and select `hello.js` in the tree view. Here's what should show u
![](chrome-tabs.svg)
Here we can see three zones:
The Sources panel has 3 parts:
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.
1. The **File Navigator** pane lists HTML, JavaScript, CSS and other files, including images that are attached to the page. Chrome extensions may appear here too.
2. The **Code Editor** pane shows the source code.
3. The **JavaScript Debugging** pane is for debugging, we'll explore it soon.
Now you could click the same toggler <span class="devtools" style="background-position:-172px -122px"></span> again to hide the resources list and give the code some space.

View file

@ -12,7 +12,7 @@ function pow(x,n) // <- no space between arguments
let x=prompt("x?",''), n=prompt("n?",'') // <-- technically possible,
// but better make it 2 lines, also there's no spaces and missing ;
if (n<0) // <- no spaces inside (n < 0), and should be extra line above it
if (n<=0) // <- no spaces inside (n <= 0), and should be extra line above it
{ // <- figure bracket on a separate line
// below - long lines can be split into multiple lines for improved readability
alert(`Power ${n} is not supported, please enter an integer number greater than zero`);
@ -39,7 +39,7 @@ function pow(x, n) {
let x = prompt("x?", "");
let n = prompt("n?", "");
if (n < 0) {
if (n <= 0) {
alert(`Power ${n} is not supported,
please enter an integer number greater than zero`);
} else {

View file

@ -86,7 +86,7 @@ For example:
```js
// backtick quotes ` allow to split the string into multiple lines
let str = `
Ecma International's TC39 is a group of JavaScript developers,
ECMA International's TC39 is a group of JavaScript developers,
implementers, academics, and more, collaborating with the community
to maintain and evolve the definition of JavaScript.
`;
@ -285,7 +285,7 @@ Of course, a team can always write their own style guide, but usually there's no
Some popular choices:
- [Google JavaScript Style Guide](https://google.github.io/styleguide/javascriptguide.xml)
- [Google JavaScript Style Guide](https://google.github.io/styleguide/jsguide.html)
- [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript)
- [Idiomatic.JS](https://github.com/rwaldron/idiomatic.js)
- [StandardJS](https://standardjs.com/)

View file

@ -125,25 +125,25 @@ Describe the architecture
Document function parameters and usage
: There's a special syntax [JSDoc](http://en.wikipedia.org/wiki/JSDoc) to document a function: usage, parameters, returned value.
For instance:
```js
/**
* Returns x raised to the n-th power.
*
* @param {number} x The number to raise.
* @param {number} n The power, must be a natural number.
* @return {number} x raised to the n-th power.
*/
function pow(x, n) {
...
}
```
For instance:
```js
/**
* Returns x raised to the n-th power.
*
* @param {number} x The number to raise.
* @param {number} n The power, must be a natural number.
* @return {number} x raised to the n-th power.
*/
function pow(x, n) {
...
}
```
Such comments allow us to understand the purpose of the function and use it the right way without looking in its code.
Such comments allow us to understand the purpose of the function and use it the right way without looking in its code.
By the way, many editors like [WebStorm](https://www.jetbrains.com/webstorm/) can understand them as well and use them to provide autocomplete and some automatic code-checking.
By the way, many editors like [WebStorm](https://www.jetbrains.com/webstorm/) can understand them as well and use them to provide autocomplete and some automatic code-checking.
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/>.
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 may be even more important to understand what's going on. Why is the task solved exactly this way? The code gives no answer.

View file

@ -1,7 +1,7 @@
# Ninja code
```quote author="Confucius"
```quote author="Confucius (Analects)"
Learning without thought is labor lost; thought without learning is perilous.
```
@ -43,7 +43,7 @@ The Dao hides in wordlessness. Only the Dao is well begun and well
completed.
```
Another way to code faster is to use single-letter variable names everywhere. Like `a`, `b` or `c`.
Another way to code shorter 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, they won't be able to "decipher" what the name `a` or `b` means.
@ -104,8 +104,8 @@ A quick read of such code becomes impossible. And when there's a typo... Ummm...
## Smart synonyms
```quote author="Confucius"
The hardest thing of all is to find a black cat in a dark room, especially if there is no cat.
```quote author="Laozi (Tao Te Ching)"
The Tao that can be told is not the eternal Tao. The name that can be named is not the eternal name.
```
Using *similar* names for *same* things makes life more interesting and shows your creativity to the public.

View file

@ -159,8 +159,8 @@ We can select one of two ways to organize the test here:
assert.equal(pow(2, 3), 8);
});
it("3 raised to power 3 is 27", function() {
assert.equal(pow(3, 3), 27);
it("3 raised to power 4 is 81", function() {
assert.equal(pow(3, 4), 81);
});
});
@ -182,7 +182,7 @@ The result:
[iframe height=250 src="pow-2" edit border="1"]
As we could expect, the second test failed. Sure, our function always returns `8`, while the `assert` expects `27`.
As we could expect, the second test failed. Sure, our function always returns `8`, while the `assert` expects `81`.
## Improving the implementation

View file

@ -1,5 +1,11 @@
describe("test", function() {
// Mocha usually waits for the tests for 2 seconds before considering them wrong
this.timeout(200000); // With this code we increase this - in this case to 200,000 milliseconds
// This is because of the "alert" function, because if you delay pressing the "OK" button the tests will not pass!
before(() => alert("Testing started before all tests"));
after(() => alert("Testing finished after all tests"));

View file

@ -4,8 +4,8 @@ describe("pow", function() {
assert.equal(pow(2, 3), 8);
});
it("3 raised to power 3 is 27", function() {
assert.equal(pow(3, 3), 27);
it("3 raised to power 4 is 81", function() {
assert.equal(pow(3, 4), 81);
});
});

View file

@ -1,19 +0,0 @@
Sure, it works, no problem.
The `const` only protects the variable itself from changing.
In other words, `user` stores a reference to the object. And it can't be changed. But the content of the object can.
```js run
const user = {
name: "John"
};
*!*
// works
user.name = "Pete";
*/!*
// error
user = 123;
```

View file

@ -1,18 +0,0 @@
importance: 5
---
# Constant objects?
Is it possible to change an object declared with `const`? What do you think?
```js
const user = {
name: "John"
};
*!*
// does it work?
user.name = "Pete";
*/!*
```

View file

@ -1,7 +1,7 @@
# Objects
As we know from the chapter <info:types>, there are seven data types in JavaScript. Six of them are called "primitive", because their values contain only a single thing (be it a string or a number or whatever).
As we know from the chapter <info:types>, there are eight data types in JavaScript. Seven of them are called "primitive", because their values contain only a single thing (be it a string or a number or whatever).
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.
@ -92,6 +92,30 @@ let user = {
```
That is called a "trailing" or "hanging" comma. Makes it easier to add/remove/move around properties, because all lines become alike.
````smart header="Object with const can be changed"
Please note: an object declared as `const` *can* be modified.
For instance:
```js run
const user = {
name: "John"
};
*!*
user.name = "Pete"; // (*)
*/!*
alert(user.name); // Pete
```
It might seem that the line `(*)` would cause an error, but no. The `const` fixes the value of `user`, but not its contents.
The `const` would give an error only if we try to set `user=...` as a whole.
There's another way to make constant object properties, we'll cover it later in the chapter <info:property-descriptors>.
````
## Square brackets
For multiword properties, the dot access doesn't work:
@ -101,7 +125,9 @@ For multiword properties, the dot access doesn't work:
user.likes birds = true
```
That's because the dot requires the key to be a valid variable identifier. That is: no spaces and other limitations.
JavaScript doesn't understand that. It thinks that we address `user.likes`, and then gives a syntax error when comes across unexpected `birds`.
The dot requires the key to be a valid variable identifier. That implies: contains no spaces, doesn't start with a digit and doesn't include special characters (`$` and `_` are allowed).
There's an alternative "square bracket notation" that works with any string:
@ -159,7 +185,7 @@ alert( user.key ) // undefined
### Computed properties
We can use square brackets in an object literal. That's called *computed properties*.
We can use square brackets in an object literal, when creating an object. That's called *computed properties*.
For instance:
@ -203,43 +229,6 @@ Square brackets are much more powerful than the dot notation. They allow any pro
So most of the time, when property names are known and simple, the dot is used. And if we need something more complex, then we switch to square brackets.
````smart header="Reserved words are allowed as property names"
A variable cannot have a name equal to one of language-reserved words like "for", "let", "return" etc.
But for an object property, there's no such restriction. Any name is fine:
```js run
let obj = {
for: 1,
let: 2,
return: 3
};
alert( obj.for + obj.let + obj.return ); // 6
```
Basically, any name is allowed, but there's a special one: `"__proto__"` that gets special treatment for historical reasons. For instance, we can't set it to a non-object value:
```js run
let obj = {};
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.
That can become a source of bugs and even vulnerabilities if we intend 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), that we'll learn in the chapter <info:map-set>, which supports arbitrary keys.
````
## Property value shorthand
In real code we often use existing variables as values for property names.
@ -250,7 +239,7 @@ For instance:
function makeUser(name, age) {
return {
name: name,
age: age
age: age,
// ...other properties
};
}
@ -268,7 +257,7 @@ function makeUser(name, age) {
*!*
return {
name, // same as name: name
age // same as age: age
age, // same as age: age
// ...
};
*/!*
@ -284,9 +273,57 @@ let user = {
};
```
## Existence check
A notable objects feature is that it's possible to access any property. There will be no error if the property doesn't exist! Accessing a non-existing property just returns `undefined`. It provides a very common way to test whether the property exists -- to get it and compare vs undefined:
## Property names limitations
As we already know, a variable cannot have a name equal to one of language-reserved words like "for", "let", "return" etc.
But for an object property, there's no such restriction:
```js run
// these properties are all right
let obj = {
for: 1,
let: 2,
return: 3
};
alert( obj.for + obj.let + obj.return ); // 6
```
In short, there are no limitations on property names. They can be any strings or symbols (a special type for identifiers, to be covered later).
Other types are automatically converted to strings.
For instance, a number `0` becomes a string `"0"` when used as a property key:
```js run
let obj = {
0: "test" // same as "0": "test"
};
// both alerts access the same property (the number 0 is converted to string "0")
alert( obj["0"] ); // test
alert( obj[0] ); // test (same property)
```
There's a minor gotcha with a special property named `__proto__`. We can't set it to a non-object value:
```js run
let obj = {};
obj.__proto__ = 5; // assign a number
alert(obj.__proto__); // [object Object] - the value is an object, didn't work as intended
```
As we see from the code, the assignment to a primitive `5` is ignored.
We'll cover the special nature of `__proto__` in [subsequent chapters](info:prototype-inheritance), and suggest the [ways to fix](info:prototype-methods) such behavior.
## Property existence test, "in" operator
A notable feature of objects in JavaScript, compared to many other languages, is that it's possible to access any property. There will be no error if the property doesn't exist!
Reading a non-existing property just returns `undefined`. So we can easily test whether the property exists:
```js run
let user = {};
@ -294,7 +331,7 @@ let user = {};
alert( user.noSuchProperty === undefined ); // true means "no such property"
```
There also exists a special operator `"in"` to check for the existence of a property.
There's also a special operator `"in"` for that.
The syntax is:
```js
@ -312,17 +349,18 @@ alert( "blabla" in user ); // false, user.blabla doesn't exist
Please note that on the left side of `in` there must be a *property name*. That's usually a quoted string.
If we omit quotes, that would mean a variable containing the actual name will be tested. For instance:
If we omit quotes, that means a variable, it should contain the actual name to be tested. For instance:
```js run
let user = { age: 30 };
let key = "age";
alert( *!*key*/!* in user ); // true, takes the name from key and checks for such property
alert( *!*key*/!* in user ); // true, property "age" exists
```
````smart header="Using \"in\" for properties that store `undefined`"
Usually, the strict comparison `"=== undefined"` check the property existance just fine. But there's a special case when it fails, but `"in"` works correctly.
Why does the `in` operator exist? Isn't it enough to compare against `undefined`?
Well, most of the time the comparison with `undefined` works fine. But there's a special case when it fails, but `"in"` works correctly.
It's when an object property exists, but stores `undefined`:
@ -336,11 +374,10 @@ alert( obj.test ); // it's undefined, so - no such property?
alert( "test" in obj ); // true, the property does exist!
```
In the code above, the property `obj.test` technically exists. So the `in` operator works right.
Situations like this happen very rarely, because `undefined` is usually not assigned. We mostly use `null` for "unknown" or "empty" values. So the `in` operator is an exotic guest in the code.
````
Situations like this happen very rarely, because `undefined` should not be explicitly assigned. We mostly use `null` for "unknown" or "empty" values. So the `in` operator is an exotic guest in the code.
## The "for..in" loop
@ -375,7 +412,6 @@ Note that all "for" constructs allow us to declare the looping variable inside t
Also, we could use another variable name here instead of `key`. For instance, `"for (let prop in obj)"` is also widely used.
### 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 they were added? Can we rely on this?
@ -459,262 +495,6 @@ for (let code in codes) {
Now it works as intended.
## Copying by reference
One of the fundamental differences of objects vs primitives is that they are stored and copied "by reference".
Primitive values: strings, numbers, booleans -- are assigned/copied "as a whole value".
For instance:
```js
let message = "Hello!";
let phrase = message;
```
As a result we have two independent variables, each one is storing the string `"Hello!"`.
![](variable-copy-value.svg)
Objects are not like that.
**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:
```js
let user = {
name: "John"
};
```
![](variable-contains-reference.svg)
Here, the object is stored somewhere in memory. And the variable `user` has a "reference" to it.
**When an object variable is copied -- the reference is copied, the object is not duplicated.**
If we imagine an object as a cabinet, then a variable is a key to it. Copying a variable duplicates the key, but not the cabinet itself.
For instance:
```js no-beautify
let user = { name: "John" };
let admin = user; // copy the reference
```
Now we have two variables, each one with the reference to the same object:
![](variable-copy-reference.svg)
We can use any variable to access the cabinet and modify its contents:
```js run
let user = { name: 'John' };
let admin = user;
*!*
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. 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
The equality `==` and strict equality `===` operators for objects work exactly the same.
**Two objects are equal only if they are the same object.**
For instance, if two variables reference the same object, they are equal:
```js run
let a = {};
let b = a; // copy the reference
alert( a == b ); // true, both variables reference the same object
alert( a === b ); // true
```
And here two independent objects are not equal, even though both are empty:
```js run
let a = {};
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 tell the truth, such comparisons are necessary very rarely and usually are a result of a coding mistake.
### Const object
An object declared as `const` *can* be changed.
For instance:
```js run
const user = {
name: "John"
};
*!*
user.age = 25; // (*)
*/!*
alert(user.age); // 25
```
It might seem that the line `(*)` would cause an error, but no, there's totally no problem. That's because `const` fixes only value of `user` itself. And here `user` stores the reference to the same object all the time. The line `(*)` goes *inside* the object, it doesn't reassign `user`.
The `const` would give an error if we try to set `user` to something else, for instance:
```js run
const user = {
name: "John"
};
*!*
// Error (can't reassign user)
*/!*
user = {
name: "Pete"
};
```
...But what if we want to make constant object properties? So that `user.age = 25` would give an error. That's possible too. We'll cover it in the chapter <info:property-descriptors>.
## Cloning and merging, Object.assign
So, copying an object variable creates one more reference to the same object.
But what if we need to duplicate an object? Create an independent copy, a clone?
That's also doable, but a little bit more difficult, because there's no built-in method for that in JavaScript. Actually, that's rarely needed. Copying by reference is good most of the time.
But if we really want that, then we need to create a new object and replicate the structure of the existing one by iterating over its properties and copying them on the primitive level.
Like this:
```js run
let user = {
name: "John",
age: 30
};
*!*
let clone = {}; // the new empty object
// let's copy all user properties into it
for (let key in user) {
clone[key] = user[key];
}
*/!*
// now clone is a fully independent clone
clone.name = "Pete"; // changed the data in it
alert( user.name ); // still John in the original object
```
Also we can use the method [Object.assign](mdn:js/Object/assign) for that.
The syntax is:
```js
Object.assign(dest, [src1, src2, src3...])
```
- Arguments `dest`, and `src1, ..., srcN` (can be as many as needed) are objects.
- It copies the properties of all objects `src1, ..., srcN` into `dest`. In other words, properties of all arguments starting from the 2nd are copied into the 1st. Then it returns `dest`.
For instance, we can use it to merge several objects into one:
```js
let user = { name: "John" };
let permissions1 = { canView: true };
let permissions2 = { canEdit: true };
*!*
// copies all properties from permissions1 and permissions2 into user
Object.assign(user, permissions1, permissions2);
*/!*
// now user = { name: "John", canView: true, canEdit: true }
```
If the receiving object (`user`) already has the same named property, it will be overwritten:
```js
let user = { name: "John" };
// overwrite name, add isAdmin
Object.assign(user, { name: "Pete", isAdmin: true });
// now user = { name: "Pete", isAdmin: true }
```
We also can use `Object.assign` to replace the loop for simple cloning:
```js
let user = {
name: "John",
age: 30
};
*!*
let clone = Object.assign({}, user);
*/!*
```
It copies all properties of `user` into the empty object and returns it. Actually, the same as the loop, but shorter.
Until now we assumed that all properties of `user` are primitive. But properties can be references to other objects. What to do with them?
Like this:
```js run
let user = {
name: "John",
sizes: {
height: 182,
width: 50
}
};
alert( user.sizes.height ); // 182
```
Now it's not enough to copy `clone.sizes = user.sizes`, because the `user.sizes` is an object, it will be copied by reference. So `clone` and `user` will share the same sizes:
Like this:
```js run
let user = {
name: "John",
sizes: {
height: 182,
width: 50
}
};
let clone = Object.assign({}, user);
alert( user.sizes === clone.sizes ); // true, same object
// user and clone share sizes
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 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://html.spec.whatwg.org/multipage/structured-data.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).
## Summary
Objects are associative arrays with several special features.
@ -732,10 +512,6 @@ Additional operators:
- To check if a property with the given key exists: `"key" in obj`.
- To iterate over an object: `for (let key in obj)` loop.
Objects are assigned and copied by reference. In other words, a variable stores not the "object value", but a "reference" (address in memory) for the value. So copying such a variable or passing it as a function argument copies that reference, not the object. All operations via copied references (like adding/removing properties) are performed on the same single object.
To make a "real copy" (a clone) we can use `Object.assign` or [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep).
What we've studied in this chapter is called a "plain object", or just `Object`.
There are many other kinds of objects in JavaScript:

View file

@ -0,0 +1,226 @@
# Object copying, references
One of the fundamental differences of objects vs primitives is that they are stored and copied "by reference".
Primitive values: strings, numbers, booleans -- are assigned/copied "as a whole value".
For instance:
```js
let message = "Hello!";
let phrase = message;
```
As a result we have two independent variables, each one is storing the string `"Hello!"`.
![](variable-copy-value.svg)
Objects are not like that.
**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:
```js
let user = {
name: "John"
};
```
![](variable-contains-reference.svg)
Here, the object is stored somewhere in memory. And the variable `user` has a "reference" to it.
**When an object variable is copied -- the reference is copied, the object is not duplicated.**
For instance:
```js no-beautify
let user = { name: "John" };
let admin = user; // copy the reference
```
Now we have two variables, each one with the reference to the same object:
![](variable-copy-reference.svg)
We can use any variable to access the object and modify its contents:
```js run
let user = { name: 'John' };
let admin = user;
*!*
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. As if we had a cabinet with two keys and used one of them (`admin`) to get into it. Then, if we later use another key (`user`) we can see changes.
## Comparison by reference
The equality `==` and strict equality `===` operators for objects work exactly the same.
**Two objects are equal only if they are the same object.**
Here two variables reference the same object, thus they are equal:
```js run
let a = {};
let b = a; // copy the reference
alert( a == b ); // true, both variables reference the same object
alert( a === b ); // true
```
And here two independent objects are not equal, even though both are empty:
```js run
let a = {};
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 tell the truth, such comparisons occur very rarely, usually as a result of a coding mistake.
## Cloning and merging, Object.assign
So, copying an object variable creates one more reference to the same object.
But what if we need to duplicate an object? Create an independent copy, a clone?
That's also doable, but a little bit more difficult, because there's no built-in method for that in JavaScript. Actually, that's rarely needed. Copying by reference is good most of the time.
But if we really want that, then we need to create a new object and replicate the structure of the existing one by iterating over its properties and copying them on the primitive level.
Like this:
```js run
let user = {
name: "John",
age: 30
};
*!*
let clone = {}; // the new empty object
// let's copy all user properties into it
for (let key in user) {
clone[key] = user[key];
}
*/!*
// now clone is a fully independent object with the same content
clone.name = "Pete"; // changed the data in it
alert( user.name ); // still John in the original object
```
Also we can use the method [Object.assign](mdn:js/Object/assign) for that.
The syntax is:
```js
Object.assign(dest, [src1, src2, src3...])
```
- The first argument `dest` is a target object.
- Further arguments `src1, ..., srcN` (can be as many as needed) are source objects.
- It copies the properties of all source objects `src1, ..., srcN` into the target `dest`. In other words, properties of all arguments starting from the second are copied into the first object.
- The call returns `dest`.
For instance, we can use it to merge several objects into one:
```js
let user = { name: "John" };
let permissions1 = { canView: true };
let permissions2 = { canEdit: true };
*!*
// copies all properties from permissions1 and permissions2 into user
Object.assign(user, permissions1, permissions2);
*/!*
// now user = { name: "John", canView: true, canEdit: true }
```
If the copied property name already exists, it gets overwritten:
```js run
let user = { name: "John" };
Object.assign(user, { name: "Pete" });
alert(user.name); // now user = { name: "Pete" }
```
We also can use `Object.assign` to replace `for..in` loop for simple cloning:
```js
let user = {
name: "John",
age: 30
};
*!*
let clone = Object.assign({}, user);
*/!*
```
It copies all properties of `user` into the empty object and returns it.
## Nested cloning
Until now we assumed that all properties of `user` are primitive. But properties can be references to other objects. What to do with them?
Like this:
```js run
let user = {
name: "John",
sizes: {
height: 182,
width: 50
}
};
alert( user.sizes.height ); // 182
```
Now it's not enough to copy `clone.sizes = user.sizes`, because the `user.sizes` is an object, it will be copied by reference. So `clone` and `user` will share the same sizes:
Like this:
```js run
let user = {
name: "John",
sizes: {
height: 182,
width: 50
}
};
let clone = Object.assign({}, user);
alert( user.sizes === clone.sizes ); // true, same object
// user and clone share sizes
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 its structure as well. That is called a "deep cloning".
We can use recursion to implement it. Or, not to reinvent the wheel, take an existing implementation, for instance [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep) from the JavaScript library [lodash](https://lodash.com).
## Summary
Objects are assigned and copied by reference. In other words, a variable stores not the "object value", but a "reference" (address in memory) for the value. So copying such a variable or passing it as a function argument copies that reference, not the object.
All operations via copied references (like adding/removing properties) are performed on the same single object.
To make a "real copy" (a clone) we can use `Object.assign` for the so-called "shallow copy" (nested objects are copied by reference) or a "deep cloning" function, such as [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep).

View file

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Before After
Before After

View file

@ -23,7 +23,7 @@ Simply put, "reachable" values are those that are accessible or usable somehow.
2. Any other value is considered reachable if it's reachable from a root by a reference or by a chain of references.
For instance, if there's an object in a local variable, and that object has a property referencing another object, that object is considered reachable. And those that it references are also reachable. Detailed examples to follow.
For instance, if there's an object in a global variable, and that object has a property referencing another object, that object is considered reachable. And those that it references are also reachable. Detailed examples to follow.
There's a background process in the JavaScript engine that is called [garbage collector](https://en.wikipedia.org/wiki/Garbage_collection_(computer_science)). It monitors all objects and removes those that have become unreachable.

View file

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Before After
Before After

View file

@ -7,7 +7,7 @@ function makeUser() {
name: "John",
ref: this
};
};
}
let user = makeUser();
@ -45,7 +45,7 @@ function makeUser() {
}
*/!*
};
};
}
let user = makeUser();

View file

@ -14,7 +14,7 @@ function makeUser() {
name: "John",
ref: this
};
};
}
let user = makeUser();

View file

@ -63,7 +63,7 @@ user.sayHi(); // Hello!
```smart header="Object-oriented programming"
When we write our code using objects to represent entities, that's called [object-oriented programming](https://en.wikipedia.org/wiki/Object-oriented_programming), in short: "OOP".
OOP is a big thing, an interesting science of its own. How to choose the right entities? How to organize the interaction between them? That's architecture, and there are great books on that topic, like "Design Patterns: Elements of Reusable Object-Oriented Software" by E.Gamma, R.Helm, R.Johnson, J.Vissides or "Object-Oriented Analysis and Design with Applications" by G.Booch, and more.
OOP is a big thing, an interesting science of its own. How to choose the right entities? How to organize the interaction between them? That's architecture, and there are great books on that topic, like "Design Patterns: Elements of Reusable Object-Oriented Software" by E. Gamma, R. Helm, R. Johnson, J. Vissides or "Object-Oriented Analysis and Design with Applications" by G. Booch, and more.
```
### Method shorthand
@ -233,98 +233,6 @@ The concept of run-time evaluated `this` has both pluses and minuses. On the one
Here our position is not to judge whether this language design decision is good or bad. We'll understand how to work with it, how to get benefits and avoid problems.
```
## Internals: Reference Type
```warn header="In-depth language feature"
This section covers an advanced topic, to understand certain edge-cases better.
If you want to go on faster, it can be skipped or postponed.
```
An intricate method call can lose `this`, for instance:
```js run
let user = {
name: "John",
hi() { alert(this.name); },
bye() { alert("Bye"); }
};
user.hi(); // John (the simple call works)
*!*
// now let's call user.hi or user.bye depending on the name
(user.name == "John" ? user.hi : user.bye)(); // Error!
*/!*
```
On the last line there is a conditional operator that chooses either `user.hi` or `user.bye`. In this case the result is `user.hi`.
Then the method is immediately called with parentheses `()`. But it doesn't work correctly!
As you can see, the call results in an error, because the value of `"this"` inside the call becomes `undefined`.
This works (object dot method):
```js
user.hi();
```
This doesn't (evaluated method):
```js
(user.name == "John" ? user.hi : user.bye)(); // Error!
```
Why? If we want to understand why it happens, let's get under the hood of how `obj.method()` call works.
Looking closely, we may notice two operations in `obj.method()` statement:
1. First, the dot `'.'` retrieves the property `obj.method`.
2. Then parentheses `()` execute it.
So, how does the information about `this` get passed from the first part to the second one?
If we put these operations on separate lines, then `this` will be lost for sure:
```js run
let user = {
name: "John",
hi() { alert(this.name); }
}
*!*
// split getting and calling the method in two lines
let hi = user.hi;
hi(); // Error, because this is undefined
*/!*
```
Here `hi = user.hi` puts the function into the variable, and then on the last line it is completely standalone, and so there's no `this`.
**To make `user.hi()` calls work, JavaScript uses a trick -- the dot `'.'` returns not a function, but a value of the special [Reference Type](https://tc39.github.io/ecma262/#sec-reference-specification-type).**
The Reference Type is a "specification type". We can't explicitly use it, but it is used internally by the language.
The value of Reference Type is a three-value combination `(base, name, strict)`, where:
- `base` is the object.
- `name` is the property name.
- `strict` is true if `use strict` is in effect.
The result of a property access `user.hi` is not a function, but a value of Reference Type. For `user.hi` in strict mode it is:
```js
// Reference Type value
(user, "hi", true)
```
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).
Reference type is a special "intermediary" internal type, with the purpose to pass information from dot `.` to calling parentheses `()`.
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). 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"
Arrow functions are special: they don't have their "own" `this`. If we reference `this` from such a function, it's taken from the outer "normal" function.

View file

@ -0,0 +1,175 @@
# Optional chaining '?.'
[recent browser="new"]
The optional chaining `?.` is an error-proof way to access nested object properties, even if an intermediate property doesn't exist.
## The problem
If you've just started to read the tutorial and learn JavaScript, maybe the problem hasn't touched you yet, but it's quite common.
For example, some of our users have addresses, but few did not provide them. Then we can't safely read `user.address.street`:
```js run
let user = {}; // the user happens to be without address
alert(user.address.street); // Error!
```
Or, in the web development, we'd like to get an information about an element on the page, but it may not exist:
```js run
// Error if the result of querySelector(...) is null
let html = document.querySelector('.my-element').innerHTML;
```
Before `?.` appeared in the language, the `&&` operator was used to work around that.
For example:
```js run
let user = {}; // user has no address
alert( user && user.address && user.address.street ); // undefined (no error)
```
AND'ing the whole path to the property ensures that all components exist, but is cumbersome to write.
## Optional chaining
The optional chaining `?.` stops the evaluation and returns `undefined` if the part before `?.` is `undefined` or `null`.
**Further in this article, for brevity, we'll be saying that something "exists" if it's not `null` and not `undefined`.**
Here's the safe way to access `user.address.street`:
```js run
let user = {}; // user has no address
alert( user?.address?.street ); // undefined (no error)
```
Reading the address with `user?.address` works even if `user` object doesn't exist:
```js run
let user = null;
alert( user?.address ); // undefined
alert( user?.address.street ); // undefined
```
Please note: the `?.` syntax makes optional the value before it, but not any further.
In the example above, `user?.` allows only `user` to be `null/undefined`.
On the other hand, if `user` does exist, then it must have `user.address` property, otherwise `user?.address.street` gives an error at the second dot.
```warn header="Don't overuse the optional chaining"
We should use `?.` only where it's ok that something doesn't exist.
For example, if according to our coding logic `user` object must be there, but `address` is optional, then `user.address?.street` would be better.
So, if `user` happens to be undefined due to a mistake, we'll know about it and fix it. Otherwise, coding errors can be silenced where not appropriate, and become more difficult to debug.
```
````warn header="The variable before `?.` must be declared"
If there's no variable `user` at all, then `user?.anything` triggers an error:
```js run
// ReferenceError: user is not defined
user?.address;
```
There must be `let/const/var user`. The optional chaining works only for declared variables.
````
## Short-circuiting
As it was said before, the `?.` immediately stops ("short-circuits") the evaluation if the left part doesn't exist.
So, if there are any further function calls or side effects, they don't occur:
```js run
let user = null;
let x = 0;
user?.sayHi(x++); // nothing happens
alert(x); // 0, value not incremented
```
## Other cases: ?.(), ?.[]
The optional chaining `?.` is not an operator, but a special syntax construct, that also works with functions and square brackets.
For example, `?.()` is used to call a function that may not exist.
In the code below, some of our users have `admin` method, and some don't:
```js run
let user1 = {
admin() {
alert("I am admin");
}
}
let user2 = {};
*!*
user1.admin?.(); // I am admin
user2.admin?.();
*/!*
```
Here, in both lines we first use the dot `.` to get `admin` property, because the user object must exist, so it's safe read from it.
Then `?.()` checks the left part: if the admin function exists, then it runs (for `user1`). Otherwise (for `user2`) the evaluation stops without errors.
The `?.[]` syntax also works, if we'd like to use brackets `[]` to access properties instead of dot `.`. Similar to previous cases, it allows to safely read a property from an object that may not exist.
```js run
let user1 = {
firstName: "John"
};
let user2 = null; // Imagine, we couldn't authorize the user
let key = "firstName";
alert( user1?.[key] ); // John
alert( user2?.[key] ); // undefined
alert( user1?.[key]?.something?.not?.existing); // undefined
```
Also we can use `?.` with `delete`:
```js run
delete user?.name; // delete user.name if user exists
```
```warn header="We can use `?.` for safe reading and deleting, but not writing"
The optional chaining `?.` has no use at the left side of an assignment:
```js run
// the idea of the code below is to write user.name, if user exists
user?.name = "John"; // Error, doesn't work
// because it evaluates to undefined = "John"
```
## Summary
The `?.` syntax has three forms:
1. `obj?.prop` -- returns `obj.prop` if `obj` exists, otherwise `undefined`.
2. `obj?.[prop]` -- returns `obj[prop]` if `obj` exists, otherwise `undefined`.
3. `obj?.method()` -- calls `obj.method()` if `obj` exists, otherwise returns `undefined`.
As we can see, all of them are straightforward and simple to use. The `?.` checks the left part for `null/undefined` and allows the evaluation to proceed if it's not so.
A chain of `?.` allows to safely access nested properties.
Still, we should apply `?.` carefully, only where it's ok that the left part doesn't to exist.
So that it won't hide programming errors from us, if they occur.

View file

@ -18,7 +18,7 @@ let id = Symbol();
Upon creation, we can give symbol a description (also called a symbol name), mostly useful for debugging purposes:
```js run
```js
// id is a symbol with the description "id"
let id = Symbol("id");
```
@ -121,7 +121,7 @@ user.id = "Their id value"
// Boom! overwritten by another script!
```
### Symbols in a literal
### Symbols in an object literal
If we want to use a symbol in an object literal `{...}`, we need square brackets around it.
@ -133,7 +133,7 @@ let id = Symbol("id");
let user = {
name: "John",
*!*
[id]: 123 // not "id: 123"
[id]: 123 // not "id": 123
*/!*
};
```
@ -178,22 +178,6 @@ alert( clone[id] ); // 123
There's no paradox here. That's by design. The idea is that when we clone an object or merge objects, we usually want *all* properties to be copied (including symbols like `id`).
````smart header="Property keys of other types are coerced to strings"
We can only use strings or symbols as keys in objects. Other types are converted to strings.
For instance, a number `0` becomes a string `"0"` when used as a property key:
```js run
let obj = {
0: "test" // same as "0": "test"
};
// both alerts access the same property (the number 0 is converted to string "0")
alert( obj["0"] ); // test
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 same entities. For instance, different parts of our application want to access symbol `"id"` meaning exactly the same property.
@ -241,7 +225,7 @@ alert( Symbol.keyFor(sym) ); // name
alert( Symbol.keyFor(sym2) ); // id
```
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`.
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 returns `undefined`.
That said, any symbols have `description` property.

View file

@ -46,7 +46,7 @@ There are three variants of type conversion, so-called "hints", described in the
`"default"`
: Occurs in rare cases when the operator is "not sure" what type to expect.
For instance, binary plus `+` can work both with strings (concatenates them) and numbers (adds them), so both strings and numbers would do. So if the a binary plus gets an object as an argument, it uses the `"default"` hint to convert it.
For instance, binary plus `+` can work both with strings (concatenates them) and numbers (adds them), so both strings and numbers would do. So if a binary plus gets an object as an argument, it uses the `"default"` hint to convert it.
Also, if an object is compared using `==` with a string, number or a symbol, it's also unclear which conversion should be done, so the `"default"` hint is used.

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