variables y tipos de datos
Variables: dónde vive tu información
En JavaScript moderno (ES6+) solo usas const y let. El var es legacy y tiene comportamientos confusos. La regla simple: empieza con const, cambia a let solo si necesitas reasignar.
// CONST: valor que no cambia (usa esto por defecto)
const nombre = 'Ana García';
const edad = 28;
const activo = true;
const colores = ['rojo', 'verde', 'azul']; // array = const aunque se modifique
const usuario = { nombre: 'Ana', rol: 'admin' }; // objeto = const
// LET: valor que necesita reasignarse
let intentos = 0;
let mensaje = '';
let loading = false;
// REASIGNACIÓN
intentos = intentos + 1; // ✓ OK, reasignación con let
nombre = 'Otra persona'; // ✗ ERROR: const no se puede reasignar
Texto. Las backticks permiten interpolación de variables.
Enteros y decimales son el mismo tipo.
Solo dos valores posibles.
Ausencia intencional de valor.
Variable declarada sin asignar.
Lista ordenada, acceso por índice.
Pares clave-valor, acceso por nombre.
También es un tipo de dato, asignable a variables.
Arrays — Operaciones esenciales
const frutas = ['manzana', 'pera', 'uva'];
// LEER
frutas[0] // 'manzana' (índice empieza en 0)
frutas.length // 3
frutas[frutas.length-1] // último elemento: 'uva'
// AÑADIR / QUITAR
frutas.push('kiwi'); // añade al final → ['manzana','pera','uva','kiwi']
frutas.pop(); // quita el último → devuelve 'kiwi'
frutas.unshift('limón'); // añade al inicio
frutas.shift(); // quita el primero
// TRANSFORMAR (devuelven NUEVO array, no mutan el original)
const mayusculas = frutas.map(f => f.toUpperCase());
const con_p = frutas.filter(f => f.includes('p'));
const primero = frutas.find(f => f.startsWith('m'));
const existe = frutas.includes('pera'); // true/false
const indice = frutas.indexOf('uva'); // 2
// REDUCIR a un valor
const total = [10, 20, 30].reduce((acc, n) => acc + n, 0); // 60
// ITERAR
frutas.forEach((fruta, i) => {
console.log(i, fruta);
});
Objects — Acceso y desestructuración
const usuario = {
nombre: 'Ana García',
email: 'ana@example.com',
edad: 28,
direccion: {
ciudad: 'Bogotá',
pais: 'Colombia'
}
};
// ACCESO
usuario.nombre // 'Ana García'
usuario['email'] // 'ana@example.com' (útil con variables)
usuario.direccion.ciudad // 'Bogotá'
// MODIFICAR
usuario.telefono = '+57 300 000 0000'; // añadir propiedad
delete usuario.edad; // eliminar
// DESESTRUCTURACIÓN (ES6) — la forma moderna
const { nombre, email } = usuario;
const { ciudad } = usuario.direccion;
// Con alias
const { nombre: nombreCompleto } = usuario;
// En arrays
const [primero, segundo, ...resto] = [1, 2, 3, 4, 5];
// Iterar propiedades
Object.keys(usuario).forEach(clave => console.log(clave));
Object.entries(usuario).forEach(([clave, valor]) => console.log(clave, valor));
funciones
Funciones: el corazón de JavaScript
Todo en JavaScript gira alrededor de funciones. Hay tres formas de definirlas y cada una tiene su contexto de uso. Entender la diferencia es crucial.
Function Declaration
// Se puede llamar ANTES de su definición (hoisting)
calcular(5, 3); // ✓ funciona
function calcular(a, b) {
return a + b;
}
Las declarations se "suben" al inicio del scope. Úsalas para funciones principales del módulo.
Function Expression
// Solo disponible DESPUÉS de su definición
// calcular(5, 3); // ✗ ERROR
const calcular = function(a, b) {
return a + b;
};
calcular(5, 3); // ✓ funciona
Una función asignada a una variable. Más explícita sobre cuándo está disponible.
Arrow Function (moderna)
// Forma corta: una sola expresión
const calcular = (a, b) => a + b;
// Forma larga: bloque con return
const calcular = (a, b) => {
const resultado = a + b;
return resultado;
};
// Sin parámetros
const saludar = () => 'Hola!';
// Un parámetro: sin paréntesis
const doble = n => n * 2;
La forma más común en código moderno. Ideal para callbacks (map, filter, addEventListener).
Default params + Rest + Spread
// Parámetros por defecto
function saludar(nombre = 'amigo', saludo = 'Hola') {
return `${saludo}, ${nombre}!`;
}
saludar(); // 'Hola, amigo!'
saludar('Ana', 'Hey'); // 'Hey, Ana!'
// Rest (...): recibe múltiples argumentos como array
function suma(...numeros) {
return numeros.reduce((total, n) => total + n, 0);
}
suma(1, 2, 3, 4, 5); // 15
// Spread (...): expande un array o un objeto
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combinado = [...arr1, ...arr2]; // [1,2,3,4,5,6]
const original = { nombre: 'Ana', rol: 'user' };
const admin = { ...original, rol: 'admin' }; // override: rol = 'admin'
el dom
DOM: el árbol que puedes modificar
El DOM (Document Object Model) es la representación del HTML como un árbol de objetos JavaScript. Puedes seleccionar cualquier elemento y modificarlo en tiempo real, sin recargar la página.
Selección de elementos
// UN elemento (el primero que encuentre)
const titulo = document.querySelector('h1');
const boton = document.querySelector('#enviar');
const card = document.querySelector('.card');
const input = document.querySelector('input[type="email"]');
// MÚLTIPLES elementos (devuelve NodeList)
const cards = document.querySelectorAll('.card');
const links = document.querySelectorAll('nav a');
// Iterar sobre múltiples
cards.forEach(card => card.classList.add('visible'));
// También puedes convertirlo a array para usar map/filter
const cardsArr = Array.from(cards);
// Dentro de un elemento (más eficiente, solo busca dentro)
const nav = document.querySelector('nav');
const linksNav = nav.querySelectorAll('a');
Leer y modificar contenido
const elemento = document.querySelector('.titulo');
// LEER
elemento.textContent // texto plano (seguro)
elemento.innerHTML // HTML interno (cuidado con XSS)
elemento.value // valor de inputs y textareas
elemento.getAttribute('href') // cualquier atributo HTML
// ESCRIBIR
elemento.textContent = 'Nuevo título';
elemento.innerHTML = '<strong>Negrita</strong> y texto';
elemento.value = 'nuevo valor';
elemento.setAttribute('data-id', '42');
// CLASES
elemento.classList.add('activo');
elemento.classList.remove('inactivo');
elemento.classList.toggle('visible');
elemento.classList.contains('activo'); // true/false
elemento.classList.replace('viejo', 'nuevo');
// ESTILOS INLINE (usa clases cuando puedas, esto es para casos dinámicos)
elemento.style.display = 'none';
elemento.style.backgroundColor = '#f0e040';
elemento.style.cssText = 'display:flex; gap:16px;';
Crear y eliminar elementos
// CREAR
const div = document.createElement('div');
div.className = 'card';
div.innerHTML = `
<h3>Título</h3>
<p>Descripción</p>
`;
// INSERTAR
document.body.appendChild(div); // al final del body
container.prepend(div); // al inicio del container
lista.insertBefore(div, lista.children[2]); // en posición específica
// insertAdjacentHTML: más control sobre dónde insertas
referencia.insertAdjacentHTML('afterend', '<p>Después del elemento</p>');
// posiciones: 'beforebegin' | 'afterbegin' | 'beforeend' | 'afterend'
// ELIMINAR
elemento.remove();
container.removeChild(hijo);
Tip DOM
innerHTML vs createElement
Para contenido estático o templates, innerHTML es conveniente pero nunca insertes datos del usuario sin sanitizar (riesgo de XSS). Para elementos dinámicos donde insertas datos del usuario, usa createElement y textContent que escapa el HTML automáticamente.
eventos
Eventos: responder al usuario
Los eventos son la forma en que JavaScript reacciona a lo que hace el usuario. Cada interacción (clic, tecla, scroll, submit) dispara un evento que puedes escuchar con addEventListener.
Clic del mouse sobre un elemento
Botones, cards, linksEnvío de formulario
FormsCambio en tiempo real de un input
Búsqueda en vivoCuando el valor cambia y el input pierde foco
Select, checkboxTecla presionada
Atajos de tecladoTecla soltada
Validación al escribirMouse entra al elemento
Tooltips, previewsMouse sale del elemento
Cerrar tooltipsElemento recibe foco
Highlight de inputsElemento pierde foco
Validación al salirEl usuario hace scroll
Infinite scroll, header stickyLa ventana cambia de tamaño
Layouts dinámicosHTML cargado y parseado
Inicialización de scriptsTodos los recursos cargados
Imágenes, scripts externosEl objeto event y event delegation
document.querySelector('form').addEventListener('submit', (event) => {
event.preventDefault(); // evita el comportamiento por defecto (recargar)
// El objeto event contiene info del evento
console.log(event.target); // el elemento que disparó el evento
console.log(event.type); // 'submit'
const datos = new FormData(event.target);
const email = datos.get('email');
const nombre = datos.get('nombre');
});
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') cerrarModal();
if (e.ctrlKey && e.key === 's') {
e.preventDefault(); // evita guardar el html del navegador
guardarDraft();
}
});
// EVENT DELEGATION: un solo listener para múltiples elementos
// Más eficiente que añadir uno a cada elemento.
// Funciona incluso con elementos añadidos dinámicamente.
document.querySelector('.lista').addEventListener('click', (e) => {
// closest() sube por el DOM hasta encontrar el selector
const item = e.target.closest('.item');
if (item) {
item.classList.toggle('completado');
const id = item.dataset.id; // lee atributo data-id
}
});
fetch api
Fetch API: datos de servidores
Fetch permite hacer peticiones HTTP desde JavaScript. Es así como tu frontend se comunica con backends, obtiene datos de APIs externas y envía formularios sin recargar la página.
Concepto clave
Qué es asíncrono
JavaScript es single-threaded: ejecuta una cosa a la vez. Cuando pides datos a un servidor (puede tardar 2 segundos), no quieres bloquear toda la página esperando la respuesta.
async/await te permite "pausar" una función mientras espera una promesa (como fetch), sin bloquear el resto de la página. El código parece síncrono pero no lo es.
// FETCH BÁSICO
async function obtenerUsuario(id) {
try {
const response = await fetch(`https://api.ejemplo.com/usuarios/${id}`);
// Siempre verifica el status — fetch no lanza error en 404/500
if (!response.ok) {
throw new Error(`Error ${response.status}: ${response.statusText}`);
}
const usuario = await response.json(); // parsea el JSON
return usuario;
} catch (error) {
console.error('Error al obtener usuario:', error);
throw error; // propaga el error para que el llamador lo maneje
}
}
// LLAMAR CON ESTADO DE LOADING
async function cargarPerfil() {
const loading = document.querySelector('.loading');
const contenido = document.querySelector('.perfil');
loading.style.display = 'block';
contenido.style.opacity = '0';
try {
const usuario = await obtenerUsuario(1);
contenido.querySelector('.nombre').textContent = usuario.name;
contenido.querySelector('.email').textContent = usuario.email;
contenido.style.opacity = '1';
} catch (error) {
contenido.innerHTML = '<p class="error">Error al cargar. Intenta de nuevo.</p>';
} finally {
loading.style.display = 'none'; // se ejecuta SIEMPRE, con o sin error
}
}
// POST: enviar datos al servidor
async function crearUsuario(datos) {
const response = await fetch('https://api.ejemplo.com/usuarios', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify(datos) // convierte el objeto a string JSON
});
if (!response.ok) throw new Error('Error al crear usuario');
return response.json();
}
// MÚLTIPLES PETICIONES EN PARALELO
async function cargarDashboard() {
const [usuario, posts, stats] = await Promise.all([
fetch('/api/usuario').then(r => r.json()),
fetch('/api/posts').then(r => r.json()),
fetch('/api/stats').then(r => r.json())
]);
// los tres se piden al mismo tiempo, no uno tras otro
}
API PARA PRACTICAR
JSONPlaceholder — sin registro, sin auth
jsonplaceholder.typicode.com es una API REST gratuita con usuarios, posts, comentarios y todos. No necesita autenticación ni registro. Perfecta para practicar.
// Obtener 10 posts
const posts = await fetch('https://jsonplaceholder.typicode.com/posts?_limit=10')
.then(r => r.json());
// Obtener un usuario
const user = await fetch('https://jsonplaceholder.typicode.com/users/1')
.then(r => r.json());
// Crear un post (simulado, no persiste)
const nuevo = await fetch('https://jsonplaceholder.typicode.com/posts', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title: 'Mi post', body: 'Contenido', userId: 1 })
}).then(r => r.json());
localstorage
localStorage: persistir datos
localStorage guarda datos en el navegador del usuario. Sobrevive al cierre de la pestaña y al reinicio del navegador. Solo almacena texto — usa JSON.stringify/parse para objetos. Límite ~5MB.
// GUARDAR
localStorage.setItem('tema', 'dark');
localStorage.setItem('usuario', JSON.stringify({ nombre: 'Ana', rol: 'admin' }));
// LEER
const tema = localStorage.getItem('tema'); // 'dark' o null si no existe
const usuario = JSON.parse(localStorage.getItem('usuario') || 'null');
// ELIMINAR
localStorage.removeItem('tema');
localStorage.clear(); // elimina TODO el localStorage de ese origen (cuidado)
// PATRÓN COMÚN: leer con fallback
const preferencias = JSON.parse(localStorage.getItem('preferencias')) || {
tema: 'dark',
idioma: 'es',
notificaciones: true
};
// PATRÓN: CRUD de un array (notas, tareas, carrito)
function cargarNotas() {
return JSON.parse(localStorage.getItem('notas')) || [];
}
function guardarNotas(notas) {
localStorage.setItem('notas', JSON.stringify(notas));
}
function agregarNota(nota) {
const notas = cargarNotas();
notas.push({ id: Date.now(), ...nota });
guardarNotas(notas);
}
function eliminarNota(id) {
const notas = cargarNotas().filter(n => n.id !== id);
guardarNotas(notas);
}
localStorage
- Persiste indefinidamente
- Compartida entre pestañas del mismo origen
- Sobrevive al cierre del navegador
sessionStorage
- Se borra al cerrar la pestaña
- Solo disponible en la pestaña actual
- Misma API que localStorage
ejercicio: app de notas
Ejercicio: app de notas con localStorage
Construirás una app completa de notas que persiste datos en el navegador. Sin backend, sin librerías. Solo HTML + CSS + JavaScript puro.
HTML y CSS base
Estructura: header con título y botón "Nueva nota", input de búsqueda, grid de cards de notas. CSS: grid auto-fit para las cards, dark theme, transiciones suaves.
Crear y mostrar notas
Función crearNota() que añade al array y guarda en localStorage. Función renderNotas() que vacía el grid y renderiza cada nota como card con título, texto y fecha.
Eliminar notas
Botón de borrar en cada card. Al clicar, elimina del array por id (usa Date.now() como id único), guarda y re-renderiza. Event delegation para no añadir un listener por card.
Búsqueda en tiempo real
Evento 'input' en el buscador. Filtra el array con .filter() antes de renderizar. Resalta el texto coincidente con un span de color diferente.
Pulido UX
Animación fadeIn al añadir notas. Confirmación al borrar. Contador de notas en el header. Estado vacío cuando no hay notas ni resultados de búsqueda.
Estructura sugerida
Cómo organizar el JS
Separa el estado (el array de notas) de la UI (el render). Nunca modifiques el DOM directamente al crear/borrar — siempre actualiza el array, guarda en localStorage y llama a renderNotas(). Este patrón es la base de React, Vue y cualquier framework moderno.
recursos
Recursos de esta sesión
MDN — JavaScript
La referencia oficial. Para cada método de array, objeto o DOM que uses, la documentación completa está aquí.
javascript.info — Tutorial completo
El mejor tutorial de JS gratuito. Cubre desde básicos hasta async/await con explicaciones y ejercicios interactivos.
JSONPlaceholder — API para practicar
API REST gratuita para practicar fetch sin registro. Incluye endpoints de usuarios, posts, comentarios y todos.
Fetch API explicado — Midudev
30 minutos para dominar fetch, async/await y manejo de errores. En español, con ejemplos reales.
Array Explorer — ¿Qué método necesito?
Herramienta interactiva: describes lo que quieres hacer con un array y te dice qué método usar. Ideal cuando empiezas.