refactor objects, add optional chaining
This commit is contained in:
parent
09a964e969
commit
b057341f6c
35 changed files with 579 additions and 435 deletions
|
@ -233,98 +233,6 @@ The concept of run-time evaluated `this` has both pluses and minuses. On the one
|
|||
Here our position is not to judge whether this language design decision is good or bad. We'll understand how to work with it, how to get benefits and avoid problems.
|
||||
```
|
||||
|
||||
## Internals: Reference Type
|
||||
|
||||
```warn header="In-depth language feature"
|
||||
This section covers an advanced topic, to understand certain edge-cases better.
|
||||
|
||||
If you want to go on faster, it can be skipped or postponed.
|
||||
```
|
||||
|
||||
An intricate method call can lose `this`, for instance:
|
||||
|
||||
```js run
|
||||
let user = {
|
||||
name: "John",
|
||||
hi() { alert(this.name); },
|
||||
bye() { alert("Bye"); }
|
||||
};
|
||||
|
||||
user.hi(); // John (the simple call works)
|
||||
|
||||
*!*
|
||||
// now let's call user.hi or user.bye depending on the name
|
||||
(user.name == "John" ? user.hi : user.bye)(); // Error!
|
||||
*/!*
|
||||
```
|
||||
|
||||
On the last line there is a conditional operator that chooses either `user.hi` or `user.bye`. In this case the result is `user.hi`.
|
||||
|
||||
Then the method is immediately called with parentheses `()`. But it doesn't work correctly!
|
||||
|
||||
As you can see, the call results in an error, because the value of `"this"` inside the call becomes `undefined`.
|
||||
|
||||
This works (object dot method):
|
||||
```js
|
||||
user.hi();
|
||||
```
|
||||
|
||||
This doesn't (evaluated method):
|
||||
```js
|
||||
(user.name == "John" ? user.hi : user.bye)(); // Error!
|
||||
```
|
||||
|
||||
Why? If we want to understand why it happens, let's get under the hood of how `obj.method()` call works.
|
||||
|
||||
Looking closely, we may notice two operations in `obj.method()` statement:
|
||||
|
||||
1. First, the dot `'.'` retrieves the property `obj.method`.
|
||||
2. Then parentheses `()` execute it.
|
||||
|
||||
So, how does the information about `this` get passed from the first part to the second one?
|
||||
|
||||
If we put these operations on separate lines, then `this` will be lost for sure:
|
||||
|
||||
```js run
|
||||
let user = {
|
||||
name: "John",
|
||||
hi() { alert(this.name); }
|
||||
}
|
||||
|
||||
*!*
|
||||
// split getting and calling the method in two lines
|
||||
let hi = user.hi;
|
||||
hi(); // Error, because this is undefined
|
||||
*/!*
|
||||
```
|
||||
|
||||
Here `hi = user.hi` puts the function into the variable, and then on the last line it is completely standalone, and so there's no `this`.
|
||||
|
||||
**To make `user.hi()` calls work, JavaScript uses a trick -- the dot `'.'` returns not a function, but a value of the special [Reference Type](https://tc39.github.io/ecma262/#sec-reference-specification-type).**
|
||||
|
||||
The Reference Type is a "specification type". We can't explicitly use it, but it is used internally by the language.
|
||||
|
||||
The value of Reference Type is a three-value combination `(base, name, strict)`, where:
|
||||
|
||||
- `base` is the object.
|
||||
- `name` is the property name.
|
||||
- `strict` is true if `use strict` is in effect.
|
||||
|
||||
The result of a property access `user.hi` is not a function, but a value of Reference Type. For `user.hi` in strict mode it is:
|
||||
|
||||
```js
|
||||
// Reference Type value
|
||||
(user, "hi", true)
|
||||
```
|
||||
|
||||
When parentheses `()` are called on the Reference Type, they receive the full information about the object and its method, and can set the right `this` (`=user` in this case).
|
||||
|
||||
Reference type is a special "intermediary" internal type, with the purpose to pass information from dot `.` to calling parentheses `()`.
|
||||
|
||||
Any other operation like assignment `hi = user.hi` discards the reference type as a whole, takes the value of `user.hi` (a function) and passes it on. So any further operation "loses" `this`.
|
||||
|
||||
So, as the result, the value of `this` is only passed the right way if the function is called directly using a dot `obj.method()` or square brackets `obj['method']()` syntax (they do the same here). Later in this tutorial, we will learn various ways to solve this problem such as [func.bind()](/bind#solution-2-bind).
|
||||
|
||||
## Arrow functions have no "this"
|
||||
|
||||
Arrow functions are special: they don't have their "own" `this`. If we reference `this` from such a function, it's taken from the outer "normal" function.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue