En primero escribías Java, lo compilabas y lo ejecutabas en la JVM. JavaScript no tiene ese paso previo: el navegador descarga el código fuente y lo ejecuta tal cual, mezclado con la carga de la página. Entender quién ejecuta qué y en qué momento evita la mitad de los errores de las primeras semanas.
1 // Motor y navegador: dos piezas distintas
Todo navegador lleva dentro un motor de JavaScript: V8 en Chrome y Edge, SpiderMonkey en Firefox, JavaScriptCore en Safari. El motor sabe ejecutar el lenguaje: variables, funciones, objetos, bucles. Nada más.
Todo lo que tiene que ver con la página lo pone el navegador, que expone al motor un conjunto de objetos llamados APIs web:
| Pieza | Qué aporta | Algunos nombres |
|---|---|---|
| Motor de JavaScript | El lenguaje en sí | let, function, Array, JSON, Math |
| APIs del navegador | Acceso a la página y al sistema | document, window, fetch, setTimeout, localStorage |
Esta separación explica algo que despista al principio: Node.js usa el mismo motor V8, así que el lenguaje es idéntico, pero no tiene document porque allí no hay ninguna página. Y al revés: en el navegador no puedes leer ficheros del disco como harías en Java, porque el navegador no expone esa capacidad a una web cualquiera.
El lenguaje es el mismo en todas partes; lo que cambia de un entorno a otro son las APIs disponibles. En este curso, el entorno es el navegador.
2 // Dónde se escribe el código
JavaScript llega a la página con la etiqueta <script>. Puede ir el código dentro de la etiqueta, pero lo habitual es enlazar un fichero aparte:
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="utf-8">
<title>Mi primera página</title>
<script src="js/app.js" defer></script>
</head>
<body>
<h1 id="titulo">Hola</h1>
</body>
</html>// js/app.js
const titulo = document.querySelector('#titulo');
titulo.textContent = 'Hola desde JavaScript';
console.log('Script cargado');No hay main, ni clase envolvente, ni compilación. El navegador ejecuta el fichero de arriba abajo en cuanto le toca, y las variables que declares a nivel superior quedan disponibles para el resto de la página.
3 // En qué momento se ejecuta
El navegador lee el HTML de principio a fin y va construyendo la página. Cuando encuentra un <script>, lo que haga depende de sus atributos:
- Sin atributos: el navegador detiene el análisis del HTML, descarga el script, lo ejecuta y sólo entonces continúa. Si el script está en el
<head>, los elementos del<body>aún no existen. defer: descarga el script en paralelo sin detenerse y lo ejecuta cuando el HTML está completo, respetando el orden en que aparecen los scripts.async: descarga en paralelo, pero lo ejecuta en cuanto llega, interrumpiendo el análisis y sin orden garantizado. Tiene sentido para scripts independientes, como una analítica.type="module": se comporta comodeferpor defecto. Lo verás en detalle en la unidad de módulos.
El error más típico del primer día es Cannot read properties of null al hacer document.querySelector(...). Casi siempre significa que el script se ejecutó antes de que existiera el elemento. La solución no es mover el script al final del <body> a ciegas, sino entender el orden: usa defer.
4 // La consola: tu primera herramienta
Todos los navegadores traen herramientas de desarrollo (F12 o Ctrl+Mayús+I). La pestaña Consola hace dos cosas a la vez:
- Muestra lo que tu código escribe con
console.log()y los errores que se producen, con el fichero y la línea exactos. - Es un intérprete: puedes escribir JavaScript y se ejecuta sobre la página abierta, con acceso a sus variables y a
document.
console.log('Un mensaje normal');
console.warn('Un aviso');
console.error('Un error');
console.table([{ nombre: 'Ana', nota: 8 }, { nombre: 'Luis', nota: 6 }]);Hay una diferencia importante con Java: un error en tiempo de ejecución no tumba la página entera. Detiene el script en el que ocurre, y todo lo que venía después en ese script no se ejecuta, pero el HTML sigue ahí. Por eso una web «a medio funcionar» suele esconder un error en rojo en la consola.
Una página tiene un botón que debería cambiar el título, pero no hace nada al pulsarlo. La consola muestra: Uncaught TypeError: Cannot read properties of null (reading 'addEventListener') at app.js:3.
- Lee la línea: el error está en
app.js, línea 3, al llamar aaddEventListenersobre algo que esnull. - Busca de dónde sale ese
null: la línea 2 hacedocument.querySelector('#boton'). - Comprueba el HTML: el botón existe, pero el
<script>está en el<head>sindefer. - Deduce el orden: cuando se ejecutó la línea 2, el navegador aún no había llegado al
<body>.
defer a la etiqueta <script>, el código se ejecuta con el documento completo y el botón funciona. Además, como el script se detenía en la línea 3, todo lo que había después tampoco se ejecutaba: un solo error explicaba varios síntomas.5 // El modo estricto
JavaScript nació en 1995 con la idea de ser tolerante: si el código tenía un descuido, el navegador intentaba seguir adelante en lugar de protestar. Uno de esos descuidos es especialmente traicionero. Si asignas un valor a una variable que nunca has declarado, JavaScript no da error: crea en silencio una variable global.
function calcularTotal(precio) {
let total = precio;
totl = precio * 1.21; // errata: querías escribir total
return total;
}
calcularTotal(100); // 100: el IVA se ha perdido sin ningún aviso
console.log(totl); // 121: la errata ha creado una variable globalEn Java esto no compilaría. Aquí funciona, y el problema aparece mucho después, cuando dos partes del programa usan sin saberlo la misma variable global.
Para cerrar esa puerta existe el modo estricto. Se activa escribiendo la cadena 'use strict' en la primera línea del fichero (o de una función), y convierte varios de esos descuidos silenciosos en errores:
'use strict';
function calcularTotal(precio) {
let total = precio;
totl = precio * 1.21; // ReferenceError: totl is not defined
return total;
}Parece una cadena suelta que no hace nada, y es a propósito: un navegador antiguo que no conozca el modo estricto la ignora sin romper nada. Estos son los cambios que más vas a notar:
| Sin modo estricto | Con 'use strict' |
|---|---|
| Asignar a una variable sin declarar crea una global | ReferenceError |
| Asignar a una propiedad de sólo lectura no hace nada | TypeError |
| Dos parámetros con el mismo nombre se aceptan | Error de sintaxis al cargar |
this en una función suelta es window |
this es undefined |
Los módulos (<script type="module">) y las clases funcionan siempre en modo estricto, sin necesidad de escribir nada. Como el código moderno se organiza en módulos, en la práctica casi siempre trabajarás en modo estricto; conviene saber qué te está protegiendo.
6 // Para practicar
Enlaza tu primer script y provoca a propósito los dos errores de esta unidad: un script que se ejecuta antes de tiempo y una variable sin declarar. Cada ejercicio trae el enunciado en los comentarios del código.
Incluir un script con defertraining-daw-sv/modulo-00/01-el-navegador-como-entorno/01_including_scriptVariables sin declarar y modo estrictotraining-daw-sv/modulo-00/01-el-navegador-como-entorno/02_strictAutoevaluación
Si document no forma parte del lenguaje JavaScript, ¿por qué puedes usarlo en cualquier script de una página?
document, window o fetch. En Node.js el mismo motor funciona, pero document no existe porque allí no hay página.Un script en el <head> hace document.querySelector('#menu') y obtiene null, aunque el elemento está en el HTML. ¿Qué ha pasado y cómo se arregla?
<script> sin atributos detiene el análisis y se ejecuta en ese momento, cuando #menu todavía no existe. Con defer (o type="module") el script espera a que el documento esté completo.¿En qué se diferencia defer de async si los dos descargan el script sin bloquear?
defer espera a que termine el análisis del HTML y respeta el orden de los scripts; async se ejecuta en cuanto termina la descarga, interrumpiendo el análisis y sin orden garantizado. Para el código de tu aplicación casi siempre quieres defer.