Cheatsheet JavaScript
Linguagem de programação para web e aplicações
JavaScript
Básico
Declaración de Variables
let nombre = "Ana"; // mutable, alcance de bloque
const PI = 3.14159; // constante, no reasignable
var antiguo = "evitar"; // alcance de función (legacy)
// let puede ser reasignado
nombre = "Maria";
// const no puede ser reasignado
// PI = 3; // TypeError!
// const con objetos: la referencia es fija
const user = { nombre: "John" };
user.nombre = "Pedro"; // OK - muta el contenido
// user = {}; // TypeError!Usa const por defecto y let cuando necesites reasignar. Evita var — tiene alcance de función y causa bugs con hoisting. const no impide la mutación de objetos, solo la reasignación de la referencia.
Truthy y Falsy
// Valores FALSY (6 en total):
false, 0, "", null, undefined, NaN
// Todo lo demás es TRUTHY:
true, 1, -1, "texto", [], {}, "0", "false"
// Ejemplos prácticos
if ("") { } // no se ejecuta (falsy)
if ("texto") { } // se ejecuta (truthy)
if ([]) { } // ¡se ejecuta! (array vacío es truthy)
if ({}) { } // ¡se ejecuta! (objeto vacío es truthy)
// Comprobar booleano real
Boolean(""); // false
Boolean("oi"); // true
!!0; // false (doble negación)
!!"texto"; // true
// Uso común: valor por defecto
let nombre = input || "Anónimo";JavaScript tiene 6 valores falsy: false, 0, "", null, undefined y NaN. Todo lo demás es truthy, incluyendo arrays y objetos vacíos. Usa !! para convertir a booleano real.
Destructuring
// Arrays
const [a, b, c] = [1, 2, 3];
const [x, , z] = [1, 2, 3]; // salta el índice 1
const [first, ...rest] = [1, 2, 3, 4];
// Valores por defecto
const [nombre = "Anónimo"] = [];
// Objetos
const { name, age } = { name: "Ana", age: 30 };
const { name: n, age: a2 } = { name: "Ana", age: 30 };
// renombra: n = "Ana", a2 = 30
// Anidado
const { addr: { city } } = { addr: { city: "Porto" } };
// En parámetros de función
function mostrar({ nombre, edad }) {
console.log(`${nombre}, ${edad}`);
}
mostrar({ nombre: "Ana", edad: 30 });
// Intercambiar variables
let x = 1, y = 2;
[x, y] = [y, x]; // x=2, y=1El destructuring extrae valores de arrays o propiedades de objetos en variables. Soporta valores por defecto, renombrado ({ name: n }), anidación y rest. Muy usado en parámetros de funciones.
typeof e instanceof
// typeof: devuelve una string con el tipo
typeof "texto"; // "string"
typeof 42; // "number"
typeof true; // "boolean"
typeof undefined; // "undefined"
typeof {}; // "object"
typeof []; // "object" (¡no distingue!)
typeof null; // "object" (¡bug!)
typeof function(){};// "function"
typeof Symbol(); // "symbol"
// instanceof: comprueba el prototipo
[] instanceof Array; // true
[] instanceof Object; // true
new Date() instanceof Date; // true
// Comprobar array correctamente
Array.isArray([1,2]); // true
Array.isArray({}); // false
// Comprobar null
const isNull = val === null;
const isNil = val == null; // null O undefinedtypeof devuelve el tipo como string pero falla con arrays y null. Usa Array.isArray() para arrays y === null para null. instanceof comprueba la cadena de prototipos.
Tipos Primitivos
// 7 tipos primitivos
let str = "texto"; // string
let num = 42; // number
let big = 9007199254740991n; // bigint
let bool = true; // boolean
let undef = undefined; // undefined
let nulo = null; // null (objeto)
let sym = Symbol("id"); // symbol
// Comprobar tipo
typeof str; // "string"
typeof num; // "number"
typeof bool; // "boolean"
typeof undef; // "undefined"
typeof nulo; // "object" (¡bug histórico!)
typeof sym; // "symbol"
typeof big; // "bigint"JavaScript tiene 7 tipos primitivos. typeof null devuelve "object" — es un bug histórico del lenguaje. Usa === null para comprobar null explícitamente.
Conversión de Tipos
// String → Number
Number("42"); // 42
parseInt("42px"); // 42
parseFloat("3.14"); // 3.14
+"42"; // 42 (unario)
// Number → String
String(42); // "42"
(42).toString(); // "42"
`${42}`; // "42"
// → Boolean
Boolean(1); // true
Boolean(""); // false
!!0; // false
// Coerción implícita (¡evitar!)
"5" + 3; // "53" (¡concatena!)
"5" - 3; // 2 (¡convierte!)
true + 1; // 2
[] + {}; // "[object Object]"
// Comprobar NaN
isNaN(NaN); // true
Number.isNaN(NaN); // true (más seguro)Usa Number(), parseInt() o el unario + para convertir strings en numeros. Cuidado con la coerción implícita: "5" + 3 concatena ("53") pero "5" - 3 convierte (2). Prefiere Number.isNaN() en lugar de isNaN().
console.log Esencial
// Básico
console.log("Valor:", 42);
console.warn("Aviso!");
console.error("Error!");
console.info("Info");
// Formato CSS
console.log("%c Texto azul", "color: blue");
// Tabla (arrays/objetos)
console.table([{ a: 1, b: 2 }, { a: 3, b: 4 }]);
// Objeto con profundidad
console.dir(obj, { depth: null });
// Agrupar
console.group("Grupo");
console.log("dentro");
console.groupEnd();
// Assert
console.assert(1 === 2, "1 no es 2!");Además de console.log(), usa console.table() para arrays/objetos, console.dir() para inspeccionar la estructura y console.group() para organizar logs relacionados.
Operadores de Comparación
// == vs === (SIEMPRE usa ===) 5 == "5"; // true (¡coerción de tipo!) 5 === "5"; // false (sin coerción) null == undefined; // true null === undefined; // false // Operadores relacionales 10 > 5; // true 10 <= 10; // true "a" < "b"; // true (orden alfabético) // Operadores lógicos true && false; // false (AND) true || false; // true (OR) !true; // false (NOT) // Nullish coalescing (??) let val = null ?? "default"; // "default" let zero = 0 ?? "default"; // 0 (¡no es null!)
Usa siempre === y !== en lugar de == y != para evitar la coerción implícita. El operador ?? (nullish) solo se activa con null o undefined, a diferencia de || que también se activa con 0 y "".
Optional Chaining (?.)
const user = {
nombre: "Ana",
dirección: {
calle: "Av. Central",
ciudad: "Lisbon"
}
};
// Sin optional chaining (verboso)
const ciudad = user && user.dirección && user.dirección.ciudad;
// Con optional chaining
const cidade2 = user?.dirección?.ciudad; // "Lisbon"
const país = user?.dirección?.país; // undefined (¡sin error!)
// Con métodos
const upper = user?.getName?.(); // undefined si no existe
// Con arrays
const arr = [1, 2, 3];
arr?.[0]; // 1
arr?.[10]; // undefined
// Combinar con nullish
const nombre = user?.nombre ?? "Anónimo";El ?. (optional chaining) accede a propiedades sin error si el objeto es null/undefined. Devuelve undefined en lugar de lanzar TypeError. Combínalo con ?? para valores por defecto.
console — Rendimiento y Conteo
// Medir tiempo
console.time("operación");
// ... código ...
console.timeEnd("operación"); // "operación: 2.5ms"
// Contador
console.count("click"); // "click: 1"
console.count("click"); // "click: 2"
console.countReset("click");
// Trace (pila de llamadas)
function a() { b(); }
function b() { console.trace(); }
a(); // muestra la pila de llamadas
// debugger: pausa en DevTools
function calcular(x) {
debugger; // breakpoint manual
return x * 2;
}console.time()/timeEnd() mide el rendimiento. console.count() cuenta llamadas. console.trace() muestra la pila de llamadas. debugger; crea un breakpoint en el código.
Template Literals
const nombre = "Ana";
const edad = 30;
// Interpolación con comillas invertidas
const msg = `Hola, ${nombre}! Tienes ${edad} anios.`;
// Expresiones dentro
const total = `Total: ${2 + 3 * 4}€`; // "Total: 14€"
// Multi-línea
const html = `
<div>
<h1>${nombre}</h1>
<p>Edad: ${edad}</p>
</div>
`;
// Función dentro
const upper = `Nombre: ${nombre.toUpperCase()}`;
// Ternario dentro
const status = `${edad >= 18 ? "Adulto" : "Menor"}`;Los template literals usan comillas invertidas ` y permiten interpolación con ${expresión}. Soportan multi-línea sin \n y aceptan cualquier expresión JavaScript dentro de ${}.
Spread y Rest (...)
// SPREAD: expandir elementos
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5]; // [1,2,3,4,5]
const obj1 = { a: 1, b: 2 };
const obj2 = { ...obj1, c: 3 }; // {a:1, b:2, c:3}
// Copiar (shallow copy)
const copia = [...arr1];
const objCopia = { ...obj1 };
// REST: recoger argumentos
function suma(...nums) {
return nums.reduce((a, b) => a + b, 0);
}
suma(1, 2, 3, 4); // 10
// Destructuring con rest
const [first, ...resto] = [1, 2, 3, 4];
// first = 1, resto = [2,3,4]
const { a, ...rest } = { a: 1, b: 2, c: 3 };
// a = 1, rest = {b:2, c:3}El operador ... tiene dos usos: spread expande arrays/objetos y rest recoge los argumentos restantes. Crea copias superficiales (shallow) — los objetos anidados comparten referencia.
Operador Ternario
// Sintaxis: condición ? valorSiTrue : valorSiFalse
const edad = 20;
const status = edad >= 18 ? "Adulto" : "Menor";
// Encadenado (evitar más de 2 niveles)
const nota = 15;
const resultado = nota >= 18 ? "Excelente"
: nota >= 14 ? "Bom"
: nota >= 10 ? "Suficiente"
: "Insuficiente";
// En JSX/templates
// {isAdmin ? <AdminPanel /> : <UserPanel />}
// Con nullish para valores por defecto
const nombre = user?.nombre ?? "Anónimo";
// Asignación condicional
let msg;
if (isAdmin) msg = "Bienvenido admin";
else msg = "Bienvenido";
// Equivalente:
const msg2 = isAdmin ? "Bienvenido admin" : "Bienvenido";El operador ternario condición ? a : b es una expresión condicional compacta. Ideal para asignaciones simples. Evita encadenar más de 2 niveles — usa if/else para lógica compleja.
Strings
Búsqueda y Verificación
const str = "Hola Mundo JavaScript";
str.length; // 20
str.indexOf("Mundo"); // 4
str.lastIndexOf("a"); // 17
str.includes("Mundo"); // true
str.startsWith("Hola"); // true
str.endsWith("Script"); // true
str.search(/mundo/i); // 4 (regex)
// match (devuelve coincidencias)
"2024-01-15".match(/\d+/g); // ["2024","01","15"]
// at() - índice negativo
"JavaScript".at(-1); // "t"
"JavaScript".at(-6); // "S"indexOf() devuelve la posición (o -1). includes(), startsWith() y endsWith() devuelven un booleano. at(-1) accede al último carácter sin calcular el índice.
Unicode y Caracteres
// Código Unicode
"A".charCodeAt(0); // 65
String.fromCharCode(65); // "A"
// Code points (emoji)
"😀".codePointAt(0); // 128512
String.fromCodePoint(128512); // "😀"
// Iterar con for...of (respeta Unicode)
for (const char of "Hola 😀") {
console.log(char);
}
// Normalización
"café".normalize("NFC");
// Escape
"linha1\nlinha2"; // nueva línea
"tab\taqui"; // tabulación
"aspas \"aqui\""; // comillas
// String.raw (sin escape)
String.raw`C:\nuevo\carpeta`; // "C:\nuevo\carpeta"
// Comparación locale
"a".localeCompare("b"); // -1
"ã".localeCompare("a"); // 1Usa for...of para iterar strings con emoji (respeta los surrogate pairs). String.raw ignora las secuencias de escape. localeCompare() compara strings con soporte de acentos.
Transformación de Strings
const str = "Hola Mundo JavaScript";
str.toUpperCase(); // "HOLA MUNDO JAVASCRIPT"
str.toLowerCase(); // "hola mundo javascript"
str.slice(4, 9); // "Mundo"
str.substring(4, 9); // "Mundo"
str.replace("Mundo", "JS"); // "Hola JS JavaScript"
str.replaceAll("a", "o"); // "Hola Mundo JovaScript"
str.trim(); // elimina espacios en los extremos
str.repeat(2); // repite la string
str.charAt(0); // "O"
// concat
"Hola".concat(" ", "Mundo"); // "Hola Mundo"Las strings son inmutables — los métodos devuelven nuevas strings. slice() acepta índices negativos, replace() solo sustituye la primera aparición (usa replaceAll() para todas).
localeCompare y Normalización
// Comparación con locale
"a".localeCompare("b"); // -1
"ã".localeCompare("a", "pt"); // 1 (después)
"Agua".localeCompare("banana", "pt"); // -1
// Ordenar con acentos
["acción", "Agua", "banana"].sort(
(a, b) => a.localeCompare(b, "pt")
);
// ["Agua", "acción", "banana"]
// Normalización Unicode
"café".normalize("NFC"); // forma compuesta
"café".normalize("NFD"); // forma descompuesta
// Comprobar igualdad visual
const s1 = "\u00E9"; // es (compuesto)
const s2 = "e\u0301"; // es (descompuesto)
s1 === s2; // ¡false!
s1.normalize() === s2.normalize(); // truelocaleCompare() compara strings respetando el idioma y los acentos. normalize() uniformiza las representaciones Unicode — esencial para comparar strings con acentos de distintas fuentes.
split, padStart y search
// split: string → array
"a,b,c".split(","); // ["a","b","c"]
"hola mundo".split(" "); // ["hola","mundo"]
"abc".split(""); // ["a","b","c"]
// join: array → string
["a","b","c"].join("-"); // "a-b-c"
// padStart / padEnd
"5".padStart(3, "0"); // "005"
"42".padEnd(5, "."); // "42..."
// search (con regex)
"Hola Mundo".search(/mundo/i); // 4
// match (devuelve coincidencias)
"2024-01-15".match(/\d+/g); // ["2024","01","15"]
// at() - índice negativo
"JavaScript".at(-1); // "t"
"JavaScript".at(-6); // "S"
// concat
"Hola".concat(" ", "Mundo"); // "Hola Mundo"split() divide una string en un array por delimitador. padStart()/padEnd() rellenan hasta un tamaño. at(-1) accede al último carácter sin calcular el índice.
Regex Básico
// Creación
const re1 = /patrón/gi;
const re2 = new RegExp("patrón", "gi");
// Flags: g (global), i (case-insensitive), m (multiline)
// Probar
/^\d+$/.test("12345"); // true
/^\d+$/.test("12a45"); // false
// Extraer
const email = "ana@mail.com";
email.match(/(\w+)@(\w+)/);
// [0]="ana@mail", [1]="ana", [2]="mail"
// Sustituir
"2024-01-15".replace(/-/g, "/"); // "2024/01/15"
// Metacaracteres comunes
// \d = dígito, \w = palabra, \s = espacio
// . = cualquiera, ^ = inicio, $ = fin
// + = 1+, * = 0+, ? = 0 o 1
// {n,m} = entre n y m
// Ejemplo: validar un email simple
const emailRe = /^[\w.]+@[\w]+\.[\w]{2,}$/;
emailRe.test("ana@mail.com"); // trueLas regex en JS usan /patrón/flags. .test() comprueba si hay coincidencia, .match() extrae grupos y .replace() sustituye. Metacaracteres: \d (dígito), \w (palabra), ^/$ (inicio/fin).
Números e Math
Redondeo y Potencias
// Redondeo Math.round(4.5); // 5 Math.floor(4.9); // 4 (hacia abajo) Math.ceil(4.1); // 5 (hacia arriba) Math.trunc(4.9); // 4 (corta el decimal) // Mínimo y máximo Math.max(1, 5, 3); // 5 Math.min(1, 5, 3); // 1 Math.max(...[1,5,3]); // 5 (spread) // Potencia y raíz Math.pow(2, 10); // 1024 2 ** 10; // 1024 (operador) Math.sqrt(16); // 4 Math.cbrt(27); // 3 // Valor absoluto Math.abs(-5); // 5
Math.floor() redondea hacia abajo, Math.ceil() hacia arriba, Math.round() al más próximo. Usa ** en lugar de Math.pow() para potencias. Math.max(...arr) con spread para arrays.
Sistemas Numéricos
// Binario (0b)
0b1010; // 10
0b11111111; // 255
// Octal (0o)
0o17; // 15
0o777; // 511
// Hexadecimal (0x)
0xFF; // 255
0x1A; // 26
// Separador visual (ES2021)
1_000_000; // 1000000
0xFF_FF; // 65535
// Convertir bases
(255).toString(16); // "ff"
(255).toString(2); // "11111111"
(10).toString(8); // "12"
// Parsear desde otras bases
parseInt("ff", 16); // 255
parseInt("1010", 2); // 10Prefijos: 0b binario, 0o octal, 0x hexadecimal. toString(base) convierte a string en otra base. parseInt(str, base) parsea desde otra base. Los guiones bajos (1_000) mejoran la legibilidad.
Aleatorio y Constantes
// Aleatorio
Math.random(); // 0 a 0.999...
Math.floor(Math.random() * 10); // 0 a 9
// Entero entre min y max (inclusivo)
function aleatorio(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
aleatorio(1, 6); // 1 a 6 (dado)
// Constantes
Math.PI; // 3.14159...
Math.E; // 2.71828...
Math.SQRT2; // 1.41421...
// Redondear con decimales
function roundDec(num, casas) {
const f = 10 ** casas;
return Math.round(num * f) / f;
}
roundDec(3.14159, 2); // 3.14Math.random() devuelve un float entre 0 y 1. Math.floor(Math.random() * n) genera enteros de 0 a n-1. Math.PI y Math.E son constantes. Para decimales, multiplica, redondea y divide.
Formato de Números
const num = 1234.5678;
// Decimales
num.toFixed(2); // "1234.57" (¡string!)
num.toPrecision(4); // "1235"
// Locale (moneda, separadores)
num.toLocaleString("pt-PT");
// "1 234,568"
// Moneda
num.toLocaleString("pt-PT", {
style: "currency",
currency: "EUR"
}); // "1 234,57 €"
// Porcentaje
(0.75).toLocaleString("pt-PT", {
style: "percent"
}); // "75%"
// Notación científica
(1234567).toExponential(2); // "1.23e+6"
// parseInt / parseFloat
parseInt("42px"); // 42
parseFloat("3.14em"); // 3.14
parseInt("0xFF", 16); // 255 (hex)toFixed() devuelve una string — usa Number() o + para convertir de vuelta. toLocaleString() formatea con los separadores y la moneda del locale. parseInt() acepta la base como 2º argumento.
Number: Métodos Estáticos
// Comprobación
Number.isInteger(42); // true
Number.isInteger(4.5); // false
Number.isFinite(Infinity); // false
Number.isNaN(NaN); // true
Number.isNaN("42"); // false (¡sin coerción!)
// Límites
Number.MAX_SAFE_INTEGER; // 9007199254740991
Number.MIN_SAFE_INTEGER; // -9007199254740991
Number.MAX_VALUE; // 1.7976931348623157e+308
Number.EPSILON; // 2.220446049250313e-16
// Comparación de floats
0.1 + 0.2 === 0.3; // ¡false!
Math.abs(0.1 + 0.2 - 0.3) < Number.EPSILON; // true
// BigInt (numeros enormes)
const big = 9007199254740993n;
big + 1n; // 9007199254740994n
// big + 1; // ¡TypeError! (mezclar tipos)Number.isNaN() no hace coerción (a diferencia del isNaN() global). Los floats tienen imprecisión: 0.1 + 0.2 !== 0.3. Usa BigInt (sufijo n) para enteros por encima de MAX_SAFE_INTEGER.
Arrays
Crear y Acceder
// Creación
const arr = [1, 2, 3];
const arr2 = new Array(5); // [empty x 5]
const arr3 = Array.of(1, 2, 3);
const arr4 = Array.from("abc"); // ["a","b","c"]
const arr5 = Array.from({length: 5}, (_, i) => i);
// [0, 1, 2, 3, 4]
// Acceso
arr[0]; // 1
arr.at(-1); // 3 (¡el último!)
arr.length; // 3
// Comprobar
arr.includes(2); // true
arr.indexOf(2); // 1
arr.findIndex(x => x > 1); // 1
// Rellenar
const filled = new Array(3).fill(0); // [0,0,0]
// Secuencia
[...Array(5).keys()]; // [0,1,2,3,4]Array.from() convierte iterables en arrays. at(-1) accede al último elemento sin arr[arr.length-1]. Array.from({length: n}, fn) crea arrays con valores generados.
find, some y every
const users = [
{ nombre: "Ana", edad: 25 },
{ nombre: "Pedro", edad: 30 },
{ nombre: "Maria", edad: 35 },
];
// find: el primero que satisface
users.find(u => u.edad > 28);
// { nombre: "Pedro", edad: 30 }
// findIndex: índice del primero
users.findIndex(u => u.nombre === "Maria"); // 2
// some: ¿ALGUNO satisface?
users.some(u => u.edad > 30); // true
// every: ¿TODOS satisfacen?
users.every(u => u.edad > 20); // true
users.every(u => u.edad > 30); // false
// includes (valor primitivo)
[1, 2, 3].includes(2); // true
// findLast (ES2023)
users.findLast(u => u.edad > 28);
// { nombre: "Maria", edad: 35 }find() devuelve el primer elemento que satisface la condición. some() comprueba si alguno pasa, every() si todos pasan. findLast() búsqueda del final al inicio.
Añadir y Eliminar
const arr = [1, 2, 3]; // Añadir arr.push(4); // [1,2,3,4] (final) arr.unshift(0); // [0,1,2,3,4] (inicio) // Eliminar arr.pop(); // elimina el último → 4 arr.shift(); // elimina el primero → 0 // splice: eliminar/insertar en posición arr.splice(1, 2); // elimina 2 desde el índice 1 arr.splice(1, 0, 9, 8); // inserta 9,8 en el índice 1 // slice: copiar parte (no muta) arr.slice(1, 3); // [2, 3] arr.slice(-2); // últimos 2 // concat (no muta) [1,2].concat([3,4]); // [1,2,3,4] [...[1,2], ...[3,4]]; // [1,2,3,4] (spread) // flat: aplanar [1, [2, [3]]].flat(); // [1, 2, [3]] [1, [2, [3]]].flat(2); // [1, 2, 3] [1, [2, [3]]].flat(Infinity); // [1, 2, 3]
push/pop operan al final, unshift/shift al inicio. splice() muta el array original; slice() devuelve una copia sin mutar. flat(Infinity) aplana completamente.
Ordenación
// ¡sort() MUTA el array original!
const nums = [10, 5, 40, 25];
// Numérico ascendente
[...nums].sort((a, b) => a - b);
// [5, 10, 25, 40]
// Numérico descendente
[...nums].sort((a, b) => b - a);
// [40, 25, 10, 5]
// Strings (alfabético)
["banana","apple","cherry"].sort();
// ["apple", "banana", "cherry"]
// Objetos por propiedad
const users = [{n:"Ana",i:25},{n:"Pedro",i:20}];
users.sort((a, b) => a.i - b.i);
// toSorted (ES2023 - ¡no muta!)
const sorted = nums.toSorted((a, b) => a - b);
// nums permanece [10, 5, 40, 25]
// Locale compare (acentos)
["acción","banana","Agua"].sort(
(a, b) => a.localeCompare(b, "pt")
);sort() muta el array original — usa [...arr].sort() o toSorted() (ES2023) para no mutar. Sin comparador, ordena como strings ("40" < "5"). Usa localeCompare() para los acentos.
map y filter
const nums = [1, 2, 3, 4, 5];
// map: transforma cada elemento
const doble = nums.map(n => n * 2);
// [2, 4, 6, 8, 10]
// filter: mantiene los que pasan la prueba
const pares = nums.filter(n => n % 2 === 0);
// [2, 4]
// map con índice
["a","b","c"].map((val, i) => `${i}:${val}`);
// ["0:a", "1:b", "2:c"]
// Encadenar
const resultado = nums
.filter(n => n > 2)
.map(n => n * 10);
// [30, 40, 50]map() transforma cada elemento y devuelve un nuevo array. filter() selecciona los que pasan la prueba. Ninguno muta el original. Encadénalos para pipelines de transformación.
forEach, for...of y entries
const arr = ["a", "b", "c"];
// forEach (no devuelve nada)
arr.forEach((val, i) => {
console.log(i, val);
});
// for...of (permite break/continue)
for (const val of arr) {
if (val === "b") break;
console.log(val);
}
// entries (índice + valor)
for (const [i, val] of arr.entries()) {
console.log(i, val);
}
// keys / values
[...arr.keys()]; // [0, 1, 2]
[...arr.values()]; // ["a", "b", "c"]
// for tradicional (rendimiento)
for (let i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
// flatMap (map + flat(1))
[[1,2],[3,4]].flatMap(a => a.map(x => x * 2));
// [2, 4, 6, 8]forEach() no permite break — usa for...of cuando necesites detener la iteración. entries() da índice + valor. flatMap() es map() + flat(1) combinados.
reduce — Acumulador
const nums = [1, 2, 3, 4, 5];
// Suma
const suma = nums.reduce((acc, n) => acc + n, 0);
// 15
// Reduce a objeto (agrupar)
const grouped = [6.1, 4.2, 6.3].reduce((acc, n) => {
const key = Math.floor(n);
acc[key] = (acc[key] || 0) + 1;
return acc;
}, {}); // {4: 1, 6: 2}
// Pipeline completo
const total = nums
.filter(n => n > 2)
.map(n => n * 10)
.reduce((acc, n) => acc + n, 0);
// 120
// flatMap (map + flat(1))
[[1,2],[3,4]].flatMap(a => a.map(x => x * 2));
// [2, 4, 6, 8]reduce() acumula todos los elementos en un único valor. El 2º argumento es el valor inicial del acumulador. Úsalo para sumar, agrupar o transformar en un objeto. flatMap() es map() + flat(1) combinados.
Métodos ES2023 (No Mutables)
const arr = [3, 1, 4, 1, 5]; // toSorted (¡no muta!) const ordenado = arr.toSorted((a, b) => a - b); // [1, 1, 3, 4, 5] | arr sin cambios // toReversed (¡no muta!) const invertido = arr.toReversed(); // [5, 1, 4, 1, 3] // toSpliced (¡no muta!) const sem2 = arr.toSpliced(1, 2); // [3, 1, 5] | elimina 2 desde el índice 1 // with (sustituir por índice) const trocado = arr.with(0, 99); // [99, 1, 4, 1, 5] // findLast / findLastIndex arr.findLast(n => n < 4); // 1 arr.findLastIndex(n => n < 4); // 3
ES2023: toSorted(), toReversed(), toSpliced() y with() devuelven nuevos arrays sin mutar el original. findLast() búsqueda del final al inicio.
Objetos
Crear y Manipular Objetos
// Literal
const obj = { nombre: "Ana", edad: 30 };
// Acceso
obj.nombre; // "Ana"
obj["edad"]; // 30
// Añadir/modificar
obj.email = "ana@mail.com";
obj.edad = 31;
// Eliminar
delete obj.email;
// Comprobar
"nombre" in obj; // true
obj.hasOwnProperty("nombre"); // true
// Claves, valores, entradas
Object.keys(obj); // ["nombre", "edad"]
Object.values(obj); // ["Ana", 31]
Object.entries(obj); // [["nombre","Ana"],["edad",31]]
// Iterar
for (const [key, val] of Object.entries(obj)) {
console.log(key, val);
}Los objetos son colecciones de pares clave-valor. Object.keys(), Object.values() y Object.entries() devuelven arrays iterables. delete elimina una propiedad.
JSON
const obj = { nombre: "Ana", edad: 30, activo: true };
// Objeto → JSON string
const json = JSON.stringify(obj);
// '{"nombre":"Ana","edad":30,"activo":true}'
// Formateado (pretty print)
JSON.stringify(obj, null, 2);
// Con filtro de claves
JSON.stringify(obj, ["nombre", "edad"]);
// JSON string → Objeto
const parsed = JSON.parse(json);
parsed.nombre; // "Ana"
// Con reviver (transformar valores)
JSON.parse(json, (key, val) => {
return key === "edad" ? val + 1 : val;
});
// Cuidado: no soporta
// undefined, funciones, Symbol, Date (se vuelve string)
JSON.stringify(new Date()); // "2024-01-15T..."JSON.stringify() convierte un objeto en una string JSON. JSON.parse() hace lo inverso. No soporta undefined, funciones ni Symbol. Las fechas se vuelven strings ISO.
Object.assign y Freeze
// Merge (shallow)
const a = { x: 1 };
const b = { y: 2 };
const c = Object.assign({}, a, b); // {x:1, y:2}
const d = { ...a, ...b }; // igual (spread)
// Clonar (shallow)
const clone = { ...a };
const clone2 = Object.assign({}, a);
// Congelar (inmutable)
const config = Object.freeze({ port: 3000 });
config.port = 8080; // ignorado (strict: error)
config.port; // 3000
// Comprobar
Object.isFrozen(config); // true
// Seal (no añade/elimina, pero edita)
const sealed = Object.seal({ a: 1 });
sealed.a = 2; // OK
sealed.b = 3; // ignorado
// Comparar objetos
Object.is(NaN, NaN); // true (¡=== da false!)Object.assign() y spread hacen un merge shallow. Object.freeze() lo hace inmutable (no añade, elimina ni edita). Object.is() es como === pero trata NaN correctamente.
Computed Properties y fromEntries
// Computed property names
const campo = "nombre";
const obj = { [campo]: "Ana", [campo + "2"]: "Maria" };
// { nombre: "Ana", nome2: "Maria" }
// Clave dinámica en método
const método = "saudar";
const persona = {
[método]() { return "Hola!"; }
};
persona.saudar(); // "Hola!"
// Object.fromEntries (inverso de entries)
const pares = [["nombre", "Ana"], ["edad", 30]];
Object.fromEntries(pares);
// { nombre: "Ana", edad: 30 }
// Transformar valores de objeto
const precios = { a: 10, b: 20, c: 30 };
const withVat = Object.fromEntries(
Object.entries(precios).map(([k, v]) => [k, v * 1.23])
);
// { a: 12.3, b: 24.6, c: 36.9 }Las computed properties ([expresión]) permiten claves dinámicas. Object.fromEntries() es el inverso de Object.entries() — convierte pares en un objeto. Útil para transformar valores de objetos.
Getters y Setters
const persona = {
_nombre: "Ana",
_edad: 30,
get nombre() {
return this._nombre.toUpperCase();
},
set nombre(val) {
if (val.length < 2) throw new Error("Nombre corto");
this._nombre = val;
},
get info() {
return `${this._nombre}, ${this._edad} anios`;
}
};
persona.nombre; // "ANA" (getter)
persona.nombre = "Maria"; // setter
persona.info; // "Maria, 30 anios"
// Object.defineProperty
Object.defineProperty(persona, "id", {
value: 123,
writable: false,
enumerable: false
});Los getters (get) y setters (set) permiten controlar el acceso a propiedades. Se acceden como propiedades normales (sin paréntesis). Object.defineProperty() define atributos como writable y enumerable.
Map y Set
// Map: objeto con claves de cualquier tipo
const map = new Map();
map.set("nombre", "Ana");
map.set(42, "numero");
map.set({id: 1}, "objeto");
map.get("nombre"); // "Ana"
map.has(42); // true
map.size; // 3
map.delete(42);
// Iterar Map
for (const [key, val] of map) {
console.log(key, val);
}
// Set: colección de valores únicos
const set = new Set([1, 2, 2, 3, 3]);
set.size; // 3 (¡duplicados eliminados!)
set.add(4);
set.has(2); // true
set.delete(1);
// Eliminar duplicados de un array
const unique = [...new Set([1,1,2,3,3])];
// [1, 2, 3]Map acepta claves de cualquier tipo (objetos, numeros) y mantiene el orden de inserción. Set guarda valores únicos. Usa [...new Set(arr)] para eliminar duplicados de un array.
Funções
Arrow Functions
// Tradicional
function suma(a, b) {
return a + b;
}
// Arrow function
const soma2 = (a, b) => a + b;
// Un parámetro (paréntesis opcionales)
const doble = n => n * 2;
// Cuerpo con múltiples líneas
const calcular = (a, b) => {
const resultado = a * b;
return resultado + 1;
};
// Sin parámetros
const hola = () => "Hola!";
// Devolver un objeto (¡paréntesis!)
const crear = (nombre) => ({ nombre, activo: true });
// Diferencia: this
// Arrow NO tiene this propio (lo hereda del scope)
const obj = {
nombre: "Ana",
// INCORRECTO con arrow:
// hablar: () => console.log(this.nombre),
// CORRECTO con función normal:
hablar() { console.log(this.nombre); }
};Las arrow functions (=>) tienen sintaxis compacta y no tienen this propio — lo heredan del scope envolvente. Para devolver un objeto literal, envuélvelo en paréntesis: () => ({...}).
Métodos de Función
function saudar(saludo, nombre) {
return `${saludo}, ${nombre}!`;
}
// call: invoca con this y args separados
saudar.call(null, "Hola", "Ana");
// "Hola, Ana!"
// apply: invoca con this y un array de args
saudar.apply(null, ["Hola", "Ana"]);
// "Hola, Ana!"
// bind: crea una nueva función con this fijo
const olaAna = saudar.bind(null, "Hola", "Ana");
olaAna(); // "Hola, Ana!"
// Uso práctico: fijar this
const btn = document.querySelector("button");
btn.addEventListener("click",
this.handleClick.bind(this)
);
// Partial application
const multiply = (a, b) => a * b;
const double = multiply.bind(null, 2);
double(5); // 10call() y apply() invocan con this definido (difieren en los args: separados vs array). bind() crea una nueva función con this fijo — útil en event handlers.
Parámetros Default y Rest
// Default parameters
function saudar(nombre = "Mundo", puntuación = "!") {
return `Hola, ${nombre}${puntuación}`;
}
saudar(); // "Hola, Mundo!"
saudar("Ana"); // "Hola, Ana!"
saudar("Ana", "."); // "Hola, Ana."
// Rest parameters
function suma(...nums) {
return nums.reduce((a, b) => a + b, 0);
}
suma(1, 2, 3); // 6
// arguments (legacy - evitar)
function legacy() {
console.log(arguments.length);
}
// Named parameters (vía destructuring)
function criarUser({ nombre, edad = 18, activo = true }) {
return { nombre, edad, activo };
}
criarUser({ nombre: "Ana", edad: 25 });Los parámetros default (= valor) se activan cuando el argumento es undefined. Rest (...args) recoge los argumentos en un array. Los named parameters vía destructuring son ideales para funciones con muchas opciones.
Recursión e IIFE
// Recursión: función que se llama a sí misma
function factorial(n) {
if (n <= 1) return 1; // caso base
return n * factorial(n - 1);
}
factorial(5); // 120
// Recursión con acumulador (tail)
function suma(n, acc = 0) {
if (n === 0) return acc;
return suma(n - 1, acc + n);
}
suma(100); // 5050
// IIFE: Immediately Invoked Function Expression
(function() {
const privado = "no accesible fora";
console.log(privado);
})();
// IIFE con arrow (moderno)
(() => {
const config = { debug: true };
// setup inicial...
})();Recursión: una función que se llama a sí misma — siempre necesita un caso base. La IIFE se ejecuta inmediatamente y crea un scope privado. Útil para setup inicial y evitar contaminar el scope global.
Closures
// Closure: función que "recuerda" el scope donde fue creada
function contador() {
let count = 0;
return {
incrementar: () => ++count,
decrementar: () => --count,
valor: () => count
};
}
const c = contador();
c.incrementar(); // 1
c.incrementar(); // 2
c.valor(); // 2
// ¡count no es accesible directamente!
// Uso práctico: función factory
function multiplicador(fator) {
return (n) => n * fator;
}
const doble = multiplicador(2);
const triple = multiplicador(3);
doble(5); // 10
triple(5); // 15
// Closure en loops (clásico)
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// 0, 1, 2 (let crea scope por iteración)Una closure es una función que mantiene acceso al scope donde fue creada, incluso después de que ese scope termine. Permite datos privados y factories. Usa let en loops para crear scope por iteración.
Callbacks y Higher-Order
// Callback: función pasada como argumento
function procesar(arr, callback) {
return arr.map(callback);
}
procesar([1,2,3], n => n * 2); // [2,4,6]
// Higher-order: recibe/devuelve funciones
function aplicar(fn, valor) {
return fn(valor);
}
aplicar(Math.sqrt, 16); // 4
// Composición
const pipe = (...fns) => (x) =>
fns.reduce((acc, fn) => fn(acc), x);
const transformar = pipe(
x => x * 2,
x => x + 1,
x => `Resultado: ${x}`
);
transformar(5); // "Resultado: 11"
// Debounce (callback aplazado)
function debounce(fn, delay) {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), delay);
};
}Una callback es una función pasada como argumento. Las higher-order functions reciben o devuelven funciones. pipe() compone funciones en secuencia. debounce() retrasa la ejecución hasta que dejen de llamarla.
Controlo de Fluxo
if/else y switch
// if/else
if (edad >= 18) {
console.log("Adulto");
} else if (edad >= 12) {
console.log("Adolescente");
} else {
console.log("Niño");
}
// switch
switch (dia) {
case 1:
case 2:
case 3:
case 4:
case 5:
console.log("Útil");
break;
case 6:
case 7:
console.log("Fin de semana");
break;
default:
console.log("Inválido");
}
// switch con expresiones
switch (true) {
case nota >= 18: console.log("Excelente"); break;
case nota >= 14: console.log("Bom"); break;
default: console.log("Suficiente");
}if/else para condiciones simples. switch para múltiples valores — ¡no olvides el break! switch(true) permite comparar expresiones como un if/else encadenado.
for, for...of y for...in
// for tradicional
for (let i = 0; i < 5; i++) {
console.log(i);
}
// for...of (arrays, strings, Map, Set)
for (const item of [1, 2, 3]) {
console.log(item);
}
// for...in (claves de objetos)
for (const key in { a: 1, b: 2 }) {
console.log(key); // "a", "b"
}
// for...of con índice
for (const [i, val] of ["a","b","c"].entries()) {
console.log(i, val);
}for...of itera valores (arrays, strings, Map, Set). for...in itera claves (objetos). Prefiere for...of con .entries() para obtener índice + valor en arrays.
while, break y continue
// while
let n = 0;
while (n < 5) {
n++;
}
// do...while (se ejecuta al menos 1 vez)
do {
n--;
} while (n > 0);
// break y continue
for (let i = 0; i < 10; i++) {
if (i === 3) continue; // salta el 3
if (i === 7) break; // se detiene en el 7
console.log(i); // 0,1,2,4,5,6
}
// Label (loops anidados)
outer: for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (i === 1 && j === 1) break outer;
}
}while repite mientras la condición sea verdadera. do...while se ejecuta al menos una vez. break sale del loop, continue salta a la siguiente iteración. Las labels permiten salir de loops anidados.
Operadores Lógicos Avanzados
// Evaluación de cortocircuito
const nombre = input || "Anónimo"; // OR: 1er truthy
const config = user && user.config; // AND: 1er falsy o último
// Nullish coalescing (??)
const val = 0 ?? "default"; // 0 (¡no es null!)
const val2 = null ?? "default"; // "default"
// Asignación lógica (ES2021)
let a = null;
a ??= "default"; // a = "default"
let b = 0;
b ||= 10; // b = 10 (0 es falsy)
b ??= 20; // b = 10 (0 no es null)
let c = { x: 1 };
c.x &&= 2; // c.x = 2 (x existe y es truthy)
// Encadenamiento de comparaciones
// INCORRECTO: 1 < x < 10
// CORRECTO:
const inRange = x > 1 && x < 10;|| devuelve el primer truthy, && el primer falsy o el último. ?? solo se activa con null/undefined. Operadores de asignación lógica: ??=, ||=, &&=.
Datas e Tempo
Objeto Date
// Crear fechas
const ahora = new Date();
const data = new Date(2024, 0, 15); // 15 Ene 2024
const data2 = new Date("2024-01-15");
const data3 = new Date(2024, 0, 15, 14, 30, 0);
// Getters
ahora.getFullYear(); // 2024
ahora.getMonth(); // 0-11 (¡0 = Enero!)
ahora.getDate(); // 1-31
ahora.getDay(); // 0-6 (0 = Domingo)
ahora.getHours(); // 0-23
ahora.getMinutes(); // 0-59
ahora.getTime(); // ms desde 1970
// Setters
data.setFullYear(2025);
data.setMonth(11); // Diciembre
// Timestamp
Date.now(); // ms actuales
// Comparar
const d1 = new Date("2024-01-01");
const d2 = new Date("2024-12-31");
d1 < d2; // truegetMonth() devuelve 0-11 (¡0 = Enero!). Date.now() da el timestamp en ms. Para comparar fechas, usa los operadores < y > directamente.
Formato de Fechas
const data = new Date(2024, 0, 15, 14, 30);
// toLocaleDateString
data.toLocaleDateString("pt-PT");
// "15/01/2024"
data.toLocaleDateString("pt-PT", {
weekday: "long",
year: "numeric",
month: "long",
day: "numeric"
});
// "lunes, 15 de enero de 2024"
// toLocaleTimeString
data.toLocaleTimeString("pt-PT");
// "14:30:00"
// ISO
data.toISOString();
// "2024-01-15T14:30:00.000Z"
// Diferencia entre fechas (en días)
const d1 = new Date("2024-01-01");
const d2 = new Date("2024-01-15");
const diffDias = (d2 - d1) / (1000 * 60 * 60 * 24);
// 14toLocaleDateString() formatea con un locale. Restar fechas da milisegundos — divide por 1000*60*60*24 para días. toISOString() devuelve el formato ISO 8601.
setTimeout y setInterval
// setTimeout: se ejecuta 1 vez tras un retraso
const timer = setTimeout(() => {
console.log("Pasaron 2s");
}, 2000);
// Cancelar
clearTimeout(timer);
// setInterval: se repite cada X ms
const interval = setInterval(() => {
console.log("Tick");
}, 1000);
// Detener
clearInterval(interval);
// Cuenta regresiva
let seg = 5;
const countdown = setInterval(() => {
console.log(seg--);
if (seg < 0) clearInterval(countdown);
}, 1000);
// requestAnimationFrame (60fps)
function animar() {
// ... dibujar ...
requestAnimationFrame(animar);
}
requestAnimationFrame(animar);setTimeout() se ejecuta una vez tras un retraso. setInterval() se repite. Ambos devuelven un ID para cancelar con clearTimeout()/clearInterval(). Usa requestAnimationFrame() para animaciones.
Intl.DateTimeFormat y Tiempo Relativo
// Intl.DateTimeFormat (reutilizable)
const fmt = new Intl.DateTimeFormat("pt-PT", {
weekday: "long",
day: "numeric",
month: "long",
year: "numeric"
});
fmt.format(new Date());
// "viernes, 15 de marzo de 2024"
// RelativeTimeFormat ("hace 3 días")
const rtf = new Intl.RelativeTimeFormat("pt", {
numeric: "auto"
});
rtf.format(-3, "day"); // "há 3 días"
rtf.format(1, "month"); // "próximo mes"
rtf.format(-2, "hour"); // "hace 2 horas"
// Diferencia relativa
function tiempoRelativo(data) {
const diff = data - Date.now();
const días = Math.round(diff / 86400000);
return rtf.format(días, "day");
}Intl.DateTimeFormat es reutilizable y más rápido que toLocaleDateString() en bucles. Intl.RelativeTimeFormat crea textos como "hace 3 días" o "próximo mes". Soporta todos los locales.
DOM e Eventos
Seleccionar Elementos
// Un elemento
const el = document.getElementById("app");
const el2 = document.querySelector(".card");
const el3 = document.querySelector("#app > p");
// Varios elementos
const todos = document.querySelectorAll(".item");
const porClasse = document.getElementsByClassName("item");
const porTag = document.getElementsByTagName("p");
// querySelectorAll devuelve un NodeList (estático)
// getElementsBy* devuelve un HTMLCollection (vivo)
todos.forEach(el => console.log(el));querySelector() acepta cualquier selector CSS y devuelve el primero. querySelectorAll() devuelve todos como un NodeList (tiene forEach). getElementsBy* devuelve un HTMLCollection (se actualiza en tiempo real).
Delegación de Eventos
// En vez de un listener por botón...
const lista = document.querySelector("#lista");
// Un listener en el padre (delegación)
lista.addEventListener("click", (e) => {
const item = e.target.closest("li");
if (!item) return;
console.log("Clicó en:", item.textContent);
// Ejemplo: eliminar item
if (e.target.matches(".remover")) {
item.remove();
}
});
// Funciona para elementos futuros (dinámicos)
const novoLi = document.createElement("li");
novoLi.innerHTML = 'Nuevo <button class="remover">X</button>';
lista.appendChild(novoLi); // ¡ya tiene listener!Delegación: un listener en el padre captura eventos de los hijos vía bubbling. Usa e.target.closest() para encontrar el elemento deseado. Funciona para elementos añadidos dinámicamente sin registrar nuevos listeners.
MutationObserver
// Observar cambios en el DOM
const observer = new MutationObserver((mutations) => {
mutations.forEach(m => {
if (m.type === "childList") {
console.log("Hijos cambiados:", m.addedNodes.length);
}
if (m.type === "attributes") {
console.log(`Atributo ${m.attributeName} cambió`);
}
});
});
// Configurar qué observar
observer.observe(document.body, {
childList: true, // añadir/eliminar hijos
attributes: true, // cambios de atributos
subtree: true, // incluir descendientes
characterData: true // cambios de texto
});
// Dejar de observar
observer.disconnect();MutationObserver detecta cambios en el DOM (hijos, atributos, texto). Reemplaza al antiguo MutationEvent (más eficiente). Útil para editores WYSIWYG, auto-guardado y detección de cambios.
Crear e Insertar Elementos
// Crear
const div = document.createElement("div");
div.textContent = "Hola";
div.classList.add("card");
div.id = "nuevo";
// Insertar
const pai = document.querySelector("#app");
pai.appendChild(div); // último hijo
pai.prepend(div); // primer hijo
pai.append(div, "texto"); // múltiples
// Insertar relativo a otro elemento
const ref = document.querySelector(".ref");
ref.before(div); // antes de ref
ref.after(div); // después de ref
ref.replaceWith(div); // reemplaza ref
// Eliminar
div.remove();createElement() crea el elemento, appendChild()/prepend() lo insertan en el padre. before()/after() insertan relativo a un elemento. remove() lo elimina del DOM.
innerHTML vs textContent
const div = document.querySelector("#app");
// textContent: solo texto (seguro)
div.textContent = "<b>Negrita</b>";
// Muestra: <b>Negrita</b> (literal)
// innerHTML: parsea HTML (¡cuidado XSS!)
div.innerHTML = "<b>Negrita</b>";
// Muestra: Negrita (formateado)
// insertAdjacentHTML (no limpia los hijos)
div.insertAdjacentHTML("beforeend", "<p>Fin</p>");
div.insertAdjacentHTML("afterbegin", "<p>Inicio</p>");
// Rendimiento: DocumentFragment
const frag = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) {
const li = document.createElement("li");
li.textContent = i;
frag.appendChild(li);
}
div.appendChild(frag); // 1 reflowtextContent es seguro (no parsea HTML). innerHTML parsea pero es vulnerable a XSS. insertAdjacentHTML() inserta sin limpiar. DocumentFragment evita múltiples reflows.
Manipular Clases y Estilos
const el = document.querySelector(".card");
// classList
el.classList.add("activo", "visible");
el.classList.remove("oculto");
el.classList.toggle("dark");
el.classList.contains("activo"); // true
el.classList.replace("old", "new");
// Estilos en línea
el.style.color = "red";
el.style.backgroundColor = "#333";
el.style.cssText = "color: red; font-size: 16px;";
// Leer estilos computados
const estilos = getComputedStyle(el);
estilos.fontSize; // "16px"
// Atributos
el.setAttribute("data-id", "42");
el.getAttribute("data-id"); // "42"
el.dataset.id; // "42" (data-*)classList gestiona clases sin sobrescribir. toggle() añade/elimina. style define estilos en línea. getComputedStyle() lee el CSS final. dataset accede a atributos data-*.
Formularios e Inputs
const form = document.querySelector("form");
const input = document.querySelector("#nombre");
// Leer valores
input.value; // texto del input
input.checked; // checkbox/radio
input.files; // file input
// FormData (todos los campos)
const datos = new FormData(form);
datos.get("nombre");
datos.append("extra", "valor");
// Submit con fetch
form.addEventListener("submit", async (e) => {
e.preventDefault();
const datos = new FormData(form);
const res = await fetch("/api", {
method: "POST",
body: datos
});
});
// Validación nativa
input.validity.valid; // true/false
input.checkValidity(); // dispara invalid
input.setCustomValidity("Error!");FormData recoge todos los campos del formulario. Úsalo con fetch() para enviar. e.preventDefault() impide el submit por defecto. input.validity da acceso a la validación nativa del navegador.
Eventos — addEventListener
const btn = document.querySelector("#btn");
// Añadir un listener
btn.addEventListener("click", function(e) {
console.log(e.target); // elemento clicado
console.log(e.type); // "click"
console.log(e.clientX); // posición X
e.preventDefault(); // impide la acción por defecto
e.stopPropagation(); // impide el bubbling
});
// Arrow function
btn.addEventListener("click", (e) => {
console.log("¡clicado!");
});
// Opciones
btn.addEventListener("click", handler, {
once: true, // se elimina tras la 1ª ejecución
capture: true, // fase de captura
passive: true // no llama a preventDefault
});
// Eliminar
btn.removeEventListener("click", handler);addEventListener() registra un handler. e.target es el elemento que originó el evento. preventDefault() impide la acción por defecto. once: true se auto-elimina tras la primera ejecución.
IntersectionObserver (Lazy Load)
// Observa elementos que entran en el viewport
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add("visible");
observer.unobserve(entry.target);
}
});
}, {
threshold: 0.1, // 10% visible
rootMargin: "50px" // margen extra
});
// Observar elementos
document.querySelectorAll(".lazy")
.forEach(el => observer.observe(el));
// Lazy load de imágenes
const imgObs = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
imgObs.unobserve(img);
}
});
});IntersectionObserver detecta cuándo los elementos entran en el viewport. Ideal para lazy loading, animaciones al hacer scroll y scroll infinito. threshold define el % visible para activar. unobserve() deja de observar.
Async e Promises
Promises — Básico
// Crear una Promise
const promessa = new Promise((resolve, reject) => {
const éxito = true;
if (éxito) {
resolve("Datos listos");
} else {
reject(new Error("Falló"));
}
});
// Consumirla
promessa
.then(resultado => console.log(resultado))
.catch(error => console.error(error))
.finally(() => console.log("Siempre se ejecuta"));
// Estados: pending → fulfilled | rejected
// Una Promise se resuelve solo 1 vezUna Promise tiene 3 estados: pending, fulfilled, rejected. resolve() cumple, reject() rechaza. .then() maneja el éxito, .catch() el error, .finally() siempre se ejecuta.
Promise.all y allSettled
// Ejecutar en paralelo
const [users, posts] = await Promise.all([
fetch("/api/users").then(r => r.json()),
fetch("/api/posts").then(r => r.json())
]);
// allSettled: espera a todas (incluso con error)
const resultados = await Promise.allSettled([
fetch("/api/a"),
fetch("/api/b") // puede fallar
]);
resultados.forEach(r => {
if (r.status === "fulfilled") console.log(r.value);
else console.log(r.reason);
});Promise.all() espera a todas y falla si una falla. Promise.allSettled() espera a todas sin fallar — devuelve el status de cada una. Usa all() cuando todas son obligatorias.
Web Workers (Básico)
// worker.js (archivo separado)
self.onmessage = (e) => {
const resultado = e.data * 2;
self.postMessage(resultado);
};
// main.js
const worker = new Worker("worker.js");
worker.onmessage = (e) => {
console.log("Resultado:", e.data);
};
worker.postMessage(21); // envía datos
// Terminar
worker.terminate();
// Inline worker (sin archivo)
const blob = new Blob([`
self.onmessage = (e) => {
self.postMessage(e.data.toUpperCase());
};
`], { type: "application/javascript" });
const inline = new Worker(URL.createObjectURL(blob));Web Workers ejecutan código en un hilo separado (no bloquea la UI). Comunícate vía postMessage()/onmessage. Ideal para cálculos pesados. terminate() finaliza el worker.
async/await
// Una función async devuelve una Promise
async function fetchData() {
try {
const res = await fetch("/api/datos");
const datos = await res.json();
return datos;
} catch (error) {
console.error("Error:", error);
throw error;
}
}
// Llamarla
fetchData()
.then(datos => console.log(datos))
.catch(e => console.error(e));
// En arrow functions
const buscar = async () => {
const res = await fetch("/api");
return res.json();
};
// IIFE async
(async () => {
const datos = await buscar();
console.log(datos);
})();async marca la función como asíncrona (devuelve una Promise). await pausa hasta que la Promise se resuelve. Usa try/catch para errores. Solo puedes usar await dentro de funciones async (o top-level en módulos).
Promise.race y any
// race: la primera en resolver (o rechazar)
const maisRapida = await Promise.race([
fetch("/api/servidor1"),
fetch("/api/servidor2")
]);
// any: la primera con éxito (ignora errores)
const primera = await Promise.any([p1, p2, p3]);
// Timeout con race
function comTimeout(promise, ms) {
const timeout = new Promise((_, reject) =>
setTimeout(() => reject(new Error("Timeout")), ms)
);
return Promise.race([promise, timeout]);
}
await comTimeout(fetch("/api"), 5000);Promise.race() devuelve la primera en resolver O rechazar. Promise.any() devuelve la primera con éxito (ignora errores). Útil para timeouts y probar múltiples servidores.
Event Loop y Microtasks
console.log("1 - Síncrono");
setTimeout(() => {
console.log("2 - Macrotask (timeout)");
}, 0);
Promise.resolve().then(() => {
console.log("3 - Microtask (promise)");
});
queueMicrotask(() => {
console.log("4 - Microtask (queue)");
});
console.log("5 - Síncrono");
// Orden: 1, 5, 3, 4, 2
// Síncrono → Microtasks → MacrotasksEl Event Loop procesa: 1) código síncrono, 2) microtasks (Promises, queueMicrotask), 3) macrotasks (setTimeout, eventos). Incluso con delay 0, setTimeout se ejecuta al final.
fetch — GET y Lectura
// GET simple
const res = await fetch("https://api.ejemplo.com/datos");
const datos = await res.json();
// Comprobar status
if (!res.ok) {
throw new Error(`HTTP ${res.status}`);
}
// Headers y status
res.headers.get("content-type");
res.status; // 200, 404, 500...
// DELETE
await fetch("/api/1", { method: "DELETE" });fetch() devuelve una Promise. res.json() parsea el body. Comprueba siempre res.ok (status 200-299) antes de usar los datos. res.status da el código HTTP.
Encadenamiento de Promises
// Encadenar .then()
fetch("/api/user/1")
.then(res => res.json())
.then(user => fetch(`/api/posts/${user.id}`))
.then(res => res.json())
.then(posts => console.log(posts))
.catch(err => console.error(err));
// Equivalente con async/await (más legible)
async function getPosts() {
const res = await fetch("/api/user/1");
const user = await res.json();
const res2 = await fetch(`/api/posts/${user.id}`);
return res2.json();
}
// Devolver un valor vs una Promise
Promise.resolve(42)
.then(n => n * 2) // 84
.then(n => Promise.resolve(n + 1)) // 85
.then(console.log);Cada .then() devuelve una nueva Promise. Puedes devolver un valor u otra Promise. async/await es más legible para operaciones secuenciales. Los errores se propagan hasta .catch().
Iteradores Asíncronos (for await...of)
// Objeto con un iterador asíncrono
const paginas = {
async *[Symbol.asyncIterator]() {
let page = 1;
while (true) {
const res = await fetch(`/api?page=${page}`);
const datos = await res.json();
if (!datos.length) return;
yield datos;
page++;
}
}
};
// Consumir con for await...of
for await (const items of paginas) {
console.log("Página:", items);
}
// Stream de una respuesta fetch
const res = await fetch("/api/grande");
const reader = res.body.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) break;
console.log("Chunk:", value.length, "bytes");
}for await...of itera sobre async iterables. async *[Symbol.asyncIterator]() define un generador asíncrono. Ideal para paginación, streams y lectura de archivos en chunks.
fetch — POST y Headers
// POST con JSON
const res = await fetch("/api/usuarios", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer token123"
},
body: JSON.stringify({ nombre: "Ana", edad: 30 })
});
const creado = await res.json();
// PUT (actualizar)
await fetch("/api/1", {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ nombre: "Maria" })
});
// POST con FormData (subida)
const form = new FormData();
form.append("fichero", fileInput.files[0]);
await fetch("/upload", { method: "POST", body: form });Para POST, define Content-Type y usa JSON.stringify() en el body. Authorization envía tokens. Para subidas, usa FormData sin definir el header (el navegador lo define automáticamente).
AbortController (Cancelar fetch)
const controller = new AbortController();
const { signal } = controller;
// Fetch con signal
const res = await fetch("/api/datos", { signal });
// Cancelar tras 5s
const timeout = setTimeout(() => {
controller.abort();
}, 5000);
try {
const datos = await res.json();
clearTimeout(timeout);
} catch (e) {
if (e.name === "AbortError") {
console.log("Petición cancelada");
}
}
// Cancelar con un botón
const btn = document.querySelector("#cancelar");
btn.addEventListener("click", () => {
controller.abort();
});AbortController cancela peticiones fetch(). Pasa signal en las opciones. controller.abort() cancela. El error tiene name === "AbortError". Útil para timeouts y cancelar peticiones al navegar.
ES6+ Moderno
Módulos (import/export)
// utils.js — Named exports
export const PI = 3.14159;
export function suma(a, b) { return a + b; }
export class Calculadora { /* ... */ }
// app.js — Named imports
import { PI, suma } from "./utils.js";
import { suma as add } from "./utils.js"; // alias
import * as utils from "./utils.js"; // namespace
utils.PI; // 3.14159
// Default export (1 por archivo)
// api.js
export default class Api { /* ... */ }
// Import default (cualquier nombre)
import Api from "./api.js";
import MiApi from "./api.js"; // mismo resultado
// Dynamic import (lazy loading)
const módulo = await import("./pesado.js");
módulo.funcion();export exporta, import importa. Los Named exports permiten varios por archivo. Default export solo 1 por archivo. import() dinámico carga bajo demanda (code splitting). Usa .js en el path.
WeakMap, WeakSet y Symbol
// Symbol: identificador único
const id = Symbol("id");
const obj = { [id]: 123 };
obj[id]; // 123
// Symbols bien conocidos
const arr = [3, 1, 2];
arr[Symbol.iterator](); // iterador
// WeakMap: claves débiles (el GC puede limpiar)
const cache = new WeakMap();
function procesar(obj) {
if (cache.has(obj)) return cache.get(obj);
const resultado = /* cálculo pesado */ obj;
cache.set(obj, resultado);
return resultado;
}
// WeakSet: objetos únicos (débiles)
const vistos = new WeakSet();
vistos.add(elemento);
vistos.has(elemento); // trueSymbol crea identificadores únicos. WeakMap/WeakSet tienen claves débiles — el Garbage Collector puede limpiar entradas si la clave se recolecta. Ideales para cachés y metadatos privados.
Clases
class Animal {
// Campos privados (ES2022)
#energia = 100;
constructor(nombre, sonido) {
this.nombre = nombre;
this.sonido = sonido;
}
hablar() {
return `${this.nombre} hace ${this.sonido}`;
}
// Getter/Setter
get energia() { return this.#energia; }
set energia(val) { this.#energia = Math.max(0, val); }
// Método estático
static crear(nombre) {
return new Animal(nombre, "...");
}
}
// Herencia
class Perro extends Animal {
constructor(nombre) {
super(nombre, "Au au");
}
hablar() {
return `${super.hablar()}!`;
}
}class define clases. constructor() inicializa. #campo es privado. extends/super() para herencia. get/set crean accessors. static pertenece a la clase, no a la instancia.
Top-level Await y using
// Top-level await (ES2022, en módulos)
// ¡No necesita IIFE async!
const datos = await fetch("/api/config")
.then(r => r.json());
// Import dinámico condicional
const módulo = process.env.DEBUG
? await import("./debug.js")
: await import("./producción.js");
// using (ES2024 - resource management)
// using declara recursos que se limpian automáticamente
function abrirFicheiro() {
return {
[Symbol.dispose]() {
console.log("Archivo cerrado");
}
};
}
{
using fichero = abrirFicheiro();
// usar fichero...
} // Symbol.dispose() llamado automáticamenteTop-level await permite await fuera de funciones async (solo en módulos ES). using (ES2024) declara recursos con limpieza automática vía Symbol.dispose() — similar al using de C# o al with de Python.
Generadores e Iteradores
// Generator function
function* contador(max) {
let i = 0;
while (i < max) {
yield i++;
}
}
const gen = contador(3);
gen.next(); // { value: 0, done: false }
gen.next(); // { value: 1, done: false }
gen.next(); // { value: 2, done: false }
gen.next(); // { value: undefined, done: true }
// Iterable con for...of
for (const n of contador(5)) {
console.log(n); // 0, 1, 2, 3, 4
}
// Lazy evaluation (infinito)
function* fibonacci() {
let [a, b] = [0, 1];
while (true) {
yield a;
[a, b] = [b, a + b];
}
}function* crea un generador. yield pausa y devuelve un valor. .next() reanuda. Son lazy — solo calculan cuando se pide. Ideales para secuencias infinitas y pipelines de datos.
Proxy y Reflect
// Proxy: intercepta operaciones
const handler = {
get(target, prop) {
console.log(`Accedió: ${prop}`);
return Reflect.get(target, prop);
},
set(target, prop, value) {
if (typeof value !== "number") {
throw new TypeError("¡Solo numeros!");
}
return Reflect.set(target, prop, value);
}
};
const datos = new Proxy({ x: 1 }, handler);
datos.x; // log + 1
datos.x = 5; // OK
datos.x = "a"; // TypeError!
// Validación reactiva
const state = new Proxy({ count: 0 }, {
set(t, p, v) {
Reflect.set(t, p, v);
render(); // re-renderiza
return true;
}
});Proxy intercepta operaciones (get, set, delete, etc.) en un objeto. Reflect proporciona los métodos estándar. Útil para validación, logging, reactividad y data binding. La base de frameworks como Vue.
Erros e Debug
try/catch/finally
try {
const datos = JSON.parse(texto);
if (!datos.nombre) {
throw new Error("Campo 'nombre' obligatorio");
}
} catch (error) {
console.error("Tipo:", error.name); // "Error"
console.error("Msg:", error.message); // mensaje
console.error("Stack:", error.stack); // trace
} finally {
console.log("Siempre se ejecuta");
// Cerrar conexiones, limpiar recursos
}
// En async/await
async function cargar() {
try {
const res = await fetch("/api");
return await res.json();
} catch (e) {
console.error("Fallo:", e.message);
return null;
}
}try ejecuta código arriesgado. catch captura errores. finally siempre se ejecuta (limpieza). throw new Error() lanza errores personalizados. En async, usa try/catch en vez de .catch().
Manejo Global de Errores
// Errores no capturados
window.addEventListener("error", (e) => {
console.error("Error global:", e.message);
console.error("Archivo:", e.filename);
console.error("Línea:", e.lineno);
// Enviar a un servicio de logging
});
// Promises rechazadas sin catch
window.addEventListener("unhandledrejection", (e) => {
console.error("Promise rechazada:", e.reason);
e.preventDefault(); // no mostrar en consola
});
// Error boundary en componentes (React-like)
class ErrorBoundary {
static capturar(fn, onError) {
try { return fn(); }
catch (e) { onError(e); return null; }
}
}window.onerror captura errores no manejados. unhandledrejection captura Promises sin .catch(). Útil para logging global y reportar errores a servicios como Sentry. No sustituye al try/catch local.
Tipos de Errores
// TypeError: tipo incorrecto
null.toString(); // TypeError
"str"(); // TypeError
// ReferenceError: la variable no existe
console.log(x); // ReferenceError
// SyntaxError: código inválido
eval("if ("); // SyntaxError
// RangeError: valor fuera del límite
new Array(-1); // RangeError
(1).toFixed(101); // RangeError
// URIError: URI inválida
decodeURIComponent("%"); // URIError
// Error personalizado
class ValidationError extends Error {
constructor(campo, msg) {
super(msg);
this.name = "ValidationError";
this.campo = campo;
}
}
throw new ValidationError("email", "Inválido");Tipos nativos: TypeError (tipo incorrecto), ReferenceError (no existe), SyntaxError (código inválido), RangeError (fuera del límite). Crea clases con extends Error para errores personalizados.
AggregateError y Error Cause
// Error cause (ES2022)
try {
await fetch("/api");
} catch (e) {
throw new Error("Fallo al cargar datos", { cause: e });
}
// error.cause contiene el error original
// AggregateError: agrupar múltiples errores
const errores = [
new Error("Campo nombre obligatorio"),
new Error("Email inválido"),
new Error("Edad mínima: 18")
];
throw new AggregateError(errores, "Validación falló");
// Promise.any lanza AggregateError si todas fallan
try {
await Promise.any([p1, p2, p3]);
} catch (e) {
e.errors; // array de todos los errores
}Error cause ({ cause: e }) encadena errores manteniendo el original. AggregateError agrupa múltiples errores en uno solo. Promise.any() lanza AggregateError cuando todas las promises fallan.
Error Boundary (Patrón)
// Wrapper genérico para operaciones arriesgadas
function tentar(fn, fallback = null) {
try {
return { datos: fn(), error: null };
} catch (e) {
return { datos: fallback, error: e };
}
}
// Uso
const { datos, error } = tentar(() => {
return JSON.parse(textoInvalido);
});
if (error) {
console.error("Parse falló:", error.message);
}
// Versión async
async function tentarAsync(fn, fallback = null) {
try {
return { datos: await fn(), error: null };
} catch (e) {
return { datos: fallback, error: e };
}
}
const { datos: user } = await tentarAsync(
() => fetch("/api/user").then(r => r.json())
);Patrón try/catch encapsulado: devuelve { datos, error } en vez de lanzar. Evita try/catch repetitivo. Inspirado en el patrón Result de lenguajes funcionales. Versión async para Promises.
Debugging con DevTools
// debugger: pausa la ejecución en DevTools
function calcular(x) {
debugger; // breakpoint manual
return x * 2;
}
// console.dir: ver la estructura del objeto
console.dir(document.body);
// Monitorear llamadas
const obj = { método() {} };
console.log(obj.método.toString());
// Performance
performance.now(); // timestamp preciso
performance.mark("inicio");
// ... código ...
performance.mark("fin");
performance.measure("op", "inicio", "fin");
// Ver mediciones
performance.getEntriesByType("measure");
// Memory
// Chrome DevTools > Memory > Heap Snapshotdebugger; crea un breakpoint en el código. performance.now() da timestamps precisos. performance.mark()/measure() miden secciones. Usa DevTools para breakpoints condicionales y watch expressions.
Truques e Dicas
Desestructuración con Defaults
// Config con defaults
function criarServer({
host = "localhost",
port = 3000,
ssl = false,
db = { host: "localhost", port: 5432 }
} = {}) {
console.log(`${host}:${port}`);
}
criarServer(); // localhost:3000
criarServer({ port: 8080, ssl: true });
// Parámetros opcionales con un objeto
function buscar({ query = "", page = 1, limit = 10 } = {}) {
return `/api?q=${query}&p=${page}&l=${limit}`;
}
// Extraer con defaults
const { nombre = "Anónimo", role = "user" } = getUser();Patrón de configuración: un objeto con defaults + = {} para permitir llamar sin argumentos. Más legible que los parámetros posicionales. Ideal para funciones con muchas opciones opcionales.
Encadenamiento e Inmutabilidad
// NO mutar — crear nuevo
const state = { items: [1, 2, 3], filtro: "todos" };
// Añadir un item
const nuevo = { ...state, items: [...state.items, 4] };
// Eliminar un item
const sem2 = {
...state,
items: state.items.filter(i => i !== 2)
};
// Actualizar un item
const actualizado = {
...state,
items: state.items.map(i =>
i === 2 ? { ...i, done: true } : i
)
};
// toSorted, toReversed, toSpliced (ES2023)
const ordenado = [3,1,2].toSorted(); // ¡no muta!
const invertido = [1,2,3].toReversed();
const semPrimeiro = [1,2,3].toSpliced(0, 1);Inmutabilidad: crea nuevos objetos/arrays en vez de mutar. spread + map/filter para transformaciones. ES2023: toSorted(), toReversed(), toSpliced() no mutan el original.
URLSearchParams y Query Strings
// Leer la query string actual
const params = new URLSearchParams(window.location.search);
params.get("page"); // "2"
params.has("filtro"); // true/false
params.getAll("tag"); // ["js", "web"]
// Crear y modificar
const p = new URLSearchParams();
p.set("q", "javascript");
p.set("page", "1");
p.append("tag", "es6");
p.toString(); // "q=javascript&page=1&tag=es6"
// Actualizar la URL sin recargar
const url = new URL(window.location);
url.searchParams.set("page", "2");
history.pushState({}, "", url);
// Parse de una URL completa
const u = new URL("https://api.com/datos?page=2&limit=10");
u.hostname; // "api.com"
u.pathname; // "/datos"
u.searchParams.get("page"); // "2"URLSearchParams manipula query strings fácilmente. history.pushState() actualiza la URL sin recargar. new URL() parsea URLs completas. Esencial para filtros, paginación y compartir estado vía URL.
Operaciones Útiles en Arrays
// Último elemento
const último = arr.at(-1);
// Eliminar duplicados
const unico = [...new Set(arr)];
// Aplanar
[[1,2],[3,4]].flat(); // [1,2,3,4]
[[1,[2,[3]]]].flat(Infinity); // [1,2,3]
// Agrupar (ES2024)
const items = [
{ tipo: "fruta", nombre: "manzana" },
{ tipo: "legume", nombre: "cenoura" },
{ tipo: "fruta", nombre: "pera" }
];
const grupos = Object.groupBy(items, i => i.tipo);
// Chunk (dividir en grupos)
function chunk(arr, size) {
return Array.from({ length: Math.ceil(arr.length / size) },
(_, i) => arr.slice(i * size, (i + 1) * size));
}
chunk([1,2,3,4,5], 2); // [[1,2],[3,4],[5]].at(-1) accede al último elemento. [...new Set()] elimina duplicados. .flat(Infinity) aplana totalmente. Object.groupBy() agrupa por clave. chunk() divide en grupos de tamaño fijo.
Sleep y Retry
// Sleep (pausa async)
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
await sleep(1000); // pausa 1s
// Retry con intentos
async function retry(fn, tentativas = 3) {
for (let i = 0; i < tentativas; i++) {
try { return await fn(); }
catch (e) {
if (i === tentativas - 1) throw e;
await sleep(1000 * (i + 1));
}
}
}
// Uso
const datos = await retry(() => fetch("/api").then(r => r.json()));sleep() pausa código async sin bloquear el hilo. retry() repite operaciones que pueden fallar con un retraso creciente (backoff). Esencial para llamadas de red inestables.
Debounce y Throttle
// Debounce: espera X ms tras la última llamada
function debounce(fn, delay = 300) {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), delay);
};
}
// Uso: buscar mientras escribes
input.addEventListener("input", debounce((e) => {
buscar(e.target.value);
}, 500));
// Throttle: como máximo 1 vez cada X ms
function throttle(fn, limit = 200) {
let último = 0;
return (...args) => {
const ahora = Date.now();
if (ahora - último >= limit) {
último = ahora;
fn(...args);
}
};
}
// Uso: scroll
window.addEventListener("scroll", throttle(() => {
verificarPosicao();
}, 100));Debounce espera X ms tras la última llamada (buscar mientras escribes). Throttle limita a 1 ejecución por X ms (scroll, resize). Ambos evitan llamadas excesivas a funciones pesadas.
Memoize y Cache
// Memoize (caché de resultados)
function memoize(fn) {
const cache = new Map();
return (...args) => {
const key = JSON.stringify(args);
if (cache.has(key)) return cache.get(key);
const resultado = fn(...args);
cache.set(key, resultado);
return resultado;
};
}
// Uso: Fibonacci con caché
const fib = memoize((n) => n <= 1 ? n : fib(n-1) + fib(n-2));
fib(40); // ¡rápido!
// Caché simple con Map
const cache = new Map();
async function getCached(url) {
if (cache.has(url)) return cache.get(url);
const res = await fetch(url).then(r => r.json());
cache.set(url, res);
return res;
}memoize() guarda resultados en una caché — ideal para funciones puras con cálculos pesados. Usa Map como caché para peticiones repetidas. Solo funciona con funciones determinísticas (mismo input = mismo output).
Deep Clone y Comparación
// Deep clone moderno
const original = { a: 1, b: { c: [1, 2] } };
const copia = structuredClone(original);
copia.b.c.push(3);
original.b.c; // [1, 2] (¡inalterado!)
// JSON (no copia funciones, Date, undefined)
const copia2 = JSON.parse(JSON.stringify(original));
// Comparar objetos (shallow)
const a = { x: 1, y: 2 };
const b = { x: 1, y: 2 };
a === b; // false (referencias diferentes)
// Shallow compare
const igual = JSON.stringify(a) === JSON.stringify(b);
// structuredClone soporta:
// Date, RegExp, Map, Set, ArrayBuffer, etc.structuredClone() hace un deep clone nativo (soporta Date, Map, Set). JSON.parse(JSON.stringify()) es limitado (pierde funciones, Date se convierte en string). Los objetos se comparan por referencia, no por valor.
Regex — Esencial
// Probar y extraer
const email = "ana@ejemplo.com";
/^[\w.-]+@[\w.-]+\.\w+$/.test(email); // true
// Capturar grupos
const match = "2024-01-15".match(/(\d{4})-(\d{2})-(\d{2})/);
match[1]; // "2024"
match[2]; // "01"
match[3]; // "15"
// Named groups
const { groups } = "2024-01-15".match(
/(?<anio>\d{4})-(?<mes>\d{2})-(?<dia>\d{2})/
);
groups.anio; // "2024"
// Replace con regex
"10.50".replace(/\./g, ","); // "10,50"
"hola mundo".replace(/\b\w/g, c => c.toUpperCase());
// "Hola Mundo"
// Split
"a,b,,c".split(/,+/); // ["a","b","","c"].test() comprueba si coincide. .match() extrae grupos con (). (?<nombre>) crea named groups. .replace() con /g sustituye todas. \d = dígito, \w = palabra, + = 1 o más.