-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy path08_error.html
More file actions
494 lines (340 loc) · 66.8 KB
/
08_error.html
File metadata and controls
494 lines (340 loc) · 66.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
<!doctype html>
<head>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-158904079-1"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-158904079-1');
</script>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Bugs y Errores :: Eloquent JavaScript</title>
<link rel=stylesheet href="js/node_modules/codemirror/lib/codemirror.css">
<script src="js/acorn_codemirror.js"></script>
<link rel=stylesheet href="css/ejs.css">
<script src="js/sandbox.js"></script>
<script src="js/ejs.js"></script><script>var chapNum = 8;var sandboxLoadFiles = ["code/chapter/08_error.js"];</script></head>
<article>
<nav><a href="07_robot.html" title="previous chapter">◀</a> <a href="index.html" title="cover">◆</a> <a href="09_regexp.html" title="next chapter">▶</a></nav>
<h1><span class=chap_num>Chapter 8</span>Bugs y Errores</h1>
<blockquote>
<p><a class="p_ident" id="p_wzHHZJLy0I" href="#p_wzHHZJLy0I" tabindex="-1" role="presentation"></a>Arreglar errores es dos veces mas difícil que escribir el código en primer lugar. Por lo tanto, si escribes código de la manera más inteligente posible, eres, por definición, no lo suficientemente inteligente como para depurarlo.</p>
<footer>Brian Kernighan and P.J. Plauger, <cite>The Elements of Programming Style</cite></footer>
</blockquote><figure class="chapter framed"><img src="img/chapter_picture_8.jpg" alt="Picture of a collection of bugs"></figure>
<p><a class="p_ident" id="p_0BYiHV2+ll" href="#p_0BYiHV2+ll" tabindex="-1" role="presentation"></a>Los defectos en los programas de computadora usualmente se llaman <em>bugs</em> (o “insectos”). Este nombre hace que los programadores se sientan bien al imaginarlos como pequeñas cosas que solo sucede se arrastran hacia nuestro trabajo. En la realidad, por supuesto, nosotros mismos los ponemos allí.</p>
<p><a class="p_ident" id="p_SBrXLZf+6/" href="#p_SBrXLZf+6/" tabindex="-1" role="presentation"></a>Si un programa es un pensamiento cristalizado, puedes categorizar en grandes rasgos a los bugs en aquellos causados al confundir los pensamientos, y los causados por cometer errores al convertir un pensamiento en código. El primer tipo es generalmente más difícil de diagnosticar y corregir que el último.</p>
<h2><a class="h_ident" id="h_EWyEuXUxbG" href="#h_EWyEuXUxbG" tabindex="-1" role="presentation"></a>Lenguaje</h2>
<p><a class="p_ident" id="p_oPCpjnvMPj" href="#p_oPCpjnvMPj" tabindex="-1" role="presentation"></a>Muchos errores podrían ser señalados automáticamente por la computadora, si esta supiera lo suficiente sobre lo que estamos tratando de hacer. Pero aquí la soltura de JavaScript es un obstáculo. Su concepto de vinculaciones y propiedades es lo suficientemente vago que rara vez atrapará errores ortograficos antes de ejecutar el programa. E incluso entonces, te permite hacer algunas cosas claramente sin sentido, como calcular <code>true * "mono"</code>.</p>
<p><a class="p_ident" id="p_9FkgM0j2UZ" href="#p_9FkgM0j2UZ" tabindex="-1" role="presentation"></a>Hay algunas cosas de las que JavaScript se queja. Escribir un programa que no siga la gramática del lenguaje inmediatamente hara que la computadora se queje. Otras cosas, como llamar a algo que no sea una función o buscar una propiedad en un valor indefinido, causará un error que sera reportado cuando el programa intente realizar la acción.</p>
<p><a class="p_ident" id="p_0feIgAvomM" href="#p_0feIgAvomM" tabindex="-1" role="presentation"></a>Pero a menudo, tu cálculo sin sentido simplemente producirá <code>NaN</code> (no es un número) o un valor indefinido. Y el programa continuara felizmente, convencido de que está haciendo algo significativo. El error solo se manifestara más tarde, después de que el valor falso haya viajado a traves de varias funciones. Puede no desencadenar un error en absoluto, pero en silencio causara que la salida del programa sea incorrecta. Encontrar la fuente de tales problemas puede ser algo difícil.</p>
<p><a class="p_ident" id="p_kqIROFgFjS" href="#p_kqIROFgFjS" tabindex="-1" role="presentation"></a>El proceso de encontrar errores—bugs—en los programas se llama <em>depuración</em>.</p>
<h2><a class="h_ident" id="h_VqnTmwySiJ" href="#h_VqnTmwySiJ" tabindex="-1" role="presentation"></a>Modo estricto</h2>
<p><a class="p_ident" id="p_F8b236+RBw" href="#p_F8b236+RBw" tabindex="-1" role="presentation"></a>JavaScript se puede hacer un <em>poco</em> más estricto al habilitar el <em>modo estricto</em>. Esto se hace al poner el string <code>"use strict"</code> (“usar estricto”) en la parte superior de un archivo o cuerpo de función. Aquí hay un ejemplo:</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_4C9+rzoXCi" href="#c_4C9+rzoXCi" tabindex="-1" role="presentation"></a><span class="cm-keyword">function</span> <span class="cm-def">puedesDetectarElProblema</span>() {
<span class="cm-string">"use strict"</span>;
<span class="cm-keyword">for</span> (<span class="cm-variable">contador</span> <span class="cm-operator">=</span> <span class="cm-number">0</span>; <span class="cm-variable">contador</span> <span class="cm-operator"><</span> <span class="cm-number">10</span>; <span class="cm-variable">contador</span><span class="cm-operator">++</span>) {
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string">"Feliz feliz"</span>);
}
}
<span class="cm-variable">puedesDetectarElProblema</span>();
<span class="cm-comment">// → ReferenceError: contador is not defined</span></pre>
<p><a class="p_ident" id="p_X7eToA87a9" href="#p_X7eToA87a9" tabindex="-1" role="presentation"></a>Normalmente, cuando te olvidas de poner <code>let</code> delante de tu vinculación, como con <code>contador</code> en el ejemplo, JavaScript silenciosamente crea una vinculación global y utiliza eso. En el modo estricto, se reportara un error en su lugar. Esto es muy útil. Sin embargo, debe tenerse en cuenta que esto no funciona cuando la vinculación en cuestión ya existe como una vinculación global. En ese caso, el ciclo aún sobrescribirá silenciosamente el valor de la vinculación.</p>
<p><a class="p_ident" id="p_PepY/96cbD" href="#p_PepY/96cbD" tabindex="-1" role="presentation"></a>Otro cambio en el modo estricto es que la vinculación <code>this</code> contiene el valor <code>undefined</code> en funciones que no se llamen como métodos. Cuando se hace una llamada fuera del modo estricto, <code>this</code> se refiere al objeto del alcance global, que es un objeto cuyas propiedades son vinculaciones globales. Entonces, si llamas accidentalmente a un método o constructor incorrectamente en el modo estricto, JavaScript producirá un error tan pronto trate de leer algo de <code>this</code>, en lugar de escribirlo felizmente al alcance global.</p>
<p><a class="p_ident" id="p_lSOsyuYctI" href="#p_lSOsyuYctI" tabindex="-1" role="presentation"></a>Por ejemplo, considera el siguiente código, que llama una función constructora sin la palabra clave <code>new</code> de modo que su <code>this</code> <em>no</em> hara referencia a un objeto recién construido:</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_dVGdKtXXIU" href="#c_dVGdKtXXIU" tabindex="-1" role="presentation"></a><span class="cm-keyword">function</span> <span class="cm-def">Persona</span>(<span class="cm-def">nombre</span>) { <span class="cm-keyword">this</span>.<span class="cm-property">nombre</span> <span class="cm-operator">=</span> <span class="cm-variable-2">nombre</span>; }
<span class="cm-keyword">let</span> <span class="cm-def">ferdinand</span> <span class="cm-operator">=</span> <span class="cm-variable">Persona</span>(<span class="cm-string">"Ferdinand"</span>); <span class="cm-comment">// oops</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">nombre</span>);
<span class="cm-comment">// → Ferdinand</span></pre>
<p><a class="p_ident" id="p_ZaxvGLctob" href="#p_ZaxvGLctob" tabindex="-1" role="presentation"></a>Así que la llamada fraudulenta a <code>Persona</code> tuvo éxito pero retorno un valor indefinido y creó la vinculación <code>nombre</code> global. En el modo estricto, el resultado es diferente.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_z7P2OYe7SA" href="#c_z7P2OYe7SA" tabindex="-1" role="presentation"></a><span class="cm-string">"use strict"</span>;
<span class="cm-keyword">function</span> <span class="cm-def">Persona</span>(<span class="cm-def">nombre</span>) { <span class="cm-keyword">this</span>.<span class="cm-property">nombre</span> <span class="cm-operator">=</span> <span class="cm-variable-2">nombre</span>; }
<span class="cm-keyword">let</span> <span class="cm-def">ferdinand</span> <span class="cm-operator">=</span> <span class="cm-variable">Persona</span>(<span class="cm-string">"Ferdinand"</span>); <span class="cm-comment">// olvide new</span>
<span class="cm-comment">// → TypeError: Cannot set property 'nombre' of undefined</span></pre>
<p><a class="p_ident" id="p_StNVJKRfoZ" href="#p_StNVJKRfoZ" tabindex="-1" role="presentation"></a>Se nos dice inmediatamente que algo está mal. Esto es útil.</p>
<p><a class="p_ident" id="p_QrETth0MQK" href="#p_QrETth0MQK" tabindex="-1" role="presentation"></a>Afortunadamente, los constructores creados con la notación <code>class</code> siempre se quejan si se llaman sin <code>new</code>, lo que hace que esto sea menos un problema incluso en el modo no-estricto.</p>
<p><a class="p_ident" id="p_R6rUgXg6qF" href="#p_R6rUgXg6qF" tabindex="-1" role="presentation"></a>El modo estricto hace algunas cosas más. No permite darle a una función múltiples parámetros con el mismo nombre y elimina ciertas características problemáticas del lenguaje por completo (como la declaración <code>with</code> (“con”), la cual esta tan mal, que no se discute en este libro).</p>
<p><a class="p_ident" id="p_7mwHYbTb6V" href="#p_7mwHYbTb6V" tabindex="-1" role="presentation"></a>En resumen, poner <code>"use strict"</code> en la parte superior de tu programa rara vez duele y puede ayudarte a detectar un problema.</p>
<h2><a class="h_ident" id="h_Oj3wsjsPjg" href="#h_Oj3wsjsPjg" tabindex="-1" role="presentation"></a>Tipos</h2>
<p><a class="p_ident" id="p_rfQ46tR6Rg" href="#p_rfQ46tR6Rg" tabindex="-1" role="presentation"></a>Algunos lenguajes quieren saber los tipos de todas tus vinculaciones y expresiones incluso antes de ejecutar un programa. Estos te dirán de una vez cuando uses un tipo de una manera inconsistente. JavaScript solo considera a los tipos cuando ejecuta el programa, e incluso a menudo intentara convertir implícitamente los valores al tipo que espera, por lo que no es de mucha ayuda</p>
<p><a class="p_ident" id="p_HZfblGVr2x" href="#p_HZfblGVr2x" tabindex="-1" role="presentation"></a>Aún así, los tipos proporcionan un marco útil para hablar acerca de los programas. Muchos errores provienen de estar confundido acerca del tipo de valor que entra o sale de una función. Si tienes esa información anotada, es menos probable que te confundas.</p>
<p><a class="p_ident" id="p_WCzAIDy04d" href="#p_WCzAIDy04d" tabindex="-1" role="presentation"></a>Podrías agregar un comentario como arriba de la función <code>robotOrientadoAMetas</code> del último capítulo, para describir su tipo.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_Mgpcqd1AXy" href="#c_Mgpcqd1AXy" tabindex="-1" role="presentation"></a><span class="cm-comment">// (EstadoMundo, Array) → {direccion: string, memoria: Array}</span>
<span class="cm-keyword">function</span> <span class="cm-def">robotOrientadoAMetas</span>(<span class="cm-def">estado</span>, <span class="cm-def">memoria</span>) {
<span class="cm-comment">// ...</span>
}</pre>
<p><a class="p_ident" id="p_9AXBes++FH" href="#p_9AXBes++FH" tabindex="-1" role="presentation"></a>Hay varias convenciones diferentes para anotar programas de JavaScript con tipos.</p>
<p><a class="p_ident" id="p_cotAyLCzAt" href="#p_cotAyLCzAt" tabindex="-1" role="presentation"></a>Una cosa acerca de los tipos es que necesitan introducir su propia complejidad para poder describir suficiente código como para poder ser útil. Cual crees que sería el tipo de la función <code>eleccionAleatoria</code> que retorna un elemento aleatorio de un array? Deberías introducir un <em>tipo
variable</em>, <em>T</em>, que puede representar cualquier tipo, para que puedas darle a <code>eleccionAleatoria</code> un tipo como <code>([T]) → T</code> (función de un array de <em>T</em>s a a <em>T</em>).</p>
<p id="typing"><a class="p_ident" id="p_1YDN4/vR0z" href="#p_1YDN4/vR0z" tabindex="-1" role="presentation"></a>Cuando se conocen los tipos de un programa, es posible que la computadora haga un <em>chequeo</em> por ti, señalando los errores antes de que el programa sea ejecutado. Hay varios dialectos de JavaScript que agregan tipos al lenguaje y y los verifica. El más popular se llama <a href="https://www.typescriptlang.org/">TypeScript</a>. Si estás interesado en agregarle más rigor a tus programas, te recomiendo que lo pruebes.</p>
<p><a class="p_ident" id="p_rK4C2YF1Oq" href="#p_rK4C2YF1Oq" tabindex="-1" role="presentation"></a>En este libro, continuaremos usando código en JavaScript crudo, peligroso y sin tipos.</p>
<h2><a class="h_ident" id="h_lehBV7kCRy" href="#h_lehBV7kCRy" tabindex="-1" role="presentation"></a>Probando</h2>
<p><a class="p_ident" id="p_Mh0wNpH7/y" href="#p_Mh0wNpH7/y" tabindex="-1" role="presentation"></a>Si el lenguaje no va a hacer mucho para ayudarnos a encontrar errores, tendremos que encontrarlos de la manera difícil: ejecutando el programa y viendo si hace lo correcto.</p>
<p><a class="p_ident" id="p_RHU6Tg5V3d" href="#p_RHU6Tg5V3d" tabindex="-1" role="presentation"></a>Hacer esto a mano, una y otra vez, es una muy mala idea. No solo es es molesto, también tiende a ser ineficaz, ya que lleva demasiado tiempo probar exhaustivamente todo cada vez que haces un cambio en tu programa.</p>
<p><a class="p_ident" id="p_9SYP3YuKrm" href="#p_9SYP3YuKrm" tabindex="-1" role="presentation"></a>Las computadoras son buenas para las tareas repetitivas, y las pruebas son las tareas repetitivas ideales. Las pruebas automatizadas es el proceso de escribir un programa que prueba otro programa. Escribir pruebas consiste en algo más de trabajo que probar manualmente, pero una vez que lo haz hecho, ganas un tipo de superpoder: solo te tomara unos segundos verificar que tu programa todavía se comporta correctamente en todas las situaciones para las que escribiste tu prueba. Cuando rompas algo, lo notarás inmediatamente, en lugar aleatoriomente encontrarte con el problema en algún momento posterior.</p>
<p><a class="p_ident" id="p_x+9D25f/yd" href="#p_x+9D25f/yd" tabindex="-1" role="presentation"></a>Las pruebas usualmente toman la forma de pequeños programas etiquetados que verifican algún aspecto de tu código. Por ejemplo, un conjunto de pruebas para el método (estándar, probablemente ya probado por otra persona) <code>toUpperCase</code> podría verse así:</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_Vhmk0Sdyxj" href="#c_Vhmk0Sdyxj" tabindex="-1" role="presentation"></a><span class="cm-keyword">function</span> <span class="cm-def">probar</span>(<span class="cm-def">etiqueta</span>, <span class="cm-def">cuerpo</span>) {
<span class="cm-keyword">if</span> (<span class="cm-operator">!</span><span class="cm-variable-2">cuerpo</span>()) <span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string-2">`Fallo: ${</span><span class="cm-variable-2">etiqueta</span><span class="cm-string-2">}</span><span class="cm-string-2">`</span>);
}
<span class="cm-variable">probar</span>(<span class="cm-string">"convertir texto Latino a mayúscula"</span>, () <span class="cm-operator">=></span> {
<span class="cm-keyword">return</span> <span class="cm-string">"hola"</span>.<span class="cm-property">toUpperCase</span>() <span class="cm-operator">==</span> <span class="cm-string">"HOLA"</span>;
});
<span class="cm-variable">probar</span>(<span class="cm-string">"convertir texto Griego a mayúsculas"</span>, () <span class="cm-operator">=></span> {
<span class="cm-keyword">return</span> <span class="cm-string">"Χαίρετε"</span>.<span class="cm-property">toUpperCase</span>() <span class="cm-operator">==</span> <span class="cm-string">"ΧΑΊΡΕΤΕ"</span>;
});
<span class="cm-variable">probar</span>(<span class="cm-string">"no convierte caracteres sin mayúsculas"</span>, () <span class="cm-operator">=></span> {
<span class="cm-keyword">return</span> <span class="cm-string">"مرحبا"</span>.<span class="cm-property">toUpperCase</span>() <span class="cm-operator">==</span> <span class="cm-string">"مرحبا"</span>;
});</pre>
<p><a class="p_ident" id="p_WBLtdfJ+Ms" href="#p_WBLtdfJ+Ms" tabindex="-1" role="presentation"></a>Escribir pruebas de esta manera tiende a producir código bastante repetitivo e incómodo. Afortunadamente, existen piezas de software que te ayudan a construir y ejecutar colecciones de pruebas (<em>suites de prueba</em>) al proporcionar un lenguaje (en forma de funciones y métodos) adecuado para expresar pruebas y obtener información informativa cuando una prueba falla. Estos generalmente se llaman <em>corredores de pruebas</em>.</p>
<p><a class="p_ident" id="p_wpLDxHSDE4" href="#p_wpLDxHSDE4" tabindex="-1" role="presentation"></a>Algunos programas son más fáciles de probar que otros programas. Por lo general, con cuantos más objetos externos interactúe el código, más difícil es establecer el contexto en el cual probarlo. El estilo de programación mostrado en el <a href="07_robot.html">capítulo anterior</a>, que usa valores persistentes auto-contenidos en lugar de cambiar objetos, tiende a ser fácil de probar.</p>
<h2><a class="h_ident" id="h_nCqjdGY4Fq" href="#h_nCqjdGY4Fq" tabindex="-1" role="presentation"></a>Depuración</h2>
<p><a class="p_ident" id="p_Wg8h9d/mPj" href="#p_Wg8h9d/mPj" tabindex="-1" role="presentation"></a>Una vez que notes que hay algo mal con tu programa porque se comporta mal o produce errores, el siguiente paso es descubir <em>cual</em> es el problema.</p>
<p><a class="p_ident" id="p_AuzxnqBdGR" href="#p_AuzxnqBdGR" tabindex="-1" role="presentation"></a>A veces es obvio. El mensaje de error apuntará a una línea específica de tu programa, y si miras la descripción del error y esa línea de código, a menudo puedes ver el problema.</p>
<p><a class="p_ident" id="p_EcaooBcW+O" href="#p_EcaooBcW+O" tabindex="-1" role="presentation"></a>Pero no siempre. A veces, la línea que provocó el problema es simplemente el primer lugar en donde un valor extraño producido en otro lugar es usado de una manera inválida. Si has estado resolviendo los ejercicios en capítulos anteriores, probablemente ya habrás experimentado tales situaciones.</p>
<p><a class="p_ident" id="p_YhdyY6oKg7" href="#p_YhdyY6oKg7" tabindex="-1" role="presentation"></a>El siguiente programa de ejemplo intenta convertir un número entero a un string en una base dada (decimal, binario, etc.) al repetidamente seleccionar el último dígito y luego dividiendo el número para deshacerse de este dígito. Pero la extraña salida que produce sugiere que tiene un error.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_BtiUK/8l4O" href="#c_BtiUK/8l4O" tabindex="-1" role="presentation"></a><span class="cm-keyword">function</span> <span class="cm-def">numeroAString</span>(<span class="cm-def">n</span>, <span class="cm-def">base</span> <span class="cm-operator">=</span> <span class="cm-number">10</span>) {
<span class="cm-keyword">let</span> <span class="cm-def">resultado</span> <span class="cm-operator">=</span> <span class="cm-string">""</span>, <span class="cm-def">signo</span> <span class="cm-operator">=</span> <span class="cm-string">""</span>;
<span class="cm-keyword">if</span> (<span class="cm-variable-2">n</span> <span class="cm-operator"><</span> <span class="cm-number">0</span>) {
<span class="cm-variable-2">signo</span> <span class="cm-operator">=</span> <span class="cm-string">"-"</span>;
<span class="cm-variable-2">n</span> <span class="cm-operator">=</span> <span class="cm-operator">-</span><span class="cm-variable-2">n</span>;
}
<span class="cm-keyword">do</span> {
<span class="cm-variable-2">resultado</span> <span class="cm-operator">=</span> <span class="cm-variable">String</span>(<span class="cm-variable-2">n</span> <span class="cm-operator">%</span> <span class="cm-variable-2">base</span>) <span class="cm-operator">+</span> <span class="cm-variable-2">resultado</span>;
<span class="cm-variable-2">n</span> <span class="cm-operator">/=</span> <span class="cm-variable-2">base</span>;
} <span class="cm-keyword">while</span> (<span class="cm-variable-2">n</span> <span class="cm-operator">></span> <span class="cm-number">0</span>);
<span class="cm-keyword">return</span> <span class="cm-variable-2">signo</span> <span class="cm-operator">+</span> <span class="cm-variable-2">resultado</span>;
}
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">numeroAString</span>(<span class="cm-number">13</span>, <span class="cm-number">10</span>));
<span class="cm-comment">// → 1.5e-3231.3e-3221.3e-3211.3e-3201.3e-3191.3e-3181.3…</span></pre>
<p><a class="p_ident" id="p_+kV6GQP18a" href="#p_+kV6GQP18a" tabindex="-1" role="presentation"></a>Incluso si ya ves el problema, finge por un momento que no lo has hecho. Sabemos que nuestro programa no funciona bien, y queremos encontrar por qué.</p>
<p><a class="p_ident" id="p_OvtUVtM+A4" href="#p_OvtUVtM+A4" tabindex="-1" role="presentation"></a>Aquí es donde debes resistir el impulso de comenzar a hacer cambios aleatorios en el código para ver si eso lo mejora. En cambio, <em>piensa</em>. Analiza lo que está sucediendo y piensa en una teoría de por qué podría ser sucediendo. Luego, haz observaciones adicionales para probar esta teoría—o si aún no tienes una teoría, haz observaciones adicionales para ayudarte a que se te ocurra una.</p>
<p><a class="p_ident" id="p_GlgF4QPkQY" href="#p_GlgF4QPkQY" tabindex="-1" role="presentation"></a>Poner algunas llamadas estratégicas a <code>console.log</code> en el programa es una buena forma de obtener información adicional sobre lo que está haciendo el programa. En en este caso, queremos que <code>n</code> tome los valores <code>13</code>, <code>1</code> y luego <code>0</code>. Vamos a escribir su valor al comienzo del ciclo.</p>
<pre class="snippet cm-s-default" data-language="null" ><a class="c_ident" id="c_nB/OeL8UNa" href="#c_nB/OeL8UNa" tabindex="-1" role="presentation"></a>13
1.3
0.13
0.013
…
1.5e-323</pre>
<p><a class="p_ident" id="p_cBSM5bQAyt" href="#p_cBSM5bQAyt" tabindex="-1" role="presentation"></a><em>Exacto</em>. Dividir 13 entre 10 no produce un número entero. En lugar de <code>n /= base</code>, lo que realmente queremos es <code>n = Math.<wbr>floor(n /<wbr> base)</code> para que el número sea correctamente “desplazado” hacia la derecha.</p>
<p><a class="p_ident" id="p_luHIBV3mvs" href="#p_luHIBV3mvs" tabindex="-1" role="presentation"></a>Una alternativa al uso de <code>console.log</code> para echarle un vistazo al comportamiento del programa es usar las capacidades del <em>depurador</em> de tu navegador. Los navegadores vienen con la capacidad de establecer un <em>punto de interrupción</em> en una línea específico de tu código. Cuando la ejecución del programa alcanza una línea con un punto de interrupción, este entra en pausa, y puedes inspeccionar los valores de las vinculaciones en ese punto. No entraré en detalles, ya que los depuradores difieren de navegador en navegador, pero mira las herramientas de
desarrollador en tu navegador o busca en la Web para obtener más información.</p>
<p><a class="p_ident" id="p_PyWqQWWne4" href="#p_PyWqQWWne4" tabindex="-1" role="presentation"></a>Otra forma de establecer un punto de interrupción es incluir una declaración <code>debugger</code> (que consiste simplemente de esa palabra clave) en tu programa. Si las herramientas de desarrollador en tu navegador están activas, el programa pausará cada vez que llegue a tal declaración.</p>
<h2><a class="h_ident" id="h_a0SXG/vlkA" href="#h_a0SXG/vlkA" tabindex="-1" role="presentation"></a>Propagación de errores</h2>
<p><a class="p_ident" id="p_KHxp/kum/L" href="#p_KHxp/kum/L" tabindex="-1" role="presentation"></a>Desafortunadamente, no todos los problemas pueden ser prevenidos por el programador. Si tu programa se comunica con el mundo exterior de alguna manera, es posible obtener una entrada malformada, sobrecargarse con el trabajo, o la red falle en la ejecución.</p>
<p><a class="p_ident" id="p_Hhh+W3s48w" href="#p_Hhh+W3s48w" tabindex="-1" role="presentation"></a>Si solo estás programando para ti mismo, puedes permitirte ignorar tales problemas hasta que estos ocurran. Pero si construyes algo que va a ser utilizado por cualquier otra persona, generalmente quieres que el programa haga algo mejor que solo estrellarse. A veces lo correcto es tomar la mala entrada en zancada y continuar corriendo. En otros casos, es mejor informar al usuario lo que salió mal y luego darse por vencido. Pero en cualquier situación, el programa tiene que hacer algo activamente en respuesta al problema.</p>
<p><a class="p_ident" id="p_FndZt9RZOd" href="#p_FndZt9RZOd" tabindex="-1" role="presentation"></a>Supongamos que tienes una función <code>pedirEntero</code> que le pide al usuario un número entero y lo retorna. Qué deberías retornar si la entrada por parte del usuario es “naranja”?</p>
<p><a class="p_ident" id="p_SQNPd296Uq" href="#p_SQNPd296Uq" tabindex="-1" role="presentation"></a>Una opción es hacer que retorne un valor especial. Opciones comunes para tales valores son <code>null</code>, <code>undefined</code>, o -1.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_lHYbkAe9Em" href="#c_lHYbkAe9Em" tabindex="-1" role="presentation"></a><span class="cm-keyword">function</span> <span class="cm-def">pedirEntero</span>(<span class="cm-def">pregunta</span>) {
<span class="cm-keyword">let</span> <span class="cm-def">resultado</span> <span class="cm-operator">=</span> <span class="cm-variable">Number</span>(<span class="cm-variable">prompt</span>(<span class="cm-variable-2">pregunta</span>));
<span class="cm-keyword">if</span> (<span class="cm-variable">Number</span>.<span class="cm-property">isNaN</span>(<span class="cm-variable-2">resultado</span>)) <span class="cm-keyword">return</span> <span class="cm-atom">null</span>;
<span class="cm-keyword">else</span> <span class="cm-keyword">return</span> <span class="cm-variable-2">resultado</span>;
}
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">pedirEntero</span>(<span class="cm-string">"Cuantos arboles ves?"</span>));</pre>
<p><a class="p_ident" id="p_QUKW3zFaM4" href="#p_QUKW3zFaM4" tabindex="-1" role="presentation"></a>Ahora cualquier código que llame a <code>pedirEntero</code> debe verificar si un número real fue leído y, si eso falla, de alguna manera debe recuperarse—tal vez preguntando nuevamente o usando un valor predeterminado. O podría de nuevo retornar un valor especial a <em>su</em> llamada para indicar que no pudo hacer lo que se pidió.</p>
<p><a class="p_ident" id="p_+0GSc1SBrK" href="#p_+0GSc1SBrK" tabindex="-1" role="presentation"></a>En muchas situaciones, principalmente cuando los errores son comunes y la persona que llama debe tenerlos explícitamente en cuenta, retornar un valor especial es una buena forma de indicar un error. Sin embargo, esto tiene sus desventajas. Primero, qué pasa si la función puede retornar cada tipo de valor posible? En tal función, tendrás que hacer algo como envolver el resultado en un objeto para poder distinguir el éxito del fracaso.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_njXvLvPiBC" href="#c_njXvLvPiBC" tabindex="-1" role="presentation"></a><span class="cm-keyword">function</span> <span class="cm-def">ultimoElemento</span>(<span class="cm-def">array</span>) {
<span class="cm-keyword">if</span> (<span class="cm-variable-2">array</span>.<span class="cm-property">length</span> <span class="cm-operator">==</span> <span class="cm-number">0</span>) {
<span class="cm-keyword">return</span> {<span class="cm-property">fallo</span>: <span class="cm-atom">true</span>};
} <span class="cm-keyword">else</span> {
<span class="cm-keyword">return</span> {<span class="cm-property">elemento</span>: <span class="cm-variable-2">array</span>[<span class="cm-variable-2">array</span>.<span class="cm-property">length</span> <span class="cm-operator">-</span> <span class="cm-number">1</span>]};
}
}</pre>
<p><a class="p_ident" id="p_J2hJBilVK0" href="#p_J2hJBilVK0" tabindex="-1" role="presentation"></a>El segundo problema con retornar valores especiales es que puede conducir a código muy incómodo. Si un fragmento de código llama a <code>pedirEntero</code> 10 veces, tiene que comprobar 10 veces si <code>null</code> fue retornado. Y si su respuesta a encontrar <code>null</code> es simplemente retornar <code>null</code> en sí mismo, los llamadores de esa función a su vez tendrán que verificarlo, y así sucesivamente.</p>
<h2><a class="h_ident" id="h_6MskLb8+Lo" href="#h_6MskLb8+Lo" tabindex="-1" role="presentation"></a>Excepciones</h2>
<p><a class="p_ident" id="p_qyY5Z373xH" href="#p_qyY5Z373xH" tabindex="-1" role="presentation"></a>Cuando una función no puede continuar normalmente, lo que nos <em>gustaría</em> hacer es simplemente detener lo que estamos haciendo e inmediatamente saltar a un lugar que sepa cómo manejar el problema. Esto es lo que el <em>manejo de excepciones</em> hace.</p>
<p><a class="p_ident" id="p_SLVYESOyUy" href="#p_SLVYESOyUy" tabindex="-1" role="presentation"></a>Las excepciones son un mecanismo que hace posible que el código que se encuentre con un problema <em>produzca</em> (o <em>lance</em>) una excepción. Una excepción puede ser cualquier valor. Producir una se asemeja a un retorno súper-cargado de una función: salta no solo de la función actual sino también fuera de sus llamadores, todo el camino hasta la primera llamada que comenzó la ejecución actual. Esto se llama <em>desenrollando la
pila</em>. Puede que recuerdes que la pila de llamadas de función fue mencionada en el <a href="funciones#pila">Capítulo 3</a>. Una excepción se aleja de esta pila, descartando todos los contextos de llamadas que encuentra.</p>
<p><a class="p_ident" id="p_YezykK0rip" href="#p_YezykK0rip" tabindex="-1" role="presentation"></a>Si las excepciones siempre se acercaran al final de la pila, estas no serían de mucha utilidad. Simplemente proporcionarían una nueva forma de explotar tu programa. Su poder reside en el hecho de que puedes establecer “obstáculos” a lo largo de la pila para <em>capturar</em> la excepción, cuando esta<br>esta se dirige hacia abajo. Una vez que hayas capturado una excepción, puedes hacer algo con ella para abordar el problema y luego continuar ejecutando el programa.</p>
<p><a class="p_ident" id="p_eAipZ/VVC7" href="#p_eAipZ/VVC7" tabindex="-1" role="presentation"></a>Aquí hay un ejemplo:</p>
<pre id="look" class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_o//VAb1NQ8" href="#c_o//VAb1NQ8" tabindex="-1" role="presentation"></a><span class="cm-keyword">function</span> <span class="cm-def">pedirDireccion</span>(<span class="cm-def">pregunta</span>) {
<span class="cm-keyword">let</span> <span class="cm-def">resultado</span> <span class="cm-operator">=</span> <span class="cm-variable">prompt</span>(<span class="cm-variable-2">pregunta</span>);
<span class="cm-keyword">if</span> (<span class="cm-variable-2">resultado</span>.<span class="cm-property">toLowerCase</span>() <span class="cm-operator">==</span> <span class="cm-string">"izquierda"</span>) <span class="cm-keyword">return</span> <span class="cm-string">"I"</span>;
<span class="cm-keyword">if</span> (<span class="cm-variable-2">resultado</span>.<span class="cm-property">toLowerCase</span>() <span class="cm-operator">==</span> <span class="cm-string">"derecha"</span>) <span class="cm-keyword">return</span> <span class="cm-string">"D"</span>;
<span class="cm-keyword">throw</span> <span class="cm-keyword">new</span> <span class="cm-variable">Error</span>(<span class="cm-string">"Dirección invalida: "</span> <span class="cm-operator">+</span> <span class="cm-variable-2">resultado</span>);
}
<span class="cm-keyword">function</span> <span class="cm-def">mirar</span>() {
<span class="cm-keyword">if</span> (<span class="cm-variable">pedirDireccion</span>(<span class="cm-string">"Hacia que dirección quieres ir?"</span>) <span class="cm-operator">==</span> <span class="cm-string">"I"</span>) {
<span class="cm-keyword">return</span> <span class="cm-string">"una casa"</span>;
} <span class="cm-keyword">else</span> {
<span class="cm-keyword">return</span> <span class="cm-string">"dos osos furiosos"</span>;
}
}
<span class="cm-keyword">try</span> {
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string">"Tu ves"</span>, <span class="cm-variable">mirar</span>());
} <span class="cm-keyword">catch</span> (<span class="cm-def">error</span>) {
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string">"Algo incorrecto sucedio: "</span> <span class="cm-operator">+</span> <span class="cm-variable-2">error</span>);
}</pre>
<p><a class="p_ident" id="p_m+sgwELbB7" href="#p_m+sgwELbB7" tabindex="-1" role="presentation"></a>La palabra clave <code>throw</code> (“producir”) se usa para generar una excepción. La captura de una se hace al envolver un fragmento de código en un bloque <code>try</code> (“intentar”), seguido de la palabra clave <code>catch</code> (“atrapar”). Cuando el código en el bloque <code>try</code> cause una excepción para ser producida, se evalúa el bloque <code>catch</code>, con el nombre en paréntesis vinculado al valor de la excepción. Después de que el bloque <code>catch</code> finaliza, o si el bloque <code>try</code> finaliza sin problemas, el programa procede debajo de toda la declaración <code>try/catch</code>.</p>
<p><a class="p_ident" id="p_F4BqMusmt9" href="#p_F4BqMusmt9" tabindex="-1" role="presentation"></a>En este caso, usamos el constructor <code>Error</code> para crear nuestro valor de excepción. Este es un constructor (estándar) de JavaScript que crea un objeto con una propiedad <code>message</code> (“mensaje”). En la mayoría de los entornos de JavaScript, las instancias de este constructor también recopilan información sobre la pila de llamadas que existía cuando se creó la excepción, algo llamado <em>seguimiento de la pila</em>. Esta información se almacena en la propiedad <code>stack</code> (“pila”) y puede ser útil al intentar depurar un problema: esta nos dice la función donde ocurrió el problema y qué funciones realizaron la llamada fallida.</p>
<p><a class="p_ident" id="p_qBRqbj4oGD" href="#p_qBRqbj4oGD" tabindex="-1" role="presentation"></a>Ten en cuenta que la función <code>mirar</code> ignora por completo la posibilidad de que <code>pedirDireccion</code> podría salir mal. Esta es la gran ventaja de las excepciones: el código de manejo de errores es necesario solamente en el punto donde el error ocurre y en el punto donde se maneja. Las funciones en el medio puede olvidarse de todo.</p>
<p><a class="p_ident" id="p_+6sQLyxE9M" href="#p_+6sQLyxE9M" tabindex="-1" role="presentation"></a>Bueno, casi...</p>
<h2><a class="h_ident" id="h_hqzvMe3/h2" href="#h_hqzvMe3/h2" tabindex="-1" role="presentation"></a>Limpiando después de excepciones</h2>
<p><a class="p_ident" id="p_nH0PHRqG6M" href="#p_nH0PHRqG6M" tabindex="-1" role="presentation"></a>El efecto de una excepción es otro tipo de flujo de control. Cada acción que podría causar una excepción, que es prácticamente cualquier llamada de función y acceso a propiedades, puede causar al control dejar tu codigo repentinamente.</p>
<p><a class="p_ident" id="p_3scGjcge25" href="#p_3scGjcge25" tabindex="-1" role="presentation"></a>Eso significa que cuando el código tiene varios efectos secundarios, incluso si parece que el flujo de control “regular” siempre sucederá, una excepción puede evitar que algunos de ellos sucedan.</p>
<p><a class="p_ident" id="p_livyuWdpOQ" href="#p_livyuWdpOQ" tabindex="-1" role="presentation"></a>Aquí hay un código bancario realmente malo.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_B4fHcUgFs9" href="#c_B4fHcUgFs9" tabindex="-1" role="presentation"></a><span class="cm-keyword">const</span> <span class="cm-def">cuentas</span> <span class="cm-operator">=</span> {
<span class="cm-property">a</span>: <span class="cm-number">100</span>,
<span class="cm-property">b</span>: <span class="cm-number">0</span>,
<span class="cm-property">c</span>: <span class="cm-number">20</span>
};
<span class="cm-keyword">function</span> <span class="cm-def">obtenerCuenta</span>() {
<span class="cm-keyword">let</span> <span class="cm-def">nombreCuenta</span> <span class="cm-operator">=</span> <span class="cm-variable">prompt</span>(<span class="cm-string">"Ingrese el nombre de la cuenta"</span>);
<span class="cm-keyword">if</span> (<span class="cm-operator">!</span><span class="cm-variable">cuentas</span>.<span class="cm-property">hasOwnProperty</span>(<span class="cm-variable-2">nombreCuenta</span>)) {
<span class="cm-keyword">throw</span> <span class="cm-keyword">new</span> <span class="cm-variable">Error</span>(<span class="cm-string-2">`La cuenta "${</span><span class="cm-variable-2">nombreCuenta</span><span class="cm-string-2">}</span><span class="cm-string-2">" no existe`</span>);
}
<span class="cm-keyword">return</span> <span class="cm-variable-2">nombreCuenta</span>;
}
<span class="cm-keyword">function</span> <span class="cm-def">transferir</span>(<span class="cm-def">desde</span>, <span class="cm-def">cantidad</span>) {
<span class="cm-keyword">if</span> (<span class="cm-variable">cuentas</span>[<span class="cm-variable-2">desde</span>] <span class="cm-operator"><</span> <span class="cm-variable-2">cantidad</span>) <span class="cm-keyword">return</span>;
<span class="cm-variable">cuentas</span>[<span class="cm-variable-2">desde</span>] <span class="cm-operator">-=</span> <span class="cm-variable-2">cantidad</span>;
<span class="cm-variable">cuentas</span>[<span class="cm-variable">obtenerCuenta</span>()] <span class="cm-operator">+=</span> <span class="cm-variable-2">cantidad</span>;
}</pre>
<p><a class="p_ident" id="p_Ue7mDsJTml" href="#p_Ue7mDsJTml" tabindex="-1" role="presentation"></a>La función <code>transferir</code> transfiere una suma de dinero desde una determinada cuenta a otra, pidiendo el nombre de la otra cuenta en el proceso. Si se le da un nombre de cuenta no válido, <code>obtenerCuenta</code> arroja una excepción.</p>
<p><a class="p_ident" id="p_mHaqpH82hL" href="#p_mHaqpH82hL" tabindex="-1" role="presentation"></a>Pero <code>transferir</code> <em>primero</em> remueve el dinero de la cuenta, y <em>luego</em> llama a <code>obtenerCuenta</code> antes de añadirlo a la otra cuenta. Si esto es interrumpido por una excepción en ese momento, solo hará que el dinero desaparezca.</p>
<p><a class="p_ident" id="p_vhZxRI0QWH" href="#p_vhZxRI0QWH" tabindex="-1" role="presentation"></a>Ese código podría haber sido escrito de una manera un poco más inteligente, por ejemplo al llamar <code>obtenerCuenta</code> antes de que se comience a mover el dinero. Pero a menudo problemas como este ocurren de maneras más sutiles. Incluso funciones que no parece que lanzarán una excepción podría hacerlo en circunstancias excepcionales o cuando contienen un error de programador.</p>
<p><a class="p_ident" id="p_05Rw4g4rL5" href="#p_05Rw4g4rL5" tabindex="-1" role="presentation"></a>Una forma de abordar esto es usar menos efectos secundarios. De nuevo, un estilo de programación que calcula nuevos valores en lugar de cambiar los datos existentes ayuda. Si un fragmento de código deja de ejecutarse en el medio de crear un nuevo valor, nadie ve el valor a medio terminar, y no hay ningún problema.</p>
<p><a class="p_ident" id="p_7MIgFxyQWl" href="#p_7MIgFxyQWl" tabindex="-1" role="presentation"></a>Pero eso no siempre es práctico. Entonces, hay otra característica que las declaraciones <code>try</code> tienen. Estas pueden ser seguidas por un bloque <code>finally</code> (“finalmente”) en lugar de o además de un bloque <code>catch</code>. Un bloque <code>finally</code> dice “no importa lo que pase, ejecuta este código después de intentar ejecutar el código en el bloque <code>try</code>.”</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_HpyAFY5eeO" href="#c_HpyAFY5eeO" tabindex="-1" role="presentation"></a><span class="cm-keyword">function</span> <span class="cm-def">transferir</span>(<span class="cm-def">desde</span>, <span class="cm-def">cantidad</span>) {
<span class="cm-keyword">if</span> (<span class="cm-variable">cuentas</span>[<span class="cm-variable-2">desde</span>] <span class="cm-operator"><</span> <span class="cm-variable-2">cantidad</span>) <span class="cm-keyword">return</span>;
<span class="cm-keyword">let</span> <span class="cm-def">progreso</span> <span class="cm-operator">=</span> <span class="cm-number">0</span>;
<span class="cm-keyword">try</span> {
<span class="cm-variable">cuentas</span>[<span class="cm-variable-2">desde</span>] <span class="cm-operator">-=</span> <span class="cm-variable-2">cantidad</span>;
<span class="cm-variable-2">progreso</span> <span class="cm-operator">=</span> <span class="cm-number">1</span>;
<span class="cm-variable">cuentas</span>[<span class="cm-variable">obtenerCuenta</span>()] <span class="cm-operator">+=</span> <span class="cm-variable-2">cantidad</span>;
<span class="cm-variable-2">progreso</span> <span class="cm-operator">=</span> <span class="cm-number">2</span>;
} <span class="cm-keyword">finally</span> {
<span class="cm-keyword">if</span> (<span class="cm-variable-2">progreso</span> <span class="cm-operator">==</span> <span class="cm-number">1</span>) {
<span class="cm-variable">cuentas</span>[<span class="cm-variable-2">desde</span>] <span class="cm-operator">+=</span> <span class="cm-variable-2">cantidad</span>;
}
}
}</pre>
<p><a class="p_ident" id="p_UIoM7t6sCy" href="#p_UIoM7t6sCy" tabindex="-1" role="presentation"></a>Esta versión de la función rastrea su progreso, y si, cuando este terminando, se da cuenta de que fue abortada en un punto donde habia creado un estado de programa inconsistente, repara el daño que hizo.</p>
<p><a class="p_ident" id="p_bvlJ3x5ow/" href="#p_bvlJ3x5ow/" tabindex="-1" role="presentation"></a>Ten en cuenta que, aunque el código <code>finally</code> se ejecuta cuando una excepción deja el bloque <code>try</code>, no interfiere con la excepción. Después de que se ejecuta el bloque <code>finally</code>, la pila continúa desenrollandose.</p>
<p><a class="p_ident" id="p_mNFQ5eg1AO" href="#p_mNFQ5eg1AO" tabindex="-1" role="presentation"></a>Escribir programas que funcionan de manera confiable incluso cuando aparecen excepciones en lugares inesperados es muy difícil. Muchas personas simplemente no se molestan, y porque las excepciones suelen reservarse para circunstancias excepcionales, el problema puede ocurrir tan raramente que nunca siquiera es notado. Si eso es algo bueno o algo realmente malo depende de cuánto daño hará el software cuando falle.</p>
<h2><a class="h_ident" id="h_PIKTwL5/C0" href="#h_PIKTwL5/C0" tabindex="-1" role="presentation"></a>Captura selectiva</h2>
<p><a class="p_ident" id="p_7iG4YNRvEv" href="#p_7iG4YNRvEv" tabindex="-1" role="presentation"></a>Cuando una excepción llega hasta el final de la pila sin ser capturada, esta es manejada por el entorno. Lo que esto significa difiere entre los entornos. En los navegadores, una descripción del error generalmente sera escrita en la consola de JavaScript (accesible a través de las herramientas de desarrollador del navegador). Node.js, el entorno de JavaScript sin navegador que discutiremos en el <a href="node">Capítulo 20</a>, es más cuidadoso con la corrupción de datos. Aborta todo el proceso cuando ocurre una excepción no manejada.</p>
<p><a class="p_ident" id="p_gLdJYSuXJP" href="#p_gLdJYSuXJP" tabindex="-1" role="presentation"></a>Para los errores de programador, solo dejar pasar al error es a menudo lo mejor que puedes hacer. Una excepción no manejada es una forma razonable de señalizar un programa roto, y la consola de JavaScript, en los navegadores moderno, te proporcionan cierta información acerca de qué llamdas de función estaban en la pila cuando ocurrió el problema.</p>
<p><a class="p_ident" id="p_igAOH/6YWz" href="#p_igAOH/6YWz" tabindex="-1" role="presentation"></a>Para problemas que se <em>espera</em> que sucedan durante el uso rutinario, estrellarse con una excepción no manejada es una estrategia terrible.</p>
<p><a class="p_ident" id="p_dwlfWoEc0e" href="#p_dwlfWoEc0e" tabindex="-1" role="presentation"></a>Usos inválidos del lenguaje, como hacer referencia a vinculaciones inexistentes, buscar una propiedad en <code>null</code>, o llamar a algo que no sea una función, también dará como resultado que se levanten excepciones. Tales excepciones también pueden ser atrapadas.</p>
<p><a class="p_ident" id="p_MibDZo2sVj" href="#p_MibDZo2sVj" tabindex="-1" role="presentation"></a>Cuando se ingresa en un cuerpo <code>catch</code>, todo lo que sabemos es que <em>algo</em> en nuestro cuerpo <code>try</code> provocó una excepción. Pero no sabemos <em>que</em>, o <em>cual</em> excepción este causó.</p>
<p><a class="p_ident" id="p_/sxyXaHiKn" href="#p_/sxyXaHiKn" tabindex="-1" role="presentation"></a>JavaScript (en una omisión bastante evidente) no proporciona soporte directo para la captura selectiva de excepciones: o las atrapas todas o no atrapas nada. Esto hace que sea tentador <em>asumir</em> que la excepción que obtienes es en la que estabas pensando cuando escribiste el bloque <code>catch</code>.</p>
<p><a class="p_ident" id="p_69/Cvwa1V5" href="#p_69/Cvwa1V5" tabindex="-1" role="presentation"></a>Pero puede que no. Alguna otra suposición podría ser violada, o es posible que hayas introducido un error que está causando una excepción. Aquí está un ejemplo que <em>intenta</em> seguir llamando <code>pedirDireccion</code> hasta que obtenga una respuesta válida:</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_7yc4v/m91+" href="#c_7yc4v/m91+" tabindex="-1" role="presentation"></a><span class="cm-keyword">for</span> (;;) {
<span class="cm-keyword">try</span> {
<span class="cm-keyword">let</span> <span class="cm-def">direccion</span> <span class="cm-operator">=</span> <span class="cm-variable">peirDirrecion</span>(<span class="cm-string">"Donde?"</span>); <span class="cm-comment">// ← error tipografico!</span>
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string">"Tu elegiste "</span>, <span class="cm-variable-2">direccion</span>);
<span class="cm-keyword">break</span>;
} <span class="cm-keyword">catch</span> (<span class="cm-def">e</span>) {
<span class="cm-variable">console</span>.<span class="cm-property">log</span> (<span class="cm-string">"No es una dirección válida. Inténtalo de nuevo"</span>);
}
}</pre>
<p><a class="p_ident" id="p_wE8LUGCmod" href="#p_wE8LUGCmod" tabindex="-1" role="presentation"></a>El constructo <code>for (;;)</code> es una forma de crear intencionalmente un ciclo que no termine por si mismo. Salimos del ciclo solamente una cuando dirección válida sea dada. <em>Pero</em> escribimos mal <code>pedirDireccion</code>, lo que dará como resultado un error de “variable indefinida”. Ya que el bloque <code>catch</code> ignora por completo su valor de excepción (<code>e</code>), suponiendo que sabe cuál es el problema, trata erróneamente al error de vinculación como indicador de una mala entrada. Esto no solo causa un ciclo infinito, también “entierra” el útil mensaje de error acerca de la vinculación mal escrita.</p>
<p><a class="p_ident" id="p_1NuYJNWPUR" href="#p_1NuYJNWPUR" tabindex="-1" role="presentation"></a>Como regla general, no incluyas excepciones a menos de que sean con el propósito de “enrutarlas” hacia alguna parte—por ejemplo, a través de la red para decirle a otro sistema que nuestro programa se bloqueó. E incluso entonces, piensa cuidadosamente sobre cómo podrias estar ocultando información.</p>
<p><a class="p_ident" id="p_0cwV1xSMNN" href="#p_0cwV1xSMNN" tabindex="-1" role="presentation"></a>Por lo tanto, queremos detectar un tipo de excepción <em>específico</em>. Podemos hacer esto al revisar el bloque <code>catch</code> si la excepción que tenemos es en la que estamos interesados y relanzar de otra manera. Pero como hacemos para reconocer una excepción?</p>
<p><a class="p_ident" id="p_86AnFAYZd1" href="#p_86AnFAYZd1" tabindex="-1" role="presentation"></a>Podríamos comparar su propiedad <code>message</code> con el mensaje de error que sucede estamos esperando. Pero esa es una forma inestable de escribir código—estariamos utilizando información destinada al consumo humano (el mensaje) para tomar una decisión programática. Tan pronto como alguien cambie (o traduzca) el mensaje, el código dejaria de funcionar.</p>
<p><a class="p_ident" id="p_iIQiklhTm1" href="#p_iIQiklhTm1" tabindex="-1" role="presentation"></a>En vez de esto, definamos un nuevo tipo de error y usemos <code>instanceof</code> para identificarlo.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_bcf52w/Gqm" href="#c_bcf52w/Gqm" tabindex="-1" role="presentation"></a><span class="cm-keyword">class</span> <span class="cm-def">ErrorDeEntrada</span> <span class="cm-keyword">extends</span> <span class="cm-variable">Error</span> {}
<span class="cm-keyword">function</span> <span class="cm-def">pedirDireccion</span>(<span class="cm-def">pregunta</span>) {
<span class="cm-keyword">let</span> <span class="cm-def">resultado</span> <span class="cm-operator">=</span> <span class="cm-variable">prompt</span>(<span class="cm-variable-2">pregunta</span>);
<span class="cm-keyword">if</span> (<span class="cm-variable-2">resultado</span>.<span class="cm-property">toLowerCase</span>() <span class="cm-operator">==</span> <span class="cm-string">"izquierda"</span>) <span class="cm-keyword">return</span> <span class="cm-string">"I"</span>;
<span class="cm-keyword">if</span> (<span class="cm-variable-2">resultado</span>.<span class="cm-property">toLowerCase</span>() <span class="cm-operator">==</span> <span class="cm-string">"derecha"</span>) <span class="cm-keyword">return</span> <span class="cm-string">"D"</span>;
<span class="cm-keyword">throw</span> <span class="cm-keyword">new</span> <span class="cm-variable">ErrorDeEntrada</span>(<span class="cm-string">"Direccion invalida: "</span> <span class="cm-operator">+</span> <span class="cm-variable-2">resultado</span>);
}</pre>
<p><a class="p_ident" id="p_kh8Okt4/9K" href="#p_kh8Okt4/9K" tabindex="-1" role="presentation"></a>La nueva clase de error extiende <code>Error</code>. No define su propio constructor, lo que significa que hereda el constructor <code>Error</code>, que espera un mensaje de string como argumento. De hecho, no define nada—la clase está vacía. Los objetos <code>ErrorDeEntrada</code> se comportan como objetos <code>Error</code>, excepto que tienen una clase diferente por la cual podemos reconocerlos.</p>
<p><a class="p_ident" id="p_WitYq1uT2f" href="#p_WitYq1uT2f" tabindex="-1" role="presentation"></a>Ahora el ciclo puede atraparlos con mas cuidado.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_eJkuDY8q04" href="#c_eJkuDY8q04" tabindex="-1" role="presentation"></a><span class="cm-keyword">for</span> (;;) {
<span class="cm-keyword">try</span> {
<span class="cm-keyword">let</span> <span class="cm-def">direccion</span> <span class="cm-operator">=</span> <span class="cm-variable">pedirDireccion</span>(<span class="cm-string">"Donde?"</span>);
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string">"Tu eliges "</span>, <span class="cm-variable-2">direccion</span>);
<span class="cm-keyword">break</span>;
} <span class="cm-keyword">catch</span> (<span class="cm-def">e</span>) {
<span class="cm-keyword">if</span> (<span class="cm-variable-2">e</span> <span class="cm-keyword">instanceof</span> <span class="cm-variable">ErrorDeEntrada</span>) {
<span class="cm-variable">console</span>.<span class="cm-property">log</span> (<span class="cm-string">"No es una dirección válida. Inténtalo de nuevo"</span>);
} <span class="cm-keyword">else</span> {
<span class="cm-keyword">throw</span> <span class="cm-variable-2">e</span>;
}
}
}</pre>
<p><a class="p_ident" id="p_ocFJsA0sbF" href="#p_ocFJsA0sbF" tabindex="-1" role="presentation"></a>Esto capturará solo las instancias de <code>error</code> y dejará que las excepciones no relacionadas pasen a través. Si reintroduce el error tipográfico, el error de la vinculación indefinida será reportado correctamente.</p>
<h2><a class="h_ident" id="h_cLlvsxFcXB" href="#h_cLlvsxFcXB" tabindex="-1" role="presentation"></a>Afirmaciones</h2>
<p><a class="p_ident" id="p_YVA81rLUbs" href="#p_YVA81rLUbs" tabindex="-1" role="presentation"></a>Las <em>afirmaciones</em> son comprobaciones dentro de un programa que verifican que algo este en la forma en la que se supone que debe estar. Se usan no para manejar situaciones que puedan aparecer en el funcionamiento normal, pero para encontrar errores hechos por el programador.</p>
<p><a class="p_ident" id="p_O1SjAWi6l1" href="#p_O1SjAWi6l1" tabindex="-1" role="presentation"></a>Si, por ejemplo, <code>primerElemento</code> se describe como una función que nunca se debería invocar en arrays vacíos, podríamos escribirla así:</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_Fj8yzS3VSx" href="#c_Fj8yzS3VSx" tabindex="-1" role="presentation"></a><span class="cm-keyword">function</span> <span class="cm-def">primerElemento</span>(<span class="cm-def">array</span>) {
<span class="cm-keyword">if</span> (<span class="cm-variable-2">array</span>.<span class="cm-property">length</span> <span class="cm-operator">==</span> <span class="cm-number">0</span>) {
<span class="cm-keyword">throw</span> <span class="cm-keyword">new</span> <span class="cm-variable">Error</span>(<span class="cm-string">"primerElemento llamado con []"</span>);
}
<span class="cm-keyword">return</span> <span class="cm-variable-2">array</span>[<span class="cm-number">0</span>];
}</pre>
<p><a class="p_ident" id="p_U2Uv2aN4lo" href="#p_U2Uv2aN4lo" tabindex="-1" role="presentation"></a>Ahora, en lugar de silenciosamente retornar <code>undefined</code> (que es lo que obtienes cuando lees una propiedad de array que no existe), esto explotará fuertemente tu programa tan pronto como lo uses mal. Esto hace que sea menos probable que tales errores pasen desapercibidos, y sea más fácil encontrar su causa cuando estos ocurran.</p>
<p><a class="p_ident" id="p_zctsiECqD1" href="#p_zctsiECqD1" tabindex="-1" role="presentation"></a>No recomiendo tratar de escribir afirmaciones para todos los tipos posibles de entradas erroneas. Eso sería mucho trabajo y llevaría a código muy ruidoso. Querrás reservarlas para errores que son fáciles de hacer (o que te encuentras haciendo constantemente).</p>
<h2><a class="h_ident" id="h_NUFOUyK+lw" href="#h_NUFOUyK+lw" tabindex="-1" role="presentation"></a>Resumen</h2>
<p><a class="p_ident" id="p_nth6pDxtNQ" href="#p_nth6pDxtNQ" tabindex="-1" role="presentation"></a>Los errores y las malas entradas son hechos de la vida. Una parte importante de la programación es encontrar, diagnosticar y corregir errores. Los problemas pueden será más fáciles de notar si tienes un conjunto de pruebas automatizadas o si agregas afirmaciones a tus programas.</p>
<p><a class="p_ident" id="p_LrLMZM5s9G" href="#p_LrLMZM5s9G" tabindex="-1" role="presentation"></a>Por lo general, los problemas causados por factores fuera del control del programa deberían ser manejados con gracia. A veces, cuando el problema pueda ser manejado localmente, los valores de devolución especiales son una buena forma de rastrearlos. De lo contrario, las excepciones pueden ser preferibles.</p>
<p><a class="p_ident" id="p_rf4spVCo/M" href="#p_rf4spVCo/M" tabindex="-1" role="presentation"></a>Al lanzar una excepción, se desenrolla la pila de llamadas hasta el próximo bloque <code>try/catch</code> o hasta el final de la pila. Se le dará el valor de excepción al bloque <code>catch</code> que lo atrape, que debería verificar que en realidad es el tipo esperado de excepción y luego hacer algo con eso. Para ayudar a controlar el impredecible flujo de control causado por las excepciones, los bloques <code>finally</code> se pueden usar para asegurarte de que una parte del código <em>siempre</em> se ejecute cuando un bloque termina.</p>
<h2><a class="h_ident" id="h_tkm7ntLto1" href="#h_tkm7ntLto1" tabindex="-1" role="presentation"></a>Ejercicios</h2>
<h3><a class="i_ident" id="i_rex7TyNReT" href="#i_rex7TyNReT" tabindex="-1" role="presentation"></a>Reintentar</h3>
<p><a class="p_ident" id="p_qbZgcF+xGx" href="#p_qbZgcF+xGx" tabindex="-1" role="presentation"></a>Digamos que tienes una función <code>multiplicacionPrimitiva</code> que, en el 20 por ciento de los casos, multiplica dos números, y en el otro 80 por ciento,<br>genera una excepción del tipo <code>FalloUnidadMultiplicadora</code>. Escribe una función que envuelva esta torpe función y solo siga intentando hasta que una llamada tenga éxito, después de lo cual retorna el resultado.</p>
<p><a class="p_ident" id="p_TCgRiMj8TA" href="#p_TCgRiMj8TA" tabindex="-1" role="presentation"></a>Asegúrete de solo manejar las excepciones que estás tratando de manejar.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_nQj5ezPzfu" href="#c_nQj5ezPzfu" tabindex="-1" role="presentation"></a><span class="cm-keyword">class</span> <span class="cm-def">FalloUnidadMultiplicadora</span> <span class="cm-keyword">extends</span> <span class="cm-variable">Error</span> {}
<span class="cm-keyword">function</span> <span class="cm-def">multiplicacionPrimitiva</span>(<span class="cm-def">a</span>, <span class="cm-def">b</span>) {
<span class="cm-keyword">if</span> (<span class="cm-variable">Math</span>.<span class="cm-property">random</span>() <span class="cm-operator"><</span> <span class="cm-number">0.2</span>) {
<span class="cm-keyword">return</span> <span class="cm-variable-2">a</span> <span class="cm-operator">*</span> <span class="cm-variable-2">b</span>;
} <span class="cm-keyword">else</span> {
<span class="cm-keyword">throw</span> <span class="cm-keyword">new</span> <span class="cm-variable">FalloUnidadMultiplicadora</span>(<span class="cm-string">"Klunk"</span>);
}
}
<span class="cm-keyword">function</span> <span class="cm-def">multiplicacionConfiable</span>(<span class="cm-def">a</span>, <span class="cm-def">b</span>) {
<span class="cm-comment">// Tu código aqui.</span>
}
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">multiplicacionConfiable</span>(<span class="cm-number">8</span>, <span class="cm-number">8</span>));
<span class="cm-comment">// → 64</span></pre>
<div class="solution"><div class="solution-text">
<p><a class="p_ident" id="p_JrUQcc4Zfe" href="#p_JrUQcc4Zfe" tabindex="-1" role="presentation"></a>La llamada a <code>multiplicacionPrimitiva</code> definitivamente debería suceder en un bloquear <code>try</code>. El bloque <code>catch</code> correspondiente debe volver a lanzar la excepción cuando no esta no sea una instancia de <code>FalloUnidadMultiplicadora</code> y asegurar que la llamada sea reintentada cuando lo es.</p>
<p><a class="p_ident" id="p_Slxz4vQdXs" href="#p_Slxz4vQdXs" tabindex="-1" role="presentation"></a>Para reintentar, puedes usar un ciclo que solo se rompa cuando la llamada tenga éxito, como en el <a href="08_error.html#look">ejemplo de <code>mirar</code></a> anteriormente en este capítulo—o usar recursión y esperar que no obtengas una cadena de fallas tan largas que desborde la pila (lo cual es una apuesta bastante segura).</p>
</div></div>
<h3><a class="i_ident" id="i_F6E6Abyzj7" href="#i_F6E6Abyzj7" tabindex="-1" role="presentation"></a>La caja bloqueada</h3>
<p><a class="p_ident" id="p_Zg+ex4nK+r" href="#p_Zg+ex4nK+r" tabindex="-1" role="presentation"></a>Considera el siguiente objeto (bastante artificial):</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_QrvcMo8vo/" href="#c_QrvcMo8vo/" tabindex="-1" role="presentation"></a><span class="cm-keyword">const</span> <span class="cm-def">caja</span> <span class="cm-operator">=</span> {
<span class="cm-property">bloqueada</span>: <span class="cm-atom">true</span>,
<span class="cm-property">desbloquear</span>() { <span class="cm-keyword">this</span>.<span class="cm-property">bloqueada</span> <span class="cm-operator">=</span> <span class="cm-atom">false</span>; },
<span class="cm-property">bloquear</span>() { <span class="cm-keyword">this</span>.<span class="cm-property">bloqueada</span> <span class="cm-operator">=</span> <span class="cm-atom">true</span>; },
<span class="cm-property">_contenido</span>: [],
<span class="cm-property">get</span> <span class="cm-property">contenido</span>() {
<span class="cm-keyword">if</span> (<span class="cm-keyword">this</span>.<span class="cm-property">bloqueada</span>) <span class="cm-keyword">throw</span> <span class="cm-keyword">new</span> <span class="cm-variable">Error</span>(<span class="cm-string">"Bloqueada!"</span>);
<span class="cm-keyword">return</span> <span class="cm-keyword">this</span>.<span class="cm-property">_contenido</span>;
}
};</pre>
<p><a class="p_ident" id="p_dMVhV4zSGg" href="#p_dMVhV4zSGg" tabindex="-1" role="presentation"></a>Es solo una caja con una cerradura. Hay un array en la caja, pero solo puedes accederlo cuando la caja esté desbloqueada. Acceder directamente a la propiedad privada <code>_contenido</code> está prohibido.</p>
<p><a class="p_ident" id="p_AU2xyE4g0d" href="#p_AU2xyE4g0d" tabindex="-1" role="presentation"></a>Escribe una función llamada <code>conCajaDesbloqueada</code> que toma un valor de función como su argumento, desbloquea la caja, ejecuta la función y luego se asegura de que la caja se bloquee nuevamente antes de retornar, independientemente de si la función argumento retorno normalmente o lanzo una excepción.</p>
<pre class="snippet cm-s-default" data-language="javascript" ><a class="c_ident" id="c_gULxJiOAyw" href="#c_gULxJiOAyw" tabindex="-1" role="presentation"></a><span class="cm-keyword">const</span> <span class="cm-def">caja</span> <span class="cm-operator">=</span> {
<span class="cm-property">bloqueada</span>: <span class="cm-atom">true</span>,
<span class="cm-property">desbloquear</span>() { <span class="cm-keyword">this</span>.<span class="cm-property">bloqueada</span> <span class="cm-operator">=</span> <span class="cm-atom">false</span>; },
<span class="cm-property">bloquear</span>() { <span class="cm-keyword">this</span>.<span class="cm-property">bloqueada</span> <span class="cm-operator">=</span> <span class="cm-atom">true</span>; },
<span class="cm-property">_contenido</span>: [],
<span class="cm-property">get</span> <span class="cm-property">contenido</span>() {
<span class="cm-keyword">if</span> (<span class="cm-keyword">this</span>.<span class="cm-property">bloqueada</span>) <span class="cm-keyword">throw</span> <span class="cm-keyword">new</span> <span class="cm-variable">Error</span>(<span class="cm-string">"Bloqueada!"</span>);
<span class="cm-keyword">return</span> <span class="cm-keyword">this</span>.<span class="cm-property">_contenido</span>;
}
};
<span class="cm-keyword">function</span> <span class="cm-def">conCajaDesbloqueada</span>(<span class="cm-def">cuerpo</span>) {
<span class="cm-comment">// Tu código aqui.</span>
}
<span class="cm-variable">conCajaDesbloqueada</span>(<span class="cm-keyword">function</span>() {
<span class="cm-variable">caja</span>.<span class="cm-property">contenido</span>.<span class="cm-property">push</span>(<span class="cm-string">"moneda de oro"</span>);
});
<span class="cm-keyword">try</span> {
<span class="cm-variable">conCajaDesbloqueada</span>(<span class="cm-keyword">function</span>() {
<span class="cm-keyword">throw</span> <span class="cm-keyword">new</span> <span class="cm-variable">Error</span>(<span class="cm-string">"Piratas en el horizonte! Abortar!"</span>);
});
} <span class="cm-keyword">catch</span> (<span class="cm-def">e</span>) {
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-string">"Error encontrado:"</span>, <span class="cm-variable-2">e</span>);
}
<span class="cm-variable">console</span>.<span class="cm-property">log</span>(<span class="cm-variable">caja</span>.<span class="cm-property">bloqueada</span>);
<span class="cm-comment">// → true</span></pre>
<p><a class="p_ident" id="p_SpAL0eOOpI" href="#p_SpAL0eOOpI" tabindex="-1" role="presentation"></a>Por puntos extras, asegúrete de que si llamas a <code>conCajaDesbloqueada</code> cuando la caja ya está desbloqueada, la caja permanece desbloqueada.</p>
<div class="solution"><div class="solution-text">
<p><a class="p_ident" id="p_6zbtjqw3P+" href="#p_6zbtjqw3P+" tabindex="-1" role="presentation"></a>Este ejercicio requiere de un bloque <code>finally</code>. Tu función deberia primero desbloquear la caja y luego llamar a la función argumento desde dentro de cuerpo <code>try</code>. El bloque <code>finally</code> después de el debería bloquear la caja nuevamente.</p>
<p><a class="p_ident" id="p_mF3GXRvB/K" href="#p_mF3GXRvB/K" tabindex="-1" role="presentation"></a>Para asegurarte de que no bloqueemos la caja cuando no estaba ya bloqueada, comprueba su bloqueo al comienzo de la función y desbloquea y bloquea solo cuando la caja comenzó bloqueada.</p>
</div></div><nav><a href="07_robot.html" title="previous chapter">◀</a> <a href="index.html" title="cover">◆</a> <a href="09_regexp.html" title="next chapter">▶</a></nav>
</article>