-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path06_object.html
More file actions
660 lines (451 loc) · 90.3 KB
/
06_object.html
File metadata and controls
660 lines (451 loc) · 90.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
<!doctype html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>The Secret Life of Objects :: Eloquent JavaScript</title>
<link rel=stylesheet href="js/node_modules/codemirror/lib/codemirror.css">
<script src="js/acorn_codemirror.js"></script>
<link rel=stylesheet href="css/ejs.css">
<script src="js/sandbox.js"></script>
<script src="js/ejs.js"></script><script>var chapNum = 6;var sandboxLoadFiles = ["code/chapter/06_object.js"];</script></head>
<article>
<nav><a href="05_higher_order.html" title="previous chapter">◀</a> <a href="index.html" title="cover">◆</a> <a href="07_robot.html" title="next chapter">▶</a></nav>
<h1><span class=chap_num>Chapter 6</span>The Secret Life of Objects</h1>
<blockquote>
<p><a class="p_ident" id="p_gDFRhOpA5B" href="#p_gDFRhOpA5B" tabindex="-1" role="presentation"></a>An abstract data type is realized by writing a special kind of program […] which defines the type in terms of the operations which can be performed on it.</p>
<footer>Barbara Liskov, <cite>Programming with Abstract Data Types</cite></footer>
</blockquote><figure class="chapter framed"><img src="img/chapter_picture_6.jpg" alt="Picture of a rabbit with its proto-rabbit"></figure>
<p><a class="p_ident" id="p_KkunxYMaeO" href="#p_KkunxYMaeO" tabindex="-1" role="presentation"></a><a href="04_data.html">Chapter 4</a> introduced JavaScript’s objects. In programming culture, we have a thing called <em>object-oriented programming</em>, a set of techniques that use objects (and related concepts) as the central principle of program organization.</p>
<p><a class="p_ident" id="p_iS8mjYhuYY" href="#p_iS8mjYhuYY" tabindex="-1" role="presentation"></a>Though no one really agrees on its precise definition, object-oriented programming has shaped the design of many programming languages, including JavaScript. This chapter will describe the way these ideas can be applied in JavaScript.</p>
<h2><a class="h_ident" id="h_hn18MBjoh2" href="#h_hn18MBjoh2" tabindex="-1" role="presentation"></a>Encapsulation</h2>
<p><a class="p_ident" id="p_76wqEvwMj/" href="#p_76wqEvwMj/" tabindex="-1" role="presentation"></a>The core idea in object-oriented programming is to divide programs into smaller pieces and make each piece responsible for managing its own state.</p>
<p><a class="p_ident" id="p_Ww3ymqEfW3" href="#p_Ww3ymqEfW3" tabindex="-1" role="presentation"></a>This way, some knowledge about the way a piece of the program works can be kept <em>local</em> to that piece. Someone working on the rest of the program does not have to remember or even be aware of that knowledge. Whenever these local details change, only the code directly around it needs to be updated.</p>
<p id="interface"><a class="p_ident" id="p_m1XIFUcUIg" href="#p_m1XIFUcUIg" tabindex="-1" role="presentation"></a>Different pieces of such a program interact with each other through <em>interfaces</em>, limited sets of functions or bindings that provide useful functionality at a more abstract level, hiding their precise implementation.</p>
<p><a class="p_ident" id="p_n3o8Ctd3cn" href="#p_n3o8Ctd3cn" tabindex="-1" role="presentation"></a>Such program pieces are modeled using objects. Their interface consists of a specific set of methods and properties. Properties that are part of the interface are called <em>public</em>. The others, which outside code should not be touching, are called <em>private</em>.</p>
<p><a class="p_ident" id="p_RExXHtzJn1" href="#p_RExXHtzJn1" tabindex="-1" role="presentation"></a>Many languages provide a way to distinguish public and private properties and prevent outside code from accessing the private ones altogether. JavaScript, once again taking the minimalist approach, does not—not yet at least. There is work underway to add this to the language.</p>
<p><a class="p_ident" id="p_h9lEzfGlTT" href="#p_h9lEzfGlTT" tabindex="-1" role="presentation"></a>Even though the language doesn’t have this distinction built in, JavaScript programmers <em>are</em> successfully using this idea. Typically, the available interface is described in documentation or comments. It is also common to put an underscore (<code>_</code>) character at the start of property names to indicate that those properties are private.</p>
<p><a class="p_ident" id="p_ysFjuxKxY0" href="#p_ysFjuxKxY0" tabindex="-1" role="presentation"></a>Separating interface from implementation is a great idea. It is usually called <em>encapsulation</em>.</p>
<h2 id="obj_methods"><a class="h_ident" id="h_fkrGgDyRWc" href="#h_fkrGgDyRWc" tabindex="-1" role="presentation"></a>Methods</h2>
<p><a class="p_ident" id="p_DKj01h8Gzf" href="#p_DKj01h8Gzf" tabindex="-1" role="presentation"></a>Methods are nothing more than properties that hold function values. This is a simple method:</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_BYfVVcFY2P" href="#c_BYfVVcFY2P" tabindex="-1" role="presentation"></a><span class="cm-keyword">let</span> <span class="cm-def">rabbit</span> <span class="cm-operator">=</span> {};
<span class="cm-variable">rabbit</span>.<span class="cm-property">speak</span> <span class="cm-operator">=</span> <span class="cm-keyword">function</span>(<span class="cm-def">line</span>) {
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string-2">`The rabbit says '${</span><span class="cm-variable-2">line</span><span class="cm-string-2">}</span><span class="cm-string-2">'`</span>);
};
<span class="cm-variable">rabbit</span>.<span class="cm-property">speak</span>(<span class="cm-string">"I'm alive."</span>);
<span class="cm-comment">// → The rabbit says 'I'm alive.'</span></pre>
<p><a class="p_ident" id="p_N+6e0UGvFo" href="#p_N+6e0UGvFo" tabindex="-1" role="presentation"></a>Usually a method needs to do something with the object it was called on. When a function is called as a method—looked up as a property and immediately called, as in <code>object.method()</code>—the binding called <code>this</code> in its body automatically points at the object that it was called on.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_cXuIwPt+3C" href="#c_cXuIwPt+3C" tabindex="-1" role="presentation"></a><span class="cm-keyword">function</span> <span class="cm-def">speak</span>(<span class="cm-def">line</span>) {
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string-2">`The ${</span><span class="cm-keyword">this</span>.<span class="cm-property">type</span><span class="cm-string-2">}</span> <span class="cm-string-2">rabbit says '${</span><span class="cm-variable-2">line</span><span class="cm-string-2">}</span><span class="cm-string-2">'`</span>);
}
<span class="cm-keyword">let</span> <span class="cm-def">whiteRabbit</span> <span class="cm-operator">=</span> {<span class="cm-property">type</span>: <span class="cm-string">"white"</span>, <span class="cm-property">speak</span>};
<span class="cm-keyword">let</span> <span class="cm-def">hungryRabbit</span> <span class="cm-operator">=</span> {<span class="cm-property">type</span>: <span class="cm-string">"hungry"</span>, <span class="cm-property">speak</span>};
<span class="cm-variable">whiteRabbit</span>.<span class="cm-property">speak</span>(<span class="cm-string">"Oh my ears and whiskers, "</span> <span class="cm-operator">+</span>
<span class="cm-string">"how late it's getting!"</span>);
<span class="cm-comment">// → The white rabbit says 'Oh my ears and whiskers, how</span>
<span class="cm-comment">// late it's getting!'</span>
<span class="cm-variable">hungryRabbit</span>.<span class="cm-property">speak</span>(<span class="cm-string">"I could use a carrot right now."</span>);
<span class="cm-comment">// → The hungry rabbit says 'I could use a carrot right now.'</span></pre>
<p id="call_method"><a class="p_ident" id="p_llBwR5t6LB" href="#p_llBwR5t6LB" tabindex="-1" role="presentation"></a>You can think of <code>this</code> as an extra parameter that is passed in a different way. If you want to pass it explicitly, you can use a function’s <code>call</code> method, which takes the <code>this</code> value as its first argument and treats further arguments as normal parameters.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_9lDr0S2nEw" href="#c_9lDr0S2nEw" tabindex="-1" role="presentation"></a><span class="cm-variable">speak</span>.<span class="cm-property">call</span>(<span class="cm-variable">hungryRabbit</span>, <span class="cm-string">"Burp!"</span>);
<span class="cm-comment">// → The hungry rabbit says 'Burp!'</span></pre>
<p><a class="p_ident" id="p_8ZeOAlGKXe" href="#p_8ZeOAlGKXe" tabindex="-1" role="presentation"></a>Since each function has its own <code>this</code> binding, whose value depends on the way it is called, you cannot refer to the <code>this</code> of the wrapping scope in a regular function defined with the <code>function</code> keyword.</p>
<p><a class="p_ident" id="p_C4AqzW0fAV" href="#p_C4AqzW0fAV" tabindex="-1" role="presentation"></a>Arrow functions are different—they do not bind their own <code>this</code> but can see the <code>this</code> binding of the scope around them. Thus, you can do something like the following code, which references <code>this</code> from inside a local function:</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_wg++tcauWw" href="#c_wg++tcauWw" tabindex="-1" role="presentation"></a><span class="cm-keyword">function</span> <span class="cm-def">normalize</span>() {
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-keyword">this</span>.<span class="cm-property">coords</span>.<span class="cm-property">map</span>(<span class="cm-def">n</span> <span class="cm-operator">=></span> <span class="cm-variable-2">n</span> <span class="cm-operator">/</span> <span class="cm-keyword">this</span>.<span class="cm-property">length</span>));
}
<span class="cm-variable">normalize</span>.<span class="cm-property">call</span>({<span class="cm-property">coords</span>: [<span class="cm-number">0</span>, <span class="cm-number">2</span>, <span class="cm-number">3</span>], <span class="cm-property">length</span>: <span class="cm-number">5</span>});
<span class="cm-comment">// → [0, 0.4, 0.6]</span></pre>
<p><a class="p_ident" id="p_YkWiHcMuVP" href="#p_YkWiHcMuVP" tabindex="-1" role="presentation"></a>If I had written the argument to <code>map</code> using the <code>function</code> keyword, the code wouldn’t work.</p>
<h2 id="prototypes"><a class="h_ident" id="h_SumMlRB7yn" href="#h_SumMlRB7yn" tabindex="-1" role="presentation"></a>Prototypes</h2>
<p><a class="p_ident" id="p_hi1TWnD/2p" href="#p_hi1TWnD/2p" tabindex="-1" role="presentation"></a>Watch closely.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_P0GVdA5c8J" href="#c_P0GVdA5c8J" tabindex="-1" role="presentation"></a><span class="cm-keyword">let</span> <span class="cm-def">empty</span> <span class="cm-operator">=</span> {};
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">empty</span>.<span class="cm-property">toString</span>);
<span class="cm-comment">// → function toString(){…}</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">empty</span>.<span class="cm-property">toString</span>());
<span class="cm-comment">// → [object Object]</span></pre>
<p><a class="p_ident" id="p_NUxqIXvg/G" href="#p_NUxqIXvg/G" tabindex="-1" role="presentation"></a>I pulled a property out of an empty object. Magic!</p>
<p><a class="p_ident" id="p_bJubL+gx0p" href="#p_bJubL+gx0p" tabindex="-1" role="presentation"></a>Well, not really. I have simply been withholding information about the way JavaScript objects work. In addition to their set of properties, most objects also have a <em>prototype</em>. A prototype is another object that is used as a fallback source of properties. When an object gets a request for a property that it does not have, its prototype will be searched for the property, then the prototype’s prototype, and so on.</p>
<p><a class="p_ident" id="p_XPuG8mFwbT" href="#p_XPuG8mFwbT" tabindex="-1" role="presentation"></a>So who is the prototype of that empty object? It is the great ancestral prototype, the entity behind almost all objects, <code>Object.prototype</code>.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_7Q/HaNra3M" href="#c_7Q/HaNra3M" tabindex="-1" role="presentation"></a><span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">Object</span>.<span class="cm-property">getPrototypeOf</span>({}) <span class="cm-operator">==</span>
<span class="cm-variable">Object</span>.<span class="cm-property">prototype</span>);
<span class="cm-comment">// → true</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">Object</span>.<span class="cm-property">getPrototypeOf</span>(<span class="cm-variable">Object</span>.<span class="cm-property">prototype</span>));
<span class="cm-comment">// → null</span></pre>
<p><a class="p_ident" id="p_GfVMu4b4D+" href="#p_GfVMu4b4D+" tabindex="-1" role="presentation"></a>As you guess, <code>Object.<wbr>getPrototypeOf</code> returns the prototype of an object.</p>
<p><a class="p_ident" id="p_4S0XbM8aBm" href="#p_4S0XbM8aBm" tabindex="-1" role="presentation"></a>The prototype relations of JavaScript objects form a tree-shaped structure, and at the root of this structure sits <code>Object.prototype</code>. It provides a few methods that show up in all objects, such as <code>toString</code>, which converts an object to a string representation.</p>
<p><a class="p_ident" id="p_j+MLWf3JXr" href="#p_j+MLWf3JXr" tabindex="-1" role="presentation"></a>Many objects don’t directly have <code>Object.prototype</code> as their prototype but instead have another object that provides a different set of default properties. Functions derive from <code>Function.<wbr>prototype</code>, and arrays derive from <code>Array.prototype</code>.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_NgntUaXZ1S" href="#c_NgntUaXZ1S" tabindex="-1" role="presentation"></a><span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">Object</span>.<span class="cm-property">getPrototypeOf</span>(<span class="cm-variable">Math</span>.<span class="cm-property">max</span>) <span class="cm-operator">==</span>
<span class="cm-variable">Function</span>.<span class="cm-property">prototype</span>);
<span class="cm-comment">// → true</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">Object</span>.<span class="cm-property">getPrototypeOf</span>([]) <span class="cm-operator">==</span>
<span class="cm-variable">Array</span>.<span class="cm-property">prototype</span>);
<span class="cm-comment">// → true</span></pre>
<p><a class="p_ident" id="p_1fIXMD62Nu" href="#p_1fIXMD62Nu" tabindex="-1" role="presentation"></a>Such a prototype object will itself have a prototype, often <code>Object.prototype</code>, so that it still indirectly provides methods like <code>toString</code>.</p>
<p><a class="p_ident" id="p_H2XhzSHHJP" href="#p_H2XhzSHHJP" tabindex="-1" role="presentation"></a>You can use <code>Object.create</code> to create an object with a specific prototype.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_gSGrvGTpkW" href="#c_gSGrvGTpkW" tabindex="-1" role="presentation"></a><span class="cm-keyword">let</span> <span class="cm-def">protoRabbit</span> <span class="cm-operator">=</span> {
<span class="cm-property">speak</span>(<span class="cm-def">line</span>) {
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string-2">`The ${</span><span class="cm-keyword">this</span>.<span class="cm-property">type</span><span class="cm-string-2">}</span> <span class="cm-string-2">rabbit says '${</span><span class="cm-variable-2">line</span><span class="cm-string-2">}</span><span class="cm-string-2">'`</span>);
}
};
<span class="cm-keyword">let</span> <span class="cm-def">killerRabbit</span> <span class="cm-operator">=</span> <span class="cm-variable">Object</span>.<span class="cm-property">create</span>(<span class="cm-variable">protoRabbit</span>);
<span class="cm-variable">killerRabbit</span>.<span class="cm-property">type</span> <span class="cm-operator">=</span> <span class="cm-string">"killer"</span>;
<span class="cm-variable">killerRabbit</span>.<span class="cm-property">speak</span>(<span class="cm-string">"SKREEEE!"</span>);
<span class="cm-comment">// → The killer rabbit says 'SKREEEE!'</span></pre>
<p><a class="p_ident" id="p_eBm7AuQUBb" href="#p_eBm7AuQUBb" tabindex="-1" role="presentation"></a>A property like <code>speak(line)</code> in an object expression is a shorthand way of defining a method. It creates a property called <code>speak</code> and gives it a function as its value.</p>
<p><a class="p_ident" id="p_y8kzFoQw1W" href="#p_y8kzFoQw1W" tabindex="-1" role="presentation"></a>The “proto” rabbit acts as a container for the properties that are shared by all rabbits. An individual rabbit object, like the killer rabbit, contains properties that apply only to itself—in this case its type—and derives shared properties from its prototype.</p>
<h2 id="classes"><a class="h_ident" id="h_7RhGr+474h" href="#h_7RhGr+474h" tabindex="-1" role="presentation"></a>Classes</h2>
<p><a class="p_ident" id="p_iYbDLfqSFW" href="#p_iYbDLfqSFW" tabindex="-1" role="presentation"></a>JavaScript’s prototype system can be interpreted as a somewhat informal take on an object-oriented concept called <em>classes</em>. A class defines the shape of a type of object—what methods and properties it has. Such an object is called an <em>instance</em> of the class.</p>
<p><a class="p_ident" id="p_GhnffIpINq" href="#p_GhnffIpINq" tabindex="-1" role="presentation"></a>Prototypes are useful for defining properties for which all instances of a class share the same value, such as methods. Properties that differ per instance, such as our rabbits’ <code>type</code> property, need to be stored directly in the objects themselves.</p>
<p id="constructors"><a class="p_ident" id="p_hflVCtYTKN" href="#p_hflVCtYTKN" tabindex="-1" role="presentation"></a>So to create an instance of a given class, you have to make an object that derives from the proper prototype, but you <em>also</em> have to make sure it, itself, has the properties that instances of this class are supposed to have. This is what a <em>constructor</em> function does.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_oOKUeIzSVa" href="#c_oOKUeIzSVa" tabindex="-1" role="presentation"></a><span class="cm-keyword">function</span> <span class="cm-def">makeRabbit</span>(<span class="cm-def">type</span>) {
<span class="cm-keyword">let</span> <span class="cm-def">rabbit</span> <span class="cm-operator">=</span> <span class="cm-variable">Object</span>.<span class="cm-property">create</span>(<span class="cm-variable">protoRabbit</span>);
<span class="cm-variable-2">rabbit</span>.<span class="cm-property">type</span> <span class="cm-operator">=</span> <span class="cm-variable-2">type</span>;
<span class="cm-keyword">return</span> <span class="cm-variable-2">rabbit</span>;
}</pre>
<p><a class="p_ident" id="p_j8irpNziDb" href="#p_j8irpNziDb" tabindex="-1" role="presentation"></a>JavaScript provides a way to make defining this type of function easier. If you put the keyword <code>new</code> in front of a function call, the function is treated as a constructor. This means that an object with the right prototype is automatically created, bound to <code>this</code> in the function, and returned at the end of the function.</p>
<p><a class="p_ident" id="p_svyxVljkeg" href="#p_svyxVljkeg" tabindex="-1" role="presentation"></a>The prototype object used when constructing objects is found by taking the <code>prototype</code> property of the constructor function.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_p+u3OtMv8K" href="#c_p+u3OtMv8K" tabindex="-1" role="presentation"></a><span class="cm-keyword">function</span> <span class="cm-def">Rabbit</span>(<span class="cm-def">type</span>) {
<span class="cm-keyword">this</span>.<span class="cm-property">type</span> <span class="cm-operator">=</span> <span class="cm-variable-2">type</span>;
}
<span class="cm-variable">Rabbit</span>.<span class="cm-property">prototype</span>.<span class="cm-property">speak</span> <span class="cm-operator">=</span> <span class="cm-keyword">function</span>(<span class="cm-def">line</span>) {
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string-2">`The ${</span><span class="cm-keyword">this</span>.<span class="cm-property">type</span><span class="cm-string-2">}</span> <span class="cm-string-2">rabbit says '${</span><span class="cm-variable-2">line</span><span class="cm-string-2">}</span><span class="cm-string-2">'`</span>);
};
<span class="cm-keyword">let</span> <span class="cm-def">weirdRabbit</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">Rabbit</span>(<span class="cm-string">"weird"</span>);</pre>
<p><a class="p_ident" id="p_IZA8w6crnz" href="#p_IZA8w6crnz" tabindex="-1" role="presentation"></a>Constructors (all functions, in fact) automatically get a property named <code>prototype</code>, which by default holds a plain, empty object that derives from <code>Object.prototype</code>. You can overwrite it with a new object if you want. Or you can add properties to the existing object, as the example does.</p>
<p><a class="p_ident" id="p_DJG7BO1oTU" href="#p_DJG7BO1oTU" tabindex="-1" role="presentation"></a>By convention, the names of constructors are capitalized so that they can easily be distinguished from other functions.</p>
<p><a class="p_ident" id="p_9bg1buEAma" href="#p_9bg1buEAma" tabindex="-1" role="presentation"></a>It is important to understand the distinction between the way a prototype is associated with a constructor (through its <code>prototype</code> property) and the way objects <em>have</em> a prototype (which can be found with <code>Object.<wbr>getPrototypeOf</code>). The actual prototype of a constructor is <code>Function.<wbr>prototype</code> since constructors are functions. Its <code>prototype</code> <em>property</em> holds the prototype used for instances created through it.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_KDYP9dCWfS" href="#c_KDYP9dCWfS" tabindex="-1" role="presentation"></a><span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">Object</span>.<span class="cm-property">getPrototypeOf</span>(<span class="cm-variable">Rabbit</span>) <span class="cm-operator">==</span>
<span class="cm-variable">Function</span>.<span class="cm-property">prototype</span>);
<span class="cm-comment">// → true</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">Object</span>.<span class="cm-property">getPrototypeOf</span>(<span class="cm-variable">weirdRabbit</span>) <span class="cm-operator">==</span>
<span class="cm-variable">Rabbit</span>.<span class="cm-property">prototype</span>);
<span class="cm-comment">// → true</span></pre>
<h2><a class="h_ident" id="h_hPv1gHC33s" href="#h_hPv1gHC33s" tabindex="-1" role="presentation"></a>Class notation</h2>
<p><a class="p_ident" id="p_T5Ghz5me/w" href="#p_T5Ghz5me/w" tabindex="-1" role="presentation"></a>So JavaScript classes are constructor functions with a prototype property. That is how they work, and until 2015, that was how you had to write them. These days, we have a less awkward notation.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_kqKA+SZ6vS" href="#c_kqKA+SZ6vS" tabindex="-1" role="presentation"></a><span class="cm-keyword">class</span> <span class="cm-def">Rabbit</span> {
<span class="cm-property">constructor</span>(<span class="cm-def">type</span>) {
<span class="cm-keyword">this</span>.<span class="cm-property">type</span> <span class="cm-operator">=</span> <span class="cm-variable-2">type</span>;
}
<span class="cm-property">speak</span>(<span class="cm-def">line</span>) {
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string-2">`The ${</span><span class="cm-keyword">this</span>.<span class="cm-property">type</span><span class="cm-string-2">}</span> <span class="cm-string-2">rabbit says '${</span><span class="cm-variable-2">line</span><span class="cm-string-2">}</span><span class="cm-string-2">'`</span>);
}
}
<span class="cm-keyword">let</span> <span class="cm-def">killerRabbit</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">Rabbit</span>(<span class="cm-string">"killer"</span>);
<span class="cm-keyword">let</span> <span class="cm-def">blackRabbit</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">Rabbit</span>(<span class="cm-string">"black"</span>);</pre>
<p><a class="p_ident" id="p_wpM2aH78Jp" href="#p_wpM2aH78Jp" tabindex="-1" role="presentation"></a>The <code>class</code> keyword starts a class declaration, which allows us to define a constructor and a set of methods all in a single place. Any number of methods may be written inside the declaration’s braces. The one named <code>constructor</code> is treated specially. It provides the actual constructor function, which will be bound to the name <code>Rabbit</code>. The others are packaged into that constructor’s prototype. Thus, the earlier class declaration is equivalent to the constructor definition from the previous section. It just looks nicer.</p>
<p><a class="p_ident" id="p_rJ0WbDXCuo" href="#p_rJ0WbDXCuo" tabindex="-1" role="presentation"></a>Class declarations currently allow only <em>methods</em>—properties that hold functions—to be added to the prototype. This can be somewhat inconvenient when you want to save a non-function value in there. The next version of the language will probably improve this. For now, you can create such properties by directly manipulating the prototype after you’ve defined the class.</p>
<p><a class="p_ident" id="p_pp17mMu8If" href="#p_pp17mMu8If" tabindex="-1" role="presentation"></a>Like <code>function</code>, <code>class</code> can be used both in statements and in expressions. When used as an expression, it doesn’t define a binding but just produces the constructor as a value. You are allowed to omit the class name in a class expression.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_79re+GWcTJ" href="#c_79re+GWcTJ" tabindex="-1" role="presentation"></a><span class="cm-keyword">let</span> <span class="cm-def">object</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-keyword">class</span> { <span class="cm-property">getWord</span>() { <span class="cm-keyword">return</span> <span class="cm-string">"hello"</span>; } };
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">object</span>.<span class="cm-property">getWord</span>());
<span class="cm-comment">// → hello</span></pre>
<h2><a class="h_ident" id="h_oUlUep3Os8" href="#h_oUlUep3Os8" tabindex="-1" role="presentation"></a>Overriding derived properties</h2>
<p><a class="p_ident" id="p_Xbxf2Ooo0z" href="#p_Xbxf2Ooo0z" tabindex="-1" role="presentation"></a>When you add a property to an object, whether it is present in the prototype or not, the property is added to the object <em>itself</em>. If there was already a property with the same name in the prototype, this property will no longer affect the object, as it is now hidden behind the object’s own property.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_vja6JbO0si" href="#c_vja6JbO0si" tabindex="-1" role="presentation"></a><span class="cm-variable">Rabbit</span>.<span class="cm-property">prototype</span>.<span class="cm-property">teeth</span> <span class="cm-operator">=</span> <span class="cm-string">"small"</span>;
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">killerRabbit</span>.<span class="cm-property">teeth</span>);
<span class="cm-comment">// → small</span>
<span class="cm-variable">killerRabbit</span>.<span class="cm-property">teeth</span> <span class="cm-operator">=</span> <span class="cm-string">"long, sharp, and bloody"</span>;
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">killerRabbit</span>.<span class="cm-property">teeth</span>);
<span class="cm-comment">// → long, sharp, and bloody</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">blackRabbit</span>.<span class="cm-property">teeth</span>);
<span class="cm-comment">// → small</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">Rabbit</span>.<span class="cm-property">prototype</span>.<span class="cm-property">teeth</span>);
<span class="cm-comment">// → small</span></pre>
<p><a class="p_ident" id="p_HM5YtS3KgJ" href="#p_HM5YtS3KgJ" tabindex="-1" role="presentation"></a>The following diagram sketches the situation after this code has run. The <code>Rabbit</code> and <code>Object</code> prototypes lie behind <code>killerRabbit</code> as a kind of backdrop, where properties that are not found in the object itself can be looked up.</p><figure><img src="img/rabbits.svg" alt="Rabbit object prototype schema"></figure>
<p><a class="p_ident" id="p_or3/lz1DV8" href="#p_or3/lz1DV8" tabindex="-1" role="presentation"></a>Overriding properties that exist in a prototype can be a useful thing to do. As the rabbit teeth example shows, overriding can be used to express exceptional properties in instances of a more generic class of objects, while letting the nonexceptional objects take a standard value from their prototype.</p>
<p><a class="p_ident" id="p_GIhnbh3MdO" href="#p_GIhnbh3MdO" tabindex="-1" role="presentation"></a>Overriding is also used to give the standard function and array prototypes a different <code>toString</code> method than the basic object prototype.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_DrIRvUgeOD" href="#c_DrIRvUgeOD" tabindex="-1" role="presentation"></a><span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">Array</span>.<span class="cm-property">prototype</span>.<span class="cm-property">toString</span> <span class="cm-operator">==</span>
<span class="cm-variable">Object</span>.<span class="cm-property">prototype</span>.<span class="cm-property">toString</span>);
<span class="cm-comment">// → false</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>([<span class="cm-number">1</span>, <span class="cm-number">2</span>].<span class="cm-property">toString</span>());
<span class="cm-comment">// → 1,2</span></pre>
<p><a class="p_ident" id="p_FYf2A2jS65" href="#p_FYf2A2jS65" tabindex="-1" role="presentation"></a>Calling <code>toString</code> on an array gives a result similar to calling <code>.<wbr>join(",")</code> on it—it puts commas between the values in the array. Directly calling <code>Object.<wbr>prototype.<wbr>toString</code> with an array produces a different string. That function doesn’t know about arrays, so it simply puts the word <em>object</em> and the name of the type between square brackets.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_XpqFUrDFJE" href="#c_XpqFUrDFJE" tabindex="-1" role="presentation"></a><span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">Object</span>.<span class="cm-property">prototype</span>.<span class="cm-property">toString</span>.<span class="cm-property">call</span>([<span class="cm-number">1</span>, <span class="cm-number">2</span>]));
<span class="cm-comment">// → [object Array]</span></pre>
<h2><a class="h_ident" id="h_gAcc11EHzV" href="#h_gAcc11EHzV" tabindex="-1" role="presentation"></a>Maps</h2>
<p><a class="p_ident" id="p_5v6QzULS7C" href="#p_5v6QzULS7C" tabindex="-1" role="presentation"></a>We saw the word <em>map</em> used in the <a href="05_higher_order.html#map">previous chapter</a> for an operation that transforms a data structure by applying a function to its elements. Confusing as it is, in programming the same word is also used for a related but rather different thing.</p>
<p><a class="p_ident" id="p_++bw9zDmtH" href="#p_++bw9zDmtH" tabindex="-1" role="presentation"></a>A <em>map</em> (noun) is a data structure that associates values (the keys) with other values. For example, you might want to map names to ages. It is possible to use objects for this.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_Wu6a8ObZI0" href="#c_Wu6a8ObZI0" tabindex="-1" role="presentation"></a><span class="cm-keyword">let</span> <span class="cm-def">ages</span> <span class="cm-operator">=</span> {
<span class="cm-property">Boris</span>: <span class="cm-number">39</span>,
<span class="cm-property">Liang</span>: <span class="cm-number">22</span>,
<span class="cm-property">Júlia</span>: <span class="cm-number">62</span>
};
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string-2">`Júlia is ${</span><span class="cm-variable">ages</span>[<span class="cm-string">"Júlia"</span>]<span class="cm-string-2">}</span><span class="cm-string-2">`</span>);
<span class="cm-comment">// → Júlia is 62</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string">"Is Jack's age known?"</span>, <span class="cm-string">"Jack"</span> <span class="cm-keyword">in</span> <span class="cm-variable">ages</span>);
<span class="cm-comment">// → Is Jack's age known? false</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string">"Is toString's age known?"</span>, <span class="cm-string">"toString"</span> <span class="cm-keyword">in</span> <span class="cm-variable">ages</span>);
<span class="cm-comment">// → Is toString's age known? true</span></pre>
<p><a class="p_ident" id="p_EQqc7pcOKT" href="#p_EQqc7pcOKT" tabindex="-1" role="presentation"></a>Here, the object’s property names are the people’s names, and the property values are their ages. But we certainly didn’t list anybody named toString in our map. Yet, because plain objects derive from <code>Object.prototype</code>, it looks like the property is there.</p>
<p><a class="p_ident" id="p_enf1/9ItBM" href="#p_enf1/9ItBM" tabindex="-1" role="presentation"></a>As such, using plain objects as maps is dangerous. There are several possible ways to avoid this problem. First, it is possible to create objects with <em>no</em> prototype. If you pass <code>null</code> to <code>Object.create</code>, the resulting object will not derive from <code>Object.prototype</code> and can safely be used as a map.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_AkRQLQc4AG" href="#c_AkRQLQc4AG" tabindex="-1" role="presentation"></a><span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string">"toString"</span> <span class="cm-keyword">in</span> <span class="cm-variable">Object</span>.<span class="cm-property">create</span>(<span class="cm-atom">null</span>));
<span class="cm-comment">// → false</span></pre>
<p><a class="p_ident" id="p_jGC2iO9E1x" href="#p_jGC2iO9E1x" tabindex="-1" role="presentation"></a>Object property names must be strings. If you need a map whose keys can’t easily be converted to strings—such as objects—you cannot use an object as your map.</p>
<p><a class="p_ident" id="p_nIsq9E5wmZ" href="#p_nIsq9E5wmZ" tabindex="-1" role="presentation"></a>Fortunately, JavaScript comes with a class called <code>Map</code> that is written for this exact purpose. It stores a mapping and allows any type of keys.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_dd6KsGgAGP" href="#c_dd6KsGgAGP" tabindex="-1" role="presentation"></a><span class="cm-keyword">let</span> <span class="cm-def">ages</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">Map</span>();
<span class="cm-variable">ages</span>.<span class="cm-property">set</span>(<span class="cm-string">"Boris"</span>, <span class="cm-number">39</span>);
<span class="cm-variable">ages</span>.<span class="cm-property">set</span>(<span class="cm-string">"Liang"</span>, <span class="cm-number">22</span>);
<span class="cm-variable">ages</span>.<span class="cm-property">set</span>(<span class="cm-string">"Júlia"</span>, <span class="cm-number">62</span>);
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string-2">`Júlia is ${</span><span class="cm-variable">ages</span>.<span class="cm-property">get</span>(<span class="cm-string">"Júlia"</span>)<span class="cm-string-2">}</span><span class="cm-string-2">`</span>);
<span class="cm-comment">// → Júlia is 62</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string">"Is Jack's age known?"</span>, <span class="cm-variable">ages</span>.<span class="cm-property">has</span>(<span class="cm-string">"Jack"</span>));
<span class="cm-comment">// → Is Jack's age known? false</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">ages</span>.<span class="cm-property">has</span>(<span class="cm-string">"toString"</span>));
<span class="cm-comment">// → false</span></pre>
<p><a class="p_ident" id="p_iIdp+mGl3Y" href="#p_iIdp+mGl3Y" tabindex="-1" role="presentation"></a>The methods <code>set</code>, <code>get</code>, and <code>has</code> are part of the interface of the <code>Map</code> object. Writing a data structure that can quickly update and search a large set of values isn’t easy, but we don’t have to worry about that. Someone else did it for us, and we can go through this simple interface to use their work.</p>
<p><a class="p_ident" id="p_tx3xnowcEp" href="#p_tx3xnowcEp" tabindex="-1" role="presentation"></a>If you do have a plain object that you need to treat as a map for some reason, it is useful to know that <code>Object.keys</code> returns only an object’s <em>own</em> keys, not those in the prototype. As an alternative to the <code>in</code> operator, you can use the <code>hasOwnProperty</code> method, which ignores the object’s prototype.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_qBrd35Qiln" href="#c_qBrd35Qiln" tabindex="-1" role="presentation"></a><span class="cm-variable">console</span>.<span class="cm-property">log</span>({<span class="cm-property">x</span>: <span class="cm-number">1</span>}.<span class="cm-property">hasOwnProperty</span>(<span class="cm-string">"x"</span>));
<span class="cm-comment">// → true</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>({<span class="cm-property">x</span>: <span class="cm-number">1</span>}.<span class="cm-property">hasOwnProperty</span>(<span class="cm-string">"toString"</span>));
<span class="cm-comment">// → false</span></pre>
<h2><a class="h_ident" id="h_mJ/JHQRHg9" href="#h_mJ/JHQRHg9" tabindex="-1" role="presentation"></a>Polymorphism</h2>
<p><a class="p_ident" id="p_ozkqUookhO" href="#p_ozkqUookhO" tabindex="-1" role="presentation"></a>When you call the <code>String</code> function (which converts a value to a string) on an object, it will call the <code>toString</code> method on that object to try to create a meaningful string from it. I mentioned that some of the standard prototypes define their own version of <code>toString</code> so they can create a string that contains more useful information than <code>"[object Object]"</code>. You can also do that yourself.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_V+CBn0VJnW" href="#c_V+CBn0VJnW" tabindex="-1" role="presentation"></a><span class="cm-variable">Rabbit</span>.<span class="cm-property">prototype</span>.<span class="cm-property">toString</span> <span class="cm-operator">=</span> <span class="cm-keyword">function</span>() {
<span class="cm-keyword">return</span> <span class="cm-string-2">`a ${</span><span class="cm-keyword">this</span>.<span class="cm-property">type</span><span class="cm-string-2">}</span> <span class="cm-string-2">rabbit`</span>;
};
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">String</span>(<span class="cm-variable">blackRabbit</span>));
<span class="cm-comment">// → a black rabbit</span></pre>
<p><a class="p_ident" id="p_HgUFdFB6vM" href="#p_HgUFdFB6vM" tabindex="-1" role="presentation"></a>This is a simple instance of a powerful idea. When a piece of code is written to work with objects that have a certain interface—in this case, a <code>toString</code> method—any kind of object that happens to support this interface can be plugged into the code, and it will just work.</p>
<p><a class="p_ident" id="p_qNZih5k+vu" href="#p_qNZih5k+vu" tabindex="-1" role="presentation"></a>This technique is called <em>polymorphism</em>. Polymorphic code can work with values of different shapes, as long as they support the interface it expects.</p>
<p><a class="p_ident" id="p_g9W26O8p4+" href="#p_g9W26O8p4+" tabindex="-1" role="presentation"></a>I mentioned in <a href="04_data.html#for_of_loop">Chapter 4</a> that a <code>for</code>/<code>of</code> loop can loop over several kinds of data structures. This is another case of polymorphism—such loops expect the data structure to expose a specific interface, which arrays and strings do. And we can also add this interface to your own objects! But before we can do that, we need to know what symbols are.</p>
<h2><a class="h_ident" id="h_Iq1mTp65i3" href="#h_Iq1mTp65i3" tabindex="-1" role="presentation"></a>Symbols</h2>
<p><a class="p_ident" id="p_9VjlXGAIAM" href="#p_9VjlXGAIAM" tabindex="-1" role="presentation"></a>It is possible for multiple interfaces to use the same property name for different things. For example, I could define an interface in which the <code>toString</code> method is supposed to convert the object into a piece of yarn. It would not be possible for an object to conform to both that interface and the standard use of <code>toString</code>.</p>
<p><a class="p_ident" id="p_v6ZgtFDNeP" href="#p_v6ZgtFDNeP" tabindex="-1" role="presentation"></a>That would be a bad idea, and this problem isn’t that common. Most JavaScript programmers simply don’t think about it. But the language designers, whose <em>job</em> it is to think about this stuff, have provided us with a solution anyway.</p>
<p><a class="p_ident" id="p_7p+O+Qr4bN" href="#p_7p+O+Qr4bN" tabindex="-1" role="presentation"></a>When I claimed that property names are strings, that wasn’t entirely accurate. They usually are, but they can also be <em>symbols</em>. Symbols are values created with the <code>Symbol</code> function. Unlike strings, newly created symbols are unique—you cannot create the same symbol twice.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_/KAipM77Y+" href="#c_/KAipM77Y+" tabindex="-1" role="presentation"></a><span class="cm-keyword">let</span> <span class="cm-def">sym</span> <span class="cm-operator">=</span> <span class="cm-variable">Symbol</span>(<span class="cm-string">"name"</span>);
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">sym</span> <span class="cm-operator">==</span> <span class="cm-variable">Symbol</span>(<span class="cm-string">"name"</span>));
<span class="cm-comment">// → false</span>
<span class="cm-variable">Rabbit</span>.<span class="cm-property">prototype</span>[<span class="cm-variable">sym</span>] <span class="cm-operator">=</span> <span class="cm-number">55</span>;
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">blackRabbit</span>[<span class="cm-variable">sym</span>]);
<span class="cm-comment">// → 55</span></pre>
<p><a class="p_ident" id="p_PBxnKMHKqV" href="#p_PBxnKMHKqV" tabindex="-1" role="presentation"></a>The string you pass to <code>Symbol</code> is included when you convert it to a string and can make it easier to recognize a symbol when, for example, showing it in the console. But it has no meaning beyond that—multiple symbols may have the same name.</p>
<p><a class="p_ident" id="p_n90pM44NAN" href="#p_n90pM44NAN" tabindex="-1" role="presentation"></a>Being both unique and usable as property names makes symbols suitable for defining interfaces that can peacefully live alongside other properties, no matter what their names are.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_I6uO/ojWit" href="#c_I6uO/ojWit" tabindex="-1" role="presentation"></a><span class="cm-keyword">const</span> <span class="cm-def">toStringSymbol</span> <span class="cm-operator">=</span> <span class="cm-variable">Symbol</span>(<span class="cm-string">"toString"</span>);
<span class="cm-variable">Array</span>.<span class="cm-property">prototype</span>[<span class="cm-variable">toStringSymbol</span>] <span class="cm-operator">=</span> <span class="cm-keyword">function</span>() {
<span class="cm-keyword">return</span> <span class="cm-string-2">`${</span><span class="cm-keyword">this</span>.<span class="cm-property">length</span><span class="cm-string-2">}</span> <span class="cm-string-2">cm of blue yarn`</span>;
};
<span class="cm-variable">console</span>.<span class="cm-property">log</span>([<span class="cm-number">1</span>, <span class="cm-number">2</span>].<span class="cm-property">toString</span>());
<span class="cm-comment">// → 1,2</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>([<span class="cm-number">1</span>, <span class="cm-number">2</span>][<span class="cm-variable">toStringSymbol</span>]());
<span class="cm-comment">// → 2 cm of blue yarn</span></pre>
<p><a class="p_ident" id="p_VJSo/GYvCc" href="#p_VJSo/GYvCc" tabindex="-1" role="presentation"></a>It is possible to include symbol properties in object expressions and classes by using square brackets around the property name. That causes the property name to be evaluated, much like the square bracket property access notation, which allows us to refer to a binding that holds the symbol.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_aZAdJfdSRz" href="#c_aZAdJfdSRz" tabindex="-1" role="presentation"></a><span class="cm-keyword">let</span> <span class="cm-def">stringObject</span> <span class="cm-operator">=</span> {
[<span class="cm-variable">toStringSymbol</span>]() { <span class="cm-keyword">return</span> <span class="cm-string">"a jute rope"</span>; }
};
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">stringObject</span>[<span class="cm-variable">toStringSymbol</span>]());
<span class="cm-comment">// → a jute rope</span></pre>
<h2><a class="h_ident" id="h_z2tOOXM8qO" href="#h_z2tOOXM8qO" tabindex="-1" role="presentation"></a>The iterator interface</h2>
<p><a class="p_ident" id="p_gtFNFSvSrt" href="#p_gtFNFSvSrt" tabindex="-1" role="presentation"></a>The object given to a <code>for</code>/<code>of</code> loop is expected to be <em>iterable</em>. This means it has a method named with the <code>Symbol.iterator</code> symbol (a symbol value defined by the language, stored as a property of the <code>Symbol</code> function).</p>
<p><a class="p_ident" id="p_gs0GMX9PA7" href="#p_gs0GMX9PA7" tabindex="-1" role="presentation"></a>When called, that method should return an object that provides a second interface, <em>iterator</em>. This is the actual thing that iterates. It has a <code>next</code> method that returns the next result. That result should be an object with a <code>value</code> property that provides the next value, if there is one, and a <code>done</code> property, which should be true when there are no more results and false otherwise.</p>
<p><a class="p_ident" id="p_a9jQxjOo3t" href="#p_a9jQxjOo3t" tabindex="-1" role="presentation"></a>Note that the <code>next</code>, <code>value</code>, and <code>done</code> property names are plain strings, not symbols. Only <code>Symbol.iterator</code>, which is likely to be added to a <em>lot</em> of different objects, is an actual symbol.</p>
<p><a class="p_ident" id="p_wSWGcm7dId" href="#p_wSWGcm7dId" tabindex="-1" role="presentation"></a>We can directly use this interface ourselves.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_CKTaBW3WjJ" href="#c_CKTaBW3WjJ" tabindex="-1" role="presentation"></a><span class="cm-keyword">let</span> <span class="cm-def">okIterator</span> <span class="cm-operator">=</span> <span class="cm-string">"OK"</span>[<span class="cm-variable">Symbol</span>.<span class="cm-property">iterator</span>]();
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">okIterator</span>.<span class="cm-property">next</span>());
<span class="cm-comment">// → {value: "O", done: false}</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">okIterator</span>.<span class="cm-property">next</span>());
<span class="cm-comment">// → {value: "K", done: false}</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">okIterator</span>.<span class="cm-property">next</span>());
<span class="cm-comment">// → {value: undefined, done: true}</span></pre>
<p id="matrix"><a class="p_ident" id="p_rqXAzunzIi" href="#p_rqXAzunzIi" tabindex="-1" role="presentation"></a>Let’s implement an iterable data structure. We’ll build a <em>matrix</em> class, acting as a two-dimensional array.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_Q128qlROKi" href="#c_Q128qlROKi" tabindex="-1" role="presentation"></a><span class="cm-keyword">class</span> <span class="cm-def">Matrix</span> {
<span class="cm-property">constructor</span>(<span class="cm-def">width</span>, <span class="cm-def">height</span>, <span class="cm-def">element</span> <span class="cm-operator">=</span> (<span class="cm-def">x</span>, <span class="cm-def">y</span>) <span class="cm-operator">=></span> <span class="cm-atom">undefined</span>) {
<span class="cm-keyword">this</span>.<span class="cm-property">width</span> <span class="cm-operator">=</span> <span class="cm-variable-2">width</span>;
<span class="cm-keyword">this</span>.<span class="cm-property">height</span> <span class="cm-operator">=</span> <span class="cm-variable-2">height</span>;
<span class="cm-keyword">this</span>.<span class="cm-property">content</span> <span class="cm-operator">=</span> [];
<span class="cm-keyword">for</span> (<span class="cm-keyword">let</span> <span class="cm-def">y</span> <span class="cm-operator">=</span> <span class="cm-number">0</span>; <span class="cm-variable-2">y</span> <span class="cm-operator"><</span> <span class="cm-variable-2">height</span>; <span class="cm-variable-2">y</span><span class="cm-operator">++</span>) {
<span class="cm-keyword">for</span> (<span class="cm-keyword">let</span> <span class="cm-def">x</span> <span class="cm-operator">=</span> <span class="cm-number">0</span>; <span class="cm-variable-2">x</span> <span class="cm-operator"><</span> <span class="cm-variable-2">width</span>; <span class="cm-variable-2">x</span><span class="cm-operator">++</span>) {
<span class="cm-keyword">this</span>.<span class="cm-property">content</span>[<span class="cm-variable-2">y</span> <span class="cm-operator">*</span> <span class="cm-variable-2">width</span> <span class="cm-operator">+</span> <span class="cm-variable-2">x</span>] <span class="cm-operator">=</span> <span class="cm-variable-2">element</span>(<span class="cm-variable-2">x</span>, <span class="cm-variable-2">y</span>);
}
}
}
<span class="cm-property">get</span>(<span class="cm-def">x</span>, <span class="cm-def">y</span>) {
<span class="cm-keyword">return</span> <span class="cm-keyword">this</span>.<span class="cm-property">content</span>[<span class="cm-variable-2">y</span> <span class="cm-operator">*</span> <span class="cm-keyword">this</span>.<span class="cm-property">width</span> <span class="cm-operator">+</span> <span class="cm-variable-2">x</span>];
}
<span class="cm-property">set</span>(<span class="cm-def">x</span>, <span class="cm-def">y</span>, <span class="cm-def">value</span>) {
<span class="cm-keyword">this</span>.<span class="cm-property">content</span>[<span class="cm-variable-2">y</span> <span class="cm-operator">*</span> <span class="cm-keyword">this</span>.<span class="cm-property">width</span> <span class="cm-operator">+</span> <span class="cm-variable-2">x</span>] <span class="cm-operator">=</span> <span class="cm-variable-2">value</span>;
}
}</pre>
<p><a class="p_ident" id="p_I/q+okK5mh" href="#p_I/q+okK5mh" tabindex="-1" role="presentation"></a>The class stores its content in a single array of <em>width</em> × <em>height</em> elements. The elements are stored row by row, so, for example, the third element in the fifth row is (using zero-based indexing) stored at position 4 × <em>width</em> + 2.</p>
<p><a class="p_ident" id="p_RlDQ7yqK2c" href="#p_RlDQ7yqK2c" tabindex="-1" role="presentation"></a>The constructor function takes a width, a height, and an optional <code>element</code> function that will be used to fill in the initial values. There are <code>get</code> and <code>set</code> methods to retrieve and update elements in the matrix.</p>
<p><a class="p_ident" id="p_DHPUGxvFrD" href="#p_DHPUGxvFrD" tabindex="-1" role="presentation"></a>When looping over a matrix, you are usually interested in the position of the elements as well as the elements themselves, so we’ll have our iterator produce objects with <code>x</code>, <code>y</code>, and <code>value</code> properties.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_LtbqF2pl4c" href="#c_LtbqF2pl4c" tabindex="-1" role="presentation"></a><span class="cm-keyword">class</span> <span class="cm-def">MatrixIterator</span> {
<span class="cm-property">constructor</span>(<span class="cm-def">matrix</span>) {
<span class="cm-keyword">this</span>.<span class="cm-property">x</span> <span class="cm-operator">=</span> <span class="cm-number">0</span>;
<span class="cm-keyword">this</span>.<span class="cm-property">y</span> <span class="cm-operator">=</span> <span class="cm-number">0</span>;
<span class="cm-keyword">this</span>.<span class="cm-property">matrix</span> <span class="cm-operator">=</span> <span class="cm-variable-2">matrix</span>;
}
<span class="cm-property">next</span>() {
<span class="cm-keyword">if</span> (<span class="cm-keyword">this</span>.<span class="cm-property">y</span> <span class="cm-operator">==</span> <span class="cm-keyword">this</span>.<span class="cm-property">matrix</span>.<span class="cm-property">height</span>) <span class="cm-keyword">return</span> {<span class="cm-property">done</span>: <span class="cm-atom">true</span>};
<span class="cm-keyword">let</span> <span class="cm-def">value</span> <span class="cm-operator">=</span> {<span class="cm-property">x</span>: <span class="cm-keyword">this</span>.<span class="cm-property">x</span>,
<span class="cm-property">y</span>: <span class="cm-keyword">this</span>.<span class="cm-property">y</span>,
<span class="cm-property">value</span>: <span class="cm-keyword">this</span>.<span class="cm-property">matrix</span>.<span class="cm-property">get</span>(<span class="cm-keyword">this</span>.<span class="cm-property">x</span>, <span class="cm-keyword">this</span>.<span class="cm-property">y</span>)};
<span class="cm-keyword">this</span>.<span class="cm-property">x</span><span class="cm-operator">++</span>;
<span class="cm-keyword">if</span> (<span class="cm-keyword">this</span>.<span class="cm-property">x</span> <span class="cm-operator">==</span> <span class="cm-keyword">this</span>.<span class="cm-property">matrix</span>.<span class="cm-property">width</span>) {
<span class="cm-keyword">this</span>.<span class="cm-property">x</span> <span class="cm-operator">=</span> <span class="cm-number">0</span>;
<span class="cm-keyword">this</span>.<span class="cm-property">y</span><span class="cm-operator">++</span>;
}
<span class="cm-keyword">return</span> {<span class="cm-property">value</span>, <span class="cm-property">done</span>: <span class="cm-atom">false</span>};
}
}</pre>
<p><a class="p_ident" id="p_yBJg8/BmDJ" href="#p_yBJg8/BmDJ" tabindex="-1" role="presentation"></a>The class tracks the progress of iterating over a matrix in its <code>x</code> and <code>y</code> properties. The <code>next</code> method starts by checking whether the bottom of the matrix has been reached. If it hasn’t, it <em>first</em> creates the object holding the current value and <em>then</em> updates its position, moving to the next row if necessary.</p>
<p><a class="p_ident" id="p_Iu3bFayPkp" href="#p_Iu3bFayPkp" tabindex="-1" role="presentation"></a>Let’s set up the <code>Matrix</code> class to be iterable. Throughout this book, I’ll occasionally use after-the-fact prototype manipulation to add methods to classes so that the individual pieces of code remain small and self-contained. In a regular program, where there is no need to split the code into small pieces, you’d declare these methods directly in the class instead.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_BmOLk4O5HN" href="#c_BmOLk4O5HN" tabindex="-1" role="presentation"></a><span class="cm-variable">Matrix</span>.<span class="cm-property">prototype</span>[<span class="cm-variable">Symbol</span>.<span class="cm-property">iterator</span>] <span class="cm-operator">=</span> <span class="cm-keyword">function</span>() {
<span class="cm-keyword">return</span> <span class="cm-keyword">new</span> <span class="cm-variable">MatrixIterator</span>(<span class="cm-keyword">this</span>);
};</pre>
<p><a class="p_ident" id="p_+6FbIloB5k" href="#p_+6FbIloB5k" tabindex="-1" role="presentation"></a>We can now loop over a matrix with <code>for</code>/<code>of</code>.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_ek9pXBwNMJ" href="#c_ek9pXBwNMJ" tabindex="-1" role="presentation"></a><span class="cm-keyword">let</span> <span class="cm-def">matrix</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">Matrix</span>(<span class="cm-number">2</span>, <span class="cm-number">2</span>, (<span class="cm-def">x</span>, <span class="cm-def">y</span>) <span class="cm-operator">=></span> <span class="cm-string-2">`value ${</span><span class="cm-variable-2">x</span><span class="cm-string-2">}</span><span class="cm-string-2">,${</span><span class="cm-variable-2">y</span><span class="cm-string-2">}</span><span class="cm-string-2">`</span>);
<span class="cm-keyword">for</span> (<span class="cm-keyword">let</span> {<span class="cm-def">x</span>, <span class="cm-def">y</span>, <span class="cm-def">value</span>} <span class="cm-keyword">of</span> <span class="cm-variable">matrix</span>) {
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">x</span>, <span class="cm-variable">y</span>, <span class="cm-variable">value</span>);
}
<span class="cm-comment">// → 0 0 value 0,0</span>
<span class="cm-comment">// → 1 0 value 1,0</span>
<span class="cm-comment">// → 0 1 value 0,1</span>
<span class="cm-comment">// → 1 1 value 1,1</span></pre>
<h2><a class="h_ident" id="h_3vwredi8nD" href="#h_3vwredi8nD" tabindex="-1" role="presentation"></a>Getters, setters, and statics</h2>
<p><a class="p_ident" id="p_6oukozZJBx" href="#p_6oukozZJBx" tabindex="-1" role="presentation"></a>Interfaces often consist mostly of methods, but it is also okay to include properties that hold non-function values. For example, <code>Map</code> objects have a <code>size</code> property that tells you how many keys are stored in them.</p>
<p><a class="p_ident" id="p_SdyGtj5JbS" href="#p_SdyGtj5JbS" tabindex="-1" role="presentation"></a>It is not even necessary for such an object to compute and store such a property directly in the instance. Even properties that are accessed directly may hide a method call. Such methods are called <em>getters</em>, and they are defined by writing <code>get</code> in front of the method name in an object expression or class declaration.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_Np05mJ4GVO" href="#c_Np05mJ4GVO" tabindex="-1" role="presentation"></a><span class="cm-keyword">let</span> <span class="cm-def">varyingSize</span> <span class="cm-operator">=</span> {
<span class="cm-property">get</span> <span class="cm-property">size</span>() {
<span class="cm-keyword">return</span> <span class="cm-variable">Math</span>.<span class="cm-property">floor</span>(<span class="cm-variable">Math</span>.<span class="cm-property">random</span>() <span class="cm-operator">*</span> <span class="cm-number">100</span>);
}
};
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">varyingSize</span>.<span class="cm-property">size</span>);
<span class="cm-comment">// → 73</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">varyingSize</span>.<span class="cm-property">size</span>);
<span class="cm-comment">// → 49</span></pre>
<p><a class="p_ident" id="p_QwpqNIZOsS" href="#p_QwpqNIZOsS" tabindex="-1" role="presentation"></a>Whenever someone reads from this object’s <code>size</code> property, the associated method is called. You can do a similar thing when a property is written to, using a <em>setter</em>.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_7LQG88c1BA" href="#c_7LQG88c1BA" tabindex="-1" role="presentation"></a><span class="cm-keyword">class</span> <span class="cm-def">Temperature</span> {
<span class="cm-property">constructor</span>(<span class="cm-def">celsius</span>) {
<span class="cm-keyword">this</span>.<span class="cm-property">celsius</span> <span class="cm-operator">=</span> <span class="cm-variable-2">celsius</span>;
}
<span class="cm-keyword">get</span> <span class="cm-property">fahrenheit</span>() {
<span class="cm-keyword">return</span> <span class="cm-keyword">this</span>.<span class="cm-property">celsius</span> <span class="cm-operator">*</span> <span class="cm-number">1.8</span> <span class="cm-operator">+</span> <span class="cm-number">32</span>;
}
<span class="cm-keyword">set</span> <span class="cm-property">fahrenheit</span>(<span class="cm-def">value</span>) {
<span class="cm-keyword">this</span>.<span class="cm-property">celsius</span> <span class="cm-operator">=</span> (<span class="cm-variable-2">value</span> <span class="cm-operator">-</span> <span class="cm-number">32</span>) <span class="cm-operator">/</span> <span class="cm-number">1.8</span>;
}
<span class="cm-keyword">static</span> <span class="cm-property">fromFahrenheit</span>(<span class="cm-def">value</span>) {
<span class="cm-keyword">return</span> <span class="cm-keyword">new</span> <span class="cm-variable">Temperature</span>((<span class="cm-variable-2">value</span> <span class="cm-operator">-</span> <span class="cm-number">32</span>) <span class="cm-operator">/</span> <span class="cm-number">1.8</span>);
}
}
<span class="cm-keyword">let</span> <span class="cm-def">temp</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">Temperature</span>(<span class="cm-number">22</span>);
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">temp</span>.<span class="cm-property">fahrenheit</span>);
<span class="cm-comment">// → 71.6</span>
<span class="cm-variable">temp</span>.<span class="cm-property">fahrenheit</span> <span class="cm-operator">=</span> <span class="cm-number">86</span>;
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">temp</span>.<span class="cm-property">celsius</span>);
<span class="cm-comment">// → 30</span></pre>
<p><a class="p_ident" id="p_Hf3p+suFR+" href="#p_Hf3p+suFR+" tabindex="-1" role="presentation"></a>The <code>Temperature</code> class allows you to read and write the temperature in either degrees Celsius or degrees Fahrenheit, but internally it stores only Celsius and automatically converts to and from Celsius in the <code>fahrenheit</code> getter and setter.</p>
<p><a class="p_ident" id="p_MwIs1kR0mD" href="#p_MwIs1kR0mD" tabindex="-1" role="presentation"></a>Sometimes you want to attach some properties directly to your constructor function, rather than to the prototype. Such methods won’t have access to a class instance but can, for example, be used to provide additional ways to create instances.</p>
<p><a class="p_ident" id="p_zLJ2u9mlXs" href="#p_zLJ2u9mlXs" tabindex="-1" role="presentation"></a>Inside a class declaration, methods that have <code>static</code> written before their name are stored on the constructor. So the <code>Temperature</code> class allows you to write <code>Temperature.<wbr>fromFahrenheit(100)</code> to create a temperature using degrees Fahrenheit.</p>
<h2><a class="h_ident" id="h_/a3bnONnws" href="#h_/a3bnONnws" tabindex="-1" role="presentation"></a>Inheritance</h2>
<p><a class="p_ident" id="p_seSalQnLZd" href="#p_seSalQnLZd" tabindex="-1" role="presentation"></a>Some matrices are known to be <em>symmetric</em>. If you mirror a symmetric matrix around its top-left-to-bottom-right diagonal, it stays the same. In other words, the value stored at <em>x</em>,<em>y</em> is always the same as that at <em>y</em>,<em>x</em>.</p>
<p><a class="p_ident" id="p_InrSr2m6Kl" href="#p_InrSr2m6Kl" tabindex="-1" role="presentation"></a>Imagine we need a data structure like <code>Matrix</code> but one that enforces the fact that the matrix is and remains symmetrical. We could write it from scratch, but that would involve repeating some code very similar to what we already wrote.</p>
<p><a class="p_ident" id="p_kRtvgxF67W" href="#p_kRtvgxF67W" tabindex="-1" role="presentation"></a>JavaScript’s prototype system makes it possible to create a <em>new</em> class, much like the old class, but with new definitions for some of its properties. The prototype for the new class derives from the old prototype but adds a new definition for, say, the <code>set</code> method.</p>
<p><a class="p_ident" id="p_a3GMshNFYT" href="#p_a3GMshNFYT" tabindex="-1" role="presentation"></a>In object-oriented programming terms, this is called <em>inheritance</em>. The new class inherits properties and behavior from the old class.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_uuBcsDdS7D" href="#c_uuBcsDdS7D" tabindex="-1" role="presentation"></a><span class="cm-keyword">class</span> <span class="cm-def">SymmetricMatrix</span> <span class="cm-keyword">extends</span> <span class="cm-variable">Matrix</span> {
<span class="cm-property">constructor</span>(<span class="cm-def">size</span>, <span class="cm-def">element</span> <span class="cm-operator">=</span> (<span class="cm-def">x</span>, <span class="cm-def">y</span>) <span class="cm-operator">=></span> <span class="cm-atom">undefined</span>) {
<span class="cm-keyword">super</span>(<span class="cm-variable-2">size</span>, <span class="cm-variable-2">size</span>, (<span class="cm-def">x</span>, <span class="cm-def">y</span>) <span class="cm-operator">=></span> {
<span class="cm-keyword">if</span> (<span class="cm-variable-2">x</span> <span class="cm-operator"><</span> <span class="cm-variable-2">y</span>) <span class="cm-keyword">return</span> <span class="cm-variable-2">element</span>(<span class="cm-variable-2">y</span>, <span class="cm-variable-2">x</span>);
<span class="cm-keyword">else</span> <span class="cm-keyword">return</span> <span class="cm-variable-2">element</span>(<span class="cm-variable-2">x</span>, <span class="cm-variable-2">y</span>);
});
}
<span class="cm-property">set</span>(<span class="cm-def">x</span>, <span class="cm-def">y</span>, <span class="cm-def">value</span>) {
<span class="cm-keyword">super</span>.<span class="cm-property">set</span>(<span class="cm-variable-2">x</span>, <span class="cm-variable-2">y</span>, <span class="cm-variable-2">value</span>);
<span class="cm-keyword">if</span> (<span class="cm-variable-2">x</span> <span class="cm-operator">!=</span> <span class="cm-variable-2">y</span>) {
<span class="cm-keyword">super</span>.<span class="cm-property">set</span>(<span class="cm-variable-2">y</span>, <span class="cm-variable-2">x</span>, <span class="cm-variable-2">value</span>);
}
}
}
<span class="cm-keyword">let</span> <span class="cm-def">matrix</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">SymmetricMatrix</span>(<span class="cm-number">5</span>, (<span class="cm-def">x</span>, <span class="cm-def">y</span>) <span class="cm-operator">=></span> <span class="cm-string-2">`${</span><span class="cm-variable-2">x</span><span class="cm-string-2">}</span><span class="cm-string-2">,${</span><span class="cm-variable-2">y</span><span class="cm-string-2">}</span><span class="cm-string-2">`</span>);
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">matrix</span>.<span class="cm-property">get</span>(<span class="cm-number">2</span>, <span class="cm-number">3</span>));
<span class="cm-comment">// → 3,2</span></pre>
<p><a class="p_ident" id="p_RZZzYHZ3d6" href="#p_RZZzYHZ3d6" tabindex="-1" role="presentation"></a>The use of the word <code>extends</code> indicates that this class shouldn’t be directly based on the default <code>Object</code> prototype but on some other class. This is called the <em>superclass</em>. The derived class is the <em>subclass</em>.</p>
<p><a class="p_ident" id="p_mss/w8sDfy" href="#p_mss/w8sDfy" tabindex="-1" role="presentation"></a>To initialize a <code>SymmetricMatrix</code> instance, the constructor calls its superclass’s constructor through the <code>super</code> keyword. This is necessary because if this new object is to behave (roughly) like a <code>Matrix</code>, it is going to need the instance properties that matrices have. To ensure the matrix is symmetrical, the constructor wraps the <code>element</code> function to swap the coordinates for values below the diagonal.</p>
<p><a class="p_ident" id="p_9XrYhhYXI8" href="#p_9XrYhhYXI8" tabindex="-1" role="presentation"></a>The <code>set</code> method again uses <code>super</code> but this time not to call the constructor but to call a specific method from the superclass’s set of methods. We are redefining <code>set</code> but do want to use the original behavior. Because <code>this.set</code> refers to the <em>new</em> <code>set</code> method, calling that wouldn’t work. Inside class methods, <code>super</code> provides a way to call methods as they were defined in the superclass.</p>
<p><a class="p_ident" id="p_apvJjMYcsg" href="#p_apvJjMYcsg" tabindex="-1" role="presentation"></a>Inheritance allows us to build slightly different data types from existing data types with relatively little work. It is a fundamental part of the object-oriented tradition, alongside encapsulation and polymorphism. But while the latter two are now generally regarded as wonderful ideas, inheritance is more controversial.</p>
<p><a class="p_ident" id="p_CWDhzksvzb" href="#p_CWDhzksvzb" tabindex="-1" role="presentation"></a>Whereas encapsulation and polymorphism can be used to <em>separate</em> pieces of code from each other, reducing the tangledness of the overall program, inheritance fundamentally ties classes together, creating <em>more</em> tangle. When inheriting from a class, you usually have to know more about how it works than when simply using it. Inheritance can be a useful tool, and I use it now and then in my own programs, but it shouldn’t be the first tool you reach for, and you probably shouldn’t actively go looking for opportunities to construct class hierarchies (family trees of classes).</p>
<h2><a class="h_ident" id="h_Fdk67dJHwg" href="#h_Fdk67dJHwg" tabindex="-1" role="presentation"></a>The instanceof operator</h2>
<p><a class="p_ident" id="p_wnmK5D9foe" href="#p_wnmK5D9foe" tabindex="-1" role="presentation"></a>It is occasionally useful to know whether an object was derived from a specific class. For this, JavaScript provides a binary operator called <code>instanceof</code>.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_1/4hH+dV3k" href="#c_1/4hH+dV3k" tabindex="-1" role="presentation"></a><span class="cm-variable">console</span>.<span class="cm-property">log</span>(
<span class="cm-keyword">new</span> <span class="cm-variable">SymmetricMatrix</span>(<span class="cm-number">2</span>) <span class="cm-keyword">instanceof</span> <span class="cm-variable">SymmetricMatrix</span>);
<span class="cm-comment">// → true</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-keyword">new</span> <span class="cm-variable">SymmetricMatrix</span>(<span class="cm-number">2</span>) <span class="cm-keyword">instanceof</span> <span class="cm-variable">Matrix</span>);
<span class="cm-comment">// → true</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-keyword">new</span> <span class="cm-variable">Matrix</span>(<span class="cm-number">2</span>, <span class="cm-number">2</span>) <span class="cm-keyword">instanceof</span> <span class="cm-variable">SymmetricMatrix</span>);
<span class="cm-comment">// → false</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>([<span class="cm-number">1</span>] <span class="cm-keyword">instanceof</span> <span class="cm-variable">Array</span>);
<span class="cm-comment">// → true</span></pre>
<p><a class="p_ident" id="p_VtvmEyRrv+" href="#p_VtvmEyRrv+" tabindex="-1" role="presentation"></a>The operator will see through inherited types, so a <code>SymmetricMatrix</code> is an instance of <code>Matrix</code>. The operator can also be applied to standard constructors like <code>Array</code>. Almost every object is an instance of <code>Object</code>.</p>
<h2><a class="h_ident" id="h_ErccPg/l98" href="#h_ErccPg/l98" tabindex="-1" role="presentation"></a>Summary</h2>
<p><a class="p_ident" id="p_VPlYPbumD2" href="#p_VPlYPbumD2" tabindex="-1" role="presentation"></a>So objects do more than just hold their own properties. They have prototypes, which are other objects. They’ll act as if they have properties they don’t have as long as their prototype has that property. Simple objects have <code>Object.prototype</code> as their prototype.</p>
<p><a class="p_ident" id="p_A+dQsxCVz8" href="#p_A+dQsxCVz8" tabindex="-1" role="presentation"></a>Constructors, which are functions whose names usually start with a capital letter, can be used with the <code>new</code> operator to create new objects. The new object’s prototype will be the object found in the <code>prototype</code> property of the constructor. You can make good use of this by putting the properties that all values of a given type share into their prototype. There’s a <code>class</code> notation that provides a clear way to define a constructor and its prototype.</p>
<p><a class="p_ident" id="p_AFyYjRH+5G" href="#p_AFyYjRH+5G" tabindex="-1" role="presentation"></a>You can define getters and setters to secretly call methods every time an object’s property is accessed. Static methods are methods stored in a class’s constructor, rather than its prototype.</p>
<p><a class="p_ident" id="p_U1iTgKNfyX" href="#p_U1iTgKNfyX" tabindex="-1" role="presentation"></a>The <code>instanceof</code> operator can, given an object and a constructor, tell you whether that object is an instance of that constructor.</p>
<p><a class="p_ident" id="p_zzr1L1PppY" href="#p_zzr1L1PppY" tabindex="-1" role="presentation"></a>One useful thing to do with objects is to specify an interface for them and tell everybody that they are supposed to talk to your object only through that interface. The rest of the details that make up your object are now <em>encapsulated</em>, hidden behind the interface.</p>
<p><a class="p_ident" id="p_NWmKpeeGiT" href="#p_NWmKpeeGiT" tabindex="-1" role="presentation"></a>More than one type may implement the same interface. Code written to use an interface automatically knows how to work with any number of different objects that provide the interface. This is called <em>polymorphism</em>.</p>
<p><a class="p_ident" id="p_fg/ZL+fqD3" href="#p_fg/ZL+fqD3" tabindex="-1" role="presentation"></a>When implementing multiple classes that differ in only some details, it can be helpful to write the new classes as <em>subclasses</em> of an existing class, <em>inheriting</em> part of its behavior.</p>
<h2><a class="h_ident" id="h_TcUD2vzyMe" href="#h_TcUD2vzyMe" tabindex="-1" role="presentation"></a>Exercises</h2>
<h3 id="exercise_vector"><a class="i_ident" id="i_zO8FRQBMAy" href="#i_zO8FRQBMAy" tabindex="-1" role="presentation"></a>A vector type</h3>
<p><a class="p_ident" id="p_YtkHcQdZSH" href="#p_YtkHcQdZSH" tabindex="-1" role="presentation"></a>Write a class <code>Vec</code> that represents a vector in two-dimensional space. It takes <code>x</code> and <code>y</code> parameters (numbers), which it should save to properties of the same name.</p>
<p><a class="p_ident" id="p_8DOrtO0lbU" href="#p_8DOrtO0lbU" tabindex="-1" role="presentation"></a>Give the <code>Vec</code> prototype two methods, <code>plus</code> and <code>minus</code>, that take another vector as a parameter and return a new vector that has the sum or difference of the two vectors’ (<code>this</code> and the parameter) <em>x</em> and <em>y</em> values.</p>
<p><a class="p_ident" id="p_F5nP+jpza3" href="#p_F5nP+jpza3" tabindex="-1" role="presentation"></a>Add a getter property <code>length</code> to the prototype that computes the length of the vector—that is, the distance of the point (<em>x</em>, <em>y</em>) from the origin (0, 0).</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_f3P62tUJWH" href="#c_f3P62tUJWH" tabindex="-1" role="presentation"></a><span class="cm-comment">// Your code here.</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-keyword">new</span> <span class="cm-variable">Vec</span>(<span class="cm-number">1</span>, <span class="cm-number">2</span>).<span class="cm-property">plus</span>(<span class="cm-keyword">new</span> <span class="cm-variable">Vec</span>(<span class="cm-number">2</span>, <span class="cm-number">3</span>)));
<span class="cm-comment">// → Vec{x: 3, y: 5}</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-keyword">new</span> <span class="cm-variable">Vec</span>(<span class="cm-number">1</span>, <span class="cm-number">2</span>).<span class="cm-property">minus</span>(<span class="cm-keyword">new</span> <span class="cm-variable">Vec</span>(<span class="cm-number">2</span>, <span class="cm-number">3</span>)));
<span class="cm-comment">// → Vec{x: -1, y: -1}</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-keyword">new</span> <span class="cm-variable">Vec</span>(<span class="cm-number">3</span>, <span class="cm-number">4</span>).<span class="cm-property">length</span>);
<span class="cm-comment">// → 5</span></pre>
<div class="solution"><div class="solution-text">
<p><a class="p_ident" id="p_jRIa414Yh5" href="#p_jRIa414Yh5" tabindex="-1" role="presentation"></a>Look back to the <code>Rabbit</code> class example if you’re unsure how <code>class</code> declarations look.</p>
<p><a class="p_ident" id="p_uZnm2ZSaRd" href="#p_uZnm2ZSaRd" tabindex="-1" role="presentation"></a>Adding a getter property to the constructor can be done by putting the word <code>get</code> before the method name. To compute the distance from (0, 0) to (x, y), you can use the Pythagorean theorem, which says that the square of the distance we are looking for is equal to the square of the x-coordinate plus the square of the y-coordinate. Thus, √(x<sup>2</sup> + y<sup>2</sup>) is the number you want, and <code>Math.sqrt</code> is the way you compute a square root in JavaScript.</p>
</div></div>
<h3><a class="i_ident" id="i_rpYp9Ou4LG" href="#i_rpYp9Ou4LG" tabindex="-1" role="presentation"></a>Groups</h3>
<p id="groups"><a class="p_ident" id="p_1TnXiDoyR2" href="#p_1TnXiDoyR2" tabindex="-1" role="presentation"></a>The standard JavaScript environment provides another data structure called <code>Set</code>. Like an instance of <code>Map</code>, a set holds a collection of values. Unlike <code>Map</code>, it does not associate other values with those—it just tracks which values are part of the set. A value can be part of a set only once—adding it again doesn’t have any effect.</p>
<p><a class="p_ident" id="p_IBo4QI1mvy" href="#p_IBo4QI1mvy" tabindex="-1" role="presentation"></a>Write a class called <code>Group</code> (since <code>Set</code> is already taken). Like <code>Set</code>, it has <code>add</code>, <code>delete</code>, and <code>has</code> methods. Its constructor creates an empty group, <code>add</code> adds a value to the group (but only if it isn’t already a member), <code>delete</code> removes its argument from the group (if it was a member), and <code>has</code> returns a Boolean value indicating whether its argument is a member of the group.</p>
<p><a class="p_ident" id="p_cHm3PZ0L5i" href="#p_cHm3PZ0L5i" tabindex="-1" role="presentation"></a>Use the <code>===</code> operator, or something equivalent such as <code>indexOf</code>, to determine whether two values are the same.</p>
<p><a class="p_ident" id="p_CbJ60eqr+J" href="#p_CbJ60eqr+J" tabindex="-1" role="presentation"></a>Give the class a static <code>from</code> method that takes an iterable object as argument and creates a group that contains all the values produced by iterating over it.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_dNauaecKx+" href="#c_dNauaecKx+" tabindex="-1" role="presentation"></a><span class="cm-keyword">class</span> <span class="cm-def">Group</span> {
<span class="cm-comment">// Your code here.</span>
}
<span class="cm-keyword">let</span> <span class="cm-def">group</span> <span class="cm-operator">=</span> <span class="cm-variable">Group</span>.<span class="cm-property">from</span>([<span class="cm-number">10</span>, <span class="cm-number">20</span>]);
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">group</span>.<span class="cm-property">has</span>(<span class="cm-number">10</span>));
<span class="cm-comment">// → true</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">group</span>.<span class="cm-property">has</span>(<span class="cm-number">30</span>));
<span class="cm-comment">// → false</span>
<span class="cm-variable">group</span>.<span class="cm-property">add</span>(<span class="cm-number">10</span>);
<span class="cm-variable">group</span>.<span class="cm-property">delete</span>(<span class="cm-number">10</span>);
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">group</span>.<span class="cm-property">has</span>(<span class="cm-number">10</span>));
<span class="cm-comment">// → false</span></pre>
<div class="solution"><div class="solution-text">
<p><a class="p_ident" id="p_fQA7KS4poU" href="#p_fQA7KS4poU" tabindex="-1" role="presentation"></a>The easiest way to do this is to store an array of group members in an instance property. The <code>includes</code> or <code>indexOf</code> methods can be used to check whether a given value is in the array.</p>
<p><a class="p_ident" id="p_2+V8dOgKHs" href="#p_2+V8dOgKHs" tabindex="-1" role="presentation"></a>Your class’s constructor can set the member collection to an empty array. When <code>add</code> is called, it must check whether the given value is in the array or add it, for example with <code>push</code>, otherwise.</p>
<p><a class="p_ident" id="p_GVg0o9XSa+" href="#p_GVg0o9XSa+" tabindex="-1" role="presentation"></a>Deleting an element from an array, in <code>delete</code>, is less straightforward, but you can use <code>filter</code> to create a new array without the value. Don’t forget to overwrite the property holding the members with the newly filtered version of the array.</p>
<p><a class="p_ident" id="p_1KdbI2vH34" href="#p_1KdbI2vH34" tabindex="-1" role="presentation"></a>The <code>from</code> method can use a <code>for</code>/<code>of</code> loop to get the values out of the iterable object and call <code>add</code> to put them into a newly created group.</p>
</div></div>
<h3><a class="i_ident" id="i_djD3XDJ27V" href="#i_djD3XDJ27V" tabindex="-1" role="presentation"></a>Iterable groups</h3>
<p id="group_iterator"><a class="p_ident" id="p_azaOoj0ezw" href="#p_azaOoj0ezw" tabindex="-1" role="presentation"></a>Make the <code>Group</code> class from the previous exercise iterable. Refer to the section about the iterator interface earlier in the chapter if you aren’t clear on the exact form of the interface anymore.</p>
<p><a class="p_ident" id="p_SoL9V7zUCt" href="#p_SoL9V7zUCt" tabindex="-1" role="presentation"></a>If you used an array to represent the group’s members, don’t just return the iterator created by calling the <code>Symbol.iterator</code> method on the array. That would work, but it defeats the purpose of this exercise.</p>
<p><a class="p_ident" id="p_rLyKomI6vw" href="#p_rLyKomI6vw" tabindex="-1" role="presentation"></a>It is okay if your iterator behaves strangely when the group is modified during iteration.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_n6ZNn/zRLj" href="#c_n6ZNn/zRLj" tabindex="-1" role="presentation"></a><span class="cm-comment">// Your code here (and the code from the previous exercise)</span>
<span class="cm-keyword">for</span> (<span class="cm-keyword">let</span> <span class="cm-def">value</span> <span class="cm-keyword">of</span> <span class="cm-variable">Group</span>.<span class="cm-property">from</span>([<span class="cm-string">"a"</span>, <span class="cm-string">"b"</span>, <span class="cm-string">"c"</span>])) {
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">value</span>);
}
<span class="cm-comment">// → a</span>
<span class="cm-comment">// → b</span>
<span class="cm-comment">// → c</span></pre>
<div class="solution"><div class="solution-text">
<p><a class="p_ident" id="p_URAzFlYuQP" href="#p_URAzFlYuQP" tabindex="-1" role="presentation"></a>It is probably worthwhile to define a new class <code>GroupIterator</code>. Iterator instances should have a property that tracks the current position in the group. Every time <code>next</code> is called, it checks whether it is done and, if not, moves past the current value and returns it.</p>
<p><a class="p_ident" id="p_il9bvEkyQT" href="#p_il9bvEkyQT" tabindex="-1" role="presentation"></a>The <code>Group</code> class itself gets a method named by <code>Symbol.iterator</code> that, when called, returns a new instance of the iterator class for that group.</p>
</div></div>
<h3><a class="i_ident" id="i_wcWSnr9zHV" href="#i_wcWSnr9zHV" tabindex="-1" role="presentation"></a>Borrowing a method</h3>
<p><a class="p_ident" id="p_HxMI3VGcht" href="#p_HxMI3VGcht" tabindex="-1" role="presentation"></a>Earlier in the chapter I mentioned that an object’s <code>hasOwnProperty</code> can be used as a more robust alternative to the <code>in</code> operator when you want to ignore the prototype’s properties. But what if your map needs to include the word <code>"hasOwnProperty"</code>? You won’t be able to call that method anymore because the object’s own property hides the method value.</p>
<p><a class="p_ident" id="p_nxOrPKy+Ky" href="#p_nxOrPKy+Ky" tabindex="-1" role="presentation"></a>Can you think of a way to call <code>hasOwnProperty</code> on an object that has its own property by that name?</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_LtMKqqkY0Q" href="#c_LtMKqqkY0Q" tabindex="-1" role="presentation"></a><span class="cm-keyword">let</span> <span class="cm-def">map</span> <span class="cm-operator">=</span> {<span class="cm-property">one</span>: <span class="cm-atom">true</span>, <span class="cm-property">two</span>: <span class="cm-atom">true</span>, <span class="cm-property">hasOwnProperty</span>: <span class="cm-atom">true</span>};
<span class="cm-comment">// Fix this call</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">map</span>.<span class="cm-property">hasOwnProperty</span>(<span class="cm-string">"one"</span>));
<span class="cm-comment">// → true</span></pre>
<div class="solution"><div class="solution-text">
<p><a class="p_ident" id="p_Pg/cWjUrKm" href="#p_Pg/cWjUrKm" tabindex="-1" role="presentation"></a>Remember that methods that exist on plain objects come from <code>Object.prototype</code>.</p>
<p><a class="p_ident" id="p_wCf1aBfcJA" href="#p_wCf1aBfcJA" tabindex="-1" role="presentation"></a>Also remember that you can call a function with a specific <code>this</code> binding by using its <code>call</code> method.</p>
</div></div><nav><a href="05_higher_order.html" title="previous chapter">◀</a> <a href="index.html" title="cover">◆</a> <a href="07_robot.html" title="next chapter">▶</a></nav>
</article>