Tipos primitivos en JavaScript
Introducción
En el núcleo de cualquier lenguaje de programación se encuentra su sistema de tipos de datos, que define qué clase de valores pueden almacenarse y manipularse. JavaScript, a pesar de ser un lenguaje de tipado dinámico donde no necesitamos declarar explícitamente el tipo de nuestras variables, tiene un conjunto bien definido de tipos de datos. Los más fundamentales son los llamados "tipos primitivos", que representan los bloques básicos con los que construimos estructuras de datos más complejas.
Comprender estos tipos primitivos, sus características y comportamientos, es esencial para escribir código JavaScript efectivo y libre de errores inesperados. En este artículo, exploraremos en detalle cada uno de los tipos primitivos de JavaScript, cómo funcionan, y las peculiaridades que los hacen únicos.
¿Qué son los tipos primitivos?
En JavaScript, un valor primitivo es un dato que no es un objeto y no tiene métodos propios. Es inmutable, lo que significa que no puede ser alterado. Si asignas un nuevo valor a una variable que contiene un primitivo, se crea un nuevo valor en memoria en lugar de modificar el existente.
JavaScript tiene actualmente seis tipos de datos primitivos principales:
- Number - Representa tanto números enteros como decimales
- String - Secuencias de caracteres (texto)
- Boolean - Valores lógicos (true o false)
- Undefined - Variable declarada pero sin valor asignado
- Null - Representa la ausencia intencional de valor
- Symbol - Identificador único e inmutable (introducido en ES6)
- BigInt - Números enteros de precisión arbitraria (introducido en ES2020)
Los primitivos se distinguen de los objetos (como arrays, funciones, o instancias de clases) en varios aspectos importantes:
- Son inmutables (no pueden cambiar)
- Se comparan por valor, no por referencia
- No tienen métodos propios (aunque JavaScript les da "métodos" a través de objetos envoltorios)
Number (enteros y decimales)
El tipo Number en JavaScript es usado para representar tanto números enteros como números de punto flotante (decimales). Internamente, JavaScript implementa todos los números siguiendo el estándar de punto flotante IEEE 754 de 64 bits (también conocido como "double precision").
Representación de números
// Enteros
let edad = 25;
let anio = 2023;
let temperatura = -5;
// Decimales (punto flotante)
let pi = 3.14159;
let descuento = 0.25;
let masa = 1.5e3; // Notación científica: 1.5 * 10^3 = 1500
Límites y precisión
Debido a la representación de punto flotante, JavaScript tiene ciertos límites en cuanto a los números que puede manejar:
// Valores mínimo y máximo representables
const MIN_VALOR = Number.MIN_VALUE; // Aproximadamente 5e-324
const MAX_VALOR = Number.MAX_VALUE; // Aproximadamente a 1.8e+308
// Números enteros seguros (representables exactamente)
const MIN_ENTERO_SEGURO = Number.MIN_SAFE_INTEGER; // -9007199254740991
const MAX_ENTERO_SEGURO = Number.MAX_SAFE_INTEGER; // 9007199254740991
console.log(Number.isSafeInteger(9007199254740991)); // true
console.log(Number.isSafeInteger(9007199254740992)); // false - este número no es seguro
Problemas de precisión
La representación de punto flotante puede causar imprecisiones en cálculos decimales:
console.log(0.1 + 0.2); // 0.30000000000000004, no exactamente 0.3
console.log(0.1 + 0.2 === 0.3); // false
// Para comparaciones con decimales, es mejor usar una pequeña tolerancia
function sonCasiIguales(a, b, tolerancia = 0.0001) {
return Math.abs(a - b) < tolerancia;
}
console.log(sonCasiIguales(0.1 + 0.2, 0.3)); // true
Valores especiales: NaN, Infinity
JavaScript tiene algunos valores numéricos especiales:
// NaN (Not a Number) - resultado de operaciones numéricas inválidas
let resultado = "texto" / 2; // NaN
let raizNegativa = Math.sqrt(-1); // NaN
console.log(Number.isNaN(resultado)); // true
console.log(Number.isNaN("texto")); // false - String no es NaN
console.log(isNaN("texto")); // true - la función global isNaN convierte a Number primero
// Una propiedad peculiar de NaN
console.log(NaN === NaN); // false - NaN no es igual a nada, ni siquiera a sí mismo
// Infinity - representación de infinito
let infinito = Infinity;
let divisionPorCero = 1 / 0; // Infinity
let negInfinito = -Infinity;
console.log(1000000000 < Infinity); // true
console.log(-Infinity < -1000000000); // true
String (cadenas de texto)
El tipo String representa secuencias de caracteres, es decir, texto. JavaScript nos ofrece tres formas de definir strings:
Formas de definir strings
// Con comillas simples
let nombre = 'Ana';
let cita = 'Ella dijo: "Hola mundo"';
// Con comillas dobles
let apellido = "Garcia";
let dialogo = "La palabra 'JavaScript' tiene 10 letras";
// Con backticks (comillas invertidas) - Template literals (ES6)
let nombreCompleto = `${nombre} ${apellido}`;
let mensaje = `Hola, ${nombre}!`;
Las comillas simples y dobles funcionan de manera similar, pero los backticks (introducidos en ES6) ofrecen características adicionales:
- Interpolación de variables y expresiones con
${...}
- Soporte para strings multilínea sin caracteres de escape
// String multilínea con backticks
let poema = `Las rosas son rojas,
Las violetas son azules,
JavaScript es divertido,
Y ahora tú también lo sabes!`;
// Interpolación de expresiones
let a = 5;
let b = 10;
let resultado = `La suma de ${a} y ${b} es ${a + b}`;
console.log(resultado); // "La suma de 5 y 10 es 15"
Caracteres de escape
Para incluir caracteres especiales en strings, usamos secuencias de escape:
// Secuencias de escape comunes
let saltoLinea = "Primera linea\nSegunda linea"; // \n - salto de línea
let tabulacion = "Columna1\tColumna2"; // \t - tabulación
let comillasEnString = "El dijo: \"Hola\""; // \" - comillas dobles
let apostrofeEnString = 'Está lloviendo'; // \' - comilla simple
let barraInvertida = "C:\\Usuarios\\Documentos"; // \\ - barra invertida
Propiedades y métodos de strings
Los strings en JavaScript tienen muchas propiedades y métodos útiles:
let texto = "JavaScript es increible";
// Propiedades
console.log(texto.length); // 24 - longitud del string
// Acceso a caracteres individuales
console.log(texto[0]); // "J" - primer carácter (índice 0)
console.log(texto.charAt(4)); // "S" - método alternativo
console.log(texto[texto.length - 1]); // "e" - último carácter
// Búsqueda
console.log(texto.indexOf("Script")); // 4 - posición donde comienza "Script"
console.log(texto.includes("increible")); // true - verifica si contiene el substring
console.log(texto.startsWith("Java")); // true - verifica si comienza con un substring
console.log(texto.endsWith("!")); // false - verifica si termina con un substring
// Transformación
console.log(texto.toLowerCase()); // "javascript es increible"
console.log(texto.toUpperCase()); // "JAVASCRIPT ES INCREIBLE"
console.log(texto.replace("increible", "asombroso")); // "JavaScript es asombroso"
console.log(" texto con espacios ".trim()); // "texto con espacios" - elimina espacios
// Extracción
console.log(texto.substring(0, 10)); // "JavaScript" - desde índice 0 hasta 10 (no incluido)
console.log(texto.slice(11)); // "es increible" - desde índice 11 hasta el final
Boolean (verdadero/falso)
El tipo Boolean es uno de los más simples pero fundamentales en JavaScript. Solo tiene dos posibles valores: true
y false
, que representan los conceptos lógicos de verdadero y falso.
Valores booleanos literales
let verdadero = true;
let falso = false;
console.log(typeof verdadero); // "boolean"
Operaciones lógicas
Los booleanos son esenciales para operaciones lógicas y control de flujo:
// Operadores lógicos
let and = true && false; // false - AND lógico (ambos deben ser true)
let or = true || false; // true - OR lógico (al menos uno debe ser true)
let not = !true; // false - NOT lógico (invierte el valor)
// Operadores de comparación producen booleanos
let mayor = 5 > 3; // true
let igual = 5 === "5"; // false (comparación estricta, diferente tipo)
let igualValor = 5 == "5"; // true (comparación no estricta, convierte tipos)
// Control de flujo
if (verdadero) {
console.log("Esta condición siempre se ejecuta");
}
if (falso) {
console.log("Esta condición nunca se ejecuta");
} else {
console.log("Esta parte sí se ejecuta");
}
Valores truthy y falsy
En JavaScript, cuando un valor no booleano se usa en un contexto que requiere un booleano (como una condición if
), se convierte automáticamente siguiendo reglas de "truthy" y "falsy":
Valores falsy (se evalúan como false):
false
0
(cero numérico)""
(string vacío)null
undefined
NaN
Valores truthy (se evalúan como true):
- Todo lo demás, incluyendo:
- Cualquier número que no sea 0 o NaN
- Cualquier string no vacío
- Objetos (incluidos arrays)
- Funciones
// Ejemplos de valores falsy
if (0) console.log("Nunca se muestra");
if ("") console.log("Nunca se muestra");
if (null) console.log("Nunca se muestra");
// Ejemplos de valores truthy
if (1) console.log("Se muestra"); // Se muestra
if ("texto") console.log("Se muestra"); // Se muestra
if ([]) console.log("Se muestra"); // Se muestra (¡array vacío es truthy!)
if ({}) console.log("Se muestra"); // Se muestra (¡objeto vacío es truthy!)
BigInt (números enteros grandes)
El tipo BigInt fue introducido en ES2020 y permite representar números enteros de precisión arbitraria, superando las limitaciones del tipo Number.
Creación de BigInts
// Sintaxis con 'n' al final
const numeroGrande = 9007199254740991n; // MAX_SAFE_INTEGER como BigInt
const otroGrande = 123456789123456789n;
// Usando el constructor BigInt()
const desdeNumero = BigInt(9007199254740991);
const desdeString = BigInt("9007199254740991");
console.log(typeof numeroGrande); // "bigint"
Operaciones con BigInt
// Operaciones aritméticas
const suma = 1234567890123456789n + 9876543210987654321n;
console.log(suma); // 11111111101111111110n
// Multiplicación de números grandes
const multiplicacion = 12345678901234567890n * 9876543210n;
console.log(multiplicacion); // 121932631137021795109830603690n
// División
const division = 10n / 3n;
console.log(division); // 3n (trunca hacia cero, no hay decimales)
// Operaciones mixtas con Number no están permitidas
try {
const mezcla = 10n + 10; // Error: Cannot mix BigInt and other types
} catch (e) {
console.log(e.message);
}
// Conversión explícita
const mezclaOk = 10n + BigInt(10);
console.log(mezclaOk); // 20n
Symbol (valores únicos)
El tipo Symbol fue introducido en ES6 (ECMAScript 2015) y representa un valor único e inmutable que puede ser utilizado como identificador para propiedades de objetos.
Creación de Symbols
// Creación básica de un Symbol
const miSimbolo = Symbol();
const otroSimbolo = Symbol();
console.log(miSimbolo === otroSimbolo); // false - cada Symbol es único
// Symbol con descripción (para depuración)
const simboloNombre = Symbol("nombre");
const simboloOtroNombre = Symbol("nombre");
console.log(simboloNombre === simboloOtroNombre); // false - aun con la misma descripción
Uso como propiedades de objetos
Una de las principales utilidades de los Symbols es como claves de propiedades de objetos:
const ID = Symbol("id");
const usuario = {
nombre: "Ana",
[ID]: 12345 // Usamos el Symbol como clave de propiedad
};
console.log(usuario.nombre); // "Ana"
console.log(usuario[ID]); // 12345
// Las propiedades Symbol no aparecen en iteraciones normales
console.log(Object.keys(usuario)); // ["nombre"]
for (let key in usuario) {
console.log(key); // Solo muestra "nombre"
}
Comprobación de tipos con typeof
El operador typeof
nos permite verificar el tipo de un valor:
console.log(typeof 42); // "number"
console.log(typeof "texto"); // "string"
console.log(typeof true); // "boolean"
console.log(typeof undefined); // "undefined"
console.log(typeof null); // "object" (esto es un bug histórico en JavaScript)
console.log(typeof {}); // "object"
console.log(typeof []); // "object" (los arrays son objetos en JavaScript)
console.log(typeof function(){}); // "function"
console.log(typeof Symbol()); // "symbol"
console.log(typeof 42n); // "bigint"
Limitaciones de typeof
:
- Devuelve "object" para
null
(un error histórico que se mantiene por compatibilidad) - No distingue entre diferentes tipos de objetos (arrays, objetos regulares, dates, etc.)
Para verificaciones más precisas, podemos usar:
// Verificar si es array
console.log(Array.isArray([1, 2, 3])); // true
console.log(Array.isArray({a: 1})); // false
// Verificar si es NaN
console.log(Number.isNaN(NaN)); // true
console.log(Number.isNaN("texto")); // false
Casos de uso para cada tipo
Number
- Cálculos matemáticos
- Contadores e índices
- Dimensiones y coordenadas
- Valores monetarios (con cuidado con la precisión)
String
- Nombres y textos
- Mensajes y comunicación
- Almacenamiento de datos en formato texto
- Manipulación de HTML/CSS/JSON
Boolean
- Condiciones de control
- Estados binarios (encendido/apagado, sí/no)
- Banderas y marcadores
- Validación de datos
BigInt
- Identificadores grandes (IDs de base de datos)
- Cálculos matemáticos de alta precisión
- Operaciones criptográficas
- Timestamps de alta precisión
Symbol
- Claves de propiedades privadas
- Prevención de colisiones de nombres
- Metaprogramación
- Extensión de objetos estándar
Resumen
En este artículo, hemos explorado en profundidad los tipos primitivos de JavaScript:
- Number: Representa tanto números enteros como decimales, con valores especiales como NaN e Infinity.
- String: Representa texto, con tres formas de definirlos (comillas simples, dobles y backticks) y numerosos métodos para manipularlos.
- Boolean: Representa valores lógicos true y false, con reglas específicas para conversiones a través de los conceptos de "truthy" y "falsy".
- Symbol: Introducido en ES6, proporciona identificadores únicos e inmutables, útiles para propiedades de objetos.
- BigInt: Introducido en ES2020, permite representar números enteros de precisión arbitraria para casos donde los límites de Number no son suficientes.
Comprender estos tipos primitivos es fundamental para dominar JavaScript y escribir código de calidad. Cada tipo tiene sus propias características y casos de uso específicos que los hacen adecuados para diferentes situaciones.
En próximos artículos, exploraremos en detalle los tipos undefined
y null
, que aunque son técnicamente considerados primitivos, merecen una atención especial por su comportamiento particular.