Tipos de datos en Javascript, lo que no te enseñaron

Tipos de datos en Javascript, lo que no te enseñaron

Introducción

Los tipos de datos en Javascript son un poco diferentes en comparación con otros lenguajes de programación como C o JAVA.

Dejando a un lado los atractivos de mis buenas partes, no tengo otros encantos

William Shakespeare, The Merry Wives of Windsor

JavaScript es un lenguaje débilmente tipeado, esto quiere decir que no es necesario definir el tipo de dato. Pero tampoco es que no tenga tipos, pues el tipo de dato es definido en tiempo de ejecución por Javascript.

Este comportamiento y especificación de los tipos de datos aplica para cualquier lugar donde se ejecute javascript, ya sea en el navegador, en node.js, mongodb, o cualquier herramienta que use Javascript.

Ahora si analizamos un poco el concepto de tipos, cuando realizas una suma entre números en el mundo real no te importa que tipo es, solo te importa que se puedan sumar, sin importar si es un entero, o decimal. Estos subtipos de números siguen siendo parte del conjunto de números reales, y son utilizados en la vida real.

Javascript nos permite aplicar este pensamiento y con eso nos ahorramos muchas comparaciones de tipos. Habrá ocasiones en que no quede de otra que convertirlos o compararlos, pero entre menos lo hagamos, más sencillo será nuestro código, y por consiguiente más fácil de entender y más rápido de ejecutar.

Se dice que Javascript tienen un sistema de tipos de datos dinámico, esto es porque las variables que se crean pueden recibir cualquier otro tipo de dato en cualquier momento, e ir cambiando de tipo según el valor que guarden. Lo que realmente nos interesa es el valor y que podemos hacer con ese valor, no tanto el tipo.

Más adelante veremos algunas conversiones y comparaciones que pueden confundir y complicar el código, la recomendación es disminuir su uso en la medida de lo posible. Aunque no veremos muy a detalle cada tipo, sí que veremos cosas más importantes, necesarias para usar los tipos de datos en Javascript.

Es útil que se sepa definir variables y constantes, si no sabes como crear una variable o una constante, puedes revisar esta información antes de continuar.

Tipos de datos primitivos

  • Es aquel que no es un objeto
  • No tiene métodos.
  • Son inmutables
  • Tenemos siete tipos datos primitivos en Javascript.
  1. Number, números como; 1, 0, 18500, 89.95124
  2. BigInt, agregado en el 2020, para representar números enteros muy, pero muy grandes, 99999999999999n
  3. String, cadena de caracteres como 'Hola' y "Buenas noches".
  4. Boolean, solo acepta true o false, es decir, si o no.
  5. null, sirve para indicar que algo es nada, su único valor es null.
  6. undefined, sirva para indicar que algo aún no está definido.
  7. Symbol, agregado en el 2015, con EcmaScript 6

Tipos de datos Objetos

Todo lo demás tipos de datos en Javascript son objetos. En la sección Objetos está la explicación.

Number

Si hubiera escrito esto antes del 2020, te diría que solo hay un tipo de dato numérico, lamentablemente (con algunas excepciones) se agregó un nuevo valor numérico llamado BigInt. En esta sección fingiremos que no existe.

Number es un valor flotante de 64 bits, es decir, es un número double en otros lenguajes de programación, básicamente este es el único tipo de dato numérico en javascript, no existen enteros (a excepción de BigInt), decimales, flotantes o doubles. Su especificación está proporcionada por IEEE-754.

La forma de definirlos y usarlos es muy sencilla, simplemente escribes el número, no importa si es entero o decimal, punto flotante o double, como mencionamos antes, todos estos serán de tipo double para javascript. Esta es la forma recomendada de definirlos.

const edad = 33;
const PI = 3.1416;
const descuento = 0.30;
const pesoEnDolares = 0.05; // que tristeza xD!!

edad + descuento; // 33.30, no nos interesa el tipo, solo la suma

Como vemos en la última línea, podemos sumar enteros y decimales sin ningún problema, no nos interesa el tipo, nos interesa la habilidad de sumarlos.

Se sabe que los valores flotantes tienen cierto error de precisión al realizar operaciones aritméticas, la operación más obvia es la división.

0.2 + 0.1; // es igual a 0.30000000000000004

Como vemos no nos da un valor exacto de 0.3, para mitigar esto puedes multiplicar los valores por 100 y luego dividir el resultado entre 100.

(0.2 * 100 + 0.1 * 100)/100; // es igual a 0.3

NaN

Existe un número especial llamado NaN. Este valor es el error de una operación con números.

25 * undefined; // es igual a NaN
25 * {}; // es igual a NaN

Todo operación que tenga NaN como uno de sus operandos, dará como resultado NaN.

25 * NaN; // es igua a NaN
25 + NaN; // es igual a NaN

NaN ni siquiera es igual a NaN

NaN === NaN; // es igual a false
NaN == NaN; // es igual a false

Para mitigar este extraño comportamiento, se recomienda usar el método Number.isNaN(), también existe una función global llamada isNaN(), pero debido a la funcionalidad implícita de Javascript en querer convertir entre tipos automáticamente, nos puede dar resultados muy extraños. Así que como recomendación siempre usa Number.isNaN(). Esta última función revisa si el tipo es Number e igual a NaN, cualquier otro valor nos regresara false.

// Usando funcion global
isNaN('jaime'); // true --> Esto esta mal, deberia seguir regresando false

// lo que realmente hace es una conversion automatica
const result = Number('jaime'); // NaN
isNaN(result); // true

Number.isNaN('343434'); // false
Number.isNaN('jaime'); //false
Number.isNaN(1 + undefined); // true

Podemos ver en el primer ejemplo que la función global regresa true cuando debería ser false, esto es por la conversión automática de tipos que hace javascript.

Función Number(valor)

Puedes convertir un valor a número usando la función Number(valor), solo ten cuidado porque esta función puede regresar 0 para muchos valores, así como también NaN.

Number('5'); // 5
Number(''); // 0
Number(undefined); // NaN
Number(null); // 0
Number('8778jaime'); // NaN
Number(false); // 0
Number(true); // 1;
Number({}); // NaN
Number([]); // 0

Number.parseInt y Number.parseFloat

Por último tenemos las funciones Number.parseInt(valor) y Number.parseFloat(valor), si el valor no es un string, entonces lo convierten a string utilizando el método .toString() antes de convertirlo a Number.

Number.parseInt(''); // NaN
Number.parseInt('78.5jaime'); // 78
Number.parseInt(78.58977); // 78
Number.parseFloat(''); // NaN
Number.parseFloat('78.5jaime'); // 78.5

BigInt

Este tipo de valor es nuevo, agregado al lenguaje en el 2020, se utiliza para representar valores enteros muy grandes, especialmente para cálculos matemáticos con números enteros muy grandes.

Para usar este tipo de dato al número se le agrega la letra n al final. Esta es la forma recomendada.

const enteroMuyGrante = 999999999999999999999999999999999999999999999999999n;
const otroEnteroMuyGrande = 55555555555555555555555555555555555555555555555n;
const result = enteroMuyGrande * otroEnteroMuyGrande; //

Se pueden realizar operaciones entre el tipo Number y el BigInt, pero debido a que la conversión entre tipos puede provocar perdida de precisión, se recomienda solo usar valores BigInt cuando se estén ocupando valores mayores a 253 y solo realizar operaciones con valores del mismo tipo BigInt.

Otra mala noticia sobre BigInt es que no puedes utilizar sobre un valor BigInt los métodos del objeto Math, por ejemplo no puedes utilizat Math.sqrt().

Math.sqrt(99); // 9.9498743710662
Math.sqrt(99999999999999999n); // Uncaught TypeError: Cannot convert a BigInt value to a number

Como recomendación final, no te compliques la vida, no uses BigInt a menos que el problema que quieras solucionar necesite forzosamente números enteros muy grandes.

Función BigInt(valor)

Puedes convertir y crear un BigInt con la función BigInt(valor)

