Capítulo 1Valores, Tipos y Operadores
Debajo de la superficie de la máquina, el programa se mueve. Sin esfuerzo, se expande y contrae. En gran armonía, los electrones se separan y reagrupan. Las formas en el monitor no son más que ondas en el agua. La escencia permanece invisible debajo.
En el mundo de las computadoras sólo 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 eléctrica, una señal débil o fuerte, o un punto brillante u opaco en la superficie de un CD. Cualquier pieza de información discreta puede ser reducida a una secuencia de ceros y unos y por lo tanto representada como bits.
Por ejemplo, piensa cómo podrías representar el número 13 en bits. Funciona de la misma forma en que escribes números decimales, pero en vez de tener 10 dígitos, tienes sólo 2, y el peso de cada uno se incrementa por un factor de 2 de derecha a izquierda. Aquí están los bits que conforman el número 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 número binario 00001101, u 8 + 4 + 1, que equivale a 13.
Valores
Imagina un mar de bits. Un océano de estos. Una computadora moderna típica tiene más de 30 mil millones de bits en su almacenamiento de datos volátil. El almacenamiento no volátil (el disco duro o su equivalente) tiene un par de órdenes de magnitud más todavía.
Para ser capaz de trabajar con semejantes cantidades de bits sin perderte, puedes separarlos en pedazos que representen piezas de información. En un entorno en JavaScript, esos pedazos son llamados valores. Aunque todos los valores están hechos de bits, juegan diferentes roles. Cada valor tiene un tipo que determina su rol. Existen seis tipos básicos de valores en JavaScript: números, cadenas, Booleanos, objetos, funciones, y valores indefinidos.
Para crear un valor, sólo tienes que invocar su nombre. Esto es conveniente. No tienes que reuinir el material de construcción de tus valores o pagar por ellos. Sólo llamas uno y woosh, lo tienes. No son creados de la nada, por supuesto. Cada valor tiene que estar almacenado en algún lugar, y si quieres usar una cantidad enorme de estos al mismo tiempo, te podrías quedar sin bits. Afortunadamente, esto se convierte en un problema sólamente 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 construcción de la próxima generación de valores.
Este capítulo introduce los elementos atómicos de los programas en JavaScript, los tipos de valores simples y los operadores que pueden actuar sobre tales valores.
Números
Los valores de tipo número(number) son, sin sorpresa alguna, valores numéricos. En un programa en JavaScript, se escriben de la siguiente forma:
13
Usa eso en un programa y causará que el patrón de bits para el número 13 exista dentro de la memoria de la computadora.
JavaScript usa una cantidad fija de bits, 64, para guardar un valor del tipo número. Existe un límite en la cantidad de patrones que se pueden hacer con 64 bits, lo que significa que la cantidad de números que puedes representar también es limitada. Para N dígitos decimales, la cantidad de números que pueden ser representados es 10N. Similarmente, dados 64 dígitos binarios, puedes representar 264 números diferentes, que es cerca de 18 cuatrillones (un 18 con 18 ceros después). Eso es mucho.
La memoria de la computadora solía ser mucho más pequeña, y la gente tendía a usar grupos de 8 ó 16 bits para representar sus números. Era fácil desbordar accidentalmente números tan pequeños: terminar con un número que no pudiera ser almacenado en el número 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 sólo cuando estés tratando con números verdaderamente astronómicos.
No todos los número enteros debajo de 18 cuatrillones caben en un número de JavaScript. Esos bits también guardan números negativos, así que un bit indica el signo del número. Un problema mayor es que los números no enteros también deben ser representados. Para hacer esto, algunos de los bits son usados para guardar la posición del punto decimal. El número entero máximo real que puede ser guardado está más cerca del rango de los 9 trillones (15 ceros), que aún es satisfactoriamente grande.
Los números fraccionarios son escritos usando un punto.
9.81
Para números muy grandes o muy pequeños, también se puede usar la notación científica, añadiendo una "e" de "exponente", seguido por el exponente del número:
2.998e8
Esto es 2.998 × 108 = 299,800,000.
Cálculos con números enteros (en inglés llamados integer) más pequeños que el supracitado 9 trillones, están garantizados para siempre ser precisos. Desafortunadamente, cálculos con números fraccionarios generalmente no lo son. Justo como π (pi) no puede ser expresado precisamente por un número finito de dígitos decimales, muchos números pierden algo de precisión cuando sólo hay 64 bits disponibles para guardarlos. Esto es una pena, pero causa problemas prácticos sólo en algunas situaciones específicas. Lo importante es estar al tanto de esto y tratar a los números digitales fraccionarios como aproximaciones y no como valores precisos.
Aritmética
Lo principal que se hace con los números es la aritmética. Las operaciones aritméticas como la suma o la multiplicación toman dos valores y producen uno nuevo a partir de estos. Así es como lucen en JavaScript:
100 + 4 * 11
Los símbolos +
y *
son llamados operadores. El primero representa la suma y el segundo
la multiplicación. Al poner un operador entre dos valores, se
aplicará la operación 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 multiplicación ralizada antes de hacer la suma? Como pudiste haber adivinado, la multiplicación ocurre primero. Pero como en matemáticas, puedes cambiar esto mediante encerrar en paréntesis la suma.
(100 + 4) * 11
Para la resta existe el operador -
,
y la división se puede hacer con el operador /
.
Cuando los operadores aparecen juntos sin paréntesis, el orden en el que
son aplicados es determinado por su precedencia. El ejemplo muestra
que la multiplicación 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 deberías de preocupar. Cuando tengas duda, simplemente agrega paréntesis.
Existe un operador aritmético más, que podrías no reconocer
inmediatamente. El símbolo %
es usado para representar la
operación 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 multiplicación
y división. Verás a menudo este operador referido como módulo,
aunque técnicamente sobrante es más preciso.
Números Especiales
Hay tres valores especiales en JavaScript que son considerados números pero no se comportan como números 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 confíes mucho en los
cálculos basados en infinitos. No son matemáticamente confiables y
pronto te llevarán al próximo número especial: NaN
.
NaN
son las siglas
de “not a number” ("no es un número"), aunque es un valor del tipo
número. Obtendrás este resultado cuando, por ejemplo, trates de
calcular 0 / 0
(cero entre cero), Infinity - Infinity
, o cualquier
otra operación numérica que no produzca un resultado
preciso, significativo.
Cadenas
El siguiente tipo de dato básico 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 difíciles. Puedes imaginar que poner comillas dentro de comillas puede ser difícil. Newlines (el carácter salto de línea, lo que obtines cuando presionas Enter), tampoco puede ser introducido entre comillas. La cadena tiene que permanecer en una sola línea.
Para hacer posible
la inclusión de estos caracteres en una cadena de texto, la siguiente
notación es usada: cuando una diagonal invertida(backslash: \
)
se encuentra dentro de un texto entre comillas, indica que el carácter
siguiente tiene un significado especial. Esto es llamado escapar el
carácter. Una comilla que es precedida por una diagonal invertida
no terminará la cadena, sino que será parte de ella. Cuando un
carácter n
sigue a una diagonal invertida, se interpreta como
una nueva línea. Similarmente, un t
después de la diagonal invertida
significa un tabulador. Tomemos la siguiente cadena:
"Esta es la primera línea\nY esta la segunda"
El verdadero texto contenido es:
Esta es la primera línea Y esta la segunda
Existen, por supuesto, situaciones en las que querrás que una
diagonal invertida sea sólo eso en una cadena de texto, no un
código especial. Si dos diagonales invertidas están juntas, se volverán
una, y sólo eso quedará como resultado en el valor de la cadena.
Así es como la cadena "Un carácter de nueva línea es escrito "\n".
"
puede ser expresada:
"Un carácter de nueva línea es escrito \"\\n\"."
Las cadenas de texto no pueden ser
divididas numéricamente, multiplicadas, o restadas, pero el carácter
+
puede ser usado en ellas. No suma, sino que concatena; pega dos
cadenas. La siguiente línea produce la cadena "concatenar"
:
"con" + "cat" + "e" + "nar"
Hay más maneras de manipular las cadenas, de las que hablaremos cuando lleguemos a los métodos en el Capítulo 4.
Operadores unarios
No todos los operadores
son símbolos. 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.
console.log(typeof 4.5) // → number console.log(typeof "x") // → string
Usaremos
console.log
para indicar que queremos ver el resultado de la evaluación
de algo. Cuando corres ese código, el valor producido debería mostrarse en
pantalla, aunque la forma en que aparece dependerá del entorno en que
estés corriendo el programa.
Los otros operadores que hemos visto operaban sobre
dos valores, pero typeof
sólamente toma uno. Los operadores que usan
dos valores son llamados operadores binarios, mientras que aquellos
que sólo 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, necesitarás 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 sólo dos valores, verdadero (true) y falso (false), que son simplemente estas palabras en inglés.
Comparaciones
Esta es una forma de producir valores booleanos:
console.log(3 > 2) // → true console.log(3 < 2) // → false
Los signos >
y <
son los símbolos 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 más o menos alfabética: las letras mayúsculas son siempre "menores"
que las minúsculas, así que "Z" < "a"
es verdad, y los caracteres no
alfabéticos (!, -, y así por el estilo) son también incluidos en el
ordenamiento. La comparación real está basada en el estándar
Unicode. Este estándar asigna un número virtualmente a cada
carácter que alguna vez necesitarás, incluyendo caracteres del griego,
árabe, japonés, tamil, y otros alfabetos. Tener tales números es
útil para guardar las cadenas de texto dentro de la computadora porque
hace posible representarlas como una secuencia de números. Cuando
comparamos cadenas, JavaScript va de izquierda a derecha, comparando
los códigos numéricos 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
Sólo existe un valor en JavaScript
que no es igual a sí mismo, y este es NaN
, que significa "no es un
número".
console.log(NaN == NaN) // → false
La intención de NaN
es representar el resultado de un cálculo sin
sentido y como tal, no es igual al resultado de cualquier otro
cálculo sin sentido.
Operadores Lógicos
Hay también algunas operaciones que pueden ser aplicadas a los valores Booleanos. JavaScript soporta tres operadores lógicos: and, or y not. Estos pueden ser usados para "razonar" con los Booleanos.
El operador &&
representa la operación
lógica and ("y"). Es un operador binario, y su resultado es verdadero(true)
sólo si los dos valores dados son verdaderos.
console.log(true && false) // → false console.log(true && true) // → true
El operador ||
denota la operación
lógica or ("o"). Devuelve verdadero si cualquiera de los dos valores
dados es verdadero.
console.log(false || true) // → true console.log(false || false) // → false
Not (Negación) es escrito como un símbolo
de admiración (!
). 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
aritmética y otros operadores, no es siempre obvio cuándo se necesitan los
paréntesis. En la prácitca, puedes avanzar sabiendo que de los operadores
que hemos visto hasta ahora, ||
tiene la menor precedencia, después
viene el &&
, siguen los operadores de comparación(>
, ==
, etc.),
y después los demás operadores. Este orden ha sido elegido tal que, en
expresiones típicas con la siguiente, sean necesarios tan pocos
paréntesis como sea posible:
1 + 1 == 2 && 10 * 10 > 50
El último operador lógico del que hablaré no es unario, ni binario, sino ternario, opera en tres valores. Este es escrito con un símbolo de interrogación 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 interrogación "escoge" cuál 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 información.
Muchas operaciones en el lenguaje que no producen un valor con significado
(lo verás después) producen undefined
simplemente porque tienen que producir
algún valor.
La diferencia en el significado entre undefined
y null
es un accidente del
diseño de JavaScript, y no importa la mayoría del tiempo. En los casos en
dónde realmente te tienes que preocupar de estos valores, te recomiendo tratarlos
como intercambiables (más de esto en un momento).
Conversión automática de tipos
En la introducción, 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 coerción de tipo. Así que
el null
en la primera expresión se vuelve 0
, y el "5"
en la
segunda expresión se convierte en 5
(de cadena a número). Aún así,
en la tercera expresión, +
intenta hacer concatenación de cadenas
antes de suma numérica, así que el 1
es convertido en "1"
(de
número a cadena).
Cuando algo que no
se corresponde con un número de manera obvia (como "cinco"
o
undefined
) es convertido a un número, el valor resultante
es NaN
. Las siguientes operaciones aritméticas sobre NaN
seguirán
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 fácil de predecir: deberías 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 mayoría
de los casos, sólo trata de convertir uno de los valores al tipo de
dato del otro valor. Sin embargo, cuando null
o undefined
están
en cualquier lado de la operación, resulta verdadero sólo 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 números a Booleanos dicen que 0
, NaN
y la
cadena vacía (""
) cuentan como false
, mientras que todos los
demás valores cuentan como true
. Debido a esto, expresiones como
0 == false
y "" == false
también son verdaderas. Para casos como
este en el que no quieres que ocurra ninguna conversión automática
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 comparación de tres caracteres defensivamente para evitar que conversiones de tipos inesperadas te causen problemas. Pero cuando estás seguro de que los tipos en ambos lados serán los mismos, no hay problema con usar los operadores más cortos.
Corto circuito de operadores lógicos
Los
operadores lógicos &&
and ||
manejan los valores de diferentes
tipos de un modo peculiar. Convertirán el valor de su lado izquierdo
para decidir qué hacer, pero dependiendo del operador y del resultado
de la conversión, 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 conversión funciona como
esperarías con valores Booleanos y debería hacer algo análogo 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 expresión que puede producir
un valor vacío, 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 sólo es evaluado cuando es
necesario. En el caso de true || X
, no importa lo que sea X
,
incluso en si es una expresión que hace algo terrible, el resultado
será verdadero, y X
no será evaluado nunca. Lo mismo ocurre para
false && X
, lo cuál es falso e ignorará X
. Esto es llamado
evaluación de corto circuito (short-circuit evaluation).
El operador condicional trabaja de una forma similar. La primera expresión es evaluada siempre, pero el segundo o tercer valor, el que no sea escogido, no se evalúa.
Resumen
Vimos cuatro tipos de valores de JavaScript en este capítulo: números, 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 aritmética(+
, -
,
*
, /
y %
), concatenación de cadenas (+
), comparación
(==
, !=
, ===
, !==
, <
, >
, <=
, >=
), y lógica
(&&
, ||
), así como varios operadores unarios (-
para hacer
negativo un número, !
para negar lógicamente y typeof
para
obtener el tipo de un valor) y un operador ternario (?:
) para
elegir entre dos valores basándonos en un tercero.
Esto te brinda suficiente información para usar JavaScript como una pequeña calculadora, pero no para mucho más. El siguiente capítulo empezará a entremezclar estas expresiones en programas básicos.