forked from pandark/eloquent-javascript-translation
-
Notifications
You must be signed in to change notification settings - Fork 0
/
chapter4.html
680 lines (647 loc) · 91.7 KB
/
chapter4.html
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
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
<html>
<head>
<link rel="stylesheet" type="text/css" href="css/book.css"/>
<link rel="stylesheet" type="text/css" href="css/highlight.css"/>
<link rel="stylesheet" type="text/css" href="css/console.css"/>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>Structures de données : objets et tableaux -- JavaScript Éloquent</title>
</head>
<body>
<script type="text/javascript" src="js/before.js"> </script>
<div class="content">
<script type="text/javascript">var chapterTag = 'data';</script>
<div class="navigation">
<a href="chapter3.html"><< Chapitre précédent</a> |
<a href="contents.html">Table des matières</a> |
<a href="index.html">Couverture</a> |
<a href="chapter5.html">Chapitre suivant >></a>
</div>
<h1><span class="number">Chapitre 4 : </span>Structures de données : objets et tableaux</h1>
<div class="block">
<p>Ce chapitre sera consacré à la résolution de quelques problèmes simples. En chemin, nous allons étudier deux nouveaux types de valeurs, les tableaux et les objets, et regarder quelques techniques les concernant.</p>
<p>Considérons la situation suivante : votre tante Emilie l'exentrique, dont la rumeur dit qu'elle vit avec cinquante chats (en fait personne n'arrive à les compter), vous envoie régulièrement des emails pour vous tenir au courant de ses exploits. Ils sont de la forme suivante :</p>
<blockquote>Mon cher neveu,
<br/><br/>
Ta mère m'a dit que tu as commencé à faire du parachutisme. Est-ce que c'est vrai ? Fait attention à toi, mon garçon ! Souviens toi ce qui est arrivé à mon mari, et ce n'était que du deuxième étage !
<br/><br/>
En tout cas, les choses sont très intéressantes de mon côté. J'ai passé toute la semaine à essayer d'obtenir l'attention de Mr. Drake, le sympatique monsieur qui a aménagé en face, mais je pense qu'il a peur des chats. A moins qu'il soit alergique ? A la prochaine rencontre, je vais essayer de poser Igor Le Gros sur son épaule. Je suis curieuse du résultat.
<br/><br/>
Par ailleurs, l'escroquerie dont je t'avais parlé fonctionne mieux que je pensais. J'ai déjà récupéré cinq 'paiements', et seulement une seule plainte. Mais j'ai quelques remors malgré tout. Et tu as sans doute raison de dire que c'est probablement illégal d'une manière ou d'une autre.
<br/><br/>
(... etc ...)
<br/><br/>
Grosses bises, Tante Emilie
<br/><br/>
Décès le 27/04/2006: Leclère Noire
<br/><br/>
Naissance le 05/04/2006 (mère Lady Penelope) : Lion Rouge, Docteur Hobbles le 3e, Petit Iroquois</blockquote>
<p>Pour amuser cette vieille dame, vous voudriez garder une trace de la généalogie de ses chats, pour pouvoir ajouter des commentaires comme "P.S. J'espère que Docteur Hobbles le 2e a bien fêté son anniversaire samedi !", ou bien "Comme va cette vieille Lady Penelope ? Elle a cinq ans maintenant, n'est-ce pas ?", en évitant de préférence de demander des nouvelles des chats décédés. Vous avez une grande quantité archivée d'email de votre tante, et par chance, elle est très constante dans sa manière de donner les renseignements sur les naissances et décès des chats, à la fin de ses emails, toujours dans le même format.</p>
<p>Vous n'avez pas envie de parcourir à la main tout ses messages. Heureusement, nous avions justement besoin d'un exemple, nous allons donc écrire un programme qui va faire le travail pour nous. Pour commencer, nous écrivons un programme qui va nous donner la liste des chats qui sont toujours vivants à la fin le dernier email.</p>
<p>Before you ask, at the start of the correspondence, aunt Emily had only a single cat: Spot. (She was still rather conventional in those days.)</p>
</div><hr/><div class="block">
<div class="picture"><img src="img/eyes.png"/></div>
</div><hr/><div class="block">
<p>Il est généralement préférable d'avoir une idée de départ sur ce que va faire un programme avant de se mettre à l'écrire... Voici le plan :</p>
<ol><li>
Commencer avec un ensemble de nom de chats qne comprenant que "Spot".
</li><li>
Parcourir chaque email dans l'archive, par ordre chronologique.
</li><li>
Chercher les paragraphes qui commencent par "Naissance le" ou "Décès le".
</li><li>
Ajouter les noms de chats trouvés dans les paragraphes qui commencent par "Naissance le" à l'ensemble de noms.
</li><li>
Supprimer les noms de chats trouvés dans les paragraphes qui commencent par "Décès le" de notre ensemble.
</li></ol>
<p>On prendra les noms à partir d'un paragraphe de la façon suivante :</p>
<ol><li>
Trouver les 'deux points' (:) dans le paragraphe.
</li><li>
Prendre la partie après ce signe.
</li><li>
Séparer en noms différents en cherchant les virgules.
</li></ol>
<p>Cet énoncé d'exercice peut rendre nécessaire d'oublier quelques instants les exceptions possibles, et d'accepter aveuglément que tante Emilie utilise toujours le même format d'écriture, qu'elle n'oublie jamais un nom de chat, ni ne fait de faute de frappe, mais votre tante est comme çà, et çà tombe bien pour nous.</p>
</div><hr/><div class="block">
<p>D'abord, je vais vous expliquer les <a name="key1"></a>propriétés. Beaucoup de valeurs en Javascript ont d'autres valeurs associés. Ces associations sont appelées propriétés. Chaque chaîne de caractère a une propriété appelée <a name="key2"></a><code>length</code>, (longueur), qui correspond à un nombre, la quantité de caractères dans cette chaîne.</p>
<p><a name="key3"></a>On peut accéder aux propriétés de deux manières :</p>
<pre class="code"><span class="keyword">var</span> <span class="variable">text</span> = <span class="string">"purple haze"</span>;
<span class="variable">show</span>(<span class="variable">text</span>[<span class="string">"length"</span>]);
<span class="variable">show</span>(<span class="variable">text</span>.<span class="property">length</span>);</pre>
<p>La seconde manière est un raccourci de la première et ne fonctionne que lorsque le nom de la propriété s'écrit comme un nom de variable ― lorsqu'il n'y a pas d'espace ou de symbole, et lorsqu'elle ne commence par par un chiffre.</p>
<p>Les valeurs <code>null</code> et <code>undefined</code> n'ont pas de propriété. Essayer de lire des propriétés de ces valeurs donnera une erreur. Essayez le code suivant, juste pour voir le type de message d'erreur que votre navigateur va retourner dans ce cas de figure. (Pour certains navigateurs, ce message sera assez mystérieux).</p>
<pre class="code invalid"><span class="keyword">var</span> <span class="variable">nothing</span> = <span class="atom">null</span>;
<span class="variable">show</span>(<span class="variable">nothing</span>.<span class="property">length</span>);</pre>
</div><hr/><div class="block">
<p>Les propriétés d'une chaîne de caractère ne peuvent pas être changées. Elles sont plus nombreuses que la longueur <code>length</code>, comme nous allons le voir, mais vous ne pouvez ni ajouter ni supprimer des propriétés.</p>
<p>C'est différent avec les valeur du type <a name="key4"></a>object. Leur rôle principal est de conserver d'autres valeurs. Ils ont, en quelque sorte leur propre, leur propre jeu de "tentacules" sous forme de propriétés. Vous pouvez les modifier, les supprimer ou en ajouter d'autres.</p>
<p><a name="key5"></a>Un objet peut s'écrire de la façon suivante :</p>
<pre class="code"><span class="keyword">var</span> <span class="variable">cat</span> = {<span class="property">colour</span>: <span class="string">"grey"</span>, <span class="property">name</span>: <span class="string">"Spot"</span>, <span class="property">size</span>: <span class="atom">46</span>};
<span class="variable">cat</span>.<span class="property">size</span> = <span class="atom">47</span>;
<span class="variable">show</span>(<span class="variable">cat</span>.<span class="property">size</span>);
<span class="keyword">delete</span> <span class="variable">cat</span>.<span class="property">size</span>;
<span class="variable">show</span>(<span class="variable">cat</span>.<span class="property">size</span>);
<span class="variable">show</span>(<span class="variable">cat</span>);</pre>
<p>Comme les variables, chaque propriété attachée à un objet a son nom sous forme d'une chaîne de caractère. la première ligne créé un objet dans lequel la propriété <code>"colour"</code> contient la chaîne <code>"grey"</code>, la propriété <code>"name"</code> est liée à la chaîne <code>"Spot"</code>, et la propriété <code>"size"</code> fait référence au nombre <code>46</code>. La deuxième ligne fait référence à la propriété <code>size</code> une nouvelle valeur, ce qui se fait de la même manière que pour la modification d'une variable.</p>
<p>Le mot-clé <a name="key6"></a><code>delete</code> supprime les propriétés. Essayer de lire une propriété qui n'existe pas donnera la valeur <code>undefined</code>.</p>
<p>Si une propriété qui n'existe pas encore est affectée avec l'opérateur <a name="key7"></a><code>=</code> operator, elle est ajoutée à l'objet.</p>
<pre class="code"><span class="keyword">var</span> <span class="variable">empty</span> = {};
<span class="variable">empty</span>.<span class="property">notReally</span> = <span class="atom">1000</span>;
<span class="variable">show</span>(<span class="variable">empty</span>.<span class="property">notReally</span>);</pre>
<p>Les propriétés dont le nom ne pourrait pas être une variable doivent être mise entre quillement au moment de la création de l'objet, et utilisées avec des parenthèses :</p>
<pre class="code"><span class="keyword">var</span> <span class="variable">thing</span> = {<span class="string">"gabba gabba"</span>: <span class="string">"hey"</span>, <span class="string">"5"</span>: <span class="atom">10</span>};
<span class="variable">show</span>(<span class="variable">thing</span>[<span class="string">"5"</span>]);
<span class="variable">thing</span>[<span class="string">"5"</span>] = <span class="atom">20</span>;
<span class="variable">show</span>(<span class="variable">thing</span>[<span class="atom">2</span> + <span class="atom">3</span>]);
<span class="keyword">delete</span> <span class="variable">thing</span>[<span class="string">"gabba gabba"</span>];</pre>
<p>Comme vous pouvez le voir, on peut mettre n'importe quelle expression entre les parenthèses. Elle sera convertie dans une chaîne pour définir le nom de la propriété. On peut aussi utiliser des variables pour donner un nom à une propriété :</p>
<pre class="code"><span class="keyword">var</span> <span class="variable">propertyName</span> = <span class="string">"length"</span>;
<span class="keyword">var</span> <span class="variable">text</span> = <span class="string">"mainline"</span>;
<span class="variable">show</span>(<span class="variable">text</span>[<span class="variable">propertyName</span>]);</pre>
<p>L'opérateur <a name="key8"></a><code>in</code> peur servir à tester si un objet possède une certaine propriété. Son résultat est un booléen.</p>
<pre class="code"><span class="keyword">var</span> <span class="variable">chineseBox</span> = {};
<span class="variable">chineseBox</span>.<span class="property">content</span> = <span class="variable">chineseBox</span>;
<span class="variable">show</span>(<span class="string">"content"</span> in <span class="variable">chineseBox</span>);
<span class="variable">show</span>(<span class="string">"content"</span> in <span class="variable">chineseBox</span>.<span class="property">content</span>);</pre>
</div><hr/><div class="block">
<p>Quand les valeurs d'un objet sont affichées sur la console, on peut cliquer à la souris pour inspecter leurs propriétés. La fenêtre de 'sortie' devient une fenêtre 'inspecteur'. Le petit 'x' en haut à droite s'utilise pour retourner à la fenêtre de sortie, et la flêche gauche permet de retourner aux propriétés de l'objet inspecté.</p>
<pre class="code"><span class="variable">show</span>(<span class="variable">chineseBox</span>);</pre>
</div><hr/><div class="block">
<a name="exercise1"></a>
<div class="exercisenum">Ex. 4.1</div>
<div class="exercise">
<p>La solution pour le problème des Chats nous parle d'un 'ensemble' de noms. Un <a name="key9"></a>ensemble (ou 'set') est un groupe de valeurs dans lequel aucune valeur ne peut apparaître plus d'une fois. Si les noms de chats sont des chaînes de caractères, pouvez-vous imaginer une façon pour qu'un objet devienne un ensemble de noms ?</p>
<p>Ecrivez mantenant comment un nom peut être ajouté à cet ensemble, comment on peut le supprimer, et comment on peut vérifier si un certain nom est bien présent dans l'ensemble.</p>
</div>
<div class="solution">
<p>Une solution consiste à mémoriser le contenu de l'ensemble comme étant une propriété d'un objet. Pour ajouter un nom, on crée une propriété avec ce nom, en lui affectant une valeur, n'importe laquelle. Pour supprimer un nom, on supprimera la propriété de l'objet. L'opérateur <code>in</code> sera utilisé pour savoir si une certaine propriété fait partie de l'ensemble <a class="footref" href="#footnote1">1</a>.</p>
<pre class="code"><span class="keyword">var</span> <span class="variable">set</span> = {<span class="string">"Spot"</span>: <span class="atom">true</span>};
<span class="comment">// Add "White Fang" to the set</span>
<span class="variable">set</span>[<span class="string">"White Fang"</span>] = <span class="atom">true</span>;
<span class="comment">// Remove "Spot"</span>
<span class="keyword">delete</span> <span class="variable">set</span>[<span class="string">"Spot"</span>];
<span class="comment">// See if "Asoka" is in the set</span>
<span class="variable">show</span>(<span class="string">"Asoka"</span> in <span class="variable">set</span>);</pre>
</div>
</div><hr/><div class="block">
<p><a name="key10"></a>On voit ici que les valeurs d'un objet peuvent changer. Les types de valeurs vues dans le <a href="chapter2.html">chapitre 2</a> sont toutes invariantes, il n'est pas possible de changer une valeur existante pour ces types de données. Vous pouvez les associer ou les réutiliser pour d'autres valeurs, mais lorsque vous prenez le contenu particulier d'une chaîne de caractères ne peut pas être modifié en essayant de changer ses propriétés.</p>
<p>A partir de deux nombres, <code>120</code> et <code>120</code>, il est possible de les considérer comme identiques pour les besoins de la programmation. Avec des objets, il y a une différence importante entre avoir deux 'références' du même objet, et avoir deux objets distincts qui possèdent les mêmes propriétés. Considérons le code suivant :</p>
<pre class="code"><span class="keyword">var</span> <span class="variable">object1</span> = {<span class="property">value</span>: <span class="atom">10</span>};
<span class="keyword">var</span> <span class="variable">object2</span> = <span class="variable">object1</span>;
<span class="keyword">var</span> <span class="variable">object3</span> = {<span class="property">value</span>: <span class="atom">10</span>};
<span class="variable">show</span>(<span class="variable">object1</span> == <span class="variable">object2</span>);
<span class="variable">show</span>(<span class="variable">object1</span> == <span class="variable">object3</span>);
<span class="variable">object1</span>.<span class="property">value</span> = <span class="atom">15</span>;
<span class="variable">show</span>(<span class="variable">object2</span>.<span class="property">value</span>);
<span class="variable">show</span>(<span class="variable">object3</span>.<span class="property">value</span>);</pre>
<p><code>object1</code> et <code>object2</code> sont deux variables attachées à la <em>même</em> valeur. Il n'y a en fait qu'un seul objet, c'est pourquoi en changeant <code>object1</code> on change également al valeur de <code>object2</code>. La variable <code>object3</code> pointe vers un autre objet, qui contient au départ la même propriété que <code>object1</code>, mais il a une existence distincte.</p>
<p>L'opérateur JavaScript <a name="key11"></a><code>==</code>, lorsqu'il compare des objets, ne retournera que la valeur booléenne <code>true</code> si chacune des valeur qu'on lui donne à comparer sont exactement les mêmes. Comparer des objets différents ayant des contenus identiques donnera le résultat <code>false</code>. C'est utile dans certaines situation, mais pas adapté dans d'autres.</p>
</div><hr/><div class="block">
<p>Les valeurs d'un objet peuvent jouer beaucoup de rôles différents. Cette utilisation pour gérer un ensemble en est une. Nous allons voir d'autres utilisations dans ce chapitre et le <a href="chapter8.html">chapitre 8</a> couvrira d'autres utilisations importantes des objets.</p>
<p>Dans le plan d'action pour le problème des Chats -en fait, appelons le un <em>algorithme</em>, au lieu d'un plan, celà nous donne l'impression qu'on sait de quoi on parle― dans l'algorythme, on parle de parcourir chaque email contenu dans une archive. Mais comment se présente cette archive ? Et d'où vient-elle ?</p>
<p>Ne vous inquiétez pas de la deuxième question pour le moment. Le <a href="chapter14.html">chapitre 14</a> explique quelques-une des possibilités pour importer des données dans vos programmes. Pour l'instant, on dira que les emails sont déjà là, comme par magie. La magie est parfois très facile, avec les ordinateurs.</p>
</div><hr/><div class="block">
<p>La façon dont l'archive est enregisrée reste une question pertinente. Elle contient une quantité d'emails. Un email peut être vu comme une chaîne de caractères, c'est évident. Toute l'archive pourrait être mise dans une énorme chaîne de caractère, mais ce ne serait pas pratique. Ce qu'il nous faut c'est une structure de chaînes de caractères distinctes.</p>
<p>Les objets sont justement utilisés pour structurer des choses. On pourrait très bien créer un objet comme celui-ci :</p>
<pre class="code"><span class="keyword">var</span> <span class="variable">mailArchive</span> = {<span class="string">"le premier email"</span>: <span class="string">"Cher neveu, ..."</span>,
<span class="string">"le deuxieme email"</span>: <span class="string">"..."</span>
<span class="comment">/* etc ... */</span>};</pre>
<p>Mais parcourir les emails du début à la fin serait difficile ― comment le programme peut-il deviner le nom de ces propriétés ? La solution est d'utiliser des noms de propriétés plus pratiques :</p>
<pre class="code"><span class="keyword">var</span> <span class="variable">mailArchive</span> = {<span class="atom">0</span>: <span class="string">"Cher neveu, ... (email numero 1)"</span>,
<span class="atom">1</span>: <span class="string">"(email numero 2)"</span>,
<span class="atom">2</span>: <span class="string">"(email numero 3)"</span>};
<span class="keyword">for</span> (<span class="keyword">var</span> <span class="variable">current</span> = <span class="atom">0</span>; <span class="variable">current</span> in <span class="variable">mailArchive</span>; <span class="variable">current</span>++)
<span class="variable">print</span>(<span class="string">"Traitement du mail #"</span>, <span class="variable">current</span>, <span class="string">": "</span>, <span class="variable">mailArchive</span>[<span class="variable">current</span>]);</pre>
<p>La chance veut qu'il existe un type d'objet particulier qui corrrespond exactement à ce type de besoin. Ce sont les tableaux (<a name="key12"></a>arrays), et ils fournissent des services très utiles, comme par exemple <a name="key13"></a><code>length</code> (longueur), une propriété qui contient le nombre d'éléments dans le tableau, et bien d'autres fonctions utiles pour ce type de structure.</p>
<p><a name="key14"></a>Pour créer de nouveaux tableaux on utilise des crochets (<code>[</code> et <code>]</code>):</p>
<pre class="code"><span class="keyword">var</span> <span class="variable">mailArchive</span> = [<span class="string">"mail un"</span>, <span class="string">"mail deux"</span>, <span class="string">"mail trois"</span>];
<span class="keyword">for</span> (<span class="keyword">var</span> <span class="variable">current</span> = <span class="atom">0</span>; <span class="variable">current</span> < <span class="variable">mailArchive</span>.<span class="property">length</span>; <span class="variable">current</span>++)
<span class="variable">print</span>(<span class="string">"Traitement du mail #"</span>, <span class="variable">current</span>, <span class="string">": "</span>, <span class="variable">mailArchive</span>[<span class="variable">current</span>]);</pre>
<p>Dans cet exemple, le nombre d'éléments n'est plus explicite. Le premier a automatiquement le numéro 0, le deuxième le numéro 1, et ainsi de suite.</p>
<p>Pourquoi commencer à 0 ? On compte d'habitude à partir de 1. Aussi étrange que celà paraîsse, la numérotation à partir de 0 est souvent plus pratique pour programmer. Acceptez le pour l'instant, vous allez vous y faire.</p>
<p>Commencer par l'élément 0 veut aussi dire que dans une structure qui a <code>X</code> éléments, le dernier élément sera trouvé à la position <code>X - 1</code>. C'est pourquoi la boucle <code>for</code> dans notre exemple teste la valeur <code>current < mailArchive.length</code>. IL n'y a pas d'élément à la position <code>mailArchive.length</code>, donc dès que <code>current</code> atteint cette valeur, on arrête la boucle.</p>
</div><hr/><div class="block">
<a name="exercise2"></a>
<div class="exercisenum">Ex. 4.2</div>
<div class="exercise">
<p>Ecrivez une fonction nommée <code>range</code> qui prend un argument, un nombre positif, et retourne un tableau contenant chaque nombre de 0 jusqu'au nombre donné en paramètre inclus.</p>
<p>Un tableau vide peut être créé en tapant simplement <code>[]</code>. Souvenez-vous que pour ajouter des propriétés à un tableau, comme pour un objet, il suffit d'affecter une valeur à la propriété avec l'opérateur <code>=</code>. La propriété <code>length</code> est mise à jout automatiquement quand des éléments sont ajoutés.</p>
</div>
<div class="solution"><pre class="code"><span class="keyword">function</span> <span class="variable">range</span>(<span class="variabledef">upto</span>) {
<span class="keyword">var</span> <span class="variabledef">result</span> = [];
<span class="keyword">for</span> (<span class="keyword">var</span> <span class="variabledef">i</span> = <span class="atom">0</span>; <span class="localvariable">i</span> <= <span class="localvariable">upto</span>; <span class="localvariable">i</span>++)
<span class="localvariable">result</span>[<span class="localvariable">i</span>] = <span class="localvariable">i</span>;
<span class="keyword">return</span> <span class="localvariable">result</span>;
}
<span class="variable">show</span>(<span class="variable">range</span>(<span class="atom">4</span>));</pre>
<p>Au lieu de nommer la variable de boucle <code>counter</code> ou <code>current</code>, comme je l'ai fait jusqu'à présent, elle s'appelle simplement <code>i</code>. L'utilisation d'une seule lettre, habituellement <code>i</code>, <code>j</code>, ou <code>k</code> pour les variables de boucle est une habitude très répandue en programmation. Son origine tient presque à de la paresse : on préfère taper un caractère que sept, et des noms comme <code>counter</code> et <code>current</code> ne donnent pas forcément plus d'information sur la variable.</p>
<p>Si un programme utilise trop souvent des variables à un caractère, sans explication, il peut devenir très difficile à comprendre. Dans mes propres programmes, j'essaie de me limiter à quelques cas de figures seulement. Les petites boucles font partie de ces cas. Si la boucle contient une autre boucle, et que celle-ci utilise aussi une variable appelée <code>i</code>, la boucle intérieure va modifier la variable dont se sert la première boucle, et rien ne va fonctionner. Ou pourrait utiliser <code>j</code> pour la boucle intérieure, mais en général, lorsque le corps d'une boucle est grand, vous devriez utiliser un nom de variable ayant une signification utile pour la compréhension.</p>
</div>
</div><hr/><div class="block">
<p>Les chaînes de caractères et les objets "tableaux" contiennent, en plus de la propriété <code>length</code>, quelques autres propriétés qui font référence à des fonctions.</p>
<pre class="code"><span class="keyword">var</span> <span class="variable">doh</span> = <span class="string">"Doh"</span>;
<span class="variable">print</span>(typeof <span class="variable">doh</span>.<span class="property">toUpperCase</span>);
<span class="variable">print</span>(<span class="variable">doh</span>.<span class="property">toUpperCase</span>());</pre>
<p>Chaque chaîne de caractère a une propriété <a name="key15"></a><code>toUpperCase</code>. Lorsqu'elle est appelée, elle retourne une copie de la chaîne, transformée avec chaque lettre en majuscule. Il y a aussi l'équivalent <a name="key16"></a><code>toLowerCase</code>. Devinez le résultat...</p>
<p>Remarquez que même si l'appel de <code>toUpperCase</code> se fait sans arguments, la fonction a malgré tout accès au contenu de la chaîne de caractère <code>"Doh"</code>, dont la valeur est une propriété. La façon dont celà fonctionne est décrit dans le <a href="chapter8.html">chapitre 8</a>.</p>
<p>Les propriétés qui se comportent comme des fonctions sont généralement appelées <a name="key17"></a>méthodes, comme pour '<code>toUpperCase</code>' qui est une méthode des objets de type 'chaîne de caractère'.</p>
<pre class="code"><span class="keyword">var</span> <span class="variable">mack</span> = [];
<span class="variable">mack</span>.<span class="property">push</span>(<span class="string">"Mack"</span>);
<span class="variable">mack</span>.<span class="property">push</span>(<span class="string">"the"</span>);
<span class="variable">mack</span>.<span class="property">push</span>(<span class="string">"Knife"</span>);
<span class="variable">show</span>(<span class="variable">mack</span>.<span class="property">join</span>(<span class="string">" "</span>));
<span class="variable">show</span>(<span class="variable">mack</span>.<span class="property">pop</span>());
<span class="variable">show</span>(<span class="variable">mack</span>);</pre>
<p>La méthode <a name="key18"></a><code>push</code>, qui est disponible pour les tableaux, est utilisée pour y ajouter des valeurs. Nous aurions pu l'utiliser dans l'exercice précédent, à la place de <code>result[i] = i</code>. Et puis on trouve aussi la méthode <a name="key19"></a><code>pop</code>, complémentaire de <code>push</code>: elle supprime le dernier élément d'un tableau et retourne sa valeur. <a name="key20"></a><code>join</code> construit une chaîne de caractère unique à partir d'un tableau de chaînes de caractères. Le paramètre utilisé avec cette méthode sera inséré entre chaque valeur du tableau, avant l'assemblage de la chaîne de caractère finale..</p>
</div><hr/><div class="block">
<p>Revenons à nos Chats : nous savons maintenant qu'utiliser un tableau serait une bonne idée pour ranger l'archive des emails. Sur cette page, la fonction <code>retrieveMails</code> sera utilisée (comme par magie) pour récupérer ce tableau complet. Parcourir les emails qu'il contient pour les traiter un par un devient simple comme un jeu d'enfant :</p>
<pre class="code"><span class="keyword">var</span> <span class="variable">mailArchive</span> = <span class="variable">retrieveMails</span>();
<span class="keyword">for</span> (<span class="keyword">var</span> <span class="variable">i</span> = <span class="atom">0</span>; <span class="variable">i</span> < <span class="variable">mailArchive</span>.<span class="property">length</span>; <span class="variable">i</span>++) {
<span class="keyword">var</span> <span class="variable">email</span> = <span class="variable">mailArchive</span>[<span class="variable">i</span>];
<span class="variable">print</span>(<span class="string">"Processing e-mail #"</span>, <span class="variable">i</span>);
<span class="comment">// Do more things...</span>
}</pre>
<p>We have also decided on a way to represent the set of cats that are alive. The next problem, then, is to find the paragraphs in an e-mail that start with <code>"born"</code> or <code>"died"</code>.</p>
</div><hr/><div class="block">
<p>The first question that comes up is what exactly a paragraph is. In this case, the string value itself can't help us much: JavaScript's concept of text does not go any deeper than the 'sequence of characters' idea, so we must define paragraphs in those terms.</p>
<p>Earlier, we saw that there is such a thing as a newline character. These are what most people use to split paragraphs. We consider a paragraph, then, to be a part of an e-mail that starts at a newline character or at the start of the content, and ends at the next newline character or at the end of the content.</p>
<p>And we don't even have to write the algorithm for splitting a string into paragraphs ourselves. Strings already have a method named <a name="key21"></a><code>split</code>, which is (almost) the opposite of the <code>join</code> method of arrays. It splits a string into an array, using the string given as its argument to determine in which places to cut.</p>
<pre class="code"><span class="keyword">var</span> <span class="variable">words</span> = <span class="string">"Cities of the Interior"</span>;
<span class="variable">show</span>(<span class="variable">words</span>.<span class="property">split</span>(<span class="string">" "</span>));</pre>
<p>Thus, cutting on newlines (<code>"\n"</code>), can be used to split an e-mail into paragraphs.</p>
</div><hr/><div class="block">
<a name="exercise3"></a>
<div class="exercisenum">Ex. 4.3</div>
<div class="exercise">
<p><code>split</code> and <code>join</code> are not precisely each other's inverse. <code>string.split(x).join(x)</code> always produces the original value, but <code>array.join(x).split(x)</code> does not. Can you give an example of an array where <code>.join(" ").split(" ")</code> produces a different value?</p>
</div>
<div class="solution"><pre class="code"><span class="keyword">var</span> <span class="variable">array</span> = [<span class="string">"a"</span>, <span class="string">"b"</span>, <span class="string">"c d"</span>];
<span class="variable">show</span>(<span class="variable">array</span>.<span class="property">join</span>(<span class="string">" "</span>).<span class="property">split</span>(<span class="string">" "</span>));</pre>
</div>
</div><hr/><div class="block">
<p>Paragraphs that do not start with either "born" or "died" can be ignored by the program. How do we test whether a string starts with a certain word? The method <a name="key22"></a><code>charAt</code> can be used to get a specific character from a string. <code>x.charAt(0)</code> gives the first character, <code>1</code> is the second one, and so on. One way to check whether a string starts with "born" is:</p>
<pre class="code"><span class="keyword">var</span> <span class="variable">paragraph</span> = <span class="string">"born 15-11-2003 (mother Spot): White Fang"</span>;
<span class="variable">show</span>(<span class="variable">paragraph</span>.<span class="property">charAt</span>(<span class="atom">0</span>) == <span class="string">"b"</span> && <span class="variable">paragraph</span>.<span class="property">charAt</span>(<span class="atom">1</span>) == <span class="string">"o"</span> &&
<span class="variable">paragraph</span>.<span class="property">charAt</span>(<span class="atom">2</span>) == <span class="string">"r"</span> && <span class="variable">paragraph</span>.<span class="property">charAt</span>(<span class="atom">3</span>) == <span class="string">"n"</span>);</pre>
<p>But that gets a bit clumsy ― imagine checking for a word of ten characters. There is something to be learned here though: when a line gets ridiculously long, it can be spread over multiple lines. The result can be made easier to read by lining up the start of the new line with the first element on the original line that plays a similar role.</p>
<p>Strings also have a method called <a name="key23"></a><code>slice</code>. It copies out a piece of the string, starting from the character at the position given by the first argument, and ending before (not including) the character at the position given by the second one. This allows the check to be written in a shorter way.</p>
<pre class="code"><span class="variable">show</span>(<span class="variable">paragraph</span>.<span class="property">slice</span>(<span class="atom">0</span>, <span class="atom">4</span>) == <span class="string">"born"</span>);</pre>
</div><hr/><div class="block">
<a name="exercise4"></a>
<div class="exercisenum">Ex. 4.4</div>
<div class="exercise">
<p>Write a function called <code>startsWith</code> that takes two arguments, both strings. It returns <code>true</code> when the first argument starts with the characters in the second argument, and <code>false</code> otherwise.</p>
</div>
<div class="solution"><pre class="code"><span class="keyword">function</span> <span class="variable">startsWith</span>(<span class="variabledef">string</span>, <span class="variabledef">pattern</span>) {
<span class="keyword">return</span> <span class="localvariable">string</span>.<span class="property">slice</span>(<span class="atom">0</span>, <span class="localvariable">pattern</span>.<span class="property">length</span>) == <span class="localvariable">pattern</span>;
}
<span class="variable">show</span>(<span class="variable">startsWith</span>(<span class="string">"rotation"</span>, <span class="string">"rot"</span>));</pre>
</div>
</div><hr/><div class="block">
<p>What happens when <code>charAt</code> or <code>slice</code> are used to take a piece of a string that does not exist? Will the <code>startsWith</code> I showed still work when the pattern is longer than the string it is matched against?</p>
<pre class="code"><span class="variable">show</span>(<span class="string">"Pip"</span>.<span class="property">charAt</span>(<span class="atom">250</span>));
<span class="variable">show</span>(<span class="string">"Nop"</span>.<span class="property">slice</span>(<span class="atom">1</span>, <span class="atom">10</span>));</pre>
<p><code>charAt</code> will return <code>""</code> when there is no character at the given position, and <code>slice</code> will simply leave out the part of the new string that does not exist.</p>
<p>So yes, that version of <code>startsWith</code> works. When <code>startsWith("Idiots", "Most honoured colleagues")</code> is called, the call to <code>slice</code> will, because <code>string</code> does not have enough characters, always return a string that is shorter than <code>pattern</code>. Because of that, the comparison with <code>==</code> will return <code>false</code>, which is correct.</p>
<p>It helps to always take a moment to consider abnormal (but valid) inputs for a program. These are usually called <a name="key24"></a>corner cases, and it is very common for programs that work perfectly on all the 'normal' inputs to screw up on corner cases.</p>
</div><hr/><div class="block">
<p>The only part of the cat-problem that is still unsolved is the extraction of names from a paragraph. The algorithm was this:</p>
<ol><li>
Find the colon in the paragraph.
</li><li>
Take the part after this colon.
</li><li>
Split this part into separate names by looking for commas.
</li></ol>
<p>This has to happen both for paragraphs that start with <code>"died"</code>, and paragraphs that start with <code>"born"</code>. It would be a good idea to put it into a function, so that the two pieces of code that handle these different kinds of paragraphs can both use it.</p>
</div><hr/><div class="block">
<a name="exercise5"></a>
<div class="exercisenum">Ex. 4.5</div>
<div class="exercise">
<p>Can you write a function <code>catNames</code> that takes a paragraph as an argument and returns an array of names?</p>
<p>Strings have an <a name="key25"></a><code>indexOf</code> method that can be used to find the (first) position of a character or sub-string within that string. Also, when <code>slice</code> is given only one argument, it will return the part of the string from the given position all the way to the end.</p>
<p>It can be helpful to use the console to 'explore' functions. For example, type <code>"foo: bar".indexOf(":")</code> and see what you get.</p>
</div>
<div class="solution"><pre class="code"><span class="keyword">function</span> <span class="variable">catNames</span>(<span class="variabledef">paragraph</span>) {
<span class="keyword">var</span> <span class="variabledef">colon</span> = <span class="localvariable">paragraph</span>.<span class="property">indexOf</span>(<span class="string">":"</span>);
<span class="keyword">return</span> <span class="localvariable">paragraph</span>.<span class="property">slice</span>(<span class="localvariable">colon</span> + <span class="atom">2</span>).<span class="property">split</span>(<span class="string">", "</span>);
}
<span class="variable">show</span>(<span class="variable">catNames</span>(<span class="string">"born 20/09/2004 (mother Yellow Bess): "</span> +
<span class="string">"Doctor Hobbles the 2nd, Noog"</span>));</pre>
<p>The tricky part, which the original description of the algorithm ignored, is dealing with spaces after the colon and the commas. The <code>+ 2</code> used when slicing the string is needed to leave out the colon itself and the space after it. The argument to <code>split</code> contains both a comma and a space, because that is what the names are really separated by, rather than just a comma.</p>
<p>This function does not do any checking for problems. We assume, in this case, that the input is always correct.</p>
</div>
</div><hr/><div class="block">
<p>All that remains now is putting the pieces together. One way to do that looks like this:</p>
<pre class="code"><span class="keyword">var</span> <span class="variable">mailArchive</span> = <span class="variable">retrieveMails</span>();
<span class="keyword">var</span> <span class="variable">livingCats</span> = {<span class="string">"Spot"</span>: <span class="atom">true</span>};
<span class="keyword">for</span> (<span class="keyword">var</span> <span class="variable">mail</span> = <span class="atom">0</span>; <span class="variable">mail</span> < <span class="variable">mailArchive</span>.<span class="property">length</span>; <span class="variable">mail</span>++) {
<span class="keyword">var</span> <span class="variable">paragraphs</span> = <span class="variable">mailArchive</span>[<span class="variable">mail</span>].<span class="property">split</span>(<span class="string">"\n"</span>);
<span class="keyword">for</span> (<span class="keyword">var</span> <span class="variable">paragraph</span> = <span class="atom">0</span>;
<span class="variable">paragraph</span> < <span class="variable">paragraphs</span>.<span class="property">length</span>;
<span class="variable">paragraph</span>++) {
<span class="keyword">if</span> (<span class="variable">startsWith</span>(<span class="variable">paragraphs</span>[<span class="variable">paragraph</span>], <span class="string">"born"</span>)) {
<span class="keyword">var</span> <span class="variable">names</span> = <span class="variable">catNames</span>(<span class="variable">paragraphs</span>[<span class="variable">paragraph</span>]);
<span class="keyword">for</span> (<span class="keyword">var</span> <span class="variable">name</span> = <span class="atom">0</span>; <span class="variable">name</span> < <span class="variable">names</span>.<span class="property">length</span>; <span class="variable">name</span>++)
<span class="variable">livingCats</span>[<span class="variable">names</span>[<span class="variable">name</span>]] = <span class="atom">true</span>;
}
<span class="keyword">else</span> <span class="keyword">if</span> (<span class="variable">startsWith</span>(<span class="variable">paragraphs</span>[<span class="variable">paragraph</span>], <span class="string">"died"</span>)) {
<span class="keyword">var</span> <span class="variable">names</span> = <span class="variable">catNames</span>(<span class="variable">paragraphs</span>[<span class="variable">paragraph</span>]);
<span class="keyword">for</span> (<span class="keyword">var</span> <span class="variable">name</span> = <span class="atom">0</span>; <span class="variable">name</span> < <span class="variable">names</span>.<span class="property">length</span>; <span class="variable">name</span>++)
<span class="keyword">delete</span> <span class="variable">livingCats</span>[<span class="variable">names</span>[<span class="variable">name</span>]];
}
}
}
<span class="variable">show</span>(<span class="variable">livingCats</span>);</pre>
<p>That is quite a big dense chunk of code. We'll look into making it a bit lighter in a moment. But first let us look at our results. We know how to check whether a specific cat survives:</p>
<pre class="code"><span class="keyword">if</span> (<span class="string">"Spot"</span> in <span class="variable">livingCats</span>)
<span class="variable">print</span>(<span class="string">"Spot lives!"</span>);
<span class="keyword">else</span>
<span class="variable">print</span>(<span class="string">"Good old Spot, may she rest in peace."</span>);</pre>
<p>But how do we list all the cats that are alive? The <a name="key26"></a><code>in</code> keyword has a somewhat different meaning when it is used together with <code>for</code>:</p>
<pre class="code"><span class="keyword">for</span> (<span class="keyword">var</span> <span class="variable">cat</span> <span class="keyword">in</span> <span class="variable">livingCats</span>)
<span class="variable">print</span>(<span class="variable">cat</span>);</pre>
<p>A loop like that will go over the names of the properties in an object, which allows us to enumerate all the names in our set.</p>
</div><hr/><div class="block">
<p>Some pieces of code look like an impenetrable jungle. The example solution to the cat problem suffers from this. One way to make some light shine through it is to just add some strategic blank lines. This makes it look better, but doesn't really solve the problem.</p>
<p>What is needed here is to break the code up. We already wrote two helper functions, <code>startsWith</code> and <code>catNames</code>, which both take care of a small, understandable part of the problem. Let us continue doing this.</p>
<pre class="code"><span class="keyword">function</span> <span class="variable">addToSet</span>(<span class="variabledef">set</span>, <span class="variabledef">values</span>) {
<span class="keyword">for</span> (<span class="keyword">var</span> <span class="variabledef">i</span> = <span class="atom">0</span>; <span class="localvariable">i</span> < <span class="localvariable">values</span>.<span class="property">length</span>; <span class="localvariable">i</span>++)
<span class="localvariable">set</span>[<span class="localvariable">values</span>[<span class="localvariable">i</span>]] = <span class="atom">true</span>;
}
<span class="keyword">function</span> <span class="variable">removeFromSet</span>(<span class="variabledef">set</span>, <span class="variabledef">values</span>) {
<span class="keyword">for</span> (<span class="keyword">var</span> <span class="variabledef">i</span> = <span class="atom">0</span>; <span class="localvariable">i</span> < <span class="localvariable">values</span>.<span class="property">length</span>; <span class="localvariable">i</span>++)
<span class="keyword">delete</span> <span class="localvariable">set</span>[<span class="localvariable">values</span>[<span class="localvariable">i</span>]];
}</pre>
<p>These two functions take care of the adding and removing of names from the set. That already cuts out the two most inner loops from the solution:</p>
<pre class="code"><span class="keyword">var</span> <span class="variable">livingCats</span> = {<span class="property">Spot</span>: <span class="atom">true</span>};
<span class="keyword">for</span> (<span class="keyword">var</span> <span class="variable">mail</span> = <span class="atom">0</span>; <span class="variable">mail</span> < <span class="variable">mailArchive</span>.<span class="property">length</span>; <span class="variable">mail</span>++) {
<span class="keyword">var</span> <span class="variable">paragraphs</span> = <span class="variable">mailArchive</span>[<span class="variable">mail</span>].<span class="property">split</span>(<span class="string">"\n"</span>);
<span class="keyword">for</span> (<span class="keyword">var</span> <span class="variable">paragraph</span> = <span class="atom">0</span>;
<span class="variable">paragraph</span> < <span class="variable">paragraphs</span>.<span class="property">length</span>;
<span class="variable">paragraph</span>++) {
<span class="keyword">if</span> (<span class="variable">startsWith</span>(<span class="variable">paragraphs</span>[<span class="variable">paragraph</span>], <span class="string">"born"</span>))
<span class="variable">addToSet</span>(<span class="variable">livingCats</span>, <span class="variable">catNames</span>(<span class="variable">paragraphs</span>[<span class="variable">paragraph</span>]));
<span class="keyword">else</span> <span class="keyword">if</span> (<span class="variable">startsWith</span>(<span class="variable">paragraphs</span>[<span class="variable">paragraph</span>], <span class="string">"died"</span>))
<span class="variable">removeFromSet</span>(<span class="variable">livingCats</span>, <span class="variable">catNames</span>(<span class="variable">paragraphs</span>[<span class="variable">paragraph</span>]));
}
}</pre>
<p>Quite an improvement, if I may say so myself.</p>
<p>Why do <code>addToSet</code> and <code>removeFromSet</code> take the set as an argument? They could use the variable <code>livingCats</code> directly, if they wanted to. The reason is that this way they are not completely tied to our current problem. If <code>addToSet</code> directly changed <code>livingCats</code>, it would have to be called <code>addCatsToCatSet</code>, or something similar. The way it is now, it is a more generally useful tool.</p>
<p>Even if we are never going to use these functions for anything else, which is quite probable, it is useful to write them like this. Because they are 'self sufficient', they can be read and understood on their own, without needing to know about some external variable called <code>livingCats</code>.</p>
<p>The functions are not pure: They change the object passed as their <code>set</code> argument. This makes them slightly trickier than real pure functions, but still a lot less confusing than functions that run amok and change any value or variable they please.</p>
</div><hr/><div class="block">
<p>We continue breaking the algorithm into pieces:</p>
<pre class="code"><span class="keyword">function</span> <span class="variable">findLivingCats</span>() {
<span class="keyword">var</span> <span class="variabledef">mailArchive</span> = <span class="variable">retrieveMails</span>();
<span class="keyword">var</span> <span class="variabledef">livingCats</span> = {<span class="string">"Spot"</span>: <span class="atom">true</span>};
<span class="keyword">function</span> <span class="variabledef">handleParagraph</span>(<span class="variabledef">paragraph</span>) {
<span class="keyword">if</span> (<span class="variable">startsWith</span>(<span class="localvariable">paragraph</span>, <span class="string">"born"</span>))
<span class="variable">addToSet</span>(<span class="localvariable">livingCats</span>, <span class="variable">catNames</span>(<span class="localvariable">paragraph</span>));
<span class="keyword">else</span> <span class="keyword">if</span> (<span class="variable">startsWith</span>(<span class="localvariable">paragraph</span>, <span class="string">"died"</span>))
<span class="variable">removeFromSet</span>(<span class="localvariable">livingCats</span>, <span class="variable">catNames</span>(<span class="localvariable">paragraph</span>));
}
<span class="keyword">for</span> (<span class="keyword">var</span> <span class="variabledef">mail</span> = <span class="atom">0</span>; <span class="localvariable">mail</span> < <span class="localvariable">mailArchive</span>.<span class="property">length</span>; <span class="localvariable">mail</span>++) {
<span class="keyword">var</span> <span class="variabledef">paragraphs</span> = <span class="localvariable">mailArchive</span>[<span class="localvariable">mail</span>].<span class="property">split</span>(<span class="string">"\n"</span>);
<span class="keyword">for</span> (<span class="keyword">var</span> <span class="variabledef">i</span> = <span class="atom">0</span>; <span class="localvariable">i</span> < <span class="localvariable">paragraphs</span>.<span class="property">length</span>; <span class="localvariable">i</span>++)
<span class="localvariable">handleParagraph</span>(<span class="localvariable">paragraphs</span>[<span class="localvariable">i</span>]);
}
<span class="keyword">return</span> <span class="localvariable">livingCats</span>;
}
<span class="keyword">var</span> <span class="variable">howMany</span> = <span class="atom">0</span>;
<span class="keyword">for</span> (<span class="keyword">var</span> <span class="variable">cat</span> <span class="keyword">in</span> <span class="variable">findLivingCats</span>())
<span class="variable">howMany</span>++;
<span class="variable">print</span>(<span class="string">"There are "</span>, <span class="variable">howMany</span>, <span class="string">" cats."</span>);</pre>
<p>The whole algorithm is now encapsulated by a function. This means that it does not leave a mess after it runs: <code>livingCats</code> is now a local variable in the function, instead of a top-level one, so it only exists while the function runs. The code that needs this set can call <code>findLivingCats</code> and use the value it returns.</p>
<p>It seemed to me that making <code>handleParagraph</code> a separate function also cleared things up. But this one is so closely tied to the cat-algorithm that it is meaningless in any other situation. On top of that, it needs access to the <code>livingCats</code> variable. Thus, it is a perfect candidate to be a function-inside-a-function. When it lives inside <code>findLivingCats</code>, it is clear that it is only relevant there, and it has access to the variables of its parent function.</p>
<p>This solution is actually <em>bigger</em> than the previous one. Still, it is tidier and I hope you'll agree that it is easier to read.</p>
</div><hr/><div class="block">
<p>The program still ignores a lot of the information that is contained in the e-mails. There are birth-dates, dates of death, and the names of mothers in there.</p>
<p>To start with the dates: What would be a good way to store a date? We could make an object with three properties, <code>year</code>, <code>month</code>, and <code>day</code>, and store numbers in them.</p>
<pre class="code"><span class="keyword">var</span> <span class="variable">when</span> = {<span class="property">year</span>: <span class="atom">1980</span>, <span class="property">month</span>: <span class="atom">2</span>, <span class="property">day</span>: <span class="atom">1</span>};</pre>
<p>But JavaScript already provides a kind of object for this purpose. Such an object can be created by using the keyword <a name="key27"></a><code>new</code>:</p>
<pre class="code"><span class="keyword">var</span> <span class="variable">when</span> = <span class="keyword">new</span> <span class="variable">Date</span>(<span class="atom">1980</span>, <span class="atom">1</span>, <span class="atom">1</span>);
<span class="variable">show</span>(<span class="variable">when</span>);</pre>
<p>Just like the notation with braces and colons we have already seen, <code>new</code> is a way to create object values. Instead of specifying all the property names and values, a function is used to build up the object. This makes it possible to define a kind of standard procedure for creating objects. Functions like this are called <a name="key28"></a>constructors, and in <a href="chapter8.html">chapter 8</a> we will see how to write them.</p>
<p>The <a name="key29"></a><code>Date</code> constructor can be used in different ways.</p>
<pre class="code"><span class="variable">show</span>(<span class="keyword">new</span> <span class="variable">Date</span>());
<span class="variable">show</span>(<span class="keyword">new</span> <span class="variable">Date</span>(<span class="atom">1980</span>, <span class="atom">1</span>, <span class="atom">1</span>));
<span class="variable">show</span>(<span class="keyword">new</span> <span class="variable">Date</span>(<span class="atom">2007</span>, <span class="atom">2</span>, <span class="atom">30</span>, <span class="atom">8</span>, <span class="atom">20</span>, <span class="atom">30</span>));</pre>
<p>As you can see, these objects can store a time of day as well as a date. When not given any arguments, an object representing the current time and date is created. Arguments can be given to ask for a specific date and time. The order of the arguments is year, month, day, hour, minute, second, milliseconds. These last four are optional, they become 0 when not given.</p>
<p>The month numbers these objects use go from 0 to 11, which can be confusing. Especially since day numbers <em>do</em> start from 1.</p>
</div><hr/><div class="block">
<p>The content of a <code>Date</code> object can be inspected with a number of <code>get...</code> methods.</p>
<pre class="code"><span class="keyword">var</span> <span class="variable">today</span> = <span class="keyword">new</span> <span class="variable">Date</span>();
<span class="variable">print</span>(<span class="string">"Year: "</span>, <span class="variable">today</span>.<span class="property">getFullYear</span>(), <span class="string">", month: "</span>,
<span class="variable">today</span>.<span class="property">getMonth</span>(), <span class="string">", day: "</span>, <span class="variable">today</span>.<span class="property">getDate</span>());
<span class="variable">print</span>(<span class="string">"Hour: "</span>, <span class="variable">today</span>.<span class="property">getHours</span>(), <span class="string">", minutes: "</span>,
<span class="variable">today</span>.<span class="property">getMinutes</span>(), <span class="string">", seconds: "</span>, <span class="variable">today</span>.<span class="property">getSeconds</span>());
<span class="variable">print</span>(<span class="string">"Day of week: "</span>, <span class="variable">today</span>.<span class="property">getDay</span>());</pre>
<p>All of these, except for <code>getDay</code>, also have a <code>set...</code> variant that can be used to change the value of the date object.</p>
<p>Inside the object, a date is represented by the amount of milliseconds it is away from January 1st 1970. You can imagine this is quite a large number.</p>
<pre class="code"><span class="keyword">var</span> <span class="variable">today</span> = <span class="keyword">new</span> <span class="variable">Date</span>();
<span class="variable">show</span>(<span class="variable">today</span>.<span class="property">getTime</span>());</pre>
<p>A very useful thing to do with dates is comparing them.</p>
<pre class="code"><span class="keyword">var</span> <span class="variable">wallFall</span> = <span class="keyword">new</span> <span class="variable">Date</span>(<span class="atom">1989</span>, <span class="atom">10</span>, <span class="atom">9</span>);
<span class="keyword">var</span> <span class="variable">gulfWarOne</span> = <span class="keyword">new</span> <span class="variable">Date</span>(<span class="atom">1990</span>, <span class="atom">6</span>, <span class="atom">2</span>);
<span class="variable">show</span>(<span class="variable">wallFall</span> < <span class="variable">gulfWarOne</span>);
<span class="variable">show</span>(<span class="variable">wallFall</span> == <span class="variable">wallFall</span>);
<span class="comment">// but</span>
<span class="variable">show</span>(<span class="variable">wallFall</span> == <span class="keyword">new</span> <span class="variable">Date</span>(<span class="atom">1989</span>, <span class="atom">10</span>, <span class="atom">9</span>));</pre>
<p>Comparing dates with <code><</code>, <code>></code>, <code><=</code>, and <code>>=</code> does exactly what you would expect. When a date object is compared to itself with <code>==</code> the result is <code>true</code>, which is also good. But when <a name="key30"></a><code>==</code> is used to compare a date object to a different, equal date object, we get <code>false</code>. Huh?</p>
<p>As mentioned earlier, <code>==</code> will return <code>false</code> when comparing two different objects, even if they contain the same properties. This is a bit clumsy and error-prone here, since one would expect <code>>=</code> and <code>==</code> to behave in a more or less similar way. Testing whether two dates are equal can be done like this:</p>
<pre class="code"><span class="keyword">var</span> <span class="variable">wallFall1</span> = <span class="keyword">new</span> <span class="variable">Date</span>(<span class="atom">1989</span>, <span class="atom">10</span>, <span class="atom">9</span>),
<span class="variable">wallFall2</span> = <span class="keyword">new</span> <span class="variable">Date</span>(<span class="atom">1989</span>, <span class="atom">10</span>, <span class="atom">9</span>);
<span class="variable">show</span>(<span class="variable">wallFall1</span>.<span class="property">getTime</span>() == <span class="variable">wallFall2</span>.<span class="property">getTime</span>());</pre>
</div><hr/><div class="block">
<p>In addition to a date and time, <code>Date</code> objects also contain information about a <a name="key31"></a>timezone. When it is one o'clock in Amsterdam, it can, depending on the time of year, be noon in London, and seven in the morning in New York. Such times can only be compared when you take their time zones into account. The <a name="key32"></a><code>getTimezoneOffset</code> function of a <code>Date</code> can be used to find out how many minutes it differs from GMT (Greenwich Mean Time).</p>
<pre class="code"><span class="keyword">var</span> <span class="variable">now</span> = <span class="keyword">new</span> <span class="variable">Date</span>();
<span class="variable">print</span>(<span class="variable">now</span>.<span class="property">getTimezoneOffset</span>());</pre>
</div><hr/><div class="block">
<a name="exercise6"></a>
<div class="exercisenum">Ex. 4.6</div>
<div class="exercise"><pre class="preformatted">"died 27/04/2006: Black Leclère"</pre>
<p>The date part is always in the exact same place of a paragraph. How convenient. Write a function <code>extractDate</code> that takes such a paragraph as its argument, extracts the date, and returns it as a date object.</p>
</div>
<div class="solution"><pre class="code"><span class="keyword">function</span> <span class="variable">extractDate</span>(<span class="variabledef">paragraph</span>) {
<span class="keyword">function</span> <span class="variabledef">numberAt</span>(<span class="variabledef">start</span>, <span class="variabledef">length</span>) {
<span class="keyword">return</span> <span class="variable">Number</span>(<span class="localvariable">paragraph</span>.<span class="property">slice</span>(<span class="localvariable">start</span>, <span class="localvariable">start</span> + <span class="localvariable">length</span>));
}
<span class="keyword">return</span> <span class="keyword">new</span> <span class="variable">Date</span>(<span class="localvariable">numberAt</span>(<span class="atom">11</span>, <span class="atom">4</span>), <span class="localvariable">numberAt</span>(<span class="atom">8</span>, <span class="atom">2</span>) - <span class="atom">1</span>,
<span class="localvariable">numberAt</span>(<span class="atom">5</span>, <span class="atom">2</span>));
}
<span class="variable">show</span>(<span class="variable">extractDate</span>(<span class="string">"died 27-04-2006: Black Leclère"</span>));</pre>
<p>It would work without the calls to <code>Number</code>, but as mentioned earlier, I prefer not to use strings as if they are numbers. The inner function was introduced to prevent having to repeat the <code>Number</code> and <code>slice</code> part three times.</p>
<p>Note the <code>- 1</code> for the month number. Like most people, Aunt Emily counts her months from 1, so we have to adjust the value before giving it to the <code>Date</code> constructor. (The day number does not have this problem, since <code>Date</code> objects count days in the usual human way.)</p>
<p>In <a href="chapter10.html">chapter 10</a> we will see a more practical and robust way of extracting pieces from strings that have a fixed structure.</p>
</div>
</div><hr/><div class="block">
<p>Storing cats will work differently from now on. Instead of just putting the value <code>true</code> into the set, we store an object with information about the cat. When a cat dies, we do not remove it from the set, we just add a property <code>death</code> to the object to store the date on which the creature died.</p>
<p>This means our <code>addToSet</code> and <code>removeFromSet</code> functions have become useless. Something similar is needed, but it must also store birth-dates and, later, the mother's name.</p>
<pre class="code"><span class="keyword">function</span> <span class="variable">catRecord</span>(<span class="variabledef">name</span>, <span class="variabledef">birthdate</span>, <span class="variabledef">mother</span>) {
<span class="keyword">return</span> {<span class="property">name</span>: <span class="localvariable">name</span>, <span class="property">birth</span>: <span class="localvariable">birthdate</span>, <span class="property">mother</span>: <span class="localvariable">mother</span>};
}
<span class="keyword">function</span> <span class="variable">addCats</span>(<span class="variabledef">set</span>, <span class="variabledef">names</span>, <span class="variabledef">birthdate</span>, <span class="variabledef">mother</span>) {
<span class="keyword">for</span> (<span class="keyword">var</span> <span class="variabledef">i</span> = <span class="atom">0</span>; <span class="localvariable">i</span> < <span class="localvariable">names</span>.<span class="property">length</span>; <span class="localvariable">i</span>++)
<span class="localvariable">set</span>[<span class="localvariable">names</span>[<span class="localvariable">i</span>]] = <span class="variable">catRecord</span>(<span class="localvariable">names</span>[<span class="localvariable">i</span>], <span class="localvariable">birthdate</span>, <span class="localvariable">mother</span>);
}
<span class="keyword">function</span> <span class="variable">deadCats</span>(<span class="variabledef">set</span>, <span class="variabledef">names</span>, <span class="variabledef">deathdate</span>) {
<span class="keyword">for</span> (<span class="keyword">var</span> <span class="variabledef">i</span> = <span class="atom">0</span>; <span class="localvariable">i</span> < <span class="localvariable">names</span>.<span class="property">length</span>; <span class="localvariable">i</span>++)
<span class="localvariable">set</span>[<span class="localvariable">names</span>[<span class="localvariable">i</span>]].<span class="property">death</span> = <span class="localvariable">deathdate</span>;
}</pre>
<p><code>catRecord</code> is a separate function for creating these storage objects. It might be useful in other situations, such as creating the object for Spot. 'Record' is a term often used for objects like this, which are used to group a limited number of values.</p>
</div><hr/><div class="block">
<p>So let us try to extract the names of the mother cats from the paragraphs.</p>
<pre class="preformatted">"born 15/11/2003 (mother Spot): White Fang"</pre>
<p>One way to do this would be...</p>
<pre class="code"><span class="keyword">function</span> <span class="variable">extractMother</span>(<span class="variabledef">paragraph</span>) {
<span class="keyword">var</span> <span class="variabledef">start</span> = <span class="localvariable">paragraph</span>.<span class="property">indexOf</span>(<span class="string">"(mother "</span>) + <span class="string">"(mother "</span>.<span class="property">length</span>;
<span class="keyword">var</span> <span class="variabledef">end</span> = <span class="localvariable">paragraph</span>.<span class="property">indexOf</span>(<span class="string">")"</span>);
<span class="keyword">return</span> <span class="localvariable">paragraph</span>.<span class="property">slice</span>(<span class="localvariable">start</span>, <span class="localvariable">end</span>);
}
<span class="variable">show</span>(<span class="variable">extractMother</span>(<span class="string">"born 15/11/2003 (mother Spot): White Fang"</span>));</pre>
<p>Notice how the start position has to be adjusted for the length of <code>"(mother "</code>, because <code>indexOf</code> returns the position of the start of the pattern, not its end.</p>
</div><hr/><div class="block">
<a name="exercise7"></a>
<div class="exercisenum">Ex. 4.7</div>
<div class="exercise">
<p>The thing that <code>extractMother</code> does can be expressed in a more general way. Write a function <code>between</code> that takes three arguments, all of which are strings. It will return the part of the first argument that occurs between the patterns given by the second and the third arguments.</p>
<p>So <code>between("born 15/11/2003 (mother Spot): White Fang", "(mother ", ")")</code> gives <code>"Spot"</code>.</p>
<p><code>between("bu ] boo [ bah ] gzz", "[ ", " ]")</code> returns <code>"bah"</code>.</p>
<p>To make that second test work, it can be useful to know that <code>indexOf</code> can be given a second, optional parameter that specifies at which point it should start searching.</p>
</div>
<div class="solution"><pre class="code"><span class="keyword">function</span> <span class="variable">between</span>(<span class="variabledef">string</span>, <span class="variabledef">start</span>, <span class="variabledef">end</span>) {
<span class="keyword">var</span> <span class="variabledef">startAt</span> = <span class="localvariable">string</span>.<span class="property">indexOf</span>(<span class="localvariable">start</span>) + <span class="localvariable">start</span>.<span class="property">length</span>;
<span class="keyword">var</span> <span class="variabledef">endAt</span> = <span class="localvariable">string</span>.<span class="property">indexOf</span>(<span class="localvariable">end</span>, <span class="localvariable">startAt</span>);
<span class="keyword">return</span> <span class="localvariable">string</span>.<span class="property">slice</span>(<span class="localvariable">startAt</span>, <span class="localvariable">endAt</span>);
}
<span class="variable">show</span>(<span class="variable">between</span>(<span class="string">"bu ] boo [ bah ] gzz"</span>, <span class="string">"[ "</span>, <span class="string">" ]"</span>));</pre>
</div>
</div><hr/><div class="block">
<p>Having <code>between</code> makes it possible to express extractMother in a simpler way:</p>
<pre class="code"><span class="keyword">function</span> <span class="variable">extractMother</span>(<span class="variabledef">paragraph</span>) {
<span class="keyword">return</span> <span class="variable">between</span>(<span class="localvariable">paragraph</span>, <span class="string">"(mother "</span>, <span class="string">")"</span>);
}</pre>
</div><hr/><div class="block">
<p>The new, improved cat-algorithm looks like this:</p>
<pre class="code"><span class="keyword">function</span> <span class="variable">findCats</span>() {
<span class="keyword">var</span> <span class="variabledef">mailArchive</span> = <span class="variable">retrieveMails</span>();
<span class="keyword">var</span> <span class="variabledef">cats</span> = {<span class="string">"Spot"</span>: <span class="variable">catRecord</span>(<span class="string">"Spot"</span>, <span class="keyword">new</span> <span class="variable">Date</span>(<span class="atom">1997</span>, <span class="atom">2</span>, <span class="atom">5</span>),
<span class="string">"unknown"</span>)};
<span class="keyword">function</span> <span class="variabledef">handleParagraph</span>(<span class="variabledef">paragraph</span>) {
<span class="keyword">if</span> (<span class="variable">startsWith</span>(<span class="localvariable">paragraph</span>, <span class="string">"born"</span>))
<span class="variable">addCats</span>(<span class="localvariable">cats</span>, <span class="variable">catNames</span>(<span class="localvariable">paragraph</span>), <span class="variable">extractDate</span>(<span class="localvariable">paragraph</span>),
<span class="variable">extractMother</span>(<span class="localvariable">paragraph</span>));
<span class="keyword">else</span> <span class="keyword">if</span> (<span class="variable">startsWith</span>(<span class="localvariable">paragraph</span>, <span class="string">"died"</span>))
<span class="variable">deadCats</span>(<span class="localvariable">cats</span>, <span class="variable">catNames</span>(<span class="localvariable">paragraph</span>), <span class="variable">extractDate</span>(<span class="localvariable">paragraph</span>));
}
<span class="keyword">for</span> (<span class="keyword">var</span> <span class="variabledef">mail</span> = <span class="atom">0</span>; <span class="localvariable">mail</span> < <span class="localvariable">mailArchive</span>.<span class="property">length</span>; <span class="localvariable">mail</span>++) {
<span class="keyword">var</span> <span class="variabledef">paragraphs</span> = <span class="localvariable">mailArchive</span>[<span class="localvariable">mail</span>].<span class="property">split</span>(<span class="string">"\n"</span>);
<span class="keyword">for</span> (<span class="keyword">var</span> <span class="variabledef">i</span> = <span class="atom">0</span>; <span class="localvariable">i</span> < <span class="localvariable">paragraphs</span>.<span class="property">length</span>; <span class="localvariable">i</span>++)
<span class="localvariable">handleParagraph</span>(<span class="localvariable">paragraphs</span>[<span class="localvariable">i</span>]);
}
<span class="keyword">return</span> <span class="localvariable">cats</span>;
}
<span class="keyword">var</span> <span class="variable">catData</span> = <span class="variable">findCats</span>();</pre>
<p>Having that extra data allows us to finally have a clue about the cats aunt Emily talks about. A function like this could be useful:</p>
<pre class="code"><span class="keyword">function</span> <span class="variable">formatDate</span>(<span class="variabledef">date</span>) {
<span class="keyword">return</span> <span class="localvariable">date</span>.<span class="property">getDate</span>() + <span class="string">"/"</span> + (<span class="localvariable">date</span>.<span class="property">getMonth</span>() + <span class="atom">1</span>) +
<span class="string">"/"</span> + <span class="localvariable">date</span>.<span class="property">getFullYear</span>();
}
<span class="keyword">function</span> <span class="variable">catInfo</span>(<span class="variabledef">data</span>, <span class="variabledef">name</span>) {
<span class="keyword">if</span> (!(<span class="localvariable">name</span> in <span class="localvariable">data</span>))
<span class="keyword">return</span> <span class="string">"No cat by the name of "</span> + <span class="localvariable">name</span> + <span class="string">" is known."</span>;
<span class="keyword">var</span> <span class="variabledef">cat</span> = <span class="localvariable">data</span>[<span class="localvariable">name</span>];
<span class="keyword">var</span> <span class="variabledef">message</span> = <span class="localvariable">name</span> + <span class="string">", born "</span> + <span class="variable">formatDate</span>(<span class="localvariable">cat</span>.<span class="property">birth</span>) +
<span class="string">" from mother "</span> + <span class="localvariable">cat</span>.<span class="property">mother</span>;
<span class="keyword">if</span> (<span class="string">"death"</span> in <span class="localvariable">cat</span>)
<span class="localvariable">message</span> += <span class="string">", died "</span> + <span class="variable">formatDate</span>(<span class="localvariable">cat</span>.<span class="property">death</span>);
<span class="keyword">return</span> <span class="localvariable">message</span> + <span class="string">"."</span>;
}
<span class="variable">print</span>(<span class="variable">catInfo</span>(<span class="variable">catData</span>, <span class="string">"Fat Igor"</span>));</pre>
<p>The first <code>return</code> statement in <code>catInfo</code> is used as an escape hatch. If there is no data about the given cat, the rest of the function is meaningless, so we immediately return a value, which prevents the rest of the code from running.</p>
<p>In the past, certain groups of programmers considered functions that contain multiple <code>return</code> statements sinful. The idea was that this made it hard to see which code was executed and which code was not. Other techniques, which will be discussed in <a href="chapter5.html">chapter 5</a>, have made the reasons behind this idea more or less obsolete, but you might still occasionally come across someone who will criticise the use of 'shortcut' return statements.</p>
</div><hr/><div class="block">
<a name="exercise8"></a>
<div class="exercisenum">Ex. 4.8</div>
<div class="exercise">
<p>The <code>formatDate</code> function used by <code>catInfo</code> does not add a zero before the month and the day part when these are only one digit long. Write a new version that does this.</p>
</div>
<div class="solution"><pre class="code"><span class="keyword">function</span> <span class="variable">formatDate</span>(<span class="variabledef">date</span>) {
<span class="keyword">function</span> <span class="variabledef">pad</span>(<span class="variabledef">number</span>) {
<span class="keyword">if</span> (<span class="localvariable">number</span> < <span class="atom">10</span>)
<span class="keyword">return</span> <span class="string">"0"</span> + <span class="localvariable">number</span>;
<span class="keyword">else</span>
<span class="keyword">return</span> <span class="localvariable">number</span>;
}
<span class="keyword">return</span> <span class="localvariable">pad</span>(<span class="localvariable">date</span>.<span class="property">getDate</span>()) + <span class="string">"/"</span> + <span class="localvariable">pad</span>(<span class="localvariable">date</span>.<span class="property">getMonth</span>() + <span class="atom">1</span>) +
<span class="string">"/"</span> + <span class="localvariable">date</span>.<span class="property">getFullYear</span>();
}
<span class="variable">print</span>(<span class="variable">formatDate</span>(<span class="keyword">new</span> <span class="variable">Date</span>(<span class="atom">2000</span>, <span class="atom">0</span>, <span class="atom">1</span>)));</pre>
</div>
</div><hr/><div class="block">
<a name="exercise9"></a>
<div class="exercisenum">Ex. 4.9</div>
<div class="exercise">
<p>Write a function <code>oldestCat</code> which, given an object containing cats as its argument, returns the name of the oldest living cat.</p>
</div>
<div class="solution"><pre class="code"><span class="keyword">function</span> <span class="variable">oldestCat</span>(<span class="variabledef">data</span>) {
<span class="keyword">var</span> <span class="variabledef">oldest</span> = <span class="atom">null</span>;
<span class="keyword">for</span> (<span class="keyword">var</span> <span class="variabledef">name</span> <span class="keyword">in</span> <span class="localvariable">data</span>) {
<span class="keyword">var</span> <span class="variabledef">cat</span> = <span class="localvariable">data</span>[<span class="localvariable">name</span>];
<span class="keyword">if</span> (!(<span class="string">"death"</span> in <span class="localvariable">cat</span>) &&
(<span class="localvariable">oldest</span> == <span class="atom">null</span> || <span class="localvariable">oldest</span>.<span class="property">birth</span> > <span class="localvariable">cat</span>.<span class="property">birth</span>))
<span class="localvariable">oldest</span> = <span class="localvariable">cat</span>;
}
<span class="keyword">if</span> (<span class="localvariable">oldest</span> == <span class="atom">null</span>)
<span class="keyword">return</span> <span class="atom">null</span>;
<span class="keyword">else</span>
<span class="keyword">return</span> <span class="localvariable">oldest</span>.<span class="property">name</span>;
}
<span class="variable">print</span>(<span class="variable">oldestCat</span>(<span class="variable">catData</span>));</pre>
<p>The condition in the <code>if</code> statement might seem a little intimidating. It can be read as 'only store the current cat in the variable <code>oldest</code> if it is not dead, and <code>oldest</code> is either <code>null</code> or a cat that was born after the current cat'.</p>
<p>Note that this function returns <code>null</code> when there are no living cats in <code>data</code>. What does your solution do in that case?</p>
</div>
</div><hr/><div class="block">
<p>Now that we are familiar with arrays, I can show you something related. Whenever a function is called, a special variable named <a name="key33"></a><code>arguments</code> is added to the environment in which the function body runs. This variable refers to an object that resembles an array. It has a property <code>0</code> for the first argument, <code>1</code> for the second, and so on for every argument the function was given. It also has a <a name="key34"></a><code>length</code> property.</p>
<p>This object is not a real array though, it does not have methods like <code>push</code>, and it does not automatically update its <code>length</code> property when you add something to it. Why not, I never really found out, but this is something one needs to be aware of.</p>
<pre class="code"><span class="keyword">function</span> <span class="variable">argumentCounter</span>() {
<span class="variable">print</span>(<span class="string">"You gave me "</span>, <span class="localvariable">arguments</span>.<span class="property">length</span>, <span class="string">" arguments."</span>);
}
<span class="variable">argumentCounter</span>(<span class="string">"Death"</span>, <span class="string">"Famine"</span>, <span class="string">"Pestilence"</span>);</pre>
<p>Some functions can take any number of arguments, like <code>print</code> does. These typically loop over the values in the <code>arguments</code> object to do something with them. Others can take optional arguments which, when not given by the caller, get some sensible default value.</p>
<pre class="code"><span class="keyword">function</span> <span class="variable">add</span>(<span class="variabledef">number</span>, <span class="variabledef">howmuch</span>) {
<span class="keyword">if</span> (<span class="localvariable">arguments</span>.<span class="property">length</span> < <span class="atom">2</span>)
<span class="localvariable">howmuch</span> = <span class="atom">1</span>;
<span class="keyword">return</span> <span class="localvariable">number</span> + <span class="localvariable">howmuch</span>;
}
<span class="variable">show</span>(<span class="variable">add</span>(<span class="atom">6</span>));
<span class="variable">show</span>(<span class="variable">add</span>(<span class="atom">6</span>, <span class="atom">4</span>));</pre>
</div><hr/><div class="block">
<a name="exercise10"></a>
<div class="exercisenum">Ex. 4.10</div>
<div class="exercise">
<p>Extend the <code>range</code> function from <a href="chapter4.html#exercise2">exercise 4.2</a> to take a second, optional argument. If only one argument is given, it behaves as earlier and produces a range from 0 to the given number. If two arguments are given, the first indicates the start of the range, the second the end.</p>
</div>
<div class="solution"><pre class="code"><span class="keyword">function</span> <span class="variable">range</span>(<span class="variabledef">start</span>, <span class="variabledef">end</span>) {
<span class="keyword">if</span> (<span class="localvariable">arguments</span>.<span class="property">length</span> < <span class="atom">2</span>) {
<span class="localvariable">end</span> = <span class="localvariable">start</span>;
<span class="localvariable">start</span> = <span class="atom">0</span>;
}
<span class="keyword">var</span> <span class="variabledef">result</span> = [];
<span class="keyword">for</span> (<span class="keyword">var</span> <span class="variabledef">i</span> = <span class="localvariable">start</span>; <span class="localvariable">i</span> <= <span class="localvariable">end</span>; <span class="localvariable">i</span>++)
<span class="localvariable">result</span>.<span class="property">push</span>(<span class="localvariable">i</span>);
<span class="keyword">return</span> <span class="localvariable">result</span>;
}
<span class="variable">show</span>(<span class="variable">range</span>(<span class="atom">4</span>));
<span class="variable">show</span>(<span class="variable">range</span>(<span class="atom">2</span>, <span class="atom">4</span>));</pre>
<p>The optional argument does not work precisely like the one in the <code>add</code> example above. When it is not given, the first argument takes the role of <code>end</code>, and <code>start</code> becomes <code>0</code>.</p>
</div>
</div><hr/><div class="block">
<a name="exercise11"></a>
<div class="exercisenum">Ex. 4.11</div>
<div class="exercise">
<p>You may remember this line of code from the introduction:</p>
<pre class="code invalid"><span class="variable">print</span>(<span class="variable">sum</span>(<span class="variable">range</span>(<span class="atom">1</span>, <span class="atom">10</span>)));</pre>
<p>We have <code>range</code> now. All we need to make this line work is a <code>sum</code> function. This function takes an array of numbers, and returns their sum. Write it, it should be easy.</p>
</div>
<div class="solution"><pre class="code"><span class="keyword">function</span> <span class="variable">sum</span>(<span class="variabledef">numbers</span>) {
<span class="keyword">var</span> <span class="variabledef">total</span> = <span class="atom">0</span>;
<span class="keyword">for</span> (<span class="keyword">var</span> <span class="variabledef">i</span> = <span class="atom">0</span>; <span class="localvariable">i</span> < <span class="localvariable">numbers</span>.<span class="property">length</span>; <span class="localvariable">i</span>++)
<span class="localvariable">total</span> += <span class="localvariable">numbers</span>[<span class="localvariable">i</span>];
<span class="keyword">return</span> <span class="localvariable">total</span>;
}
<span class="variable">print</span>(<span class="variable">sum</span>(<span class="variable">range</span>(<span class="atom">1</span>, <span class="atom">10</span>)));</pre>
</div>
</div><hr/><div class="block">
<p>The previous chapter showed the functions <code>Math.max</code> and <code>Math.min</code>. With what you know now, you will notice that these are really the properties <code>max</code> and <code>min</code> of the object stored under the name <a name="key35"></a><code>Math</code>. This is another role that objects can play: A warehouse holding a number of related values.</p>
<p>There are quite a lot of values inside <code>Math</code>, if they would all have been placed directly into the global environment they would, as it is called, pollute it. The more names have been taken, the more likely one is to accidentally overwrite the value of some variable. For example, it is not a far shot to want to name something <code>max</code>.</p>
<p>Most languages will stop you, or at least warn you, when you are defining a variable with a name that is already taken. Not JavaScript.</p>
<p>In any case, one can find a whole outfit of mathematical functions and constants inside <code>Math</code>. All the trigonometric functions are there ― <code>cos</code>, <code>sin</code>, <code>tan</code>, <code>acos</code>, <code>asin</code>, <code>atan</code>. π and e, which are written with all capital letters (<code>PI</code> and <code>E</code>), which was, at one time, a fashionable way to indicate something is a constant. <code>pow</code> is a good replacement for the <code>power</code> functions we have been writing, it also accepts negative and fractional exponents. <code>sqrt</code> takes square roots. <code>max</code> and <code>min</code> can give the maximum or minimum of two values. <a name="key36"></a><a name="key37"></a><a name="key38"></a><code>round</code>, <code>floor</code>, and <code>ceil</code> will round numbers to the closest whole number, the whole number below it, and the whole number above it respectively.</p>
<p>There are a number of other values in <code>Math</code>, but this text is an introduction, not a <a name="key39"></a>reference. References are what you look at when you suspect something exists in the language, but need to find out what it is called or how it worked exactly. Unfortunately, there is no one comprehensive complete reference for JavaScript. This is mostly because its current form is the result of a chaotic process of different browsers adding different extensions at different times. The ECMA standard document that was mentioned in the introduction provides a solid documentation of the basic language, but is more or less unreadable. For things like the <code>Math</code> object and other elementary functionality, a rather good reference can be found <a href="http://www.webreference.com/javascript/reference/core_ref/contents.html">here</a>. The old documentation from Netscape, which can still be found at <a href="http://docs.sun.com/source/816-6408-10">Sun's website</a>, can also be helpful, but is outdated and not entirely correct anymore.</p>
</div><hr/><div class="block">
<p>Maybe you already thought of a way to find out what is available in the <code>Math</code> object:</p>
<pre class="code"><span class="keyword">for</span> (<span class="keyword">var</span> <span class="variable">name</span> <span class="keyword">in</span> <span class="variable">Math</span>)
<span class="variable">print</span>(<span class="variable">name</span>);</pre>
<p>But alas, nothing appears. Similarly, when you do this:</p>
<pre class="code"><span class="keyword">for</span> (<span class="keyword">var</span> <span class="variable">name</span> <span class="keyword">in</span> [<span class="string">"Huey"</span>, <span class="string">"Dewey"</span>, <span class="string">"Loui"</span>])
<span class="variable">print</span>(<span class="variable">name</span>);</pre>
<p>You only see <code>0</code>, <code>1</code>, and <code>2</code>, not <code>length</code>, or <code>push</code>, or <code>join</code>, which are definitely also in there. Apparently, some properties of objects are hidden<a name="key40"></a>. There is a good reason for this: All objects have a few methods, for example <a name="key41"></a><code>toString</code>, which converts the object into some kind of relevant string, and you do not want to see those when you are, for example, looking for the cats that you stored in the object.</p>
<p>Why the properties of <code>Math</code> are hidden is unclear to me. Someone probably wanted it to be a mysterious kind of object.</p>
<p>All properties your programs add to objects are visible. There is no way to make them hidden, which is unfortunate because, as we will see in <a href="chapter8.html">chapter 8</a>, it would be nice to be able to add methods to objects without having them show up in our <code>for</code>/<code>in</code> loops.</p>
</div><hr/><div class="block">
<p><a name="key42"></a>Some properties are read-only, you can get their value but not change it. For example, the properties of a string value are all read-only.</p>
<p><a name="key43"></a>Other properties can be 'watched'. Changing them causes <em>things</em> to happen. For example, lowering the length of an array causes excess elements to be discarded:</p>
<pre class="code"><span class="keyword">var</span> <span class="variable">array</span> = [<span class="string">"Heaven"</span>, <span class="string">"Earth"</span>, <span class="string">"Man"</span>];
<span class="variable">array</span>.<span class="property">length</span> = <span class="atom">2</span>;
<span class="variable">show</span>(<span class="variable">array</span>);</pre>
<p>In some browsers, objects have a method <code>watch</code>, which can be used to add a watcher to your own properties. Internet Explorer does not support this though, so it is of little use when writing programs that must run on all the 'big' browsers.</p>
<pre class="code invalid"><span class="keyword">var</span> <span class="variable">winston</span> = {<span class="property">mind</span>: <span class="string">"compliant"</span>};
<span class="keyword">function</span> <span class="variable">watcher</span>(<span class="variabledef">propertyName</span>, <span class="variabledef">from</span>, <span class="variabledef">to</span>) {
<span class="keyword">if</span> (<span class="localvariable">to</span> == <span class="string">"compliant"</span>)
<span class="variable">print</span>(<span class="string">"Doubleplusgood."</span>);
<span class="keyword">else</span>
<span class="variable">print</span>(<span class="string">"Transmitting information to Thought Police..."</span>);
}
<span class="variable">winston</span>.<span class="property">watch</span>(<span class="string">"mind"</span>, <span class="variable">watcher</span>);
<span class="variable">winston</span>.<span class="property">mind</span> = <span class="string">"rebellious"</span>;</pre>
</div>
<ol class="footnotes"><li>
<a name="footnote1"></a>
There are a few subtle problems with this approach, which will be discussed and solved in <a href="chapter8.html">chapter 8</a>. For this chapter, it works well enough.
</li></ol>
<div class="navigation">
<a href="chapter3.html"><< Chapitre précédent</a> |
<a href="contents.html">Table des matières</a> |
<a href="index.html">Couverture</a> |
<a href="chapter5.html">Chapitre suivant >></a>
</div>
<div class="footer">
© <a href="mailto:[email protected]">Marijn Haverbeke</a>
(<a href="http://creativecommons.org/licenses/by/3.0/deed.fr">licence</a>),
écrit entre mars et juillet 2007, dernière modification le 11 juillet 2011.
</div>
</div>
<script type="text/javascript" src="js/ejs.js"> </script>
</body>
</html>