BigInt('9999999999'); // 9999999999n

Boolean

Boolean es un tipo de dato lógico, el cual es representado únicamente por dos valores true y false. Muy útil para tomar decisiones en nuestros programas. Nombrado así por el matemático George Boole.

Se pueden definir valores tipo Booleanos de la siguiente manera. Esta es la forma recomendada

const tieneHijos = true;
const esEstudiante = false;

Se usan mucho en operaciones lógicas, por ejemplo:

if (tieneHijos) {
  // Si "si" tiene hijos, preguntar cuantos hijos tiene	
} else {
  // si "no"
}

if (esEstudiante) {
  // Si "si" es estuduante, aplicar descuento
} else {
  // si "no"
}

Función Boolean(valor)

Los Booleanos también tiene una función Boolean(valor), es adecuado para crear booleanos y convertir explicitamente un valor a Booleano. Más detalles abajo en la sección Valores que pueden generar Booleanos.

null

Este tipo de dato en Javascript es muy fácil, es un dato que significa que no es nada en comparación con los demás tipos de datos. En otros lenguajes null hace referencia a un apuntador o una referencia de memoria que está vacía, pero en javascript es diferente, simplemente es un valor que significa «nada».

const nada = null;

undefined

Este tipo de dato representa algo que aún no está definido. Es el valor automático de las variables, parámetros y propiedades de objetos que no se les ha definido un valor.

let nombre; // undefined
let edad; // undefined
nombre === undefined; // true
edad === undefined; // true

Symbol

Este tipo de dato se utiliza principalmente para garantizar que su valor sea único e inmutable. Se puede usar como propiedades de objetos para reducir accesos a esas propiedades y con eso evitar modificaciones indeseadas. Cuando veamos más a detalle a los objetos veremos en la práctica su uso.

Para crear este tipo de dato primitivo se usa la función Symbol(). Es la única forma de crear un symbol y es la recomendada.

const nombre = Symbol('Jaime');
const nombreIgual = Symbol('Jaime');
nombre === nombreIgual; // false

String o cadena de caracteres

Los string nos permiten guardar información en forma de texto. Son una secuencia de uno o más caracteres de 16 bits, UTF-16. No existe el tipo `char` como en otros lenguajes, si deseas un carácter, pues simplemente creas un string con un solo carácter. ¿Sencillo no?

Tenemos tres formas para crear un string.

En realidad se puede también crear strings con la función String(valor) o en forma de constructor new String(valor), pero no nos compliquemos la existencia y ocupes solo las tres de abajo por simplicidad:

  1. Con comillas simples, ''. Recomendada si no necesitas evaluar expresiones dentro.
  2. Comillas dobles, ""
  3. backticks, `` . Recomendada si necesitas evaluar expresiones dentro.
const nombre = 'Jaime';
const apellidoPaterno = "Cervantes";
const apellidoMaterno = `Velasco`;

La tercera forma de definir Strings, nos permite usar expresiones dentro de la cadena de caracteres, por lo que es muy útil. Por ejemplo:

const nombre = 'Jaime';
const apellidoPaterno = "Cervantes";
const apellidoMaterno = `Velasco`;

const nombreCompleto = `${nombre} ${apellidoPaterno} ${apellidoMaterno}`; // Jaime Cervantes Velasco

Los strings son inmutables, cuando modificas o extiendes un string realmente se genera uno nuevo.

Dos cadenas de caracteres pueden ser iguales

Puedes comprobar que dos cadenas de caracteres son iguales con ===.

"jaime" === "jaime"; // true
'jaime' === "jaime"; // true
`jaime` === 'jaime'; // true

Función String(valor)

Para convertir un valor a cadena de caracteres puedes usar la función String(valor).

String(1); // '1'
String(null); // 'null'
String(undefined); // 'undefined'

String.length

La propiedad length de un string se usa para saber la longitud de una cadena de caracteres, verás con el tiempo que esto es útil cuando trabajas con cadenas de caracteres.

const nombre = 'Jaime Cervantes Velasco';
nombre.length // 23

