From n00b to ZeroCool / Origen

JavaScript: el lenguaje que nadie planeó… y aun así sostiene la web

Historia real y guía práctica para dominar JS en React+Vite: coerción, scope, async, módulos y bugs típicos del jale diario.

Lo que vale la pena leer aquí

undefined is not a function

Intro con gancho

Son las 11:47 pm. Tu laptop ya pide jubilación, el ventilador suena como dron y tú nomás querías cerrar el deploy. Abres DevTools y ahí está el madrazo:

undefined is not a function

Y lo peor: hace una hora “sí jalaba”. El jefe cae con el clásico: “es un cambio chiquito, ¿no?” y tú viendo cómo una app entera se te voltea por una línea.

Esa es la relación con JavaScript: improvisado, lleno de decisiones raras… pero es el pegamento de la web. Si vas en la ruta From HTML to Fullstack, JS es el puente entre “puse un botón” y “esto ya es una app con estados, requests, errores y deadlines”.

Qué vas a aprender

  • Por qué JavaScript terminó dominando (y cómo su historia se nota en tus bugs).
  • 6 trampas que rompen apps reales: coerción, this, scope, mutación, async, módulos.
  • Un setup rápido con Vite + React para practicar sin pelearte con tooling viejo.
  • Reglas de jale para escribir JS más predecible en frontend.
  • Snippets y mini-ejercicios que se sienten como tickets de verdad.

Contexto práctico

Si vienes de HTML/CSS, el primer golpe con JavaScript suele ser uno de estos:

  1. “¿Por qué esto no truena hasta runtime?”
  2. “¿Por qué [] + {} da algo que parece broma?”
  3. “¿Por qué mi console.log sale raro con setTimeout?”

Y encima está la vida real:

  • El PM ya mandó WhatsApp: “urge para mañana, el cliente lo quiere en production”.
  • Te pasaron un snippet de StackOverflow del 2017 con la frase maldita: “funciona, nomás no lo muevas”.
  • Estás chambeando entre pendientes, con CFE coqueteando con el apagón.

La neta: JS no se domina coleccionando rarezas. Se domina entendiendo cómo toma decisiones y poniéndole barandales a tu workflow.

Paso a paso: domina lo que de verdad te rompe

1) Arranca un playground serio (React + Vite)

Probar en un archivo suelto sirve… hasta que te topas con módulos, bundling y el DOM real. Mejor practica donde vive tu stack hoy.

npm create vite@latest js-real-life -- --template react
cd js-real-life
npm install
npm run dev

Abre src/App.jsx y deja un área para hacer pruebas sin ensuciar todo.

export default function App() {
  return (
    <main style={{ padding: 16, fontFamily: 'system-ui' }}>
      <h1>JS Real Life</h1>
      <p>Abre la consola. Aquí se rompen cosas con dignidad.</p>
    </main>
  );
}

Decisión práctica: si tu código vive en módulos ES dentro de un bundler moderno, tu práctica también.

2) Coerción: cuando JS “te ayuda” sin pedir permiso

JS convierte tipos automáticamente. A veces te salva. A veces te mete un bug silencioso que aparece justo cuando ya hiciste deploy.

Pruébalo en consola:

console.log('5' + 1); // '51'
console.log('5' - 1); // 4
console.log(false == 0); // true
console.log(false === 0); // false

Regla de jale: usa === casi siempre. Deja == para casos raros y bien justificados (y ojalá comentados).

Caso típico de UI (inputs):

const quantity = '2';
const price = 199;

// Bug común:
const total = quantity * price; // 398 (aquí “funciona”)
const label = 'Total: $' + total; // 'Total: $398'

// Pero si alguien cambia * por +:
const broken = quantity + price; // '2199'

Decisión práctica: convierte desde la entrada. No esperes “a ver si pega”.

const quantityNumber = Number(quantity);
if (Number.isNaN(quantityNumber)) throw new Error('Cantidad inválida');

3) let, const y el scope: defensa contra bugs fantasmas

var tiene scope de función y hoisting que se presta a confusión. En frontend moderno, usarlo es comprarte talacha gratis.

Ejemplo clásico que luego se vuelve ticket:

for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log('var i:', i), 0);
}
// imprime 3, 3, 3

for (let j = 0; j < 3; j++) {
  setTimeout(() => console.log('let j:', j), 0);
}
// imprime 0, 1, 2

Regla de jale:

  • const por default.
  • let solo si reasignas.
  • var solo si estás tocando legacy (y con cuidado, porque luego nadie lo “rollbackea”).

4) Objetos/arrays: mutación silenciosa y “¿por qué se movió todo?”

En React esto pega durísimo. Mutar estado puede dejarte UI que no re-renderiza o, peor, inconsistencias que no se ven hasta que alguien hace click en el flujo raro.

const user = { name: 'Ana', role: 'admin' };
const alias = user;

alias.role = 'viewer';
console.log(user.role); // 'viewer' (sorpresa)

Regla de jale: si quieres copiar, copia.

const copy = { ...user, role: 'viewer' };

Con arrays:

const items = [1, 2, 3];
const next = items.map(x => x * 2); // no muta

items.push(4); // muta

Decisión práctica en React: evita push, splice, mutaciones directas. Prefiere map, filter, toSorted (si tu target lo soporta) o copias con spread.

5) this: el villano de los callbacks (y por qué casi ya no lo usas)

this depende de cómo llamas a la función, no de dónde está escrita. Y eso rompe cuando pasas funciones como callbacks.

const obj = {
  name: 'Byte',
  say() {
    return `Hola ${this.name}`;
  }
};

const fn = obj.say;
console.log(fn()); // 'Hola undefined' (en strict mode)

Soluciones reales:

  • Arrow functions para callbacks.
  • .bind(obj) cuando de plano lo necesitas.
const safe = obj.say.bind(obj);
console.log(safe()); // 'Hola Byte'

Decisión práctica: en React funcional casi no necesitas this. Si te está dando guerra, probablemente tu diseño pide refactor, no hacks.

6) Async: promesas, microtareas y el bug de “ya quedó… ah no”

Lo más común: olvidar await o asumir que todo corre en orden “como se ve”.

const wait = (ms) => new Promise(r => setTimeout(r, ms));

async function run() {
  console.log('A');
  wait(10); // bug: no esperaste
  console.log('B');
}
run();

Arreglado:

async function run() {
  console.log('A');
  await wait(10);
  console.log('B');
}

Caso real en UI (fetch + estado):

async function loadUser() {
  const res = await fetch('/api/user');
  if (!res.ok) throw new Error('Falló la carga');
  return res.json();
}

Decisión práctica: maneja error y loading aunque sea básico.

  • loading para evitar doble click.
  • try/catch para darle feedback a la banda.

7) Módulos ES: deja de vivir en el caos del global

Antes todo vivía en global. Hoy, con Vite, casi todo es módulo. Si algo “no existe”, muchas veces es porque no lo importaste.

// utils/math.js
export const sum = (a, b) => a + b;
// App.jsx
import { sum } from './utils/math';
console.log(sum(2, 3));

Regla de jale: si ves referencias “mágicas”, sospecha. Imports explícitos te salvan cuando llega el refactor de última hora.

JavaScript: el lenguaje que nadie planeó… y aun así sostiene la web - visual explicativa 1
Visual de apoyo: Intro con gancho

Screenshots sugeridos

  • Consola del navegador con ejemplos de coerción ('5'+1 vs '5'-1).
  • DevTools mostrando el call stack de un error típico (undefined is not a function).
  • Vite corriendo en localhost y la estructura de carpetas (src, main.jsx, App.jsx).
  • Un ejemplo de React re-render: antes/después de mutar vs copiar un array.

Errores comunes + solución

Error 1: “Mi if entra cuando no debería”

Síntoma: validaciones raras con strings vacíos, 0, null.

if ('0') console.log('entra'); // entra
if (0) console.log('entra');   // no entra

Solución: define reglas explícitas.

const value = '0';
const hasValue = value !== '' && value !== null && value !== undefined;

Error 2: “Cambié el estado pero React no actualiza”

Síntoma: UI no se refresca.

Causa típica: mutación directa.

Solución: crea una copia.

setItems(prev => prev.map(x => x.id === id ? { ...x, done: true } : x));

Error 3: “Me marca CORS / fetch falla y ni sé por qué”

Síntoma: TypeError: Failed to fetch.

Solución práctica (frontend):

  • Revisa la pestaña Network.
  • Verifica URL y método.
  • En dev, usa proxy si aplica (cuando ya haya backend). Si no hay backend, mockea.
const data = await Promise.resolve({ name: 'Mock' });

Error 4: “map truena: cannot read properties of undefined”

Síntoma: users.map cuando users todavía no llega.

Solución: default seguro.

const usersSafe = users ?? [];
return usersSafe.map(u => u.name);

Error 5: “No entiendo por qué mi console.log sale desordenado”

Causa: async + event loop.

Solución: marca el flujo y usa await.

console.log('antes');
await doThing();
console.log('después');
JavaScript: el lenguaje que nadie planeó… y aun así sostiene la web - visual explicativa 2
Visual de apoyo: Qué vas a aprender

Checklist final

  • Uso === y convierto tipos desde la entrada (inputs, query params).
  • const por default; let solo si reasigno; evito var.
  • No muté arrays/objetos que viven en estado; uso copias (..., map, filter).
  • En async, cada promesa importante tiene await (o se maneja con .then/.catch).
  • Mis funciones no dependen de this salvo casos justificados.
  • Todo lo que uso está importado (módulos ES), cero globals mágicos.
  • Puedo explicar qué hace mi código con 2 logs bien puestos (sin rezarle al debugger).

FAQ

1) ¿Por qué dicen que JavaScript “nadie lo planeó”?

Porque se armó rápido para el navegador, con decisiones a contrarreloj y compatibilidad como prioridad. Luego crecieron frameworks, módulos y tooling encima… sin poder romper la web que ya existía.

2) ¿Entonces JavaScript es “malo”?

No. Es un lenguaje con historia pesada y tradeoffs raros. Si le pones barandales (buenas prácticas, linting, tests), te deja ser bien productivo.

3) ¿Qué me conviene primero: aprender JS “vanilla” o irme directo a React?

Un básico sólido de JS (tipos, funciones, arrays, objetos, async) te ahorra sufrimiento en React. Pero se vale aprenderlos en paralelo: montas mini componentes, rompes cosas y entiendes por qué.

4) ¿Qué parte de JS pega más en frontend?

Async (fetch y estados de carga), mutación (estado/UI) y coerción (inputs). De ahí salen muchos bugs “misteriosos”.

5) ¿TypeScript arregla esto?

Ayuda muchísimo (tipos, autocomplete, refactors). Pero no reemplaza entender coerción, scope, mutación y async. Si no entiendes JS, TS solo te grita con mejores mensajes.

Siguiente episodio: teaser

Ya con JS más domesticado, toca usarlo para UI de verdad.
Seguimos con React en Vite: componentes, estado y el primer workflow que aguanta cambios sin romperse cada viernes.