Skip to content

Commit 611fefd

Browse files
committed
fixes #1569
1 parent d4fd448 commit 611fefd

File tree

1 file changed

+64
-30
lines changed
  • 1-js/04-object-basics/05-object-toprimitive

1 file changed

+64
-30
lines changed

1-js/04-object-basics/05-object-toprimitive/article.md

+64-30
Original file line numberDiff line numberDiff line change
@@ -46,21 +46,27 @@ There are three variants of type conversion, so-called "hints", described in the
4646
`"default"`
4747
: Occurs in rare cases when the operator is "not sure" what type to expect.
4848

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.
5052

5153
```js
52-
// binary plus
53-
let total = car1 + car2;
54+
// binary plus uses the "default" hint
55+
let total = obj1 + obj2;
5456

55-
// obj == string/number/symbol
57+
// obj == number uses the "default" hint
5658
if (user == 1) { ... };
5759
```
5860

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.
6064

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.
6267

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+
```
6470
6571
**To do the conversion, JavaScript tries to find and call three object methods:**
6672
@@ -112,7 +118,29 @@ If there's no `Symbol.toPrimitive` then JavaScript tries to find them and try in
112118
- `toString -> valueOf` for "string" hint.
113119
- `valueOf -> toString` otherwise.
114120

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`:
116144

117145
```js run
118146
let user = {
@@ -159,7 +187,7 @@ In the absence of `Symbol.toPrimitive` and `valueOf`, `toString` will handle all
159187

160188
The important thing to know about all primitive-conversion methods is that they do not necessarily return the "hinted" primitive.
161189

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"`.
163191

164192
The only mandatory thing: these methods must return a primitive, not an object.
165193

@@ -169,35 +197,41 @@ For historical reasons, if `toString` or `valueOf` returns an object, there's no
169197
In contrast, `Symbol.toPrimitive` *must* return a primitive, otherwise there will be an error.
170198
```
171199

172-
## Further operations
200+
## Further conversions
173201

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.
175207

176208
For instance:
177209

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+
};
179217

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+
```
187220

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).
190223

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:
198225

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
234+
```
201235

202236
## Summary
203237

0 commit comments

Comments
 (0)