Objetos

Todo lo demás tipos de datos son objetos, una función es un objeto. Un objeto es una colección de pares nombre: valor, parecido a los «arrays asociativos» de PHP. Estos pares de nombre/valor se les llama propiedades, una propiedad es como una variable y puede contener cualquier tipo de valor.

Algunos objetos en Javascript:

  • Object
  • Function
  • Array
  • Date
  • RegExp
  • Error

Objetos envolventes de primitivos

Incluso los tipos de datos primitivos String, Boolean, Number, BigInt y Symbol tiene su correspondiente representación en Objeto, llamados objetos envolventes. De hecho Javascript implícitamente convierte estos datos primitivos a objetos para poder ocupar métodos y propiedades útiles. Por ejemplo la propiedad length de una cadena de caracteres.

¿Cómo pensabas obtener la longitud de una cadena de caracteres? Entendiendo que una cadena de caracteres es un dato primitivo y no tiene métodos

'Jaime Cervantes Velasco'.length; // 23, el pri
// A groso modo, lo que sucede implictamente al querer obtener lenght:
const nuevoString = new String('Jaime Cervantes Velasco');
nuevoString.length; // 23

(5).toFixed(2); // 5.00
// Igual a groso modo pasa algo asi:
const numero = new Number(5);
numero.toFixed(2); // 5.00

false.toString(); // 'false'
// Igual a groso modo pasa algo asi:
const booleano = new Boolean(false);
numero.toString(2); // false

// Estos dos tipos de datos primitivos no permiten el uso de new
(9n).toLocaleString(); // '9'
Symbol('Hi').toString(); // 'Symbol(Hi)'

Aunque los tipos de datos BigInt y Symbol no permiten el uso de new no quiere decir que por detrás no se creen objetos, está claro que javascript los envuelve en un objeto porque nos permite utilizar los métodos del ejemplo, toLocaleString y toString.

¿Cómo crear un objeto?

Para crear un objeto no necesitas crear una clase, tú simplemente creas el objeto y lo empiezas a utilizar. La forma más fácil de crear un objeto es usando la definición que llaman objeto literal, donde simplemente abres y cierras llaves {}. Esta es la forma recomendada.

const vacio = {};
const persona = {
  nombre: 'Jaime',
  apellidos: 'Cervantes Velasco',
  edad: 33,
  getNombre: function () {
    return `${this.nombre} ${this.apellidos}`;
  },
  hablar: function () {
    return `Hola, soy ${this.getNombre()}, tengo ${this.edad} años.`;
  }
};

const frase = persona.hablar();
console.log(frase); // 'Hola soy Jaime Cervantes Velasco, tengo 33 años.'

En el ejemplo tenemos un objeto persona, tiene varias propiedades de diferentes tipos, su nombre es un String, su edad es un NumbergetNombre y hablar son de tipo Function, las funciones que son miembros de un objeto se les llama métodos.

Más abajo hay ejemplos de objetos.

Accediendo a propiedades con corchetes, ['key']

En el ejemplo anterior vimos como acceder al método hablar(), este método sigue siendo una propiedad y es de tipo function. Pero existe otra notación para acceder a las propiedades, usando corchetes. Muy parecido a como se acceden a los elementos de un array.

persona.edad; // regresa 33
persona['edad']; // regresa 33

// obtenemos la funciónn con corcheteds y la invocamos con parentesis
const frase = persona['hablar']();
console.log(frase); // 'Hola soy Jaime Cervantes Velasco, tengo 29 años.'

¿Cómo crear un objeto function?

function imprimirPersona(persona) {
  console.log(persona.nombre);
  console.log(persona.edad);
}

Como la función imprimirPersona es un objeto, podemos agregarle propiedades sin ningún problema.

imprimirPersona.miPropiedad = 'Mi propiedad de una funcion';
console.log(imprimierPersona.miPropiedad); // 'Mi propiedad de una funcion

¿Cómo crear un objeto Array?

La forma recomendada es simplemente crearlos usando corchetes [].

