Hay un momento concreto en el que todo el mundo que aprende JavaScript descubre que llenar el código de console.log() no escala. Pones uno, otro, otro más, y al final tienes veinte logs en pantalla y no sabes cuál corresponde a qué. O peor: arreglas el bug, se te olvida quitar los console.log, y los mandas a producción.
Chrome DevTools resuelve esto de raíz. Puedes pausar la ejecución del código, inspeccionar el valor de cualquier variable en ese instante, y ver exactamente qué petición de red falló y por qué. Sin tocar el código. Sin adivinar.
Qué vas a aprender
- Usar breakpoints para pausar y examinar código en ejecución
- Inspeccionar y editar el DOM en tiempo real con el panel Elements
- Analizar peticiones de red y detectar errores de API con Network
- Medir rendimiento con el panel Performance
- Detectar memory leaks con el panel Memory
- Shortcuts que reducen a la mitad el tiempo de debugging
⚠️ Lo que necesitas antes de empezar:
- JavaScript básico: variables, funciones, DOM — si aún no lo tienes, empieza por ahí
- Google Chrome instalado (versión actualizada)
- Un editor de código: VS Code funciona bien
El problema real con console.log
El console.log no es malo. El problema es usarlo como única herramienta de debugging. Aquí está el patrón que repite todo el mundo al principio:
// ❌ El debugging "a lo bestia"
function calculateTotal(items) {
console.log('items:', items);
let total = 0;
console.log('total inicial:', total);
for (let item of items) {
console.log('item actual:', item);
total += item.price;
console.log('total después de sumar:', total);
}
console.log('total final:', total);
return total;
}
El problema no es solo que es lento. Es que estás modificando el código para debuggearlo, lo cual puede camuflar el bug o introducir nuevos. Y cuando termines, tienes que acordarte de quitar todos esos logs. Aquí es donde la mayoría se atasca durante meses.
Proyecto: Bug Hunter Dashboard
Vamos a crear una pequeña app con tres bugs intencionales. El objetivo es encontrarlos y arreglarlos usando solo DevTools, sin tocar el código a ciegas:
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Bug Hunter Dashboard</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<header>
<h1>🔍 Bug Hunter Dashboard</h1>
<p>Encuentra y arregla los bugs usando Chrome DevTools</p>
</header>
<main>
<!-- Sección 1: Calculadora con bugs -->
<section id="calculator">
<h2>🧮 Calculadora (tiene bugs)</h2>
<div class="calculator-container">
<input type="number" id="num1" placeholder="Número 1">
<input type="number" id="num2" placeholder="Número 2">
<button onclick="calculate()">Sumar</button>
<div id="result">Resultado: -</div>
</div>
</section>
<!-- Sección 2: Lista de tareas con errores -->
<section id="todo-list">
<h2>📝 Lista de Tareas (también tiene bugs)</h2>
<div class="todo-container">
<input type="text" id="todo-input" placeholder="Nueva tarea...">
<button onclick="addTask()">Agregar</button>
<ul id="task-list"></ul>
</div>
</section>
<!-- Sección 3: API caller con fallos -->
<section id="api-section">
<h2>🌐 API Caller (fallos de red)</h2>
<button onclick="fetchUserData()">Cargar Usuarios</button>
<div id="loading" style="display: none;">Cargando...</div>
<div id="user-data"></div>
</section>
</main>
<script src="script.js"></script>
</body>
</html>
// 🐛 Bug #1: Calculadora suma strings en lugar de números
function calculate() {
const num1 = document.getElementById('num1').value;
const num2 = document.getElementById('num2').value;
const result = num1 + num2; // ❌ Suma strings!
document.getElementById('result').textContent = 'Resultado: ' + result;
}
// 🐛 Bug #2: Error en querySelector
function addTask() {
const input = document.getElementById('todo-input');
const taskList = document.querySelector('#task-lists'); // ❌ ID incorrecto!
if (input.value.trim()) {
const li = document.createElement('li');
li.textContent = input.value;
taskList.appendChild(li); // ❌ Falla porque taskList es null
input.value = '';
}
}
// 🐛 Bug #3: API call con URL incorrecta
async function fetchUserData() {
const loading = document.getElementById('loading');
const userData = document.getElementById('user-data');
loading.style.display = 'block';
try {
// ❌ URL con typo
const response = await fetch('https://jsonplaceholder.typicode.com/userss');
const users = await response.json();
userData.innerHTML = users.map(user => `<p>${user.name}</p>`).join('');
} catch (error) {
// ❌ Error handling insuficiente
userData.innerHTML = 'Error cargando datos';
} finally {
loading.style.display = 'none';
}
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Arial', sans-serif;
line-height: 1.6;
color: #333;
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
header {
text-align: center;
margin-bottom: 40px;
padding: 20px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border-radius: 10px;
}
section {
margin-bottom: 30px;
padding: 20px;
border: 1px solid #ddd;
border-radius: 8px;
background: #f9f9f9;
}
input, button {
padding: 10px;
margin: 5px;
border: 1px solid #ccc;
border-radius: 4px;
font-size: 16px;
}
button {
background: #007bff;
color: white;
cursor: pointer;
transition: background 0.3s;
}
button:hover {
background: #0056b3;
}
#result {
margin-top: 10px;
font-weight: bold;
padding: 10px;
background: #e9ecef;
border-radius: 4px;
}
#task-list li {
list-style: none;
padding: 8px;
background: white;
margin: 5px 0;
border-radius: 4px;
border-left: 4px solid #28a745;
}
Tres bugs distintos, tres herramientas distintas de DevTools para encontrarlos. Así aprendes cuándo usar cada panel.
Cómo abrir Chrome DevTools
Antes de nada, hay que saber cómo abrir la herramienta. Tienes varias opciones:
🔧 Métodos para abrir DevTools:
- F12 - El método más rápido
- Ctrl+Shift+I (Windows/Linux) o Cmd+Option+I (Mac)
- Clic derecho en cualquier elemento → "Inspeccionar"
- Menú Chrome → Más herramientas → Herramientas para desarrolladores
La mayoría usa F12 y ya está. Una vez abierto, verás una barra de pestañas arriba. Los cuatro paneles que vas a usar más son:
Panel Console: más que un simple log
La mayoría usa la Console solo para ver console.log. Pero desde ahí puedes ejecutar JavaScript directamente sobre la página, consultar el valor de cualquier variable global, y usar atajos que no existen fuera de DevTools:
// 🔍 Inspeccionar elementos directamente
$('#num1') // Equivale a document.querySelector
$$('button') // Equivale a document.querySelectorAll
// 📊 Logs más informativos
console.table(datos) // Muestra arrays/objetos como tabla
console.group('Cálculos') // Agrupa logs
console.error('Error!') // Log con estilo de error
console.warn('Cuidado!') // Log con estilo de warning
// 🕐 Medir performance
console.time('operacion');
// ... código a medir ...
console.timeEnd('operacion');
Lo más útil que te llevas de la Console
El atajo $_ devuelve el resultado de la última expresión que evaluaste. Si ejecutas document.querySelector('.mi-clase') y quieres seguir trabajando con ese elemento, escribe $_ en lugar de repetir todo el selector.
Bug #1: la calculadora que no suma
Prueba la calculadora del dashboard: mete 5 y 3, pulsa Sumar. Resultado: "53". No 8. El bug más clásico de JavaScript: concatenar strings en lugar de sumar números.
Sin DevTools, lo resolverías así:
function calculate() {
const num1 = document.getElementById('num1').value;
console.log('num1:', num1, typeof num1); // ❌ Modificando código
const num2 = document.getElementById('num2').value;
console.log('num2:', num2, typeof num2); // ❌ Más ruido
}
Con DevTools, no necesitas tocar el código. Usas un breakpoint:
Cómo poner un breakpoint y usarlo
- Abre el panel Sources en DevTools
- En el árbol de archivos de la izquierda, busca y abre
script.js - Haz clic en el número de línea donde está
const num1 = ... - Aparece un punto azul — eso es el breakpoint
- Vuelve a la calculadora y pulsa Sumar
- La ejecución se pausa exactamente en esa línea
Con el código pausado, pasa el ratón por encima de num1. Verás que el valor es "5" entre comillas, no el número 5. En el panel de la derecha (Scope) también lo ves: tipo string. Ahí está el problema.
También puedes abrir la Console mientras el código está pausado y escribir typeof num1. Te responde "string". Con eso ya sabes exactamente qué arreglar, sin haber añadido ni una línea de log.
function calculate() {
// ✅ Convertir a números explícitamente
const num1 = parseFloat(document.getElementById('num1').value);
const num2 = parseFloat(document.getElementById('num2').value);
const result = num1 + num2;
document.getElementById('result').textContent = 'Resultado: ' + result;
}
Bug #2: el elemento que no existe
Ahora prueba añadir una tarea a la lista. No funciona. La Console te dice exactamente por qué — aquí es donde la mayoría no mira y acaba revisando el código durante media hora:
Leer el error de la Console
- Abre la Console en DevTools
- Intenta agregar una tarea
- Aparece un error en rojo:
Cannot read properties of null (reading 'appendChild') - Eso significa que
taskListesnull— el selector no encontró ningún elemento
En la Console, puedes verificarlo directamente:
// 🔍 Investigar qué elementos existen
document.querySelector('#task-lists') // null - no existe!
document.querySelector('#task-list') // <ul id="task-list"></ul> - ¡aquí está!
// 🎯 El problema: typo en el ID
// ❌ Código busca: '#task-lists' (con S)
// ✅ HTML tiene: '#task-list' (sin S)
⚠️ El error más común con querySelector
Cuando el selector no encuentra nada, devuelve null. Si luego intentas usar ese null como si fuera un elemento (null.appendChild(...)), peta. Siempre que veas este tipo de error, lo primero es probar el selector directamente en Console para ver qué devuelve.
Bug #3: la petición de red que falla
Para el tercer bug usamos el panel Network. Abre ese panel antes de pulsar "Cargar Usuarios", para que registre lo que ocurre:
Leer el panel Network
- Abre el panel Network en DevTools
- Pulsa "Cargar Usuarios" en la app
- Aparece una fila en rojo — petición fallida
- Haz clic en esa fila
- Ve a la pestaña Headers → ahí ves la URL exacta que se usó
- Ve a Response → verás el error 404 del servidor
El código usa /userss (doble s) en lugar de /users. Una letra de diferencia, imposible de ver leyendo el código. El panel Network lo deja en evidencia en dos segundos.
Los códigos de estado que más verás son: 200 (OK), 404 (URL no encontrada), 401 (no autenticado), 500 (error del servidor). Cuando una petición falla, el color rojo en Network es la primera señal.
Panel Elements: el DOM en vivo
El panel Elements es donde puedes ver el HTML real de la página y modificarlo sin recargar. Útil para probar cambios de CSS antes de escribirlos en el archivo:
🎨 Funciones útiles del panel Elements:
- Inspeccionar elemento: Clic derecho → "Inspeccionar" en cualquier parte de la página
- Editar HTML: Doble clic en cualquier tag para editarlo
- Editar CSS: Panel "Styles" a la derecha
- Add/remove clases: Panel "Styles" → .cls
- Ver computed styles: Pestaña "Computed"
/* Prueba cambiar estos estilos en el panel Elements: */
/* 🎨 Cambiar color de fondo */
header {
background: #ff6b6b; /* Rojo en lugar del gradiente */
}
/* 🔲 Cambiar tamaño de botones */
button {
padding: 15px 30px; /* Botones más grandes */
font-size: 18px;
}
Panel Performance: encontrar código lento
A veces no hay un error visible, la app simplemente va lenta. Para eso está el panel Performance. Permite grabar lo que hace el navegador durante unos segundos y ver exactamente dónde se gasta el tiempo:
// 🐌 Función intencionalmente lenta
function slowFunction() {
let result = 0;
for (let i = 0; i < 1000000; i++) {
result += Math.random();
}
return result;
}
// 📊 Medir performance
console.time('Función lenta');
slowFunction();
console.timeEnd('Función lenta');
📊 Usar Performance panel:
- Abre el panel Performance
- Haz clic en Record (círculo rojo)
- Ejecuta la función lenta en la Console
- Para la grabación después de unos segundos
- Analiza el flame graph - verás dónde se gasta el tiempo
Sources Panel: breakpoints avanzados
El panel Sources es donde vive el debugging de verdad. Ya viste cómo poner un breakpoint básico. Aquí están las herramientas que lo rodean y que marcan la diferencia:
Breakpoints condicionales
Los breakpoints normales se activan siempre que pasa el código por esa línea. Pero si tienes un bucle de 1000 iteraciones y el bug solo aparece en la iteración 743, necesitas un breakpoint condicional:
function processItems(items) {
for (let i = 0; i < items.length; i++) {
const item = items[i];
// ⭐ Pon un breakpoint condicional aquí
// Condición: i === 5
console.log('Procesando item:', item);
}
}
// Probar con un array
processItems(['a', 'b', 'c', 'd', 'e', 'f', 'g']);
🎯 Crear breakpoint condicional:
- Clic derecho en el número de línea (no clic normal)
- Selecciona "Add conditional breakpoint"
- Escribe la condición:
i === 5 - Ejecuta la función - solo se pausará cuando i sea 5
Memory leaks: cuando la app se ralentiza sin error visible
Un memory leak ocurre cuando el código acumula memoria sin liberarla. La app funciona al principio, pero después de unos minutos va cada vez más lenta. El culpable más habitual son los event listeners que se añaden pero nunca se eliminan:
// 🐛 Memory leak - listeners que nunca se limpian
function createMemoryLeak() {
const element = document.createElement('div');
const data = new Array(1000000).fill('memory');
// ❌ Event listener que nunca se limpia
element.addEventListener('click', function() {
console.log(data.length); // Mantiene referencia a 'data'
});
// El elemento no se usa, pero el listener mantiene todo en memoria
}
// ✅ Versión correcta
function createProperCleanup() {
const element = document.createElement('div');
const controller = new AbortController();
element.addEventListener('click', function() {
console.log('click');
}, { signal: controller.signal });
// Cleanup when needed
controller.abort(); // Removes all listeners
}
Simular móviles con DevTools
DevTools tiene un modo de simulación de dispositivos que te permite ver cómo queda tu página en un iPhone, un Android, o cualquier resolución personalizada. Esto es lo más rápido para detectar problemas de responsive sin sacar el teléfono:
📱 Device simulation:
- Ctrl+Shift+M - Activar device toolbar
- Seleccionar dispositivo - iPhone, iPad, etc.
- Custom dimensions - Tamaños específicos
- Network throttling - Simular conexión lenta
- Rotate device - Portrait/landscape
Detectar vulnerabilidades XSS con DevTools
La Console también sirve para detectar problemas de seguridad. Si tu código usa innerHTML con datos que vienen del usuario, estás abriendo la puerta a ataques XSS. DevTools te deja verificarlo:
// 🔍 Comprobar Content Security Policy
// Si ves errores CSP en Console, significa que hay problemas de seguridad
// ⚠️ Ejemplo de código peligroso (para detectar)
function unsafeCode(userInput) {
// ❌ NUNCA hagas esto - XSS vulnerability
document.getElementById('output').innerHTML = userInput;
// ✅ Versión segura
document.getElementById('output').textContent = userInput;
}
// 🔍 Test XSS en Console
unsafeCode('<script>alert("XSS")</script>');
Shortcuts que ahorran tiempo
// 🎯 Shortcuts útiles en Console
// Limpiar console
clear(); // o Ctrl+L
// Última expresión evaluada
$_ // Resultado de la última operación
// Último elemento inspeccionado
$0 // Último elemento seleccionado
$1 // Penúltimo elemento
// Buscar funciones
dir(window) // Lista todas las propiedades de window
keys(localStorage) // Claves del localStorage
values(localStorage) // Valores del localStorage
Más bugs para practicar
La teoría se asienta practicando. Aquí tienes tres bugs más para encontrar con DevTools, cada uno con una causa distinta:
// 🐛 Bug #4: Array manipulation error
function removeItem(arr, index) {
// ❌ Problema: modifica el array original
return arr.splice(index, 1);
}
// 🐛 Bug #5: Async/await sin error handling
async function loadData() {
// ❌ Si la API falla, la app crashea
const response = await fetch('/api/data');
const data = await response.json();
return data;
}
// 🐛 Bug #6: Event listener leak
function setupButton() {
const button = document.querySelector('#my-button');
// ❌ Añade listener cada vez que se llama
button.addEventListener('click', handleClick);
}
Cómo sacarle partido al ejercicio
- Crea el Bug Hunter Dashboard con los seis bugs
- Antes de mirar el código, intenta identificar cada bug solo con DevTools
- Anota qué panel usaste para cada uno y cuánto tardaste
- La segunda vez que lo hagas, verás que tardas la mitad
El siguiente paso natural es aprender a escribir tests. Un breakpoint te dice dónde está el bug cuando ya existe. Un test te avisa antes de que llegue a producción.
El siguiente paso: que los bugs no lleguen a producción
DevTools te ayuda a encontrar bugs. Los tests te ayudan a evitar que aparezcan. Si quieres combinar las dos habilidades, empieza por aquí:
Métodos de array en JavaScript: map, filter y reduceEl siguiente bloque de JavaScript que más vas a usar, con ejemplos que puedes probar ahora mismo en la Console de DevTools