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/09-object-toprimitive/article.md
+35-13Lines changed: 35 additions & 13 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -3,19 +3,36 @@
3
3
4
4
What happens when objects are added `obj1 + obj2`, subtracted `obj1 - obj2` or printed using `alert(obj)`?
5
5
6
-
In that case, objects are auto-converted to primitives, and then the operation is carried out.
6
+
JavaScript doesn't exactly allow to customize how operators work on objects. Unlike some other programming languages, such as Ruby or C++, we can't implement a special object method to handle an addition (or other operators).
7
+
8
+
In case of such operations, objects are auto-converted to primitives, and then the operation is carried out over these primitives and results in a primitive value.
9
+
10
+
That's an important limitation, as the result of `obj1 + obj2` can't be another object!
11
+
12
+
E.g. we can't make objects representing vectors or matrices (or archievements or whatever), add them and expect a "summed" object as the result. Such architectural feats are automatically "off the board".
13
+
14
+
So, because we can't do much here, in real projects people don't do maths with objects. When it happens, it's usually because of coding mistake.
15
+
16
+
In this chapter we'll cover how an object converts to primitive and how to customize it.
17
+
18
+
We have two purposes:
19
+
20
+
1. It will allow us to understand what's going on in case of coding mistakes, when such an operation happened accidentally.
21
+
2. There are exceptions, where such operations are possible and look good. E.g. subtracting or comparing dates (`Date` objects). We'll come across them later.
22
+
23
+
## Conversion rules
7
24
8
25
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 fill it.
9
26
10
27
1. All objects are `true` in a boolean context. There are only numeric and string conversions.
11
28
2. The numeric conversion happens when we subtract objects or apply mathematical functions. For instance, `Date` objects (to be covered in the chapter <info:date>) can be subtracted, and the result of `date1 - date2` is the time difference between two dates.
12
29
3. As for the string conversion -- it usually happens when we output an object like `alert(obj)` and in similar contexts.
13
30
14
-
## ToPrimitive
15
-
16
31
We can fine-tune string and numeric conversion, using special object methods.
17
32
18
-
There are three variants of type conversion, so-called "hints", described in the [specification](https://tc39.github.io/ecma262/#sec-toprimitive):
33
+
There are three variants of type conversion, that happen in various situations.
34
+
35
+
They're called "hints", as described in the [specification](https://tc39.github.io/ecma262/#sec-toprimitive):
19
36
20
37
`"string"`
21
38
: For an object-to-string conversion, when we're doing an operation on an object that expects a string, like `alert`:
@@ -82,11 +99,14 @@ Let's start from the first method. There's a built-in symbol named `Symbol.toPri
82
99
83
100
```js
84
101
obj[Symbol.toPrimitive] = function(hint) {
85
-
// must return a primitive value
102
+
// here goes the code to convert this object to a primitive
103
+
// it must return a primitive value
86
104
// hint = one of "string", "number", "default"
87
105
};
88
106
```
89
107
108
+
If the method `Symbol.toPrimitive` exists, it's used for all hints, and no more methods are needed.
109
+
90
110
For instance, here `user` object implements it:
91
111
92
112
```js run
@@ -111,12 +131,12 @@ As we can see from the code, `user` becomes a self-descriptive string or a money
111
131
112
132
## toString/valueOf
113
133
114
-
Methods `toString` and `valueOf` come from ancient times. They are not symbols (symbols did not exist that long ago), but rather "regular" string-named methods. They provide an alternative "old-style" way to implement the conversion.
134
+
If there's no `Symbol.toPrimitive` then JavaScript tries to find methods`toString` and `valueOf`:
115
135
116
-
If there's no `Symbol.toPrimitive` then JavaScript tries to find them and try in the order:
136
+
- For the "string" hint: `toString`, and if it doesn't exist, then `valueOf` (so `toString` has the priority for stirng conversions).
137
+
- For other hints: `valueOf`, and if it doesn't exist, then `toString` (so `valueOf` has the priority for maths).
117
138
118
-
-`toString -> valueOf` for "string" hint.
119
-
-`valueOf -> toString` otherwise.
139
+
Methods `toString` and `valueOf` come from ancient times. They are not symbols (symbols did not exist that long ago), but rather "regular" string-named methods. They provide an alternative "old-style" way to implement the conversion.
120
140
121
141
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).
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
158
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.
159
+
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
160
141
-
Let's implement these methods.
161
+
Let's implement these methods to customize the conversion.
142
162
143
163
For instance, here `user` does the same as above using a combination of `toString` and `valueOf` instead of `Symbol.toPrimitive`:
In the absence of `Symbol.toPrimitive` and `valueOf`, `toString` will handle all primitive conversions.
185
205
186
-
##Return types
206
+
### A conversion can return any primitive type
187
207
188
208
The important thing to know about all primitive-conversion methods is that they do not necessarily return the "hinted" primitive.
189
209
@@ -252,4 +272,6 @@ The conversion algorithm is:
252
272
3. Otherwise if hint is `"number"` or `"default"`
253
273
- try `obj.valueOf()` and `obj.toString()`, whatever exists.
254
274
255
-
In practice, it's often enough to implement only `obj.toString()` as a "catch-all" method for all conversions that return a "human-readable" representation of an object, for logging or debugging purposes.
275
+
In practice, it's often enough to implement only `obj.toString()` as a "catch-all" method for string conversions that should return a "human-readable" representation of an object, for logging or debugging purposes.
276
+
277
+
As for math operations, JavaScript doesn't provide a way to "override" them using methods, so real life projects rarely use them on objects.
0 commit comments