const vacio = [];
const numeros = [1, 2, 3, 4, 5, 6];
const animales = ['perro', 'gato', 'caballo'];
const conObjetos = [
  		{
          nombre: 'Jaime',
          edad: 33
        },
        function imprimierPersona(persona) { 
          console.log(persona);
        }
	];

Como un array es un objeto, igual que una función, también le puedes agregar propiedades.

const numeros = [1, 2, 3, 4, 5, 6];
numeros.miPropiedad = 'Mi propiedad de un arreglo';

console.log(numeros.miPropiedad); // 'Mi propiedad de un arreglo

¿Cómo crear un objeto Date?

const fecha = new Date();

console.log(fecha); // Fri May 28 2021 10:46:27 GMT-0500 (hora de verano central)

Como te podrás imaginar, al objeto date también le podemos agregar propiedades porque es un objeto

De momento espero que esto te dé una idea de la importancia de los objetos en Javascript y por eso se merece una explicación más extensa en una futura publicación.

Valores que pueden generar un boleano false

Existen valores verdaderos y valores falsos, que aplicados en una condición se comportan como Booleanos. Esto es debido a que Javascript hace una conversión de tipos implícita, que nos puede sorprender si no tenemos cuidado, por eso aquí te dejo la lista de valores que pueden regresar un Boleano false.

  • false
  • null
  • undefined
  • 0
  • 0n
  • NaN
  • », «», « (cadena de caracteres vacía)

Todos los demás valores, incluyendo los objetos, en una condición o pasados a la función Boolean(valor) regresan el Boleano true. Un string con un espacio " ", regresa true, y un string con valor "false" también regresa true.

Función Boolean(valor)

Existe una función global para crear valores Booleanos, Boolean(valor). Donde si valor es uno verdadero, regresa true, de lo contrario, regresa false.

Boolean(false); // false
Boolean(null); // false
Boolean(undefined); // false
Boolean(0); // false
Boolean(0n); // false
Boolean(NaN); // false
Boolean(''); // false

// Todos los demas valores van a regresar true
Boolean(' '); // true
Boolean('false'); // true
Boolean('jaime cervantes'); // true
Boolean({}); // true
Boolean([]); // true;

En condiciones lógicas

Las condiciones lógicas en javascript verifican sin una expresión regresa true o false, ejecutan un conversión implicita.

if (false) {	
} else {
  console.log(false);
}

if (null) {
} else {
  console.log(false);
}

if (undefined) {
} else {
  console.log(false);
}

if (0) {
} else {
  console.log(false);
}

if (0n) {
} else {
  console.log(false);
}

if (NaN) {
} else {
  console.log(false);
}

if ('') {
} else {
  console.log(false);
}

if (' ') {
  console.log(true);
}

if ('false') {
  console.log(true);
}

if ({}) {
  console.log(true);
}

if ([]) {
  console.log(true);
}

Verificar tipos con el operador typeof

Este operador nos permite identificar el tipo de dato con el que estamos trabajando, útil en caso de que no sepamos de donde viene la información, o queremos tratar los datos con base en su tipo. El operador regresa el nombre en minúsculas del tipo de dato.

typeof 5; // 'number
typeof NaN; // 'number'
typeof 99999999999999999999999999999999999999999999999n; // 'bigint'
typeof true; // 'boolean'
typeof null; // 'object', esto es un error que existe desde la primera versión de JS
typeof undefined; // 'undefined'
typeof Symbol('simbolo'); // 'symbol'
typeof 'cadena de caracteres'; // 'string'
typeof {}; // 'object'
typeof []; // 'object
typeof new Date(); // 'object'
typeof new String('Jaime'); // Object

Existe un error conocido en javascript, cuando se ejecuta el operador typeof sobre null, este regresa ‘object'. Este error existe desde la primera versión de Javascript, cosas malas pasan cuando escribes código a las carreras.

Si todos los demás valores que no son primitivos son objetos, cuando usamos el operador typeof sobre ellos, ¿Qué tipo de datos nos indicara que son?, exacto, 'object'. Tal como en el ejemplo de arriba cuando pasamos un objeto literal, un array vacío, un objeto tipo fecha y cuando ocupamos los objetos envolventes de primitivos como new String('Jaime').

