Módulo 00 // Unidad 01

El navegador como entorno de ejecución

Dónde vive JavaScript, qué pone el lenguaje y qué pone el navegador

Unidad para alumnos del curso

Comprobando tu sesión…

Inicia sesión con la cuenta de Google con la que estás matriculado para ver esta unidad.

Esa cuenta no tiene acceso a este curso. Prueba con otra.

No se ha podido cargar el inicio de sesión de Google. Comprueba la conexión o si algún bloqueador lo impide, y recarga la página.

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:

CÓMO SE CARGA UN SCRIPT · TIEMPO → <script> detiene el análisis el HTML espera <script defer> ejecuta al final el HTML no se detiene <script async> ejecuta al llegar análisis del HTML descarga ejecución del script
Fíjate en dónde cae la barra roja respecto a la barra del HTML: sólo con defer se ejecuta el script cuando la página ya está completa y sin haber detenido nada.
  • 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 como defer por 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:

  1. Muestra lo que tu código escribe con console.log() y los errores que se producen, con el fichero y la línea exactos.
  2. 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.

  1. Lee la línea: el error está en app.js, línea 3, al llamar a addEventListener sobre algo que es null.
  2. Busca de dónde sale ese null: la línea 2 hace document.querySelector('#boton').
  3. Comprueba el HTML: el botón existe, pero el <script> está en el <head> sin defer.
  4. Deduce el orden: cuando se ejecutó la línea 2, el navegador aún no había llegado al <body>.
Añadiendo 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 global

En 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_strict

Autoevaluación

Si document no forma parte del lenguaje JavaScript, ¿por qué puedes usarlo en cualquier script de una página?
Porque lo proporciona el navegador, no el motor. El motor ejecuta el lenguaje (variables, funciones, objetos) y el navegador le inyecta objetos globales como 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?
El script se ejecutó antes de que el navegador analizara el resto del HTML. Un <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?
En cuándo se ejecuta. 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.