Depurar JavaScript con DevTools
En esta lección
Todo programador pasa más tiempo arreglando código que escribiéndolo. Un bug es un fallo: el programa se para con un error o, peor aún, funciona pero da un resultado equivocado. Depurar es encontrar por qué. No es cuestión de suerte ni de talento: es un método que se aprende. En esta lección vas a aprender a leer los mensajes de error, a sacar el máximo partido a la consola y a usar las herramientas de desarrollo del navegador (DevTools) para parar el programa y mirar dentro.
Qué son las excepciones y cómo capturarlas con try...catch lo tienes en errores y excepciones. Aquí nos centramos en encontrar el fallo.
Leer un error y su stack trace
Cuando un error no se captura, la consola lo muestra en rojo. Por ejemplo:
Uncaught TypeError: Cannot read properties of undefined (reading 'nombre')
at mostrarUsuario (app.js:12:24)
at cargarPerfil (app.js:30:3)
at HTMLButtonElement.<anonymous> (app.js:41:5)
Léelo en tres partes:
- Tipo:
TypeError. Te dice la familia del problema. - Mensaje: qué ha pasado. Aquí, que intentaste leer
nombrede algo que valeundefined. - Stack trace (traza de la pila): el camino de llamadas que llevó al error, de la más reciente a la más antigua. La primera línea es donde explotó: función
mostrarUsuario, archivoapp.js, línea 12, columna 24. Las siguientes cuentan quién la llamó:cargarPerfil, que a su vez fue llamada desde el manejador de un botón.
En DevTools, el nombre del archivo es un enlace: pulsa y te lleva a la línea exacta.
Consejo: el error casi nunca está en la línea que falla, sino antes. Si en la línea 12 algo vale
undefined, pregúntate de dónde venía ese valor. Sube por la traza.
Los errores más típicos
Estos mensajes (en Chrome y Node) te los vas a encontrar cientos de veces. Aprende a reconocerlos:
Cannot read properties of undefined (reading 'x')
Intentas leer algo.x y algo vale undefined (o null, con el mensaje of null). Causas habituales:
- La variable no se ha inicializado o una función no ha hecho
return. - Un índice fuera del array:
lista[lista.length].nombre. - Datos de una API que no tienen la forma que esperabas.
document.querySelectorno encontró el elemento (devuelvenull): verásCannot set properties of null (setting 'textContent').
Solución: comprueba el valor con console.log justo antes; si puede faltar legítimamente, usa ?. (lo tienes en operadores).
x is not a function
Llamas con () a algo que no es una función:
const pedido = {};
pedido.calcular(); // TypeError: pedido.calcular is not a function
"hola".push("x"); // TypeError: "hola".push is not a function
Suele ser un nombre mal escrito (lenght, forEch), un método que no existe en ese tipo (los textos no tienen push) o una variable que tapó a la función.
x is not defined
Usas una variable que no existe en ese ámbito:
console.log(totl); // ReferenceError: totl is not defined
Casi siempre es una errata o una variable declarada dentro de otra función o bloque. Repasa ámbito y closures.
Otros que conviene conocer
| Mensaje | Qué significa |
|---|---|
Assignment to constant variable. | Reasignas una const |
Cannot access 'x' before initialization | Usas una let/const antes de su línea |
Unexpected token | Error de sintaxis: falta o sobra un paréntesis, llave o coma |
Unexpected token ... in JSON / Expected property name | JSON.parse con un texto que no es JSON |
Maximum call stack size exceeded | Recursividad sin fin |
Failed to fetch | La petición no llegó: sin red, URL mala o CORS |
La consola a fondo
console.log es la herramienta de depuración más usada del mundo. Pero el objeto console tiene más métodos útiles. Pruébalos en la consola del navegador (F12): el editor de DevSchool solo muestra log, warn y error.
console.log con etiqueta
Un truco sencillo: mete la variable entre llaves y verás su nombre y su valor.
const usuario = { nombre: "Ana" };
console.log({ usuario }); // { usuario: { nombre: 'Ana' } }
console.log("total:", total, "descuento:", descuento);
Un console.log(5) suelto no dice nada cuando tienes diez en pantalla.
console.error y console.warn
Igual que log, pero en rojo y en amarillo, y se pueden filtrar por nivel en DevTools. Úsalos en los catch para que los errores destaquen.
console.table
Muestra arrays de objetos como una tabla. Perfecto para ver datos de una API:
console.table([
{ nombre: "Ana", nota: 8 },
{ nombre: "Luis", nota: 4 },
]);
┌─────────┬────────┬──────┐
│ (index) │ nombre │ nota │
├─────────┼────────┼──────┤
│ 0 │ 'Ana' │ 8 │
│ 1 │ 'Luis' │ 4 │
└─────────┴────────┴──────┘
console.group
Agrupa mensajes bajo un título (en el navegador se pueden plegar):
console.group("Pedido 1024");
console.log("Cliente: Ana");
console.log("Total: 45 €");
console.groupEnd();
console.time, console.count y console.assert
console.time("ordenar");
lista.sort((a, b) => a - b);
console.timeEnd("ordenar"); // ordenar: 70.4ms
for (const t of ["a", "b", "a"]) {
console.count(t); // a: 1, b: 1, a: 2
}
console.assert(stock >= 0, "El stock es negativo"); // solo escribe si es false
time/timeEndmide cuánto tarda un trozo de código.countcuenta cuántas veces pasa algo (¿se llama dos veces esta función?).assertavisa solo cuando una condición que debería cumplirse no se cumple.
Cuidado: en el navegador,
console.logde un objeto muestra una referencia “viva”: si lo despliegas después, verás su valor actual, no el del momento del log. Para una foto fija, usaconsole.log(JSON.stringify(obj))ostructuredClone(obj).
Borra los console.log de depuración cuando termines: ensucian la consola y a veces muestran datos que no deberían verse.
DevTools: parar el programa y mirar dentro
console.log obliga a adivinar dónde mirar. Las DevTools del navegador (F12 o Ctrl+Mayús+I; Cmd+Opción+I en Mac) te dejan pausar el programa en cualquier línea y ver todas las variables. Estas son las pestañas que más usarás:
| Pestaña | Para qué |
|---|---|
| Console | Ver mensajes y errores, y ejecutar JavaScript en la página |
| Sources (Depurador en Firefox) | Ver el código, poner breakpoints e ir paso a paso |
| Network (Red) | Ver cada petición: URL, estado, tiempo y respuesta |
| Elements (Inspector) | Ver y editar el HTML y el CSS en vivo |
| Application (Almacenamiento) | Ver localStorage, cookies, etc. |
La pestaña Console
Además de mostrar mensajes, es una calculadora de JavaScript sobre la página actual. Escribe document.querySelector("h1") o el nombre de una variable global y pulsa Intro. Cuando el programa está pausado, la consola trabaja con las variables del punto donde se ha parado.
Breakpoints
Un breakpoint (punto de interrupción) es una marca en una línea que dice “para aquí”. En Sources, abre tu archivo y haz clic en el número de línea: aparece una marca azul. La próxima vez que el código pase por ahí, se detendrá antes de ejecutar esa línea.
Con el programa pausado puedes:
- Pasar el ratón por encima de cualquier variable para ver su valor.
- Ver todas las variables en el panel Scope (ámbito).
- Ver en Call Stack la cadena de llamadas que ha llevado hasta ahí.
También puedes poner el breakpoint desde el propio código con la palabra debugger:
function calcularTotal(carrito) {
let total = 0;
for (const item of carrito) {
debugger; // si DevTools está abierto, se pausa aquí
total += item.precio * item.cantidad;
}
return total;
}
Si DevTools está cerrado, debugger no hace nada. Aun así, quítalo antes de publicar.
Otros breakpoints útiles: condicionales (clic derecho en el número de línea → Add conditional breakpoint, y escribes por ejemplo item.precio === undefined) y, en el panel lateral, pausar en excepciones (Pause on exceptions) o en eventos (Event Listener Breakpoints → click).
Paso a paso
Con el programa pausado, estos botones controlan la ejecución:
| Botón | Tecla | Qué hace |
|---|---|---|
| Resume | F8 | Continúa hasta el siguiente breakpoint |
| Step over | F10 | Ejecuta la línea actual y para en la siguiente |
| Step into | F11 | Si la línea llama a una función, entra en ella |
| Step out | Mayús+F11 | Termina la función actual y vuelve a quien la llamó |
Watch
En el panel Watch añades expresiones que quieres vigilar, como total, carrito.length o item.precio * item.cantidad. Se recalculan a cada paso, así ves cómo cambian sin llenar el código de console.log.
La pestaña Network
Cuando trabajas con fetch, muchos fallos no están en tu código sino en la petición. En Network ves cada una: la URL exacta, el método, el código de estado (200, 404, 500…), las cabeceras y la respuesta del servidor. Si tu lista sale vacía, mira aquí primero: quizá la API devolvió un error o datos con otra forma.
Una estrategia para encontrar bugs
Cuando algo falla, no cambies cosas al azar. Sigue un método:
- Reprodúcelo. Consigue que el fallo ocurra siempre, con unos pasos concretos.
- Lee el error completo, tipo, mensaje y traza. Ve a la línea.
- Formula una hipótesis: “creo que
usuarioesundefinedporque la petición aún no ha terminado”. - Compruébala con un
console.logo un breakpoint justo antes del fallo. Compara lo que esperas con lo que hay. - Acota. Si no sabes dónde está, divide: pon un log a mitad del camino. ¿Los datos ya están mal ahí? Entonces el fallo está antes. Repite.
- Arregla una sola cosa y vuelve a probar.
- Explícalo en voz alta. Contarle el código línea a línea a alguien (o a un pato de goma) hace que veas el error tú mismo. Se llama rubber duck debugging y funciona de verdad.
Un ejemplo con un bug
Esta función debería calcular la media de unas notas, pero devuelve NaN:
function calcularMedia(notas) {
let suma = 0;
for (let i = 0; i <= notas.length; i++) {
suma += notas[i];
}
return suma / notas.length;
}
console.log(calcularMedia([7, 8, 9])); // NaN
No hay error en rojo: es un fallo lógico, el peor tipo. Aplicamos el método. Hipótesis: “algo de lo que sumo no es un número”. Lo comprobamos con un log dentro del bucle:
vuelta 0: nota=7, suma=0
vuelta 1: nota=8, suma=7
vuelta 2: nota=9, suma=15
vuelta 3: nota=undefined, suma=24
Hay una cuarta vuelta con notas[3], que no existe. 24 + undefined es NaN. La condición i <= notas.length debe ser i < notas.length. Es el clásico error de “uno de más” (off-by-one). Pruébalo tú en el editor de esta página.
Con un breakpoint en la línea de suma += y un Watch sobre notas[i] lo habrías visto igual, sin tocar el código.
Resumen
| Herramienta | Úsala para |
|---|---|
| Leer tipo, mensaje y traza | Saber qué ha fallado y dónde |
console.log({ variable }) | Ver valores con su nombre |
console.table | Ver arrays de objetos |
console.group / time / count / assert | Ordenar, medir, contar y vigilar |
Breakpoint o debugger | Pausar y mirar todas las variables |
| Step over / into / out | Avanzar línea a línea |
| Watch | Vigilar expresiones mientras avanzas |
| Network | Revisar peticiones y respuestas |
Y el método: reproducir, leer, formular una hipótesis, comprobarla, acotar y arreglar una sola cosa cada vez.
Pruébalo tú
Cambia el código y pulsa Ejecutar (o Ctrl + Enter).
Pon a prueba lo que has aprendido
¿Te ha quedado claro? Márcala y verás tu progreso en el explorador.