Existe una excepción en typeof, cuando el valor es una función, regresa 'function'.

typeof function() {}; // 'function'

Verificar tipos con el método Object.prototype.toString

Como vimos, es algo confuso utilizar el operador typeof, yo prefiero utilizar un método más seguro, es algo raro, pero como todo en Javascript es un objeto y el objeto base del cual parten todos los tipos de datos en javascript es Object, con excepción de los datos primitivos ( Aunque estos a su vez tienen su versión objeto), entonces podemos hacer algo como lo siguiente.

Object.prototype.toString.call(5); // '[object Number]'
Object.prototype.toString.call(NaN); // '[object Number]'
Object.prototype.toString.call(99999999999999999999999999999999999999999999999n); // '[object BigInt]'
Object.prototype.toString.call(true); // '[object Boolean]'
Object.prototype.toString.call(null); // '[object Null]', 
Object.prototype.toString.call(undefined); // '[object Undefined]'
Object.prototype.toString.call(Symbol('simbolo')); // '[object Symbol]'
Object.prototype.toString.call('cadena de caracteres'); // '[object String]'
Object.prototype.toString.call({}); // '[object Object]'
Object.prototype.toString.call([]); // '[object Array]'
Object.prototype.toString.call(new Date()); // '[object Date]'
Object.prototype.toString.call(new String('Jaime')); // '[object String]'

A partir de este método, se puede crear una función que verifique el tipo de dato.

function getTypeOf(valor) {
  const textoLargoTipo = Object.prototype.toString.call(valor); // '[object Number]', '[object Boolean]', etc
  const tipoCorto = textoLargoTipo.slice(8, -1); // Number, Boolean, Null, Undefined, Symbol, String, etc.
  const tipoEnMinusculas = tipoCorto.toLowerCase(); // number, boolean, null, undefined, symbol, string, etc.
  
  return enMinusculas; 
}

getType(1); // 'number'
getType(new Date()); // date
getType([]); // array

La propiedad especial prototype es muy importante en Javascript, si has leído más de una de mis publicaciones, te habrás percatado que he mencionado que Javascript es un lenguaje de programación multiparadigma y orientado a objetos basado en prototipos, en otra publicación explicaremos a detalle la funcionalidad de prototipos en Javascript.

Conclusión

Todo en Javascript es un objeto, con la excepción de los tipos primitivos, pero como ya vimos, al querer usarlos como objetos invocando algún método, es ahí donde javascript los envuelve (en la practica todo es un objeto).

Cualquier lenguaje de programación tiene buenas cosas que podemos aprovechar, pero también puede tener cosas extrañas y confusas (comparando con la mayoria de los lenguajes de programación modernos), como el operador typeof y el método Object.prototype.toString, el valor NaN, los valores que pueden generar un boleano false, y muchas otras cosas que no vimos por razones de simplicidad. Queda del lado del programador tomar las mejores partes y eliminar o mitigar las que no nos ayudan a comunicarnos mejor con nuestro equipo de trabajo, siempre con un enfoque a simplificar las cosas.

En el caso de la creación de algún tipo de dato en Javascript, la manera más simple y fácil de usar, es simplemente creando los valores que vamos a utilizar. Tal como se recomendó en secciones anteriores, a esta forma de crearlos se les llama literales.

const numero = 9999.54;
const enteroGrande = 99999999999999n;
const boleano = true;
const nulo = null;
let noDefinido;
const simbolo = Symbol('identificador');
const cadenaDeCaracteres = 'Jaime Cervantes Velasco';
const cadenaDeCaractresConExpresion = `El numero ${numero} es "Number"`;
const objeto = {};
const arreglo = [];

Existen aún más detalles que cubrir sobre los tipos de datos en javascript, pero sobre la marcha iremos viendo más temas necesarios, de momento me parece que esta información es suficiente.

Deja un comentario

A %d blogueros les gusta esto: