Merge branch 'en' of https://github.com/iliakan/javascript-tutorial into en
This commit is contained in:
commit
d184bf8a98
21 changed files with 57 additions and 57 deletions
|
@ -1,4 +1,4 @@
|
||||||
The test demonstrates one of temptations a developer meets when writing tests.
|
The test demonstrates one of the temptations a developer meets when writing tests.
|
||||||
|
|
||||||
What we have here is actually 3 tests, but layed out as a single function with 3 asserts.
|
What we have here is actually 3 tests, but layed out as a single function with 3 asserts.
|
||||||
|
|
||||||
|
|
|
@ -384,7 +384,7 @@ The spec can be used in three ways:
|
||||||
2. **Docs** -- the titles of `describe` and `it` tell what the function does.
|
2. **Docs** -- the titles of `describe` and `it` tell what the function does.
|
||||||
3. **Examples** -- the tests are actually working examples showing how a function can be used.
|
3. **Examples** -- the tests are actually working examples showing how a function can be used.
|
||||||
|
|
||||||
With the spec, we can safely improve, change, even rewrite the function from the scratch and make sure it still works right.
|
With the spec, we can safely improve, change, even rewrite the function from scratch and make sure it still works right.
|
||||||
|
|
||||||
That's especially important in large projects when a function is used in many places. When we change such a function -- there's just no way to manually check if every place that uses them still works right.
|
That's especially important in large projects when a function is used in many places. When we change such a function -- there's just no way to manually check if every place that uses them still works right.
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ Here Babel.JS comes to the rescue.
|
||||||
|
|
||||||
Actually, there are two parts in Babel:
|
Actually, there are two parts in Babel:
|
||||||
|
|
||||||
1. First, the transpiler program, which rewrites the code. The developer run it on his own computer. It rewrites the code into the older standard. And then the code is delivered to the website for users. Modern project build system like [webpack](http://webpack.github.io/) or [brunch](http://brunch.io/) provide means to run transpiler automatically on every code change, so that doesn't involve any time loss from our side.
|
1. First, the transpiler program, which rewrites the code. The developer runs it on his own computer. It rewrites the code into the older standard. And then the code is delivered to the website for users. Modern project build system like [webpack](http://webpack.github.io/) or [brunch](http://brunch.io/) provide means to run transpiler automatically on every code change, so that doesn't involve any time loss from our side.
|
||||||
|
|
||||||
2. Second, the polyfill.
|
2. Second, the polyfill.
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ importance: 5
|
||||||
|
|
||||||
# Hello, object
|
# Hello, object
|
||||||
|
|
||||||
Write the code, each line for an action:
|
Write the code, one line for each action:
|
||||||
|
|
||||||
1. Create an empty object `user`.
|
1. Create an empty object `user`.
|
||||||
2. Add the property `name` with the value `John`.
|
2. Add the property `name` with the value `John`.
|
||||||
|
|
|
@ -35,7 +35,7 @@ let user = { // an object
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
A property has a key (also known as "name" and "identifier") before the colon `":"` and a value to the right of it.
|
A property has a key (also known as "name" or "identifier") before the colon `":"` and a value to the right of it.
|
||||||
|
|
||||||
In the `user` object, there are two properties:
|
In the `user` object, there are two properties:
|
||||||
|
|
||||||
|
@ -125,7 +125,7 @@ delete user["likes birds"];
|
||||||
|
|
||||||
Now everything is fine. Please note that the string inside the brackets is properly quoted (any type of quotes will do).
|
Now everything is fine. Please note that the string inside the brackets is properly quoted (any type of quotes will do).
|
||||||
|
|
||||||
Square brackets are also provide a way to access a property by the name from the variable:
|
Square brackets also provide a way to obtain the property name as the result of any expression - as opposed to a litteral string - like from a variable as follows:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
let key = "likes birds";
|
let key = "likes birds";
|
||||||
|
@ -134,7 +134,7 @@ let key = "likes birds";
|
||||||
user[key] = true;
|
user[key] = true;
|
||||||
```
|
```
|
||||||
|
|
||||||
Here, the variable `key` may be calculated at run-time or depend on the user input. And then we use it to access the property. That gives us a great deal of flexibility. The dot notation cannot be used in similar way.
|
Here, the variable `key` may be calculated at run-time or depend on the user input. And then we use it to access the property. That gives us a great deal of flexibility. The dot notation cannot be used in a similar way.
|
||||||
|
|
||||||
For instance:
|
For instance:
|
||||||
|
|
||||||
|
@ -270,7 +270,7 @@ let user = {
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
## Existance check
|
## 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:
|
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:
|
||||||
|
|
||||||
|
@ -296,7 +296,7 @@ alert( "age" in user ); // true, user.age exists
|
||||||
alert( "blabla" in user ); // false, user.blabla doesn't exist
|
alert( "blabla" in user ); // false, user.blabla doesn't exist
|
||||||
```
|
```
|
||||||
|
|
||||||
Please note that at the left side of `in` there must be a *property name*. That's usually a quoted string.
|
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 to be tested. For instance:
|
If we omit quotes, that would mean a variable containing the actual name to be tested. For instance:
|
||||||
|
|
||||||
|
@ -698,7 +698,7 @@ alert(clone.sizes.width); // 51, see the result from the other one
|
||||||
|
|
||||||
To fix that, we should use the cloning loop that examines each value of `user[key]` and, if it's an object, then replicate it's structure as well. That is called a "deep cloning".
|
To fix that, we should use the cloning loop that examines each value of `user[key]` and, if it's an object, then replicate it's structure as well. That is called a "deep cloning".
|
||||||
|
|
||||||
There's a standard algorithm for deep cloning that handles the case above and more complex cases, called the [Structured cloning algorithm](https://w3c.github.io/html/infrastructure.html#internal-structured-cloning-algorithm). Not to reinvent the wheel, we can use a working implementation of it from the JavaScript library [lodash](https://lodash.com), the method is called [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep).
|
There's a standard algorithm for deep cloning that handles the case above and more complex cases, called the [Structured cloning algorithm](https://w3c.github.io/html/infrastructure.html#internal-structured-cloning-algorithm). In order not to reinvent the wheel, we can use a working implementation of it from the JavaScript library [lodash](https://lodash.com), the method is called [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -117,9 +117,9 @@ delete family.mother.husband;
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
It's not enough to delete any one of them, because all objects would still be reachable.
|
It's not enough to delete only one of these two references, because all objects would still be reachable.
|
||||||
|
|
||||||
But if we delete both, then we can see that John has no incoming references any more:
|
But if we delete both, then we can see that John has no incoming reference any more:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
|
@ -158,7 +158,7 @@ For instance, different parts of our application want to access symbol `"id"` me
|
||||||
|
|
||||||
To achieve that, there exists a *global symbol registry*. We can create symbols in it and and access them later, and it guarantees that repeated accesses by the same name return exactly the same symbol.
|
To achieve that, there exists a *global symbol registry*. We can create symbols in it and and access them later, and it guarantees that repeated accesses by the same name return exactly the same symbol.
|
||||||
|
|
||||||
To can create or read a symbol in the registry, use `Symbol.for(name)`.
|
In order to create or read a symbol in the registry, use `Symbol.for(name)`.
|
||||||
|
|
||||||
For instance:
|
For instance:
|
||||||
|
|
||||||
|
|
|
@ -221,11 +221,11 @@ In non-strict mode (if one forgets `use strict`) the value of `this` in such cas
|
||||||
Please note that usually a call of a function that uses `this` without an object is not normal, but rather a programming mistake. If a function has `this`, then it is usually meant to be called in the context of an object.
|
Please note that usually a call of a function that uses `this` without an object is not normal, but rather a programming mistake. If a function has `this`, then it is usually meant to be called in the context of an object.
|
||||||
|
|
||||||
```smart header="The consequences of unbound `this`"
|
```smart header="The consequences of unbound `this`"
|
||||||
If you come from another programming languages, then you are probably used to an idea of a "bound `this`", where methods defined in an object always have `this` referencing that object.
|
If you come from another programming language, then you are probably used to the idea of a "bound `this`", where methods defined in an object always have `this` referencing that object.
|
||||||
|
|
||||||
In JavaScript `this` is "free", its value is evaluated at the call time and depends not on where the method was declared, but rather on what's the object "before dot".
|
In JavaScript `this` is "free", its value is evaluated at call-time and does not depend on where the method was declared, but rather on what's the object "before the dot".
|
||||||
|
|
||||||
The concept of run-time evaluated `this` has both pluses and minuses. From one side, a function can be reused for different objects. From the other side, greater flexibility opens a place for mistakes.
|
The concept of run-time evaluated `this` has both pluses and minuses. On the one hand, a function can be reused for different objects. On the the other hand, greater flexibility opens a place for mistakes.
|
||||||
|
|
||||||
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 evade problems.
|
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 evade problems.
|
||||||
```
|
```
|
||||||
|
@ -238,7 +238,7 @@ 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.
|
If you want to go on faster, it can be skipped or postponed.
|
||||||
```
|
```
|
||||||
|
|
||||||
An intricate method call can loose `this`, for instance:
|
An intricate method call can lose `this`, for instance:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
let user = {
|
let user = {
|
||||||
|
@ -322,7 +322,7 @@ So, as the result, the value of `this` is only passed the right way if the funct
|
||||||
|
|
||||||
## Arrow functions have no "this"
|
## Arrow functions have no "this"
|
||||||
|
|
||||||
Arrow functions are special: they don't have "own" `this`. If we reference `this` from such function, it's taken from the outer "normal" function.
|
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.
|
||||||
|
|
||||||
For instance, here `arrow()` uses `this` from the outer `user.sayHi()` method:
|
For instance, here `arrow()` uses `this` from the outer `user.sayHi()` method:
|
||||||
|
|
||||||
|
|
|
@ -87,7 +87,7 @@ The constructor can't be called again, because it is not saved anywhere, just cr
|
||||||
|
|
||||||
## Dual-use constructors: new.target
|
## Dual-use constructors: new.target
|
||||||
|
|
||||||
Inside a function, we can check how it is called with `new` or without it, using a special `new.target` property.
|
Inside a function, we can check whether it was called with `new` or without it, using a special `new.target` property.
|
||||||
|
|
||||||
It is empty for ordinary runs and equals the function if called with `new`:
|
It is empty for ordinary runs and equals the function if called with `new`:
|
||||||
|
|
||||||
|
@ -116,7 +116,7 @@ let john = User("John"); // redirects call to new User
|
||||||
alert(john.name); // John
|
alert(john.name); // John
|
||||||
```
|
```
|
||||||
|
|
||||||
This approach is sometimes used in libraries to make the syntax more flexible. Probably not a good thing to use everywhere though, because it makes a bit less obvious what's going on for a person who's familiar with internals of `User`.
|
This approach is sometimes used in libraries to make the syntax more flexible. Probably not a good thing to use everywhere though, because it makes a bit less obvious what's going on for a person who's familiar with the internals of `User`.
|
||||||
|
|
||||||
## Return from constructors
|
## Return from constructors
|
||||||
|
|
||||||
|
@ -158,7 +158,7 @@ function SmallUser() {
|
||||||
alert( new SmallUser().name ); // John
|
alert( new SmallUser().name ); // John
|
||||||
```
|
```
|
||||||
|
|
||||||
Most of time constructors return nothing. Here we mention the special behavior with returning objects mainly for the sake of completeness.
|
Most of the time constructors return nothing. Here we mention the special behavior with returning objects mainly for the sake of completeness.
|
||||||
|
|
||||||
````smart header="Omitting brackets"
|
````smart header="Omitting brackets"
|
||||||
By the way, we can omit brackets after `new`, if it has no arguments:
|
By the way, we can omit brackets after `new`, if it has no arguments:
|
||||||
|
@ -208,7 +208,7 @@ john = {
|
||||||
- Constructor functions or, shortly, constructors, are regular functions, but there's a common agreement to name them with capital letter first.
|
- Constructor functions or, shortly, constructors, are regular functions, but there's a common agreement to name them with capital letter first.
|
||||||
- Constructor functions should only be called using `new`. Such call implies a creation of empty `this` at the start and returning the populated one at the end.
|
- Constructor functions should only be called using `new`. Such call implies a creation of empty `this` at the start and returning the populated one at the end.
|
||||||
|
|
||||||
We can use constructor functions to make multiple similar objects. But the topic is much deeper than described here. So we'll return it later and cover more in-depth.
|
We can use constructor functions to make multiple similar objects. But the topic is much deeper than described here. So we'll return to it later and cover it more in-depth.
|
||||||
|
|
||||||
JavaScript provides constructor functions for many built-in language objects: like `Date` for dates, `Set` for sets and others that we plan to study.
|
JavaScript provides constructor functions for many built-in language objects: like `Date` for dates, `Set` for sets and others that we plan to study.
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ An object
|
||||||
: Is capable of storing multiple values as properties.
|
: Is capable of storing multiple values as properties.
|
||||||
Can be created with `{}`, for instance: `{name: "John", age: 30}`. There are other kinds of objects in JavaScript, e.g. functions are objects.
|
Can be created with `{}`, for instance: `{name: "John", age: 30}`. There are other kinds of objects in JavaScript, e.g. functions are objects.
|
||||||
|
|
||||||
One of the best thing about objects is that we can store a function as one of properties:
|
One of the best things about objects is that we can store a function as one of properties:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
let john = {
|
let john = {
|
||||||
|
@ -85,7 +85,7 @@ We'll see more specific methods in chapters <info:number> and <info:string>.
|
||||||
````warn header="Constructors `String/Number/Boolean` are for internal use only"
|
````warn header="Constructors `String/Number/Boolean` are for internal use only"
|
||||||
Some languages like Java allow to create "wrapper objects" for primitives explicitly using syntax like `new Number(1)` or `new Boolean(false)`.
|
Some languages like Java allow to create "wrapper objects" for primitives explicitly using syntax like `new Number(1)` or `new Boolean(false)`.
|
||||||
|
|
||||||
In JavaScript that's also possible for historical reasons, but highly not recommended. Things will go crazy in many places.
|
In JavaScript that's also possible for historical reasons, but highly **not recommended**. Things will go crazy in many places.
|
||||||
|
|
||||||
For instance:
|
For instance:
|
||||||
|
|
||||||
|
@ -95,7 +95,7 @@ alert( typeof 1 ); // "number"
|
||||||
alert( typeof new Number(1) ); // "object"!
|
alert( typeof new Number(1) ); // "object"!
|
||||||
```
|
```
|
||||||
|
|
||||||
And, because `zero` is an object:
|
And, because in what follows `zero` is an object, the alert will show up:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
let zero = new Number(0);
|
let zero = new Number(0);
|
||||||
|
@ -105,7 +105,7 @@ if (zero) { // zero is true, because it's an object
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
From the other side, using same functions `String/Number/Boolean` without `new` is a totally sane and useful thing. They convert a value to the corresponding type: to a string, a number, or a boolean (primitive).
|
From the other side, using the same functions `String/Number/Boolean` without `new` is a totally sane and useful thing. They convert a value to the corresponding type: to a string, a number, or a boolean (primitive).
|
||||||
|
|
||||||
This is totally valid:
|
This is totally valid:
|
||||||
```js
|
```js
|
||||||
|
|
|
@ -18,7 +18,7 @@ Here the precision loss made the number a little bit greater, so it rounded up.
|
||||||
|
|
||||||
**How can we fix the problem with `6.35` if we want it to be rounded the right way?**
|
**How can we fix the problem with `6.35` if we want it to be rounded the right way?**
|
||||||
|
|
||||||
We should use bring it closer to an integer prior to rounding:
|
We should bring it closer to an integer prior to rounding:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
alert( (6.35 * 10).toFixed(20) ); // 63.50000000000000000000
|
alert( (6.35 * 10).toFixed(20) ); // 63.50000000000000000000
|
||||||
|
|
|
@ -12,7 +12,7 @@ For instance:
|
||||||
alert( 1.35.toFixed(1) ); // 1.4
|
alert( 1.35.toFixed(1) ); // 1.4
|
||||||
```
|
```
|
||||||
|
|
||||||
How do you think why in the similar example below `6.35` is rounded to `6.3`, not `6.4`?
|
In the similar example below, why is `6.35` rounded to `6.3`, not `6.4`?
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
alert( 6.35.toFixed(1) ); // 6.3
|
alert( 6.35.toFixed(1) ); // 6.3
|
||||||
|
|
|
@ -12,7 +12,7 @@ Imagine, we need to write a billion. The obvious way is:
|
||||||
let billion = 1000000000;
|
let billion = 1000000000;
|
||||||
```
|
```
|
||||||
|
|
||||||
But in real life we usually dislike writing many zeroes. It's easy to mistype. Also we are lazy. We we usually write something like `"1bn"` for a billion or `"7.3bn"` for 7 billions 300 millions. The similar is true for other big numbers.
|
But in real life we usually dislike writing many zeroes. It's easy to mistype. Also we are lazy. We will usually write something like `"1bn"` for a billion or `"7.3bn"` for 7 billions 300 millions. The similar is true for other big numbers.
|
||||||
|
|
||||||
In JavaScript, we can do almost the same by appending the letter `"e"` to the number and specifying the zeroes count:
|
In JavaScript, we can do almost the same by appending the letter `"e"` to the number and specifying the zeroes count:
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ let ms = 1e-6; // six zeroes to the left from 1
|
||||||
|
|
||||||
If we count the zeroes in `0.000001`, there are 6 of them. So naturally it's `1e-6`.
|
If we count the zeroes in `0.000001`, there are 6 of them. So naturally it's `1e-6`.
|
||||||
|
|
||||||
In other words, a negative number after `"e"` means a division by 1 with the given number of zeries:
|
In other words, a negative number after `"e"` means a division by 1 with the given number of zeroes:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
// -3 divides by 1 with 3 zeroes
|
// -3 divides by 1 with 3 zeroes
|
||||||
|
|
|
@ -221,7 +221,7 @@ There are multiple ways to look for a substring in a string.
|
||||||
|
|
||||||
The first method is [str.indexOf(substr, pos)](mdn:js/String/indexOf).
|
The first method is [str.indexOf(substr, pos)](mdn:js/String/indexOf).
|
||||||
|
|
||||||
It looks for the `substr` in `str`, starting from the given position `pos`, and returns the position where the match was found or `-1` if nothing found.
|
It looks for the `substr` in `str`, starting from the given position `pos`, and returns the position where the match was found or `-1` if nothing can be found.
|
||||||
|
|
||||||
For instance:
|
For instance:
|
||||||
|
|
||||||
|
@ -337,7 +337,7 @@ if (~str.indexOf("Widget")) {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
It is usually not recommended to use language features in a non-obvious way, but this particular trick is widely used in the old code, so we should understand it.
|
It is usually not recommended to use language features in a non-obvious way, but this particular trick is widely used in old code, so we should understand it.
|
||||||
|
|
||||||
Just remember: `if (~str.indexOf(...))` reads as "if found".
|
Just remember: `if (~str.indexOf(...))` reads as "if found".
|
||||||
````
|
````
|
||||||
|
@ -476,7 +476,7 @@ Although, there are some oddities.
|
||||||
|
|
||||||
That may lead to strange results if we sort country names. Usually people would await for `Zealand` to be after `Österreich` in the list.
|
That may lead to strange results if we sort country names. Usually people would await for `Zealand` to be after `Österreich` in the list.
|
||||||
|
|
||||||
To understand what happens, let's review the internal representaion of strings in JavaScript.
|
To understand what happens, let's review the internal representation of strings in JavaScript.
|
||||||
|
|
||||||
All strings are encoded using [UTF-16](https://en.wikipedia.org/wiki/UTF-16). That is: each character has a corresponding numeric code. There are special methods that allow to get the character for the code and back.
|
All strings are encoded using [UTF-16](https://en.wikipedia.org/wiki/UTF-16). That is: each character has a corresponding numeric code. There are special methods that allow to get the character for the code and back.
|
||||||
|
|
||||||
|
@ -496,7 +496,7 @@ All strings are encoded using [UTF-16](https://en.wikipedia.org/wiki/UTF-16). Th
|
||||||
alert( String.fromCodePoint(90) ); // Z
|
alert( String.fromCodePoint(90) ); // Z
|
||||||
```
|
```
|
||||||
|
|
||||||
We can also add unicode charactes by their codes using `\u` followed by the hex code:
|
We can also add unicode characters by their codes using `\u` followed by the hex code:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
// 90 is 5a in hexadecimal system
|
// 90 is 5a in hexadecimal system
|
||||||
|
|
|
@ -4,7 +4,7 @@ importance: 3
|
||||||
|
|
||||||
# Is array copied?
|
# Is array copied?
|
||||||
|
|
||||||
What this code is going to show?
|
What is this code going to show?
|
||||||
|
|
||||||
```js
|
```js
|
||||||
let fruits = ["Apples", "Pear", "Orange"];
|
let fruits = ["Apples", "Pear", "Orange"];
|
||||||
|
|
|
@ -46,7 +46,7 @@ fruits[2] = 'Pear'; // now ["Apple", "Orange", "Pear"]
|
||||||
...Or add a new one to the array:
|
...Or add a new one to the array:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
fruits[3] = 'Lemon'; // now ["Apple", "Orange", "Plum", "Lemon"]
|
fruits[3] = 'Lemon'; // now ["Apple", "Orange", "Pear", "Lemon"]
|
||||||
```
|
```
|
||||||
|
|
||||||
The total count of the elements in the array is its `length`:
|
The total count of the elements in the array is its `length`:
|
||||||
|
@ -211,7 +211,7 @@ arr.push("Pear"); // modify the array by reference
|
||||||
alert( fruits ); // Banana, Pear - 2 items now
|
alert( fruits ); // Banana, Pear - 2 items now
|
||||||
```
|
```
|
||||||
|
|
||||||
...But what makes arrays really special is their internal representation. The engine tries to store its elements in the contiguous memory area, one after another, just as painted on the illustrations in this chapter, and there are other optimizations as well, to make arrays work really fast.
|
...But what makes arrays really special is their internal representation. The engine tries to store its elements in the contiguous memory area, one after another, just as depicted on the illustrations in this chapter, and there are other optimizations as well, to make arrays work really fast.
|
||||||
|
|
||||||
But they all break if we quit working with an array as with an "ordered collection" and start working with it as if it were a regular object.
|
But they all break if we quit working with an array as with an "ordered collection" and start working with it as if it were a regular object.
|
||||||
|
|
||||||
|
@ -225,7 +225,7 @@ fruits[99999] = 5; // assign a property with the index far greater than its leng
|
||||||
fruits.age = 25; // create a property with an arbitrary name
|
fruits.age = 25; // create a property with an arbitrary name
|
||||||
```
|
```
|
||||||
|
|
||||||
That's possible, because arrays are objects at base. We can add any properties to them.
|
That's possible, because arrays are objects at their base. We can add any properties to them.
|
||||||
|
|
||||||
But the engine will see that we're working with the array as with a regular object. Array-specific optimizations are not suited for such cases and will be turned off, their benefits disappear.
|
But the engine will see that we're working with the array as with a regular object. Array-specific optimizations are not suited for such cases and will be turned off, their benefits disappear.
|
||||||
|
|
||||||
|
@ -235,7 +235,7 @@ The ways to misuse an array:
|
||||||
- Make holes, like: add `arr[0]` and then `arr[1000]` (and nothing between them).
|
- Make holes, like: add `arr[0]` and then `arr[1000]` (and nothing between them).
|
||||||
- Fill the array in the reverse order, like `arr[1000]`, `arr[999]` and so on.
|
- Fill the array in the reverse order, like `arr[1000]`, `arr[999]` and so on.
|
||||||
|
|
||||||
Please think of arrays as about special structures to work with the *ordered data*. They provide special methods for that. Arrays are carefully tuned inside JavaScript engines to work with contiguous ordered data, please use them this way. And if you need arbitrary keys, chances are high that you actually require a regular object `{}`.
|
Please think of arrays as special structures to work with the *ordered data*. They provide special methods for that. Arrays are carefully tuned inside JavaScript engines to work with contiguous ordered data, please use them this way. And if you need arbitrary keys, chances are high that you actually require a regular object `{}`.
|
||||||
|
|
||||||
## Performance
|
## Performance
|
||||||
|
|
||||||
|
@ -344,7 +344,7 @@ Note that we usually don't use arrays like that.
|
||||||
|
|
||||||
Another interesting thing about the `length` property is that it's writable.
|
Another interesting thing about the `length` property is that it's writable.
|
||||||
|
|
||||||
If we increase it manually, nothing interesting happens. But if we decrease it, the array is truncated. The process is irreversable, here's the example:
|
If we increase it manually, nothing interesting happens. But if we decrease it, the array is truncated. The process is irreversible, here's the example:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
let arr = [1, 2, 3, 4, 5];
|
let arr = [1, 2, 3, 4, 5];
|
||||||
|
|
|
@ -330,7 +330,7 @@ alert(lengths); // 5,7,6
|
||||||
|
|
||||||
### sort(fn)
|
### sort(fn)
|
||||||
|
|
||||||
The method [arr.sort](mdn:js/Array/sort) sorts the array *at place*.
|
The method [arr.sort](mdn:js/Array/sort) sorts the array *in place*.
|
||||||
|
|
||||||
For instance:
|
For instance:
|
||||||
|
|
||||||
|
@ -384,7 +384,7 @@ Now it works as intended.
|
||||||
|
|
||||||
Let's step aside and thing what's happening. The `arr` can be array of anything, right? It may contain numbers or strings or html elements or whatever. We have a set of *something*. To sort it, we need an *ordering function* that knows how to compare its elements. The default is a string order.
|
Let's step aside and thing what's happening. The `arr` can be array of anything, right? It may contain numbers or strings or html elements or whatever. We have a set of *something*. To sort it, we need an *ordering function* that knows how to compare its elements. The default is a string order.
|
||||||
|
|
||||||
The `arr.sort(fn)` method has a built-in implementation of sorting algorithm. We don't need to care which how it exactly works (an optimized [quicksort](https://en.wikipedia.org/wiki/Quicksort) most of time). It will walk the array, compare its elements using the provided function and reorder them, all we need is to provide the `fn` which does the comparison.
|
The `arr.sort(fn)` method has a built-in implementation of sorting algorithm. We don't need to care how it exactly works (an optimized [quicksort](https://en.wikipedia.org/wiki/Quicksort) most of the time). It will walk the array, compare its elements using the provided function and reorder them, all we need is to provide the `fn` which does the comparison.
|
||||||
|
|
||||||
By the way, if we ever want to know which elements are compared -- nothing prevents from alerting them:
|
By the way, if we ever want to know which elements are compared -- nothing prevents from alerting them:
|
||||||
|
|
||||||
|
@ -418,7 +418,7 @@ Remember [arrow functions](info:function-expression#arrow-functions)? We can use
|
||||||
arr.sort( (a, b) => a - b );
|
arr.sort( (a, b) => a - b );
|
||||||
```
|
```
|
||||||
|
|
||||||
This works exactly the same as a longer function expression above.
|
This works exactly the same as the other, longer, version above.
|
||||||
````
|
````
|
||||||
|
|
||||||
### reverse
|
### reverse
|
||||||
|
@ -657,7 +657,7 @@ let youngerUsers = users.filter(user.younger, user);
|
||||||
alert(youngerUsers.length); // 2
|
alert(youngerUsers.length); // 2
|
||||||
```
|
```
|
||||||
|
|
||||||
In the call above, we use `user.younger` as a filter and also provide `user` as the context for it. If we did't provide the context, `users.filter(user.younger)` would call `user.younger` as a standalone function, with `this=undefined`. That would mean an instant error.
|
In the call above, we use `user.younger` as a filter and also provide `user` as the context for it. If we didn't provide the context, `users.filter(user.younger)` would call `user.younger` as a standalone function, with `this=undefined`. That would mean an instant error.
|
||||||
|
|
||||||
## Other methods
|
## Other methods
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ Iterables are widely used by the core JavaScript, as we'll see many operators an
|
||||||
|
|
||||||
We can easily grasp the concept of iterables by making one of our own.
|
We can easily grasp the concept of iterables by making one of our own.
|
||||||
|
|
||||||
For instance, we have an object, that is not an array, but looks a suitable for `for..of`.
|
For instance, we have an object, that is not an array, but looks suitable for `for..of`.
|
||||||
|
|
||||||
Like a `range` object that represents an interval of numbers:
|
Like a `range` object that represents an interval of numbers:
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ To make the `range` iterable (and thus let `for..of` work) we need to add a meth
|
||||||
- When `for..of` wants the next value, it calls `next()` on that object.
|
- When `for..of` wants the next value, it calls `next()` on that object.
|
||||||
- The result of `next()` must have the form `{done: Boolean, value: any}`, where `done=true` means that the iteration is finished, otherwise `value` must be the new value.
|
- The result of `next()` must have the form `{done: Boolean, value: any}`, where `done=true` means that the iteration is finished, otherwise `value` must be the new value.
|
||||||
|
|
||||||
Here's the full impelementation for `range`:
|
Here's the full implementation for `range`:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
let range = {
|
let range = {
|
||||||
|
@ -138,7 +138,7 @@ for(let char of str) {
|
||||||
|
|
||||||
Normally, internals of iterables are hidden from the external code. There's a `for..of` loop, that works, that's all it needs to know.
|
Normally, internals of iterables are hidden from the external code. There's a `for..of` loop, that works, that's all it needs to know.
|
||||||
|
|
||||||
But to understand things a little bit more deeper let's see how to create an iterator explicitly. We'll do that same as `for..of`, but with direct calls.
|
But to understand things a little bit more deeper let's see how to create an iterator explicitly. We'll do that the same way as `for..of`, but with direct calls.
|
||||||
|
|
||||||
For instance, this code gets a string iterator and calls it "manually":
|
For instance, this code gets a string iterator and calls it "manually":
|
||||||
|
|
||||||
|
@ -168,7 +168,7 @@ There are two official terms that look similar, but are very different. Please b
|
||||||
|
|
||||||
Naturally, they can combine. For instance, strings are both iterable and array-like.
|
Naturally, they can combine. For instance, strings are both iterable and array-like.
|
||||||
|
|
||||||
But an iterable may be not array-like and vise versa.
|
But an iterable may be not array-like and vice versa.
|
||||||
|
|
||||||
For example, the `range` in the example above is iterable, but not array-like, because it does not have indexed properties and `length`.
|
For example, the `range` in the example above is iterable, but not array-like, because it does not have indexed properties and `length`.
|
||||||
|
|
||||||
|
@ -187,7 +187,7 @@ for(let item of arrayLike) {}
|
||||||
*/!*
|
*/!*
|
||||||
```
|
```
|
||||||
|
|
||||||
What they share in common -- both iterables and array-likes are usually *not arrays*, they don't have `push`, `pop` etc. That's rather inconvenient if we have such object and want to work with it as with an array.
|
What they share in common -- both iterables and array-likes are usually *not arrays*, they don't have `push`, `pop` etc. That's rather inconvenient if we have such an object and want to work with it as with an array.
|
||||||
|
|
||||||
## Array.from
|
## Array.from
|
||||||
|
|
||||||
|
@ -236,7 +236,7 @@ let arr = Array.from(range, num => num * num);
|
||||||
alert(arr); // 1,4,9,16,25
|
alert(arr); // 1,4,9,16,25
|
||||||
```
|
```
|
||||||
|
|
||||||
We can also use `Array.from` to turn a string into array of characters:
|
We can also use `Array.from` to turn a string into an array of characters:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
let str = '𝒳😂';
|
let str = '𝒳😂';
|
||||||
|
@ -249,7 +249,7 @@ alert(chars[1]); // 😂
|
||||||
alert(chars.length); // 2
|
alert(chars.length); // 2
|
||||||
```
|
```
|
||||||
|
|
||||||
Unlike `str.split`, it relies on iterable nature of the string and so, just like `for..of`, correctly works with surrogate pairs.
|
Unlike `str.split`, it relies on the iterable nature of the string and so, just like `for..of`, correctly works with surrogate pairs.
|
||||||
|
|
||||||
Technically here it does the same as:
|
Technically here it does the same as:
|
||||||
|
|
||||||
|
@ -266,7 +266,7 @@ alert(chars);
|
||||||
|
|
||||||
...But is shorter.
|
...But is shorter.
|
||||||
|
|
||||||
We can even built surrogate-aware `slice` on it:
|
We can even build surrogate-aware `slice` on it:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
function slice(str, start, end) {
|
function slice(str, start, end) {
|
||||||
|
|
|
@ -153,7 +153,7 @@ for(let entry of recipeMap) { // the same as of recipeMap.entries()
|
||||||
```
|
```
|
||||||
|
|
||||||
```smart header="The insertion order is used"
|
```smart header="The insertion order is used"
|
||||||
The iteration goes in the same order as the values were inserted. `Map` guarantees that unlike a regular `Object`.
|
The iteration goes in the same order as the values were inserted. `Map` preserves this order, unlike a regular `Object`.
|
||||||
```
|
```
|
||||||
|
|
||||||
Besides that, `Map` has a built-in `forEach` method, similar to `Array`:
|
Besides that, `Map` has a built-in `forEach` method, similar to `Array`:
|
||||||
|
@ -371,7 +371,7 @@ john = null;
|
||||||
// so the object is removed both from the memory and from visitsCountMap automatically
|
// so the object is removed both from the memory and from visitsCountMap automatically
|
||||||
```
|
```
|
||||||
|
|
||||||
With a regular `Map`, cleaning up after a user left becomes a tedious task: we not only need to remove the user from its main storage (be it a variable or an array), but also need to clean up the additional stores like `visitsCountMap`. And it can become cumbersome in more complex cases when users are managed in one place of the code and the additional structure is at another place and is getting no information about removals.
|
With a regular `Map`, cleaning up after a user has left becomes a tedious task: we not only need to remove the user from its main storage (be it a variable or an array), but also need to clean up the additional stores like `visitsCountMap`. And it can become cumbersome in more complex cases when users are managed in one place of the code and the additional structure is at another place and is getting no information about removals.
|
||||||
|
|
||||||
`WeakMap` can make things simpler, because it is cleaned up automatically. The information in it like visits count in the example above lives only while the key object exists.
|
`WeakMap` can make things simpler, because it is cleaned up automatically. The information in it like visits count in the example above lives only while the key object exists.
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ let salaries = {
|
||||||
|
|
||||||
Create the function `topSalary(salaries)` that returns the name of the top-paid person.
|
Create the function `topSalary(salaries)` that returns the name of the top-paid person.
|
||||||
|
|
||||||
- If `salaries` is empty, it shoul return `null`.
|
- If `salaries` is empty, it should return `null`.
|
||||||
- If there are multiple top-paid persons, return any of them.
|
- If there are multiple top-paid persons, return any of them.
|
||||||
|
|
||||||
P.S. Use `Object.entries` and destructuring to iterate over key/value pairs.
|
P.S. Use `Object.entries` and destructuring to iterate over key/value pairs.
|
||||||
|
|
|
@ -140,7 +140,7 @@ The value of `rest` is the array of the remaining array elements. We can use any
|
||||||
|
|
||||||
### Default values
|
### Default values
|
||||||
|
|
||||||
The there are less values in the array than variables in the assignment -- there will be no error, absent values are considered undefined:
|
If there are fewer values in the array than variables in the assignment -- there will be no error, absent values are considered undefined:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
*!*
|
*!*
|
||||||
|
@ -150,7 +150,7 @@ let [firstName, surname] = [];
|
||||||
alert(firstName); // undefined
|
alert(firstName); // undefined
|
||||||
```
|
```
|
||||||
|
|
||||||
If we want a "default" value to take place of the absent one, we can provide it using `=`:
|
If we want a "default" value to replace the missing one, we can provide it using `=`:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
*!*
|
*!*
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue