Sei sulla pagina 1di 146

Introduccin

Este libro trata de como hacer que las computadoras hagan lo que t quieres que hagan. Las
computadoras son tan comunes como los destornilladores hoy en da, pero tienen mucha ms
complejidad oculta y, por lo tanto, son ms difciles de operar y entender. Para muchos siguen
siendo cosas extraas, un poco amenazadoras.

Hemos encontrado dos formas efectivas de cerrar la brecha ente nosotros, suaves organismos
biolgicos con talento para el razonamiento social y espacial, y las computadoras,
manipuladoras sin sentimientos de datos sin sentido. La primera es usar nuestros sentidos del
mundo fsico y construir interfaces que imitan ese mundo y nos permiten manipular figuras
en una pantalla con nuestros dedos. Esto funciona muy bien para interacciones casuales con
la mquina.

Pero an no hemos encontrado una buena forma de usar la interfaz grfica para comunicar a
la computadora cosas que el diseador de interfaces no anticip. Para interfaces abiertas,
como indicarle a la computadora que ejecute tareas arbitrarias, hemos tenido ms suerte con
otra estrategia, que hace uso de nuestro talento para el lenguaje: ensearle a la computadora
uno.

Los lenguajes humanos permiten que palabras y frases se combinen en muchas diferentes
formas, lo cul nos permite decir una amplia variedad de cosas. Los lenguajes
computacionales, aunque son gramaticalmente menos flexibles, siguen un principio similar.

La computacin casual se ha extendido mucho en los ltimos 20 aos, y las interfaces


basadas en el lenguaje, que alguna vez fueron la forma predeterminada en la que las personas
interactuaban con las computadoras, han sido reemplazadas en gran medida por interfaces
grficas. Pero todava estn ah, si sabes donde buscar. Uno de tales lenguajes, JavaScript,
est presente en casi todos los navegadores web existentes y por lo tanto, disponible en
prcticamente todos los dispositivos de consumo.

Este libro trata de hacer que te familiarices lo suficiente con este lenguaje para que puedas
hacer que la computadora haga lo que t quieras.
Acerca de la Programacin
No ilumino a aquellos que no estn deseosos de aprender, tampoco despierto a quienes no
estn ansiosos de darse una explicacin a s mismos. Si les he presentado una esquina del
cuadro y ellos no vienen con las otras tres, no debera recorrer otra vez los puntos."

Confucio

A parte de explicar JavaScript, te introducir en los principios bsicos de la programacin.


Programar, resulta, es difcil. Las reglas fundamentales tpicamente son simples y claras. Pero
los programas construidos sobre esas reglas tienden a volverse lo suficientemente complejos
para introducir sus propias reglas y ms complejidad. En cierta forma, ests construyendo tu
propio laberinto y podras perderte en l.

Habr ocasiones en las que al leer este libro te sentirs terriblemente frustrado. Si eres nuevo
programando, tendrs mucho material nuevo para digerir. Mucho de este material ser
despus combinado en formas que requerirn que hagas conexiones adicionales.

Es tu responsabilidad realizar el esfuerzo necesario. Cuando se te haga difcil seguir este


libro, no concluyas rpidamente nada acerca de tus capacidades. Tu eres bueno, slo necesitas
mantener el paso. Toma un respiro, vuelve a leer el material, y siempre asegrate de leer y
entender los programas de ejemplo y los ejercicios. Aprender es un trabajo duro, pero todo lo
que aprendas ahora es tuyo y har el aprendizaje cada vez ms fcil.

El programador de computadoras es el creador de universos de los cuales l slo es


responsable. Universos de complejidad virtualmente ilimitada pueden ser creados en la forma
de programas de computadora."

Joseph Weizenbaum, Computer Power and Human Reason

Un programa es muchas cosas. Es una pieza de texto escrita por un programador, es la fuerza
que dirige a la computadora para hacer lo que hace, son datos en la memoria de la
computadora, y an as controla las acciones realizadas en esta misma memoria. Las
analogas que tratan de comparar a las computadoras con objetos que conocemos tienden a
quedarse cortas. Una que se ajusta superficialmente es la de mquina, un montn de piezas
separadas que se relacionan, y para hacerlo funcionar, tenemos que considerar las formas en
que esas piezas se interconectan y contribuyen al funcionamiento del todo.

Una computadora es una mquina que actu como anfitriona de estas mquinas inmateriales.
Las computadoras por s mismas slo pueden hacer cosas estpidamente simples. La razn
por la que son tan poderosas es que hacen esas cosas a una velocidad increblemente rpida.
Un programa puede combinar ingeniosamente un nmero enorme de esas acciones simples
para lograr cosas muy complicadas.

Para algunos de nosotros, escribir programas de computadoras es un juego fascinante. Un


programa es una construccin del pensamiento. No tiene costo construirlo, no pesa nada, y
crece fcilmente bajo nuestras manos tecleando.
Pero si no tenemos cuidado, el tamao y la complejidad de un programa se saldrn de control,
confundiendo incluso a la persona que lo cre. Mantener los programas bajo control es el
principal problema de la programacin. Cuando funcionan, es hermoso. El Arte de programar
es la habilidad de controlar la complejidad. Un gran programa est dominado, hecho simple
en su complejidad.

Muchos programadores creen que esta complejidad es mejor controlada usando slo un
pequeo conjunto de tcnicas bien entendidas en sus programas. Estos han compuesto reglas
estrictas (mejores prcticas) que prescriben la forma que los programas deberan tener, y
los ms celosos de ellos considerarn a aquellos que se salen de esta pequea zona segura
como malos programadores.

Qu hostilidad hacia la riqueza de la programacin, la de tratar de reducirla a algo simple y


predecible, tratar de hacer tab a todos los programas extraos y bellos! El panorama de todas
las tcnicas de programacin es enorme, fascinante en su diversidad, y permanece
inexplorado en gran parte. Ciertamente es peligroso ir, porque seduce al programador novato
con todo tipo de confusiones, pero eso slo significa que debes de andar con cuidado y estar
alerta. Conforme vayas aprendiendo, siempre habr nuevos retos y nuevo territorio por
explorar. Los programadores que se nieguen a explorar dejarn de progresar, olvidarn su
alegra, y se aburrirn con su trabajo.

Por qu el lenguaje importa


En el principio, cuando naci la computacin, no haba lenguajes de programacin. Los
programas lucan algo as:

00110001 00000000 00000000


00110001 00000001 00000001
00110011 00000001 00000010
01010001 00001011 00000010
00100010 00000010 00001000
01000011 00000001 00000000
01000001 00000001 00000001
00010000 00000010 00000000
01100010 00000000 00000000

Eso es un programa para sumar los nmeros del 1 al 10 e imprimir el resultado: 1 + 2 + ...
+ 10 = 55. Podra correr en una simple, hipottica mquina. Para programar las primeras
computadoras, era necesario configurar grandes conjuntos de tiras de switches en la posicin
correcta o perforar tiras de tarjetas e introducirlas en la computadora. Probablemente te
puedes imaginar cuan tedioso y propenso al error era este procedimiento. Incluso escribir
programas simples requera gran inteligencia y disciplina. Los programas complejos eran casi
inconcebibles.

Claro, introducir manualmente estos oscuros patrones de bits (los unos y ceros) dieron al
programador un profundo sentimiento de ser un poderoso mago. Y eso ha valido algo en
trminos de satisfaccin en el trabajo.

Cada lnea del programa anterior contiene una instruccin. Podra ser escrita en espaol como
sigue:
1. Guarda el nmero 0 en la direccin de memoria 0.
2. Guarda el nmero 1 en la direccin de memoria 1.
3. Guarda el valor de la direccin de memoria 1 en la direccin 2.
4. Resta 11 del valor en la direccin de memoria 2.
5. Si el valor en la direccin de memoria 2 es el nmero 0,
contina con la instruccin 9.
6. Suma el valor de la direccin de memoria 1 al valor de la
direccin de memoria 0.
7. Suma 1 al valor de la direccin de memoria 1.
8. Contina con la instruccin 3.
9. Devuelve el valor de la direccin de memoria 0.

Aunque eso es ms legible que una sopa de bits, sigue sin ser agradable. Podra ayudar usar
nombres en los nmeros para las instrucciones y direcciones de memoria.

Pon total igual a 0.


Pon conteo igual a 1.
[bucle]
Pon comparacin igual a conteo.
Resta 11 de comparacin.
Si comparacin es cero, contina en [final].
Suma conteo a total.
Suma 1 a conteo.
Contina en [bucle].
[final]
Devuelve total.

Puedes entender cmo funciona el programa en este punto? Las primeras dos lneas ponen
en dos locaciones de memoria sus valores iniciales: total ser usado para construir el
resultado del clculo y conteo mantendr el registro del nmero en el que estamos trabajando
en este momento. Las lneas que usan comparacin son probablemente las ms raras. El
programa quiere ver si conteo es igual a 11 para saber si puede terminar. A causa de que
nuestra mquina hipottica es ms bien primitiva, solo puede probar si un nmero es cero y
tomar una decisin (o salto) basada en eso. As que usa la direccin de memoria etiquetada
como comparacin para calcular el valor de conteo - 11 y toma una decisin basada en ese
valor. Las prximas dos lneas suman el valor de conteo al resultado e incrementan conteo
en 1 cada vez que el programa ha decido que conteo no es todava 11.

Este es el mismo programa en JavaScript:

var total = 0, conteo = 1;


while (conteo <= 10) {
total += conteo;
conteo += 1;
}
console.log(total);
// 55

Esta versin nos da unas cuantas mejoras ms. Y lo ms importante es que ya no hay
necesidad de especificar la forma en que queremos que nuestro programa salte de atrs para
adelante. La construccin del lenguaje while se encarga de eso. Contina ejecutando el
bloque (dentro de las llaves) debajo de l mientras la condicin que se le dio se mantenga.
Esa condicin es conteo <= 10, lo que significa "conteo es menor o igual que 10". Ya no
tenemos que crear un valor temporal y compararlo con 0, lo cul era un detalle sin inters
para nosotros. Parte del poder de los lenguajes de programacin es que estos se encargan de
los detalles que no nos interesan.

Al final del programa, despus de que la construccin while ha terminado, la operacin


console.log es aplicada al resultado para imprimirlo como resultado.

Finalmente, as es como el programa lucira si sucediera que tenemos las convenientes


operaciones range y sum disponibles, una crea una coleccin de nmeros dentro de un rango
y la otra calcula la suma de una coleccin de nmeros, respectivamente:

console.log(sum(range(1, 10)));
// 55

La moraleja de esta historia es que el mismo programa puede ser expresado en formas largas,
cortas, legibles e ilegibles. La primera versin del programa era extremadamente difcil de
entender, mientras que la ltima est casi en lenguaje humano, ingls: log(registra) la
sum(suma) del rango de nmeros del 1 al 10. (Veremos en captulos posteriores como
construir operaciones como sum y range.)

Un buen lenguaje de programacin ayuda al programador mediante permitirle hablar acerca


de las acciones que la computadora tiene que realizar en un nivel ms alto. Ayuda a omitir
detalles que no nos interesan, provee convenientes bloques de construccin (tales como
while y console.log), y te permite definir tus propios bloques (como sum y range), y hace
fcil componer esos bloques.

Qu es JavaScript?
JavaScript fue introducido en 1995 como una forma de aadir programas a las pginas web
en el navegador Netscape Navigator. Desde entonces el lenguaje ha sido adoptado por la
mayora de los navegadores ms importantes. Ha hecho posibles las aplicaciones web
modernas, aplicaciones con las que puedes interactuar directamente, sin hacer recarga de la
pgina para cada accin. Pero tambin es usado en sitios web ms tradicionales para aadirles
distintas formas de interactividad y hacerlos ms ingeniosos.

Es importante hacer notar que JavaScript no tiene casi nada que ver con el lenguaje de
programacin llamado Java. El nombre tan parecido fue inspirado por razones de marketing
ms que de buen juicio. Cuando JavaScript estaba entrando, el lenguaje Java estaba siendo
promovido fuertemente y ganando popularidad. Alguien pens que sera buena idea colgarse
de su xito. Ahora ya ya nos quedamos con el nombre.

Despus de su adopcin fuera de Netscape, un documento de estndar fue escrito para


describir la forma en que JavaScript debera de trabajar para asegurarse de que distintos
programas que argumentaban soportar JavaScript hablaran realmente del mismo lenguaje.
Este documento es llamado el estndar ECMAScript, en honor a la Ecma International
Organization, que realiz la estandarizacin. En la prctica, los trminos ECMAScript y
JavaScript pueden ser usados indistintamente, son dos nombres para el mismo lenguaje.

Existen aquellos que dirn cosas terribles acerca de JavaScript. Muchas de esas cosas son
ciertas. La primera vez que tuve que programar algo en JavaScript, rpidamente llegu a
despreciarlo. Aceptara cualquier cosa que yo escribiera pero la interpretaba en una forma
completamente distinta a lo que yo quera decir. Esto tena mucho que ver con el hecho de
que yo no tena idea de lo que estaba haciendo, por supuesto, pero aqu existe un problema
real: JavaScript es extremadamente liberal en lo que permite. La idea detrs de este diseo es
que hara la programacin en JavaScript ms fcil para los principiantes. En realidad, la
mayor parte de las veces eso hace ms difcil encontrar los errores en tus programas debido a
que el sistema no te los sealar.

Esta flexibilidad tiene sus ventajas tambin. Permite muchas tcnicas que son imposibles en
otros lenguajes ms rgidos, y como vers, puede ser usada para superar algunas de las fallas
de JavaScript (por ejemplo en el Captulo 10). Despus de aprender el lenguaje propiamente
y de trabajar con l por un tiempo, JavaScript realmente me ha gustado.

Han existido varias versiones de JavaScript. ECMAScript versin 3 fue la versin


ampliamente soportada en la poca de ascensin a la dominacin de JavaScript, ms o menos
entre 2000 y 2010. Durante este tiempo, el trabajo fue dirigido a una ambiciosa versin 4, que
planeaba varias mejoras radicales y extensiones al lenguaje. Cambiar un lenguaje vivo,
ampliamente usado en una forma tan radical result ser polticamente difcil, y el trabajo en la
versin 4 fue abandonado en 2008, llevando as a la salida de la mucho menos ambiciosa
versin 5 en 2009. Nosotros estamos en el punto en el que todos los navegadores mayores
soportan la versin 5, que es la versin en la que este libro se enfocar. La versin 6 est en
proceso de ser terminada, y algunos navegadores estn empezando a soportar nuevas
caractersticas de esta versin.

Los navegadores web no son las nicas plataformas en las que JavaScript es usado. Algunas
Bases de Datos, como MongoDB y CouchDB, usan JavaScript como su lenguaje de manejo y
consulta. Varias plataformas para programacin de computadoras de escritorio y servidores,
ms notablemente el proyecto Node.js (el tema del Captulo 20), estn haciendo disponible
un entorno poderoso para la programacin en JavaScript fuera del navegador.

Cdigo, y qu hacer con l


El cdigo es el texto del que estn compuestos los programas. La mayor parte de los captulos
de este libro contienen un montn de l. En mi experiencia, leer y escribir cdigo son partes
indispensables de aprender a programar, as que trata de no slo darles una mirada rpida a
los ejemplos. Lelos atentamente y entindelos. Esto podra ser lento y confuso al principio,
pero prometo que rpidamente le agarrars el modo. Lo mismo es aplicable a los ejercicios.
No asumas que los entiendes hasta que realmente hayas escrito una solucin que funcione.

Te recomiendo probar tus soluciones a los ejercicios en un intrprete de JavaScript real. De


esa forma obtendrs retroalimentacin inmediata acerca de si lo que ests haciendo funciona,
y, yo espero, sers tentado a experimentar e ir ms all de los ejercicios.

Cuando ests leyendo este libro en tu navegador, puedes editar(y correr) todos los programas
de ejemplo haciendo clic en ellos.

Si quieres correr los programas de este libro fuera del ambiente que se provee, hay que
prestarle atencin a ciertas cosas. Muchos ejemplos deberan trabajar por s mismos. Pero el
cdigo de los captulos ms avanzados est escrito para un entorno especfico (el navegador o
Node.js) y slo puede correr ah. Adems, muchos captulos definen programas ms grandes,
y las partes del cdigo que aparecen en l dependen de entre ellas o de archivos externos. El
entorno en el sitio tiene links para descargar los archivos Zip que contienen todos los scripts y
datos necesarios para hacer funcionar el cdigo de cualquier captulo.

Vista general del libro


Este libro est compuesto por tres partes. Los primeros 11 captulos hablan de JavaScript en
s mismo. Los siguientes ocho son acerca de los navegadores web y la forma en que
JavaScript es usado para programarlos. Finalmente, los ltimos dos captulos estn dedicados
a Node.js, otro entorno para programar en JavaScript.

A lo largo del libro hay cinco captulos de proyecto, que describen programas de ejemplo ms
grandes para darte una prueba de la programacin en el mundo real. En orden de aparicin
trabajaremos en simulacin de vida artificial, un lenguaje de programacin, un juego de
plataforma, un programa de pintura, y un sitio dinmico.

La parte del lenguaje del libro empieza con cuatro captulos para presentar la estructura
bsica de JavaScript. Estos presentan estructuras de control (como la palabra while que viste
en esta introduccin), funciones (escribir tus propias operaciones), y estructuras de datos.
Despus de esto, sers capaz de escribir programas simples. Despus, los captulos 5 y 6
presentan tcnicas para usar funciones y objetos para escribir cdigo ms abstracto y de esta
manera mantener a la complejidad bajo control.

Despus de un primer captulo de proyecto, la primera parte del libro contina con captulos
acerca de manejo y correccin de errores, expresiones regulares (una herramienta importante
para el manejo de datos de texto), y modularidad, otra arma contra la complejidad. El
segundo captulo de proyecto termina con la primera parte del libro.

En la segunda parte, los Captulos 12 a 19, describen las herramientas a las que JavaScript
tiene acceso en el navegador web. Aprenders a mostrar cosas en la pantalla (Captulos 13 y
16), responder a los datos de entrada del usuario (Captulos 14 y 18), y a comunicarte a travs
de la red (Captulo 17). Otra vez, hay dos proyectos en esta parte.

Despus de eso, el Captulo 20 describe Node.js, y el Captulo 21 construye un sencillo


sistema web usando esa herramienta.

Convenciones Tipogrficas
En este libro, texto escrito en fuente monoespacio representar elementos de programas;
algunas veces programas completos y otras, partes de programas que hayan sido definidos
cerca de ah. Los programas(de los cuales has visto unos pocos), estn escritos como sigue:

function fac(n) {
if (n == 0)
return 1;
else
return fac(n - 1) * n;
}
Algunas veces, para mostrar la salida que un programa produce, la salida esperada ser
escrita despus de este, con dos diagonales y una flecha enfrente.

console.log(fac(8));
// 40320

Buena suerte!

Captulo 1
Valores, Tipos y Operadores
Debajo de la superficie de la mquina, el programa se mueve. Sin esfuerzo, se expande y
contrae. En gran armona, los electrones se separan y reagrupan. Las formas en el monitor no
son ms que ondas en el agua. La escencia permanece invisible debajo.

Master Yuan-Ma, The Book of Programming

En el mundo de las computadoras slo existen los datos. Puedes leer, modificar y crear
nuevos datos, pero cualquier cosa que no sea datos simplemente no existe. Todos estos datos
son guardados en largas secuencias de bits y por lo tanto son parecidos.

Los bits son cualquier tipo de cosas con dos valores, normalmente descritos como ceros y
unos. Dentro de la computadora, toman formas como alta o baja carga elctrica, una seal
dbil o fuerte, o un punto brillante u opaco en la superficie de un CD. Cualquier pieza de
informacin discreta puede ser reducida a una secuencia de ceros y unos y por lo tanto
representada como bits.

Por ejemplo, piensa cmo podras representar el nmero 13 en bits. Funciona de la misma
forma en que escribes nmeros decimales, pero en vez de tener 10 dgitos, tienes slo 2, y el
peso de cada uno se incrementa por un factor de 2 de derecha a izquierda. Aqu estn los bits
que conforman el nmero 13, con los pesos de cada uno mostrados debajo de ellos.

0 0 0 0 1 1 0 1
128 64 32 16 8 4 2 1

As que ese es el nmero binario 00001101, u 8 + 4 + 1, que equivale a 13.

Valores
Imagina un mar de bits. Un ocano de estos. Una computadora moderna tpica tiene ms de
30 mil millones de bits en su almacenamiento de datos voltil. El almacenamiento no voltil
(el disco duro o su equivalente) tiene un par de rdenes de magnitud ms todava.
Para ser capaz de trabajar con semejantes cantidades de bits sin perderte, puedes separarlos
en pedazos que representen piezas de informacin. En un entorno en JavaScript, esos pedazos
son llamados valores. Aunque todos los valores estn hechos de bits, juegan diferentes roles.
Cada valor tiene un tipo que determina su rol. Existen seis tipos bsicos de valores en
JavaScript: nmeros, cadenas, Booleanos, objetos, funciones, y valores indefinidos.

Para crear un valor, slo tienes que invocar su nombre. Esto es conveniente. No tienes que
reuinir el material de construccin de tus valores o pagar por ellos. Slo llamas uno y woosh,
lo tienes. No son creados de la nada, por supuesto. Cada valor tiene que estar almacenado en
algn lugar, y si quieres usar una cantidad enorme de estos al mismo tiempo, te podras
quedar sin bits. Afortunadamente, esto se convierte en un problema slamente si los necesitas
todos al mismo tiempo. Tan pronto como dejes de usar un valor se disipar, dejando sus bits
para que sean reciclados como material de construccin de la prxima generacin de valores.

Este captulo introduce los elementos atmicos de los programas en JavaScript, los tipos de
valores simples y los operadores que pueden actuar sobre tales valores.

Nmeros
Los valores de tipo nmero(number) son, sin sorpresa alguna, valores numricos. En un
programa en JavaScript, se escriben de la siguiente forma:

13

Usa eso en un programa y causar que el patrn de bits para el nmero 13 exista dentro de la
memoria de la computadora.

JavaScript usa una cantidad fija de bits, 64, para guardar un valor del tipo nmero. Existe un
lmite en la cantidad de patrones que se pueden hacer con 64 bits, lo que significa que la
cantidad de nmeros que puedes representar tambin es limitada. Para N dgitos decimales, la
cantidad de nmeros que pueden ser representados es 10N. Similarmente, dados 64 dgitos
binarios, puedes representar 264 nmeros diferentes, que es cerca de 18 cuatrillones (un 18
con 18 ceros despus). Eso es mucho.

La memoria de la computadora sola ser mucho ms pequea, y la gente tenda a usar grupos
de 8 16 bits para representar sus nmeros. Era fcil desbordar accidentalmente nmeros tan
pequeos: terminar con un nmero que no pudiera ser almacenado en el nmero dado de bits.
Hoy, incluso las computadoras personales tienen mucha memoria, as que eres libre de usar
grupos de 64 bits, lo que significa que necesitas preocuparte del desbordamiento slo cuando
ests tratando con nmeros verdaderamente astronmicos.

No todos los nmero enteros debajo de 18 cuatrillones caben en un nmero de JavaScript.


Esos bits tambin guardan nmeros negativos, as que un bit indica el signo del nmero. Un
problema mayor es que los nmeros no enteros tambin deben ser representados. Para hacer
esto, algunos de los bits son usados para guardar la posicin del punto decimal. El nmero
entero mximo real que puede ser guardado est ms cerca del rango de los 9 trillones (15
ceros), que an es satisfactoriamente grande.

Los nmeros fraccionarios son escritos usando un punto.

9.81

Para nmeros muy grandes o muy pequeos, tambin se puede usar la notacin cientfica,
aadiendo una "e" de "exponente", seguido por el exponente del nmero:

2.998e8

Esto es 2.998 108 = 299,800,000.

Clculos con nmeros enteros (en ingls llamados integer) ms pequeos que el supracitado
9 trillones, estn garantizados para siempre ser precisos. Desafortunadamente, clculos con
nmeros fraccionarios generalmente no lo son. Justo como (pi) no puede ser expresado
precisamente por un nmero finito de dgitos decimales, muchos nmeros pierden algo de
precisin cuando slo hay 64 bits disponibles para guardarlos. Esto es una pena, pero causa
problemas prcticos slo en algunas situaciones especficas. Lo importante es estar al tanto de
esto y tratar a los nmeros digitales fraccionarios como aproximaciones y no como valores
precisos.

Aritmtica

Lo principal que se hace con los nmeros es la aritmtica. Las operaciones aritmticas como
la suma o la multiplicacin toman dos valores y producen uno nuevo a partir de estos. As es
como lucen en JavaScript:

100 + 4 * 11

Los smbolos + y * son llamados operadores. El primero representa la suma y el segundo la


multiplicacin. Al poner un operador entre dos valores, se aplicar la operacin a esos valores
y producir un nuevo valor.

Significa el ejemplo anterior: "suma 4 y 100, y multiplica el resultado por 11", o es la


multiplicacin ralizada antes de hacer la suma? Como pudiste haber adivinado, la
multiplicacin ocurre primero. Pero como en matemticas, puedes cambiar esto mediante
encerrar en parntesis la suma.

(100 + 4) * 11

Para la resta existe el operador -, y la divisin se puede hacer con el operador /.


Cuando los operadores aparecen juntos sin parntesis, el orden en el que son aplicados es
determinado por su precedencia. El ejemplo muestra que la multiplicacin se aplica antes que
la suma. El operador / tiene la misma precedencia que *. De igual forma pasa con + y -.
Cuando varios operadores con la misma precedencia aparecen juntos, como en 1 - 2 + 1,
son aplicados de izquierda a derecha: (1 - 2) + 1.

Estas reglas de precedencia son algo de lo que no te deberas de preocupar. Cuando tengas
duda, simplemente agrega parntesis.

Existe un operador aritmtico ms, que podras no reconocer inmediatamente. El smbolo %


es usado para representar la operacin sobrante. X % Y es el sobrante de dividir X entre Y. Por
ejemplo, 314 % 100 produce 14, y 144 % 12 da 0. La precedencia del sobrante es la misma
que la de la multiplicacin y divisin. Vers a menudo este operador referido como mdulo,
aunque tcnicamente sobrante es ms preciso.

Nmeros Especiales

Hay tres valores especiales en JavaScript que son considerados nmeros pero no se
comportan como nmeros normales.

Los primeros dos son Infinity y -Infinity, que representan infinitos positivos y
negativos. Infinity - 1 sigue siendo Infinity, y as por el estilo. No confes mucho en los
clculos basados en infinitos. No son matemticamente confiables y pronto te llevarn al
prximo nmero especial: NaN.

NaN son las siglas de not a number ("no es un nmero"), aunque es un valor del tipo
nmero. Obtendrs este resultado cuando, por ejemplo, trates de calcular 0 / 0 (cero entre
cero), Infinity - Infinity, o cualquier otra operacin numrica que no produzca un
resultado preciso, significativo.

Cadenas
El siguiente tipo de dato bsico son las cadenas. Estas son usadas para representar texto. Son
declaradas al poner el contenido entre comillas.

"Parcha mi bote con goma de mascar"


'Monkeys wave goodbye'

Tanto las comillas simples como las dobles pueden ser usadas para declarar cadenas de texto
mientras coincidan al principio y al final.

Casi cualquier cosa puede estar entre comillas, y JavaScript crear una cadena. Pero unos
cuantos caracteres son un poco difciles. Puedes imaginar que poner comillas dentro de
comillas puede ser difcil. Newlines (el carcter salto de lnea, lo que obtines cuando
presionas Enter), tampoco puede ser introducido entre comillas. La cadena tiene que
permanecer en una sola lnea.

Para hacer posible la inclusin de estos caracteres en una cadena de texto, la siguiente
notacin es usada: cuando una diagonal invertida(backslash: \) se encuentra dentro de un
texto entre comillas, indica que el carcter siguiente tiene un significado especial. Esto es
llamado escapar el carcter. Una comilla que es precedida por una diagonal invertida no
terminar la cadena, sino que ser parte de ella. Cuando un carcter n sigue a una diagonal
invertida, se interpreta como una nueva lnea. Similarmente, un t despus de la diagonal
invertida significa un tabulador. Tomemos la siguiente cadena:

"Esta es la primera lnea\nY esta la segunda"

El verdadero texto contenido es:

Esta es la primera lnea


Y esta la segunda

Existen, por supuesto, situaciones en las que querrs que una diagonal invertida sea slo eso
en una cadena de texto, no un cdigo especial. Si dos diagonales invertidas estn juntas, se
volvern una, y slo eso quedar como resultado en el valor de la cadena. As es como la
cadena "Un carcter de nueva lnea es escrito "\n"." puede ser expresada:

"Un carcter de nueva lnea es escrito \"\\n\"."

Las cadenas de texto no pueden ser divididas numricamente, multiplicadas, o restadas, pero
el carcter + puede ser usado en ellas. No suma, sino que concatena; pega dos cadenas. La
siguiente lnea produce la cadena "concatenar":

"con" + "cat" + "e" + "nar"

Hay ms maneras de manipular las cadenas, de las que hablaremos cuando lleguemos a los
mtodos en el Captulo 4.

Operadores unarios
No todos los operadores son smbolos. Algunos son escritos como palabras. Un ejemplo es el
operador typeof, que produce una cadena de texto que representa el tipo del valor que le
pasaste.

edit & run code by clicking it


console.log(typeof 4.5)
// number
console.log(typeof "x")
// string

Usaremos console.log para indicar que queremos ver el resultado de la evaluacin de algo.
Cuando corres ese cdigo, el valor producido debera mostrarse en pantalla, aunque la forma
en que aparece depender del entorno en que ests corriendo el programa.

Los otros operadores que hemos visto operaban sobre dos valores, pero typeof slamente
toma uno. Los operadores que usan dos valores son llamados operadores binarios, mientras
que aquellos que slo toman uno son llamados operadores unarios. El operador menos puede
usar tanto como operador binario como operador unario.

console.log(- (10 - 2))


// -8

Valores Booleanos
A menudo, necesitars un valor que simplemente distinga entre dos posibilidades, como "s"
y "no" o "encendido" y "apagado". Para esto, JavaScript tiene un tipo Booleano, que tiene
slo dos valores, verdadero (true) y falso (false), que son simplemente estas palabras en
ingls.

Comparaciones

Esta es una forma de producir valores booleanos:

console.log(3 > 2)
// true
console.log(3 < 2)
// false

Los signos > y < son los smbolos tradicionales para "es mayor que" y "es menor que",
respectivamente. Estos son operadores binarios. Aplicarlos resulta en un valor Booleano que
indica si son ciertos en ese caso.

Las cadenas de texto pueden ser comparadas de la misma manera.

console.log("Aardvark" < "Zoroaster")


// true

La manera en que las cadenas son ordenadas es ms o menos alfabtica: las letras maysculas
son siempre "menores" que las minsculas, as que "Z" < "a" es verdad, y los caracteres no
alfabticos (!, -, y as por el estilo) son tambin incluidos en el ordenamiento. La
comparacin real est basada en el estndar Unicode. Este estndar asigna un nmero
virtualmente a cada carcter que alguna vez necesitars, incluyendo caracteres del griego,
rabe, japons, tamil, y otros alfabetos. Tener tales nmeros es til para guardar las cadenas
de texto dentro de la computadora porque hace posible representarlas como una secuencia de
nmeros. Cuando comparamos cadenas, JavaScript va de izquierda a derecha, comparando
los cdigos numricos de los caracteres uno por uno.

Otros operadores similares son >= (mayor o igual a), <= (menor o igual a), == (igual a), y !=
(no es igual a).

console.log("Itchy" != "Scratchy")
// true

Slo existe un valor en JavaScript que no es igual a s mismo, y este es NaN, que significa "no
es un nmero".

console.log(NaN == NaN)
// false

La intencin de NaN es representar el resultado de un clculo sin sentido y como tal, no es


igual al resultado de cualquier otro clculo sin sentido.
Operadores Lgicos

Hay tambin algunas operaciones que pueden ser aplicadas a los valores Booleanos.
JavaScript soporta tres operadores lgicos: and, or y not. Estos pueden ser usados para
"razonar" con los Booleanos.

El operador && representa la operacin lgica and ("y"). Es un operador binario, y su


resultado es verdadero(true) slo si los dos valores dados son verdaderos.

console.log(true && false)


// false
console.log(true && true)
// true

El operador || denota la operacin lgica or ("o"). Devuelve verdadero si cualquiera de los


dos valores dados es verdadero.

console.log(false || true)
// true
console.log(false || false)
// false

Not (Negacin) es escrito como un smbolo de admiracin (!). Es un operador binario que
voltea el valor que se le de; !true produce false y !false regresa true.

Cuando mezclamos estos operadores Booleanos con aritmtica y otros operadores, no es


siempre obvio cundo se necesitan los parntesis. En la prcitca, puedes avanzar sabiendo
que de los operadores que hemos visto hasta ahora, || tiene la menor precedencia, despus
viene el &&, siguen los operadores de comparacin(>, ==, etc.), y despus los dems
operadores. Este orden ha sido elegido tal que, en expresiones tpicas con la siguiente, sean
necesarios tan pocos parntesis como sea posible:

1 + 1 == 2 && 10 * 10 > 50

El ltimo operador lgico del que hablar no es unario, ni binario, sino ternario, opera en tres
valores. Este es escrito con un smbolo de interrogacin y dos puntos, como sigue:

console.log(true ? 1 : 2);
// 1
console.log(false ? 1 : 2);
// 2

Este es llamado el operador condicional (o algunas veces el operador tenario dado que es el
nico operador de este tipo en el lenguaje). El valor a la izquierda del signo de interrogacin
"escoge" cul de los otros dos valores resultar. Cuando es verdadero, el valor central es
escogido, y cuando es falso, el valor de la derecha se da como resultado.
Valores Indefinidos (Undefined)
Existen dos valores especiales, escritos null y undefined, que son usados para denotar la
ausencia de un valor con significado. Son valores en s mismos, pero no poseen ninguna
informacin.

Muchas operaciones en el lenguaje que no producen un valor con significado (lo vers
despus) producen undefined simplemente porque tienen que producir algn valor.

La diferencia en el significado entre undefined y null es un accidente del diseo de


JavaScript, y no importa la mayora del tiempo. En los casos en dnde realmente te tienes que
preocupar de estos valores, te recomiendo tratarlos como intercambiables (ms de esto en un
momento).

Conversin automtica de tipos


En la introduccin, mencion que JavaScript acepta casi cualquier programa que le des,
incluso programas que hagan cosas raras. Esto es muy bien demostrado por las siguientes
expresiones:

console.log(8 * null)
// 0
console.log("5" - 1)
// 4
console.log("5" + 1)
// 51
console.log("cinco" * 2)
// NaN
console.log(false == 0)
// true

Cuando un operador es aplicado al tipo "incorrecto" de valor, JavaScript convertir


silenciosamente el valor en el tipo de dato que espera, usando un conjunto de reglas que a
menudo no son lo que t quieres o esperas. Esto es llamado coercin de tipo. As que el null
en la primera expresin se vuelve 0, y el "5" en la segunda expresin se convierte en 5 (de
cadena a nmero). An as, en la tercera expresin, + intenta hacer concatenacin de cadenas
antes de suma numrica, as que el 1 es convertido en "1" (de nmero a cadena).

Cuando algo que no se corresponde con un nmero de manera obvia (como "cinco" o
undefined) es convertido a un nmero, el valor resultante es NaN. Las siguientes operaciones
aritmticas sobre NaN seguirn produciendo NaN, as que si te encuentras con uno de estos en
un lugar inesperado, busca conversiones accidentales de tipo.

Cuando comparamos valores del mismo tipo usando ==, la salida es fcil de predecir:
deberas obtener verdadero cuando los dos valores sean el mismo, excepto en el caso de NaN.
Pero cuando los tipos son diferentes, JavaScript usa un complicado y confuso conjunto de
reglas para determinar qu hacer. En la mayora de los casos, slo trata de convertir uno de
los valores al tipo de dato del otro valor. Sin embargo, cuando null o undefined estn en
cualquier lado de la operacin, resulta verdadero slo en el caso de que los dos lados sean
null o undefined.
console.log(null == undefined);
// true
console.log(null == 0);
// false

Este ltimo comportamiento es til a menudo. Cuando quieres probar si un valor tiene un
significado real en vez de null o undefined, simplemente comparas contra null con el
operador == (o !=).

Y si quieres probar si algo se refiere precisamente al valor false? Las reglas para convertir
cadenas y nmeros a Booleanos dicen que 0, NaN y la cadena vaca ("") cuentan como false,
mientras que todos los dems valores cuentan como true. Debido a esto, expresiones como 0
== false y "" == false tambin son verdaderas. Para casos como este en el que no quieres
que ocurra ninguna conversin automtica de tipos, existen dos operadores extra: === y !==.
El primero prueba si un valor es precisamente igual a otro, y el segundo si no es precisamente
igual. As que "" === false es falso como se espera.

Yo recomiendo usar la comparacin de tres caracteres defensivamente para evitar que


conversiones de tipos inesperadas te causen problemas. Pero cuando ests seguro de que los
tipos en ambos lados sern los mismos, no hay problema con usar los operadores ms cortos.

Corto circuito de operadores lgicos

Los operadores lgicos && and || manejan los valores de diferentes tipos de un modo
peculiar. Convertirn el valor de su lado izquierdo para decidir qu hacer, pero dependiendo
del operador y del resultado de la conversin, devuelven el valor del lado izquierdo original o
el del lado derecho.

El operador ||, por ejemplo, regresar el valor de la izquierda cuando pueda ser convertido a
true y regresar el valor a la derecha en cualquier otro caso. Esta conversin funciona como
esperaras con valores Booleanos y debera hacer algo anlogo con valores de otros tipos.

console.log(null || "user")
// user
console.log("Karl" || "user")
// Karl

Esta funcionalidad permite al operador || ser usado como una manera de respaldarnos con un
valor por defecto. Si le das en el lado izquierdo una expresin que puede producir un valor
vaco, el valor de la derecha ser usado como reemplazo dado el caso.

El operador && funciona de manera similar, pero en sentido opuesto. Cuando el valor a su
izquierda es algo que se convierte en falso, regresa este valor, y en otro caso regresa el valor
de la derecha.

Otra importante propiedad de estos dos operadores es que el lado derecho slo es evaluado
cuando es necesario. En el caso de true || X, no importa lo que sea X, incluso en si es una
expresin que hace algo terrible, el resultado ser verdadero, y X no ser evaluado nunca. Lo
mismo ocurre para false && X, lo cul es falso e ignorar X. Esto es llamado evaluacin de
corto circuito (short-circuit evaluation).
El operador condicional trabaja de una forma similar. La primera expresin es evaluada
siempre, pero el segundo o tercer valor, el que no sea escogido, no se evala.

Resumen
Vimos cuatro tipos de valores de JavaScript en este captulo: nmeros, cadenas, Booleanos, y
valores indefinidos.

Estos valores son creados al escribir su nombre (true, null) o valor (13, "abc"). Puedes
combinar y transformar valores con los operadores. Vimos operadores binarios para
aritmtica(+, -, *, / y %), concatenacin de cadenas (+), comparacin (==, !=, ===, !==, <, >,
<=, >=), y lgica (&&, ||), as como varios operadores unarios (- para hacer negativo un
nmero, ! para negar lgicamente y typeof para obtener el tipo de un valor) y un operador
ternario (?:) para elegir entre dos valores basndonos en un tercero.

Esto te brinda suficiente informacin para usar JavaScript como una pequea calculadora,
pero no para mucho ms. El siguiente captulo empezar a entremezclar estas expresiones en
programas bsicos.

Captulo 2
Estructura del Programa
Y mi corazn brilla rojo debajo de mi delgada, translcida piel y tienen que administrarme
10cc de JavaScript para hacerme regresar (respondo bien a las toxinas en la sangre).
Hombre, esa cosa te sacar de tus casillas!

_why, Why's (Poignant) Guide to Ruby

En este captulo, vamos a empezar a hacer cosas que realmente pueden ser llamadas
programacin. Vamos a ampliar nuestro conocimiento del lenguaje JavaScript, ms all de
los sustantivos y fragmentos de oraciones que hemos visto hasta ahora, hasta el punto en que
podamos expresar alguna prosa significativa.

Expresiones y declaraciones
En el Captulo 1, creamos algunos valores y despus les aplicamos operadores para obtener
nuevos valores. Crear valores de esta forma es una parte esencial de cada programa de
JavaScript, pero esto es slo una parte.

Un fragmento de cdigo que produce un valor es llamado una expresin. Cada valor que se
escribe literalmente (tal como 22 o "psicoanlisis") es una expresin. Una expresin entre
parntesis es tambin una expresin, como un operador binario aplicado a dos expresiones o
un operador unario aplicado a una expresion.

Esto muestra parte de la belleza de una interfaz basada en el lenguaje. Las expresiones se
pueden anidar en una forma muy similar a la forma de sub-frases en la que las lenguas
humanas son anidadas, y las sub-frases pueden contener sus propias sub-frases, etc. Esto nos
permite combinar expresiones para expresar clculos arbitrariamente complejos.

Si una expresin corresponde a un fragmento de frase, una declaracin en JavaScript


corresponde a una frase completa en un lenguaje humano. Un programa es simplemente una
lista de declaraciones.

El tipo ms simple de declaracin es una expresin con un punto y coma despus de ella.
Esto es un programa:

edit & run code by clicking it


1;
!false;

Sin embargo, es un programa intil. Una expresin puede estar presente para slo producir un
valor, que puede entonces ser utilizado por la expresin que la contiene. Una declaracin
existe por s sola y que equivale a algo slo si afecta al mundo. Podra mostrar algo en la
pantalla -que cuenta como cambiar el mundo_ o podra cambiar el estado interno de la
mquina de estados de manera que afectar las declaraciones que vienen despues de ella.
Estos cambios se llaman efectos colaterales. Las declaraciones en el ejemplo anterior solo
producen los valores 1 y verdadero y los desechan inmediatamente. Esto no deja ningn
cambio en el mundo en absoluto. Al ejecutar el programa, nada observable sucede.

En algunos casos, JavaScript te permite omitir el punto y coma al final de una declaracin. En
otros casos, tiene que estar all, o la siguiente linea ser tratada como parte de la misma
declaracin. Las reglas para cuando se puede omitir con seguridad son algo complejas y
propensas a errores. En este libro, cada declaracin que necesite un punto y coma siempre
ser terminada por un punto y coma4. Te recomiendo que hagas lo mismo en tus propios
programas, al menos hasta que hayas aprendido ms sobre sutilezas involucradas en omitir el
punto y coma.

Variables
Cmo mantiene un programa su estado interno? Cmo recuerda algo? Hemos visto cmo
producir nuevos valores de viejos valores, pero esto no cambia los valores antiguos, y el
nuevo valor tiene que ser inmediatamente utilizado o se disipar de nuevo. Para atrapar y
mantener los valores, JavaScript proporciona una cosa llamada variable.

var atrapado = 5 * 5;

Y eso nos da nuestra segunda clase de declaracin. La palabra especial(palabra clave o


keyword) var indica que esta frase va a definir una variable. Es seguida por el nombre de la
variable y, si queremos dar de inmediato un valor, con un operador de = y una expresin.

La declaracin anterior crea una variable llamada atrapado y se usa para retener el nmero
que se produce al multiplicar 5 por 5.

Despus de que una variable se ha definido, su nombre puede ser usado como una expresin.
El valor de esa expresin es el valor que la variable alberga actualmente. He aqu un ejemplo:
var diez = 10;
console.log(diez * diez);
// 100

Los nombres de variables pueden ser cualquier palabra que no sea una palabra clave (tal
como var). Estos no pueden incluir espacios. Los dgitos tambin pueden ser parte de la
variable nombre catch22 es un nombre vlido, por ejemplo-, pero el nombre no debe
comenzar con un dgito. Un nombre de variable no puede incluir puntuacin, a excepcin de
los caracteres $ y _.

(((variable,asigancin))Cuando una variable apunta a un valor, eso no quiere decir que est
ligada a ese valor para siempre. El operador = se puede utilizar en cualquier momento en
variables existentes para desconectarlas de su valor actual y apuntarlas a uno nuevo.

var tono = "claro";


console.log(tono);
// claro
tono = "oscuro";
console.log(tono);
// oscuro

Podras imaginar las variables como tentculos, en lugar de la cajas. Estas no contienen
valores; los agarran; dos variables pueden referirse al mismo valor. Un programa puede
acceder slo los valores que todava mantiene detenidos. Cuando necesitas recordar algo,
haces crecer un tentculo para agarrarlo o cambias unos de tus tentculos existentes para
agarrarlo.

Veamos un ejemplo. Para recordar el nmero de dlares que Luigi an te debe, se crea una
variable. Y luego, cuando te paga $35, le das a esta variable un valor nuevo.

var deudaDeLuigi = 140;


deudaDeLuigi = deudaDeLuigi - 35;
console.log(deudaDeLuigi);
// 105
Cuando se define una variable sin darle un valor, el tentculo no tiene nada que sostener, por
lo que termina en el aire. Si preguntas por el valor de una variable vaca, obtendrs el valor
undefined (indefinido).

Una sola declaracin var puede definir mltiples variables. Las definiciones deben estar
separadas por comas.

var uno = 1, dos = 2;


console.log(uno + dos);
// 3

Palabras clave y palabras reservadas


Palabras con un significado especial, como var, son palabras clave, y no pueden ser
utilizadas como nombres de variables. Tambin hay un nmero de palabras que son
reservadas para uso en futuras versiones de JavaScript. Estas tambin estn oficialmente no
permitidas como nombres de variables, aunque algunos entornos de JavaScript las permiten.
La lista completa de palabras clave y palabras reservadas es bastante larga.

break case catch class const continue debugger


default delete do else enum export extends false
finally for function if implements import in
instanceof interface let new null package private
protected public return static super switch this
throw true try typeof var void while with yield

No te preocupes por memorizarlas, pero recuerda que esto podra ser el problema cuando una
definicin de variable no funcione como se esperaba.

El entorno
La coleccin de variables y sus valores que existe en un momento dado se llama el entorno.
Cuando un programa se pone en marcha, este entorno no est vaco. Siempre contiene
variables que forman parte del lenguaje estndar, y la mayora del tiempo, contiene variables
que proporcionan formas de interactuar con el sistema que lo contiene. Por ejemplo, en un
navegador, existen variables y funciones para inspeccionar e influir en la pgina web cargada
en ese momento y leer entrada del ratn y del teclado.

Funciones
Una gran cantidad de los valores proporcionados en el entorno por defecto tienen el tipo
function. Una funcin(function) es un pedazo de programa encerrado en un valor. Tales
valores pueden ser aplicados con el fin de ejecutar el programa envuelto. Por ejemplo, en un
entorno de navegador, la variable alert contiene una funcin que muestra un pequeo
cuadro de dilogo con un mensaje. Se utiliza como sigue:

alert("Good Morning!");
La ejecucin de una funcin es denominada invocar, llamar, o aplicar la funcin. Puedes
llamar a una funcin poniendo parntesis despus de una expresin que produce un valor de
la funcin. Por lo general, se usa directamente el nombre de la variable que contiene la
funcin. Los valores entre parntesis se le pasan a el programa dentro de la funcin. En el
ejemplo, la funcin alert utiliza la cadena que le damos como el texto que se mostrar en el
cuadro de dilogo. Los valores dados a las funciones se denominan argumentos. La funcin
alert necesita solo uno, pero otras funciones pueden necesitar un nmero diferente o
diferentes tipos de argumentos.

La funcin console.log
La funcin alert puede ser til para imprimir cuando estamos experimentando, pero quitar
del camino todas esas pequeas ventanas puede desesperarte. En ejemplos pasados, hemos
usado console.log para devolver valores. La mayora sistemas JavaScript (incluyendo a
todos los navegadores web modernos y a Node.js) nos dan una funcin console.log que
imprime sus argumentos en algn dispositivo de salida de texto. En los navegadores la salida
queda en la consola de JavaScript. Esta parte del navegador est escondida por defecto, pero
la mayora de los navegadores la abren cuando presionas F12 o, en Mac, cuando presionas
Command-Option-I. Si esto no funciona, busca en los mens un item llamado "Consola Web"
o "Herramientas de Desarrollador".

Cuando corras los ejemplos, o tu propio cdigo, en las pginas de este libro, la salida de
console.log ser mostrada despus del ejemplo, en vez de en la consola de JavaScript del
navegador.

var x = 30;
console.log("el valor de x es", x);
// el valor de x es 30

Aunque los nombres de variable no pueden contener el caracter punto, console.log


claramente tiene uno. Esto es porque console.log no es una variable simple. Es en realidad
una expresin que trae la propiedad log del valor mantenido por la variable console.
Veremos que significa exactamente en el Captulo 4.

Valores de Retorno
Mostrar un cuadro de dilogo o escribir texto en la pantalla es un efecto secundario. Muchas
funciones son tiles porque producen valores, y en ese caso, no necesitan tener un efecto
secundario para ser tiles. Por ejemplo, la funcin Math.max toma un nmero indeterminado
de nmeros y regresa el ms grande.

console.log(Math.max(2, 4));
// 4
Cuando una funcin produce un valor, se dice que regresa ese valor. Cualquier cosa que
produce un valor es una expresin en JavaScript, lo que significa que puede ser usada dentro
de expresiones ms grandes. Aqu, una llamada a Math.min, que es lo opuesto a Math.max, es
usada como entrada de un operador de suma:

console.log(Math.min(2, 4) + 100);
// 102

El prximo captulo explica como escribir tus propias funciones.

Pedir informacin y confirmar


Los entornos de navegador tienen otras funciones ms all de alert para mostrar ventanas.
Puedes preguntar al usuario una cuestin estilo OK/Cancelar usando confirm. Esto regresa
un Booleano: true si el usuario hace click en OK y false si el usuario presiona en Cancelar.

confirm("Entonces, deberamos?");

La funcin prompt puede ser usada para hacer una pregunta "abierta". El primer argumento
es la pregunta, el segundo es un texto con el que usuario inicia. Se puede escribir una lnea de
texto en el cuadro de dilogo, y la funcin regresar este texto como una cadena.

prompt("Tell me everything you know.", "...");

Estas dos funciones no son usadas mucho en la programacin web moderna, principalmente
porque no tienes control sobre la forma en que las ventanas resultantes se vern, pero son
tiles para programas de prueba y experimentos.

Control de flujo
Cuando tu programa contiene ms de una sentencia, las sentencias son ejecutadas (fcil de
predecir), de arriba hacia abajo. Como un ejemplo bsico, este programa tiene dos sentencias.
La primera le pide un nmero al usuario, y la segunda, que se ejecuta despus, muestra el
cuadrado de ese nmero

var elNumero = Number(prompt("Dame un nmero", ""));


alert("T nmero es la raz cuadrada de " +
elNumero * elNumero);
La funcin Numero convierte un valor a un nmero. Necesitamos esa conversin porque el
resultado de prompt es un valor de cadena de texto (string), y queremos un nmero. Hay
funciones similares llamadas String y Boolean que convierten valores a estos tipos.

Aqu est la trivial representacin esquemtica de un flojo de control recto.

Ejecucin Condicional
Ejecutar sentencia en lnea recta no es la unica opcin que tenemos. Una alternativa es la
ejecucin condicional, en dondne escogemos entre dos rutas diferentes basados en un valor
Booleano, como el siguiente:

La ejecucin condicional se escribe con la palabra clave if en JavaScript. En el caso sencillo,


queremos que algo de cdigo se ejecute si, y slo si, cierta condicin se cumple. Por ejemplo,
en el programa previo, podramos querer mostrar el cuadrado de la entrada slo si la entrada
es un nmero.

var elNumero = Number(prompt("Dame un nmero", ""));


if (!isNaN(elNumero))
alert("Tu nmero es la raz cuadrada de " +
elNumero * elNumero);

Con esta modificacin, si le das "queso", no se mostrar ninguna salida.

La palabra clave if ejecuta o salta una sentencia dependiendo del valor de una expresin
Booleana. La expresin de decisin se escribe despus de la palabra clave, entre parntesis,
seguida por una sentencia a ejecutar.

La funcin isNaN es una funcin estndar de JavaScript que regresa true slo si el
argumento que le diste es NaN. Resulta que la funcin Number regresa NaN cuando le das una
cadena que no a menos que elNumero no sea un nmero, has esto.

A menudo no slo tendrs cdigo que se ejecute cuando una condicin sea verdadera, sino
tambin que maneje el otro caso. Este camino alternativo es representado por la segunda
flecha en el diagrama. La palabra clave else puede ser usada, junto con if, para crear dos
rutas de ejecucin separadas y alternativas.

var elNumero = Number(prompt("Dame un nmero", ""));


if (!isNaN(elNumero))
alert("Tu nmero es la raz cuadrada de " +
elNumero * elNumero);
else
alert("Hey. Por qu no me diste un nmero?");

Si tenemos ms de dos caminos a escoger, varios pares de if/else pueden ser


"encadenados". Aqu hay un ejemplo:

var num = Number(prompt("Dame un nmero", "0"));

if (num < 10)


alert("Chico");
else if (num < 100)
alert("Mediano");
else
alert("Grande");

El programa primero checar s num es menor que 10. Si lo es, escoge ese camino, muestra
"Chico" y termina. Si no lo es, toma el el branch else, que en s mismo contiene un segundo
if. Si la segunda condicin (< 100) se cumple, significa que el nmero est entre 10 y 100, y
se muestra "Mediano". Si no lo es, el segundo y ltimo else es escogido.

El diagrama de flujo para este programa es algo as:

bucles while y do
Piensa en un programa que imprima todos los nmeros primos del 1 al 12. Una manera de
escribirlo sera como sigue:

console.log(0);
console.log(2);
console.log(4);
console.log(6);
console.log(8);
console.log(10);
console.log(12);

Eso funciona, pero la idea de escribir un programa es trabajar menos, no ms. Si necesitamos
todos los nmeros menores que 1,000, lo anterior sera imposible de trabajar. Lo que
necesitamos es una forma de repetir algo de cdigo. Esta forma de control de flujo es llamada
bucle:
El control de flujo por bucles nos permite regresar a cierto punto en el progrma en el que
estuvimos antes y repetirlo con nuestro estado actual. Si combinamos esto con una variable
que mantenga una cuenta, podemos hacer algo como lo siguiente:

var numero = 0;
while (numero <= 12) {
console.log(numero);
numero = numero + 2;
}
// 0
// 2
// etcetera

Una sentencia que comienza con la palabra clave while crea un bucle. Despus de while
viene una expresin en parntesis y despus una sentencia, muy parecido a el if. El bucle
ejecuta la sentencia mientras la expresin produzca un valor que sea true cuando se
convierta a un tipo Booleano.

En este bucle, queremos tanto imprimir el nmero como sumar dos a nuestra variable.
Cuando necesitamos ejecutar mltiples sentencias dentro de un bucle, lo encerramos en llaves
({ y }). Las llaves hacen por las sentencias lo que los parntesis hacen por las expresiones: las
agrupan, hacindolos valer por una sola sentencia. Una secuencia de sentencias encerradas en
llaves es llamada un bloque.

Muchos programadores de JavaScript encierran cada cuerpo de un bucle o if en llaves. Lo


hacen en nombre de la consistencia y para evitar tener que aadir o quitar las llaves cuando el
nmero de sentencias en el cuerpo cambie. En este libro escribir la mayora de los cuerpos
de una sola sentencia sin bloques, porque valoro la brevedad. T eres libre de usar el estilo
que prefieras.

La variable nmero demuestra la forma en que una variable puede dar seguimiento al
progreso de un programa. Cada vez que el bucle se repite, numero se incrementa en 2.
Entonces, al principio de cada repeticin. es comparada con el nmero 12 para decidir si el
programa ha hecho todo el trabajo que tena que hacer.

Como un ejemplo que hace realmente algo til, podemos escribir un programa que calcula y
mustra el valor de 2^10 (dos a la dcima potencia). Usamos dos varianles: una para mantener
el resultado y una para contar cuantas veces hemos multiplicado este resultado por dos. El
bucle verifica si la segunda variable ha llegado a 10 y entonces actualiza las dos variables.

var resultado = 1;
var contador = 0;
while (contador < 10) {
resultado = resultado * 2;
contador = contador + 1;
}
console.log(resultado);
// 1024

El contador pudo tambin empezar en 1 y verificar que <=10, pero, por razones que se
aclararn en el Captulo 4, es una buena idea acostumbrarse a contar desde 0.

El bucle do es una estructura de control similar al bucle while. Se diferencia en slo un


punto: un bucle do siempre ejecuta su cuerpo por lo menos una vez y empieza a verificar si
debera para slo despus de la primera ejecucin. Para reflejar esto, la condicin aprece
despus del cuerpo del bucle:

do {
var tuNombre = prompt("Quin eres?");
} while (!tuNombre);
console.log(tuNombre);

Este programa te obligar a introducir un nombre. Preguntar una y otra vez hasta que
obtenga algo que no sea una cadena vaca. Aplicar el operador ! convierte un valor a
Booleano negndolo y todas las cadenas excepto "" se convierten a true. Esto significa qie
el bucle contina corriendo hasta que des un nombre que no sea una cadena vaca.

Indentando cdigo
Probablemente has notado los espacios que pongo en frente de algunas sentencias. En
JavaScript, no son requeridos, la computadora aceptar el programa bien sin ellos. De hecho,
incluso los saltos de lnea en los programas son opcionales. Puedes escribir un programa en
una sola lnea si as lo prefieres. El rol de la indentacin dentro de los bloques es hacer notar
la estructura del cdigo. En cdigo complejo, en dnde nuevos bloques son abiertos dentro de
otros bloques, puede ser difcil de ver en dnde termina uno y empieza otro. Con la
indentacin correcta, la forma visual del programa se corresponde con la forma de los
bloques dentro de l. A m me gusta usar dos espacios por cada bloque abierto, pero los
gustos varan; algunas personas usan cuatro espacios, y algunas otras usan el carcter tab.

Bucles for
Muchos bucles siguen el patrn de los ejemplos previos del while. Primero, una variable
contador es creada para dar seguimiento al progreso del bucle. Entonces viene el bucle
while, cuya expresin condicional normamelte verifica si el contador ha alcanzafo cierto
lmite. El final del cuerpo del bucle, el contador es actualizado para dar seguimiento al
progreso.

Debido a que este patrn es tan comn, JavaScript y otros lenguajes similares proveen una
versin un poco ms corta y ms completa, el bucle for.

for (var numero = 0; numero <= 12; numero = numero + 2)


console.log(numero);
// 0
// 2
// etc.
Este programa es exactamente equivalente a el ejemplo previo de impresin de nmeros
pares. El nico cambio es que todas las sentencias que estn relacionadas con el "estado" del
bucle estn agrupadas.

Los parntesis despus de una palabra clave for tienen que contener dos punto y coma. La
parte que est antes del primer punto y coma inicializa el bucle, normalmente al definir una
variable. La segunda parte es la expresin que verifica si el bucle tiene que continuar. La
parte final actualiza el estado del bucle antes de cada iteracin. En la mayora de los casos,
esto es ms corto y claro que una construccin con while.

Aqu est un cdigo que calcula 210, usando el for en vez del while.

var resultado = 1;
for (var contador = 0; contador < 10; contador = contador + 1)
resultado = resultado * 2;
console.log(resultado);
// 1024

Nota que incluso aunque no se abri ningn bloque con un {, la sentencia del bucle est
indentada dos espacios para dejar claro que "pertenece" al la lnea que est antes de ella.

Forzando la salida de un bucle


Hacer que la condicin del bucle produzca false no es la nica forma de que un bucle
termine. Existe una una sentencia especial llamada break que tiene el efecto de salir
inmediatamente del bucle que la est encerrando.

El siguiente programa ilustra la sentencia break. Encuentra el primer nmero que es ms


grande o igual que 20 y divisible por 7.

for (var actual = 20; ; actual++) {


if (actual % 7 == 0)
break;
}
console.log(actual);
// 21

Usar el operador de sobrante o mdulo (%) es una forma fcil de probar si un nmero es
divisible por otro. Si lo es, el sobrante de la divisin es cero.

La construccin for en este ejemplo no tiene la parte que verifica si el bucle debe terminar.
Esto significa que el loop nunca terminar hasta que la sentencia break que est adentro sea
ejecutada.

Si dejaras afuera esa sentencia break o accidentalmente escribieras una condicin que
siempre produzca true, tu programa se quedara atorado en un bucle infinito. Un programa
que se queda en un bucle infinito nunca terminar de correr, y eso usualmente es malo.

Si creas un bucle infinito en uno de los ejemplos de estas pginas, usualmente se te


preguntar si quieres detener el script despus de unos cuantos segundos. Si eso falla, tendrs
que cerrar la pestaa en la que ests trabajando, o, en otros nevagadores, cerrar el navegador
entero para recuperarte.

La palabra clave continue es similar a break en que influencia el progreso del bucle.
Cuando se encuentra continue en el cuerpo de un bucle, el control sale del curpo del bucle
inmediatamente y contina en la prxima iteracin del bucle.

Actualizando variables sucintamente


Especialmente cuando estamos en un bucle, un programa necesita "actualizar" una variable
para mantener un valor basado en el valor previo de esa variable.

contador = contador + 1;

JavaScript nos da un atajo para esto:

contador += 1;

Atajos similares funcionan igual para muchos otros operadores, como resultado *= 2 para
duplicar resultado o counter -= 1 para restar uno.

Esto nos permite acortar nuestro ejemplo de la cuenta un poco ms.

for (var numero = 0; numero <= 12; numero += 2)


console.log(numero);

Para contador += 1 y contador -= 1, hay incluso equivalentes ms cortos: contador+= y


contador--.

Despachando sobre un valor con switch


Es comn ver cdigo as:

if (variable == "valor1") accion1();


else if (variable == "valor2") accion2();
else if (variable == "valor3") accion3();
else accionDefault();

Existe una estructura llamada switch que est hecha para resolver este "despachado" de un
modo ms directo. Desafortunadamente, la sintaxis que JavaScript usa para esto (que es
heredada de la lnea de lenguajes de programacin de C o Java) es un poco incmoda; una
cadena de sentencias if a menudo luce mejor. Aqu hay un ejemplo:

switch (prompt("Cmo est el clima?")) {


case "lluvioso":
console.log("Recuerda llevar un paraguas.");
break;
case "soleado":
console.log("Viste ligero.");
case "nublado":
console.log("Sal a la calle.");
break;
default:
console.log("Tipo de Clima desconocido.");
break;
}

Puedes poner cualquier cantidad de etiquetas case dentro del bloque switch abierto. El
programa saltar a la etiqueta que corresponda al valor que se le dio al switch o a default si
no se encuentra valor que corresponda. Se empiezan a ejecutar las sentencias desde ah,
incluso si estn bajo otra etiqueta, hasta que se llegue a una sentencia breal. En algunos
casos, como en el caso de "soleado" en el ejemplo, esto puede ser usado para compartir
cdigo entre casos (recomienda salir a la calle tanto para clima soleado como para nublado).
Pero cuidado: es fcil olvidar el break, lo cul causar que el programa ejecute cdigo que
no quieres que se ejecute.

Capitalizacin
Los nombres de variable no pueden contener espacios, pero an as es til ocupar varias
palabras para describir claramente lo que esa variable representa. Estas son tus elecciones
para escribir un nombre de variable con varias palabras:

toortuguitadifusa
tortuguita_difusa
TortuguitaDifusa
tortuguitaDifusa

El primer estilo puede ser difcil de leer. Personalmente, me gusta como luce con los guiones
bajos, aunque es un poco difcil de escribir. Las funciones estndar de JavaScript, y la
mayora de los programadores de JavaScript, siguen el ltimo estilo; capitalizan cada palabra
excepto la primera. No es difcil acostumbrarse a pequeas cosas como esas y cdigo con
estilos de nombrado mezclados puede ser desagradable de leer, as que simplemente
seguiremos esta convencin.

En algunos casos, como en el de la funcin Number, la primera letra de una variable tambin
es mayscula. Esto fue hecho para marcar la funcin como un constructor. Lo que es un
constructor, quedar claro en el Captulo 6. Por ahora, lo importante es no preocuparse por
esta aparente falta de consistencia.

Comentarios
A menudo, el cdigo por s mismo no transmite toda la informacin que quieres que un
programa transmita para los lectores humanos, o la transmite de un modo tan crptico que las
personas podran no entenderlo. En otras ocasiones, simplemente puedieras sentirte potico o
quisieras incluir algunos pensamientos como parte del programa. Para eso son los
comentarios.

Un comentario es una pieza de texto que es parte del programa pero es completamente
ignorado por la computadora. JavaScript tiene dos formas para escribir comentarios. Para
escribir un comentario de una lnea, puedes usar dos diagonales (//) con el texto despus.

var balandeDeCuenta = calcularBalance(cuenta);


// Es en un claro de bosque donde canta un ro.
balandeDeCuenta.ajustar();
// Cuelgan enloquecidamente de la hierbas harapos
var reporte = new Reporte();
// De plata; donde el sol de la orgullosa montaa
agregaAReporte(balandeDeCuenta, reporte);
// Luce: Es un pequeo valle espumoso de luz.

Un // slo es vlido hasta el fin de la lnea. Una seccin de texto entre /* y */ ser ignorada,
sin importar si contiene saltos de lnea. Esto es til a menudo para aadir bloques de
informacin acerca de un archivo o un pedazo de un programa.

/*
Primero, encontr este nmero garabateado en la parte trasera de
una de mis libretas hace unos cuantos aos. Desde entonces,
a menudo ha aparecido de la nada, apareciendo en nmeros de
telfono y nmeros de serie de productos que he comprado.
Obviamente le gusto, as que decid conservarlo.
*/
var miNumero = 11213;

Sumario
Ya sabes que un programa est construido de sentencias, que a su vez pueden contener ms
sentencias algunas veces. Las sentencias normalmente contienen expresiones, que a su vez
pueden contener expresiones ms pequeas.

Colocar una sentencia tras otras te da un programa que es ejecutado de arriba hacia abajo.
Puedes introducir disturbios en el flujo de control usando sentencias condicionales (if, else,
and switch) y de bucle (while, do, and for).

Las variables pueden ser usadas para guardar piezas de informacin bajo un nombre, y son
tiles para llevar registro del estado del programa. El entorno es el conjunto de variables que
estn definidas. Los sistemas de JavaScript siempre colocan varias variables estndar tiles
en tu entorno.

Las funciones son valores especiales que encapsulan un pedazo del programa. Puedes invocar
una escribiendo nombreDeLaFuncion(argumento1, argumento2). Esta llamada a la
funcin es una expresin, y podra producir un valor.

Ejercicios
Si no ests seguro de cmo probar tus soluciones a los ejercicios, visita la introduccin

Cada ejercicio empieza con una descripcin del problema. Lela e intenta resolver el
ejercicio. Si tienes problemas, considera leer las pistas despus del ejercicio. Las soluciones
completas a los ejercicios no estn incluidas en este libro, pero puedes encontrarlas en lnea
en eloquentjavascript.net/code. Si quieres aprender a partir de los ejercicios, recomiendo
mirar las soluciones slo despus de que hayas resuelto el ejercicio, o al menos lo hayas
atacado con el tiempo y esfuerzo suficientes para tener un pequeo dolor de cabeza.

Iterando un tringulo
Escribe un bucle que haga siete llamadas a console.log para producir el siguiente tringulo:

#
##
###
####
#####
######
#######

Puede ser til saber que puedes encontrar la longitud de una cadena de texto escribiendo
.length despus de ella.

var abc = "abc";


console.log(abc.length);
// 3

La mayora de los ejercicios contienen una porcin de cdigo que puedes modificar para
resolver el ejercicio. Recuerda que puedes hacer click en los bloques de cdigo para editarlos.

// Tu cdigo aqu:

FizzBuzz

Escribe un programa que use console.log para imprimir todos los nmeros del 1 al 100, con
dos excepciones. Para nmeros divisibles por 3, imprime "Fizz" en vez del nmero y para
nmeros divisibles por 5 (pero no por 3), imprime "Buzz".

Cuando tengas eso funcionando, modifica tu programa para imprimir "FizzBuzz", para
nmeros que sean divisibles tanto por 3 como por 5 (y que siga imprimiendo "Fizz" o
"Buzz" para nmeros divisibles por slo uno de ellos).

(Esta es en realidad una pregunta de entrevista de la que se dice que elimina a un porcentaje
significativo de programadores candidatos. As que si lo resolviste, puedes sentirte bien
contigo mismo.)

// Tu cdigo aqu:

Tablero de Ajedrez

Escribe un programa que cree un cadena de texto que represente una cuadrcula de 8x8,
usando el salto de lnea como separador. En cada posicin de la cuadrcula est o un espacio o
un carcter "#". Los caracteres deberan formar un tablero de ajedrez.

Pasar esta cadena a console.log debera mostrar algo como esto:

# # # #
# # # #
# # # #
# # # #
# # # #
# # # #
# # # #
# # # #

Cuando tengas un programa que genere este patrn, defina una variable tamano = 8 y
cambia el programa de tal manera que trabaje para cualquier tamano, produciendo una
cuadrcula del ancho y alto dado.

Captulo 3
Funciones
La gente piensa que las ciencias de la computacin son las artes de los genios pero la realidad
es lo contrario, es slo un montn de gente haciendo cosas que se construyen sobre otras,
como una pared de piedras muy pequeas.

Donald Knuth

Has visto valores funcin, como alert, y cmo llamarlos. Las funciones son el pan de cada
da en la programacin en JavaScript. El concepto de de envolver una porcin del programa
en un valor(variable) tiene muchos usos. Es una herramienta para estructurar programas ms
grandes, para reducir la repeticin, para asociar nombres con subprogramas, y para separar
estos programas de los dems.

La aplicacin ms obvia de las funciones es la de definir un nuevo vocabulario. Crear nuevas


palabras en la prosa regular en lenguaje humano es usualmente un mal estilo. Pero en
programacin, es indispensable.

Un adulto promedio tiene unas 20,000 palabras en su vocabulario. Pocos lenguajes de


programacin tienen 20,000 comandos incorporados. Y el vocabulario que est disponible
tiende a ser definido de forma ms precisa, as que es menos flexible que en un lenguaje
humano. En consecuencia, usualmente tenemos que aadir algo de nuestro propio
vocabulario para evitar repetirnos demasiado.

Definiendo una funcin


Una definicin de una funcin es slo una definicin regular de una variable cuando ocurre
que el valor dado a la variable es una funcin. Por ejemplo, el siguiente cdigo define la
variable cuadrado para referirse a la funcin que produce el cuadrado de un nmero dado:

edit & run code by clicking it


var cuadrado = function(x) {
return x * x;
};

console.log(cuadrado(12));
// 144
Una funcin es creada por una expresin que empieza con la palbra reservada function. Las
funciones tienen un conjunto de parametros (en este caso slo x) y un cuerpo, que contiene
las sentencias que sern ejecutadas cuando la funcin sea llamada. El cuerpo de la funcin
tiene que estar siempre encerrado en llaves, incluso cuando consista de una sola instruccin
(como en el ejemplo previo).

Una funcin puede tener varios parmetros o puede no tener ninguno. En el siguiente ejemplo
hazRuido no tiene parmetros, mientras que potencia tiene dos:

var hazRuido = function() {


console.log("Pling!");
};

hazRuido();
// Pling!

var potencia = function(base, exponente) {


var resultado = 1;
for (var cuenta = 0; cuenta < exponente; cuenta++)
resultado *= base;
return resultado;
};

console.log(potencia(2, 10));
// 1024

Algunas funciones producen un valor, como potencia y cuadrado, y algunas no, como
hazRuido, la cul produce slo un efecto secundario. Una sentencia return determina el
valor que una funcin regresa. Cuando el control pasa a esta sentencia, inmediatamente salta
fuera de la funcin actual y pasa el valor retornado a la cdigo que llam la funcin. La
palabra reservada return sin una expresin despus de ella har que la funcin devuelva
undefined.

Parmetros y mbitos
Los parametros para una funcin se comportan como variables normales, pero su valor inicial
esta dado por quien llama a la funcin, no por el cdigo mismo de la funcin.

Un propiedad importante de las funciones es que las varibales creades dentro de ellas,
incluyendo sus parmetros, son locales para la funcin. Este significa, por ejemplo, que la
varibale resultado en el ejemplo potencia ser creada de nuvo cada vez que la funcin es
llamada, y estas instancias separadas no interfieren entre ellas.

Esta "localidad" de las variables aplica slo a los parmetros y variables declaradas con la
palabra reservada var dentro del cuerpo de la funcin. Las variables declaradas duera de
cualquier funcin son llamadas globales, porque son visibles a travs de todo el programa. Es
posible acceder a estas variables desde dentro de una funcin, mientras no hayas declarado
una variable local con el mismo nombre.

El siguiente cdigo demuestra eso. Define y llama dos funciones que asignan un valor a la
variable x. La primera declara la variable como local y de esta manera cambia nicamente la
variable que cre. La segunda no declara x localmente, as que la x dentro de ella hace
referencia a la x definida al principio del ejemplo.

var x = "fuera";

var f1 = function() {
var x = "dentro de f1";
};
f1();
console.log(x);
// fuera

var f2 = function() {
x = "dentro de f2";
};
f2();
console.log(x);
// dentro de f2

Este comportamiento ayuda a prevenir interferencia accidental entre las funciones. Si todas
las variables fueran compartidas por el programa entero, sera muy difcil asegurarse de que
algn nombre no fue usado para dos propsitos diferentes. Y si reusaste un nombre de
variable, podras ver efectos extraos de cdigo no relacionado causando problemas en el
valor de tu variable. Al tratar tus variables locales a la funcin, el lenguaje hace posible leer y
entender las funciones como un pequeos universos, sin tener que preocuparse de todo el
cdigo a la vez.

mbitos Anidados
JavaScript distingue nos solo entre variables globales y locales Las funciones pueden ser
creadas dentro de otras funciones, produciendo distindos grados de localidad.

Por ejemplo, esta funcin sin sentido, tiene dos funciones dentro de ella:

var landscape = function() {


var resultado = "";
var meseta = function(tamano) {
for (var cuenta = 0; cuenta < size; cuenta++)
resultado += "_";
};
var montana = function(tamano) {
result += "/";
for (var cuenta = 0; cuenta < size; cuenta++)
resultado += "'";
resultado += "\\";
};

meseta(3);
montana(4);
meseta(6);
montana(1);
meseta(1);
return resultado;
};

console.log(landscape());
// ___/''''\______/'\_

Las funciones meseta y montana pueden "ver" la variable llamada resultado, debido a que
estn dentro de la funcin que la define. Pero no pueden ver la variable cuenta entre ellas,
porque estn definidas fuera del mbito de la otra. El entorno fuera de la funcin paisaje no
puede acceder a ninguna de las variables definidas dentro de paisaje.

En pocas palabras, cada mbito local tambin puede ver los mbitos locales que lo contienen.
El conjunto de variables visible dentro de la funcin es determinado por el lugar de la funcin
en el texto del programa. Todas las variables de los bloques que envuelven una la definicin
de una funcin son visibles para ella, esto es, en todos los cuerpos de las funciones que la
envuelven y las correspondientes al nivel superior(mbito global). Este manera de resolver la
visibilidad de las variables es llamada definicin lxica de mbito(lexical scoping).

Las personas que tienen experiencia con otros lenguajes de programcin podran esperar que
cualquier bloque entre llaves produzca un nuevo entorno local. Pero en JavaScript, las
funciones son lo nico que crea un nuevo mbito. Pero puedes usar bloques independientes.

var algo = 1;
{
var algo = 2;
// Haz algo con la variable...
}
// Fuera del bloque...

Pero la variable algo dentro del bloque se refiere a la misma variable que la que est fuera
del bloque. De hecho, aunque bloques como este son permitidos, slo son tiles para agrupar
el cuerpo de las sentencias if o de los bucles.

Si esto te parece raro, no eres el nico. La prxima versin de JavaScript introducir una
palabra reservada let, que trabaja como var pero crea una variable que es local para el
bloque en el que est, no para la funcin.

Funciones como valores


Las variables de funcin normalmente actan como nombres para una parte especfica del
programa. Esa variables es definida una vez y nunca cambiada. Esto hace fcil empezar a
confundir la funcin con su nombre.

Pero son diferentes entre s. Un valor funcin puede hacer todo los que los otros valores
pueden hacer; lo puedes usar en expresiones arbitrarias, no slo llamarlos. Es posible guardar
la funcin en un nuevo lugar, pasar como argumento a otra funcin, etc. De manera parecida,
la variable que contiene una funcin sigue siendo una variable normal a la que se le puede
asignar otro valor, como sigue:

var lanzarMisiles = function(valor) {


sistemaDeMisiles.lanzar("ahora");
};
if (modoSeguro)
lanzarMisiles = function(valor) {/* No hacer nada. */};
En Captulo 5, hablaremos de las maravillosas cosas que puedes hacer al pasar valores
funcin a otras funciones.

Notacin de Declaracin
Existe una forma ms corta de decir var square = function. La palabra reservada
function puede ser usada al principio de una sentencia, como sigue:

function cuadrado(x) {
return x * x;
}

Esta es una declaracin de funcin. La expresin define la variable cuadrado y la apunta a la


funcin dada. Hasta aqu todo bien. sin embargo, hay una pequea sutileza con esta forma de
definicin.

console.log("El futuro dice: ", futuro());

function future() {
return "Seguimos sin tener carros voladores.";
}

Este cdigo funciona aunque la funcin est definida debajo del cdigo que la usa. Esto es
debido a que las declaraciones de funcin no toman parte en el flujo de control regular de
arriba hacia abajo. Son movidas conceptualmente a la parte superior de su mbito y pueden
ser usadas por todo el cdigo en ese mbito. Esto es til algunas veces porque nos da la
libertad de organizar el cdigo de una manera parezca significativa sin preocuparnos por
definir todas las funciones antes de su primer uso.

Qu pasa cuando pones una declaracin de funcin dentro de un bloque condicional (if) o
dentro de un bucle? Bueno, mejor no lo hagas. Diferentes plataformas de JavaScript en
diferentes navegadores hacen diferentes cosas tradicionalmente en esa situacin, y el ltimo
estndar de hecho lo prohibe. Si quieres que tus programas sean consistentes, usa las
sentencia de declaracin de funcin en el bloque ms externo de tu funcin o programa.

function ejemplo() {
function a() {} // Bien
if (alguna_condicion) {
function b() {} // Peligro!
}
}

La pila de llamadas
Ser til mirar ms de cerca la forma en que el control se mueve a travs de las funciones.
Aqu hay un simple programa que hace unas cuantas llamadas a funciones.

function saluda(a_quien) {
console.log("Hola " + a_quien);
}
saluda("Harry");
console.log("Adis");
(((console.log))Una ejecucin de este programa va ms o menos as: la llamada a saluda
causa que el control pase al inicio de esa funcin (lnea 2). Esta llama a control.log (una
funcin includa en los navegadores), que toma el control, hace su trabajo, y devuelve el
control a la lnea 2. Despus, se alcanza el dinal de la funcin saluda, as que se regresa al
lugar en dnde se llam, en la lnea 4. La lnea siguiente llama a console.log otra vez.

Podemos mostrar el flujo de control esquemticamente as:

raz
saluda
console.log
saluda
raz
console.log
raz

Debido a que una funcin tiene que saltar de regreso al lugar en que fue llamada cuando
termine, la computadora debe recordar el contexto en el que fue llamada. En un caso,
console.log tiene que regresar a la funcin saluda. En el otro caso salta al final del
programa.

El lugar en el que la computadora guarda este context es la pila de llamadas. Cada vez que
una funcin es llamada, el contexto actual es puesto en la parte superior de esta pila. Cuando
la funcin retorne, remueve el contexto superior de la "pila" y lo usa para continuar la
ejecucin.

Guardar esta pila requiere espacio en la memoria de la coputadora. Cuando la pila se hace
demasiado grande la computadora mostrar un error parecido a "out of stack space" (sin
espacio en la en la pila) o "too much recursion" (demasiada recursin). El siguiente cdigo lo
ilustra al preguntarle algo realmente difcil a la computadora, lo que causa un ir y venir
infinito entre funciones. Ms bien, sera infinito, si la computadora tuviera una pila infinita.
Como son las cosas, nos quedaremos sin espacio, o "volaremos la pila".

function gallina() {
return huevo();
}
function huevo() {
return gallina();
}
console.log(gallina() + " fue primero");
// ??

Argumentos Opcionales
El siguiente cdigo es permitido y se ejecuta sin ningn problema:

alert("Hola", "Buenas Noches", "Cmo ests?");

La funcin alert oficialmente acepta slo un argumento. An as, cuando la llamas como
aqu, no se queja. Simplemente ignora los otros argumentos y te muestra "Hola".
JavaScript es extremadamente abierta de mente acerca del nmero de argumentos que le
pasas a una funcin. S le pasas demasiados, loa argumentos extra son ignorados. Si le pasas
muy pocos, los parmetros quie faltan simplemente son asignados a undefined.

El lado malo de esto es que es posible, probable a menudo, que le pasars accidentalmente un
nmero incorrecto de argumentos a las funciones y nadie te avisar.

El lado bueno de este comportamiento es que puede ser usado para tener una funcin que
tome parmetros "opcionales". Por ejemplo, la siguiente versin de potencia puede ser
llamada con dos argumentos o uno solo, caso en el que el exponente se asume como dos, y la
funcin se comporta como cuadrado.

function potencia(base, exponente) {


if (exponente == undefined)
exponente = 2;
var resultado = 1;
for (var cuenta = 0; cuenta < exponente; cuenta++)
resultado *= base;
return resultado;
}

console.log(potencia(4));
// 16
console.log(potencia(4, 3));
// 64

En el prximo captulo, veremos una forma en la que el cuerpo de una funcin puede
obetener la lista exacta de argumentos que se le pasaron. Esto es til porque permite a una
funcin aceptar un nmero indeterminado de argumentos. Por ejemplo, console.log hace
uso de esto: imprime todos los valores que se le pasaron.

console.log("R", 2, "D", 2);


// R 2 D 2

Closure
La habilidad de tratar Funciones como valores, combinada con el hecho de que las varibles
locales son "re-creadas" cada vez que una funcin es llamada, saca a la luz una pregunta
interesante. Qu pasa con las variables locales cuando la funcin que las cre ya no est
activa?

El siguiente cdigo muestra un ejemplo de esto. Define una funncin, envuelveValor, que
crea una variable local. Despus devuelve una funcin que accede y devuelve esta variable
local.

function envuelveValor(n) {
var variableLocal = n;
return function() { return variableLocal; };
}

var envoltura1 = envuelveValor(1);


var envoltura2 = envuelveValor(2);
console.log(envoltura11());
// 1
console.log(envoltura2());
// 2

Esto est permitido y funciona como esperaras; la variable todava puede leerse. De hecho,
mltiples instancias de las variables pueden existir al mismo tiempo, lo que es otra buena
ilustracin del concepto de que las variables locales son re-creadas realmente para cada
llamada; diferentes llamadas no pueden afectar otras variables locales.

Esta caracterstica -ser capaces de hacer referencia a una instancia local de varables en un
funcin que las encierra- se llama closure. Una funcin que "encapsula" algunas variables
locales es llamada una closure. Este comportamiento no slo te libera de preocuparte de los
tiempos de vida de las variables, adems permite algunos usos creativos de las funciones.

Con un pequeo cambio, podemos podemos hacer del ejemplo anterior funciones que
multiplicquen por un nmero arbitrario.

function multiplicador(factor) {
return function(numero) {
return numero * factor;
};
}

var doble = multiplicador(2);


console.log(doble(5));
// 10

La variable explcita variableLocal de la funcin envuelveValor en el ejemplo previo no


es necesaria porque un parmetro en s mismo es una variable local.

Concebir los parmetros de esta forma requiere algo de prctica. Un buen modelo mental es
pensar en la palabra clave function como si "congelara" el cdigo que est dentro de ella en
un paquete(el valor funcin). As, cuando leas return function(...){...}, piensa en que
esto regresa un acceso a un conjunto de clculos, congelados para uso posterior.

En el ejemplo, multiplicador regresa un pedazo congelado de cdigo que se guarda en la


variable doble. La ltima lnea entonces llama el valor guardado en esta variable, haciendo
que el cdigo congelado (return numero * factor;) se active. Este todava tiene acceso a
la variable factor de la llamada a multiplicador que lo cre, y adems obtiene acceso al
argumento que se pasa cuando se activa el cdigo, 5, a travs de su parmetro numero.

Recursin
Es perfectamente correcto que una funcin se llame a s misma, mientras tenga cuidado de no
desbordar la pila. Una funcin que se llama a s misma se llama recursiva. La recursin
permite que algunas funciones se escriban con un estilo diferente. Tomemos por ejemplo, esta
implementacin alternativa de potencia:

function potencia(base, exponente) {


if (exponente == 0)
return 1;
else
return base * potencia(base, exponente - 1);
}

console.log(potencia(2, 3));
// 8

Esto es ms cercano al modo en que los matemticos definen la potenciacin y describe el


concepto de un modo ms elegante que la variante que lo hace con bucles. La funcin se
llama a s misma varias veces con diferentes argumentos para conseguir la multiplicacin
repetida.

Pero esta implementacin tiene un problema importante: en implementaciones tpicas de


JavaScript, es cerca de 10 veces ms lenta que la versin con bucles. Correr a travs de un
siple bucle es ms barato que llamar a una funcin muchas veces.

El dilema de velocidad contra elegancia es interesante. Puedes verlo como un continuum


entre amigabilidad-humano y amigabilidad-mquina. Casi cualquier programa puede hacerse
ms rpido hacindolo ms grande y convolucionado. El programador debe de decidir el
balance apropiado.

En el caso de la funcin anterior potencia la poco elegante versin(iterativa) es an bastante


simple y fcil de leer. No tiene mucho sentido reemplazarla con la versin recursiva. A
menudo, sin embargo, un programa tiene conceptos tan complejos que sacrificar un poco de
eficiencia para hacer el programa ms claro se vuelve una opcin atractiva.

La regla bsica, que ha sido repetida por muchos programadores y con la cul concuerdo de
todo corazn, es no preocuparse por la eficiencia hasta que ests seguro que el programa es
demasiado lento. Si lo es, busca las partes que estn abarcando la mayora del tiempo y
empieza a cambiar elegancia por eficiencia en esas partes.

Por supuesto, esta regla no significa que debas ignorar el rendimiento completamente. En
muchos casos, como en la funcin potencia, no se gana demasiada simplicidad de la
solucin "elegante". Y algunas veces un programador experimentado puede inmediatamente
que un soulucin simple nunca va a ser lo suficientemente rpida.

La razn por la que estoy hablando tanto de esto es que muchos programadores novatos se
enfocan fanticamente en la eficiencia, incluso en los detalles ms pequeos. El resultado son
programas ms grandes, ms complicados y a menudo menos correctos, que toman ms
tiempo en escribirse que sus equivalentes ms sencillos y que generalmente corren solo un
poco ms rpido.

Pero la recursin no es siempre slo una alternativa menos eficiente a los bucles. Alguos
problemas son mucho ms fciles de resolver con recursin que con bucles. La mayora de
estos son problemas que requieren explorar o procesar varias "ramas", cada una de las cules
se puede ramificar otra vez.

Considera el siguente rompecabezas: empezando por el nmero 1 y aadiendo repetidamente


5 o multiplicndolo por 3, un nmero infinito de nuevos nmeros puede ser producido.
Cmo escribiras una funcin que, dado un nmero, trate de encontrar una secuencia de
sumas y multipliclaciones que producen ese nmero? Por ejemplo, el nmero 13 puede ser
producido al multiplicar por 3 primero y despus sumar 5 dos veces, mientras que el nmero
15 no puede ser producido.
Aqu hay una solucin recursiva:

function encontrarSolucion(objetivo) {
function encontrar(inicio, historia) {
if (inicio == objetivo)
return historia;
else if (inicio > objetivo)
return null;
else
return encontrar(inicio + 5, "(" + historia + " + 5)") ||
encontrar(inicio * 3, "(" + historia + " * 3)");
}
return encontrar(1, "1");
}

console.log(encontrarSolucion(24));
// (((1 * 3) + 5) * 3)

Nota que este programa no encuentra necesariamente la ruta ms corta de operaciones. Se


satisface cuando cuentre cualquier sequencia.

No necesariamente espero que veas como este trabaja esto inmediatamente. Pero trabajemos
en eso, porque es un gran ejercicio en el pensamiento recursivo.

La funcin interna encontrar hace la recursividad. Toma dos argumentos el nmero actual
y una cadena que registra como hemos alcanzado el nmero y regresa o una cadena que
muestra como llegar al objetivo o null.

Para hacer esto la funcin realiza una de tres acciones. Si el nmero actual es el nmero
objetivo, entonces la historia actual es una forma de alcanzar este objetivo, as que
siemplemente es devuelta. Si el nmero actual es mayot que el nmero objetivo, no tiene
sentido seguir haciendo ms exploraciones porque tanto sumar como multiplicar slo har
mayor el nmero. Y finalmente, si estamos todava debajo del objetivo la funcin prueba los
dos caminos posibles que empiezan con el nmero actual llamndose dos veces a s misma,
una vez por cada uno de los pasos permitos. Si la primera llamada regresa cualquier cosa que
no sea null, se devuelve como resultado. De otra forma, la segunda opcin es la que se
devuelve, independientemente de si produce una cadena o null.

Para entender mejor como esta funcin produce el efecto que estamos buscando, miremos a
todas las llamadas a encontrar que se hacen cuando estamos buscando la solucin para el
nmero 13.

encuentra(1, "1")
encuentra(6, "(1 + 5)")
encuentra(11, "((1 + 5) + 5)")
encuentra(16, "(((1 + 5) + 5) + 5)")
demasiado grande
encuentra(33, "(((1 + 5) + 5) * 3)")
demasiado grande
encuentra(18, "((1 + 5) * 3)")
demasiado grande
encuentra(3, "(1 * 3)")
encuentra(8, "((1 * 3) + 5)")
encuentra(13, "(((1 * 3) + 5) + 5)")
Encontrado!
El sangrado (indentacin) indica la profundidad en la pila de llamadas. La primera vez que
encuentra es llamada, se llama dos veces a s misma para explorar las soluciones que
empiezan con (1 + 5) y (1 * 3). La primera llamada intenta encontrar una solucin que
empiece con (1 + 5) y, usando la recursin, explora todas las solucines que produzcan un
nmero menor o igual que el nmero buscado. Dado que no encuentra una solucin que
corresponda con el objetivo, regresa null a la primera llamada. Entonces el operador || ahce
que la llamada que explora (1 * 3) suceda. Esta bsqueda tiene ms suerte debido a que su
primera llamada recursiva, a travs de otra llamada recursiva, llega al nmero que buscamos,
13. Esta llamada recursuva, la ms interna, regresa una cadena y cada uno de los operadores
|| en la llamada superior inmediata pasan esa cadena, finalmente regresando nuestra
solucin.

Funciones crecientes
Existen dos formas ms o menos naturales de intorducir las funciones en los programas.

La primera es que te encuentras a ti mismo escribiendo el mismo cdigo varias veces.


Necesitamos evitar hacer debido a que ms cdigo significa ms espacio para que los errores
se oculten y ms material para leer para que leer para las personas que quiere entender el
programa. As que tomamos funcionalidad repetida, encontramos un buen nombre para esta,
y la ponemos dentro de una funcin. La segunda forma es que encuentres que necesitas cierta
funcionalidad que no has escrito an y que suena como a que merece su propia funcin.
Empezars por nombrar la funcin y despus escribirs su cuerpo. Podras incluso empezar a
escribir el cdigo que usa la funcin antes de realmente definir la funcin misma.

Qu tan difcil es encontrar un nombre para una funcin es un buen indicador de que tan claro
tienes el concepto de aquello que ests tratando de envolver. Hagamos un ejemplo.

Necesitamos escribir un problema que imprima dos nmeros, el nmero de las vacas y de los
pollos de una granja, con las palabras Cows y Pollos despus de estos, y completados con
ceros para que siempre sean de 3 dgitos de longitud.

007 Vacas
011 Pollos

Esto claramente requiere una funcin de dos argumentos. Empecemos a programar.

function imprimeInventarioGranja(vacas, pollos) {


var vacasString = String(vacas);
while (vacasString.length < 3)
vacasString = "0" + vacasString;
console.log(vacasString + " Vacas");
var pollosString = String(pollos);
while (pollosString.length < 3)
pollosString = "0" + pollosString;
console.log(pollosString + " Pollos");
}
imprimeInventarioGranja(7, 11);
Aadir .length despues de un valor de cadena nos dar la longitud de esa cadena. As, los
while continun agregando ceros al principio de la cadena del nmero hasta que son por lo
menos de 3 caracteres.

Misin cumplida! Pero casi cuando le vamos a mandar el cdigo al granjero (con una buena
factura) nos llama y nos dice que ha empezado a crar puercos, y que si podramos extender el
software para tambin mostrar los puerquitos, por favor?.

De seguro podemos. Pero mientras estamos en el proceso de copiar y pegar esas cuatro lneas
una vez ms, paramos y reconsideramos. Existe una mejor forma. Aqu hay un intento:

function imprimeConCerosYEtiqueta(numero, etiqueta) {


var numeroString = String(numero);
while (numeroString.length < 3)
numeroString = "0" + numeroString;
console.log(numeroString + " " + etiqueta);
}

function imprimeInventarioGranja(vacas, pollos, puercos) {


imprimeConCerosYEtiqueta(vacas, "Vacas");
imprimeConCerosYEtiqueta(pollos, "Pollos");
imprimeConCerosYEtiqueta(puercos, "Puercos");
}

imprimeInventarioGranja(7, 11, 3);

Funciona! Pero ese nombre, imprimeConCerosYEtiqueta, es un poco feo. Amonotona tres


cosasimprimir, completar con ceros, y agragar la etiquetaen una sola funcin.

En vez de quitar la parte repetida de nuestra programa por mayoreo, tratemos de escoger un
solo concepto.

function rellenoConCero(numero, ancho) {


var cadena = String(numero);
while (cadena.length < ancho)
cadena = "0" + cadena;
return cadena;
}

function imprimeInvantarioGranja(vacas, pollos, puercos) {


console.log(rellenoConCero(vacas, 3) + " Vacas");
console.log(rellenoConCero(pollos, 3) + " Pollos");
console.log(rellenoConCero(puercos, 3) + " Puercos");
}

imprimeInvantarioGranja(7, 16, 3);

Una funcin con un bonito y obvio nombre como rellenoConCero hace ms fcil para
alguien que lea el cdigo descubrir lo que hace. Y eso es til en ms situaciones que slo en
este programa especfico. Por ejemplo, puedes usarla para imprimir una tabla bien alineada
de nmeros.

Qu tan inteligente y verstil debera ser nuestra funcin? Podramos escribir cualquier cosa
desde una funcin terriblemente simple que slamente rellena un nmero de tal manera que
tenga 3 caracteres hasta una complicada, muy generalizada funcin, un sistema de formateo
de nmeros que maneje fracciones, nmeros negativos, alineacin de puntos, relleno con
diferentes carecteres y as por el estilo.

Un principio til no aadir caractersticas a menos que ests completamente seguro de que las
vas a ocupar. Puede ser tentador escribir marcos de trabajo generals "frameworks" para cada
pequeo pedazo de funcionalidad que te encuentras. Resiste el impulso. No acabars ningn
trabajo real y terminars escribiendo mucho cdigo que nadie usar alguna vez.

Funciones y efectos colaterales


Las funciones pueden ser ms o menos divididas en aquellas que son llamadas por sus efectos
colaterales y aquellas que son llamadas por su valor de resultado. (Aunque es completamente
posible tener tanto efectos colaterales como retornar un valor).

La primer funcin de ayuda en el ejemplo de la granja, imprimeConCerosYEtiqueta, es


llamada por su efecto colateral: imprime su valor de resultado. La segunda versin,
rellenoConCero, es llamada por su valor de resultado. No es coincidencia que la segunda
sea til en ms situaciones que la primera. Las funciones que crean valores son ms fciles de
combinar en nuevas formas que funciones que realizan directamente un efecto colateral.

Una funcin pura es un tipo de funcin que produce un valor que no slo carece de efectos
colaterales, tampoco usa los efectos colaterales de otro cdigopor ejemplo, no lee variables
globales que son ocasionalmente cambiadas por otro cdigo. Una funcin pura tiene la
agradable propiedad de que, cuando se llama los mismos argumentos, siempre produce el
mismo valor (y no hace nada ms). Esto hace fcil razonar con ella. Una llamada a una
funcin como esta puede ser sustituida mentalmente como su resultado, sin cambiar el
significado del cdigo. Cuando no estas seguro de que una funcin pura funciona
correctamente, puedes probarlo simplemente llamndola, y sabiendo que trabaja bien este
contexto, trabajar bien en cualquier context. Funciones no puras pueden regresar diferentes
valores basadas en todo tipo de factores y tienen efectos secundarios y que pueden ser difcil
de probar y de razonar con ellas.

An as, no hay necesidad de sentirse mal cuando escribimos funciones que no son puras o de
armar una guerra santa para eliminarlas de tu cdigo. Los efectos secundarios a menudo son
tiles. No habra forma de imprimir una versin pura de console.log, por ejemplo, y
console.log es ciertamente til. Algunas operaciones son adems ms fciles de expresar en
una forma eficiente cuando se usan los efectos scundarios, as que la velocidad de cmputo
puede ser una razn para evitar la pureza.

Sumrio
Este captulo ense como escribir tus propias funciones. La palabra resevada function,
cuando se usa como una expresin, puede crear un valor funcin. Cuando es usada como
sentencia, puede ser usada para declarar una variable y darle una funcin como valor.

// Crear un valor funcin f


var f = function(a) {
console.log(a + 2);
};
// Declarar g como funcin
function g(a, b) {
return a * b * 3.5;
}

Un aspecto clave para entender las funciones es entender los mbitos locales. Los parmetros
y variables declaradas dentro de una funcin son locales para la funcin, re-creados cada vez
que la funcin es llamada y no visibles de desde fuera. Las funciones declaradas dentro de
otra funcin tienen acceso al mbito local externo de la funcin.

Seprar las tareas que tu tarea realiza en diferentes funciones es til. No tendrs que repetir lo
mismo tanto, y las funciones pueden hacer un programa ms legible al agrupar el cdigo en
partes conceptuales, de la misma forma que captulos y secciones ayudan a organizar el texto
regular.

Ejercicios
Mnimo

El captulo anterior intordujo la funcin estndar Math.min que devuelve su argumento ms


pequeo. Ahora nosotros mismos podemos hacer eso. Escribe una funcin min que tome dos
argumentos y devuelva el mnimo.

// Tu cdigo va aqu.

console.log(min(0, 10));
// 0
console.log(min(0, -10));
// -10

Si tienes problemas poniendo las llaves y los parntesis en el lugar correcto para obtener una
definicin de funcin vlida, empieza por copiar uno de los ejemplos del captulo y
modificarlo.

Una funcin puede contener mltiples return.

Recursin

Hemos visto que % (el operador de sobrante) puede ser usado para probar si un nmero es par
o impar usando % 2 para checar si es divisible por dos. Aqu hay otra forma de definir si un
nmero entero es par o impar:

Cero es par.

Uno es impar.

Para cualquier otro nmero N, su paridad es la misma que N - 2.

Escribe una funcin recursiva esPar que corresponda a esta descripcin. La funcin debera
aceptar un numero como parmetro y regresar un Booleano.
Pruebala con 50 y 75. Observa cmo se comporta con -1. Por qu? Puedes pensar en alguna
forma de componer esto?

// Tu cdigo va aqu.

console.log(esPar(50));
// true
console.log(esPar(75));
// false
console.log(esPar(-1));
// ??

La funcin ser algo similar al encuentra interno en la solucin recursiva ejemplo


encuentraSolucion en este captulo, con una cadena de if/else if/else que probar cul
de los tres casos aplica. El else final, correspondiente al tercer caso, hace la llamada
recursiva. Cada una de las ramas debe de contener una sentencia return o de alguna otra
forma arrglrselas para devolver un valor especfico.

Cuando se le da un nmero negativo, la funcin va llamarse a s misma una y otra vez,


pasndose un nmero cada vez ms negativo, as alejndose ms y ms de regresar una
solucin. En algn momento se quedar sin espacio en la pila y abortar.

Contando Frijoles

Puedes obtener el n-avo caracter, o letra, de una cadena escribiendo "cadena".charAt(N),


similar a como obtienes su longitud con "cadena".length. El valor obtenido ser una
cadena que contiene slo un caracter(por ejemplo, "b"). El primer caracter tiene la posicin
cero, lo cual hace que el ltimo pueda ser encontrado en la posicin string.lenght -1. En
otras palabras, una cadenas de dos caracteres tiene un lenght o longitud de 2, y sus
caracteres tienen las posiciones 0 y 1.

Escribe una funcin cuentaFs que tome una cadena como su nico argumento y regrese un
nmero que indique cunntos caracteres F mayscula hay en la cadena.

A continuacin, escribe una funcion llamada cuentaCaracter que se comoporte como


cuentaFs, con la diferencia de que tome un segundo caracter que indique el caracter que ser
contado(en vez de slo caracteres F). Reescribe cuentaFs para hacer uso de esta nueva
funcin.

// Tu cdigo va a aqu.

console.log(cuentaFs("FFC"));
// 2
console.log(cuentaCaracter("ferrocarril", "r"));
// 4
Captulo 4
Estructuras de Datos: Objetos y Arreglos
En dos ocasiones me han preguntado Disculpe, Sr. Babbage, si introduce los nmeros
incorrectos en la mquina, van a salir las respuestas correctas? [...] No puedo terminar de
comprender el tipo de confusin de ideas que podran provocar esta pregunta.

Charles Babbage, Passages from the Life of a Philosopher (1864)

Nmeros, Booleanos y cadenas son los ladrillos de los que estn hechas las estructuras de
datos. Pero no podrs construir mucha casa de un solo ladrillo. Los objetos nos permiten
agrupar valores, incluyendo otros objetos, permitindonos construir estructuras ms
complejas.

Los programas que hemos construido hasta ahora han sido seriamente limitados debido al
hecho de que estaban operando nicamente en tipos de datos simples. Este captulo agregar
a tu caja de herramientas un entendimiento bsico de las estructuras de datos. Al finalizarlo,
sabrs lo suficiente para empezar a escribir algunos programas de utilidad.

El captulo trabajar a lo largo de un ejemplo de programacin ms o menos realista,


introduciendo conceptos conforme apliquen al problema en cuestin. El cdigo de ejemplo
muchas veces construir sobre funciones y variables que fueron presentadas previamente en
el texto.

El hombre ardilla
De vez en cuando, comnmente entre las ocho y las diez de la noche, Jacques se transforma
en un pequeo y peludo roedor con una frondosa cola.

Por un lado, Jacques esta bastante contento de no tener la clsica licantropa. Convertirse en
una ardilla suele causar menos problemas que convertirse en un lobo. En vez de tener que
preocuparse por comerse accidentalmente a un vecino (eso sera penoso), le preocupa el ser
deborado por el gato del vecino. Despus de un par de ocasiones donde se despert, en una
delgada rama en la cima de un roble, desnudo y desorientado, se ha asegurado de cerrar
puertas y ventanas de su cuarto por las noches y poner algunas bellotas en el suelo para
mantenerse ocupado.
Eso resuelve los problemas del gato y el roble. Pero Jacques an sufre de su enfermedad. Las
ocurrencias irregulares de la transformacin le hacen sospechar que pudieran ser detonadas
por algo. Por algn tiempo, crey que suceda slo en los dias que haba tocado rboles. As
que dej de tocar rboles de manera definitiva e incluso evit acercarse a ellos. Pero el
problema presisti.

Cambiando a una perspectiva ms cientfica, Jacques intenta empezar un registro diario de


todo lo que hizo ese da y si tuvo una transformacin. Con estos datos espera limitar las
condiciones que disparan las transformaciones.

Lo primero que hace es disear una estructura de datos para almacenar esta informacin.

Conjuntos de datos
Para trabajar con un pedazo de datos digitales, primero tendremos que encontrar una forma
de representarlo en la memoria de nuestra mquina. Digamos, como un pequeo ejemplo, que
queremos representar una coleccin de nmeros: 2, 3, 5, 7 y 11.

Podramos ponernos creativos usando cadenas; despus de todo, las cadenas pueden ser de
cualquier longitud, as que podemos poner mucha informacin en ellas; y usar "2 3 5 7 11"
como nuestra representacin. Pero esto es incmodo. De alguna forma tendras que extraer
los dgitos y convertirlos de vuelta a nmeros para acceder a ellos.

Afortunadamente, Javascript proporciona un tipo de dato especfico para almacenar


secuencias de valores. Se le llama arreglo (array) y se escribe como una lista de valores entre
corchetes, separados por comas.

edit & run code by clicking it


var listaDeNumeros = [2, 3, 5, 7, 11];
console.log(listaDeNumeros[1]);
// 3
console.log(listaDeNumeros[1 - 1]);
// 2

La notacin para obtener los elementos dentro de un arreglo tambin utiliza corchetes. Un par
de corchetes inmediatamente despus de una expresin, con otra expresin dentro de los
corchetes, buscar el elemento en la expresin de la izquierda que corresponda al ndice dado
por la expresin en corchetes.

El primer ndice de un arreglo es cero, no uno. As que el primer elemento puede leerse
usando listaDeNumeros[0]. Si no tienes antecedentes en programacin, acostumbrarte a
esta convencin puede tomarte algn tiempo. Pero el conteo con base cero tiene una larga
tradicin en tecnologa y mientras la convencin se siga de manera consistente (que se ha
hecho en Javascript), funciona bien.

Propiedades
Hemos visto algunas expresiones sospechosas como myString.length (para obtener la
longitud de una cadena) y Math.max (la funcin mximo) en ejemplos pasados. Estas son
expresiones que acceden una propiedad de algn valor. En el primer caso, accedemos a la
propiedad length de el valor en myString. En el segundo, accedemos a la propiedad llamada
max en el objeto Math (que es una coleccin de valores y funciones relacionadas con las
matemticas).

Casi todos los valores de JavaScript tienen propiedades. Las excepciones son null y
undefined. Si intentas acceder una propiedad de alguno de estos valores invlidos recibirs
un error.

null.length;
// TypeError: Cannot read property 'length' of null

Las dos maneras comunes de acceder a propiedades en Javascript son con un punto y con
corchetes. Ambas valor.x y valor[x] acceden a una propiedad en valor, pero no
necesariamente la misma propiedad. La diferencia radica en cmo se interpreta x. Cuando
usamos un punto, la parte despus del punto debe ser un nombre de variable vlido y nombra
de manera directa a la propiedad. Cuando usamos corchetes, la expresin dentro de los
corchetes es evaluada para obtener el nombre de la propiedad. Mientras que valor.x busca
la propiedad de valor llamada x, valor[x] intenta evaluar la expresin x y usa el
resultado como el nombre de la propiedad.

As que si sabes que la propiedad que te interesa se llama length, usas valor.length. Si
deseas extraer la propiedad nombrada por el valor almacenado en la variable i, usas
valor[i]. Y debido a que el nombre de las propiedades puede ser cualquier cadena, si
quieres accesar una propiedad llamada 2 o Fulano, debes utilizar corchetes:valor[2] or
valor["Fulano de Tal"]. As lo haras incluso si conoces el nombre preciso de la
propiedad de antemano, ya que ni 2 ni Fulano de Tal son nombres vlidos de variables y
por lo tanto no puede accederse a traves de la notacin con punto.

Los elementos en un arreglo se almacenan en propiedades. Debido a que los nombres de estas
propiedades son nmeros y usualmente necesitamos obtener su nombre de una variable,
tenemos que usar la sintaxis de corchetes para accesarlos. La propiedad length (longitud) de
un arreglo nos dice cuantos elementos contiene. Este nombre de propiedad es un nombre de
variable vlido, y conocemos su nombre por anticipado, as que para encontrar la longitud de
un arreglo, comnmente escribiremos arreglo.length ya que es ms fcil de escribir que
arreglo["length"].

Mtodos
Ambos objetos, las cadenas y los arreglos contienen, adicionalmente a la propiedad length
(longitud), varias propiedades que refieren a valores que son funciones.
var doh = "Doh";
console.log(typeof doh.toUpperCase);
// function
console.log(doh.toUpperCase());
// DOH

Todas las cadenas tienen una propiedad toUpperCase. Cuando es invocada, regresar una
copia de la cadena en la que todas las letras han sido convertidas a maysculas. Tambin
existe toLowerCase. Puedes adivinar que es lo que hace.

Es interesante que, aunque la funcin toUpperCase no recibe ningn argumento, delaguna


forma accede a la cadena "Doh", el valor en que se llam la funcin. Esto funciona como se
describe en el Captulo 6.

Las propiedades que contienen funciones genralmente son llamadas mtodos del valor al que
pertenecen. As, "toUpperCase es un mtodo de una cadena".

Este ejemplo demuestra algunos mtodos que los objetos arrays tienen:

var mack = [];


mack.push("Mack");
mack.push("the", "Knife");
console.log(mack);
// ["Mack", "the", "Knife"]
console.log(mack.join(" "));
// Mack the Knife
console.log(mack.pop());
// Knife
console.log(mack);
// ["Mack", "the"]

El mtodo push puede ser usado para aadir valores al final de un array. El mtodo pop hace
lo contrario: elimina el valor al final del array y lo regresa. Un array de cadenas puede ser
transformado a una sola cadena con el mtodo join. El argumento que se da a join
determina el texto que se usa entre los elementos del array.

Objetos
De regreso con el hombre ardilla. Un conjunto de entradas diaras de registro pueden ser
representadas como un array. Pero las entradas no consisten de slo un nmero o una cadena,
cada entrada necesita guarda una lista de actividades y un valor Booleano que indica si
Jacques se convirti en ardilla. Idealmente, nos gustara agrupar estos valores juntos en un
solo valor y poner estos valores agrupados en un array de entradas del registro.

Los valores del tipo objeto son colecciones arbitrarias de propiedades, y podemos aadir o
remover estas propiedades a placer. Una manera de crear un objeto es usando la notacin de
llaves.

var dia1 = {
ardilla: false,
eventos: ["trabajo", "toqu un arbol", "pizza", "correr",
"televisin"]
};
console.log(dia1.ardilla);
// false
console.log(dia1.lobo);
// undefined
dia1.lobo = false;
console.log(dia1.lobo);
// false

Dentro de las llaves, podemos dar una lista de propiedades separadas por comas. Cada
propiedad est escritra como un nombre seguido por dos puntos, seguidos pos una expresin
que le asigna el valor a esa propiedad. Los espacios y saltos de lnea no son afectan. Cunado
un objeto tiene mltiples lneas, indentarlas como en el ejemplo previo mejora la legibilidad.
Las propiedades que tengan nombres que no sean nombres de variable vlidos o nmeros
vlidos tienen que estar entrecomilladas.

var descripciones = {
trabajo: "Fui al trabajo",
"toqu un arbol": "Toqu el arbol del vecino"
};

Esto significa que las llaves tienen dos significados en JavaScript. Al principio de una
sentencia, empiezan un bloque de sentencia. En cualquier otra posicin, describen un objeto.
Afortunadamente, casi nunca es til empezar una sentencia con un objeto declarado con
llaves, y en programas tpicos, no hay ambigedad entre esos dos usos.

Al leer una propiedad que no existe se produce el valor undefined, es lo que pasa la primera
vez que intentamos leer la propiedad lobo en el ejemplo previo.

Es posible asignar un valor a una expresin de propiedad con el operador =. Esto reemplazar
el valor de la propiedad si ya exista o crear una nueva propiedad en el objeto si no.

Para regresar brevemente a nuestro modelo de tentculos para asignaciones de variable, la


asignacin de propiedades es parecida. Estas agarran valores, pero otras variables y
propiedades pudieran estar agarrando estos mismos valores. Puedes pensar que los objetos
son pulpos con un nmero indefinido de tentculos, cada uno de los cuales tiene un nombre
escrito en l.
El operador delete le corta un tentculo a este pulpo. Es un operador unario que, cuando es
aplicado a una expresin de acceso a una propiedad, eliminar la propiedad nombrada del
objeto. Esto no es algo que se haga comnmente, pero es posible.

var unObjeto = {izq: 1, der: 2};


console.log(unObjeto.izq);
// 1
delete unObjeto.izq;
console.log(unObjeto.izq);
// undefined
console.log("izq" in unObjeto);
// false
console.log("der" in unObjeto);
// true

El operador binario in, cuando se aplica a una cadena y un objeto, devuelve un valor
Booleano que indica si el objeto tiene esa propiedad. La diferencia entre asignarle undefined
a una propiedad y borrarla de verdad es que, en el primer caso, el objeto an tiene la
propiedad (simplemente no tiene un valor muy interesante), mientras que en el segundo caso,
la propiedad no est presente e in devolver false.

Los arrays son solamente un tipo de objeto especializado en almacenar secuencias de cosas.
Si evaluas typeof [1, 2], se prodcue "object". Puedes verlos como pulpos largos y planos
con todos sus tentculos en una fila limpia, etiquetados con nmeros.

As que podemos representar el diario de Jacques coomo un array de objetos.

var diario = [
{eventos: ["trabajo", "tocar un arbol", "pizza",
"correr", "televisin"],
ardilla: false},
{eventos: ["trabajo", "helado", "coliflor",
"lasagna", "tocar un arbol", "lavarse los dientes"],
ardilla: false},
{eventos: ["fin de seamana", "bicicleta", "descanso",
"cacahuates", "cerveza"],
ardilla: true},
/* y contina... */
];
Mutabilidad
Nos meteremos en la programacin de verdad pronto. Pero primero, hay una ltima pieza de
teora que entender.

Hemos visto que los valores objeto pueden ser modificados. Los tipos de valores de los que
platicamos en captulos anteriores, como nmeros, cadenas, Booleanos son todos inmutables;
es imposible cambiar un valor existente de esos tipos. Puedes combinarlos y derivar nuevos
valores de ellos, pero cuando tomas un valor cadena especfico, ese valor siempre
permanecer igual. El texto que est adentro no puede ser cambiado. Si tienes una referencia
a una cadena que contiene "cat", no se posible para el cdigo cambiar un carctes en esa
cadena para hacerlo decir "rat".

Con los objeto, por otro lado, el contenido de un valor puede ser modificado al cambiar sus
propiedades.

Cuando tenemos dos nmeros,, 120 y 120, podemos considerar que son precisamente el
mismo nmero independientemente si se refieren o no a los mismos bits fsicos. Pero con los
objetos, hay una diferencia entre tener la dos referencias al mismo objeto y tener dos objetos
que contienen las mismas propiedades. Considera el siguiente cdigo:

var objeto1 = {valor: 10};


var objeto2 = objeto1;
var objeto3 = {valor: 10};

console.log(objeto1 == objeto2);
// true
console.log(objeto1 == objeto3);
// false

objeto1.valor = 15;
console.log(objeto2.valor);
// 15
console.log(objeto3.valor);
// 10

Las variables objeto1 y objeto2 agarran el mismo objeto, sta es la razn de que al cambiar
objeto1 tambin se cambia el valor del objeto2. La variable objeto3 apunta a un objeto
diferente, que inicialmente contiene las mismas propiedades que el objeto1 pero vive una
vida separada.

El operador == de JavaScript, cuando compara objetos regresar true slo si los dos objetos
son precisamente el mismo valor. Comparar diferentes objetos regresar false, incluso si
tienen contenidos idnticos. No existe comparacin "profunda" construida en JavaScript, la
cual se basa en el contenido de los objetos, pero puedes escribirla t mismo (que ser uno de
los ejercicios al final de este captulo).

El registro del licntropo


As, Jacques inicia su intrprete de JavaScript y configura el entorno que necesita para llevar
su diario.
var diario = [];

function agregarEntrada(eventos, meVolviArdilla) {


diario.push({
eventos: eventos,
ardilla: meVolviArdilla
});
}

Y despus, cada noche a las 10, o algunas veces la maana siguiente, despus de haber bajado
de la parte superior de su librero, registra el da.

agregarEntrada(["trabajo", "toqu un rbol", "pizza", "correr",


"televisin"], false);
agregarEntrada(["trabajo", "helado", "coliflor", "lasagna",
"toqu un rbol", "me lav los dientes"], false);
agregarEntrada(["fin de semana", "bicicleta", "descanso", "cacachuates",
"cerveza"], true);

Una vez que tiene suficientes datos, quiere calcular la correlacin entre su ardillamiento y
cada uno de los eventos e, idealmente, aprender algo til de esas correlaciones.

La Correlacin es una medida de dependencia entre variables (variables en el sentido de la


estadstica, no en el de JavaScript). Es expresada usualmente como un coeficiente que va del
-1 al 1. Una correlacin de 0 significa que las variables no estn relacionadas, mientras que
una correlacin de uno indica que estn perfectamente relacionadas: si conoces una, entonces
conoces la otra. El uno negativo tambin significa que estn perfectamente relacionadas pero
que son opuestas, cuando una es verdadera, la otra es falsa.

Para variables binarias (Booleanas), el coeficiente phi () da una buena medida de


correlacin y es relativamente fcil de calcular. Para calcular necesitamos una tabla n que
contine el nmero de veces que varias combinaciones de variables fueron observadas. Por
ejemplo, podramos tomar el evento de comer pizza y ponerlo en la tabla de la siguiente
manera:

puede ser calculado usando la siguiente frmula, en donde n se refiere a la tabla:

n11n00 - n10n01
= n1n0n1n0
La notacin n01 indica el nmero de medidas en el que la primer vairiable (ardillificacin) es
falsa (0) y la segunda variable (pizza) es verdadera (1). En este ejemplo, n01 es 9.

El valor n1 se refiere a la suma de todas las medidas en donde la primera variable es


verdadera, que es 5 en la tabla de ejemplo. De la misma manera, n0 se refiere a la suma de
medidas en donde la segunda variable es falsa.

As que para la tabla de la pizza, la parte de arriba de la lnea de divisin (el dividendo) sera
176 - 49 = 40, y la parte de debajo de la lnea (el divisor) sera la raz cuadrada de
5851080, o 340000. Esto da como resultado 0.069, que es muy pequeo. Comer
pizza no parece tener influencia en las transformaciones.

Calculando correlaciones
Podemos representar una tabla de dos por dos en JavaScript con un array de 4 elementos
([76, 9, 4, 1]). Podramos tambin usar otras representaciones, como un array que
contenga dos arrays de dos elementos cada uno ([[76, 9], [4, 1]]) o como un objeto con
nombre de propiedades como "11" y "01", pero el array plano es simple y hace que las
expresiones de acceso a la tabla sean convenientemente cortas. Interpretaremos los ndices
del array como un nmero binario de dos bits, en donde el dgito ms a la izquierda (el ms
siginificativo) se refiere a la variable de la ardilla y el ms a la derecha (menos significativo)
se refiere a la variable del evento. Por ejemplo, el nmero binario 10 se refiere al caso en que
Jacques se convirti en ardilla, pero el evento (digamos, "pizza"), no ocurri. Esto pas 4
veces. Y como el binario 10 es 2 en notacin decimal, guardaremos ese nmero en el ndice 2
del array.

Esta es la funcin que calcula el coeficiente del ese array:

function phi(tabla) {
return (tabla[3] * tabla[0] - tabla[2] * tabla[1]) /
Math.sqrt((tabla[2] + tabla[3]) *
(tabla[0] + tabla[1]) *
(tabla[1] + tabla[3]) *
(tabla[0] + tabla[2]));
}

console.log(phi([76, 9, 4, 1]));
// 0.068599434

Esto es simplemente una traduccin directa de la fmula de la a JavaScript. Math.sqrt es


la funcin raz cuadrada, provista por el objeto Math en un entorno de JavaScript estndar.
Tenemos que sumar los dos campos de la tabla para obtener campos como n1 porque la suma
de las filas o columnas no estn guardadas directamente en nuestra estructura de datos.

Jacques mantuvo su diario por tres meses. El set de datos resultante est disponible en el rea
de cdigo para este captulo, en donde est guardada la variable DIARIO, y como descarga en
file.

Para extraer una tabla de 2 x 2 po un evento especfico de este diario, tenemos que iterar
sobre todas las entradas y contar cuntas veces el evento ocurre en relacin con las
transformaciones a ardilla.
function tieneEvento(evento, entrada) {
return entrada.eventos.indexOf(evento) != -1;
}

function tablaPara(evento, diario) {


var tabla = [0, 0, 0, 0];
for (var i = 0; i < diario.length; i++) {
var entrada = diario[i], index = 0;
if (tieneEvento(evento, entrada)) index += 1;
if (entrada.ardilla) index += 2;
tabla[index] += 1;
}
return tabla;
}

console.log(tabalaPara("pizza", DIARIO));
// [76, 9, 4, 1]

La funcin tieneEvento verifica si una entrada tiene una entrada dada. Los arrays tienen un
mtodo indexOf que intenta encontrar un valor dado (en este caso, el nombre del evento) en
el array y regresan el ndice en el cul fue encontrado o -1 si no se encontr. As que si la
llamada a indexOf no regresa -1, entonces sabemos que el evento fue encontrado en la
entrada.

El cuerpo del loop en tablaPara encuentra en qu celda de la tabla cae cada entrada del
diario mediante verificar si la entrada contiene el evento que est buscando y si el evento
ocurre junto con un incidente de ardilla. Entonces, el loop agrega uno al nmero en el array
que corresponde con esta categora en la tabla.

Ahora tenemos las herramientas que necesitamos para calcular las correlaciones individuales.
El nico paso que queda es encontrar la correlacin para cada tipo de evento registrado y ver
si algo sobresale. Pero, cmo debereiamos guardar esas correlaciones una vez que las
calculamos?

Objetos como mapas


Una camino posible es guardar todas las correlaciones en un array, usando objetos con
propiedades nombre y valor. Pero eso hace que buscar la correlacin por un evnto dado sea
un poco difcil: tienes que iterar sobre el array completo para buscar el objeto con el nombre
correcto. Podemos poner este cdigo en una funcin, pero seguiramos escribiendo ms
cdigo, y la computadora estara haciendo ms trabajo del necesario.

Una mejor forma es usar propiedades de un objeto nombradas como los tipos de eventos.
Podemos usar la notacin de acceso de corchetes (parntesis cuadrados) para crear y leer las
propiedades y el operador in para verificar si una propiedad existe.

var mapa = {};


function guardarPhi(evento, phi) {
mapa[evento] = phi;
}

guardarPhi("pizza", 0.069);
guardarPhi("toqu un rbol", -0.081);
console.log("pizza" in mapa);
// true
console.log(mapa["touched tree"]);
// -0.081

Un mapa es una manera de ir de valores en un dominio (en este caso nombres de eventos) a
valores correspondientes en otro dominio (en este caso coeficientes ).

Existen algunos problemas potenciales al usar objetos como este, que discutiremos en el
Captulo 6, pero por ahora, no nos preocupemos de eso.

Y si queremos hallar todos los eventos para los que tenemos guardados un coeficiente? Las
propiedades no forman una serie predecible, como lo haran en un array, as que no podemos
usar un for normal. JavaScript tiene un construccin iterativa especficamente para funcionar
sobre las propiedades de un objeto. Luce un poco como un loop for normal pero se distingue
por el uso de la palabra in.

for (var evento in mapa)


console.log("La correlacin para '" + evento +
"' es " + mapa[evento]);
// La correlacin para 'pizza' is 0.069
// La correlacin para 'toqu un arbol' es -0.081

Para encontrar todos los tipos de eventos que estn presentes en el set de datos, simplemente
procesamos cada entrada en turno y despus iteramos en los eventos de esa entrada.
Mantenemos un objeto phis que tiene los coeficientes de correlacin para todos los tipos de
eventos que hemos visto hasta ahora. Cuando encontramos un tipo de evento que no est en
el objeto phis todava, calculamos la correlacin y la aadimos al objeto.

function juntarCorrelaciones(diario) {
var phis = {};
for (var entrada = 0; entrada < diario.length; entrada++) {
var eventos = diario[entrada].eventos;
for (var i = 0; i < eventos.length; i++) {
var evento = eventos[i];
if (!(evento in phis))
phis[evento] = phi(tablaPara(evento, diario));
}
}
return phis;
}

var correlaciones = juntarCorrelaciones(DIARIO);


console.log(correlaciones.pizza);
// 0.068599434

Veamos lo que resulta.

for (var eventos in correlaciones)


console.log(eventos + ": " + correlaciones[eventos]);
// zanahoria: 0.0140970969
// ejercicio: 0.0685994341
// fin de semana: 0.1371988681
// pan: -0.0757554019
// pudding: -0.0648203724
// y as...
La mayora de las correlaciones estn cerca de cero. Comer zanahorias, pan o pudding
aparentemente no desencadenan la licantropa de la ardilla. Sin embargo, parece que s ocurre
ms frecuentemente los fines de semana de alguna manera. Filtremos los resultados para
mostrar slo las correlaciones mayores que 0.1 o menores que -0.1.

for (var event in correlations) {


var correlation = correlations[event];
if (correlation > 0.1 || correlation < -0.1)
console.log(event + ": " + correlation);
}
// weekend: 0.1371988681
// brushed teeth: -0.3805211953
// candy: 0.1296407447
// work: -0.1371988681
// spaghetti: 0.2425356250
// reading: 0.1106828054
// peanuts: 0.5902679812

A-ha! There are two factors whose correlation is clearly stronger than the others. Eating
peanuts has a strong positive effect on the chance of turning into a squirrel, whereas brushing
his teeth has a significant negative effect.

Interesting. Lets try something.

for (var i = 0; i < JOURNAL.length; i++) {


var entrada = JOURNAL[i];
if (hasEvent("peanuts", entrada) &&
!hasEvent("brushed teeth", entrada))
entrada.events.push("peanut teeth");
}
console.log(phi(tableFor("peanut teeth", JOURNAL)));
// 1

Well, thats unmistakable! The phenomenon occurs precisely when Jacques eats peanuts and
fails to brush his teeth. If only he werent such a slob about dental hygiene, hed have never
even noticed his affliction.

Knowing this, Jacques simply stops eating peanuts altogether and finds that this completely
puts an end to his transformations.

All is well with Jacques for a while. But a few years later, he loses his job and is eventually
forced to take employment with a circus, where he performs as The Incredible Squirrelman
by stuffing his mouth with peanut butter before every show. One day, fed up with this pitiful
existence, Jacques fails to change back into his human form, hops through a crack in the
circus tent, and vanishes into the forest. He is never seen again.

Further arrayology
Before finishing up this chapter, I want to introduce you to a few more object-related
concepts. Well start by introducing some generally useful array methods.
We saw push and pop, which add and remove elements at the end of an array, earlier in this
chapter. The corresponding methods for adding and removing things at the start of an array
are called unshift and shift.

var todoList = [];


function rememberTo(task) {
todoList.push(task);
}
function whatIsNext() {
return todoList.shift();
}
function urgentlyRememberTo(task) {
todoList.unshift(task);
}

The previous program manages lists of tasks. You add tasks to the end of the list by calling
rememberTo("eat"), and when youre ready to do something, you call whatIsNext() to get
(and remove) the front item from the list. The urgentlyRememberTo function also adds a task
but adds it to the front instead of the back of the list.

The indexOf method has a sibling called lastIndexOf, which starts searching for the given
element at the end of the array instead of the front.

console.log([1, 2, 3, 2, 1].indexOf(2));
// 1
console.log([1, 2, 3, 2, 1].lastIndexOf(2));
// 3

Both indexOf and lastIndexOf take an optional second argument that indicates where to
start searching from.

Another fundamental method is slice, which takes a start index and an end index and returns
an array that has only the elements between those indices. The start index is inclusive, the end
index exclusive.

console.log([0, 1, 2, 3, 4].slice(2, 4));


// [2, 3]
console.log([0, 1, 2, 3, 4].slice(2));
// [2, 3, 4]

When the end index is not given, slice will take all of the elements after the start index.
Strings also have a slice method, which has a similar effect.

The concat method can be used to glue arrays together, similar to what the + operator does
for strings. The following example shows both concat and slice in action. It takes an array
and an index, and it returns a new array that is a copy of the original array with the element at
the given index removed.

function remove(array, index) {


return array.slice(0, index)
.concat(array.slice(index + 1));
}
console.log(remove(["a", "b", "c", "d", "e"], 2));
// ["a", "b", "d", "e"]
Strings and their properties
We can read properties like length and toUpperCase from string values. But if you try to
add a new property, it doesnt stick.

var myString = "Fido";


myString.myProperty = "value";
console.log(myString.myProperty);
// undefined

Values of type string, number, and Boolean are not objects, and though the language doesnt
complain if you try to set new properties on them, it doesnt actually store those properties.
The values are immutable and cannot be changed.

But these types do have some built-in properties. Every string value has a number of
methods. The most useful ones are probably slice and indexOf, which resemble the array
methods of the same name.

console.log("coconuts".slice(4, 7));
// nut
console.log("coconut".indexOf("u"));
// 5

One difference is that a strings indexOf can take a string containing more than one character,
whereas the corresponding array method looks only for a single element.

console.log("one two three".indexOf("ee"));


// 11

The trim method removes whitespace (spaces, newlines, tabs, and similar characters) from
the start and end of a string.

console.log(" okay \n ".trim());


// okay

We have already seen the string types length property. Accessing the individual characters
in a string can be done with the charAt method but also by simply reading numeric
properties, like youd do for an array.

var string = "abc";


console.log(string.length);
// 3
console.log(string.charAt(0));
// a
console.log(string[1]);
// b

The arguments object


Whenever a function is called, a special variable named arguments is added to the
environment in which the function body runs. This variable refers to an object that holds all
of the arguments passed to the function. Remember that in JavaScript you are allowed to pass
more (or fewer) arguments to a function than the number of parameters the function itself
declares.

function noArguments() {}
noArguments(1, 2, 3); // This is okay
function threeArguments(a, b, c) {}
threeArguments(); // And so is this

The arguments object has a length property that tells us the number of arguments that were
really passed to the function. It also has a property for each argument, named 0, 1, 2, and so
on.

If that sounds a lot like an array to you, youre right, it is a lot like an array. But this object,
unfortunately, does not have any array methods (like slice or indexOf), so it is a little
harder to use than a real array.

function argumentCounter() {
console.log("You gave me", arguments.length, "arguments.");
}
argumentCounter("Straw man", "Tautology", "Ad hominem");
// You gave me 3 arguments.

Some functions can take any number of arguments, like console.log. These typically loop
over the values in their arguments object. They can be used to create very pleasant
interfaces. For example, remember how we created the entries to Jacques journal.

addEntry(["work", "touched tree", "pizza", "correr",


"television"], false);

Since he is going to be calling this function a lot, we could create an alternative that is easier
to call.

function addEntry(squirrel) {
var entry = {events: [], squirrel: squirrel};
for (var i = 1; i < arguments.length; i++)
entry.events.push(arguments[i]);
journal.push(entry);
}
addEntry(true, "work", "touched tree", "pizza",
"running", "television");

This version reads its first argument (squirrel) in the normal way and then goes over the
rest of the arguments (the loop starts at index 1, skipping the first) to gather them into an
array.

The Math object


As weve seen, Math is a grab-bag of number-related utility functions, such as Math.max
(maximum), Math.min (minimum), and Math.sqrt (square root).

The Math object is used simply as a container to group a bunch of related functionality. There
is only one Math object, and it is almost never useful as a value. Rather, it provides a
namespace so that all these functions and values do not have to be global variables.
Having too many global variables pollutes the namespace. The more names that have been
taken, the more likely you are to accidentally overwrite the value of some variable. For
example, its not unlikely that youll want to name something max in one of your programs.
Since JavaScripts built-in max function is tucked safely inside the Math object, we dont have
to worry about overwriting it.

Many languages will stop you, or at least warn you, when you are defining a variable with a
name that is already taken. JavaScript does neither, so be careful.

Back to the Math object. If you need to do trigonometry, Math can help. It contains cos
(cosine), sin (sine), and tan (tangent), as well as their inverse functions, acos, asin, and
atan, respectively. The number (pi)or at least the closest approximation that fits in a
JavaScript numberis available as Math.PI. (There is an old programming tradition of
writing the names of constant values in all caps.)

function randomPointOnCircle(radius) {
var angle = Math.random() * 2 * Math.PI;
return {x: radius * Math.cos(angle),
y: radius * Math.sin(angle)};
}
console.log(randomPointOnCircle(2));
// {x: 0.3667, y: 1.966}

If sines and cosines are not something you are very familiar with, dont worry. When they are
used in this book, in Chapter 13, Ill explain them.

The previous example uses Math.random. This is a function that returns a new pseudorandom
number between zero (inclusive) and one (exclusive) every time you call it.

console.log(Math.random());
// 0.36993729369714856
console.log(Math.random());
// 0.727367032552138
console.log(Math.random());
// 0.40180766698904335

Though computers are deterministic machinesthey always react the same way if given the
same inputit is possible to have them produce numbers that appear random. To do this, the
machine keeps a number (or a bunch of numbers) in its internal state. Then, every time a
random number is requested, it performs some complicated deterministic computations on
this internal state and returns part of the result of those computations. The machine also uses
the outcome to change its own internal state so that the next random number produced will
be different.

If we want a whole random number instead of a fractional one, we can use Math.floor
(which rounds down to the nearest whole number) on the result of Math.random.

console.log(Math.floor(Math.random() * 10));
// 2
Multiplying the random number by 10 gives us a number greater than or equal to zero, and
below 10. Since Math.floor rounds down, this expression will produce, with equal chance,
any number from 0 through 9.

There are also the functions Math.ceil (for ceiling, which rounds up to a whole number)
and Math.round (to the nearest whole number).

The global object


The global scope, the space in which global variables live, can also be approached as an
object in JavaScript. Each global variable is present as a property of this object. In browsers,
the global scope object is stored in the window variable.

var myVar = 10;


console.log("myVar" in window);
// true
console.log(window.myVar);
// 10

Summary
Objects and arrays (which are a specific kind of object) provide ways to group several values
into a single value. Conceptually, this allows us to put a bunch of related things in a bag and
run around with the bag, instead of trying to wrap our arms around all of the individual things
and trying to hold on to them separately.

Most values in JavaScript have properties, the exceptions being null and undefined.
Properties are accessed using value.propName or value["propName"]. Objects tend to use
names for their properties and store more or less a fixed set of them. Arrays, on the other
hand, usually contain varying numbers of conceptually identical values and use numbers
(starting from 0) as the names of their properties.

There are some named properties in arrays, such as length and a number of methods.
Methods are functions that live in properties and (usually) act on the value they are a property
of.

Objects can also serve as maps, associating values with names. The in operator can be used
to find out whether an object contains a property with a given name. The same keyword can
also be used in a for loop (for (var name in object)) to loop over an objects properties.

Exercises
The sum of a range

The introduction of this book alluded to the following as a nice way to compute the sum of a
range of numbers:

console.log(sum(range(1, 10)));
Write a range function that takes two arguments, start and end, and returns an array
containing all the numbers from start up to (and including) end.

Next, write a sum function that takes an array of numbers and returns the sum of these
numbers. Run the previous program and see whether it does indeed return 55.

As a bonus assignment, modify your range function to take an optional third argument that
indicates the step value used to build up the array. If no step is given, the array elements go
up by increments of one, corresponding to the old behavior. The function call range(1, 10,
2) should return [1, 3, 5, 7, 9]. Make sure it also works with negative step values so that
range(5, 2, -1) produces [5, 4, 3, 2].

// Your code here.

console.log(range(1, 10));
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
console.log(range(5, 2, -1));
// [5, 4, 3, 2]
console.log(sum(range(1, 10)));
// 55

Reversing an array

Arrays have a method reverse, which changes the array by inverting the order in which its
elements appear. For this exercise, write two functions, reverseArray and
reverseArrayInPlace. The first, reverseArray, takes an array as argument and produces a
new array that has the same elements in the inverse order. The second,
reverseArrayInPlace, does what the reverse method does: it modifies the array given as
argument in order to reverse its elements. Neither may use the standard reverse method.

Thinking back to the notes about side effects and pure functions in the previous chapter,
which variant do you expect to be useful in more situations? Which one is more efficient?

// Your code here.

console.log(reverseArray(["A", "B", "C"]));


// ["C", "B", "A"];
var arrayValue = [1, 2, 3, 4, 5];
reverseArrayInPlace(arrayValue);
console.log(arrayValue);
// [5, 4, 3, 2, 1]

A list

Objects, as generic blobs of values, can be used to build all sorts of data structures. A
common data structure is the list (not to be confused with the array). A list is a nested set of
objects, with the first object holding a reference to the second, the second to the third, and so
on.

var list = {
value: 1,
rest: {
value: 2,
rest: {
value: 3,
rest: null
}
}
};

The resulting objects form a chain, like this:

A nice thing about lists is that they can share parts of their structure. For example, if I create
two new values {value: 0, rest: list} and {value: -1, rest: list} (with list
referring to the variable defined earlier), they are both independent lists, but they share the
structure that makes up their last three elements. In addition, the original list is also still a
valid three-element list.

Write a function arrayToList that builds up a data structure like the previous one when
given [1, 2, 3] as argument, and write a listToArray function that produces an array
from a list. Also write the helper functions prepend, which takes an element and a list and
creates a new list that adds the element to the front of the input list, and nth, which takes a
list and a number and returns the element at the given position in the list, or undefined when
there is no such element.

If you havent already, also write a recursive version of nth.

// Your code here.

console.log(arrayToList([10, 20]));
// {value: 10, rest: {value: 20, rest: null}}
console.log(listToArray(arrayToList([10, 20, 30])));
// [10, 20, 30]
console.log(prepend(10, prepend(20, null)));
// {value: 10, rest: {value: 20, rest: null}}
console.log(nth(arrayToList([10, 20, 30]), 1));
// 20

Deep comparison

The == operator compares objects by identity. But sometimes, you would prefer to compare
the values of their actual properties.

Write a function, deepEqual, that takes two values and returns true only if they are the same
value or are objects with the same properties whose values are also equal when compared
with a recursive call to deepEqual.

To find out whether to compare two things by identity (use the === operator for that) or by
looking at their properties, you can use the typeof operator. If it produces "object" for both
values, you should do a deep comparison. But you have to take one silly exception into
account: by a historical accident, typeof null also produces "object".

// Your code here.


var obj = {here: {is: "an"}, object: 2};
console.log(deepEqual(obj, obj));
// true
console.log(deepEqual(obj, {here: 1, object: 2}));
// false
console.log(deepEqual(obj, {here: {is: "an"}, object: 2}));
// true

Captulo 5
Funciones de Order Superior
Tzu-li y Tzu-ssu estaban presumiendo acerca del tamao de sus litmos programas.
Doscientas mil lneas , dijo Tzu-li, sin contar los comentarios!. Tzu-ssu respondi, Pssh,
el mo tiene ya casi un millin de lneas. El Maestro Yuan-Ma dijo, Mi mejor programa
tiene quinientas lneas. Oyendo esto, Tzu-li y Tzu-ssu fueron iluminados.

Master Yuan-Ma, The Book of Programming

Hay dos formas de construir un diseo de software: Una forma es hacerlo tan simple que
obviamente no tenga deficiencias, y la otra forma es hacerlo tan complicado que no haya
obvias deficiecias.

C.A.R. Hoare, 1980 ACM Turing Award Lecture

Un programa grande es costoso y no slo por el tiempo que toma construirlo. El tamao casi
siempre involucra complejidad, y la complejidad confunde a los programadores. Los
programadores confundidos, a su vez, tienden a introducir errores (bugs) en los programas.
Un programa grande, adems, da un amplio espacio para que estos bugs se oculten,
hacindolos difciles de encontrar.

Regresemos brevemente a los dos programas finales en la introduccin. El primero es auto-


contenido y tiene seis lneas de longitud.

var total = 0, cuenta = 1;


while (cuenta <= 10) {
total += cuenta;
cuenta += 1;
}
console.log(total);

El segundo depende de dos funciones externas y es una sola lnea

console.log(suma(rango(1, 10)));

Cul de los dos es ms probable que tenga un bug?


Si contamos el tamao de definicin de suma y rango, el segungo programa tambin es
grandeincluso ms grande que el primero. Pero an as, yo dira que es ms probable que sea
correcto.

Es ms probable que sea correcto porque la solucin es expresada en un vocabulario que


corresponde al problema que est siendo resuelto. Sumar un rango de enteros no tiene que ver
con bucles y contadores. Es acerca de rangos y sumas.

Las definiciones de este vocabulario (las funciones suma y rango ) todava incluirn bucles,
contadores y otros detalles incidentales. Pero debido a que estn expresando conceptos ms
simples que el programa como un todo, es ms fcil acertar.

Abstraccin
En el contexto de la programacin, vocabularios de este estilo son usualmente llamados
abstraccin. Las abstracciones econden detalles y nos dan la habilidad de hablar de los
problemas en un nivel ms alto (o ms abstracto).

Como una analoga, compara estas dos recetas para la sopa de guisantes:

Pon una 1 taza de guisantes secos por persona en un contenedor. Agrega agua hasta que los
guisantes estn cubiertos. Deja los guisantes en el agua por lo menos 12 horas. Saca los
guisantes del agua y ponlos en en un sartn para cocer. Agrega 4 copas de agua por persona.
Cubre el sartn y mantn los hirvindose a fuego lento por dos horas. Agrega la mitad de una
cebolla por persona. Crtala en piezas con un cuchillo. Agrgalas a los guisantes. Toma un
diente de ajo por persona. Crtalos en piezas con un cuchillo. Agrgalos a los guisantes.
Toma una zanahoria por persona. Crtalas en piezas. Con un cuchillo! Agrgalas a los
guisantes. Cocina por 10 minutos ms.

Y la segunda receta:

Por persona: 1 taza de guisantes partidos secos, media cebolla picada, un diente de ajo y una
zanohoria.

Remoja los guisantes por 12 horas Soak peas for 12 hours. Hierve a fuego lento por 2 horas
en 4 tazas(por persona). Pica y agrega los vegetales. Cocina por 10 minutos ms.

La segunda es ms corta y ms fcil de interpretar. Pero necesitas entender unos cuntas


palabras relacionadas con la cocina-remojar, picar y, imagino, vegetal.

Cuando programamos, no podemos confiar en que todas las palbras que necesitamos estn
esperndonos en el diccionario. As que podras caer en el patrn de la primera receta
trabajar en los pasos precisos que la computadora debe realizar, uno por uno, sin ver los
conceptos de alto nivel que estos expresan.

Se tiene que convertir en una segunda naturaleza, para un programador, notar cuando un
concepto est rogando ser abstrado en una nueva palabra.

Abstrayendo transversal de array


Las fuunciones planas, como hemos visto hasta ahora, son una buena forma de construir
abstracciones. Pero algunas veces se quedan cortas.

En el captulo antterior, este tipo de bucle for apareci varias veces:

var array = [1, 2, 3];


for (var i = 0; i < array.length; i++) {
var actual = array[i];
console.log(actual);
}

Est tratando de decir, "Cada elemento, escrbelo en la consola". Pero usa una forma
rebuscada que implica una variable i, una revisin del tamao del array y una declaracin de
variable extra para guardar el elemento actual. A parte de causar un poco de dolor de ojos,
esto nos da mucho espacio para errores potenciales. Podramos qccidentalmente reusar la
variable i, escribir mal length como lenght, confundir las variables i y actual y as por el
estilo. As que tratemos de abstraer esto en una funcin. Puedes pensar en alguna forma?

Bueno, es fcil escribir una funcin que vaya a travs de un array y llamar console.log en
cada elemento.

function logEach(array) {
for (var i = 0; i < array.length; i++)
console.log(array[i]);
}

Pero, qu pasa si queremos hacer otra cosa que loggear los elementos? Debido a que "hacer
algo" puede ser representado como una funcin y las funciones son slo valores, podemos
pasar nuestra accin como un valor funcin.

function forEach(array, accion) {


for (var i = 0; i < array.length; i++)
accion(array[i]);
}

forEach(["Wampeter", "Foma", "Granfalloon"], console.log);


// Wampeter
// Foma
// Granfalloon

<<<<<<< HEAD A menudo, no le pasas una funcin predefinida a forEach sino que creas
una funcin en el acto.

(In some browsers, calling console.log in this way does not work. You can use alert
instead of console.log if this example fails to work.)

Often, you dont pass a predefined function to forEach but create a function value on the
spot instead. >>>>>>> marijnh/master

var numeros = [1, 2, 3, 4, 5], suma = 0;


forEach(numeros, function(numero) {
suma += numero;
});
console.log(suma);
// 15

Esto luce muy parecido al clsico bucle for, con su cuerpo escrito debajo de l. Sin embargo,
ahora el cuerpo est dentro del valor funcin, as como dentro de los parntesis de la llamada
a forEach. Esta es la razn de que tenga que ser terminado con una llave y un parntesis de
cierre.

Usando este patrn, podemos especificar un nombre de variable para el elemento


actual(numero), en vez de tener que tomarlo del array manuelmente.

De hecho, no necesitamos escribir forEach nosotros mismos. Est disponible como un


mtodo estndar en los arrays. Debido a que el array le es pasado como el elemento sobre el
que acta el mtodo, forEach slo recibe un argumento requerido: la funcin que ser
ejecutada para cada elemento.

Para ilustrar lo til que esto es, miremos otra vez la funcin del captulo anterior. Contiene
dos bucles que recorren un arreglo.

function reuneCorrelaciones(diario) {
var phis = {};
for (var entrada = 0; entrada < diario.length; entrada++) {
var eventos = diario[entrada].events;
for (var i = 0; i < events.length; i++) {
var evento = eventos[i];
if (!(evento in phis))
phis[evento] = phi(tableFor(evento, diario));
}
}
return phis;
}

forEach la hace un poco ms corta y limpia.

function reuneCorrelaciones(diario) {
var phis = {};
diario.forEach(function(entrada) {
entrada.eventos.forEach(function(evento) {
if (!(evento in phis))
phis[evento] = phi(tableFor(evento, diario));
});
});
return phis;
}

Las funciones que operan en otras funceiones, ya sea tomndolas como argumentos o
regresndolas, son llamadas funciones de orden superior. Si ya has aceptado el hecho de que
las funciones son valores reulares, no hay nada de especial en el hecho de que estas funciones
existan. El trminos viene de las matemticas, en dnde la distincin entre las funciones y
otros valores es tomado ms seriamente.

Las funciones de orden superior nos permiten abstraer acciones, no slo valores. Pueden
venir en diferentes formas. Por ejemplo puedes tener funciones que creen nuevas funciones.

function mayorQue(n) {
return function(m) { return m > n; };
}
var mayorQue10 = mayorQue(10);
console.log(mayorQue10(11));
// true

Y puedes tener funciones que cambien otras funciones.

function ruidosa(f) {
return function(arg) {
console.log("llamando con", arg);
var val = f(arg);
console.log("llamada con", arg, "- got", val);
return val;
};
}
ruidosa(Boolean)(0);
// llamando con 0
// llamada con 0 - obtuve false

Incluso puedes escribir funciones que creen nuevos tipos control de flujo.

function a_menos_que(condicion, entonces) {


if (!condicion) entonces();
}
function repetir(veces, cuerpo) {
for (var i = 0; i < veces; i++) cuerpo(i);
}

repetir(3, function(n) {
a_menos_que(n % 2, function() {
console.log(n, "es par");
});
});
// 0 es par
// 2 es par

The lexical scoping rules that we discussed in Chapter 3 work to our advantage when using
functions in this way. In the previous example, the n variable is a parameter to the outer
function. Because the inner function lives inside the environment of the outer one, it can use
n. The bodies of such inner functions can access the variables around them. They can play a
role similar to the {} blocks used in regular loops and conditional statements. An important
difference is that variables declared inside inner functions do not end up in the environment
of the outer function. And that is usually a good thing.

The noisy function defined earlier, which wraps its argument in another function, has a
rather serious deficit.

function noisy(f) {
return function(arg) {
console.log("calling with", arg);
var val = f(arg);
console.log("called with", arg, "- got", val);
return val;
};
}
If f takes more than one parameter, it gets only the first one. We could add a bunch of
arguments to the inner function (arg1, arg2, and so on) and pass them all to f, but it is not
clear how many would be enough. This solution would also deprive f of the information in
arguments.length. Since wed always pass the same number of arguments, it wouldnt
know how many arguments were originally given.

For these kinds of situations, JavaScript functions have an apply method. You pass it an array
(or array-like object) of arguments, and it will call the function with those arguments.

function transparentWrapping(f) {
return function() {
return f.apply(null, arguments);
};
}

Thats a useless function, but it shows the pattern we are interested inthe function it returns
passes all of the given arguments, and only those arguments, to f. It does this by passing its
own arguments object to apply. The first argument to apply, for which we are passing null
here, can be used to simulate a method call. We will come back to that in the next chapter.

Higher-order functions that somehow apply a function to the elements of an array are widely
used in JavaScript. The forEach method is the most primitive such function. There are a
number of other variants available as methods on arrays. To familiarize ourselves with them,
lets play around with another data set.

A few years ago, someone crawled through a lot of archives and put together a book on the
history of my family name (Haverbekemeaning Oatbrook). I opened it hoping to find
knights, pirates, and alchemists ... but the book turns out to be mostly full of Flemish farmers.
For my amusement, I extracted the information on my direct ancestors and put it into a
computer-readable format.

The file I created looks something like this:

[
{"name": "Emma de Milliano", "sex": "f",
"born": 1876, "died": 1956,
"father": "Petrus de Milliano",
"mother": "Sophia van Damme"},
{"name": "Carolus Haverbeke", "sex": "m",
"born": 1832, "died": 1905,
"father": "Carel Haverbeke",
"mother": "Maria van Brussel"},
and so on
]

This format is called JSON (pronounced Jason), which stands for JavaScript Object
Notation. It is widely used as a data storage and communication format on the Web.

JSON is similar to JavaScripts way of writing arrays and objects, with a few restrictions. All
property names have to be surrounded by double quotes, and only simple data expressions are
allowedno function calls, variables, or anything that involves actual computation.
Comments are not allowed in JSON.
JavaScript gives us functions, JSON.stringify and JSON.parse, that convert data from and
to this format. The first takes a JavaScript value and returns a JSON-encoded string. The
second takes such a string and converts it to the value it encodes.

var string = JSON.stringify({name: "X", born: 1980});


console.log(string);
// {"name":"X","born":1980}
console.log(JSON.parse(string).born);
// 1980

The variable ANCESTRY_FILE, available in the sandbox for this chapter and in a downloadable
file on the website, contains the content of my JSON file as a string. Lets decode it and see
how many people it contains.

var ancestry = JSON.parse(ANCESTRY_FILE);


console.log(ancestry.length);
// 39

To find the people in the ancestry data set who were young in 1924, the following function
might be helpful. It filters out the elements in an array that dont pass a test.

function filter(array, test) {


var passed = [];
for (var i = 0; i < array.length; i++) {
if (test(array[i]))
passed.push(array[i]);
}
return passed;
}

console.log(filter(ancestry, function(person) {
return person.born > 1900 && person.born < 1925;
}));
// [{name: "Philibert Haverbeke", }, ]

This uses the argument named test, a function value, to fill in a gap in the computation.
The test function is called for each element, and its return value determines whether an
element is included in the returned array.

Three people in the file were alive and young in 1924: my grandfather, grandmother, and
great-aunt.

Note how the filter function, rather than deleting elements from the existing array, builds
up a new array with only the elements that pass the test. This function is pure. It does not
modify the array it is given.

Like forEach, filter is also a standard method on arrays. The example defined the function
only in order to show what it does internally. From now on, well use it like this instead:

console.log(ancestry.filter(function(person) {
return person.father == "Carel Haverbeke";
}));
// [{name: "Carolus Haverbeke", }]
Say we have an array of objects representing people, produced by filtering the ancestry
array somehow. But we want an array of names, which is easier to read.

The map method transforms an array by applying a function to all of its elements and building
a new array from the returned values. The new array will have the same length as the input
array, but its content will have been mapped to a new form by the function.

function map(array, transform) {


var mapped = [];
for (var i = 0; i < array.length; i++)
mapped.push(transform(array[i]));
return mapped;
}

var overNinety = ancestry.filter(function(person) {


return person.died - person.born > 90;
});
console.log(map(overNinety, function(person) {
return person.name;
}));
// ["Clara Aernoudts", "Emile Haverbeke",
// "Maria Haverbeke"]

Interestingly, the people who lived to at least 90 years of age are the same three people who
we saw beforethe people who were young in the 1920s, which happens to be the most
recent generation in my data set. I guess medicine has come a long way.

Like forEach and filter, map is also a standard method on arrays.

Another common pattern of computation on arrays is computing a single value from them.
Our recurring example, summing a collection of numbers, is an instance of this. Another
example would be finding the person with the earliest year of birth in the data set.

The higher-order operation that represents this pattern is called reduce (or sometimes fold).
You can think of it as folding up the array, one element at a time. When summing numbers,
youd start with the number zero and, for each element, combine it with the current sum by
adding the two.

The parameters to the reduce function are, apart from the array, a combining function and a
start value. This function is a little less straightforward than filter and map, so pay careful
attention.

function reduce(array, combine, start) {


var current = start;
for (var i = 0; i < array.length; i++)
current = combine(current, array[i]);
return current;
}

console.log(reduce([1, 2, 3, 4], function(a, b) {


return a + b;
}, 0));
// 10
The standard array method reduce, which of course corresponds to this function, has an
added convenience. If your array contains at least one element, you are allowed to leave off
the start argument. The method will take the first element of the array as its start value and
start reducing at the second element.

To use reduce to find my most ancient known ancestor, we can write something like this:

console.log(ancestry.reduce(function(min, cur) {
if (cur.born < min.born) return cur;
else return min;
}));
// {name: "Pauwels van Haverbeke", born: 1535, }

Consider how we would have written the previous example (finding the person with the
earliest year of birth) without higher-order functions. The code is not that much worse.

var min = ancestry[0];


for (var i = 1; i < ancestry.length; i++) {
var cur = ancestry[i];
if (cur.born < min.born)
min = cur;
}
console.log(min);
// {name: "Pauwels van Haverbeke", born: 1535, }

There are a few more variables, and the program is two lines longer but still quite easy to
understand.

Higher-order functions start to shine when you need to compose functions. As an example,
lets write code that finds the average age for men and for women in the data set.

function average(array) {
function plus(a, b) { return a + b; }
return array.reduce(plus) / array.length;
}
function age(p) { return p.died - p.born; }
function male(p) { return p.sex == "m"; }
function female(p) { return p.sex == "f"; }

console.log(average(ancestry.filter(male).map(age)));
// 61.67
console.log(average(ancestry.filter(female).map(age)));
// 54.56

(Its a bit silly that we have to define plus as a function, but operators in JavaScript, unlike
functions, are not values, so you cant pass them as arguments.)

Instead of tangling the logic into a big loop, it is neatly composed into the concepts we are
interested indetermining sex, computing age, and averaging numbers. We can apply these
one by one to get the result we are looking for.

This is fabulous for writing clear code. Unfortunately, this clarity comes at a cost.
In the happy land of elegant code and pretty rainbows, there lives a spoil-sport monster called
inefficiency.

A program that processes an array is most elegantly expressed as a sequence of cleanly


separated steps that each do something with the array and produce a new array. But building
up all those intermediate arrays is somewhat expensive.

Likewise, passing a function to forEach and letting that method handle the array iteration for
us is convenient and easy to read. But function calls in JavaScript are costly compared to
simple loop bodies.

And so it goes with a lot of techniques that help improve the clarity of a program.
Abstractions add layers between the raw things the computer is doing and the concepts we
are working with and thus cause the machine to perform more work. This is not an iron law
there are programming languages that have better support for building abstractions without
adding inefficiencies, and even in JavaScript, an experienced programmer can find ways to
write abstract code that is still fast. But it is a problem that comes up a lot.

Fortunately, most computers are insanely fast. If you are processing a modest set of data or
doing something that has to happen only on a human time scale (say, every time the user
clicks a button), then it does not matter whether you wrote a pretty solution that takes half a
millisecond or a super-optimized solution that takes a tenth of a millisecond.

It is helpful to roughly keep track of how often a piece of your program is going to run. If you
have a loop inside a loop (either directly or through the outer loop calling a function that ends
up performing the inner loop), the code inside the inner loop will end up running NM times,
where N is the number of times the outer loop repeats and M is the number of times the inner
loop repeats within each iteration of the outer loop. If that inner loop contains another loop
that makes P rounds, its body will run MNP times, and so on. This can add up to large
numbers, and when a program is slow, the problem can often be traced to only a small part of
the code, which sits inside an inner loop.

My grandfather, Philibert Haverbeke, is included in the data file. By starting with him, I can
trace my lineage to find out whether the most ancient person in the data, Pauwels van
Haverbeke, is my direct ancestor. And if he is, I would like to know how much DNA I
theoretically share with him.

To be able to go from a parents name to the actual object that represents this person, we first
build up an object that associates names with people.

var byName = {};


ancestry.forEach(function(person) {
byName[person.name] = person;
});

console.log(byName["Philibert Haverbeke"]);
// {name: "Philibert Haverbeke", }

Now, the problem is not entirely as simple as following the father properties and counting
how many we need to reach Pauwels. There are several cases in the family tree where people
married their second cousins (tiny villages and all that). This causes the branches of the
family tree to rejoin in a few places, which means I share more than 1/2G of my genes with
this person, where G for the number of generations between Pauwels and me. This formula
comes from the idea that each generation splits the gene pool in two.

A reasonable way to think about this problem is to look at it as being analogous to reduce,
which condenses an array to a single value by repeatedly combining values, left to right. In
this case, we also want to condense our data structure to a single value but in a way that
follows family lines. The shape of the data is that of a family tree, rather than a flat list.

The way we want to reduce this shape is by computing a value for a given person by
combining values from their ancestors. This can be done recursively: if we are interested in
person A, we have to compute the values for As parents, which in turn requires us to compute
the value for As grandparents, and so on. In principle, thatd require us to look at an infinite
number of people, but since our data set is finite, we have to stop somewhere. Well allow a
default value to be given to our reduction function, which will be used for people who are not
in the data. In our case, that value is simply zero, on the assumption that people not in the list
dont share DNA with the ancestor we are looking at.

Given a person, a function to combine values from the two parents of a given person, and a
default value, reduceAncestors condenses a value from a family tree.

function reduceAncestors(person, f, defaultValue) {


function valueFor(person) {
if (person == null)
return defaultValue;
else
return f(person, valueFor(byName[person.mother]),
valueFor(byName[person.father]));
}
return valueFor(person);
}

The inner function (valueFor) handles a single person. Through the magic of recursion, it
can simply call itself to handle the father and the mother of this person. The results, along
with the person object itself, are passed to f, which returns the actual value for this person.

We can then use this to compute the amount of DNA my grandfather shared with Pauwels
van Haverbeke and divide that by four.

function sharedDNA(person, fromMother, fromFather) {


if (person.name == "Pauwels van Haverbeke")
return 1;
else
return (fromMother + fromFather) / 2;
}
var ph = byName["Philibert Haverbeke"];
console.log(reduceAncestors(ph, sharedDNA, 0) / 4);
// 0.00049

The person with the name Pauwels van Haverbeke obviously shared 100 percent of his DNA
with Pauwels van Haverbeke (there are no people who share names in the data set), so the
function returns 1 for him. All other people share the average of the amounts that their
parents share.
So, statistically speaking, I share about 0.05 percent of my DNA with this 16th-century
person. It should be noted that this is only a statistical approximation, not an exact amount. It
is a rather small number, but given how much genetic material we carry (about 3 billion base
pairs), theres still probably some aspect in the biological machine that is me that originates
with Pauwels.

We could also have computed this number without relying on reduceAncestors. But
separating the general approach (condensing a family tree) from the specific case (computing
shared DNA) can improve the clarity of the code and allows us to reuse the abstract part of
the program for other cases. For example, the following code finds the percentage of a
persons known ancestors who lived past 70 (by lineage, so people may be counted multiple
times):

function countAncestors(person, test) {


function combine(current, fromMother, fromFather) {
var thisOneCounts = current != person && test(current);
return fromMother + fromFather + (thisOneCounts ? 1 : 0);
}
return reduceAncestors(person, combine, 0);
}
function longLivingPercentage(person) {
var all = countAncestors(person, function(person) {
return true;
});
var longLiving = countAncestors(person, function(person) {
return (person.died - person.born) >= 70;
});
return longLiving / all;
}
console.log(longLivingPercentage(byName["Emile Haverbeke"]));
// 0.129

Such numbers are not to be taken too seriously, given that our data set contains a rather
arbitrary collection of people. But the code illustrates the fact that reduceAncestors gives us
a useful piece of vocabulary for working with the family tree data structure.

The bind method, which all functions have, creates a new function that will call the original
function but with some of the arguments already fixed.

The following code shows an example of bind in use. It defines a function isInSet that tells
us whether a person is in a given set of strings. To call filter in order to collect those person
objects whose names are in a specific set, we can either write a function expression that
makes a call to isInSet with our set as its first argument or partially apply the isInSet
function.

var theSet = ["Carel Haverbeke", "Maria van Brussel",


"Donald Duck"];
function isInSet(set, person) {
return set.indexOf(person.name) > -1;
}

console.log(ancestry.filter(function(person) {
return isInSet(theSet, person);
}));
// [{name: "Maria van Brussel", },
// {name: "Carel Haverbeke", }]
console.log(ancestry.filter(isInSet.bind(null, theSet)));
// same result

The call to bind returns a function that will call isInSet with theSet as first argument,
followed by any remaining arguments given to the bound function.

The first argument, where the example passes null, is used for method calls, similar to the
first argument to apply. Ill describe this in more detail in the next chapter.

Being able to pass function values to other functions is not just a gimmick but a deeply useful
aspect of JavaScript. It allows us to write computations with gaps in them as functions and
have the code that calls these functions fill in those gaps by providing function values that
describe the missing computations.

Arrays provide a number of useful higher-order methodsforEach to do something with


each element in an array, filter to build a new array with some elements filtered out, map to
build a new array where each element has been put through a function, and reduce to
combine all an arrays elements into a single value.

Functions have an apply method that can be used to call them with an array specifying their
arguments. They also have a bind method, which is used to create a partially applied version
of the function.

Use the reduce method in combination with the concat method to flatten an array of
arrays into a single array that has all the elements of the input arrays.

var arrays = [[1, 2, 3], [4, 5], [6]];


// Your code here.
// [1, 2, 3, 4, 5, 6]

Using the example data set from this chapter, compute the average age difference between
mothers and children (the age of the mother when the child is born). You can use the average
function defined earlier in this chapter.

Note that not all the mothers mentioned in the data are themselves present in the array. The
byName object, which makes it easy to find a persons object from their name, might be useful
here.

function average(array) {
function plus(a, b) { return a + b; }
return array.reduce(plus) / array.length;
}

var byName = {};


ancestry.forEach(function(person) {
byName[person.name] = person;
});

// Your code here.

// 31.2
When we looked up all the people in our data set that lived more than 90 years, only the latest
generation in the data came out. Lets take a closer look at that phenomenon.

Compute and output the average age of the people in the ancestry data set per century. A
person is assigned to a century by taking their year of death, dividing it by 100, and rounding
it up, as in Math.ceil(person.died / 100).

function average(array) {
function plus(a, b) { return a + b; }
return array.reduce(plus) / array.length;
}

// Your code here.

// 16: 43.5
// 17: 51.2
// 18: 52.8
// 19: 54.8
// 20: 84.7
// 21: 94

For bonus points, write a function groupBy that abstracts the grouping operation. It should
accept as arguments an array and a function that computes the group for an element in the
array and returns an object that maps group names to arrays of group members.

Arrays also come with the standard methods every and some. Both take a predicate function
that, when called with an array element as argument, returns true or false. Just like && returns
a true value only when the expressions on both sides are true, every returns true only when
the predicate returns true for all elements of the array. Similarly, some returns true as soon as
the predicate returns true for any of the elements. They do not process more elements than
necessaryfor example, if some finds that the predicate holds for the first element of the
array, it will not look at the values after that.

Write two functions, every and some, that behave like these methods, except that they take
the array as their first argument rather than being a method.

// Your code here.

console.log(every([NaN, NaN, NaN], isNaN));


// true
console.log(every([NaN, NaN, 4], isNaN));
// false
console.log(some([NaN, 3, 4], isNaN));
// true
console.log(some([2, 3, 4], isNaN));
// false
Captulo 6
La Vida Secreta De Los Objetos
El problema con los lenguajes orientados a objetos es que tienen todos un medio implcito
que llevan consigo. Tu quieres un pltano pero eres un gorila con un pltano y la jungla
entera.

Joe Armstrong, entrevistado en Coders at Work

Cuando un programador dice objeto, este es un trmino amplio. En mi profesin, los


objetos son una forma de vida, tema de guerras sagradas y una amada palabra llamativa que
todava no ha perdido su gran poder.

Para alguien ajeno, esto es probablemente un poco confuso. Empecemos con una
introduccin a la historia de los objetos como una forma de programacin.

Historia
Esta historia, como la mayor parte de las historias de programacin, comienza con el
problema de la complejidad. Una filosofa es que la complejidad puede ser manejable
separndola en pequeas partes, que son aisladas unas de otras. Estas partes han terminado
con el nombre de objetos.

Un objeto es una cpsula opaca que oculta una sofisticada complejidad en su interior y en su
lugar nos ofrece unos pocos reguladores y conectores (como por ejemplo mtodos) que
presentan una interfaz a travs de la cual el objeto es usado. La idea es que la la interfaz es
relativamente simple y todas las cosas complejas que van dentro del objeto pueden ser
ignoradas cuando trabajamos con el.
Como ejemplo, puedes imaginarte un objeto que te d una interfaz para un rea de la pantalla.
Te da una forma de dibujar formas o texto en este rea pero oculta los detalles de como esas
formas son convertidas a los pixeles que decoran la pantalla actualmete. Puedes tener un
conjunto de mtodos-por ejemplo dibujarCirculo-y estos son lo nico que necesitas para
usar un objeto.

Estas ideas fueron puestas en marcha en los 70, los 80 y los 90, fueron acompaadas por un
gran bombo-la revolucin de la programacin orientada a objetos. Inmediatamente haba un
grupo de gente declarando que los objetos eran el camino correcto a la programacin-y que
no incluir objetos era un sin sentido, estaba obsoleto.

Este tipo de fanatismo siempre produce mucha estupidez no prctica y ha habido una pequea
contra revolucin despus de esto. Actualmente en algunos crculos, los objetos tienen una
reputacin bastante mala.

Yo prefiero abordar el tema desde la prctica, en lugar de desde la ideologa. Hay varios
conceptos tiles, ms importantes que la encapsulacin (distinguir entre la complejidad
interna y externa de la interfaz), que la cultura de la programacin orientada a objetos a
popularizado. Estos son dignos de estudio.

En este captulo se describen de forma excntrica los objetos y las tcnicas clsicas sobre
como se relacionan entre s los objetos en JavaScript.

Mtodos
Los mtodos son propiedades simples que contienen funciones como valores. Este es un
mtodo simple:

var conejo = {};


conejo.hablar = function(linea) {
console.log("El conejo dice '" + linea + "'");
};

conejo.hablar("Estoy vivo.");
// El conejo dice 'Estoy vivo.'
Normalmente el mtodo necesita hacer algo con el objeto desde el que se le ha llamado.
Cuando una funcin es llamada como mtodo-se busca como propiedad y es inmediatamente
llamada, como en objeto.metodo()la variable especial this esta en su cuerpo y apuntar
al objeto que la ha llamado.

function hablar(linea) {
console.log("El conejo " + this.tipo + " dice '" +
line + "'");
}
var conejoBlanco = {tipo: "blanco", hablar: hablar};
var conejoGordo = {tipo: "gordo", hablar: hablar};

conejoBlanco.hablar("Por mis orejas y los pelos de mi " +


"bigote, que tarde se est haciendo!");
// El conejo blanco dice 'Por mis orejas y los pelos'
// de mi bigote, que tarde se est haciendo!'
conejoGordo.hablar("Puedes estar seguro de que me comera +"
"una zanahoria.");
// El conejo gordo dice 'Puedes estar seguro de que
// me comera una zanahoria.'

El cdigo usa la palabra clave this para la salida del tipo de conejo que est hablando. Se
puede rellamar con los mtodos apply y bind, ambos toman un primer argumento que puede
ser utilizado para simular llamadas al mtodo. El primer argumento es de echo utilizado para
dar valor a this.

Hay un mtodo similar a apply, llamado call. Este llama a la funcin que es un mtodo,
pero toma sus argumentos normalmente, en lugar de con un array. Como apply y bind, call
puede pasar un valor especfico de this.

hablar.apply(conejoGordo, ["Burp!"]);
// El conejo gordo dice 'Burp!'
hablar.call({tipo: "viejo"}, "Oh!, Ah!");
// El conejo viejo dice 'Oh!, Ah!'

Prototipos
Fjate detenidamente.

var vacio = {};


console.log(vacio.toString);
// function toString(){}
console.log(vacio.toString());
// [object Object]

Acabo de extraer una propiedad de un objeto vaco. Magia!

Bien, no realmente. Simplemente he omitido informacin acerca de como los Objetos


funcionan en JavaScript. Adems de sus propiedades, casi todos los objetos adems tienen un
prototipo. Un prototipo es otro objeto que es usado como alternativa fuente de propiedades.
Cuando un objeto tiene una llamada a una propiedad que no posee, se buscar en su
prototipo, despus en el prototipo de su prototipo y as sucesivamente.
Entonces, Cual es el prototipo de este objeto vaco? Es el genial prototipo ancestral, la
entidad detrs de casi todos los objetos, Object.prototype.

console.log(Object.getPrototypeOf({}) ==
Object.prototype);
// true
console.log(Object.getPrototypeOf(Object.prototype));
// null

Como imaginars la funcin Object.getPrototypeOf devuelve el prototipo de un objeto.

Las relaciones de prototipo en JavaScript tienen forma de rbol, y la raz de esta estructura es
Object.prototype. Este provee unos pocos mtodos que se mostrarn en casi todos los
objetos, como toString, que convierte un objeto en una representacin en una cadena de
texto.

Muchos objetos no tienen directamente Object.prototype como su prototipo, pero en su


lugar tienen otro objeto, que les provee sus propiedades por defecto. Las funciones derivan de
Function.prototype, y los arrays derivan de Array.prototype.

console.log(Object.getPrototypeOf(isNaN) ==
Function.prototype);
// true
console.log(Object.getPrototypeOf([]) ==
Array.prototype);
// true

Como un objeto prototipo tiene su propio prototipo normalmente Object.prototype,


entonces este indirectamente provee de mtodos como toString.

La funcin Object.getPrototypeOf obviamente devuelve el prototipo de un objeto. Puedes


usar Object.create para crear un objeto con un prototipo especfico.

var protoConejo = {
hablar: function(linea) {
console.log("El conejo " + this.type + " dice '" +
linea + "'");
}
};
var conejoAsesino = Object.create(protoConejo);
conejoAsesino.type = "asesino";
conejoAsesino.hablar("SKREEEE!");
// El conejo asesino dice 'SKREEEE!'

El proto conejo acta como container para las propiedades que son compartidas por todos
los conejos. Un objeto conejo individual, como el conejo asesino, contiene propiedades que
se aplican nicamente a s mismo, en este caso su tipo y propiedades derivadas de su
prototipo.

Constructores
Una forma ms conveniente de crear objetos que deriven su forma de prototipos compartidos
es usar un constructor. En JavaScript, llamar a una funcin con la palabra clave new delante
de ella, hace que sea tratada como un constructor. El constructor tendr su variable this en
los lmites del objeto creado, y si no se especfica otro valor de objeto este ser el nuevo
objeto que retorne la llamada.

Un objeto creado con new se dice que es una instancia de su constructor.

Tenemos un constructor simple para los conejos. Es una convencin capitalizar (poner la
primera letra en mayscula) los nombres de los constructores as son fcilmente distinguidos
de otras funciones.

function Conejo(tipo) {
this.tipo = tipo;
}

var conejoAsesino = new Conejo("asesino");


var conejoNegro = new Conejo("negro");
console.log(conejoNegro.tipo);
// negro

Los constructores (de hecho, todas las funciones) automticamente tienen una propiedad
llamada prototype, que por defecto contiene un objeto plano, vaco que deriva de
Object.prototype. Todas las instancias creadas con este constructor tendrn este objeto
como su prototipo. As que para aadir un mtodo hablar a los conejos creados con el
constructor Conejo, simplemente hacemos lo siguiente:

Conejo.prototype.hablar = function(linea) {
console.log("El conejo " + this.tipo + " dice '" +
linea + "'");
};
conejoNegro.hablar("Maldicin...");
// El conejo negro dice 'Maldicin...'

Es importante notar la diferencia entre la forma en que un prototipo es asociado con un


constructor (a travs de su propiedad prototype) y la forma en la que los objetos tienen un
prototipo (que podemos consultar con Object.getPrototypeOf). El prototipo actual de un
constructor es Function.prototype desde que los constructores son funciones. Esta
propiedad prototype ser el prototipo de las instancias creadas a travs de el pero no su
propio prototipo.

Sobre Escribiendo Las Propiedades Derivadas


Cuando aades una propiedad a un objeto, est presente en el prototipo o no, la propiedad es
aadida a ese objeto, que de ahora en adelante tendr como su propiedad. Si existe una
propiedad con el mismo nombre en el prototipo, esta propiedad no afectar ms al objeto. El
prototipo por si mismo no cambia.

Conejo.prototype.dentadura = "pequea";
console.log(conejoNegro.dentadura);
// pequea
conejoAsensino.dentadura = "larga, afilada y sangrienta";
console.log(conejoAsesino.dentadura);
// larga, afilada y sangrienta
console.log(conejoNegro.dentadura);
// pequea
console.log(Conejo.prototype.dentadura);
// pequea

El siguiente diagrama representa la situacin despus de ejecutar este cdigo. El Conejo y


Objeto prototipos estn detrs de conejoAsesino como una especie teln de fondo, donde
sus propiedades que no son encontradas en el objeto por si mismo pueden ser buscadas.

Sobre escribir propiedades que existen en un prototipo, es a menudo algo til que hacer.
Como muestra el ejemplo de la dentadura del conejo, esto puede ser usado para expresar
propiedades excepcionales en instancias de una clase ms genrica de objetos, mientras
dejamos los objetos no excepcionales simplemente tomar un valor estndar de su prototipo.

Esto es adems usado para dar a los prototipos de funcin y array un mtodo toString
diferente del bsico prototipo de los objetos.

console.log(Array.prototype.toString ==
Object.prototype.toString);
// false
console.log([1, 2].toString());
// 1,2

Llamar a toString en un array da un resultado similar a .join(",")-esto pone comas entre


los valores del array. Una llamada directa a Object.prototype.toString con un array
produce una cadena de texto diferente. Esta funcin no sabe acerca de arrays, as que
simplemente pone la palabra "object" y el nombre del tipo entre corchetes.

console.log(Object.prototype.toString.call([1, 2]));
// [object Array]

Interferencia de prototipos
Un prototipo puede ser usado en cualquier momento para aadir nuevas propiedades y
mtodos a todos los objetos basados en l. Por ejemplo, puede ser necesario para poner a
nuestros conejos a bailar.

Conejo.prototype.bailar = function() {
console.log("El conejo " + this.type + " baila un paso.");
};
conejoAsesino.bailar();
// El conejo asesino baila un paso.

Esto es conveniente. Pero hay situaciones donde esto causa problemas. En captulos
anteriores, hemos usado un objeto como forma de asociar valores con nombres creando
propiedades para los nombres y dndoles los correspondientes valores como su valor. Aqu
hay un ejemplo Chapter 4:

var mapa = {};


function guardarPhi(evento, phi) {
mapa[evento] = phi;
}

guardarPhi("pizza", 0.069);
guardarPhi("rbol tocado", -0.081);

Podemos iterar sobre todos los valores de phi en el objeto usando un bucle for/in y
comprobar cuando un nombre esta usando el operador regular in. Pero desafortunadamente,
el objeto del prototipo continua con su camino.

Object.prototype.sinSentido = "hola";
for (var nombre in mapa)
console.log(nombre);
// pizza
// rbol tocado
// sinSentido
console.log("sinSentido" in mapa);
// true
console.log("toString" in mapa);
// true

// Borrar la propiedad problemtica


delete Object.prototype.sinSentido;

Esta todo mal. No hay evento llamado "sinSentido" en nuestro set de datos. Y definitivamente
no hay evento llamado "toString".

Extraamente, toString no se muestra en el bucle for/in, pero el operador in ha retornado


true para el. Esto es por que JavaScript distingue entre propiedades enumerable
(enumerables) y nonenumerable (no enumerables).

Todas las propiedades que creamos simplemente asignndolas son enumerables. Las
propiedades estndar en Object.prototype son todas nonenumerable, que es por lo que no
se muestran en un bucle como un for/in.

Es posible definir nuestras propias propiedades nonenumberable usando la funcin


Object.defineProperty, esta nos permite controlar el tipo de propiedad que estamos
creando.

Object.defineProperty(Object.prototype, "ocultarSinSentido",
{enumerable: false, value: "hola"});
for (var nombre in mapa)
console.log(nombre);
// pizza
// rbol tocado
console.log(mapa.ocultarSinSentido);
// hola

Entonces ahora la propiedad esta, pero no se muestra en un bucle. Esto es bueno. Pero
seguimos teniendo el problema con el operador regular in demandando que las propiedades
del Object.prototype existen en nuestro objeto. Para esto, podemos usar el mtodo de
objeto hasOwnProperty.

console.log(mapa.hasOwnProperty("toString"));
// false

Este mtodo nos dice cuando el objeto por si mismo tiene la propiedad, sin mirar en sus
prototipos. Esto es a menudo una informacin ms til que la que nos da el operador in.

Cuando tu ests preocupado de que algo (algn otro cdigo que has incluido en tu programa)
puede tener problemas con el objeto base prototipo, te recomiendo escribir bucles for/in
como este:

for (var nombre in mapa) {


if (mapa.hasOwnProperty(nombre)) {
// ... esta es una propiedad propia
}
}

Objetos sin prototipo


Pero el agujero del conejo no acaba aqu. Qu pasa si alguien registra el nombre
hasOwnProperty en nuestro objeto mapa y le asigna el valor 42? Ahora la llamada a
mapa.hasOwnProperty intentar llamar a la propiedad local, que contiene un nmero, no una
funcin.

En este caso, los prototipos solo continan su camino y nosotros podemos preferir tener
objetos sin prototipos por ahora. Vemos la funcin Object.create, que nos permite crear un
objeto con un prototipo especfico. Le puedes pasar null como prototipo para crear un objeto
vaco sin prototipo. Para objetos como map, donde las propiedades pueden ser cualquiera, esto
es exactamente lo que queremos.

var mapa = Object.create(null);


mapa["pizza"] = 0.069;
console.log("toString" in mapa);
// false
console.log("pizza" in mapa);
// true

Mucho mejor! Ya no necesitaremos la chapuza de hasOwnProperty por que todas las


propiedades que el objeto tiene son sus propias propiedades. Ahora podemos usar de forma
segura bucles for/in, no hay problema con lo que la gente le haya estado haciendo a
Object.prototype.

Polimorfismo
Cuando llamas a la funcin String, que convierte un valor en una cadena, en un objeto, esta
llamar al mtodo toString cuando el objeto trate de crear una cadena con sentido para
retornarla. He mencionado que alguno de los prototipos estndar definen su propia versin de
toString as que ellos pueden crear cadenas que contengan informacin ms til que
"[object Object]".

Esta es una simple instancia de una poderosa idea. Cuando un trozo de cdigo es escrito para
trabajar con objetos que tienen una interfaz concreta -en este caso, un mtodo toString-
entonces cualquier tipo de objeto que soporte esta interfaz y pueda ser introducido en el
cdigo, simplemente funcionar.

Esta tcnica es llamada polimorfismo-aunque no hay cambio de forma real actualmente


involucrado. El cdigo polimrfico puede trabajar con valores de diferentes formas, tantas
como sean soportadas por la interfaz.

Dando estilo a una tabla


Voy a trabajar a travs de un ejemplo un poco ms complicado en un intento de darte una idea
mejor de como se utiliza el polimorfismo y la programacin orientada a objetos en general. El
proyecto es este: escribiremos un programa que, dado un array de arrays, de tabla celdas,
construya una cadena de texto que contenga un genial diseo de tabla-significa que las
columnas y las filas estn correctamente alineadas. Algo como esto:

nombre altura pas


------------ ------ -------------
Kilimanjaro 5895 Tanzania
Everest 8848 Nepal
Mount Fuji 3776 Japan
Mont Blanc 4808 Italy/France
Vaalserberg 323 Netherlands
Denali 6168 United States
Popocatepetl 5465 Mexico

La forma en que nuestro sistema de generacin de tablas funcionar es que la funcin


generadora preguntar a cada celda cual va a ser su ancho y alto y despus usar esa
informacin para determinar la anchura de las columnas y la altura de las filas. La funcin
generadora despus pedir a las celdas que se dibujen a s mismas con el tamao correcto y
ensamblando los resultados en una sola cadena.

El programa de estilo se comunicar con los objetos celda a travs de una interfaz bien
definida. De esta forma, los tipos de celda que el programa soporta no estarn fijados.
Podremos aadir nuevos tipos de celda ms adelante-por ejemplo, celdas subrayadas para la
cabecera de la tabla-y si lo soporta nuestra interfaz, simplemente funcionar, sin requerir
cambios al programa de diseo.

Esta es la interfaz:

minAltura() devuelve un nmero indicando la altura mnima que la celda requiere


(en lineas).
minAnchura() devuelve un nmero indicando la anchura mnima de esta celda en
caracteres).

dibujar(anchura, altura) devuelve un array de tamao altura, que contiene una


serie de cadenas que son cada anchura en caracteres. Esto representa el contenido de
la celda.

Voy a hacer uso intensivo de mtodos de orden superior en arrays en este ejemplo, ya que se
presta bien a este enfoque.

La primera parte del programa calcula arrays de los mnimos anchos de columna y altos de
fila para una grilla de celdas. La variable filas contendr un array de arrays, con cada array
interno representado una fila de celdas.

function alturasFila(filas) {
return filas.map(function(fila) {
return fila.reduce(function(max, celda) {
return Math.max(max, celda.minAltura());
}, 0);
});
}

function anchurasColumna(filas) {
return filas[0].map(function(_, i) {
return filas.reduce(function(max, fila) {
return Math.max(max, fila[i].minAnchura());
}, 0);
});
}

Usar un nombre de variable que comience con un guin bajo (_) o que consista en un simple
guin bajo es una forma de indicar (a los lectores humanos) que este argumento no se
utilizar.

La funcin alturasFila no debera ser demasiado difcil de seguir. Esta usa reduce para
calcular la altura mxima de un array de celdas y est dentro de un map para conseguir que se
haga para todas las filas en el array filas.

Las cosas son un poco mas complicadas para la funcin anchurasColumna por que el array
exterior es un array de filas, no de columnas. Se me ha olvidado mencionar que a map (como
a forEach, filter, y mtodos similares de array) se les puede pasar un segundo argumento,
este es en la funcin el ndice del elemento actual. Mapeando los elementos de la primera fila
y usando solo el segundo argumento de la funcin mapping, colWidths genera un array con
un elemento para cada ndice de columna. La llamada a reduce se ejecuta sobre el array
externo filas para cada ndice y se extrae la anchura de la celda ms ancha para ese ndice.

Aqu esta el cdigo para dibujar una tabla:

function dibujarTabla(filas) {
var alturas = alturasFilas(filas);
var anchuras = anchurasColumnas(filas);

function dibujarLinea(bloques, numLinea) {


return bloques.map(function(bloque) {
return bloque[numLinea];
}).join(" ");
}

function dibujarFila(fila, numFila) {


var bloques = fila.map(function(celda, numColumna) {
return celda.dibujar(anchuras[numColumna], alturas[numFila]);
});
return bloques[0].map(function(_, numLinea) {
return dibujarLinea(bloques, numLinea);
}).join("\n");
}

return filas.map(dibujarFila).join("\n");
}

La funcin dibujarTabla usa la funcin auxiliar interna dibujarFila para dibujar todas las
filas y despus unirlas todas con el caracteres de nueva lnea.

La funcin dibujarFila por si misma convierte los objetos celda en la fila a bloques, que
son los arrays de cadenas representando el contenido de las celdas, separados por lnea. Una
celda simple contiene simplemente el nmero 3776 puede ser representado como un elemento
simple de array como ["3776"], como una celda subrayada nos va a ocupar dos lineas ser
representada por el array ["nombre", "------"].

Los bloques para una fila, que tienen la misma altura, deben aparecer uno junto a otro en la
salida final. La segunda llamada a map en dibujarFila genera esta salida lnea a lnea
mapeando a travs de las lneas desde el bloque ms a la izquierda y, para cada uno de estos,
coleccionando una lnea que ocupa la anchura total de la tabla. Estas lneas estn unidas con
el carcter nueva lnea para proveer la fila entera como valor de retorno de dibujarFila.

La funcin dibujarLinea extrae lneas que deben aparecer unas junto a otras de un array de
bloques y las une con un carcter espacio para crear un hueco de un carcter entre las
columnas de la tabla.

Ahora vamos a escribir un constructor, para las celdas que contienen texto, que implementa la
interfaz para las celdas de la tabla. El constructor separa una cadena en un array de lneas
usando el mtodo de string split, que separa una cadena en cada ocurrencia de su argumento
y retorna un array de piezas. El mtodo minAnchura encuentra la mxima anchura de lnea en
este array.

function repetir(cadena, veces) {


var resultado = "";
for (var i = 0; i < veces; i++)
resultado += cadena;
return resultado;
}

function CeldaTexto(texto) {
this.texto = texto.split("\n");
}
CeldaTexto.prototype.minAnchura = function() {
return this.texto.reduce(function(anchura, linea) {
return Math.max(anchura, linea.length);
}, 0);
};
CeldaTexto.prototype.minAltura = function() {
return this.texto.length;
};
CeldaTexto.prototype.dibujar = function(anchura, altura) {
var resultado = [];
for (var i = 0; i < altura; i++) {
var linea = this.texto[i] || "";
resultado.push(linea + repetir(" ", anchura - linea.length));
}
return resultado;
};

El cdigo usa una funcin auxiliar llamada repetir que genera una cadena cuyo valor es el
argumento cadena repetido las veces que se indica. El mtodo dibujar se usa para aadir
espacio a las lneas ya que todas ellas tiene la longitud requerida.

Vamos a probar lo que hemos escrito hasta ahora generando un damero de 5 x 5.

var filas = [];


for (var i = 0; i < 5; i++) {
var fila = [];
for (var j = 0; j < 5; j++) {
if ((j + i) % 2 == 0)
fila.push(new CeldaTexto("##"));
else
fila.push(new CeldaTexto(" "));
}
filas.push(fila);
}
console.log(dibujarTabla(filas));
// ## ## ##
// ## ##
// ## ## ##
// ## ##
// ## ## ##

Esto funciona! Pero como todas las celda tienen la misma anchura, el cdigo de disear tabla
no hace algo realmente interesante.

La fuente de datos de la tabla de las montaas que estamos tratando de generar esta
disponible en la variable MOUNTAINS en el sandbox y adems es descargable y desde la web .

Queremos destacar la fila de arriba, que contiene los nombres de las columnas, subrayando
las celdas con una serie de caracteres guin. No hay problema-simplemente escribiremos un
tipo de celda que soporte subrayado.

<<<<<<< HEAD
function CeldaSubrayada(contenido) {
this.contenido = contenido;
};
CeldaSubrayada.prototype.minAnchura = function() {
return this.contenido.minAnchura();
=======
function UnderlinedCell(inner) {
this.inner = inner;
}
UnderlinedCell.prototype.minWidth = function() {
return this.inner.minWidth();
>>>>>>> marijnh/master
};
CeldaSubrayada.prototype.minAltura = function() {
return this.contenido.minAltura() + 1;
};
CeldaSubrayada.prototype.dibujar = function(anchura, altura) {
return this.contenido.dibujar(anchura, altura - 1)
.concat([repetir("-", anchura)]);
};

Una celda subrayada contiene otra celda. Esto significa que su tamao mnimo ser el mismo
que el de la celda interna (llamando a travs de los mtodos de estas celdas minAnchura y
minAltura) pero aade uno a la altura para contar el espacio tomado por el subrayado.

Dibujar una celda es muy simple -nosotros tomamos el contenido de la celda interior y le
concatenamos a una lnea simple de guiones.

Teniendo un mecanismo de subrayado, ahora podemos escribir una funcin que genere una
grilla de celdas para nuestro set de datos.

function datosTabla(datos) {
var keys = Object.keys(datos[0]);
var encabezados = keys.map(function(nombre) {
return new CeldaSubrayada(new TextCell(nombre));
});
var cuerpo = datos.map(function(row) {
return keys.map(function(nombre) {
return new CeldaTexto(String(row[nombre]));
});
});
return [encabezados].concat(cuerpo);
}

console.log(dibujarTabla(datosTabla(MOUNTAINS)));
// nombre altura pas
// ------------ ------ -------------
// Kilimanjaro 5895 Tanzania
// etctera

La funcin estndar Object.keys retorna un array de nombres de propiedades en un objeto.


La fila de arriba de la tabla debe contener celdas subrayadas que den los nombres a las
columnas. Debajo, los valores de todos los objetos en el set de datos parecen celdas
normales-los extraeremos mapeando sobre el array keys as que estamos seguros de que el
orden de las celdas es el mismo en cada fila.

La tabla resultante parece la del ejemplo mostrado antes, excepto por que on tiene el
alineamiento a la derecha de los nmero en la columna altura. Vamos a conseguirlo en un
momento.

Getters y setters
Cuando especificamos una interfaz, es posible incluir propiedades que no son mtodos.
Podemos tener definida minAltura y minAnchura para simplemente almacenar nmeros.
Pero esto podra requerir que lo calculramos en l constructor, esto aade cdigo en el que
no es estrictamente relevante para construir el objeto. Esto podra causar problemas si, por
ejemplo, el interior de una celda subrayada cambia, en este punto el tamao del subrayado de
la celda debera cambiar tambin.

Esto ha servido como excusa para adoptar el principio de no incluir nunca propiedades que
no sean mtodos en las interfaces. Ms que un acceso directo a un propiedad de valor simple,
se pueden usar los mtodos getAlgo y setAlgo para leer y escribir la propiedad. Esta
aproximacin tiene el inconveniente de que tu tienes que escribir -y leer- un montn de
mtodos adicionales.

Afortunadamente, JavaScript provee de una tcnica que nos da lo mejor de ambos mundos.
Podemos especificar propiedades que, dese fuera, parezcan propiedades normales pero
secretamente tienen mtodos asociados con ellas.

var pila = {
elementos: ["cascara de huevo", "peladura de naranja", "gusano"],
get altura() {
return this.elementos.length;
},
set altura(valor) {
console.log("Ignorando el intento de guardar la altura: ", valor);
}
};

console.log(pila.altura);
// 3
pila.altura = 100;
// Ignorando el intento de guardar la altura: 100

En un objeto literal, la notacin get o set para propiedades te permite especificar una
funcin para ser ejecutada cuando la propiedad es leda o escrita. Podemos incluso aadir una
propiedad a un objeto existente, por ejemplo un prototipo, usando la funcin
Object.defineProperty (que hemos usado previamente para crear propiedades
nonenumerable).

Object.defineProperty(CeldaTexto.prototype, "alturaProp", {
get: function() { return this.texto.length; }
});

var celda = new CeldaTexto("sin\nsalida");


console.log(celda.alturaProp);
// 2
celda.alturaProp = 100;
console.log(celda.alturaProp);
// 2

Puedes usar la propiedad similar set, en el objeto pasndola a defineProperty, para


especificar un mtodo setter. Cuando se define un getter pero no un setter, escribir la
propiedad es simplemente ignorado.

Herencia
Todava no hemos acabado el ejercicio de diseo de tabla. Ayuda a la legibilidad alinear a la
derecha las columnas con nmeros. Debemos crear otro tipo de celda que es como
CeldaTexto, pero sin espacio en la parte derecha, estas tienen el espacio en la parte izquierda
as que alinimoslas a la derecha.

Podemos simplemente escribir un nuevo constructor entero con los tres mtodos en su
prototipo. Pero los prototipos pueden tener sus prototipos, y esto nos permite hacer algo
inteligente.

function DCeldaTexto(texto) {
CeldaTexto.call(this, texto);
}
DCeldaTexto.prototype = Object.create(CeldaTexto.prototype);
DCeldaTexto.prototype.dibujar = function(anchura, altura) {
var resultado = [];
for (var i = 0; i < altura; i++) {
var linea = this.text[i] || "";
resultado.push(repetir(" ", anchura - linea.length) + linea);
}
return resultado;
};

Reutilizamos el constructor y los mtodos minAltura y minAnchura de CeldaTexto. Una


DCeldaTexto es ahora bsicamente equivalente a CeldaTexto, excepto por que su mtodo
dibujar contiene una funcin diferente.

Este patrn es llamado herencia. Este nos permite generar tipos de datos muy similares desde
tipos de datos existente con poco trabajo relativamente. Tpicamente, el nuevo constructor
llamar al viejo constructor (usando el mtodo call para permitir darle al nuevo objeto su
valor this). Una vez este constructor se ha llamado, podemos asumir que todos los campos
que el tipo de objeto viejo tena han sido aadidos. Arreglamos el constructor del prototipo
para derivarlo al del viejo prototipo as que las instancias de este prototipo tendrn tambin
acceso a las propiedades del viejo prototipo. Finalmente podemos sobre escribir alguna de
esas propiedades aadindolas a nuestro nuevo prototipo.

Ahora, si ajustamos un poco la funcin datosTabla para usar DCeldaTextos para celdas
cuyo valor sea un nmero, tendremos la tabla que estbamos buscando.

function datosTabla(datos) {
var keys = Object.keys(datos[0]);
var encabezados = keys.map(function(nombre) {
return new CeldaSubrayada(new CeldaTexto(nombre));
});
var cuerpo = datos.map(function(row) {
return keys.map(function(nombre) {
var valor = row[nombre];
// Esto ha cambiado:
if (typeof valor == "number")
return new DCeldaTexto(String(valor));
else
return new CeldaTexto(String(valor));
});
});
return [encabezados].concat(cuerpo);
}
console.log(dibujarTabla(datosTabla(MOUNTAINS)));
// preciosa tabla alineada

La herencia es una parte fundamental de la tradicin de la orientacin a objetos, junto con la


encapsulacin y el polimorfismo. Pero mientras las dos ltimas son generalmente
consideradas como ideas geniales, la herencia es algo controvertido.

La principal razn para esto es que a menudo es confundida con el polimorfismo, vendido
como una herramienta ms poderosa de lo que en realidad es, y posteriormente sobre
utilizado de todas las malas formas posibles. Mientras que la encapsulacin y el polimorfismo
pueden ser usados para separar trozos de cdigo de otros, reduciendo el enmaraado general
del programa, la herencia fundamentalmente empata con ambos, creando ms enmaraado.

Puedes tener polimorfismo sin herencia, como hemos visto. No te voy a decir que evites la
herencia por completo-Yo la uso regularmente en mis programas. Pero tu debes verla como
un truco un poco sucio que te puede ayudar a definir nuevos tipos con poco cdigo, no como
un gran principio de organizacin de cdigo. Una forma mejor de extender tipos es a travs
de composicin, como CeldaSubrayada genera otro objeto celda simplemente guardndolo
en una propiedad y remitiendo las llamadas a los mtodos a sus propios mtodoss.

El operador instanceof
Es ocasionalmente til conocer cuando un objeto deriva de un constructor especfico. Para
esto, JavaScript tiene un operador binario llamado instanceof.

console.log(new DCeldaTexto("A") instanceof DCeldaTexto);


// true
console.log(new DCeldaTexto("A") instanceof CeldaTexto);
// true
console.log(new CeldaTexto("A") instanceof DCeldaTexto);
// false
console.log([1] instanceof Array);
// true

El operador ver a travs de los tipos heredados. Una DCeldaTexto es una instancia de
CeldaTexto porque DCeldaTexto.prototype deriva de CeldaTexto.prototype. El
operador puede ser aplicado a constructores estndar como Array. Aunque casi todos los
Objetos son una instancia de Object.

Resumen
Entonces los objetos son ms complicados de lo que inicialmente he mostrado. Tienen
prototipos, que son otros objetos, y actuarn como si tuviesen las propiedades que no tienen,
si las tienen sus prototipos. Los objetos simples tienen Object.prototype como su
prototipo.

Los constructores, que son funciones cuyos nombres normalmente empiezan con una
mayscula, pueden ser usados con el operador new para crear nuevos objetos. Los nuevos
prototipos de los objetos se encontrarn en la propiedad prototype de la funcin
constructora. Puedes hacer buen uso de esto poniendo las propiedades que comparten todos
los valores de un tipo dado en su prototipo. El operador instanceOf puede, dado un objeto y
un constructor, decirte cuando ese objeto es una instancia de ese constructor.

Algo til para hacer con objetos es especificar una interfaz para ellos y decir a todos los que
van a comunicarse con el objeto que lo hagan solo a travs de la interfaz. El resto de detalles
que maquillan tu objeto son ahora encapsulados, ocultados tras la interfaz.

Ahora que estamos hablando en trminos de interfaces, quien dice que solo un tipo de objeto
puede implementarse en esa interfaz? Tener diferentes objetos expuestos a la misma interfaz
y despus escribir cdigo que funcione en cualquier objeto, es la interfaz llamada
polimrfica. Esto es muy til.

Cuando implementamos mltiples tipos que difieren solo en algunos detalles, esto puede
ayudar a simplificar haciendo el que el prototipo del nuevo tipo de objeto derive del prototipo
del viejo y tener un nuevo constructor que puede llamar al antiguo. Esto te da un tipo de
objeto similar al viejo pero puedes aadir y sobre escribir propiedades que veas que encajan.

Ejercicios
Un tipo vector

Escribe un constructor Vector que represente un vector en un espacio de dos dimensiones.


Este toma x e y como parmetros (nmeros), que se deben guardar como propiedades del
mismo nombre.

Aade al prototipo Vector dos mtodos, mas y menos, que toman otro vector como parmetro
y devuelven un nuevo vector con el resultado de la suma o resta de los dos vectores (el vector
almacenado en this y el parmetro) con sus valores x e y.

Aade una propiedad getter longitud al prototipo que calcule la longitud del vector-esto es,
la distancia del punto (x, y) desde el origen (0,0).

// Your code here.

console.log(new Vector(1, 2).mas(new Vector(2, 3)));


// Vector{x: 3, y: 5}
console.log(new Vector(1, 2).menos(new Vector(2, 3)));
// Vector{x: -1, y: -1}
console.log(new Vector(3, 4).longitud);
// 5

Otra celda

Implementa un tipo de celda llamado CeldaEstirar(contenido, anchura, altura) que


se ajuste a la interfaz tabla celda descrita previamente en el captulo. Esta debe contener otra
celda (como hace CeldaSurayada) y asegurar que la celda resultante tiene al menos la
anchura y altura dadas, incluso si el contenido de la celda pueda ser naturalmente menor.

// Your code here.

var sc = new CeldaEstirar(new CeldaTexto("abc"), 1, 2);


console.log(sc.minAnchura());
// 3
console.log(sc.minAltura());
// 2
console.log(sc.dibujar(3, 2));
// ["abc", " "]

Interface secuencia

Disea una interfaz que resuma la iteracin sobre una coleccin de valores. El objeto que
provee a esta interfaz representa una secuencia. La interfaz debe mostrar como se hace esto
posible usando un objeto para iterar sobre la secuencia, mirando los valores que tienen el
elemento y con forma de detectar cuando se ha llegado al final de la secuencia.

Cuando hayas especificado tu interfaz, intenta escribir una funcin mostrarCinco que tome
el objeto secuencia y llame a console.log en sus primeros cinco elementos-o menos, si la
secuencia tiene menos de cinco elementos.

Despus implementa un objeto del tipo ArraySec que contenga un array y permita la
iteracin sobre el array usando la interfaz que has diseado. Implementa otro tipo de objeto
RangoSec que itere sobre un rango de enteros (tomando los argumentos desde y hasta en su
constructor) en su lugar.

// Your code here.

mostrarCinco(new ArraySeq([1, 2]));


// 1
// 2
mostrarCinco(new RangeSeq(100, 1000));
// 100
// 101
// 102
// 103
// 104

Captulo 7
Proyecto: Vida Electrnica
[...] La pregunta de si las mquinas pueden pensar [...] es tan relevante como la pregunta de si
los submarinos pueden nadar.

Edsger Dijkstra, The Threats to Computing Science

In project chapters, En los captulos de proyecto, dejar de abrumarte con teora nueva por
un breve momento, y en lugar de eso trabajaremos a travs de un programa juntos. La teora
es indispensable cuando aprendemos a programar, pero debera ser acompaada de lecturas y
la comprensin de programas no triviales.

Nuestro proyecto en este captulo es construir un ecosistema virtual, un mundo pequeo


poblado con bichos que se mueven alrededor y luchan por sobrevivir.
Definicin
Para hacer esta tarea manejable, nosotros simplificaremos radicalmente el concepto de mundo
(world). Es decir un mundo ser una cuadricula de dos dimensiones donde cada entidad
ocupa un cuadro completo de ella. En cada turn, todos los bichos tienen oportunidad de hacer
alguna accin.

Por lo tanto, cortamos ambos tiempo y espacio en dos unidades con un tamao fijo: cuadros
para espacio y turnos para tiempo. Por supuesto, esto es una burda e imprecisa aproximacin.
Pero nuestra simulacin pretende ser entretenida, no precisa, as que podemos acortar
libremente las dimensiones.

Podemos definir un mundo como un plan, una matriz de cadenas que establece la cuadrcula
del mundo usando un carcter por cuadro. .

var plan = ["############################",


"# # # o ##",
"# #",
"# ##### #",
"## # # ## #",
"### ## # #",
"# ### # #",
"# #### #",
"# ## o #",
"# o # o ### #",
"# # #",
"############################"];

El carcter "#" en este programa representa paredes y rocas, y el carcter "o" representa
bichos (critters). Los espacios, como posiblemente habrs adivinado, son espacios vacos.

Una matriz unidimensional puede ser usada para crear un objeto mundo (world). Tal objeto
mantiene seguimiento del tamao y el contenido. El mundo tiene un mtodo toString que
convierte al mundo nuevamente en una cadena imprimible (parecida al programa en el que se
bas) de manera que podamos ver qu es lo que est pasando dentro. El objeto mundo
tambin tiene un mtodo turn, el cual permite a todos los bichos que lo habitan tomar un
turno y luego actualizar el mundo reflejando sus acciones.

Representando el espacio.
La cuadrcula (grid) que modela el mundo tiene un ancho y altura fija. Los cuadros son
identificados por sus coordenadas "X" y "Y". Usamos un tipo sencillo, Vector (como los
vistos en los ejercicios del captulo anterior), para representar estas coordenadas en pares.

function Vector(x, y) {
this.x = x;
this.y = y;
}
Vector.prototype.plus = function(other) {
return new Vector(this.x + other.x, this.y + other.y);
};
A continuacion, necesitamos un tipo de objeto que modele por si mismo la cuadricula (grid).
La cuadricula es parte del mundo, pero nosotros estamos haciendo la cuadricula como un
objeto separado (que sera una propiedad del objeto mundo) para mantener el objeto world
simple. El mundo debe ocuparse de las cosas relacionadas con el mundo, y la cuadricula debe
ocuparse de las cosas relacionadas con la cuadricula.

Para almacenar una cuadricula de valores, tenemos varias opciones. Podemos utilizar una
matriz de matrices de filas (array of row arrays) y utilizar dos propiedades de acceso para
llegar a una cruadricula especfica:

var grid = [["top left", "top middle", "top right"],


["bottom left", "bottom middle", "bottom right"]];
console.log(grid[1][2]);
// bottom right

O podemos utilizar una sola matriz, con el tamao de ancho x alto, y decidir que el elemento
en (x,y) se encuentra en la posicin x + (y x ancho) de la matriz.

var grid = ["top left", "top middle", "top right",


"bottom left", "bottom middle", "bottom right"];
console.log(grid[2 + (1 * 3)]);
// bottom right

Dado que el acceso real a esta matriz (Array) ser envuelto en mtodos en el objeto de tipo
cuadricula, no le importa al cdigo externo cual enfoque tomamos. Eleg la segunda
representacin, ya que hace que sea mucho ms fcil crear la matriz. Al llamar al constructor
de Array con un solo nmero como argumento, se crea una nueva matriz vaca de la longitud
dada.

Este cdigo define el objeto cuadricula (Grid) con algunos mtodos bsicos:

function Grid(width, height) {


this.space = new Array(width * height);
this.width = width;
this.height = height;
}
Grid.prototype.isInside = function(vector) {
return vector.x >= 0 && vector.x < this.width &&
vector.y >= 0 && vector.y < this.height;
};
Grid.prototype.get = function(vector) {
return this.space[vector.x + this.width * vector.y];
};
Grid.prototype.set = function(vector, value) {
this.space[vector.x + this.width * vector.y] = value;
};

Y aqu es una prueba trivial:

var grid = new Grid(5, 5);


console.log(grid.get(new Vector(1, 1)));
// undefined
grid.set(new Vector(1, 1), "X");
console.log(grid.get(new Vector(1, 1)));
// X
Una interfaz para programar bichos (critter)
Antes de que podamos comenzar con nuestro constructor en nuestro mundo, debemos
obtener ms especificaciones sobre los objetos critter que estarn viviendo dentro de l.
Mencion que el mundo preguntar a las criaturas qu acciones quieren tomar. Esto funciona
de esta manera: cada objeto critter tiene un mtodo act que, cuando se lo llama, devuelve una
accin. Una accin es un objeto con una propiedad de tipo (type), que indica el tipo de accin
que el critter quiere tomar, por ejemplo "mover". La accin tambin puede contener
informacin adicional, como la direccin en la que el critter quiere moverse.

Los Critters son terriblemente miopes y pueden ver solamente los cuadrados directamente
alrededor de ellos en la cuadricula. Pero incluso esta visin limitada puede ser til al decidir
qu accin tomar. Cuando se llama al mtodo act, se recibe un objeto view que permite al
critter inspeccionar su entorno. Nombramos los ocho cuadrados circundantes por sus
direcciones de la brjula: "n" para el norte, "ne" para el noreste, y as sucesivamente. Este es
el objeto que usaremos para asignar los nombres de direccin a los desplazamientos de
coordenadas:

var directions = {
"n": new Vector( 0, -1),
"ne": new Vector( 1, -1),
"e": new Vector( 1, 0),
"se": new Vector( 1, 1),
"s": new Vector( 0, 1),
"sw": new Vector(-1, 1),
"w": new Vector(-1, 0),
"nw": new Vector(-1, -1)
};

El objeto view tiene un mtodo look, que toma una direccin y devuelve un carcter, por
ejemplo "" cuando hay una pared en esa direccin, o " " (espacio) cuando no hay nada all. El
objeto tambin proporciona los mtodos convenientes find y findAll. Ambos toman un
carcter de mapa como argumento. El primero devuelve una direccin en la que el carcter se
puede encontrar con respecto al critter o devuelve null si no existe tal direccin. El segundo
devuelve una matriz que contiene todas las direcciones con ese carcter. Por ejemplo, una
criatura sentada a la izquierda (al oeste) de una pared obtendr ["ne", "e", "se"] al llamar a
findAll en su objeto de vista con el carcter "" como argumento.

Aqu est un critter simple y estpido que sigue su nariz hasta que golpea un obstculo y
luego rebota en una direccin al azar:

function randomElement(array) {
return array[Math.floor(Math.random() * array.length)];
}

var directionNames = "n ne e se s sw w nw".split(" ");

function BouncingCritter() {
this.direction = randomElement(directionNames);
};

BouncingCritter.prototype.act = function(view) {
if (view.look(this.direction) != " ")
this.direction = view.find(" ") || "s";
return {type: "move", direction: this.direction};
};

La funcin de ayuda randomElement simplemente selecciona un elemento aleatorio de una


matriz (Array), usando Math.random ms algn clculo aritmtico obtendremos un ndice
aleatorio. Esto lo usaremos ms adelante porque la aleatoriedad puede ser til en
simulaciones.

Para escoger una direccin aleatoria, el constructor BouncingCritter llama a randomElement


en una matriz directionNames. Tambin podramos haber usado Object.keys para obtener esta
matriz del objeto directions que definimos anteriormente, pero eso no proporciona garantas
sobre el orden en el que se enumeran las propiedades. En la mayora de las situaciones, los
motores de JavaScript modernos devolvern las propiedades en el orden en que se definieron,
pero no necesariamente.

El || "S" en el mtodo act est ah para evitar que this.direction de valor null si el critter
est de alguna manera atrapado sin espacio vaco alrededor de l (por ejemplo, cuando se
aglomeran en una esquina con otros critters).

El objeto del mundo


Ahora podemos comenzar con el tipo de objeto World. El constructor toma un plan (la matriz
de cadenas que representa la cuadrcula del mundo, descrita anteriormente) y una leyenda
(legend) como argumentos. Una leyenda es un objeto que nos dice qu significa cada carcter
en el mapa. Contiene un constructor para cada carcter, excepto el carcter de espacio, que
siempre refiere a null y es el valor que usaremos para representar el espacio vaco.

function elementFromChar(legend, ch) {


if (ch == " ")
return null;
var element = new legend[ch]();
element.originChar = ch;
return element;
}

function World(map, legend) {


var grid = new Grid(map[0].length, map.length);
this.grid = grid;
this.legend = legend;

map.forEach(function(line, y) {
for (var x = 0; x < line.length; x++)
grid.set(new Vector(x, y),
elementFromChar(legend, line[x]));
});
}

En elementFromChar, primero creamos una instance del tipo correcto buscando el


constructor del carcter y aplicndole new. A continuacin, agregamos una propiedad
originChar a ella para que sea fcil averiguar de qu carcter se cre el elemento
originalmente.
Necesitamos esta propiedad originChar al implementar el mtodo toString de word. Este
mtodo construye una cadena maplike del estado actual del mundo realizando un bucle
bidimensional sobre los cuadros de la cuadrcula.

function charFromElement(element) {
if (element == null)
return " ";
else
return element.originChar;
}

World.prototype.toString = function() {
var output = "";
for (var y = 0; y < this.grid.height; y++) {
for (var x = 0; x < this.grid.width; x++) {
var element = this.grid.get(new Vector(x, y));
output += charFromElement(element);
}
output += "\n";
}
return output;
};

Una pared es un objeto simple: se usa slo para ocupar espacio y no tiene mtodo act.

function Wall() {}

Cuando probamos el objeto World creando una instancia basada en el plan del captulo
anterior y luego invocando toString sobre l, obtenemos una cadena muy similar al plan.

var world = new World(plan, {"#": Wall,


"o": BouncingCritter});
console.log(world.toString());
// ############################
// # # # o ##
// # #
// # ##### #
// ## # # ## #
// ### ## # #
// # ### # #
// # #### #
// # ## o #
// # o # o ### #
// # # #
// ############################

This y su alcance
El constructor World contiene una llamada a forEach. Una cosa interesante a notar es que
dentro de la funcin pasada a forEach ya no estamos directamente en el mbito de funciones
del constructor. Cada llamada de funcin obtiene su propia vinculacin para this, por lo que
el this en la funcin interna no se refiere al mismo objeto en la nueva construccin que se
refiere this en la funcion externa. De hecho, cuando una funcin no se llama como un
(mtodo), this har referencia al objeto global.
Esto significa que no podemos escribir this.grid para acceder a la cuadrcula desde dentro del
bucle. En su lugar, la funcin externa crea una variable local normal, grid, a travs de la cual
la funcin interna obtiene acceso a la cuadrcula.

Esto es un poco un error de diseo en JavaScript. Afortunadamente, la siguiente versin del


lenguaje proporciona una solucin para este problema. Mientras tanto, hay soluciones. Un
patrn comn es decir var self = this y a partir de entonces se refiere a self, que es una
variable normal y, por lo tanto, visible a las funciones internas.

Otra solucin es utilizar el mtodo bind, que nos permite proporcionar un objeto explcito a
enlazar.

var test = {
prop: 10,
addPropTo: function(array) {
return array.map(function(elt) {
return this.prop + elt;
}.bind(this));
}
};
console.log(test.addPropTo([5]));
// [15]

La funcin pasada al map es el resultado de la llamada de enlace (bind call) y por lo tanto
this est vinculado al primer argumento dado a bind, el valor this de la funcin externa (que
contiene el objeto de prueba).

La mayora de los mtodos estndar de orden superior en matrices (arrays), como forEach y
map, toman un segundo argumento opcional que tambin se puede utilizar para proporcionar
un this para las llamadas a la funcin de iteracin. As que podra expresar el ejemplo anterior
de una manera un poco ms simple.

var test = {
prop: 10,
addPropTo: function(array) {
return array.map(function(elt) {
return this.prop + elt;
}, this); // no bind
}
};
console.log(test.addPropTo([5]));
// [15]

Esto slo funciona para las funciones de orden superior que soportan dicho parmetro de
contexto. Cuando no lo hacen, tendr que utilizar uno de los otros enfoques.

En nuestras propias funciones de orden superior, podemos utilizar este parmetro de contexto
utilizando el mtodo call para llamar a la funcin dada como argumento. Por ejemplo, aqu
hay un mtodo forEach para nuestro tipo Grid, que llama a una funcin dada para cada
elemento de la cuadrcula que no es null o undefined:

Grid.prototype.forEach = function(f, context) {


for (var y = 0; y < this.height; y++) {
for (var x = 0; x < this.width; x++) {
var value = this.space[x + y * this.width];
if (value != null)
f.call(context, value, new Vector(x, y));
}
}
};

Animando la vida
El siguiente paso es escribir un mtodo de turn para el objeto word que da a los bichos la
oportunidad de actuar. Recorrer la cuadrcula usando el mtodo forEach que acabamos de
definir, buscando objetos con un mtodo act. Cuando encuentre uno, turn llamadra dicho
mtodo act para obtener un objeto y llevar a cabo ese tipo de accin. Por ahora, solo se
entienden las acciones tipo "mover".

Hay un problema potencial con este enfoque. Puedes detectarlo? Si dejamos que los bichos
se muevan al cruzarlos, pueden moverse a un cuadrado que todava no hemos visto, y les
permitiremos moverse de nuevo cuando alcancemos ese cuadrado. Por lo tanto, tenemos que
mantener una serie de criaturas que ya han tenido su turno e ignorarlos cuando los vemos de
nuevo.

World.prototype.turn = function() {
var acted = [];
this.grid.forEach(function(critter, vector) {
if (critter.act && acted.indexOf(critter) == -1) {
acted.push(critter);
this.letAct(critter, vector);
}
}, this);
};

Utilizamos el segundo parmetro del mtodo forEach de la cuadrcula para poder acceder al
this correcto dentro de la funcin interna. El mtodo letAct contiene la lgica real que permite
a los critters moverse.

World.prototype.letAct = function(critter, vector) {


var action = critter.act(new View(this, vector));
if (action && action.type == "move") {
var dest = this.checkDestination(action, vector);
if (dest && this.grid.get(dest) == null) {
this.grid.set(vector, null);
this.grid.set(dest, critter);
}
}
};

World.prototype.checkDestination = function(action, vector) {


if (directions.hasOwnProperty(action.direction)) {
var dest = vector.plus(directions[action.direction]);
if (this.grid.isInside(dest))
return dest;
}
};
Primero, pedimos simplemente al critter que acte, pasndola un objeto view con datos sobre
el mundo y la posicin actual de la criatura (definiremos view en un momento). El mtodo
act devuelve una accin de algn tipo.

Si el tipo (type) de la accin no es "mover", se ignora. Si es "mover", si tiene una propiedad


de direccin que se refiere a una direccin vlida, y si el cuadrado en esa direccin es vaco
(null), fijamos el cuadrado donde el critter estaba como null y almacenamos el critter en el
cuadrado de destino.

Tenga en cuenta que letAct se encarga de ignorar la entrada no valida, no asume que la
propiedad direction de la accin es vlida o que la propiedad type tiene sentido. Este tipo de
(programacin defensiva) tiene sentido en algunas situaciones. La principal razn para
hacerlo es validar entradas procedentes de fuentes que no controla (como la entrada de
usuario o de archivo), pero tambin puede ser til para aislar subsistemas entre s. En este
caso, la intencin es que los critters puedan ser programados descuidadamente (no tienen que
verificar si sus acciones previstas tienen sentido. Pueden solicitar una accin, y el mundo
averiguar si permitirla).

Estos dos mtodos no forman parte de la interfaz externa de un objeto World. Ellos son un
detalle interno. Algunos lenguajes proporcionan formas de declarar explcitamente ciertos
mtodos y propiedades privadas y sealan un error cuando intenta utilizarlos desde fuera del
objeto. JavaScript no, por lo que tendr que depender de alguna otra forma de comunicacin
para describir lo que es parte de la interfaz de un objeto. A veces puede ayudar utilizar un
esquema de nomenclatura para distinguir entre propiedades externas e internas, por ejemplo
prefijando todas las internas con un carcter de subrayado (_). Esto har que los usos
accidentales de las propiedades que no sean parte de la interfaz de un objeto sean ms fciles
de detectar.

La parte que falta, el tipo view, se parece a esto:

function View(world, vector) {


this.world = world;
this.vector = vector;
}
View.prototype.look = function(dir) {
var target = this.vector.plus(directions[dir]);
if (this.world.grid.isInside(target))
return charFromElement(this.world.grid.get(target));
else
return "#";
};
View.prototype.findAll = function(ch) {
var found = [];
for (var dir in directions)
if (this.look(dir) == ch)
found.push(dir);
return found;
};
View.prototype.find = function(ch) {
var found = this.findAll(ch);
if (found.length == 0) return null;
return randomElement(found);
};
El mtodo look calcula las coordenadas que estamos tratando de ver y, si estn dentro de la
cuadrcula, encuentra el carcter correspondiente al elemento que se encuentra all. Para las
coordenadas fuera de la cuadrcula, look simplemente finge que hay una pared all de modo
que si usted define un mundo sin paredes, las criaturasno sern tentadas de intentar caminar
fuera de los bordes.

Se mueve
Hemos instanciado un objeto del mundo anterior. Ahora que hemos aadido todos los
mtodos necesarios, debera ser posible hacer que el mundo se mueva.

for (var i = 0; i < 5; i++) {


world.turn();
console.log(world.toString());
}
// five turns of moving critters

Sin embargo, imprimir muchas copias del mapa es una manera bastante desagradable de
observar un mundo. Es por eso que el sandbox proporciona una funcin animateWorld que
ejecutar un mundo como una animacin en pantalla, movindose tres veces por segundo,
hasta que pulse el botn de parada.

animateWorld(world);
// life!

La implementacin de animateWorld seguir siendo un misterio por ahora, pero despus de


haber ledo los captulos posteriores de este libro, y estudiar la integracin de JavaScript en
los navegadores web, ya no se ver tan mgico.

Ms formas de vida
El punto culminante dramtico de nuestro mundo, si usted mira para un pedacito, es cuando
dos critters rebotan el uno al otro. Puedes pensar en otra forma interesante de
comportamiento?

Podemos hacer un critter que se mueve a lo largo de las paredes (WallFollower).


Conceptualmente, el critter mantiene su mano izquierda (pata, tentculo, lo que sea) a la
pared y sigue adelante. Este no es un cambio trivial de implementar:

Tenemos que ser capaces de hacer calculos" con las direcciones de la brjula. Como las
direcciones son modeladas por un conjunto de cadenas, deberamos definir nuestra propia
operacin (dirPlus) para calcular direcciones relativas. As dirPlus ("n", 1) significa una
vuelta de 45 grados en sentido horario desde el norte, dando "ne". Del mismo modo, dirPlus
("s", -2) significa 90 grados en sentido antihorario desde el sur, que es el este.

function dirPlus(dir, n) {
var index = directionNames.indexOf(dir);
return directionNames[(index + n + 8) % 8];
}

function WallFollower() {
this.dir = "s";
}

WallFollower.prototype.act = function(view) {
var start = this.dir;
if (view.look(dirPlus(this.dir, -3)) != " ")
start = this.dir = dirPlus(this.dir, -2);
while (view.look(this.dir) != " ") {
this.dir = dirPlus(this.dir, 1);
if (this.dir == start) break;
}
return {type: "move", direction: this.dir};
};

El mtodo act slo tiene que "escanear" el entorno del critter, comenzando por su lado
izquierdo y en sentido horario hasta encontrar un cuadrado vaco. Entonces se mueve en la
direccin de ese cuadrado vaco.

Lo que complica las cosas es que un critter puede terminar en el medio del espacio vaco, ya
sea como su posicin inicial o como resultado de caminar alrededor de otro critter. Si
aplicamos el enfoque que acabo de describir en el espacio vaco, el pobre critter seguir
girando a la izquierda en cada paso, corriendo en crculos.

De modo que hay una comprobacin extra (la sentencia if) para iniciar el escaneado hacia la
izquierda slo si parece que el critter acaba de pasar algn tipo de obstculo, es decir, si el
espacio detrs y hacia la izquierda del critter no est vaco. De lo contrario, el critter
comienza a escanear directamente delante, de modo que va a caminar recto en el espacio
vaco.

Y finalmente, hay una prueba que compara this.dir con start despus de cada paso a travs
del loop para cerciorarse de que el loop no funcionar para siempre cuando el critter est
amurallado adentro o apretado adentro por otros critters y no puede encontrar un cuadrado
vaco.

Este pequeo mundo muestra las criaturas que siguen la pared:

animateWorld(new World(
["############",
"# # #",
"# ~ ~ #",
"# ## #",
"# ## o####",
"# #",
"############"],
{"#": Wall,
"~": WallFollower,
"o": BouncingCritter}
));

Una simulacin ms realista


Para hacer la vida en nuestro mundo ms interesante, vamos a aadir los conceptos de la
comida y la reproduccin. Cada ser vivo en el mundo recibe una nueva propiedad, la energa,
que se reduce mediante la realizacin de acciones y el aumento de comer cosas. Cuando la
criatura tiene suficiente energa, puede reproducirse, generando una nueva criatura del mismo
tipo. Para mantener las cosas simples, las criaturas en nuestro mundo se reproducen
asexualmente por s solas.

Si los bichos slo se mueven y comen unos a otros, el mundo pronto sucumbir a la ley de
entropa creciente, se quedar sin energa, y se convertir en un desierto sin vida. Para evitar
que esto suceda (demasiado rpido, al menos), aadimos plantas al mundo. Las plantas no se
mueven. Simplemente utilizan la fotosntesis para crecer (es decir, aumentar su energa) y
reproducirse.

Para que esto funcione, necesitaremos un mundo con un mtodo letAct diferente. Podramos
simplemente reemplazar el mtodo del (prototipo world), pero me he vuelto muy apegado a
nuestra simulacin con las criaturas que siguen a la pared y odiara romper ese viejo mundo.

Una solucin es usar la (herencia). Creamos un nuevo (constructor), LifelikeWorld, cuyo


prototipo se basa en el (prototipo World) pero que anula el mtodo letAct. El nuevo mtodo
letAct delega el trabajo de realizar una accin a varias funciones almacenadas en el objeto
actionTypes.

function LifelikeWorld(map, legend) {


World.call(this, map, legend);
}
LifelikeWorld.prototype = Object.create(World.prototype);

var actionTypes = Object.create(null);

LifelikeWorld.prototype.letAct = function(critter, vector) {


var action = critter.act(new View(this, vector));
var handled = action &&
action.type in actionTypes &&
actionTypes[action.type].call(this, critter,
vector, action);
if (!handled) {
critter.energy -= 0.2;
if (critter.energy <= 0)
this.grid.set(vector, null);
}
};

El nuevo mtodo letAct comprueba primero si se ha devuelto una accin, si existe una
(funcin de controlador) para este tipo de accin y, por ltimo, si ese controlador devuelve
true, lo que indica que se ha tratado correctamente la accin.

Tenga en cuenta el uso de call para dar al manejador acceso al mundo a travs de this. Si la
accin no funcion por alguna razn, la accin predeterminada es que la criatura simplemente
espere. Pierde un quinto punto de energa, y si su nivel de energa es igual o menor que cero,
la criatura muere y se borra de la cuadrcula.

Manejadores de acciones
actionTypes.grow = function(critter) {
critter.energy += 0.5;
return true;
};
Crecer siempre tiene xito y aade medio punto al nivel de energa (energy) de la planta.
Moverse es ms complicado.

actionTypes.move = function(critter, vector, action) {


var dest = this.checkDestination(action, vector);
if (dest == null ||
critter.energy <= 1 ||
this.grid.get(dest) != null)
return false;
critter.energy -= 1;
this.grid.set(vector, null);
this.grid.set(dest, critter);
return true;
};

Esta accin comprueba primero, utilizando el mtodo checkDestination definido


anteriormente, si la accin proporciona un destino vlido. Si no, o si el destino no est vaco,
o si el critter carece de la energa requerida, move devuelve false para indicar que no se tom
ninguna accin. De lo contrario, se mueve el critter y resta el costo de la energa.

Adems de moverse, las criaturas pueden comer.

actionTypes.eat = function(critter, vector, action) {


var dest = this.checkDestination(action, vector);
var atDest = dest != null && this.grid.get(dest);
if (!atDest || atDest.energy == null)
return false;
critter.energy += atDest.energy;
this.grid.set(dest, null);
return true;
};

Comer otra criatura (critter) tambin implica proporcionar un cuadrado de destino vlido.
Esta vez, el destino no debe estar vaco y debe contener algo con energa, como un critter
(pero no una pared - las paredes no son comestibles). Si es as, la energa de la comida es
transferida al comedor, y la vctima es removida de la cuadrcula.

Y finalmente, permitimos que nuestras criaturas se reproduzcan.

actionTypes.reproduce = function(critter, vector, action) {


var baby = elementFromChar(this.legend,
critter.originChar);
var dest = this.checkDestination(action, vector);
if (dest == null ||
critter.energy <= 2 * baby.energy ||
this.grid.get(dest) != null)
return false;
critter.energy -= 2 * baby.energy;
this.grid.set(dest, baby);
return true;
};

Reproducirse cuesta dos veces el nivel de energa de un critter recin nacido. As que primero
creamos un beb (hipottico) usando elementFromChar en el propio carcter de origen del
critter. Una vez que tenemos un beb, podemos encontrar su nivel de energa y probar si el
padre tiene suficiente energa para llevarlo con xito al mundo. Tambin necesitamos un
destino vlido (y vaco).

Si todo est bien, el beb es puesto en la cuadrcula (ya no es hipottico), y la energa se


gasta.

Llenar el nuevo mundo


Ahora tenemos un marco (framework) para simular estas criaturas en forma ms realista.
Podramos poner las criaturas del viejo mundo en ella, pero slo moriran ya que no tienen
una propiedad de energa. As que hagamos nuevos. Primero escribiremos una planta (Plant),
que es una forma de vida bastante simple.

function Plant() {
this.energy = 3 + Math.random() * 4;
}
Plant.prototype.act = function(view) {
if (this.energy > 15) {
var space = view.find(" ");
if (space)
return {type: "reproduce", direction: space};
}
if (this.energy < 20)
return {type: "grow"};
};

Las plantas comienzan con un nivel de energa entre 3 y 7, aleatorizado para que no se
reproduzcan todos en el mismo turno (turn). Cuando una planta alcanza 15 puntos de energa
y hay un espacio vaco cerca, se reproduce en ese espacio vaco. Si una planta no puede
reproducirse, simplemente crece hasta alcanzar el nivel de energa 20.

Ahora definimos un comedor de plantas (PlantEater).

function PlantEater() {
this.energy = 20;
}
PlantEater.prototype.act = function(view) {
var space = view.find(" ");
if (this.energy > 60 && space)
return {type: "reproduce", direction: space};
var plant = view.find("*");
if (plant)
return {type: "eat", direction: plant};
if (space)
return {type: "move", direction: space};
};

Usaremos el carcter * para las plantas (Plant), as que eso es lo que esta criatura buscar
cuando busque comida.

Darle vida
Y eso nos da suficientes elementos para probar nuestro nuevo mundo. Imagnese el siguiente
mapa como un valle cubierto de hierba con una manada de herbvoros en ella, algunas rocas y
una exuberante vegetacin en todas partes.

var valley = new LifelikeWorld(


["############################",
"##### ######",
"## *** **##",
"# *##** ** O *##",
"# *** O ##** *#",
"# O ##*** #",
"# ##** #",
"# O #* #",
"#* #** O #",
"#*** ##** O **#",
"##**** ###*** *###",
"############################"],
{"#": Wall,
"O": PlantEater,
"*": Plant}
);

Veamos ver ocurre cuando ejecutamos este codigo.

animateWorld(valley);

La mayora de las veces, las plantas se multiplican y se expanden con bastante rapidez, pero
luego la abundancia de alimentos causa una explosin poblacional de los herbvoros, que
proceden a eliminar todas o casi todas las plantas, resultando en un hambre masiva de las
criaturas. A veces, el ecosistema se recupera y comienza otro ciclo. En otras ocasiones, una
de las especies muere completamente. Si son los herbvoros, todo el espacio se llenar de
plantas. Si son las plantas, las criaturas restantes mueren de hambre, y el valle se convierte en
una tierra balda desolada. Ah, la crueldad de la naturaleza.

Ejercicios
Estupidez artificial

Tener extinguidos a los habitantes de nuestro mundo despus de unos minutos es algo
deprimente. Para hacer frente a esto, podramos tratar de crear un comedor de plantas ms
inteligente (smarter plant eater).

Hay varios problemas obvios con nuestros herbvoros. En primer lugar, son terriblemente
codiciosos, tragan cada planta que ven hasta que han borrado toda la vida vegetal. En
segundo lugar, su movimiento al azar (recuerde que el mtodo view.find: devuelve una
direccin al azar cuando coinciden varias direcciones) hace que tropiecen ineficazmente y
mueren de hambre si no encuentran ninguna planta cerca. Y finalmente, se reproducen muy
rpido, lo que hace que los ciclos entre la abundancia y el hambre sean bastante intensos.

Crea un nuevo tipo de critter intentando abordar uno o ms de estos puntos y sustituyelo por
el antiguo tipo PlantEater en el mundo del valle. Haga algunos ajustes mas si lo ve necesario.

// Your code here


function SmartPlantEater() {}

animateWorld(new LifelikeWorld(
["############################",
"##### ######",
"## *** **##",
"# *##** ** O *##",
"# *** O ##** *#",
"# O ##*** #",
"# ##** #",
"# O #* #",
"#* #** O #",
"#*** ##** O **#",
"##**** ###*** *###",
"############################"],
{"#": Wall,
"O": SmartPlantEater,
"*": Plant}
));

Depredadores

Cualquier ecosistema serio tiene una cadena alimentaria ms larga que un solo eslabn.
Escriba otro critter que sobreviva comiendose al critter herbvoro. Usted notar que la
estabilidad es an ms difcil de lograr ahora que hay ciclos en mltiples niveles. Trate de
encontrar una estrategia para hacer que el ecosistema funcione sin problemas durante al
menos un tiempo.

Una cosa que ayudar es hacer el mundo ms grande. De esta manera, los auges o bajas de la
poblacin local son menos propensos a eliminar completamente una especie, y hay espacio
para la poblacin de presas relativamente grande que se necesita para mantener una pequea
poblacin de depredadores.

// Your code here


function Tiger() {}

animateWorld(new LifelikeWorld(
["####################################################",
"# #### **** ###",
"# * @ ## ######## OO ##",
"# * ## O O **** *#",
"# ##* ########## *#",
"# ##*** * **** **#",
"#* ** # * *** ######### **#",
"#* ** # * # * **#",
"# ## # O # *** ######",
"#* @ # # * O # #",
"#* # ###### ** #",
"### **** *** ** #",
"# O @ O #",
"# * ## ## ## ## ### * #",
"# ** # * ##### O #",
"## ** O O # # *** *** ### ** #",
"### # ***** ****#",
"####################################################"],
{"#": Wall,
"@": Tiger,
"O": SmartPlantEater, // from previous exercise
"*": Plant}
));

Captulo 8
Bugs y manejo de errores
La depuracin es dos veces ms difcil que escribir cdigo. Por lo tanto, si usted escribe su
cdigo lo mejor que puede, por definicin, usted no es lo suficientemente inteligente como
para depurar lo que escribi.

Brian Kernighan and P.J. Plauger, The Elements of Programming Style

Yuan-Ma haba escrito un pequeo programa que usaba muchas variables globales y atajos de
mala calidad. Al leerlo, un estudiante le pregunt: 'Nos advirti contra estas tcnicas, sin
embargo usted las usa en su programa. Cmo puede ser? "El maestro respondi:" No hay
necesidad de buscar una manguera de agua si la casa no est en llamas ".

Master Yuan-Ma, The Book of Programming

Un programa es un pensamiento cristalizado. A veces son pensamientos con errores, otras


veces los errores se producen al convertir nuestros pensamientos en cdigo. De cualquier
manera, el resultado es un programa defectuoso.

Las fallas en un programa se llaman generalmente bugs. Los bugs pueden ser errores de
programacin o problemas en otros sistemas con los que nuestro programa interacta.
Algunos son inmediatamente evidentes, mientras que otros son sutiles y pueden permanecer
ocultos en un sistema durante aos.

A menudo, los problemas solamente surgen cuando un programa encuentra una situacin que
el programador no consider originalmente. A veces tales situaciones son inevitables. Cuando
se le pide al usuario que ingrese su edad (your age_) y este tipea naranja (_orange),
esto pone a nuestro programa a prueba. Est situacin tiene que ser anticipada y manejada de
alguna manera.

Errores de programacin
Cuando se trata de errores del programador, nuestro tarea es simple: encontrarlos y
arreglarlos. Tales errores pueden ir desde simples errores de tipeo, que causan que la
computadora nos avice tan pronto como pone los ojos en nuestro programa, a errores sutiles a
nuestra comprensin de la forma en que el programa opera, causando resultados incorrectos
slo en situaciones especficas. Los errores de este ltimo tipo pueden tomar semanas para
manifestarse.
El grado en que los lenguajes le ayudan a encontrar tales errores vara. No es sorprendente
que JavaScript est en el extremo de apenas ayuda de esa escala. Algunos lenguajes quieren
saber los tipos de todas sus variables y expresiones antes incluso de ejecutar un programa y le
dirn de inmediato cuando un tipo se utiliza de una manera inconsistente. JavaScript
considera los tipos slo cuando realmente ejecuta el programa, e incluso entonces, le permite
hacer algunas cosas claramente absurdas sin quejarse, como x = true * "monkey".

S hay algunas cosas que JavaScript no permite hacer. Escribir un programa que no es
sintcticamente vlido inmediatamente provocar un error. Otras cosas, como llamar a algo
que no es una funcin o buscar una propiedad en un valor indefinido, provocar que se
informe un error cuando el programa se est ejecutando y encuentra dicha accin sin sentido.

Pero muchas veces, un clculo sin sentido simplemente producir un NaN (no un nmero) o
undefined (valor indefinido). Y el programa contina feliz, convencido de que est haciendo
algo significativo. El error se manifestar slo ms tarde, despus de que el valor falso haya
recorrido varias funciones. No desencadena un error, pero en silencio hace que el resultado
del programa sea incorrecto. Encontrar la fuente de tales problemas puede ser difcil.

El proceso de encontrar errores (bugs) en los programas se llama depuracin (debugging).

Modo estricto (Strict mode)


JavaScript se puede hacer un poco ms estricto al permitir el modo estricto. Esto se hace
poniendo la cadena "use strict" en la parte superior de un archivo o en el cuerpo de funcin.
He aqu un ejemplo:

function canYouSpotTheProblem() {
"use strict";
for (counter = 0; counter < 10; counter++)
console.log("Happy happy");
}

canYouSpotTheProblem();
// ReferenceError: counter is not defined

Normalmente, cuando te olvidas de poner var delante de tu variable, como counter en el


ejemplo, JavaScript crea una variable global en silencio y usa eso. En el modo estricto, sin
embargo, un error se informa en su lugar. Esto es muy til. Debe tenerse en cuenta, sin
embargo, que esto no funciona cuando la variable en cuestin ya existe como global, slo
funciona cuando no existe y la asignacin que hemos hecho la habra creado.

Otro cambio en modo estricto es que this mantiene el valor undefined en funciones que no se
llaman como mtodos. Al hacer una llamada fuera del modo estricto, this se refiere al objeto
de alcance global. As que si accidentalmente llama a un mtodo o constructor
incorrectamente en modo estricto, JavaScript producir un error tan pronto como intente leer
algo de esto, en lugar de trabajar felizmente con el objeto global, crear y leer variables
globales.

Por ejemplo, considere el siguiente cdigo, que llama a un constructor sin la palabra clave
new, de modo que this no se refiere al objeto recien construdo:
function Person(name) { this.name = name; }
var ferdinand = Person("Ferdinand"); // oops
console.log(name);
// Ferdinand

As que la llamada falsa a Person tuvo xito pero devolvi un valor undefined y cre el
nombre de la variable en forma global. En modo estricto, el resultado es diferente.

"use strict";
function Person(name) { this.name = name; }
// Oops, forgot 'new'
var ferdinand = Person("Ferdinand");
// TypeError: Cannot set property 'name' of undefined

Nos dice inmediatamente que algo est mal. Esto es muy til.

El modo estricto (Strict mode) hace algunas cosas ms: Se prohbe dar a una funcin
mltiples parmetros con el mismo nombre y elimina completamente ciertas caractersticas
problemticas del lenguaje (como la declaracin which, que es tan equivocada que no volver
a mencionarla en este libro).

En pocas palabras, poner un use strict en la parte superior de su programa rara vez causa dao
y puede ayudarle a detectar un problema.

Tests
Si el lenguaje no va a hacer mucho para ayudarnos a encontrar errores, tendremos que
encontrarlos de la manera ms difcil: ejecutando el programa y ver si hace lo correcto.

Hacer esto a mano, una y otra vez, es una manera segura de volverse loco. Afortunadamente,
a menudo es posible escribir un segundo programa que automatice las pruebas de su
programa.

Como ejemplo, volvemos a utilizar el tipo Vector.

function Vector(x, y) {
this.x = x;
this.y = y;
}
Vector.prototype.plus = function(other) {
return new Vector(this.x + other.x, this.y + other.y);
};

Escribiremos un programa para comprobar que nuestra implementacin de Vector funciona


como se pretende. Luego, cada vez que cambiamos la implementacin, seguimos ejecutando
el programa de prueba para que podamos estar razonablemente seguros de que no rompimos
nada. Cuando agregamos funcionalidad adicional (por ejemplo, un nuevo mtodo) al tipo
Vector, tambin agregamos pruebas para la nueva caracterstica.

function testVector() {
var p1 = new Vector(10, 20);
var p2 = new Vector(-10, 5);
var p3 = p1.plus(p2);
if (p1.x !== 10) return "fail: x property";
if (p1.y !== 20) return "fail: y property";
if (p2.x !== -10) return "fail: negative x property";
if (p3.x !== 0) return "fail: x from plus";
if (p3.y !== 25) return "fail: y from plus";
return "everything ok";
}
console.log(testVector());
// everything ok

Escribir pruebas como sta tiende a producir cdigo bastante repetitivo, incmodo.
Afortunadamente, existen piezas de software que ayudan a crear y ejecutar conjuntos de
pruebas (test suites) proporcionando un lenguaje (en forma de funciones y mtodos)
adecuado para expresar las pruebas y para emitir informacin puntual cuando una prueba
falla. Estos se llaman testing frameworks.

Debugging
Una vez que usted nota que hay algo mal con su programa porque se comporta mal o produce
errores, el siguiente paso es averiguar cul es el problema.

A veces es obvio. El mensaje de error apuntar en una lnea especfica de su programa y si


observa la descripcin del error y esa lnea de cdigo, a menudo puede ver el problema.

Pero no siempre. A veces la lnea que desencaden el problema es simplemente el primer


lugar donde se utiliza de manera no vlida un valor erroneo que fue producido en otro lugar.
Y a veces no hay ningn mensaje de error en absoluto, slo un resultado no vlido. Si ha
estado resolviendo los ejercicios en los captulos anteriores, es probable que ya haya
experimentado tales situaciones.

El programa de ejemplo siguiente intenta convertir un nmero entero en una cadena en


cualquier base (decimal, binario, etc.) seleccionando repetidamente el ltimo dgito y luego
dividiendo el nmero para eliminar este dgito. Pero la valor de retorno ridculo que produce
actualmente sugiere que tiene un error (bug).

function numberToString(n, base) {


var result = "", sign = "";
if (n < 0) {
sign = "-";
n = -n;
}
do {
result = String(n % base) + result;
n /= base;
} while (n > 0);
return sign + result;
}
console.log(numberToString(13, 10));
// 1.5e-3231.3e-3221.3e-3211.3e-3201.3e-3191.3e-3181.3

Incluso si usted ya noto cual es el problema, finja por un momento que aun no lo a
descubierto. Sabemos que nuestro programa est funcionando mal, y queremos averiguar por
qu.
Aqu es donde debe resistir el impulso de comenzar a hacer cambios aleatorios en el cdigo.
Trate de analizar lo que est sucediendo y llegar a una teora de por qu podra estar
sucediendo. Luego, haga observaciones adicionales para probar esta teora o, si an no tiene
una teora, haga observaciones adicionales que podran ayudarle a encontrar una.

Poner unas pocas llamadas console.log estratgicas en el programa es una buena manera de
obtener informacin adicional sobre lo que est haciendo el programa. En este caso,
queremos que n tome los valores 13, 1 y luego 0. Vamos a escribir su valor al inicio del bucle.

13
1.3
0.13
0.013

1.5e-323

Right. Dividir 13 por 10 no produce un nmero entero. En lugar de n / = base, lo que


realmente queremos es n = Math.floor (n / base) para que el nmero se desplace
correctamente a la derecha.

Una alternativa al uso de console.log es utilizar las capacidades de depuracin de su


navegador. Los navegadores modernos vienen con la capacidad de establecer un punto de
interrupcin en una lnea especfica de su cdigo. Esto har que la ejecucin del programa se
detenga cada vez que se alcanza la lnea con el punto de interrupcin y le permite
inspeccionar los valores de las variables en ese punto. No entrar en detalles aqu ya que los
depuradores difieren de navegador a navegador, pero busque en las herramientas de
desarrollo de su navegador y busque en la Web ms informacin. Otra forma de establecer un
punto de interrupcin es incluir una declaracin de depurador (que consiste simplemente en
la palabra clave debugger) en su programa. Si las herramientas de desarrollo de su navegador
estn activas, el programa se detendr cada vez que llegue a esa declaracin, y podr
inspeccionar su estado.

Propagacin de errores
No todos los problemas pueden ser prevenidos por el programador. Si su programa se
comunica con el mundo exterior de alguna manera, existe la posibilidad de que la entrada que
obtenga sea invlida o que otros sistemas con los que intente hablar estn rotos o
inaccesibles.

Los programas sencillos, o programas que se ejecutan slo bajo su supervisin, pueden
permitirse el lujo de dejar de funcionar cuando se produce un problema de este tipo.
Examinars el problema y lo intentars de nuevo. Pero las aplicaciones "reales" no se pueden
simplemente desconectar. A veces lo correcto es tomar la entrada mala y seguir corriendo. En
otros casos, es mejor informar al usuario de lo que sali mal y luego desconectar. Pero en
cualquier situacin, el programa tiene que hacer algo activamente en respuesta al problema.

Digamos que tiene una funcin promptInteger que pide al usuario un nmero entero y lo
devuelve. Pero Qu debe devolver si el usuario ingresa una palabra, por ejemplo confunde
"your age" por _orange_?
Una opcin es hacer que devuelva un valor especial. Las opciones comunes para estos
valores son null y no undefined.

function promptNumber(question) {
var result = Number(prompt(question, ""));
if (isNaN(result)) return null;
else return result;
}

console.log(promptNumber("How many trees do you see?"));

Esta es una buena estrategia. Ahora cualquier cdigo que llama a promptNumber debe
comprobar si se ha ledo un nmero real y, de no ser as, debe reiniciar el proceso de alguna
manera, tal vez preguntando de nuevo o rellenando con un valor predeterminado. O podra
devolver un valor especial a su llamador (caller) para indicar que no pudo hacer lo que se le
pidi.

En muchas situaciones, sobre todo cuando los errores son comunes y el metodo caller debe
tenerlo explcitamente en cuenta, devolver un valor especial es una manera prolija de indicar
un error. Sin embargo, tiene sus desventajas. Primero, qu pasa si la funcin ya puede
devolver todo tipo posible de valores? Para esta funcin, es difcil encontrar un valor especial
que pueda distinguirse de un resultado vlido.

La segunda cuestin con la devolucin de valores especiales es que puede conducir a algn
cdigo muy abarrotado. Si un pedazo de cdigo llama a promptNumber 10 veces, tiene que
comprobar 10 veces si se devolvi null. Y si su respuesta a encontrar null es simplemente
devolver null a s mismo, el llamador (caller) a su vez tendr que comprobarlo, y as
sucesivamente.

Excepciones
Cuando una funcin no puede proceder normalmente, lo que nos gustara hacer es
simplemente detener lo que estamos haciendo y trabajar en un contexto donde sabemos cmo
manejar el problema. Esto es el manejo de excepciones.

Las excepciones (exceptions) son un mecanismo que hace posible que el cdigo que se
ejecuta en un problema lance (throw) una excepcin, que es simplemente lanzar un valor.
Elevar una excepcin se asemeja en cierta medida a un retorno sper cargado de una funcin:
se eleva no slo de la funcin actual, sino tambin de sus llamantes, hasta la primera llamada
que inici la ejecucin actual. Esto se llama desenrollar la pila (unwinding the stack).
Recuerde el concepto de pila de llamadas de funcin en el Captulo 3. Una excepcin
reduce el zoom de esta pila, eliminando todos los contextos de llamada que encuentra.

Si las excepciones siempre estan cerca de la parte inferior de la pila, no seran de mucha
utilidad, pues slo proporcionara una nueva manera de lanzar su programa. Su poder reside
en el hecho de que usted puede establecer "obstculos" a lo largo de la pila para capturar la
excepcin. Entonces usted puede actuar, despus de que el programa contina funcionando
en el punto donde registr la excepcin.

He aqu un ejemplo:
function promptDirection(question) {
var result = prompt(question, "");
if (result.toLowerCase() == "left") return "L";
if (result.toLowerCase() == "right") return "R";
throw new Error("Invalid direction: " + result);
}

function look() {
if (promptDirection("Which way?") == "L")
return "a house";
else
return "two angry bears";
}

try {
console.log("You see", look());
} catch (error) {
console.log("Something went wrong: " + error);
}

La palabra clave throw se utiliza para generar una excepcin. Capturar una se hace
envolviendo (wrapping) un pedazo de cdigo en un bloque try, seguido por la palabra clave
catch. Cuando el cdigo del bloque try genera una excepcin, se evala el bloque catch. El
nombre de la variable (entre parntesis) despus de la captura se enlazar al valor de la
excepcin. Una vez finalizado el bloqueo del bloque (o si el bloque try termina sin
problemas), el control se desarrolla debajo de toda la sentencia try/catch.

En este caso, utilizamos el constructor Error para crear nuestro valor de excepcin (new
Error). . Se trata de un constructor de JavaScript estndar que crea un objeto con una
propiedad de mensaje. En los entornos de JavaScript modernos, las instancias de este
constructor tambin recopilan informacin sobre la pila de llamadas que exista cuando se
cre la excepcin, un denominado seguimiento de la pila (stack trace). Esta informacin se
almacena en la propiedad de la pila y puede ser til al intentar depurar un problema: nos
indica la funcin exacta donde ocurri el problema y cules otras funciones llevaron a la
llamada que fall.

Tenga en cuenta que en nuestro ejemplo la funcin look ignora por completo la posibilidad de
que promptDirection pueda salir mal. Esta es la gran ventaja de las excepciones: el cdigo de
manejo de errores slo es necesario en el punto donde se produce el error y en el punto donde
se maneja. Las funciones intermedias pueden desentenderse de todo. Bueno, casi...

Limpieza despus de excepciones


Considere la siguiente situacin: una funcin, withContext, quiere asegurarse de que, durante
su ejecucin, la variable de nivel superior context contiene un valor especfico. Despus de
que termina, restaura esta variable a su valor anterior.

var context = null;

function withContext(newContext, body) {


var oldContext = context;
context = newContext;
var result = body();
context = oldContext;
return result;
}

Qu pasa si body plantea una excepcin? En ese caso, la llamada a withContext ser
eliminada de la pila por la excepcin, y context nunca volver a su valor anterior.

Hay una caracterstica ms que tiene try. Puede ser seguida por un bloque finally en lugar de,
o adems de, un bloque catch. Un bloque finally dice "No importa lo que pase, ejecute este
cdigo despus de intentar ejecutar el cdigo en el bloque try". Si una funcin tiene que
limpiar algo, el cdigo de limpieza normalmente se debe poner en el bloque finally.

function withContext(newContext, body) {


var oldContext = context;
context = newContext;
try {
return body();
} finally {
context = oldContext;
}
}

Tenga en cuenta que ya no tenemos que almacenar el resultado del body (que queremos
devolver) en una variable. Incluso si regresamos directamente desde el bloque try, se
ejecutar el bloque finally. Podemos hacerlo y estar seguros:

try {
withContext(5, function() {
if (context < 10)
throw new Error("Not enough context!");
});
} catch (e) {
console.log("Ignoring: " + e);
}
// Ignoring: Error: Not enough context!

console.log(context);
// null

A pesar de que la funcin llamada por withContext explot, en withContext se limpi


adecuadamente la variable context.

Selective catching
Cuando una excepcin hace todo el camino a la parte inferior de la pila sin ser capturada, se
maneja por el medio ambiente. Lo que esto significa difiere entre entornos. En los
navegadores, normalmente se escribe una descripcin del error en la consola de JavaScript
(accesible a travs del men Herramientas o Desarrollador del navegador).

Para los errores o problemas del programador que el programa no puede manejar,
simplemente dejar pasar el error es lo correcto a menudo. Una excepcin no tratada es una
forma razonable de sealar un programa roto, y la consola de JavaScript, en navegadores
modernos, le proporcionar cierta informacin sobre qu llamadas de funcin estaban en la
pila cuando ocurri el problema.
Para los problemas que se espera que sucedan durante el uso rutinario, estrellarse con una
excepcin no tratada no es una respuesta amigable.

Los usos no vlidos del lenguaje, como hacer referencia a una variable inexistente, buscar
una propiedad en null o llamar a algo que no es una funcin, tambin darn lugar a que se
generen excepciones. Tales excepciones se pueden capturar igual que sus propias
excepciones.

Cuando se introduce un catch, todo lo que sabemos es que algo en nuestro try caus una
excepcin. Pero no sabemos qu, ni qu excepcin caus.

JavaScript (en una omisin bastante flagrante) no proporciona soporte directo para capturar
selectivamente las excepciones: o bien las captura todas o no las captura. Esto hace que sea
muy fcil asumir que la excepcin que se obtiene es la misma que la que se estaba pensando
cuando escribi el bloque catch.

Pero puede que no lo sea. Es posible que se haya violado alguna otra suposicin o que haya
introducido un error en algn lugar que est causando una excepcin. He aqu un ejemplo,
que intenta seguir llamando a promptDirection hasta obtener una respuesta vlida:

for (;;) {
try {
var dir = promtDirection("Where?"); // typo!
console.log("You chose ", dir);
break;
} catch (e) {
console.log("Not a valid direction. Try again.");
}
}

El constructo for (;;) es una forma de crear intencionalmente un bucle que no termina por s
solo. Salimos del bucle slo cuando se da una direccin vlida. Pero hemos escrito
errneamente promptDirection, lo que resultar en un error de "variable indefinida". Debido a
que el bloque de catch ignora completamente su valor de excepcin (e), suponiendo que sabe
cul es el problema, trata errneamente el error de variable como indicativo de entrada
incorrecta. Esto no slo causa un bucle infinito, sino que tambin "entierra" el til mensaje de
error sobre la variable mal escrita.

AComo regla general, no cubra las excepciones de captura a menos que sea con el propsito
de "enrutarlas" en alguna parte, por ejemplo, a travs de la red para decirle a otro sistema que
nuestro programa se ha estrellado. E incluso entonces, piense cuidadosamente en cmo
podra estar ocultando informacin.

As que queremos capturar un tipo especfico de excepcin. Podemos hacer esto


comprobando en el bloque catch si la excepcin que conseguimos es la que buscamos y
probar de otra manera. Pero, cmo reconocemos una excepcin?

Por supuesto, podramos igualar su propiedad de mensaje con el mensaje de error que
esperamos. Pero esa es una manera inestable de escribir cdigo: usariamos la informacin
destinada al consumo humano (el mensaje) para tomar una decisin programtica. Tan pronto
como alguien cambia (o traduce) el mensaje, el cdigo dejar de funcionar.
Ms bien, definamos un nuevo tipo de error y usamos instanceof para identificarlo.

function InputError(message) {
this.message = message;
this.stack = (new Error()).stack;
}
InputError.prototype = Object.create(Error.prototype);
InputError.prototype.name = "InputError";

El prototipo se crea al derivar de Error.prototype para que instanceof Error tambin devuelva
true para los objetos InputError. Tambin se le da una propiedad de nombre (name) ya que
los tipos de error estndar (Error, SyntaxError, ReferenceError, etc.) tambin tienen dicha
propiedad.

La asignacin a la propiedad stack intenta dar a este objeto un rastreo de pila algo til (en
plataformas que lo soportan) creando un objeto de error regular y luego utilizando la
propiedad stack de ese objeto como propio.

Ahora promptDirection puede lanzar tal error.

function promptDirection(question) {
var result = prompt(question, "");
if (result.toLowerCase() == "left") return "L";
if (result.toLowerCase() == "right") return "R";
throw new InputError("Invalid direction: " + result);
}

Y el bucle for puede atraparlo con ms cuidado.

for (;;) {
try {
var dir = promptDirection("Where?");
console.log("You chose ", dir);
break;
} catch (e) {
if (e instanceof InputError)
console.log("Not a valid direction. Try again.");
else
throw e;
}
}

Esto captura slo instancias de InputError y permite excepciones a travs de throw. Si


reintroduce el error de tipeo, el error de variable undefined se informar correctamente.

Assertions
Las afirmaciones son una herramienta para comprobar la salud bsica de los errores de
programacin. Considere la funcin auxiliar assert:

function AssertionFailed(message) {
this.message = message;
}
AssertionFailed.prototype = Object.create(Error.prototype);
function assert(test, message) {
if (!test)
throw new AssertionFailed(message);
}

function lastElement(array) {
assert(array.length > 0, "empty array in lastElement");
return array[array.length - 1];
}

Esto proporciona una manera compacta de cumplir las expectativas, ayudando a alertar del
programa si la condicin declarada no se cumple. Por ejemplo, la funcin lastElement, que
recupera el ltimo elemento de una matriz, retornara indefinidamente arrays vacos si la
asercin (assertion) se omitiera. Obtener el ltimo elemento de una matriz vaca no tiene
mucho sentido, por lo que es casi seguro que es el resultado de un error de programador.

Las afirmaciones (Assertions) son una manera de asegurarse de que los errores causan fallas
en el punto del error, en lugar de producir silenciosamente valores sin sentido que pueden
causar problemas en una parte no relacionada del sistema.

Resumn
Los errores y malas aportaciones son hechos de la vida. Los errores en los programas
necesitan ser encontrados y arreglados. Pueden ser ms fciles de notar al tener suites de
pruebas (test suites) automatizadas y aadir aserciones a sus programas.

Los problemas causados por factores ajenos al control del programa normalmente deben
manejarse con gracia. A veces, cuando el problema se puede manejar localmente, los valores
de retorno especiales son una forma sana de rastrearlos. De lo contrario, las excepciones son
preferibles.

El lanzamiento de una excepcin provoca que la pila de llamadas se desenrolle hasta el


siguiente bloque de try/catch o hasta la parte inferior de la pila. El valor de excepcin se dar
al bloque de catch que lo captura, lo que debera comprobar que es realmente el tipo esperado
de excepcin y luego hacer algo con l. Para manejar el flujo de control impredecible causado
por las excepciones, finalmente los bloques se pueden usar para asegurar que un pedazo de
cdigo siempre se ejecuta al finalizar un bloque.

Ejercicios
Retry

Digamos que tiene una funcin primitiveMultiply que, en el 50 por ciento de los casos,
multiplica dos nmeros, y en el otro 50 por ciento, plantea una excepcin del tipo
MultiplicatorUnitFailure. Escribir una funcin que envuelva esta mala funcin y sigue
intentandolo hasta que una llamada tenga xito, despus de lo cual devuelva el resultado.

Asegrese de manejar solo las excepciones que desea manejar.

function MultiplicatorUnitFailure() {}
function primitiveMultiply(a, b) {
if (Math.random() < 0.5)
return a * b;
else
throw new MultiplicatorUnitFailure();
}

function reliableMultiply(a, b) {
// Your code here.
}

console.log(reliableMultiply(8, 8));
// 64

La caja cerrada

Considere el siguiente (y bastante artificial) objeto: object:

var box = {
locked: true,
unlock: function() { this.locked = false; },
lock: function() { this.locked = true; },
_content: [],
get content() {
if (this.locked) throw new Error("Locked!");
return this.cContent;
}
};

It is a box Es una caja con una cerradura. Dentro hay una matriz, pero slo se puede obtener
cuando se desbloquea la caja. No se permite acceder directamente a la propiedad cContent.

Escriba una funcin llamada WithBoxUnlocked que toma un valor de funcin como
argumento, desbloquea la casilla, ejecuta la funcin y, a continuacin, asegura que la casilla
se bloquea de nuevo antes de volver, independientemente de si la funcin de argumento ha
devuelto normalmente o ha lanzado una excepcin.

function withBoxUnlocked(body) {
// Your code here.
}

withBoxUnlocked(function() {
box.content.push("gold piece");
});

try {
withBoxUnlocked(function() {
throw new Error("Pirates on the horizon! Abort!");
});
} catch (e) {
console.log("Error raised:", e);
}
console.log(box.locked);
// true

Para obtener puntos adicionales, asegrese de que si llama a WithBoxUnlocked cuando la


caja ya est desbloqueada, la caja permanezca desbloqueada.
Captulo 9
Expresiones Regulares
Algunas personas, cuando se enfrentan a un problema, dicen ya s, usar expresiones
regulares. entonces tienen dos problemas.

Jamie Zawinski

Yuan-Ma dijo: "Cuando se corta contra la veta de la madera, se necesita mucha fuerza.
Cuando se programa contra la lgica de un problema, se necesita mucho cdigo."

Master Yuan-Ma, The Book of Programming

Las herramientas y tcnicas de programacin sobreviven y se propagan de una manera


catica y evolutiva. No siempre son las formas mas bonitas y luminosos las que ganan, sino
las que funcionan lo suficientemente bien en el nicho adecuado. Por ejemplo, aquellas que
estan correctamente integrada con otra pieza tecnologica.

En este captulo, hablar de una de esas herramientas, las expresiones regulares. Las
expresiones regulares son una forma de describir patrones en los datos tipo cadena. Forman
un lenguaje pequeo y separado que forma parte de JavaScript y muchos otros idiomas y
herramientas.

Las expresiones regulares son terriblemente incmodas y extremadamente tiles. Su sintaxis


es crptica, y la interfaz de programacin que proporciona JavaScript es torpe. Pero son una
poderosa herramienta para inspeccionar y procesar cadenas. La correcta comprensin de
expresiones regulares le har un programador ms eficaz.

Creacin de una expresin regular


Una expresin regular es un tipo de objeto. Puede construirse con el constructor _RegExp_ o
escribirse como un valor literal encerrando el patrn en caracteres de barra diagonal (_/_).

var re1 = new RegExp("abc");


var re2 = /abc/;

Ambos objetos de expresin regular representan el mismo patrn pattern: un carcter a


seguido de un b seguido de un c.

Cuando se utiliza el constructor _RegExp_, el patrn se escribe como una cadena normal, por
lo que se aplican las reglas habituales para las barras invertidas.
La segunda notacin, donde el patrn aparece entre los caracteres de barra (_/_), trata las
barras inversas de manera algo diferente. En primer lugar, ya que una barra inclinada termina
el patrn, necesitamos poner una barra invertida antes de cualquier barra inclinada que
queramos ser parte del patrn. Adems, las barras invertidas que no forman parte de cdigos
de caracteres especiales (como _\n_) se conservarn, en lugar de ignorarse como estn en
cadenas, y cambiaran el significado del patrn. Algunos caracteres, como signos de
interrogacin (_?_) y signos ms (_+_), tienen significados especiales en expresiones
regulares y deben estar precedidos por una barra invertida si estn destinados a representar el
carcter en s.

var eighteenPlus = /eighteen\+/;

Saber cuales carcteres deben usar la barra invertida y cuales no al momento que se escriben
expresiones regulares requiere que usted conozca a cada uno de los carcteres especiales.
Como en un principio esto puede ser muy engorroso, es recomendable que en caso de duda,
slo ponga una barra invertida antes de cualquier carcter que no sea una letra, nmero o
espacio en blanco.

Testeando las coincidencias (matches)


Los objetos de expresiones regulares tienen varios mtodos. El ms simple es _test_. Si le
pasas una cadena, devolver un booleano dicindote si la cadena contiene una coincidencia
con el patrn de la expresin propuesta.

console.log(/abc/.test("abcde"));
// true
console.log(/abc/.test("abxde"));
// false

Una expresin regular que consta slo de caracteres no especiales representa simplemente esa
secuencia de caracteres. Si abc se produce en cualquier parte de la cadena que estamos
probando (no slo al principio), el metodo _test_ devolver _true_.

Matchear un conjunto de caracteres


Descubrir si una cadena contiene abc podra hacerse con una llamada al mtodo _indexOf_.
Las expresiones regulares nos permiten ir ms all y expresar patrones ms complicados.

Digamos que queremos buscar en una cadena cualquier nmero del 0 al 9. En una expresin
regular, poner un conjunto de caracteres entre corchetes (_[]_) hace en la expresin de
destino busque coincidencias con cualquiera de los caracteres entre los corchetes.

Las dos expresiones siguientes coinciden con todas las cadenas que contienen un dgito:

console.log(/[0123456789]/.test("in 1992"));
// true
console.log(/[0-9]/.test("in 1992"));
// true
Entre corchetes, se puede utilizar un guin (_-_) entre dos caracteres para indicar un rango,
donde el orden se determina por el nmero Unicode de cada cracter. Como los caracteres
_0_ a _9_ se sitan uno al lado del otro en este orden (cdigos 48 a 57), entonces _[0-9]_
cubre todos ellos y coincide con cualquier dgito.

Hay una serie de grupos de caracteres comunes que tienen sus propios atajos incorporados.
Los dgitos son uno de ellos: _\d_ significa lo mismo que _[0-9]_.

\d Cualquier carcter de dgito


\w Un carcter alfanumrico (carcter de texto o (word character)
\s Cualquier espacio en blanco (espacio, pestaa, nueva lnea y similar)
\D Carcter que no es un dgito
\W Un carcter no alfanumrico
\S Un carcter no-espacio-en-blanco
. Cualquier carcter excepto carcter de nueva lnea

As que se podra coincidir (matchear) con un formato de fecha y hora como 30-01-2003
15:20 con la siguiente expresin:

var dateTime = /\d\d-\d\d-\d\d\d\d \d\d:\d\d/;


console.log(dateTime.test("30-01-2003 15:20"));
// true
console.log(dateTime.test("30-jan-2003 15:20"));
// false

Se ve horrible, verdad? Tiene demasiadas barras invertidas y es difcil detectar el patrn real
expresado. Veremos una versin ligeramente mejorada de esta expresin ms adelante ms
adelante.

Estos cdigos de barra invertida tambin se pueden utilizar dentro de corchetes. Por ejemplo,
_[\ d.]_ significa cualquier dgito o un carcter de perodo. Pero tenga en cuenta que el
propio perodo, cuando se usa entre corchetes, pierde su significado especial. Lo mismo
ocurre con otros caracteres especiales, como _+_.

Para invertir un conjunto de caracteres, es decir, para expresar que desea que coincida con
cualquier carcter excepto los del conjunto, puede escribir (^) despus del corchete de
apertura.

var notBinary = /[^01]/;


console.log(notBinary.test("1100100010100110"));
// false
console.log(notBinary.test("1100100010200110"));
// true

Repitiendo partes de un patrn


Ya sabemos cmo hacer coincidir un solo dgito. Qu pasa si queremos igualar un nmero
entero? Una secuencia de uno o ms dgitos?
Cuando se coloca un signo ms (_+_) despus de algo en una expresin regular, indica que el
elemento se puede repetir ms de una vez. As, _/\d+/_ coincide con uno o ms caracteres de
dgito.

console.log(/'\d+'/.test("'123'"));
// true
console.log(/'\d+'/.test("''"));
// false
console.log(/'\d*'/.test("'123'"));
// true
console.log(/'\d*'/.test("''"));
// true

El asterisco (_) tiene un significado similar, pero tambin permite que el patrn coincida
cero veces. Algo con un _ al final, nunca impide un patrn de emparejamiento. Igualar cero
instancias si no puede encontrar cualquier texto adecuado al patrn.

Un signo de interrogacin (?) hace que una parte de un patrn sea "opcional", lo que significa
que puede ocurrir ninguna o una vez. En el ejemplo siguiente, se permite que el carcter u se
produzca, pero el patrn tambin coincide (matchea) cuando este falta.

var neighbor = /neighbou?r/;


console.log(neighbor.test("neighbour"));
// true
console.log(neighbor.test("neighbor"));
// true

Para indicar que un patrn debe ocurrir un nmero exacto de veces, utilice llaves (_{}_).
Poner `_{4}_ despus de un elemento, por ejemplo, indica que se requiere que
el elemento se encuentre (o matchee) exactamente cuatro veces. Tambin es
posible especificar un rango de esta manera: _{2,4}_` significa que el elemento
debe ocurrir al menos dos veces y como mximo cuatro veces.

Aqu hay otra versin del patrn de fecha y hora que permite los das, meses y horas de un
solo dgito y dos dgitos, lo que lo hace ms agradable y fcil de leer.

var dateTime = /\d{1,2}-\d{1,2}-\d{4} \d{1,2}:\d{2}/;


console.log(dateTime.test("30-1-2003 8:45"));
// true

Tambin puede especificar rangos abiertos cuando se usan llaves, omitiendo el nmero
despus de la coma. As que _{5,}_ significa cinco o ms veces.

Agrupar subexpresiones
Para usar un operador como _*_ o _+_ en ms de un elemento a la vez, puede utilizar
parntesis _()_. Una parte de una expresin regular que est encerrada entre parntesis
cuenta como un solo elemento en lo que se refiere a los operadores que lo siguen.

var cartoonCrying = /boo+(hoo+)+/i;


console.log(cartoonCrying.test("Boohoooohoohooo"));
// true
El primer _+_ y el segundo _+_ se aplican solamente al segundo _o_ en _boo_ y _hoo_,
respectivamente. El tercero _+_ se aplica a todo el grupo _(hoo+)_, haciendo coincidir una o
ms secuencias como esa.

El _i_ al final de la expresin en el ejemplo anterior hace que esta expresin regular no
sea sensible a maysculas y minsculas (case insensitive), permitiendo que coincida con la
mayscula B en la cadena de entrada, aunque el patrn es todo en minsculas.

Matcheos y grupos
El mtodo _test_ es la forma ms simple de buscar coincidencias con una expresin regular.
Slo dice si coincide y nada ms. Las expresiones regulares tambin tienen un mtodo
_exec_ (ejecutar) que devolver _null_ si no se encontr ninguna coincidencia o, si
encontr una, devolver un objeto con informacin sobre la coincidencia.

var match = /\d+/.exec("one two 100");


console.log(match);
// ["100"]
console.log(match.index);
// 8

Un objeto devuelto desde el mtodo _exec_ tiene una propiedad _ndex_ que nos dice en
dnde comienza la coincidencia correcta en la cadena. Aparte de eso, el objeto se ve como (y
de hecho es) una matriz de cadenas, cuyo primer elemento es la cadena que matche (en el
ejemplo anterior, sta es la secuencia de dgitos que estbamos buscando).

Los valores de cadena tienen un mtodo _match_ de coincidencia que se comporta de manera
similar.

console.log("one two 100".match(/\d+/));


// ["100"]

Cuando la expresin regular contiene subexpresiones agrupadas con parntesis, el texto que
coincida con esos grupos tambin aparecer en la matriz. Todo el _match_ es siempre el
primer elemento. El siguiente elemento es la parte que coincidi por el primer grupo (aquel
cuyo parntesis de apertura viene primero en la expresin), luego el segundo grupo, y as
sucesivamente.

var quotedText = /'([^']*)'/;


console.log(quotedText.exec("she said 'hello'"));
// ["'hello'", "hello"]

Cuando un grupo no encuentra coincidencia en absoluto (por ejemplo, cuando es seguido por
un signo de interrogacin), su posicin en la matriz de salida se mantendr indefinida. Del
mismo modo, cuando un grupo es matcheado varias veces, slo el ltimo match termina en la
matriz.

console.log(/bad(ly)?/.exec("bad"));
// ["bad", undefined]
console.log(/(\d)+/.exec("123"));
// ["123", "3"]
Los grupos pueden ser tiles para extraer partes de una cadena. Si no queremos solamente
verificar si una cadena contiene una fecha sino tambin queremos extraerla y construir un
objeto que la represente, podemos insertar parntesis alrededor de los patrones de dgitos y
escoger directamente la fecha del resultado de _exec_.

Pero primero, un breve desvo en el que discutimos la forma preferida de almacenar valores
de fecha y hora en JavaScript.

El tipo fecha
JavaScript tiene un tipo (_typeof_) de objeto estndar para representar fechas, o ms bien,
puntos en el tiempo. Se llama _Date_. Si simplemente se crea un objeto _Date_ con _new_,
se obtienen la fecha y la hora actual.

console.log(new Date());
// Wed Dec 04 2013 14:24:57 GMT+0100 (CET)

Tambin puede crear un objeto para una hora especfica.

console.log(new Date(2009, 11, 9));


// Wed Dec 09 2009 00:00:00 GMT+0100 (CET)
console.log(new Date(2009, 11, 9, 12, 59, 59, 999));
// Wed Dec 09 2009 12:59:59 GMT+0100 (CET)

JavaScript utiliza una convencin donde los nmeros de mes empiezan en cero (por lo que
diciembre es 11), sin embargo, los nmeros de da empiezan en uno. Esto puede ser muy
confuso y tonto. Ten mucho cuidado.

Los ltimos cuatro argumentos (horas, minutos, segundos y milisegundos) son opcionales y
se toman como cero cuando no se dan.

Las marcas de tiempo se almacenan como el nmero de milisegundos desde el comienzo de


1970, usando nmeros negativos para tiempos anteriores a 1970 (siguiendo una convencin
establecida por Unix time creada en esa decada). El mtodo _getTime_ en un objeto de
_Date_ devuelve este nmero. Es un nmero grande.

console.log(new Date(2013, 11, 19).getTime());


// 1387407600000
console.log(new Date(1387407600000));
// Thu Dec 19 2013 00:00:00 GMT+0100 (CET)

Si le da al constructor _Date_ un solo argumento, ese argumento se trata como un recuento


de milisegundos. Puede obtener el recuento actual de milisegundos creando un nuevo objeto
_Date_ y llamando a _getTime_, pero tambin llamando a la funcin _Date.now_.

Los objetos _Date_ proporcionan mtodos como _getFullYear_, _getMonth_, _getDate_,


_getHours_, _getMinutes_ y _getSeconds_ para extraer sus componentes. Tambin hay un
mtodo _getYear_ que da un valor de ao de dos dgitos bastante intil (como 93 o 14).

Poniendo parntesis alrededor de las partes de la expresin que nos interesa, ahora podemos
crear fcilmente un objeto _Date_ a partir de una cadena.
function findDate(string) {
var dateTime = /(\d{1,2})-(\d{1,2})-(\d{4})/;
var match = dateTime.exec(string);
return new Date(Number(match[3]),
Number(match[2]) - 1,
Number(match[1]));
}
console.log(findDate("30-1-2003"));
// Thu Jan 30 2003 00:00:00 GMT+0100 (CET)

Word and string boundaries


Desafortunadamente, _findDate_ tambin obtendra fechas sin sentido del tipo 00-1-3000 de
la cadena "100-1-30000". Una coincidencia puede ocurrir en cualquier parte de la cadena, por
lo que en este caso, slo comenzar en el segundo carcter y finalizar en el segundo al
ltimo carcter.

Si queremos hacer que la coincidencia abarque toda la cadena, podemos agregar los
marcadores _ y _$_. El __ coincide con el inicio de la cadena de entrada, mientras que _$_
coincide con el final. Por lo tanto, _/\d+$/_ coincide con una cadena compuesta enteramente
de uno o ms dgitos, _/!/_ coincide con cualquier cadena que comience con un signo de
exclamacin y _/x^/ no coincide con ninguna cadena (no puede haber una x antes el
comienzo de la cadena).

Si, por el contrario, slo queremos asegurarnos de que la fecha empiece y termine en un
lmite de palabra, podemos usar el marcador \b. Un lmite de palabra puede ser el comienzo o
el final de la cadena o cualquier punto de la cadena que tenga un carcter de palabra (como en
\w) en un lado y un carcter nonword en el otro.

console.log(/cat/.test("concatenate"));
// true
console.log(/\bcat\b/.test("concatenate"));
// false

Tenga en cuenta que un marcador de lmite no representa un carcter real. Slo hace que la
expresin regular coincida solamente si una determinada condicin se mantiene donde
aparece en el patrn.

Patrones de eleccin
Digamos que no queremos saber si una parte del texto contiene un nmero, sino que
buscamos un nmero seguido por una de las palabras pig, cow o chicken, o cualquiera de sus
formas plurales.

Podramos escribir tres expresiones regulares y probarlas a su vez, pero hay una manera ms
agradable. El carcter | (tubo) indica una opcin entre el patrn a su izquierda y el patrn a su
derecha. As que puedo decir esto:

var animalCount = /\b\d+ (pig|cow|chicken)s?\b/;


console.log(animalCount.test("15 pigs"));
// true
console.log(animalCount.test("15 pigchickens"));
// false

Los parntesis () se pueden usar para limitar la parte del patrn a la que se aplica el operador
| (tubo) y se pueden poner varios operadores de este tipo, uno al lado del otro, para expresar
opciones entre ms de dos patrones.

La mecnica del matcheo o coincidencia


Las expresiones regulares pueden considerarse diagramas de flujo. Este es el diagrama para el
ejemplo anterior:

Nuestra expresin coincide (matchea) con una cadena de caracteres si podemos seguir el
camino desde el lado izquierdo del diagrama hacia el lado derecho. Mantenemos una posicin
en la cadena y, cada vez que nos movemos a travs de una caja, verificamos que la parte de la
cadena que dejamos atras coincide con dicha casilla.

As que si tratamos de matchear "the 3 pigs" con nuestra expresin regular, nuestro
progreso a travs del diagrama de flujo sera as:

En la posicin 4, hay un lmite de palabras boundary, por lo que podemos pasar de la


primera casilla.

Todava en la posicin 4, encontramos un dgito, as que tambin podemos pasar de la


segunda caja.

En la posicin 5, un camino recorre hacia antes de la segunda (dgito) caja, mientras


que el otro se mueve hacia adelante a travs de la caja que contiene un solo carcter
de espacio. Hay un espacio aqu, no un dgito, as que debemos tomar el segundo
camino.

Ahora estamos en la posicin 6 (el comienzo de "pigs") y en la rama de tres vas en el


diagrama. No vemos "cow" o "chiken" aqu, pero s vemos "pig", as que tomamos esa
rama.

En la posicin 9, despus de la rama de tres vas, una ruta omite la caja s y va


directamente al lmite final de la palabra, mientras que la otra ruta coincide con una s.
Hay un carcter de s aqu, no una frontera de la palabra, as que vamos a travs de la
caja de s.
Estamos en la posicin 10 (el final de la cadena) y puede coincidir slo con un lmite
de palabra boundary. El final de una cadena cuenta como un lmite de palabra, por lo
que pasamos por el ltimo cuadro y hemos matcheado con esta cadena..

Conceptualmente, un motor de expresiones regulares busca una coincidencia en una cadena


de la siguiente manera: comienza al principio de la cadena e intenta una coincidencia all. En
este caso, hay un lmite de palabra (word boundary) all, por lo que pasara la primera casilla,
pero no hay dgitos, por lo que fallara en la segunda casilla. Luego se mueve al segundo
carcter de la cadena e intenta comenzar un nuevo partido all... y as sucesivamente, hasta
que encuentre una coincidencia o llegue al final de la cadena y decida que realmente no hay
coincidencia.

Retroceso
La expresin regular _/\b([01]+b|\d+|[\da-f]+h)\b/_ coincide con un nmero binario
seguido de a b, un nmero decimal regular sin carcter de sufijo o un nmero hexadecimal
(Es decir, base 16, con las letras de a - f para representar dgitos 10 a 15) seguido de h. Este
es el diagrama correspondiente:

Al hacer coincidir esta expresin, suceder a menudo que la rama superior (binaria) se
ingrese aunque la entrada no contenga realmente un nmero binario. Al buscar en la cadena
"103", por ejemplo, slo queda claro en el 3 que estamos en la rama equivocada. La cadena
coincide con la expresin, pero no con la rama en la que estamos actualmente.

As que el matcher retrocede. Al entrar en una rama recuerda su posicin actual (en este caso,
al comienzo de la cadena, justo despus de la primera caja de lmites en el diagrama) para que
pueda volver atrs y probar otra rama si la actual no funciona. Para la cadena "103", despus
de encontrar el carcter 3, comenzar a probar la rama para los nmeros decimales. ste
coincide, por lo que un match es reportado al final de todo.

El matcher se detiene tan pronto como encuentra una coincidencia completa. Esto significa
que si varias ramas podran potencialmente coincidir con una cadena, slo se utiliza la
primera (ordenada segn aparecen en la expresin regular).
El retroceso tambin ocurre para los operadores de repeticin como _+_ y _*_. Si coincide
con _/^.*x/_ contra "abcxe", la parte _*_ Intentar primero consumir toda la cadena. El
motor entonces se dar cuenta de que necesita una _x_ para que coincida con el patrn.
Puesto que no hay _x_ superado el final de la cadena, el operador _*_ intentara igualar un
carcter menos. Pero el _matcher_ tampoco encuentra una _x_ despus de _abcx_, por lo
que retrocede de nuevo, haciendo coincidir el operador _*_ con _abc_. Ahora si encuentra
una _x_ donde lo necesita e informa de una coincidencia exitosa desde las posiciones _0_ a
_4_.

Es posible escribir expresiones regulares con mucho retroceso (backtracking). Esto se


produce cuando un patrn puede coincidir con una pieza de entrada de muchas maneras
diferentes. Por ejemplo, si nos confundimos al escribir una expresin regular de un nmero
binario, podramos escribir algo como _/([01]+)+b/_.

Si intenta hacer coincidir una serie larga de ceros y unos sin ningn carcter b, el matcher
primero pasar por el bucle interno hasta que se quede sin dgitos. Entonces advierte que no
hay b, por lo que retrocede una posicin, pasa por el bucle externo una vez, y se da de nuevo,
tratando de retroceder fuera del bucle interior una vez ms. Seguir intentando cada ruta
posible a travs de estos dos bucles. Esto significa que la cantidad de trabajo se duplica con
cada carcter adicional. Para incluso slo unas pocas docenas de caracteres, el matcheo
resultante puede ser prcticamente eterno.

El mtodo de reemplazo
Los valores de cadena tienen un mtodo _replace_, que se puede usar para reemplazar parte
de la cadena con otra cadena.

console.log("papa".replace("p", "m"));
// mapa

El primer argumento tambin puede ser una expresin regular, en cuyo caso la primera
coincidencia de la expresin regular se sustituye. Cuando se agrega una opcin _g_ (global) a
la expresin regular, todas las coincidencias de la cadena se reemplazarn, no slo la primera.

console.log("Borobudur".replace(/[ou]/, "a"));
// Barobudur
console.log("Borobudur".replace(/[ou]/g, "a"));
// Barabadar
Hubiera sido razonable si la opcin entre reemplazar un solo patrn o todos los patrones se
hiciera a travs de un argumento adicional o proporcionando un mtodo diferente como
_replaceAll_, pero por alguna razn, esta opcin se indica en una propiedad de la misma
expresin regular.

El poder real de usar expresiones regulares con _replace_ viene del hecho de que podemos
referirnos de nuevo a los grupos coincidentes en la cadena de reemplazo. Por ejemplo,
digamos que tenemos una cadena grande que contiene los nombres de las personas, un
nombre por lnea, en el formato Apellido, Nombre. Si queremos intercambiar estos nombres y
eliminar la coma para obtener un simple formato apellido-nombre, podemos utilizar el
siguiente cdigo:

console.log(
"Hopper, Grace\nMcCarthy, John\nRitchie, Dennis"
.replace(/([\w ]+), ([\w ]+)/g, "$2 $1"));
// Grace Hopper
// John McCarthy
// Dennis Ritchie

Los _$1_ y _$2_ en la cadena de reemplazo se refieren a los grupos entre parntesis en el
patrn. $1 se sustituye por el texto que coincide con el primer grupo, _$2_ por el segundo, y
as sucesivamente hasta _$9_. Todo el _match_ puede ser referido con _$&_.

Tambin es posible pasar una funcin, en lugar de una cadena, como el segundo argumento a
reemplazar. Para cada reemplazo, la funcin se llamar con los grupos coincidentes (as como
la coincidencia completa) como argumentos, y su valor de retorno se insertar en la nueva
cadena.

Aqu hay un ejemplo sencillo:

var s = "the cia and fbi";


console.log(s.replace(/\b(fbi|cia)\b/g, function(str) {
return str.toUpperCase();
}));
// the CIA and FBI

Y aqu hay una ms interesante:

var stock = "1 lemon, 2 cabbages, and 101 eggs";


function minusOne(match, amount, unit) {
amount = Number(amount) - 1;
if (amount == 1) // only one left, remove the 's'
unit = unit.slice(0, unit.length - 1);
else if (amount == 0)
amount = "no";
return amount + " " + unit;
}
console.log(stock.replace(/(\d+) (\w+)/g, minusOne));
// no lemon, 1 cabbage, and 100 eggs

Este ejemplo toma una cadena, encuentra todas las veces que aparece un nmero seguido de
una palabra alfanumrica, y devuelve una cadena (string) en la que cada una de tales
apariciones se decrementa en una.
El grupo _(\d+)_ termina como el argumento _amount_ de la funcin, y el grupo _(\w+)_ se
vincula al argumento _unit_. La funcin convierte _amount_ en un nmero -que siempre
funciona, ya que coincide con _\d+-_ y hace algunos ajustes en caso de que slo quede uno o
cero, como quitar el plural o avisar la falta de stock con la palabra no.

Codicia
No es difcil utilizar _replace_ para escribir una funcin que elimina todos los comentarios
de una pieza de cdigo JavaScript. He aqu un primer intento:

function stripComments(code) {
return code.replace(/\/\/.*|\/\*[^]*\*\//g, "");
}
console.log(stripComments("1 + /* 2 */3"));
// 1 + 3
console.log(stripComments("x = 10;// ten!"));
// x = 10;
console.log(stripComments("1 /* a */+/* b */ 1"));
// 1 1

La parte anterior al operador _or_ (o) simplemente hace coincidir dos caracteres de barra
diagonal seguidos de cualquier nmero de caracteres de no-nueva-lnea. La parte para
comentarios multilnea est ms involucrada. Utilizamos _[^]_ (cualquier carcter que no
est en el conjunto vaco de caracteres) como una forma de coincidir con cualquier carcter.
No podemos usar un punto aqu porque los comentarios de bloque pueden continuar en una
nueva lnea y los puntos no coinciden con el carcter de nueva-lnea.

Pero la salida del ejemplo anterior parece funcionar mal. Por qu?

La parte _[^]*_ de la expresin, tal como describ en la seccin de retroceso, se ajustar


primero tanto como pueda. Si esto hace que la siguiente parte del patrn falle, el matcher
retrocede un carcter e intenta de nuevo desde all. En el ejemplo, el primero busca coincidir
con todo el resto de la cadena (string) y luego retrocede desde all. Encontrar una instancia
de _*/_ despus de volver cuatro caracteres y emparejar eso. Esto no es lo que queramos: la
intencin era hacer coincidir un solo comentario, no ir hasta el final del cdigo y encontrar el
final del ltimo comentario de bloque.

Debido a este comportamiento, decimos que los operadores de repeticin (_+_, _*_, _?_, y
_{}_) son codiciosos, lo que significa que coinciden tanto como pueden y retroceden desde
all. Si lo combinas con un signo de interrogacin (_+?_, _*?_, _??_, _{}_?), pierden su
codicia y empiezan por coincidir lo menos posible, matcheando ms slo cuando el patrn
restante no encaja en el match ms pequeo.

Y eso es exactamente lo que queremos en este caso. Al tener _*_ coincidir con la menor
extensin de caracteres que nos lleve a un _*/_, consumimos un comentario de bloque y nada
ms.

function stripComments(code) {
return code.replace(/\/\/.*|\/\*[^]*?\*\//g, "");
}
console.log(stripComments("1 /* a */+/* b */ 1"));
// 1 + 1
Una gran cantidad de errores bug en los programas de expresin regular se puede detectar al
comprobar el uso indebido de este comportamiento codicioso de un operador de repeticin.
Cuando utilice un operador de repeticin, considere primero la variante de cambiar su
comportamiento con _?_.

Creacin dinmica de objetos RegExp


Hay casos en los que puede que no conozca el patrn exacto que necesita para matchear en el
momento que est escribiendo su cdigo. Digamos que desea buscar el nombre del usuario en
un fragmento de texto y adjuntarlo en caracteres de subrayado para que se destaque. Dado
que sabr el nombre slo una vez que el programa se est ejecutando, no podr utilizar la
notacin basada en barras.

Pero puede construir una cadena y usar el constructor _RegExp_. He aqu un ejemplo:

var name = "harry";


var text = "Harry is a suspicious character.";
var regexp = new RegExp("\\b(" + name + ")\\b", "gi");
console.log(text.replace(regexp, "_$1_"));
// _Harry_ is a suspicious character.

Al crear los _\b_ boundary marcadores de frontera, tenemos que usar dos barras invertidas
porque las estamos escribiendo en una cadena normal, no una expresin regular con barra
inclinada. El segundo argumento para el constructor _RegExp_ contiene las opciones para la
expresin regular, en este caso _"gi"_ para global y sin distincin entre maysculas y
minsculas.

Pero qu ocurre si el nombre es _"dea+hl[]rd"_, un nombre posble si nuestro usuario es un


nerd adolescente? Esto dara lugar a una expresin regular absurda, que no coincidir con el
nombre del usuario.

Para evitar esto, podemos agregar barras inversas antes de cualquier carcter en el que no
confiamos. Agregar barras invertidas antes de caracteres alfabticos es una mala idea porque
cosas como _\b_ y _\n_ tienen un significado especial. Pero escapar de todo lo que no es
alfanumrico o espacio en blanco es seguro.

var name = "dea+hl[]rd";


var text = "This dea+hl[]rd guy is super annoying.";
var escaped = name.replace(/[^\w\s]/g, "\\$&");
var regexp = new RegExp("\\b(" + escaped + ")\\b", "gi");
console.log(text.replace(regexp, "_$1_"));
// This _dea+hl[]rd_ guy is super annoying.

El mtodo search (bsqueda)


El mtodo _indexOf_ de cadenas no se puede llamar desde una expresin regular. Pero hay
otro mtodo, _search_, que se puede invocar en una expresin regular. Al igual que
_indexOf_, devuelve el primer ndice en el que se encontr la expresin o devolver -1 si no
encontr nada.

console.log(" word".search(/\S/));
// 2
console.log(" ".search(/\S/));
// -1

Desafortunadamente, no hay manera de indicar que el matcheo comience en un determinado


indice (como si podemos hacerlo con el segundo argumento en _indexOf_).

La propiedad lastIndex
El mtodo _exec_ tampoco proporciona una manera comoda de iniciar una bsqueda desde
una posicin dada en la cadena, pero al menos si proporciona una manera incmoda de
hacerlo.

Los objetos de expresin regular tienen propiedades. Una de estas propiedades es _source_,
que contiene la cadena original de la que se cre la expresin. Otra propiedad es
_lastIndex_, que controla, solo en algunas circunstancias, donde se iniciar la prxima
partida.

Esas circunstancias son que la expresin regular debe tener activada la opcin global (_g_), y
la coincidencia debe pasar a travs del mtodo _exec_. Una vez ms, una solucin ms
lgica habra sido simplemente permitir que un argumento extra fuera pasado a _exec_, pero
la cordura no es una caracterstica de la interfaz de expresin regular de JavaScript.

var pattern = /y/g;


pattern.lastIndex = 3;
var match = pattern.exec("xyzzy");
console.log(match.index);
// 4
console.log(pattern.lastIndex);
// 5

Si el resultado fue satisfactorio, la llamada a _exec_ actualiza automticamente la propiedad


_lastIndex_ para apuntar despus de la coincidencia. Si no se encontr ninguna
coincidencia, _lastIndex_ se pone de nuevo a cero, que es tambin el valor que tiene en un
objeto de expresin regular de nueva construccin.

Cuando se utiliza un valor de expresin regular global para varias llamadas _exec_, estas
actualizaciones automticas a la propiedad _lastIndex_ pueden causar problemas. Su
expresin regular podra iniciarse accidentalmente en un ndice que qued de una llamada
anterior.

var digit = /\d/g;


console.log(digit.exec("here it is: 1"));
// ["1"]
console.log(digit.exec("and now: 1"));
// null

Otro efecto interesante de la opcin global es que cambia la forma en que funciona el mtodo
de coincidencia en cadenas (_match_). Cuando se llama con una expresin global, en lugar
de devolver una matriz similar a la devuelta por _exec_, _match_ encontrar todas las
coincidencias del patrn en la cadena y devuelve una matriz que contiene las cadenas
coincidentes.
console.log("Banana".match(/an/g));
// ["an", "an"]

As que tenga cuidado con las expresiones regulares globales. Los casos en los que son
necesarios (las llamadas a _replace_ y los lugares donde se desea utilizar explcitamente
_lastIndex_) suelen ser los nicos lugares donde debera utilizarlos.

Bucle de matchs

Un patrn comn es escanear todas las instancias de un patrn en una cadena, de una manera
que nos da acceso al objeto de coincidencia en el cuerpo del bucle, usando _lastIndex_ y
_exec_.

var input = "A string with 3 numbers in it... 42 and 88.";


var number = /\b(\d+)\b/g;
var match;
while (match = number.exec(input))
console.log("Found", match[1], "at", match.index);
// Found 3 at 14
// Found 42 at 33
// Found 88 at 40

Esto hace uso del hecho de que el valor de una expresin de asignacin (_=_) es el valor
asignado. Por lo tanto, utilizando _match = number.exec(input)_ como condicin en la
sentencia _while_, realizamos la coincidencia al comienzo de cada iteracin, guardamos su
resultado en una variable y detenemos el bucle (loop) cuando no se encuentran ms
coincidencias.

Anlisis de un archivo INI


Para concluir el captulo, veremos un problema que requiere expresiones regulares. Imagina
que estamos escribiendo un programa para recolectar automticamente informacin sobre
nuestros enemigos desde Internet. (En realidad, no escribiremos ese programa aqu, solo la
parte que lee el archivo de configuracin. Sentimos decepcionarlo.) El archivo de
configuracin se ver as:

searchengine=http://www.google.com/search?q=$1
spitefulness=9.7

; comments are preceded by a semicolon...


; each section concerns an individual enemy
[larry]
fullname=Larry Doe
type=kindergarten bully
website=http://www.geocities.com/CapeCanaveral/11451

[gargamel]
fullname=Gargamel
type=evil sorcerer
outputdir=/home/marijn/enemies/gargamel

Las reglas exactas para este formato (que en realidad es un formato ampliamente utilizado,
generalmente llamado archivo INI) son las siguientes:
Las lneas en blanco y lneas que comienzan con punto y coma son ignoradas.

Lneas envueltas en llaves ([]) inician una nueva seccin.

Las lneas que contienen un identificador alfanumrico seguido de un carcter =


aaden una configuracin a la seccin actual.

Cualquier otra cosa es invlida.

Nuestra tarea es convertir una cadena como sta en una matriz de objetos, cada uno con una
propiedad _name_ y una matriz de configuraciones. Necesitaremos un objeto para cada
seccin y otro para la configuracin global en la parte superior.

Dado que el formato tiene que ser procesado lnea por lnea, dividir el archivo en lneas
separadas es un buen comienzo. No obstante, algunos sistemas operativos utilizan no slo un
carcter de nueva lnea para separar lneas, sino un carcter de retorno de carro seguido de
una nueva lnea (_"\r\n"_). Dado que el mtodo _split_ tambin permite que una
expresin regular sea su argumento, podemos dividir en una expresin regular como
_/\r?\n/_ para dividir de una manera que permita tanto _"\n"_ como _"\r\n"_ entre
lneas.

function parseINI(string) {
// Start with an object to hold the top-level fields
var currentSection = {name: null, fields: []};
var categories = [currentSection];

string.split(/\r?\n/).forEach(function(line) {
var match;
if (/^\s*(;.*)?$/.test(line)) {
return;
} else if (match = line.match(/^\[(.*)\]$/)) {
currentSection = {name: match[1], fields: []};
categories.push(currentSection);
} else if (match = line.match(/^(\w+)=(.*)$/)) {
currentSection.fields.push({name: match[1],
value: match[2]});
} else {
throw new Error("Line '" + line + "' is invalid.");
}
});

return categories;
}

Este cdigo pasa por todas las lneas del archivo, actualizando el objeto "currentSection" a
medida que avanza. En primer lugar, comprueba si la lnea se puede ignorar, utilizando la
expresin _/^\s*(;.*)?$/_. Ves cmo funciona? La parte entre los parntesis coincidir
con los comentarios, y el signo _?_ se asegurar de que tambin coincida con las lneas que
contienen slo espacios en blanco.

Cuando la lnea no es un comentario, el cdigo comprueba si la lnea inicia una nueva


seccin. Si es as, crea un nuevo objeto de seccin actual, al que se aadirn los ajustes
siguientes.
La ltima posibilidad significativa es que la lnea es un ajuste normal, por lo que nuestro
cdigo lo agregar al objeto de seccin actual.

Si una lnea no coincide con ninguno de estos formularios, la funcin produce un error.

Tenga en cuenta el uso recurrente de _^_ y _$_ para asegurarse de que la expresin coincide
con toda la lnea, no slo parte de ella. Dejando estos resultados en cdigo que funciona sobre
todo, pero se comporta extraamente para algunos tipos de input, lo que puede originar un
error difcil de rastrear.

El patrn _if (match = string.match(...))_ es similar al truco de usar una asignacin


como condicin para _while_. A menudo no estaremos seguros de que nuestra llamada a
_matchear_ tendr xito, por lo que se puede acceder al objeto resultante slo dentro de una
instruccin if que las pruebas de ello. Para no romper la agradable cadena que forma la
sentencia _if_, asignamos el resultado de la coincidencia a una variable e inmediatamente
utilizamos esa asignacin como el _test_ en la sentencia _if_.

Caracteres internacionales
Debido a la implementacin simplista inicial de JavaScript y al hecho de que este enfoque se
estableci como base del comportamiento estndar, las expresiones regulares de JavaScript
son bastante torpes acerca de los caracteres que no aparecen en el idioma ingls. Por ejemplo,
en lo que se refiere a las expresiones regulares de JavaScript, un "carcter para texto" (word
character) es slo uno de los 26 caracteres del alfabeto latino (maysculas o minsculas) y,
por alguna razn, el carcter underscore (). Caracteres como _ o , que definitivamente son
caracteres de texto, no coincidirn con _\w_ (y coincidirn con _\W_, carcter para no texto
(nonword category)).

Por un extrao accidente histrico, _\s_ (espacio en blanco) no tiene este problema y
coincide con todos los caracteres que el estndar Unicode considera espacios en blanco,
incluyendo cosas como el espacio no separable y el separador de vocales mongol.

Algunas implementaciones de expresiones regulares en otros lenguajes de programacin


tienen sintaxis para coincidir con categoras de caracteres Unicode especficas, como "todas
las letras maysculas", "todos los signos de puntuacin" o "caracteres de control". Hay planes
para agregar soporte para JavaScript de estas categoras, pero por desgracia parece que no en
un futuro prximo.

Resumen
Las expresiones regulares son objetos que representan patrones en cadenas. Utilizan su propia
sintaxis para expresar estos patrones.

/abc/ Una secuencia de caracteres


/[abc]/ Cualquier carcter en un conjunto de caracteres
/[^abc]/ Cualquier carcter que no este en un conjunto de caracteres
/[0-9]/ Cualqier carcter en un rango de caracteres
/x+/ Una o mas ocurrencias del patron x
/x+?/ Una o mas ocurrencias, no codiciosos
/x*/ Cero o mas ocurrencias
/x?/ Cero o una ocurrencia
/x{2,4}/ Entre dos o cuatro ocurrencias
/(abc)/ Un grupo
/a|b|c/ Cualqiera de estos patrones
/\d/ Cualquier carcter digital
/\w/ Cualquier carcter alfanmerico (word character)
/\s/ Cualquier carcter de espacio en blanco
/./ Cualquier carcter excepto nuevas lneas
/\b/ Limite de palabra
/^/ Inicio de la entrada
/$/ Fin de la entrada

Una expresin regular tiene un mtodo _test_ para comprobar si una cadena dada tiene
coincidencias con ella. Tambin tiene un mtodo _exec_ que, cuando se encuentra una
coincidencia, devuelve una matriz que contiene todos los grupos coincidentes. Dicha matriz
tiene una propiedad de ndice (_index_) que indica dnde se inici la coincidencia.

Las cadenas tienen un mtodo _match_ para buscar coincidencia con una expresin regular y
un mtodo _search_ para realizar busquedas, que devuelve slo la posicin inicial de la
coincidencia. El mtodo _replace_ puede reemplazar las coincidencias (los matcheos) de un
patrn con una cadena de reemplazo. Como alternativa puedes crear una funcin _replace_,
y as crear una cadena de reemplazo basada en el texto de coincidencia y los grupos
coincidentes.

Las expresiones regulares pueden tener opciones, que se escriben despus de la barra de
cierre (|). La opcin _i_ hace que el matcheo sea insensible a mayusculas y minusculas (case
insensitive), mientras que la opcin _g_ hace que la expresin sea global, eso genera, entre
otras cosas, hace que el mtodo replace reemplace todas las instancias en lugar de slo la
primera.

El constructor _RegExp_ se puede utilizar para crear un valor de expresin regular a partir de
una cadena.

Las expresiones regulares son una herramienta muy afilada con un comando torpe. Ello
simplifican algunas tareas pero pueden volverse incontrolables cuando se aplican a problemas
complejos. Parte de saber cmo usarlos es resistir el impulso de intentar utilizarlas en los
casos incorrectos.

Ejercicios
Es casi inevitable que cuando haga estos ejercicios, se confunda y se frustre por el
comportamiento inexplicable de alguna expresin regular. A veces, ayuda a introducir su
expresin en una herramienta en lnea como debuggex.com para ver si su solucin
corresponde con lo que usted buscaba y experimentar con la forma en que sta responde a
distintas cadenas.
Regexp golf

Code golf es un trmino usado en un juego que busca expresar un programa determinado en
la mnima cantidad de caracteres posible. Del mismo modo, regexp golf es la prctica de
escribir una expresin regular tan pequea como sea posible para que coincida con un patrn
dado, y slo ese patrn.

Para cada uno de los siguientes elementos, escriba una expresin regular que compruebe si
alguna de las subcadenas se produce en una cadena. La expresin regular debe coincidir slo
con cadenas que contengan una de las subcadenas descritas. No se preocupe por los lmites de
palabras a menos que se mencionen explcitamente. Cuando logre que su expresin funcione,
vea si puede lograr reducir la expresin.

1. car and cat

2. pop and prop

3. ferret, ferry, y ferrari

4. Cualquier palabra que termine en ious

5. Un carcter de espacio en blanco seguido de un punto, coma, dos puntos o punto y


coma

6. Una palabra de ms de seis letras

7. Una palabra sin la letra e

Consulte la tabla del resumen del captulo para obtener ayuda. Pruebe cada solucin con unas
cuantas cadenas de prueba.

// Fill in the regular expressions

verify(/.../,
["my car", "bad cats"],
["camper", "high art"]);

verify(/.../,
["pop culture", "mad props"],
["plop"]);

verify(/.../,
["ferret", "ferry", "ferrari"],
["ferrum", "transfer A"]);

verify(/.../,
["how delicious", "spacious room"],
["ruinous", "consciousness"]);

verify(/.../,
["bad punctuation ."],
["escape the dot"]);

verify(/.../,
["hottentottententen"],
["no", "hotten totten tenten"]);

verify(/.../,
["red platypus", "wobbling nest"],
["earth bed", "learning ape"]);

function verify(regexp, yes, no) {


// Ignore unfinished exercises
if (regexp.source == "...") return;
yes.forEach(function(s) {
if (!regexp.test(s))
console.log("Failure to match '" + s + "'");
});
no.forEach(function(s) {
if (regexp.test(s))
console.log("Unexpected match for '" + s + "'");
});
}

Estilo de citas

Imagine que ha escrito una historia en Ingles y ha usado comillas simples para marcar partes
del dilogo. Ahora desea reemplazar todas las citas de dilogo con comillas dobles,
manteniendo las comillas simples que se usan en contracciones.

Piense en un patrn que distinga estos dos tipos de comillado y cree una llamada al mtodo
_replace_ que haga la sustitucin adecuada.

var text = "'I'm the cook,' he said, 'it's my job.'";


// Change this call.
console.log(text.replace(/A/g, "B"));
// "I'm the cook," he said, "it's my job."

Numbers again

Una serie de dgitos puede ser igualada por la expresin regular simple _/\d+/_.

Escriba una expresin que coincida slo con los nmeros vlidos para JavaScript. Debe
soportar un signo positivo o negativo adicional delante del nmero, el punto decimal y la
notacin de exponente _5e-3_ o 1E10- tambien con la opcion de un signo delante del
exponente. Observe tambin que no es necesario que haya dgitos delante o despus del
punto, pero el nmero no puede ser solamente un punto, es decir, _.5_ y _5._ son nmeros
JavaScript vlidos, pero un punto solitario no lo es.Write an expression that matches only
JavaScript-style

// Fill in this regular expression.


var number = /^...$/;

// Tests:
["1", "-1", "+15", "1.55", ".5", "5.", "1.3e2", "1E-4",
"1e+12"].forEach(function(s) {
if (!number.test(s))
console.log("Failed to match '" + s + "'");
});
["1a", "+-1", "1.2.3", "1+1", "1e4.5", ".5.", "1f5",
"."].forEach(function(s) {
if (number.test(s))
console.log("Incorrectly accepted '" + s + "'");
});

Potrebbero piacerti anche