Valores, 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. Master Yuan-Ma, The Book of Programming

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 10^N^. Similarmente, dados 64 dígitos binarios, puedes representar 2^64^ 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 × 10^8 = 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ó 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 link:04_data.html#methods[Capítulo 4].

Operadores Unitario

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 unitarios. El operador menos puede usar tanto como operador binario como operador unitario.

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 a virtualemente 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 esxiste 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 unitario, 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 unitarios (- 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.

results matching ""

    No results matching ""