You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: 1-js/04-object-basics/03-symbol/article.md
+1-3Lines changed: 1 addition & 3 deletions
Original file line number
Diff line number
Diff line change
@@ -233,6 +233,4 @@ Other symbols will also become familiar when we study the corresponding language
233
233
- Symbols created with `Symbol(name)` are always different, even if they have the same name. If we want same-named symbols to be equal, then we should use the global registry: `Symbol.for(name)` returns (creates if needed) a global symbol with the given name. Multiple calls return the same symbol.
234
234
- There are system symbols used by JavaScript and accessible as `Symbol.*`. We can use them to alter some built-in behaviors.
235
235
236
-
Technically, symbols are not 100% hidden. There is a build-in method [Object.getOwnPropertySymbols(obj)](mdn:js/Object/getOwnPropertySymbols) that allows to get all symbols. Also there is a method named [Reflect.ownKeys(obj)](mdn:js/Reflect/ownKeys) that returns *all* keys of an object including symbolic ones. So they are not really hidden.
237
-
238
-
But most libraries, built-in methods and syntax constructs adhere to a common agreement that they are. And the one who explicitly calls the aforementioned methods probably understands well what he's doing.
236
+
Technically, symbols are not 100% hidden. There is a build-in method [Object.getOwnPropertySymbols(obj)](mdn:js/Object/getOwnPropertySymbols) that allows to get all symbols. Also there is a method named [Reflect.ownKeys(obj)](mdn:js/Reflect/ownKeys) that returns *all* keys of an object including symbolic ones. So they are not really hidden. But most libraries, built-in methods and syntax constructs adhere to a common agreement that they are. And the one who explicitly calls the aforementioned methods probably understands well what he's doing.
Copy file name to clipboardExpand all lines: 1-js/04-object-basics/04-object-methods/article.md
+48-30Lines changed: 48 additions & 30 deletions
Original file line number
Diff line number
Diff line change
@@ -9,9 +9,9 @@ let user = {
9
9
};
10
10
```
11
11
12
-
And, in the real world, a user can `act`: to select something from the shopping cart, to login, to logout etc.
12
+
And, in the real world, a user can *act*: select something from the shopping cart, login, logout etc.
13
13
14
-
Let's implement the same in JavaScript using functions in properties.
14
+
Actions are represented in JavaScript by functions in properties.
15
15
16
16
[cut]
17
17
@@ -42,7 +42,7 @@ A function that is the property of an object is called its *method*.
42
42
43
43
So, here we've got a method `sayHi` of the object `user`.
44
44
45
-
Of course, we could use a Function Declaration to add a method:
45
+
Of course, we could use a pre-declared function as a method, like this:
46
46
47
47
```js run
48
48
let user = {
@@ -55,19 +55,17 @@ function sayHi() {
55
55
alert("Hello!");
56
56
};
57
57
58
-
// then add the method
58
+
// then add as a method
59
59
user.sayHi= sayHi;
60
60
*/!*
61
61
62
62
user.sayHi(); // Hello!
63
63
```
64
64
65
-
That would also work, but is longer. Also we get an "extra" function `sayHi` outside of the `user` object. Usually we don't want that.
66
-
67
65
```smart header="Object-oriented programming"
68
66
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".
69
67
70
-
OOP is a big thing, an interesting science of its own. How to choose the right entities? How to organize the interaction between them? That's architecture.
68
+
OOP is a big thing, an interesting science of its own. How to choose the right entities? How to organize the interaction between them? That's architecture, and there are great books on that topic, like "Design Patterns: Elements of Reusable Object-Oriented Software" by E.Gamma, R.Helm, R.Johnson, J.Vissides or "Object-Oriented Analysis and Design with Applications" by G.Booch, and more. We'll scratch the surface of that topic later in the chapter <info:object-oriented-programming>.
71
69
```
72
70
### Method shorthand
73
71
@@ -100,7 +98,7 @@ To say the truth, the notations are not fully identical. There are subtle differ
100
98
101
99
It's common that an object method needs to access the information stored in the object to do its job.
102
100
103
-
For instance, `user.sayHi()` may need to mention the name of the user.
101
+
For instance, the code inside `user.sayHi()` may need the name of the `user`.
104
102
105
103
**To access the object, a method can use the `this` keyword.**
106
104
@@ -115,7 +113,7 @@ let user = {
115
113
116
114
sayHi() {
117
115
*!*
118
-
alert(this.name ); // "this" means "this object"
116
+
alert(this.name);
119
117
*/!*
120
118
}
121
119
@@ -126,14 +124,20 @@ user.sayHi(); // John
126
124
127
125
Here during the execution of `user.sayHi()`, the value of `this` will be `user`.
128
126
129
-
Technically, it's also possible to access the object without `this`:
127
+
Technically, it's also possible to access the object without `this`, by referencing it via the outer variable:
130
128
131
129
```js
132
-
...
130
+
let user = {
131
+
name:"John",
132
+
age:30,
133
+
133
134
sayHi() {
134
-
alert( *!*user.name*/!* );
135
+
*!*
136
+
alert(user.name); // "user" instead of "this"
137
+
*/!*
135
138
}
136
-
...
139
+
140
+
};
137
141
```
138
142
139
143
...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.
@@ -207,27 +211,29 @@ function sayHi() {
207
211
alert(this);
208
212
}
209
213
210
-
sayHi();
214
+
sayHi();// undefined
211
215
```
212
216
213
217
In this case `this` is `undefined` in strict mode. If we try to access `this.name`, there will be an error.
214
218
215
-
In non-strict mode (if you forgot`use strict`) the value of `this` in such case will be the *global object* (`"window"` for browser, we'll study it later). This is just a historical thing that `"use strict"` fixes.
219
+
In non-strict mode (if one forgets`use strict`) the value of `this` in such case will be the *global object* (`window` in a browser, we'll get to it later). This is a historical behavior that `"use strict"` fixes.
216
220
217
-
Please note that usually a call of a function using`this` without an object is not normal, but rather a programming mistake. If a function has `this`, then it is usually meant to be called in the context of an object.
221
+
Please note that usually a call of a function that uses`this` without an object is not normal, but rather a programming mistake. If a function has `this`, then it is usually meant to be called in the context of an object.
218
222
219
223
```smart header="The consequences of unbound `this`"
220
224
If you come from another programming languages, then you are probably used to an idea of a "bound `this`", where methods defined in an object always have `this` referencing that object.
221
225
222
-
The idea of unbound, run-time evaluated `this`has both pluses and minuses. From one side, a function can be reused for different objects. From the other side, greater flexibility opens a place for mistakes.
226
+
In JavaScript `this`is "free", its value is evaluated at the call time and depends not on where the method was declared, but rather on what's the object "before dot".
223
227
224
-
Here we are not to judge whether this language design decision is good or bad. We will understand how to work with it, how to get benefits and evade problems.
228
+
The concept of run-time evaluated `this` has both pluses and minuses. From one side, a function can be reused for different objects. From the other side, greater flexibility opens a place for mistakes.
229
+
230
+
Here our position is not to judge whether this language design decision is good or bad. We'll understand how to work with it, how to get benefits and evade problems.
225
231
```
226
232
227
233
## Internals: Reference Type
228
234
229
235
```warn header="In-depth language feature"
230
-
This section covers advanced topic that may interest those who want to know JavaScript better.
236
+
This section covers an advanced topic, to understand certain edge-cases better.
231
237
232
238
If you want to go on faster, it can be skipped or postponed.
233
239
```
@@ -249,20 +255,32 @@ user.hi(); // John (the simple call works)
249
255
*/!*
250
256
```
251
257
252
-
On the last line there is an intricate code that evaluates an expression to get the method. In this case the result is `user.hi`.
258
+
On the last line there is a ternary operator that chooses either `user.hi` or `user.bye`. In this case the result is `user.hi`.
253
259
254
-
The method is immediately called with brackets`()`. But that doesn't work right. You can see that the call results in an error, cause the value of `"this"` inside the call becomes `undefined`.
260
+
The method is immediately called with parentheses`()`. But it doesn't work right!
255
261
256
-
Actually, anything more complex than a simple `obj.method()` (or square brackets here) looses `this`.
262
+
You can see that the call results in an error, cause the value of `"this"` inside the call becomes `undefined`.
257
263
258
-
If we want to understand why it happens -- let's get under the hood of how `obj.method()` call works.
264
+
This works (object dot method):
265
+
```js
266
+
user.hi();
267
+
```
268
+
269
+
This doesn't (evaluated method):
270
+
```js
271
+
(user.name=="John"?user.hi:user.bye)(); // Error!
272
+
```
273
+
274
+
Why? If we want to understand why it happens -- let's get under the hood of how `obj.method()` call works.
259
275
260
276
Looking closely, we may notice two operations in `obj.method()` statement:
261
277
262
-
- the dot `'.'` retrieves the property `obj.method`.
263
-
- brackets `()` execute it (assuming that's a function).
278
+
1. First, the dot `'.'` retrieves the property `obj.method`.
279
+
2. Then parentheses `()` execute it.
280
+
281
+
So, how the information about `this` gets passed from the first part to the second one?
264
282
265
-
So, you might have already asked yourself, why does it work? That is, if we put these operations on separate lines, then `this` will be lost for sure:
283
+
If we put these operations on separate lines, then `this` will be lost for sure:
266
284
267
285
```js run
268
286
let user = {
@@ -277,7 +295,7 @@ hi(); // Error, because this is undefined
277
295
*/!*
278
296
```
279
297
280
-
That's because a function is a value of its own. It does not carry the object. So `hi = user.hi`saves it into the variable, and then on the last line it is completely standalone.
298
+
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`.
281
299
282
300
**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).**
283
301
@@ -289,14 +307,14 @@ The value of Reference Type is a three-value combination `(base, name, strict)`,
289
307
-`name` is the property.
290
308
-`strict` is true if `use strict` is in effect.
291
309
292
-
The result of a property access `'.'` is a value of Reference Type. For `user.hi` in strict mode it is:
310
+
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:
293
311
294
312
```js
295
313
// Reference Type value
296
314
(user, "hi", true)
297
315
```
298
316
299
-
When brackets`()` are called on the Reference Type, they receive the full information about the object and it's method, and can set the right `this` (`=user` in this case).
317
+
When parentheses`()` are called on the Reference Type, they receive the full information about the object and it's method, and can set the right `this` (`=user` in this case).
300
318
301
319
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 "looses" `this`.
302
320
@@ -320,7 +338,7 @@ let user = {
320
338
user.sayHi(); // Ilya
321
339
```
322
340
323
-
That's a special feature of arrow functions, it's useful when we actually do not want to have a separate `this`, but rather to take it from the outer context. Later in the chapter <info:arrow-functions> we'll dig more deeply into what's going on.
341
+
That's a special feature of arrow functions, it's useful when we actually do not want to have a separate `this`, but rather to take it from the outer context. Later in the chapter <info:arrow-functions> we'll go more deeply into arrow functions.
Copy file name to clipboardExpand all lines: 1-js/04-object-basics/05-object-toprimitive/article.md
+26-36Lines changed: 26 additions & 36 deletions
Original file line number
Diff line number
Diff line change
@@ -1,41 +1,32 @@
1
1
2
2
# Object to primitive conversion
3
3
4
-
In the chapter <info:type-conversions> we've seen the rules for numeric, string and boolean conversions of primitives.
4
+
What happens when objects are added `obj1 + obj2`, substracted `obj1 - obj2` or printed using `alert(obj)`?
5
5
6
-
But we left a gap for objects. Now let's close it. And, in the process, we'll see some built-in methods and the example of a built-in symbol.
6
+
There are special methods in objects that do the conversion.
7
7
8
-
[cut]
9
-
10
-
## Where and why?
11
-
12
-
The process of object to primitive conversion can be customized, here we'll see how to implement our own methods for it. But first, let's see when it happens.
8
+
In the chapter <info:type-conversions> we've seen the rules for numeric, string and boolean conversions of primitives. But we left a gap for objects. Now, as we know about methods and symbols it becomes possible to close it.
13
9
14
-
That's because the conversion of an object to primitive value (a number or a string) is a rare thing in practice.
10
+
[cut]
15
11
16
12
For objects, there's no to-boolean conversion, because all objects are `true` in a boolean context. So there are only string and numeric conversions.
17
13
18
-
For instance, numeric conversion happens in most mathematical functions. But do we run maths on an object as a whole? Rarely the case. It also occurs when we compare an object against a primitive: `user > 18`. But should we write like that? What such comparison actually means? Maybe we want to compare `18` against the age of the `user`? Then it would be more obvious to write `user.age > 18`. And it's easier to read and understand it too.
19
-
20
-
As for the string conversion... Where does it occur? Usually, when we output an object like `alert(obj)`. But `alert` and similar ways to show an object are only used for debugging and logging purposes. For real stuff, the output is more complicated. It is usually implemented with special object methods like `user.format(...)` or in more advanced ways.
14
+
The numeric conversion happens when we substract objects or apply mathematical functions. For instance, `Date` objects (to be covered in the chapter <info:date>) can be substracted, and the result of `date1 - date2` is the time difference between two dates.
21
15
22
-
Still, there are still valid reasons why we should know how to-primitive conversion works:
23
-
24
-
- Simple object-as-string output (`alert` and alike) is useable sometimes.
25
-
- Many built-in objects implement their own to-primitive conversion, we need to know how to work with that.
26
-
- Sometimes an unexpected conversion happens, and we should understand what's going on.
27
-
- The final one. There are quizzes and questions on interviews that rely on that knowledge. Looks like people think it's a good sign that person understands JavaScript if he knows type conversions well.
16
+
As for the string conversion -- it usually happens when we output an object like `alert(obj)` and in similar contexts.
28
17
29
18
## ToPrimitive
30
19
31
-
When an object is used in the context where a primitive is required, it is converted to primitive using the `ToPrimitive` algorithm, thoroughly described in the [specification](https://tc39.github.io/ecma262/#sec-toprimitive)).
20
+
When an object is used in the context where a primitive is required, for instance, in an `alert` or mathematical operations, it's converted to a primitive value using the `ToPrimitive` algorithm ([specification](https://tc39.github.io/ecma262/#sec-toprimitive)).
21
+
22
+
That algorithm allows to customize the conversion using in a special object method.
32
23
33
-
That algorithm joins all conversion cases and allows to customize them in a special object method.
24
+
Depending on the context, the conversion has a so-called "hint".
34
25
35
-
When a built-in function (like `alert` or mathematical functions) or an operator (like an addition or substraction) get an object as their argument, they initiate the to-primitive conversion using one of 3 so-called "hints":
26
+
There are 3 variants:
36
27
37
28
`"string"`
38
-
: When an operation expects a string, for object-to-string conversions, like:
29
+
: When an operation expects a string, for object-to-string conversions, like`alert`:
39
30
40
31
```js
41
32
// output
@@ -46,7 +37,7 @@ When a built-in function (like `alert` or mathematical functions) or an operator
46
37
```
47
38
48
39
`"number"`
49
-
: When an operation expects a number, for object-to-number conversions, like:
40
+
: When an operation expects a number, for object-to-number conversions, like maths:
50
41
51
42
```js
52
43
// explicit conversion
@@ -63,9 +54,7 @@ When a built-in function (like `alert` or mathematical functions) or an operator
63
54
`"default"`
64
55
: Occurs in rare cases when the operator is "not sure" what type to expect.
65
56
66
-
For instance, binary plus `+` can work both with strings (concatenates them) and numbers (adds them).
67
-
68
-
Or when an object is compared with a string, number or a symbol using `==`, then also the `"default"` hint is used.
57
+
For instance, binary plus `+` can work both with strings (concatenates them) and numbers (adds them), so both strings and numbers would do. Or when an object is compared using `==` with a string, number or a symbol.
69
58
70
59
```js
71
60
// binary plus
@@ -75,7 +64,7 @@ When a built-in function (like `alert` or mathematical functions) or an operator
75
64
if (user == 1) { ... };
76
65
```
77
66
78
-
Seems right to use that hint for binary addition `+` and equality check `==`, because they operate both on strings and numbers. Although, there's some inconsistency here. The greater/less operator `<>` can work with both strings and numbers too. Still, it uses "number" hint, not "default". That's for historical reasons.
67
+
The greater/less operator `<>` can work with both strings and numbers too. Still, it uses "number" hint, not "default". That's for historical reasons.
79
68
80
69
In practice, all built-in objects except for one case (`Date` object, we'll learn it later) implement `"default"` conversion the same way as `"number"`. And probably we should do the same.
81
70
@@ -185,19 +174,19 @@ An operation that initiated the conversion gets that primitive, and then continu
toString() { // toString handles all ToPrimitive in the absense of other methods
181
+
toString() { // toString handles all conversions in the absense of other methods
193
182
return"2";
194
183
}
195
184
};
196
185
197
186
alert(obj *2); // 4, ToPrimitive gives "2", then it becomes 2
198
187
```
199
188
200
-
- Binary plus first checks ifthe primitive is a string, and then does concatenation, otherwise performs `ToNumber` and works with numbers.
189
+
- Binary plus performs checks the primitive --if it's a string, then it does concatenation, otherwise performs `ToNumber` and works with numbers.
201
190
202
191
String example:
203
192
```js run
@@ -221,21 +210,22 @@ For instance:
221
210
alert(obj + 2); // 3 (ToPrimitive returned boolean, not string => ToNumber)
222
211
```
223
212
224
-
```smart header="Minor notes"
225
-
- If `Symbol.toPrimitive` exists, it *must* return an primitive, otherwise there will be an error.
226
-
- Methods `toString` or `valueOf` *should* return a primitive: if any of them returns and object, then it is ignored (like if the method didn't exist). That's the historical behavior.
213
+
```smart header="Historical notes"
214
+
For historical reasons, methods `toString` or `valueOf` *should* return a primitive: if any of them returns an object, then there's no error, but the object is ignored (like if the method didn't exist).
215
+
216
+
In contrast, `Symbol.toPrimitive` *must* return an primitive, otherwise there will be an error.
227
217
```
228
218
229
219
## Summary
230
220
231
221
The object-to-primitive conversion is called automatically by many built-in functions and operators that expect a primitive as a value.
232
222
233
223
There are 3 types (hints) of it:
234
-
-`"string"`
235
-
-`"number"`
236
-
-`"default"`
224
+
- `"string"` (for `alert` and other string conversions)
225
+
- `"number"` (for maths)
226
+
- `"default"` (few operators)
237
227
238
-
So, operations use the hint depending on what they expect to get. The specification describes explicitly which operator uses which hint. There are very few operators that "don't know what to expect" and use the `"default"`hint. Usuallyfor built-in objects it works the same way as `"number"`, so in practice the last two are often merged together.
228
+
The specification describes explicitly which operator uses which hint. There are very few operators that "don't know what to expect" and use the `"default"` hint. Usually for built-in objects `"default"` hint is handled the same way as `"number"`, so in practice the last two are often merged together.
0 commit comments