This commit is contained in:
Ilya Kantor 2016-07-07 19:00:12 +03:00
parent 354c616abc
commit a9222196ae
10 changed files with 362 additions and 649 deletions

View file

@ -15,6 +15,8 @@ n = 12.345;
A *number* type serves both for integer and floating point numbers. A *number* type serves both for integer and floating point numbers.
There are many operations for numbers, e.g. multiplication `*`, division `/`, addition `+`, substraction `-` and so on.
Besides regular numbers there are so-called "special numeric values" which also belong to that type: `Infinity`, `-Infinity` and `NaN`. Besides regular numbers there are so-called "special numeric values" which also belong to that type: `Infinity`, `-Infinity` and `NaN`.
- `Infinity` represents the mathematical [Infinity](https://en.wikipedia.org/wiki/Infinity). It is a value that's greater than any number. - `Infinity` represents the mathematical [Infinity](https://en.wikipedia.org/wiki/Infinity). It is a value that's greater than any number.
@ -33,13 +35,13 @@ Besides regular numbers there are so-called "special numeric values" which also
- `NaN` represents a computational error. It is a result of an incorrect or an undefined mathematical operation, for instance: - `NaN` represents a computational error. It is a result of an incorrect or an undefined mathematical operation, for instance:
```js run ```js run
alert( "not a number" * 2 ); // NaN alert( "not a number" / 2 ); // NaN
``` ```
`NaN` is sticky. Any further operation on `NaN` would give `NaN`: `NaN` is sticky. Any further operation on `NaN` would give `NaN`:
```js run ```js run
alert( "not a number" * 2 + 5 - 9 ); // NaN alert( "not a number" / 2 + 5 ); // NaN
``` ```
So, in a long mathematical expression if we have `NaN` in one place, it propagates to the whole result. So, in a long mathematical expression if we have `NaN` in one place, it propagates to the whole result.
@ -232,6 +234,8 @@ Here we have a variable `key` which contains the property name, probably evaluat
Most of time, the dot is used to access object properties, but when we need a complex property name or to pass the name as a variable, then -- we go square brackets. Most of time, the dot is used to access object properties, but when we need a complex property name or to pass the name as a variable, then -- we go square brackets.
Javascript supports object inheritance. There are many types that are based on objects: `Date` for dates, `Array` for ordered data, `Error` for error-reporting and so on. So the word "object" is applicable to a variety of things. The term *plain objects* is used to represent "basic" objects, the ones we create with `{ ... }`.
Objects in JavaScript are very powerful. Here we've just scratched the surface of the topic that is really huge. We'll be closely working with objects and learning more about them in further parts of the tutorial. Objects in JavaScript are very powerful. Here we've just scratched the surface of the topic that is really huge. We'll be closely working with objects and learning more about them in further parts of the tutorial.
## Arrays ## Arrays
@ -263,7 +267,7 @@ alert( fruits[2] ); // Plum
alert( fruits.length ); // 3 alert( fruits.length ); // 3
``` ```
Please note that arrays do not form a separate language type. They are based on objects, but have many features of their own including methods to add, remove, extract elements from the array, to sort arrays and more. We'll cover them in the chapter <info:array>. Please note that arrays do not form a separate language type. They are based on objects. But they greatly extend them with features of their own, methods to add, remove, extract elements from the array, to sort arrays and more. We'll cover them in the chapter <info:array>.
## Symbol type ## Symbol type

View file

@ -16,7 +16,7 @@ null + 1 = 1 // (3)
undefined + 1 = NaN // (4) undefined + 1 = NaN // (4)
``` ```
1. The plus `"+"` operator in this case first converts `1` to a string: `"" + 1 = "1"`, and then adds `0`. 1. The addition with a string `"" + 1` converts `1` to a string: `"" + 1 = "1"`, and then we have `"1" + 0`, the same rule is applied.
2. The minus `"-"` operator only works with numbers, it converts an empty string `""` to zero immediately. 2. The substruction `"-"` (like most math operations) only works with numbers, it converts an empty string `""` to zero immediately.
3. `null` becomes `0` after the numeric conversion. 3. `null` becomes `0` after the numeric conversion.
4. `undefined` becomes `NaN` after the numeric conversion. 4. `undefined` becomes `NaN` after the numeric conversion.

View file

@ -4,8 +4,6 @@ importance: 5
# Type conversions # Type conversions
Let's recap type conversions given in the [previous capter](/types) in the context of operators.
What will be the result of these evaluation? What will be the result of these evaluation?
```js no-beautify ```js no-beautify

View file

@ -1,4 +1,4 @@
# Type conversions # Type Conversions
A variable in JavaScript can contain any data. A variable can at one moment be a string and later recieve a numeric value: A variable in JavaScript can contain any data. A variable can at one moment be a string and later recieve a numeric value:
@ -8,21 +8,11 @@ let message = "hello";
message = 123456; message = 123456;
``` ```
...But sometimes we need to convert a value from one type to another. For example, `alert` automatically converts any value to a string, to show it. Or, so to say, an `if(value)` test converts the `value` to boolean type to see if it's `true` or `false`. ...But some operations implicitly convert a value from one type to another. For example, `alert` automatically converts any value to a string, to show it. Or mathematical operations convert values to numbers. That is called *type coercion*.
There are also cases when we need to explicitly convert between types to ensure that we store the right data the right way or to use special features of a certain type. There are also cases when we need to explicitly convert between types to ensure that we store the right data the right way or to use special features of a certain type.
There are many type conversions in JavaScript, fully listed in [the specification](http://www.ecma-international.org/ecma-262/6.0/index.html#sec-type-conversion). ## ToString
Three conversions that happen the most often:
1. String conversion.
2. Numeric conversion.
3. Boolean conversion.
Let's see how they work and when they happen.
## String conversion
The string conversion happens when we need a string form of a value. The string conversion happens when we need a string form of a value.
@ -46,36 +36,19 @@ alert(typeof a); // string
*/!* */!*
``` ```
The string conversion is obvious. A `false` becomes `"false"`, `null` becomes `"null"` etc. The string conversion is mostly obvious. A `false` becomes `"false"`, `null` becomes `"null"` etc.
For objects it's a little bit trickier. By default, regular objects are converted like this: ## ToNumber
```js run
alert( {} ); [object Object]
```
Although, some object subtypes have their own way of formatting, for instance, arrays turn into the comma-delimited list of items:
```js run
let arr = [1,2,3];
alert( arr ); // 1,2,3
alert( String(arr) === '1,2,3' ); // true
```
Later we'll see how to create custom rules for string conversions for our objects.
## Numeric conversion
Numeric conversion happens in mathematical functions and expressions automatically. Numeric conversion happens in mathematical functions and expressions automatically.
For example, a mathematical operation like division '/' can be applied to non-numbers: For example, when division '/' is applied to non-numbers:
```js run ```js run
alert( "6" / "2" ); // 3, strings become numbers alert( "6" / "2" ); // 3, strings are converted to numbers
``` ```
We can use a `Number(value)` function to convert any `value` to a number: We can use a `Number(value)` function to explicitly convert a `value`:
```js run ```js run
let str = "123"; let str = "123";
@ -86,9 +59,9 @@ let n = Number(str); // becomes a number 123
alert(typeof n); // number alert(typeof n); // number
``` ```
The conversion is usually applied when we have a numeric value coming from a text form field or another string-based source. The explicit conversion is usually required when we read a value coming from a text form field or another string-based source, but we expect a number to be entered.
If the string is not a number, the result of such conversion is `NaN`, for instance: If the string is not a valid number, the result of such conversion is `NaN`, for instance:
```js run ```js run
let age = Number("an arbitrary string instead of a number"); let age = Number("an arbitrary string instead of a number");
@ -116,7 +89,22 @@ alert( Number(false) ); // 0
Please note that `null` and `undefined` behave differently here: `null` becomes a zero, while `undefined` becomes `NaN`. Please note that `null` and `undefined` behave differently here: `null` becomes a zero, while `undefined` becomes `NaN`.
## Boolean conversion ````smart header="Addition '+' concatenates strings"
Almost all mathematical operations convert values to numbers. With a notable exception of the addition `+`. If one of the added values is a string, then another one is also converted to a string.
Then it concatenates (joins) them:
```js run
alert( 1 + '2' ); // '12' (string to the right)
alert( '1' + 2 ); // '12' (string to the left)
alert( 1 + 2 ); // 3, numbers (for the contrast)
```
That only happens when one of arguments is a string, in other cases values are converted to numbers.
````
## ToBoolean
Boolean conversion is the simplest one. Boolean conversion is the simplest one.
@ -136,6 +124,49 @@ alert( Boolean(" ") ); // any non-empty string, even whitespaces are true
``` ```
```` ````
````warn header="Empty objects and arrays are truthy"
All objects become `true`:
```js run
alert( Boolean([]) ); // true
alert( Boolean({}) ); // true
```
````
## ToPrimitive
A string or numeric conversion of an object is a two-stage process. The object is first converted to a primitive value, and then ToString/ToNumber rules are applied to it.
The conversion is customizable on a per-object basis, so we'd better deal with it later when we know more about objects.
For now, let's just see two common rules that we often meet when showing objects.
- When a plain object is converted into a string, is becomes `[object Object]`:
```js run
alert( {} ); // [object Object]
alert( {name: "John"} ); // [object Object]
```
- An array becomes a comma-delimited list of items:
```js run
let arr = [1, 2, 3];
alert( arr ); // 1,2,3
alert( String(arr) === '1,2,3' ); // true
```
We'll return to it in the chapter [todo].
```smart header="It was only about ToString/ToNumber"
For ToBoolean, there is no complexity neither customizability.
The rule is simple: all objects are truthy.
```
## Summary ## Summary
There exist three most widely used type conversions: to string, to number and to boolean. There exist three most widely used type conversions: to string, to number and to boolean.
@ -158,11 +189,10 @@ To boolean:
|`0`, `null`, `undefined`, `NaN`, `""` |`false`| |`0`, `null`, `undefined`, `NaN`, `""` |`false`|
|any other value| `true` | |any other value| `true` |
Objects provide advanced means to specify how they are converted to string and number, we'll see them later, when study objects in-depth.
Most of these rules are easy to understand and memorize. The notable exceptions where people usually make mistakes are: Most of these rules are easy to understand and memorize. The notable exceptions where people usually make mistakes are:
- `undefined` is `NaN` as a number. - `undefined` is `NaN` as a number.
- `"0"` is true as a boolean. - `"0"` is true as a boolean.
In the next chapter we'll study operators. You will find enough tasks for type conversion there. Objects can define their own methods of converting to a string or a number, we'll see them later. But they can't redefine the conversion to boolean.

View file

@ -69,7 +69,7 @@ For instance, the case matters. A capital letter `"A"` is not equal to the lower
## Comparison of different types ## Comparison of different types
When compared values belong to different types, they get autoconverted to numbers. When compared values belong to different types, they are coerced to numbers.
For example: For example:
@ -187,7 +187,7 @@ We've got such result, because:
- Comparisons `(1)` and `(2)` return `false` because `undefined` gets converted to `NaN`. And `NaN` is a special numeric value which returns `false` for all comparisons. - Comparisons `(1)` and `(2)` return `false` because `undefined` gets converted to `NaN`. And `NaN` is a special numeric value which returns `false` for all comparisons.
- The equality check `(3)` returns `false`, because `undefined` only equals `null` and no other value. - The equality check `(3)` returns `false`, because `undefined` only equals `null` and no other value.
### How to live a good life ### Evade problems
Why did we observe these examples? Should we remember these pecularities all the time? Well, not really. Actually, these tricky things will gradually become familiar over the time, but there's a solid way to evade any problems with them. Why did we observe these examples? Should we remember these pecularities all the time? Well, not really. Actually, these tricky things will gradually become familiar over the time, but there's a solid way to evade any problems with them.
@ -195,6 +195,18 @@ Just treat any comparison with `undefined/null` except the strict equality `===`
Don't use comparisons `>= > < <=` with a variable which may be `null/undefined`, unless you are really sure what you're doing. If a variable can have such values, then check it separately. Don't use comparisons `>= > < <=` with a variable which may be `null/undefined`, unless you are really sure what you're doing. If a variable can have such values, then check it separately.
## Comparison with objects
For equality checks two objects are always treated as non-equal.
```js run
alert( {} == {} ); // false
alert( [] == [] ); // false
```
Note that Javascript does not try to compare the objects by content. The rule is simple: different objects are not equal.
For other comparisons like `< >` operators, or when an object is compared with a primitive, objects are first converted to primitives, and then the comparison runs as usual.
## Summary ## Summary

View file

@ -156,78 +156,12 @@ 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
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 ## Function Expression vs Function Declaration
Let's formulate the key differences between Function Declarations and Expressions. Let's formulate the key differences between Function Declarations and Expressions.
Here's the syntax distinction between these two. First, the syntax: how to see what is what in the code.
- *Function Declaration:* a function, declared as a separate statement, in the main code flow. - *Function Declaration:* a function, declared as a separate statement, in the main code flow.
@ -247,7 +181,7 @@ Here's the syntax distinction between these two.
} }
``` ```
Another difference is when they are actualy created by the JavaScript engine. The more subtle 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.** **Function Expressions are created when the execution reaches them and are usable since then.**

