Skip to content

Commit 14d6875

Browse files
committed
work
1 parent a922219 commit 14d6875

File tree

1 file changed

+122
-7
lines changed
  • 1-js/2-first-steps/21-object-tostring-valueof

1 file changed

+122
-7
lines changed

1-js/2-first-steps/21-object-tostring-valueof/article.md

Lines changed: 122 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,50 @@
33

44
In the chapter <info:type-conversions> we've seen the rules for numeric, string and boolean conversions.
55

6-
But we left the gap for objects. Now let's close it.
6+
But we left a gap for objects. Now let's close it.
77

88
[cut]
99

10-
The operation that converts an object to a primitive is called [ToPrimitive](https://tc39.github.io/ecma262/#sec-toprimitive).
10+
For objects, there's a special additional conversion called [ToPrimitive](https://tc39.github.io/ecma262/#sec-toprimitive).
1111

12-
Some build-in language objects have their own implementation of it, but for most objects, including our own, it comes in two flavours:
12+
For some built-in objects it is implemented in special way, but mostly comes in two flavors:
1313

14-
- string
15-
- number
14+
- `ToPrimitive(obj, "string")` for a conversion to string
15+
- `ToPrimitive(obj, "number")` for a conversion to number
1616

17-
TODO
17+
So, if we convert an object to string, then first `ToPrimitive(obj, "string")` is applied, and then the resulting primitive is converted using primitive rules. The similar thing for a numeric conversion.
1818

19+
What's most interesting in `ToPrimitive` is its customizability.
1920

21+
## toString and valueOf
2022

21-
The method `toString` is automatically called by Javascript when the object is converted to a string:
23+
`ToPrimitive` is customizable via methods `toString()` and `valueOf()`.
24+
25+
The general algorithm of `ToPrimitive(obj, "string")` is:
26+
27+
28+
1. Call the method `obj.toString()` if it exists.
29+
2. If the result is a primitive, return it.
30+
3. Call the method `obj.valueOf()` if it exists.
31+
4. If the result is a primitive, return it.
32+
5. Otherwise `TypeError` (conversion failed)
33+
34+
35+
The `ToPrimitive(obj, "number")` is the same, but `valueOf()` and `toString()` are swapped:
36+
37+
1. Call the method `obj.valueOf()` if it exists.
38+
2. If the result is a primitive, return it.
39+
3. Call the method `obj.toString()` if it exists.
40+
4. If the result is a primitive, return it.
41+
5. Otherwise `TypeError` (conversion failed)
42+
43+
```smart header="ToPrimitive returns a primitive"
44+
As we can see, the result of `ToPrimitive` is always a primitive, because even if `toString/valueOf` return a non-primitive value, it is ignored.
45+
46+
But it can be any primitive. There's no control whether `toString()` returns exactly a string or, say a boolean.
47+
```
48+
49+
Let's see an example. Here we implement our own string conversion for `user`:
2250

2351
```js run
2452
let user = {
@@ -41,6 +69,93 @@ alert( user ); // User John
4169
Looks much better than the default `[object Object]`, right?
4270

4371

72+
Now let's add a custom numeric conversion with `valueOf`:
73+
74+
```js run
75+
let user = {
76+
77+
name: 'John',
78+
age: 30,
79+
80+
*!*
81+
valueOf() {
82+
return this.age;
83+
}
84+
*/!*
85+
86+
};
87+
88+
*!*
89+
alert( +user ); // 30
90+
*/!*
91+
```
92+
93+
In most projects though, only `toString()` is used, because objects are printed out (especially for debugging) much more often than added/substracted/etc.
94+
95+
If only `toString()` is implemented, then both string and numeric conversions use it.
96+
97+
## Examples for built-ins
98+
99+
Let's check a few examples to finally get the whole picture.
100+
101+
```js run
102+
alert( [] + 1 ); // '1'
103+
alert( [1] + 1 ); // '11'
104+
alert( [1,2] + 1 ); // '1,21'
105+
```
106+
107+
The array from the left side of `+` is first converted to primitive using `toPrimitive(obj, "number")`.
108+
109+
For arrays (and most other built-in objects) only `toString` is implemented, and it returns a list of items.
110+
111+
So we'll have:
112+
113+
```js
114+
alert( '' + 1 ); // '1'
115+
alert( '1' + 1 ); // '11'
116+
alert( '1,2' + 1 ); // '1,21'
117+
```
118+
119+
Now the addition has the first operand -- a string, so it converts the second one to a string also. Hence the result.
120+
121+
Now with a plain object:
122+
123+
```js run
124+
alert( +{} ); // NaN
125+
alert( {} + {} ); // [object Object][object Object]
126+
```
127+
128+
Plain objects actually have both `toString()` and `valueOf()`:
129+
130+
................TODO ALG OF OBJECT TOSTRING
131+
132+
The result of these operations should be somewhat obvious now.
133+
134+
135+
136+
137+
138+
139+
## [[Class]]
140+
141+
From the chapter <info:types> we know that `typeof` cannot distinguish different kinds of objects. Arrays, plain objects and others are all the same "object" for it.
142+
143+
But there's a semi-hidden way to access the right class.
144+
145+
Most built-in objects
146+
147+
148+
149+
150+
Here are some built-in objects
151+
152+
Most built-in object implement only `toString()`. From the algorithm string conversion is much more widely used
153+
154+
155+
156+
157+
158+
44159
The similar thing with the method `valueOf`. It is called when the object is converted to a number.
45160

46161
```js run

0 commit comments

Comments
 (0)