work
This commit is contained in:
parent
b043ea701d
commit
354c616abc
20 changed files with 859 additions and 41 deletions
|
@ -127,15 +127,15 @@ let age = null;
|
||||||
|
|
||||||
In JavaScript `null` is not a "reference to a non-existing object" or a "null pointer" like in some other languages.
|
In JavaScript `null` is not a "reference to a non-existing object" or a "null pointer" like in some other languages.
|
||||||
|
|
||||||
It's just a special value which has the sense of "nothing" or "value unknown".
|
It's just a special value which has the sense of "nothing", "empty" or "value unknown".
|
||||||
|
|
||||||
The code above states that the `age` is unknown.
|
The code above states that the `age` is unknown or empty for some reason.
|
||||||
|
|
||||||
## The "undefined" value
|
## The "undefined" value
|
||||||
|
|
||||||
The special value `undefined` stands apart. It makes a type of its own, just like `null`.
|
The special value `undefined` stands apart. It makes a type of its own, just like `null`.
|
||||||
|
|
||||||
The sense of `undefined` is "value is not assigned".
|
The sense of `undefined` is "value is not assigned".
|
||||||
|
|
||||||
If a variable is declared, but not assigned, then its value is exactly `undefined`:
|
If a variable is declared, but not assigned, then its value is exactly `undefined`:
|
||||||
|
|
||||||
|
@ -157,6 +157,7 @@ alert( x ); // "undefined"
|
||||||
|
|
||||||
...But it's not recommended to do that. Normally, we use `null` to write an "empty" or an "unknown" value into the variable, and `undefined` is only used for checks, to see if the variable is assigned or similar.
|
...But it's not recommended to do that. Normally, we use `null` to write an "empty" or an "unknown" value into the variable, and `undefined` is only used for checks, to see if the variable is assigned or similar.
|
||||||
|
|
||||||
|
|
||||||
## Objects
|
## Objects
|
||||||
|
|
||||||
The `object` type is special.
|
The `object` type is special.
|
||||||
|
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
|
@ -1,4 +1,4 @@
|
||||||
# Function expressions
|
# Function expressions and more
|
||||||
|
|
||||||
In JavaScript, a function is a value.
|
In JavaScript, a function is a value.
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ This syntax is called a "Function Declaration".
|
||||||
```js
|
```js
|
||||||
let sayHi = function() {
|
let sayHi = function() {
|
||||||
alert( "Hello" );
|
alert( "Hello" );
|
||||||
}
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
The latter syntax is called a "Function Expression".
|
The latter syntax is called a "Function Expression".
|
||||||
|
@ -70,7 +70,7 @@ That's what happens above in detail:
|
||||||
Note, that we could also have used a Function Expression to declare `sayHi`, in the first line:
|
Note, that we could also have used a Function Expression to declare `sayHi`, in the first line:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
let sayHi = function() { ... }
|
let sayHi = function() { ... };
|
||||||
|
|
||||||
let func = sayHi;
|
let func = sayHi;
|
||||||
// ...
|
// ...
|
||||||
|
@ -78,6 +78,27 @@ let func = sayHi;
|
||||||
|
|
||||||
Everything would work the same. Even more obvious what's going on, right?
|
Everything would work the same. Even more obvious what's going on, right?
|
||||||
|
|
||||||
|
|
||||||
|
````smart header="Why semicolon?"
|
||||||
|
There might be a question, why Function Expression has a semicolon `;` at the end, and Function Declaration does not:
|
||||||
|
|
||||||
|
```js
|
||||||
|
function sayHi() {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
let sayHi = function() {
|
||||||
|
// ...
|
||||||
|
}*!*;*/!*
|
||||||
|
```
|
||||||
|
|
||||||
|
The answer is simple:
|
||||||
|
- There's no need in `;` at the end of code blocks and syntax structures that use them like `if { ... }`, `for { }`, `function f { }` and alike.
|
||||||
|
- A Function Expression appears in the context of the statement: `let sayHi = value;`. It's not a code block. The semicolon `;` is recommended at the end of statements, no matter what is the `value`. So the semicolon here is not related to Function Expression itself in any way, it just terminates the statement.
|
||||||
|
````
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Function is an object
|
## Function is an object
|
||||||
|
|
||||||
Every value in Javascript has the type. What type of value is a function?
|
Every value in Javascript has the type. What type of value is a function?
|
||||||
|
@ -135,7 +156,6 @@ A function can be perceived as an *action*.
|
||||||
We can copy it between variables and run when we want. We can even add properties to it if we wish.
|
We can copy it between variables and run when we want. We can even add properties to it if we wish.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## Function Expression as a method
|
## Function Expression as a method
|
||||||
|
|
||||||
Now let's step back and reconsider. We have two ways of declaring a function. Do we really need both? What's so good about Function Expressions that makes it useful?
|
Now let's step back and reconsider. We have two ways of declaring a function. Do we really need both? What's so good about Function Expressions that makes it useful?
|
||||||
|
@ -271,6 +291,7 @@ let sayHi = function(name) { // (*) no magic any more
|
||||||
|
|
||||||
Function Expressions are created when the execution reaches them. That would happen only in the line `(*)`. Too late.
|
Function Expressions are created when the execution reaches them. That would happen only in the line `(*)`. Too late.
|
||||||
|
|
||||||
|
|
||||||
### Function Declaration in a block
|
### Function Declaration in a block
|
||||||
|
|
||||||
When Function Declaration is made within a code block, it is visible everywhere inside that block. But not outside of it.
|
When Function Declaration is made within a code block, it is visible everywhere inside that block. But not outside of it.
|
||||||
|
@ -389,6 +410,112 @@ It's also a little bit easier to look up Function Declarations in the code, they
|
||||||
But if a Function Declaration does not fit for some reason (we've seen an example), then a Function Expression should be used.
|
But if a Function Declaration does not fit for some reason (we've seen an example), then a Function Expression should be used.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Named Function Expression
|
||||||
|
|
||||||
|
Now the feature that exists for a Function Expression, but not for Function Declaration, and hence may be a reason to use them.
|
||||||
|
|
||||||
|
**We can specify an "internal" name for a Function Expression.**
|
||||||
|
|
||||||
|
Compare these two variants:
|
||||||
|
|
||||||
|
```js
|
||||||
|
let sayHi = function() { // (1)
|
||||||
|
alert('Hello');
|
||||||
|
};
|
||||||
|
|
||||||
|
let sayHi = function *!*func*/!*() { // (2)
|
||||||
|
alert('Hello');
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Both create a function and put it into the variable `sayHi`. And usually we don't specify anything in the `function()`, so the 1st variant works fine.
|
||||||
|
|
||||||
|
Although the function can move to another variable. As we've seen it's easy to copy a function and maybe replace the previous value with something else:
|
||||||
|
|
||||||
|
```js run
|
||||||
|
let sayHi = function() {
|
||||||
|
alert('Hello');
|
||||||
|
};
|
||||||
|
|
||||||
|
// oh maybe that's better?
|
||||||
|
let oldSayHi = sayHi; // keep the old variant here
|
||||||
|
sayHi = function() { // replace with a newer one
|
||||||
|
alert("What's up dude?");
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
oldSayHi(); // Hello
|
||||||
|
sayHi(); // What's up dude?
|
||||||
|
```
|
||||||
|
|
||||||
|
The problem may occur if a function references *itself* from inside. It happens when the function wants to access its properties (`sayHi.counter` in the example above), or sometimes it wants to call itself one more time.
|
||||||
|
|
||||||
|
|
||||||
|
But if the function has moved, then the old name is not relevant any move!
|
||||||
|
|
||||||
|
|
||||||
|
Let's see:
|
||||||
|
|
||||||
|
```js run
|
||||||
|
// create a function
|
||||||
|
let sayHi = function() {
|
||||||
|
sayHi.counter++;
|
||||||
|
alert('Hi ' + sayHi.counter);
|
||||||
|
};
|
||||||
|
sayHi.counter = 0;
|
||||||
|
|
||||||
|
// move it
|
||||||
|
let movedSayHi = sayHi;
|
||||||
|
|
||||||
|
// overwrite the old name to make things more obvious
|
||||||
|
sayHi = null;
|
||||||
|
|
||||||
|
*!*
|
||||||
|
movedSayHi(); // Error: Cannot read property 'counter' of null
|
||||||
|
*/!*
|
||||||
|
```
|
||||||
|
|
||||||
|
The optional `name` which we can put into `function name()` is exactly meant to solve this kind of problems.
|
||||||
|
|
||||||
|
- It is only visible from inside the function.
|
||||||
|
- It always references the current function.
|
||||||
|
|
||||||
|
Let's use it to fix the code:
|
||||||
|
|
||||||
|
```js run
|
||||||
|
// now with the internal name "say"
|
||||||
|
*!*
|
||||||
|
let sayHi = function say() {
|
||||||
|
say.counter++;
|
||||||
|
alert('Hi ' + say.counter); // and use it everywhere inside
|
||||||
|
};
|
||||||
|
*/!*
|
||||||
|
sayHi.counter = 0;
|
||||||
|
|
||||||
|
let movedSayHi = sayHi;
|
||||||
|
|
||||||
|
sayHi = null;
|
||||||
|
|
||||||
|
movedSayHi(); // Hi 1
|
||||||
|
movedSayHi(); // Hi 2 (works)
|
||||||
|
|
||||||
|
alert(say); // Error (say is undefined, that's normal)
|
||||||
|
```
|
||||||
|
|
||||||
|
Please note that:
|
||||||
|
|
||||||
|
- The name `say` references the current function no matter where it is. That's why it works.
|
||||||
|
- The name `say` exists only inside the function. The last line demonstrates that.
|
||||||
|
|
||||||
|
So the outer code still uses `sayHi` (or `movedSayHi` later). It doesn't see `say`, actually it doesn't need to see it. The `say` is an "internal function name", how it calls itself privately.
|
||||||
|
|
||||||
|
A Function Expression with a name is called *Named Function Expression*.
|
||||||
|
|
||||||
|
The "internal name" feature described here is only available for Function Expressions, not to Function Declarations. For Function Declarations, the `function name()` behaves like `sayHi`, there's just no syntax possibility to add a one more "internal" name for them.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Arrow functions [#arrow-functions]
|
## Arrow functions [#arrow-functions]
|
||||||
|
|
||||||
Enough with the complexities for now. Let's relax with another syntax of functions that can make our code shorter.
|
Enough with the complexities for now. Let's relax with another syntax of functions that can make our code shorter.
|
||||||
|
@ -504,11 +631,14 @@ It is used in very specific cases, like when we receive the code from the server
|
||||||
- Functions are values. They can be assigned, copied or declared in any place of the code.
|
- Functions are values. They can be assigned, copied or declared in any place of the code.
|
||||||
- If the function is declared as a separate statement, in the main code flow -- that's called a Function Declaration.
|
- If the function is declared as a separate statement, in the main code flow -- that's called a Function Declaration.
|
||||||
- If the function is created as a part of an expression -- it's a Function Expression.
|
- If the function is created as a part of an expression -- it's a Function Expression.
|
||||||
- Function Declarations are processed before the code block is executed. They are visible everywhere in the block. Or in the whole script if not enclosed in a block.
|
- Function Declarations are processed before the code block is executed. They are visible everywhere in the block (or the script).
|
||||||
- Function Expressions are created when the execution flow reaches them.
|
- Function Expressions are created when the execution flow reaches them.
|
||||||
|
- Function Expressions allow to specify an optional name for internal needs (Named Function Expression).
|
||||||
|
|
||||||
|
|
||||||
If we simple want to create a function, then in most cases Function Declaration is preferable. Novice programmers sometimes overuse Function Expression by creating many functions with `let func = function()`, but compare, which code is more readable:
|
If we simple want to create a function, then in most cases Function Declaration is preferable.
|
||||||
|
|
||||||
|
Novice programmers sometimes tend to overuse Function Expression by creating many functions with `let func = function()`, but compare, which code is more readable:
|
||||||
|
|
||||||
```js no-beautify
|
```js no-beautify
|
||||||
let f = function() { /* expression */ }
|
let f = function() { /* expression */ }
|
||||||
|
@ -518,12 +648,14 @@ function f() { /* declaration */ }
|
||||||
|
|
||||||
Function Declaration is shorter and more obvious. The additional bonus -- it can be called before the actual declaration.
|
Function Declaration is shorter and more obvious. The additional bonus -- it can be called before the actual declaration.
|
||||||
|
|
||||||
**Use Function Expression to write elegant code when the function must be created at-place, inside another expression or when Function Declaration doesn't fit well for the task.**
|
**Use Function Expression only when Function Declaration does not fit the task.**
|
||||||
|
|
||||||
|
We've seen a couple of examples of that in the chapter. And will see more in the future.
|
||||||
|
|
||||||
We also touched two other ways to create a function:
|
We also touched two other ways to create a function:
|
||||||
|
|
||||||
- Arrow functions are handy for one-liners. The come in two flavours:
|
- Arrow functions are handy for one-liners. The come in two flavours:
|
||||||
1. Without figure brackets: `(...args) => expression` -- returns the evaluated `expression.
|
1. Without figure brackets: `(...args) => expression` -- returns the evaluated `expression`.
|
||||||
2. With brackets: `(...args) => { body }` -- need an explicit `return` statement to return something, but can be more complex.
|
2. With brackets: `(...args) => { body }` -- need an explicit `return` statement to return something, but can be more complex.
|
||||||
|
|
||||||
- `new Function(args, body)`
|
- `new Function(args, body)`
|
663
1-js/2-first-steps/20-object-methods/article.md
Normal file
663
1-js/2-first-steps/20-object-methods/article.md
Normal file
|
@ -0,0 +1,663 @@
|
||||||
|
# Function expressions and more
|
||||||
|
|
||||||
|
In JavaScript, a function is a value.
|
||||||
|
|
||||||
|
We can declare it as we did before:
|
||||||
|
|
||||||
|
```js
|
||||||
|
function sayHi() {
|
||||||
|
alert( "Hello" );
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This syntax is called a "Function Declaration".
|
||||||
|
|
||||||
|
...But there is another way of creating a function:
|
||||||
|
|
||||||
|
```js
|
||||||
|
let sayHi = function() {
|
||||||
|
alert( "Hello" );
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
The latter syntax is called a "Function Expression".
|
||||||
|
|
||||||
|
The meaning of these code samples is the same: "create a function and put it into the variable `sayHi`".
|
||||||
|
|
||||||
|
Let's stress: a function is not a "magical language structure", but a kind of value. Both syntaxes mean the same: create a special "function" value and put it into the variable.
|
||||||
|
|
||||||
|
No matter, how the function is defined -- it's just a value, stored in the variable `sayHi`.
|
||||||
|
|
||||||
|
We can even print out that value using `alert`:
|
||||||
|
|
||||||
|
```js run
|
||||||
|
function sayHi() {
|
||||||
|
alert( "Hello" );
|
||||||
|
}
|
||||||
|
|
||||||
|
*!*
|
||||||
|
alert( sayHi ); // shows the function code
|
||||||
|
*/!*
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that there are no brackets after `sayHi` in the last line, because we do not intend to run the function. There are programming languages where any mention of a function name causes it's call, but JavaScript is not like that. In JavaScript, a function is a value and we can deal with that as a value. The code above shows its string representation, that is its source code.
|
||||||
|
|
||||||
|
It is a special value of course, in the sense that we can call it using brackets: `"sayHi()"`.
|
||||||
|
|
||||||
|
But it's still a value. So we can work with it like with other kinds of values.
|
||||||
|
|
||||||
|
We can copy a function to another variable:
|
||||||
|
|
||||||
|
```js run no-beautify
|
||||||
|
function sayHi() { // (1) create
|
||||||
|
alert( "Hello" );
|
||||||
|
}
|
||||||
|
|
||||||
|
let func = sayHi; // (2) copy
|
||||||
|
|
||||||
|
func(); // Hello // (3) call the copy (it works)!
|
||||||
|
sayHi(); // Hello // this works too (why wouldn't it)
|
||||||
|
```
|
||||||
|
|
||||||
|
That's what happens above in detail:
|
||||||
|
|
||||||
|
1. Function Declaration `(1)` creates the function and puts it into the variable named `sayHi`.
|
||||||
|
2. Line `(2)` copies it into variable `func`.
|
||||||
|
|
||||||
|
Please note again: there are no brackets after `sayHi`. If they were, then `func = sayHi()` would write *the result of the call* `sayHi()` into `func`, not *the function* `sayHi` itself.
|
||||||
|
3. Now the function can be called both as `sayHi()` and `func()`.
|
||||||
|
|
||||||
|
Note, that we could also have used a Function Expression to declare `sayHi`, in the first line:
|
||||||
|
|
||||||
|
```js
|
||||||
|
let sayHi = function() { ... };
|
||||||
|
|
||||||
|
let func = sayHi;
|
||||||
|
// ...
|
||||||
|
```
|
||||||
|
|
||||||
|
Everything would work the same. Even more obvious what's going on, right?
|
||||||
|
|
||||||
|
|
||||||
|
````smart header="Why semicolon?"
|
||||||
|
There might be a question, why Function Expression has a semicolon `;` at the end, and Function Declaration does not:
|
||||||
|
|
||||||
|
```js
|
||||||
|
function sayHi() {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
let sayHi = function() {
|
||||||
|
// ...
|
||||||
|
}*!*;*/!*
|
||||||
|
```
|
||||||
|
|
||||||
|
The answer is simple:
|
||||||
|
- There's no need in `;` at the end of code blocks and syntax structures that use them like `if { ... }`, `for { }`, `function f { }` and alike.
|
||||||
|
- A Function Expression appears in the context of the statement: `let sayHi = value;`. It's not a code block. The semicolon `;` is recommended at the end of statements, no matter what is the `value`. So the semicolon here is not related to Function Expression itself in any way, it just terminates the statement.
|
||||||
|
````
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Function is an object
|
||||||
|
|
||||||
|
Every value in Javascript has the type. What type of value is a function?
|
||||||
|
|
||||||
|
In Javascript, a function is an object.
|
||||||
|
|
||||||
|
For example, all functions have property `name` (function name) and `length` (number of arguments):
|
||||||
|
|
||||||
|
```js run
|
||||||
|
function sayHi() {
|
||||||
|
alert("Hi");
|
||||||
|
}
|
||||||
|
|
||||||
|
alert( sayHi.name ); // sayHi
|
||||||
|
alert( sayHi.length ); // 0
|
||||||
|
```
|
||||||
|
|
||||||
|
We can add our own properties to it as well:
|
||||||
|
|
||||||
|
```js run
|
||||||
|
function sayHi() {
|
||||||
|
alert("Hi");
|
||||||
|
|
||||||
|
*!*
|
||||||
|
// let's count how many times we run
|
||||||
|
sayHi.counter++;
|
||||||
|
*/!*
|
||||||
|
}
|
||||||
|
sayHi.counter = 0; // initial value
|
||||||
|
|
||||||
|
sayHi(); // Hi
|
||||||
|
sayHi(); // Hi
|
||||||
|
|
||||||
|
alert( `Called ${sayHi.counter} times` ); // Called 2 times
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```warn header="A property is not a variable"
|
||||||
|
A property assigned to a function like `sayHi.counter = 0` does *not* define a local variable `counter` inside it. In other words, a property `sayHi.counter` and `let counter` inside the function (if we have it) are two unrelated things.
|
||||||
|
|
||||||
|
We can treat a function as an object for convenience, store properties in it, that has no effect on its execution.
|
||||||
|
```
|
||||||
|
|
||||||
|
There are many well-known Javascript libraries that make a great use of custom function properties.
|
||||||
|
|
||||||
|
They create a "main" function and attach many other "helper" functions to it. For instance, the [jquery](https://jquery.com) library creates a function named `$`. The [lodash](https://lodash.com) library creates a function `_`. And then adds `_.clone`, `_.keyBy` and other properties to (see the [docs](https://lodash.com/docs) when you want learn more about them).
|
||||||
|
|
||||||
|
So, a function can do a useful job by itself and also carry a bunch of other functionality in properties.
|
||||||
|
|
||||||
|
```smart header="A function is a value representing an \"action\""
|
||||||
|
Regular values like strings or numbers represent the *data*.
|
||||||
|
|
||||||
|
A function can be perceived as an *action*.
|
||||||
|
|
||||||
|
We can copy it between variables and run when we want. We can even add properties to it if we wish.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Function Expression as a method
|
||||||
|
|
||||||
|
Now let's step back and reconsider. We have two ways of declaring a function. Do we really need both? What's so good about Function Expressions that makes it useful?
|
||||||
|
|
||||||
|
Actually, yes, we do. For example, we can assign functions to object properties using function expressions.
|
||||||
|
|
||||||
|
As we remember from the chapter <info:types>, objects are data structures meant to store collections of data. Most often, we create objects to represent entities of the real world, like users, goodies and so on:
|
||||||
|
|
||||||
|
```js
|
||||||
|
let user = {
|
||||||
|
name: "John",
|
||||||
|
age: 30
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
In the real world, a user can `act`: to select something from the shopping cart, to login, to logout etc. For the start, let's teach him to say hello:
|
||||||
|
|
||||||
|
```js run
|
||||||
|
let user = {
|
||||||
|
name: "John",
|
||||||
|
age: 30
|
||||||
|
};
|
||||||
|
|
||||||
|
*!*
|
||||||
|
user.sayHi = function() {
|
||||||
|
alert("Hello!");
|
||||||
|
};
|
||||||
|
*/!*
|
||||||
|
|
||||||
|
user.sayHi(); // Hello!
|
||||||
|
```
|
||||||
|
|
||||||
|
You see? We've just used a Function Expression to create the function and assign it to the property `user.sayHi` of the object.
|
||||||
|
|
||||||
|
Then we can call it any time. The user now can speak!
|
||||||
|
|
||||||
|
That is how a so-called "object-oriented code" is written. We make objects which reflect entities of the real world: like a user, or a document, or a button that is clickable etc.
|
||||||
|
|
||||||
|
An object stores its data in regular properties (like `name`, `age` etc) and has functions to express itself. Function properties are usually called *methods*. So, one can say that in the code above "`sayHi` is a method of the object `user`".
|
||||||
|
|
||||||
|
Of course we could use a Function Declaration for the same purpose:
|
||||||
|
|
||||||
|
```js run
|
||||||
|
let user = {
|
||||||
|
// ...
|
||||||
|
};
|
||||||
|
|
||||||
|
*!*
|
||||||
|
function sayHi() {
|
||||||
|
alert("Hello!");
|
||||||
|
};
|
||||||
|
|
||||||
|
user.sayHi = sayHi;
|
||||||
|
*/!*
|
||||||
|
|
||||||
|
user.sayHi(); // Hello!
|
||||||
|
```
|
||||||
|
|
||||||
|
That would also work, but is longer. Also we get an "extra" function `sayHi` outside of the `user` object. Here we don't want it.
|
||||||
|
|
||||||
|
```smart header="Object-oriented programming"
|
||||||
|
When we write our code using objects to represent entities, that's called an [object-oriented programming](https://en.wikipedia.org/wiki/Object-oriented_programming), in short: "OOP".
|
||||||
|
|
||||||
|
As of now, we already know how to create an object with `{...}` and how to store data and add a method to it. But we will study it in detail later when we get enough familarity with basic functions of the language.
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Function Expression vs Function Declaration
|
||||||
|
|
||||||
|
Let's formulate the key differences between Function Declarations and Expressions.
|
||||||
|
|
||||||
|
Here's the syntax distinction between these two.
|
||||||
|
|
||||||
|
- *Function Declaration:* a function, declared as a separate statement, in the main code flow.
|
||||||
|
|
||||||
|
```js
|
||||||
|
// Function Declaration
|
||||||
|
function sum(a, b) {
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- *Function Expression:* a function, created in the context of an expression.
|
||||||
|
|
||||||
|
Here the function is created in the context of an "assignment expression =":
|
||||||
|
```js
|
||||||
|
// Function Expression
|
||||||
|
let sum = function(a, b) {
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Another difference is when they are actualy created by the JavaScript engine.
|
||||||
|
|
||||||
|
**Function Expressions are created when the execution reaches them and are usable since then.**
|
||||||
|
|
||||||
|
That's kind of obvious. Once the execution flow passes to the right side of the assignment `let sum = function` -- here we go, the function is created and can be used (assigned, called etc) from now on.
|
||||||
|
|
||||||
|
Function Declarations are different.
|
||||||
|
|
||||||
|
**Function Declarations are usable in the whole script/code block.**
|
||||||
|
|
||||||
|
In other words, when JavaScript *prepares* to run the script/code block, it first looks for Function Declarations in it and creates the functions. We can think of it as an "initialization stage".
|
||||||
|
|
||||||
|
And after all Function Declarations are processed, it actually executes it.
|
||||||
|
|
||||||
|
As a natural effect, a function declared as Function Declaration can be called earlier than it is defined.
|
||||||
|
|
||||||
|
For example, this works:
|
||||||
|
|
||||||
|
```js run refresh untrusted
|
||||||
|
*!*
|
||||||
|
sayHi("John"); // Hello, John
|
||||||
|
*/!*
|
||||||
|
|
||||||
|
function sayHi(name) {
|
||||||
|
alert( `Hello, ${name}` );
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Function Declaration `sayHi` is created when JavaScript is preparing to start the script and is visible everywhere in it.
|
||||||
|
|
||||||
|
...And if there were Function Expression, then it wouldn't work:
|
||||||
|
|
||||||
|
```js run refresh untrusted
|
||||||
|
*!*
|
||||||
|
sayHi("John"); // error!
|
||||||
|
*/!*
|
||||||
|
|
||||||
|
let sayHi = function(name) { // (*) no magic any more
|
||||||
|
alert( `Hello, ${name}` );
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Function Expressions are created when the execution reaches them. That would happen only in the line `(*)`. Too late.
|
||||||
|
|
||||||
|
|
||||||
|
### Function Declaration in a block
|
||||||
|
|
||||||
|
When Function Declaration is made within a code block, it is visible everywhere inside that block. But not outside of it.
|
||||||
|
|
||||||
|
Sometimes that's handy to declare a local function, only needed in that only block. But can also be a problem.
|
||||||
|
|
||||||
|
For instance, let's imagine that we need to declare a function `welcome()` depending on the `age` variable that we get in run time. And then use it sometimes later.
|
||||||
|
|
||||||
|
The code below doesn't work:
|
||||||
|
|
||||||
|
```js run
|
||||||
|
let age = prompt("What is your age?", 18);
|
||||||
|
|
||||||
|
if (age < 18) {
|
||||||
|
|
||||||
|
function welcome() {
|
||||||
|
alert("Hello!");
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
function welcome() {
|
||||||
|
alert("Greetings!");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
*!*
|
||||||
|
welcome(); // Error: welcome is not defined
|
||||||
|
*/!*
|
||||||
|
```
|
||||||
|
|
||||||
|
The Function Declaration is visible only inside the code block where it resides.
|
||||||
|
|
||||||
|
We can call it from within the block, but not from outside:
|
||||||
|
|
||||||
|
```js run
|
||||||
|
let age = 16; // take 16 as an example
|
||||||
|
|
||||||
|
if (age < 18) {
|
||||||
|
*!*
|
||||||
|
welcome(); // \ (runs)
|
||||||
|
*/!*
|
||||||
|
// |
|
||||||
|
function welcome() { // |
|
||||||
|
alert("Hello!"); // | Function Declaration is available
|
||||||
|
} // | everywhere in the block when it's declared
|
||||||
|
// |
|
||||||
|
*!*
|
||||||
|
welcome(); // / (runs)
|
||||||
|
*/!*
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// \
|
||||||
|
function welcome() { // |
|
||||||
|
alert("Greetings!"); // | in this if test we don't enter this block,
|
||||||
|
} // | so this "welcome" is never created
|
||||||
|
// /
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we're out of figure brackets,
|
||||||
|
// so we can not see Function Declarations made inside of them.
|
||||||
|
|
||||||
|
*!*
|
||||||
|
welcome(); // Error: welcome is not defined
|
||||||
|
*/!*
|
||||||
|
```
|
||||||
|
|
||||||
|
What can we do to make `welcome` visible outside?
|
||||||
|
|
||||||
|
The right thing would be to use a Function Expression and assign `welcome` to the variable which is declared outside of `if` and has the proper visibility:
|
||||||
|
|
||||||
|
```js run
|
||||||
|
let age = prompt("What is your age?", 18);
|
||||||
|
|
||||||
|
let welcome;
|
||||||
|
|
||||||
|
if (age < 18) {
|
||||||
|
|
||||||
|
welcome = function() {
|
||||||
|
alert("Hello!");
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
welcome = function() {
|
||||||
|
alert("Greetings!");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
*!*
|
||||||
|
welcome(); // ok now
|
||||||
|
*/!*
|
||||||
|
```
|
||||||
|
|
||||||
|
Or we could go on to simplify it even further using a question mark operator `?`:
|
||||||
|
|
||||||
|
```js run
|
||||||
|
let age = prompt("What is your age?", 18);
|
||||||
|
|
||||||
|
let welcome = (age < 18) ?
|
||||||
|
function() { alert("Hello!"); } : function() { alert("Greetings!"); }
|
||||||
|
|
||||||
|
*!*
|
||||||
|
welcome(); // ok now
|
||||||
|
*/!*
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```smart header="What to choose: a Declaration or an Expression?"
|
||||||
|
As a rule of thumb, a Function Declaration is prefered. It gives more freedom in how to organize our code, because we can call it both above and below.
|
||||||
|
|
||||||
|
It's also a little bit easier to look up Function Declarations in the code, they increase readability of the code.
|
||||||
|
|
||||||
|
But if a Function Declaration does not fit for some reason (we've seen an example), then a Function Expression should be used.
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Named Function Expression
|
||||||
|
|
||||||
|
Now the feature that exists for a Function Expression, but not for Function Declaration, and hence may be a reason to use them.
|
||||||
|
|
||||||
|
**We can specify an "internal" name for a Function Expression.**
|
||||||
|
|
||||||
|
Compare these two variants:
|
||||||
|
|
||||||
|
```js
|
||||||
|
let sayHi = function() { // (1)
|
||||||
|
alert('Hello');
|
||||||
|
};
|
||||||
|
|
||||||
|
let sayHi = function *!*func*/!*() { // (2)
|
||||||
|
alert('Hello');
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Both create a function and put it into the variable `sayHi`. And usually we don't specify anything in the `function()`, so the 1st variant works fine.
|
||||||
|
|
||||||
|
Although the function can move to another variable. As we've seen it's easy to copy a function and maybe replace the previous value with something else:
|
||||||
|
|
||||||
|
```js run
|
||||||
|
let sayHi = function() {
|
||||||
|
alert('Hello');
|
||||||
|
};
|
||||||
|
|
||||||
|
// oh maybe that's better?
|
||||||
|
let oldSayHi = sayHi; // keep the old variant here
|
||||||
|
sayHi = function() { // replace with a newer one
|
||||||
|
alert("What's up dude?");
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
oldSayHi(); // Hello
|
||||||
|
sayHi(); // What's up dude?
|
||||||
|
```
|
||||||
|
|
||||||
|
The problem may occur if a function references *itself* from inside. It happens when the function wants to access its properties (`sayHi.counter` in the example above), or sometimes it wants to call itself one more time.
|
||||||
|
|
||||||
|
|
||||||
|
But if the function has moved, then the old name is not relevant any move!
|
||||||
|
|
||||||
|
|
||||||
|
Let's see:
|
||||||
|
|
||||||
|
```js run
|
||||||
|
// create a function
|
||||||
|
let sayHi = function() {
|
||||||
|
sayHi.counter++;
|
||||||
|
alert('Hi ' + sayHi.counter);
|
||||||
|
};
|
||||||
|
sayHi.counter = 0;
|
||||||
|
|
||||||
|
// move it
|
||||||
|
let movedSayHi = sayHi;
|
||||||
|
|
||||||
|
// overwrite the old name to make things more obvious
|
||||||
|
sayHi = null;
|
||||||
|
|
||||||
|
*!*
|
||||||
|
movedSayHi(); // Error: Cannot read property 'counter' of null
|
||||||
|
*/!*
|
||||||
|
```
|
||||||
|
|
||||||
|
The optional `name` which we can put into `function name()` is exactly meant to solve this kind of problems.
|
||||||
|
|
||||||
|
- It is only visible from inside the function.
|
||||||
|
- It always references the current function.
|
||||||
|
|
||||||
|
Let's use it to fix the code:
|
||||||
|
|
||||||
|
```js run
|
||||||
|
// now with the internal name "say"
|
||||||
|
*!*
|
||||||
|
let sayHi = function say() {
|
||||||
|
say.counter++;
|
||||||
|
alert('Hi ' + say.counter); // and use it everywhere inside
|
||||||
|
};
|
||||||
|
*/!*
|
||||||
|
sayHi.counter = 0;
|
||||||
|
|
||||||
|
let movedSayHi = sayHi;
|
||||||
|
|
||||||
|
sayHi = null;
|
||||||
|
|
||||||
|
movedSayHi(); // Hi 1
|
||||||
|
movedSayHi(); // Hi 2 (works)
|
||||||
|
|
||||||
|
alert(say); // Error (say is undefined, that's normal)
|
||||||
|
```
|
||||||
|
|
||||||
|
Please note that:
|
||||||
|
|
||||||
|
- The name `say` references the current function no matter where it is. That's why it works.
|
||||||
|
- The name `say` exists only inside the function. The last line demonstrates that.
|
||||||
|
|
||||||
|
So the outer code still uses `sayHi` (or `movedSayHi` later). It doesn't see `say`, actually it doesn't need to see it. The `say` is an "internal function name", how it calls itself privately.
|
||||||
|
|
||||||
|
A Function Expression with a name is called *Named Function Expression*.
|
||||||
|
|
||||||
|
The "internal name" feature described here is only available for Function Expressions, not to Function Declarations. For Function Declarations, the `function name()` behaves like `sayHi`, there's just no syntax possibility to add a one more "internal" name for them.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Arrow functions [#arrow-functions]
|
||||||
|
|
||||||
|
Enough with the complexities for now. Let's relax with another syntax of functions that can make our code shorter.
|
||||||
|
|
||||||
|
Arrow functions act like a function expression, but look a little bit differently.
|
||||||
|
|
||||||
|
The syntax is:
|
||||||
|
|
||||||
|
```js
|
||||||
|
let func = (arg1, arg2, ...argN) => expression
|
||||||
|
```
|
||||||
|
|
||||||
|
...This creates a function `func` that has arguments `arg1..argN`, evaludates the `expression` on the right side with their use and returns its result.
|
||||||
|
|
||||||
|
In other words, it's roughly the same as:
|
||||||
|
|
||||||
|
```js
|
||||||
|
let func = function(arg1, arg2, ...argN) {
|
||||||
|
return expression;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Let's see the example:
|
||||||
|
|
||||||
|
```js run
|
||||||
|
let sum = (a, b) => a + b;
|
||||||
|
|
||||||
|
alert( sum(1, 2) ); // 3
|
||||||
|
```
|
||||||
|
|
||||||
|
Here the function is same as:
|
||||||
|
|
||||||
|
```js
|
||||||
|
let sum = function(a, b) {
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If we have only one argument, then brackets can be omitted, making that even shorter:
|
||||||
|
|
||||||
|
```js run
|
||||||
|
// same as
|
||||||
|
// let double = function(n) { return n*2 }
|
||||||
|
let double = n => n*2;
|
||||||
|
|
||||||
|
alert( double(3) ); // 6
|
||||||
|
```
|
||||||
|
|
||||||
|
If there are no arguments, we can put empty brackets. For instance, here's the rewritten example with `welcome()`:
|
||||||
|
|
||||||
|
```js run
|
||||||
|
let age = prompt("What is your age?", 18);
|
||||||
|
|
||||||
|
let welcome = (age < 18) ? () => alert('Hello') : () => alert("Greetings!");
|
||||||
|
|
||||||
|
welcome(); // ok now
|
||||||
|
```
|
||||||
|
|
||||||
|
The syntax may appear unfamiliar and not very readable at first, but that quickly changes as the eyes get used to the structure.
|
||||||
|
|
||||||
|
Arrow functions are very convenient for simple one-line actions, when we're just lazy to write many words.
|
||||||
|
|
||||||
|
```smart header="Multiline arrow functions"
|
||||||
|
|
||||||
|
The examples above took arguments from the left of `=>` and evaluate the right-side expression with them.
|
||||||
|
|
||||||
|
Sometimes we need something a little bit more complex, like multiple expressions or statements. It is also possible, but we should enclose them in figure brackets. Then use a normal `return` within them.
|
||||||
|
|
||||||
|
Like this:
|
||||||
|
|
||||||
|
```js run
|
||||||
|
let sum = (a, b) => { // the figure bracket opens a multiline function
|
||||||
|
let result = a + b;
|
||||||
|
*!*
|
||||||
|
return result; // if we use figure brackets, must use return
|
||||||
|
*/!*
|
||||||
|
}
|
||||||
|
|
||||||
|
alert( sum(1, 2) ); // 3
|
||||||
|
```
|
||||||
|
|
||||||
|
```smart header="More to come"
|
||||||
|
Here we praised arrow functions for shortness. But that's not all! Arrow functions have other interesting features in them. We'll return to them later and see where else they shine.
|
||||||
|
|
||||||
|
As for now, we can already use them for one-line actions.
|
||||||
|
```
|
||||||
|
|
||||||
|
## new Function
|
||||||
|
|
||||||
|
And the last syntax for the functions:
|
||||||
|
```js
|
||||||
|
let func = new Function('a, b', 'return a + b');
|
||||||
|
```
|
||||||
|
|
||||||
|
The major difference is that it creates a function literally from a string, at run time. See, both arguments are strings. The first one lists the arguments, while the second one is the function body.
|
||||||
|
|
||||||
|
All previous declarations required us, programmers, to write the function code in the script.
|
||||||
|
|
||||||
|
But `new Function` allows to turn any string into a function, for example we can receive a new function from the server and then execute it:
|
||||||
|
|
||||||
|
```js
|
||||||
|
let str = ... receive the code from the server dynamically ...
|
||||||
|
|
||||||
|
let func = new Function('', str);
|
||||||
|
func();
|
||||||
|
```
|
||||||
|
|
||||||
|
It is used in very specific cases, like when we receive the code from the server, or to dynamically compile a function from a template. The need for such uses arises at advanced stages of the development.
|
||||||
|
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
- Functions are values. They can be assigned, copied or declared in any place of the code.
|
||||||
|
- If the function is declared as a separate statement, in the main code flow -- that's called a Function Declaration.
|
||||||
|
- If the function is created as a part of an expression -- it's a Function Expression.
|
||||||
|
- Function Declarations are processed before the code block is executed. They are visible everywhere in the block (or the script).
|
||||||
|
- Function Expressions are created when the execution flow reaches them.
|
||||||
|
- Function Expressions allow to specify an optional name for internal needs (Named Function Expression).
|
||||||
|
|
||||||
|
|
||||||
|
If we simple want to create a function, then in most cases Function Declaration is preferable.
|
||||||
|
|
||||||
|
Novice programmers sometimes tend to overuse Function Expression by creating many functions with `let func = function()`, but compare, which code is more readable:
|
||||||
|
|
||||||
|
```js no-beautify
|
||||||
|
let f = function() { /* expression */ }
|
||||||
|
|
||||||
|
function f() { /* declaration */ }
|
||||||
|
```
|
||||||
|
|
||||||
|
Function Declaration is shorter and more obvious. The additional bonus -- it can be called before the actual declaration.
|
||||||
|
|
||||||
|
**Use Function Expression only when Function Declaration does not fit the task.**
|
||||||
|
|
||||||
|
We've seen a couple of examples of that in the chapter. And will see more in the future.
|
||||||
|
|
||||||
|
We also touched two other ways to create a function:
|
||||||
|
|
||||||
|
- Arrow functions are handy for one-liners. The come in two flavours:
|
||||||
|
1. Without figure brackets: `(...args) => expression` -- returns the evaluated `expression`.
|
||||||
|
2. With brackets: `(...args) => { body }` -- need an explicit `return` statement to return something, but can be more complex.
|
||||||
|
|
||||||
|
- `new Function(args, body)`
|
||||||
|
|
||||||
|
This syntax allows to create a function from a string, that may be composed dynamically during the execution.
|
|
@ -1,8 +1,4 @@
|
||||||
# TODO: Code quality
|
# TODO: Code quality
|
||||||
|
|
||||||
In order to keep the code quality, you need at least:
|
In this chapter we cover common, but very important things. Probably you'll want to reread some chapters later when you learn more about the language and write more code.
|
||||||
<ol>
|
|
||||||
<li>The ability to debug code and fix bugs.</li>
|
|
||||||
<li>Good style code.</li>
|
|
||||||
<li>Test code is desirable - in automatic mode.</li>
|
|
||||||
</ol>
|
|
||||||
|
|
|
@ -2,21 +2,21 @@
|
||||||
|
|
||||||
JavaScript has a built-in object [Date](mdn:js/Date) for date/time management.
|
JavaScript has a built-in object [Date](mdn:js/Date) for date/time management.
|
||||||
|
|
||||||
It contains the date, the time, the timezone, everything.
|
It contains the date, time, timezone -- everything.
|
||||||
|
|
||||||
[cut]
|
[cut]
|
||||||
|
|
||||||
## Creation
|
## Creation
|
||||||
|
|
||||||
To create a new `Date` object use one of the following syntaxes:
|
To create a new `Date` object call `new Date()` with one of the following arguments:
|
||||||
|
|
||||||
|
|
||||||
`new Date()`
|
`new Date()`
|
||||||
: Create a `Date` object for the current date and time:
|
: Without arguments -- create a `Date` object for the current date and time:
|
||||||
|
|
||||||
```js run
|
```js run
|
||||||
let now = new Date();
|
let now = new Date();
|
||||||
alert( now ); // current date/time
|
alert( now ); // shows current date/time
|
||||||
```
|
```
|
||||||
|
|
||||||
`new Date(milliseconds)`
|
`new Date(milliseconds)`
|
||||||
|
@ -32,10 +32,18 @@ To create a new `Date` object use one of the following syntaxes:
|
||||||
alert( Jan02_1970 );
|
alert( Jan02_1970 );
|
||||||
```
|
```
|
||||||
|
|
||||||
The number of milliseconds is called a *timestamp*. It is a lightweight numeric representation of a date. We can always create a date from the timestamp number using `new Date(timestamp)` and get the timestamp from the existing `Date` object using `date.getTime()` (see below).
|
The number of milliseconds that has passed since the beginning of 1970 is called a *timestamp*.
|
||||||
|
|
||||||
|
It is a lightweight numeric representation of a date. We can always create a date from a timestamp using `new Date(timestamp)` and convert the existing `Date` object to a timestamp, there's a `date.getTime()` method (see below).
|
||||||
|
|
||||||
`new Date(datestring)`
|
`new Date(datestring)`
|
||||||
: If there is a single argument -- a string, then it is parsed with the `Date.parse` algorithm (see below).
|
: If there is a single argument and it's a string, then it is parsed with the `Date.parse` algorithm (see below).
|
||||||
|
|
||||||
|
|
||||||
|
```js run
|
||||||
|
let date = new Date("2017-01-26");
|
||||||
|
alert( date ); // Thu Jan 26 2017 ...
|
||||||
|
```
|
||||||
|
|
||||||
`new Date(year, month, date, hours, minutes, seconds, ms)`
|
`new Date(year, month, date, hours, minutes, seconds, ms)`
|
||||||
: Create the date with the given components in the local time zone. Only two first arguments are obligatory.
|
: Create the date with the given components in the local time zone. Only two first arguments are obligatory.
|
||||||
|
@ -147,7 +155,7 @@ alert( today ); // today, 00:00:00 sharp.
|
||||||
|
|
||||||
## Autocorrection
|
## Autocorrection
|
||||||
|
|
||||||
The *autocorrection* -- is a very handy property of the `Date` objects. We can set out-of-range values, and it will auto-adjust itself.
|
The *autocorrection* is a very handy feature of `Date` objects. We can set out-of-range values, and it will auto-adjust itself.
|
||||||
|
|
||||||
For instance:
|
For instance:
|
||||||
|
|
||||||
|
@ -156,7 +164,7 @@ let date = new Date(2013, 0, *!*32*/!*); // 32 Jan 2013 ?!?
|
||||||
alert(date); // ...is 1st Feb 2013!
|
alert(date); // ...is 1st Feb 2013!
|
||||||
```
|
```
|
||||||
|
|
||||||
**Out-of-range date components are distributed around automatically.**
|
**Out-of-range date components are distributed automatically.**
|
||||||
|
|
||||||
Let's say we need to increase the date "28 Feb 2016" by 2 days. It may be "2 Mar" or "1 Mar" in case of a leap-year. We don't need to think about it. Just add 2 days. The `Date` object will do the rest:
|
Let's say we need to increase the date "28 Feb 2016" by 2 days. It may be "2 Mar" or "1 Mar" in case of a leap-year. We don't need to think about it. Just add 2 days. The `Date` object will do the rest:
|
||||||
|
|
||||||
|
|
|
@ -18,16 +18,16 @@ We can imagine it as a cabinet with signed files. Every piece of data is stored
|
||||||
|
|
||||||
## Object literals
|
## Object literals
|
||||||
|
|
||||||
An empty object ("empty cabinet") can be created using one of to syntaxes:
|
An empty object ("empty cabinet") can be created using one of two syntaxes:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
let user = new Object(); // works the same as below
|
let user = new Object(); // "object constructor" syntax
|
||||||
let user = {};
|
let user = {}; // "object literal" syntax
|
||||||
```
|
```
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Usually, the figure brackets `{...}` syntax is used, because it's shorter. It is called an *object literal*.
|
Usually, the figure brackets `{...}` are used, they are more powerful shorter. The declaration is called an *object literal*.
|
||||||
|
|
||||||
We can set properties immediately:
|
We can set properties immediately:
|
||||||
|
|
||||||
|
@ -108,8 +108,19 @@ let obj = {
|
||||||
}
|
}
|
||||||
|
|
||||||
alert( obj["0"] ); // test
|
alert( obj["0"] ); // test
|
||||||
|
alert( obj[0] ); // test (same property)
|
||||||
```
|
```
|
||||||
|
````
|
||||||
|
|
||||||
|
````smart header="Trailing comma"
|
||||||
|
The last property may end with a comma:
|
||||||
|
```js
|
||||||
|
let user = {
|
||||||
|
name: "John",
|
||||||
|
age: 30*!*,*/!*
|
||||||
|
}
|
||||||
|
```
|
||||||
|
That is called a "trailing" or "hanging" comma. Makes it easier to add/move/remove property, because all lines become alike.
|
||||||
````
|
````
|
||||||
|
|
||||||
````smart header="Reserved words are allowed as property names"
|
````smart header="Reserved words are allowed as property names"
|
||||||
|
@ -392,6 +403,7 @@ alert(*!*user.name*/!*); // 'Pete', changes are seen from the "user" reference
|
||||||
|
|
||||||
Quite obvious, if we used one of the keys (`admin`) and changed something inside the cabinet, then if we use another key later (`user`), we find things modified.
|
Quite obvious, if we used one of the keys (`admin`) and changed something inside the cabinet, then if we use another key later (`user`), we find things modified.
|
||||||
|
|
||||||
|
|
||||||
## Cloning objects
|
## Cloning objects
|
||||||
|
|
||||||
What if we need to duplicate an object? Create an independant copy, a clone?
|
What if we need to duplicate an object? Create an independant copy, a clone?
|
||||||
|
|
|
@ -76,6 +76,21 @@ alert( arr[1].name ); // John
|
||||||
arr[3](); // hello
|
arr[3](); // hello
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
````smart header="Trailing comma"
|
||||||
|
An array may end with a comma:
|
||||||
|
```js
|
||||||
|
let fruits = [
|
||||||
|
"Apple",
|
||||||
|
"Orange",
|
||||||
|
"Plum"*!*,*/!*
|
||||||
|
];
|
||||||
|
```
|
||||||
|
|
||||||
|
The "trailing comma" style makes it easier to insert/remove items, because all lines become alike.
|
||||||
|
````
|
||||||
|
|
||||||
|
|
||||||
## Methods pop/push, shift/unshift
|
## Methods pop/push, shift/unshift
|
||||||
|
|
||||||
A [queue](https://en.wikipedia.org/wiki/Queue_(abstract_data_type)) is one of most common uses of an array. In computer science, this means an ordered collection of elements which supports two operations:
|
A [queue](https://en.wikipedia.org/wiki/Queue_(abstract_data_type)) is one of most common uses of an array. In computer science, this means an ordered collection of elements which supports two operations:
|
||||||
|
@ -292,21 +307,12 @@ But that's actually a bad idea. There are potential problems with it:
|
||||||
|
|
||||||
1. The loop `for..in` iterates over *all properties*, not only the numeric ones.
|
1. The loop `for..in` iterates over *all properties*, not only the numeric ones.
|
||||||
|
|
||||||
In the browser as well as in other environments, there are many collections of elements that *look like arrays*. That is, they have `length` and indexes properties, but they have *other non-numeric properties too*, which we usually don't need. The `for..in` loop will list them. If we need to work with arrays and those array-like structures, then these "extra" properties can become a problem.
|
There are so-called "array-like" objects in the browser and in other environments, that *look like arrays*. That is, they have `length` and indexes properties, and can be used in `for..of`, but they have *other non-numeric properties and methods*, which we usually don't need in the loop. The `for..in` will list them. If we need to work with array-like objects, then these "extra" properties can become a problem.
|
||||||
|
|
||||||
2. The `for..in` loop is optimized for generic objects, not arrays, and thus is 10-100 times slower.
|
2. The `for..in` loop is optimized for generic objects, not arrays, and thus is 10-100 times slower.
|
||||||
|
|
||||||
So we should never use `for..in` for arrays.
|
So we should never use `for..in` for arrays.
|
||||||
|
|
||||||
```smart header="Iterable objects"
|
|
||||||
Javascript has a generic concept of *iterables* or, in other words, "array-like" objects.
|
|
||||||
|
|
||||||
An array-like object can have methods and properties of its own, but also implement special methods to be useable in `for..of` loop. We'll often meet such objects and meanwhile will learn to implement iterables by ourselves.
|
|
||||||
|
|
||||||
TODO ??????????????????HERE ?????????????
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## A word about "length"
|
## A word about "length"
|
||||||
|
|
||||||
The `length` property automatically updates when we modify the array. It is actually not the *count* of values in the array, but the greatest numeric index plus one.
|
The `length` property automatically updates when we modify the array. It is actually not the *count* of values in the array, but the greatest numeric index plus one.
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
# Структуры данных
|
# Data structures
|
||||||
|
|
||||||
Изучаем JavaScript: расширенное знакомство со встроенными типами данных, их особенностями.
|
More data structures and more in-depth study of the known ones.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue