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/05-object-toprimitive/article.md
+64-30
Original file line number
Diff line number
Diff line change
@@ -46,21 +46,27 @@ There are three variants of type conversion, so-called "hints", described in the
46
46
`"default"`
47
47
: Occurs in rare cases when the operator is "not sure" what type to expect.
48
48
49
-
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, it's also unclear which conversion should be done.
49
+
For instance, binary plus `+` can work both with strings (concatenates them) and numbers (adds them), so both strings and numbers would do. So if the a binary plus gets an object as an argument, it uses the `"default"` hint to convert it.
50
+
51
+
Also, if an object is compared using `==` with a string, number or a symbol, it's also unclear which conversion should be done, so the `"default"` hint is used.
50
52
51
53
```js
52
-
// binary plus
53
-
let total = car1 + car2;
54
+
// binary plus uses the "default" hint
55
+
let total = obj1 + obj2;
54
56
55
-
// obj == string/number/symbol
57
+
// obj == number uses the "default" hint
56
58
if (user == 1) { ... };
57
59
```
58
60
59
-
The greater/less operator `<>` can work with both strings and numbers too. Still, it uses "number" hint, not "default". That's for historical reasons.
61
+
The greater and less comparison operators, such as `<` `>`, can work with both strings and numbers too. Still, they use the `"number"` hint, not `"default"`. That's for historical reasons.
62
+
63
+
In practice though, we don't need to remember these peculiar details, because all built-in objects except for one case (`Date` object, we'll learn it later) implement `"default"` conversion the same way as `"number"`. And we can do the same.
60
64
61
-
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.
65
+
```smart header="No `\"boolean\"` hint"
66
+
Please note -- there are only three hints. It's that simple.
62
67
63
-
Please note -- there are only three hints. It's that simple. There is no "boolean" hint (all objects are `true` in boolean context) or anything else. And if we treat `"default"` and `"number"` the same, like most built-ins do, then there are only two conversions.
68
+
There is no "boolean" hint (all objects are `true` in boolean context) or anything else. And if we treat `"default"` and `"number"` the same, like most built-ins do, then there are only two conversions.
69
+
```
64
70
65
71
**To do the conversion, JavaScript tries to find and call three object methods:**
66
72
@@ -112,7 +118,29 @@ If there's no `Symbol.toPrimitive` then JavaScript tries to find them and try in
112
118
-`toString -> valueOf` for "string" hint.
113
119
-`valueOf -> toString` otherwise.
114
120
115
-
For instance, here `user` does the same as above using a combination of `toString` and `valueOf`:
121
+
These methods must return a primitive value. If `toString` or `valueOf` returns an object, then it's ignored (same as if there were no method).
122
+
123
+
By default, a plain object has following `toString` and `valueOf` methods:
124
+
125
+
- The `toString` method returns a string `"[object Object]"`.
126
+
- The `valueOf` method returns an object itself.
127
+
128
+
Here's the demo:
129
+
130
+
```js run
131
+
let user = {name:"John"};
132
+
133
+
alert(user); // [object Object]
134
+
alert(user.valueOf() === user); // true
135
+
```
136
+
137
+
So if we try to use an object as a string, like in an `alert` or so, then by default we see `[object Object]`.
138
+
139
+
And the default `valueOf` is mentioned here only for the sake of completeness, to avoid any confusion. As you can see, it returns the object itself, and so is ignored. Don't ask me why, that's for historical reasons. So we can assume it doesn't exist.
140
+
141
+
Let's implement these methods.
142
+
143
+
For instance, here `user` does the same as above using a combination of `toString` and `valueOf` instead of `Symbol.toPrimitive`:
116
144
117
145
```js run
118
146
let user = {
@@ -159,7 +187,7 @@ In the absence of `Symbol.toPrimitive` and `valueOf`, `toString` will handle all
159
187
160
188
The important thing to know about all primitive-conversion methods is that they do not necessarily return the "hinted" primitive.
161
189
162
-
There is no control whether `toString()` returns exactly a string, or whether `Symbol.toPrimitive` method returns a number for a hint "number".
190
+
There is no control whether `toString` returns exactly a string, or whether `Symbol.toPrimitive` method returns a number for a hint `"number"`.
163
191
164
192
The only mandatory thing: these methods must return a primitive, not an object.
165
193
@@ -169,35 +197,41 @@ For historical reasons, if `toString` or `valueOf` returns an object, there's no
169
197
In contrast, `Symbol.toPrimitive` *must* return a primitive, otherwise there will be an error.
170
198
```
171
199
172
-
## Further operations
200
+
## Further conversions
173
201
174
-
An operation that initiated the conversion gets the primitive, and then continues to work with it, applying further conversions if necessary.
202
+
As we know already, many operators and functions perform type conversions, e.g. multiplication `*` converts operatnds to numbers.
203
+
204
+
If we pass an object as an argument, then there are two stages:
205
+
1. The object is converted to a primitive (using the rules described above).
206
+
2. If the resulting primitive isn't of the right type, it's converted.
175
207
176
208
For instance:
177
209
178
-
- Mathematical operations, except binary plus, convert the primitive to a number:
210
+
```js run
211
+
let obj = {
212
+
// toString handles all conversions in the absence of other methods
213
+
toString() {
214
+
return"2";
215
+
}
216
+
};
179
217
180
-
```js run
181
-
let obj = {
182
-
// toString handles all conversions in the absence of other methods
183
-
toString() {
184
-
return"2";
185
-
}
186
-
};
218
+
alert(obj *2); // 4, object converted to primitive "2", then multiplication made it a number
219
+
```
187
220
188
-
alert(obj *2); // 4, object converted to primitive "2", then multiplication made it a number
189
-
```
221
+
1. The multiplication `obj * 2` first converts the object to primitive (that's a string `"2"`).
222
+
2. Then `"2" * 2` becomes `2 * 2` (the string is converted to number).
190
223
191
-
- Binary plus will concatenate strings in the same situation:
192
-
```js run
193
-
let obj = {
194
-
toString() {
195
-
return "2";
196
-
}
197
-
};
224
+
Binary plus will concatenate strings in the same situation, as it gladly accepts a string:
198
225
199
-
alert(obj + 2); // 22 (conversion to primitive returned a string => concatenation)
200
-
```
226
+
```js run
227
+
let obj = {
228
+
toString() {
229
+
return"2";
230
+
}
231
+
};
232
+
233
+
alert(obj +2); // 22 ("2" + 2), conversion to primitive returned a string => concatenation
0 commit comments