Saltar al contenido
depuracion.js · devschool

Depurar JavaScript con DevTools

Lección 20 de 38 · 11 min de lectura · Actualizado el

En esta lección
  1. Leer un error y su stack trace
  2. Los errores más típicos
  3. La consola a fondo
  4. DevTools: parar el programa y mirar dentro
  5. Una estrategia para encontrar bugs
  6. Un ejemplo con un bug
  7. Resumen

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:

  1. Tipo: TypeError. Te dice la familia del problema.
  2. Mensaje: qué ha pasado. Aquí, que intentaste leer nombre de algo que vale undefined.
  3. 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, archivo app.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.querySelector no encontró el elemento (devuelve null): verás Cannot 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

MensajeQué significa
Assignment to constant variable.Reasignas una const
Cannot access 'x' before initializationUsas una let/const antes de su línea
Unexpected tokenError de sintaxis: falta o sobra un paréntesis, llave o coma
Unexpected token ... in JSON / Expected property nameJSON.parse con un texto que no es JSON
Maximum call stack size exceededRecursividad sin fin
Failed to fetchLa 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/timeEnd mide cuánto tarda un trozo de código.
  • count cuenta cuántas veces pasa algo (¿se llama dos veces esta función?).
  • assert avisa solo cuando una condición que debería cumplirse no se cumple.

Cuidado: en el navegador, console.log de 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, usa console.log(JSON.stringify(obj)) o structuredClone(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ñaPara qué
ConsoleVer 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ónTeclaQué hace
ResumeF8Continúa hasta el siguiente breakpoint
Step overF10Ejecuta la línea actual y para en la siguiente
Step intoF11Si la línea llama a una función, entra en ella
Step outMayús+F11Termina 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:

  1. Reprodúcelo. Consigue que el fallo ocurra siempre, con unos pasos concretos.
  2. Lee el error completo, tipo, mensaje y traza. Ve a la línea.
  3. Formula una hipótesis: “creo que usuario es undefined porque la petición aún no ha terminado”.
  4. Compruébala con un console.log o un breakpoint justo antes del fallo. Compara lo que esperas con lo que hay.
  5. 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.
  6. Arregla una sola cosa y vuelve a probar.
  7. 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 trazaSaber qué ha fallado y dónde
console.log({ variable })Ver valores con su nombre
console.tableVer arrays de objetos
console.group / time / count / assertOrdenar, medir, contar y vigilar
Breakpoint o debuggerPausar y mirar todas las variables
Step over / into / outAvanzar línea a línea
WatchVigilar expresiones mientras avanzas
NetworkRevisar 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).

depuracion.js
Consola

Pon a prueba lo que has aprendido

[JavaScript] ¿Qué error aparece al ejecutar este código?
const lista = [{ nombre: "Ana" }];
console.log(lista[1].nombre);

[JavaScript] En un stack trace, ¿qué indica la primera línea que empieza por 'at'?

[JavaScript] ¿Qué hace la instrucción debugger en tu código?

[JavaScript] Una lista que cargas con fetch sale vacía y la consola no muestra ningún error. ¿Dónde miras primero?

¿Te ha quedado claro? Márcala y verás tu progreso en el explorador.