View file

@ -1,168 +1,6 @@
# Function expressions and more # Object methods, "this"
In JavaScript, a function is a value. As we remember from the chapter <info:types>, objects to store keyed collections of data. We usually create objects to represent entities of the real world, like users, orders and so on:
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 ```js
let user = { let user = {
@ -171,7 +9,13 @@ let user = {
}; };
``` ```
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: And, in the real world, a user can `act`: to select something from the shopping cart, to login, to logout etc.
Let's implement the same in Javascript.
[cut]
For the start, let's teach the `user` to say hello:
```js run ```js run
let user = { let user = {
@ -188,13 +32,21 @@ user.sayHi = function() {
user.sayHi(); // 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. Here 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! Then we can call it. 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. A function that is the property of an object is called its *method*.
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`". So, here we've got a method `sayHi` of the object `user`.
```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".
OOP is a big thing, an interesting science of its own. How to choose the right entities? How to organize the code and the interaction between them? That's an architecture.
We will make use of OOP further when we get enough familarity with basic functions of the language.
```
Of course we could use a Function Declaration for the same purpose: Of course we could use a Function Declaration for the same purpose:
@ -216,448 +68,276 @@ 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. 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" ## Method syntax
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. In an object literal, there's a shorter syntax for methods:
```
## 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 ```js
// Function Declaration // these objects do the same
function sum(a, b) {
return a + b; let user = {
sayHi: function() {
alert("Hello");
} }
``` };
- *Function Expression:* a function, created in the context of an expression.
Here the function is created in the context of an "assignment expression =": let user = {
```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 sayHi() { // same as "sayHi: function()"
*/!* */!*
alert("Hello");
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. As demonstrated, we can omit a colon with the word `"function"`. And it actually looks better.
To say the truth, the notations are not fully identical. There are subtle differences related to object inheritance (to be covered later), but for now they do not matter. In almost all cases the shorter syntax is preferred.
### Function Declaration in a block ## "this" in methods
When Function Declaration is made within a code block, it is visible everywhere inside that block. But not outside of it. It's common that an object method needs to access the information stored in the object to do its job.
Sometimes that's handy to declare a local function, only needed in that only block. But can also be a problem. For instance, `user.sayHi()` may need to mention the name.
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. **To access the object, a method can use the `this` keyword.**
The code below doesn't work: The value of `this` is the object "before dot", the one used to call the method.
For instance:
```js run ```js run
let age = prompt("What is your age?", 18); let user = {
name: "John",
if (age < 18) { age: 30,
function welcome() {
alert("Hello!");
}
} else {
function welcome() {
alert("Greetings!");
}
}
sayHi() {
*!* *!*
welcome(); // Error: welcome is not defined alert( this.name ); // "this" means "this object"
*/!* */!*
}
};
user.sayHi(); // John
``` ```
The Function Declaration is visible only inside the code block where it resides. Here during the execution of `user.sayHi()`, the value of `this` will be `user`.
We can call it from within the block, but not from outside: Technically, it's also possible to access the object without `this`:
```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 ```js
let sayHi = function() { // (1) ...
alert('Hello'); sayHi() {
}; alert( *!*user.name*/!* );
}
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. ...But such code is unreliable. If we decide to copy `user` to another variable, e.g. `admin = user` and overwrite `user` with something else, then it will access the wrong object.
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: That's demonstrated below:
```js run ```js run
let sayHi = function() { let user = {
alert('Hello'); name: "John",
}; age: 30,
sayHi() {
*!*
alert( user.name ); // leads to an error
*/!*
}
// 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 let admin = user;
sayHi(); // What's up dude? user = null; // overwrite to make things obvious
admin.sayHi(); // wops! inside sayHi(), the old name is used! error!
``` ```
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. The variant with `this.name` instead of `user.name` would work fine here.
## "this" is free
But if the function has moved, then the old name is not relevant any move! In Javascript, "this" keyword behaves unlike most other programming languages.
The "this" keyword can be used in any function. There's no syntax error in the code like that:
Let's see: ```js
function sayHi() {
alert( this.name );
}
```
It is fully syntactically free. The function does not yet know what "this" value will be. It will be evaluated during the run-time.
For instance, it may have different "this" when called in the context of different objects:
```js run ```js run
// create a function let user = { name: "John" };
let sayHi = function() { let admin = { name: "Admin" };
sayHi.counter++;
alert('Hi ' + sayHi.counter); function sayHi() {
alert( this.name );
}
user.f = sayHi;
admin.g = sayHi;
// "this" inside the function is the object "before the dot"
user.f(); // John
admin.g(); // Admin
admin['g'](); // Admin (dot or square brackets to access the method doesn't matter)
```
Actually, we can call the function without an object at all:
```js run
function sayHi() {
alert(this);
}
sayHi();
```
In this case `this` is `undefined`. If we try to access `this.name`, there will be an error.
Such calls are often a result of the programming mistake. If a function has `this`, then it is usually to be called in the context of an object.
```smart header="And without `use strict`..."
If you forget `use strict`, then you may see something like "window" in the example above.
That's one of the odd things of the previous standard that `"use strict"` fixes.
```
## "this" is for direct calls
The value of `this` is only passed the right way if the function is called using a dot `'.'` or square brackets.
A more intricate call would lead to losing `this`, for instance:
```js run
let user = {
name: "John",
hi() { alert(this.name); },
bye() { alert("Bye"); }
}; };
sayHi.counter = 0;
// move it user.hi(); // John (the simple call works)
let movedSayHi = sayHi;
// overwrite the old name to make things more obvious
sayHi = null;
*!* *!*
movedSayHi(); // Error: Cannot read property 'counter' of null // now let's call user.hi or user.bye depending on the name
(user.name == "John" ? user.hi : user.bye)(); // undefined
*/!* */!*
``` ```
The optional `name` which we can put into `function name()` is exactly meant to solve this kind of problems. On the last line the method is retrieved during the execution of the ternary `?`, and immediately called. But `"this"` is lost, the result is not `"John"` how it should be.
- It is only visible from inside the function. If we want to understand why it happens -- the reason is in the details of how `obj.method()` works.
- It always references the current function.
Let's use it to fix the code: The method call has two independant operations in it: a dot `'.'` to access the property and brackets `()` to execute it (assuming that's a function).
```js run As we've already seen, the function is a value of its own. It does not memorize the object by itself. So to "carry" it to the brackets, Javascript uses a trick -- the dot `'.'` returns not a function, but a value of the special Reference Type.
// 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; The Reference Type is a "specification type". It does not exist in real, but used internally to explain how some language features work.
sayHi = null; The value of the Reference Type is a tuple `(base, name, strict)`, where:
movedSayHi(); // Hi 1 - `base` is the object.
movedSayHi(); // Hi 2 (works) - `name` is the property.
- `strict` is true if `use strict` is in effect.
alert(say); // Error (say is undefined, that's normal) The result of a property access `'.'` is a value of the Reference Type. For `user.sayHi` in strict mode it is:
```js
// base name strict
(user, "sayHi", true)
``` ```
Please note that: Any operation on the Reference Type immediately "resolves" it:
- The name `say` references the current function no matter where it is. That's why it works. - Brackets `()` get the property `base[name]` and execute it with `this = base`.
- The name `say` exists only inside the function. The last line demonstrates that. - Other operators just get `base[name]` and use it.
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. So any operation on the result of dot `'.'` except a direct call discards `this`.
A Function Expression with a name is called *Named Function Expression*. ## Explicit "this" with "call/apply"
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. We can call a function explicitly providing the value of `"this"`.
## 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: The syntax is:
```js ```js
let func = (arg1, arg2, ...argN) => expression func.call(context, arg1, arg2, ...)
``` ```
...This creates a function `func` that has arguments `arg1..argN`, evaludates the `expression` on the right side with their use and returns its result. For instance:
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 ```js run
let sum = (a, b) => a + b; function sayHi() {
alert(this.name);
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 let user = { name: "John" };
let admin = { name: "Admin" };
// use call to pass different objects as "this"
sayHi.call( user ); // John
sayHi.call( admin ); // Admin
``` ```
```smart header="More to come" The first parameter of `call` is the intended value of `"this"`, the latter are arguments.
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. These calls are roughly equivalent:
```
## new Function
And the last syntax for the functions:
```js ```js
let func = new Function('a, b', 'return a + b'); func(1, 2, 3);
func.call(obj, 1, 2, 3)
``` ```
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. ...Except that the `call` sets "this" of course!
All previous declarations required us, programmers, to write the function code in the script. That's handy when we want to use a function in the context of different objects, but do not want to actually assign it to them.
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: ### "func.apply"
There's also a similar syntax:
```js ```js
let str = ... receive the code from the server dynamically ... func.apply(context, args)
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. It does the same as `call`: executes the function providing `context` as `this`, but where `call` awaits a list of arguments, `apply` awaits a single array of arguments.
These two calls do the same:
```js
func.call(obj, 1, 2, 3);
func.apply(obj, [1, 2, 3]);
```
In old times `apply` was more powerful, because it allows to form the array of arguments dynamically.
But in the modern language, we have the spread operator `'...'` and can use it to unfurl an array into the list of for `call`, so these two are equal:
```js
let args = [1, 2, 3];
func.call(obj, ...args);
func.apply(obj, args);
```
So the use of `apply` over `call` is mainly a metter of personal preference. And it's somewhat better optimized than the spread operator, because it exists longer.
## Binding "this" with "bind"
There's still a way to bind "this" to a function.
[todo] ????
## Summary ## Summary
- Functions are values. They can be assigned, copied or declared in any place of the code. [todo]
- 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.

View file

@ -0,0 +1,63 @@
# Object to primitive conversion
In the chapter <info:type-conversions> we've seen the rules for numeric, string and boolean conversions.
But we left the gap for objects. Now let's close it.
[cut]
The operation that converts an object to a primitive is called [ToPrimitive](https://tc39.github.io/ecma262/#sec-toprimitive).
Some build-in language objects have their own implementation of it, but for most objects, including our own, it comes in two flavours:
- string
- number
TODO
The method `toString` is automatically called by Javascript when the object is converted to a string:
```js run
let user = {
name: 'John',
*!*
toString() {
return `User ${this.firstName}`;
}
*/!*
};
*!*
alert( user ); // User John
*/!*
```
Looks much better than the default `[object Object]`, right?
The similar thing with the method `valueOf`. It is called when the object is converted to a number.
```js run
let room = {
number: 777,
valueOf() {
return this.number;
},
};
alert( +room ); // 777, valueOf is called
```
What really sounds strange -- is the name of the method. Why is it called "valueOf", why not "toNumber"?
The reason is that `valueOf` is used by default to convert an object to a primitive for operations where a primitive value is required.

View file

@ -90,6 +90,6 @@ alert(null.test); // error
## Summary ## Summary
- Primitives except `null` and `undefined` provide various methods. We plan to study those in the next chapters. - Primitives except `null` and `undefined` provide many helpful methods. We plan to study those in the next chapters.
- Primitives are not objects, but still have methods. Formally, these methods work via temporary objects, but JavaScript engines are tuned to optimize that internally, so they are not expensive to call. - Formally, these methods work via temporary objects, but JavaScript engines are tuned to optimize that internally, so they are not expensive to call.

View file

@ -235,17 +235,9 @@ Property value shorthands
``` ```
Methods definitions Methods definitions
: For properties that are functions, there's also a shorter syntax. : For properties that are functions, we've already seen a shorter syntax.
```js ```js
// these two objects are equal
let user = {
sayHi: function() {
alert("Hello");
}
};
let user = { let user = {
sayHi() { // same as "sayHi: function()" sayHi() { // same as "sayHi: function()"
alert("Hello"); alert("Hello");