Skip to content

Commit 3f261c0

Browse files
committed
Merge branch 'master' of C:/Users/osvaldo/Documents/GitHub/copy/en.javascript.info into fix-miscellaneous
2 parents 24dc611 + 53d05bc commit 3f261c0

File tree

5 files changed

+218
-0
lines changed

5 files changed

+218
-0
lines changed
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
**Error**!
2+
3+
Try it:
4+
5+
```js run
6+
let user = {
7+
name: "John",
8+
go: function() { alert(this.name) }
9+
}
10+
11+
(user.go)() // error!
12+
```
13+
14+
The error message in most browsers does not give us much of a clue about what went wrong.
15+
16+
**The error appears because a semicolon is missing after `user = {...}`.**
17+
18+
JavaScript does not auto-insert a semicolon before a bracket `(user.go)()`, so it reads the code like:
19+
20+
```js no-beautify
21+
let user = { go:... }(user.go)()
22+
```
23+
24+
Then we can also see that such a joint expression is syntactically a call of the object `{ go: ... }` as a function with the argument `(user.go)`. And that also happens on the same line with `let user`, so the `user` object has not yet even been defined, hence the error.
25+
26+
If we insert the semicolon, all is fine:
27+
28+
```js run
29+
let user = {
30+
name: "John",
31+
go: function() { alert(this.name) }
32+
}*!*;*/!*
33+
34+
(user.go)() // John
35+
```
36+
37+
Please note that parentheses around `(user.go)` do nothing here. Usually they setup the order of operations, but here the dot `.` works first anyway, so there's no effect. Only the semicolon thing matters.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
importance: 2
2+
3+
---
4+
5+
# Syntax check
6+
7+
What is the result of this code?
8+
9+
10+
```js no-beautify
11+
let user = {
12+
name: "John",
13+
go: function() { alert(this.name) }
14+
}
15+
16+
(user.go)()
17+
```
18+
19+
P.S. There's a pitfall :)
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
2+
Here's the explanations.
3+
4+
1. That's a regular object method call.
5+
6+
2. The same, parentheses do not change the order of operations here, the dot is first anyway.
7+
8+
3. Here we have a more complex call `(expression).method()`. The call works as if it were split into two lines:
9+
10+
```js no-beautify
11+
f = obj.go; // calculate the expression
12+
f(); // call what we have
13+
```
14+
15+
Here `f()` is executed as a function, without `this`.
16+
17+
4. The similar thing as `(3)`, to the left of the dot `.` we have an expression.
18+
19+
To explain the behavior of `(3)` and `(4)` we need to recall that property accessors (dot or square brackets) return a value of the Reference Type.
20+
21+
Any operation on it except a method call (like assignment `=` or `||`) turns it into an ordinary value, which does not carry the information allowing to set `this`.
22+
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
importance: 3
2+
3+
---
4+
5+
# Explain the value of "this"
6+
7+
In the code below we intend to call `obj.go()` method 4 times in a row.
8+
9+
But calls `(1)` and `(2)` works differently from `(3)` and `(4)`. Why?
10+
11+
```js run no-beautify
12+
let obj, method;
13+
14+
obj = {
15+
go: function() { alert(this); }
16+
};
17+
18+
obj.go(); // (1) [object Object]
19+
20+
(obj.go)(); // (2) [object Object]
21+
22+
(method = obj.go)(); // (3) undefined
23+
24+
(obj.go || obj.stop)(); // (4) undefined
25+
```
26+
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
2+
# Reference Type
3+
4+
```warn header="In-depth language feature"
5+
This article covers an advanced topic, to understand certain edge-cases better.
6+
7+
It's not important. Many experienced developers live fine without knowing it. Read on if you're want to know how things work under the hood.
8+
```
9+
10+
A dynamically evaluated method call can lose `this`.
11+
12+
For instance:
13+
14+
```js run
15+
let user = {
16+
name: "John",
17+
hi() { alert(this.name); },
18+
bye() { alert("Bye"); }
19+
};
20+
21+
user.hi(); // works
22+
23+
// now let's call user.hi or user.bye depending on the name
24+
*!*
25+
(user.name == "John" ? user.hi : user.bye)(); // Error!
26+
*/!*
27+
```
28+
29+
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`.
30+
31+
Then the method is immediately called with parentheses `()`. But it doesn't work correctly!
32+
33+
As you can see, the call results in an error, because the value of `"this"` inside the call becomes `undefined`.
34+
35+
This works (object dot method):
36+
```js
37+
user.hi();
38+
```
39+
40+
This doesn't (evaluated method):
41+
```js
42+
(user.name == "John" ? user.hi : user.bye)(); // Error!
43+
```
44+
45+
Why? If we want to understand why it happens, let's get under the hood of how `obj.method()` call works.
46+
47+
## Reference type explained
48+
49+
Looking closely, we may notice two operations in `obj.method()` statement:
50+
51+
1. First, the dot `'.'` retrieves the property `obj.method`.
52+
2. Then parentheses `()` execute it.
53+
54+
So, how does the information about `this` get passed from the first part to the second one?
55+
56+
If we put these operations on separate lines, then `this` will be lost for sure:
57+
58+
```js run
59+
let user = {
60+
name: "John",
61+
hi() { alert(this.name); }
62+
}
63+
64+
*!*
65+
// split getting and calling the method in two lines
66+
let hi = user.hi;
67+
hi(); // Error, because this is undefined
68+
*/!*
69+
```
70+
71+
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`.
72+
73+
**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).**
74+
75+
The Reference Type is a "specification type". We can't explicitly use it, but it is used internally by the language.
76+
77+
The value of Reference Type is a three-value combination `(base, name, strict)`, where:
78+
79+
- `base` is the object.
80+
- `name` is the property name.
81+
- `strict` is true if `use strict` is in effect.
82+
83+
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:
84+
85+
```js
86+
// Reference Type value
87+
(user, "hi", true)
88+
```
89+
90+
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).
91+
92+
Reference type is a special "intermediary" internal type, with the purpose to pass information from dot `.` to calling parentheses `()`.
93+
94+
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`.
95+
96+
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).
97+
98+
## Summary
99+
100+
Reference Type is an internal type of the language.
101+
102+
Reading a property, such as with dot `.` in `obj.method()` returns not exactly the property value, but a special "reference type" value that stores both the property value and the object it was taken from.
103+
104+
That's for the subsequent method call `()` to get the object and set `this` to it.
105+
106+
For all other operations, the reference type automatically becomes the property value (a function in our case).
107+
108+
The whole mechanics is hidden from our eyes. It only matters in subtle cases, such as when a method is obtained dynamically from the object, using an expression.
109+
110+
111+
112+
113+
114+
result of dot `.` isn't actually a method, but a value of `` needs a way to pass the information about `obj`

0 commit comments

Comments
 (0)