En la lección anterior seguiste un ciclo que conoces bien: escribir código, guardar, ejecutar node src/catalogo.js, leer la salida, corregir, repetir. Ese ciclo es correcto para construir un programa, pero es lentísimo cuando lo que quieres es responder a una pregunta pequeña: ¿cómo se comporta reduce con este array?, ¿qué devuelve exactamente Object.entries sobre una sesión?, ¿cuánto suma el aforo total del Auditorio Ribera?

Para eso existe el REPL: un intérprete interactivo donde escribes una expresión y ves el resultado al instante. Es una de las herramientas más infravaloradas del ecosistema y, bien usada, ahorra horas. En esta lección aprenderás a manejarlo con soltura y a explorar en vivo los datos de Escena Viva antes de escribirlos en un script.

Contenido

  1. Qué es un REPL y para qué sirve
  2. Arrancar el REPL y evaluar expresiones
  3. La variable _ y el resultado de las declaraciones
  4. Autocompletado, historial y navegación
  5. Los comandos de punto
  6. Cargar el catálogo de Escena Viva en el REPL
  7. Explorar y transformar los datos en vivo
  8. El modo .editor para funciones de varias líneas
  9. Ejecutar código suelto sin REPL: node -e y node -p
  10. Peculiaridades: promesas, await de nivel superior y otras sorpresas
  11. Cuándo usar el REPL y cuándo no

  1. Qué es un REPL y para qué sirve

REPL son las siglas de Read-Eval-Print-Loop: leer, evaluar, imprimir y repetir. Es un bucle que:

  1. Lee (Read) lo que escribes.
  2. Lo evalúa (Eval) como código JavaScript.
  3. Imprime (Print) el resultado.
  4. Repite (Loop) esperando la siguiente línea.
flowchart LR
    R["Read<br/>lee tu línea"] --> E["Eval<br/>ejecuta el código"]
    E --> P["Print<br/>muestra el resultado"]
    P --> L["Loop<br/>espera la siguiente"]
    L --> R

No es una peculiaridad de Node: la consola del navegador es un REPL, y también lo son python, irb o psql. Su valor está en el coste cero del experimento: probar algo no requiere crear ficheros ni recordar rutas.

Usos reales en el día a día:

  • Comprobar el comportamiento exacto de un método de array que no usas a menudo.
  • Verificar una expresión regular antes de meterla en el código.
  • Explorar la forma de unos datos que acabas de recibir.
  • Convertir valores rápidamente (fechas, céntimos a euros, bytes a megabytes).
  • Comprobar qué exporta un módulo instalado, sin leer su documentación.

  1. Arrancar el REPL y evaluar expresiones

Escribe node sin argumentos:

node
Welcome to Node.js v24.5.0.
Type ".help" for more information.
>

El símbolo > indica que el REPL espera una línea. Prueba:

> 2 + 2
4
> 'Teatro' + ' ' + 'Almendra'
'Teatro Almendra'
> [1, 2, 3].map((n) => n * 10)
[ 10, 20, 30 ]
> Math.round(2500 / 100)
25

El REPL mantiene el estado entre líneas: las variables que declaras siguen existiendo.

> const sala = 'Auditorio Ribera'
undefined
> sala.toUpperCase()
'AUDITORIO RIBERA'
> sala.length
16

También funciona con código de varias líneas: si una línea está incompleta, el símbolo cambia a ... y el REPL espera a que cierres.

> const sesion = {
...   id: 'ses-001-1',
...   aforo: 420,
...   vendidas: 180
... }
undefined
> sesion.aforo - sesion.vendidas
240

Para salir: .exit, o Ctrl+D, o Ctrl+C dos veces.

  1. La variable _ y el resultado de las declaraciones

El resultado undefined de las declaraciones

Habrás notado algo desconcertante: al escribir const sala = 'Auditorio Ribera' el REPL respondió undefined. No es un error. El REPL imprime el valor de la expresión evaluada, y en JavaScript una declaración de variable no produce ningún valor: produce undefined.

> const precio = 2500
undefined
> precio            // Ahora sí, esto es una expresión que vale 2500
2500
> let x = 10; x     // Truco: separar con ";" para ver el valor
10

Lo mismo ocurre con function, class, bucles y console.log (que devuelve undefined después de imprimir):

> console.log('hola')
hola
undefined

La primera línea es lo que imprime console.log; la segunda es su valor de retorno. Con el tiempo dejas de verlo.

La variable _

_ guarda el resultado de la última expresión evaluada. Es comodísimo para encadenar exploraciones sin declarar variables intermedias.

> [2500, 2200, 1800, 1500].reduce((suma, p) => suma + p, 0)
8000
> _ / 4
2000
> _ / 100
20

Hay una variable hermana, _error, que guarda el último error lanzado:

> JSON.parse('{ esto no es json }')
Uncaught SyntaxError: Expected property name or '}' in JSON at position 2
> _error.message
"Expected property name or '}' in JSON at position 2"

Cuidado: si asignas manualmente _ = 5, pierdes esta funcionalidad en esa sesión (el REPL te avisa). Evita usar _ como nombre de variable propia.

  1. Autocompletado, historial y navegación

Estas teclas convierten el REPL en algo verdaderamente rápido:

Tecla Efecto
Tab Autocompleta. Si hay varias opciones, pulsa dos veces para listarlas
↑ / ↓ Recorre el historial de comandos
Ctrl+R Búsqueda inversa en el historial (escribe parte del comando)
Ctrl+A / Ctrl+E Ir al principio / final de la línea
Ctrl+U Borrar la línea entera
Ctrl+L Limpiar la pantalla
Ctrl+C Cancelar la línea actual (dos veces: salir)
Ctrl+D Salir

El autocompletado es la joya. Escribe un objeto y un punto, y pulsa Tab:

> const sesion = { id: 'ses-001-1', aforo: 420, vendidas: 180 }
undefined
> sesion.
sesion.__proto__             sesion.constructor           sesion.hasOwnProperty
...
sesion.aforo                 sesion.id                    sesion.vendidas

También funciona con módulos internos, lo que lo convierte en una documentación instantánea:

> const path = require('node:path')
undefined
> path.
path.basename    path.dirname     path.extname     path.join
path.normalize   path.parse       path.relative    path.resolve

El historial se conserva entre sesiones en el fichero ~/.node_repl_history. Puedes cerrar el REPL, abrirlo mañana y recuperar con ↑ lo que probaste hoy.

  1. Los comandos de punto

Todo lo que empieza por . en el REPL es una orden del propio REPL, no JavaScript.

Comando Qué hace
.help Lista todos los comandos disponibles
.break Abandona una expresión multilínea a medias (alias: Ctrl+C)
.clear Reinicia el contexto: borra todas tus variables
.editor Entra en modo editor multilínea
.exit Sale del REPL
.load <fichero> Carga y evalúa un fichero en la sesión actual
.save <fichero> Guarda en un fichero todo lo que has escrito en la sesión

Ejemplo del ciclo completo .load / .save:

> .save informes/sesion-repl.js
Session saved to: informes/sesion-repl.js

.save escribe todas las líneas que has tecleado (no los resultados). Es una forma cómoda de rescatar una exploración que ha salido bien y convertirla en el punto de partida de un script.

.break te salva del atasco clásico:

> const evento = {
...   titulo: 'Concierto de Otono',
... .break
>

Sin .break (o Ctrl+C), el REPL se queda esperando eternamente el cierre de la llave.

.clear merece un aviso: reinicia el contexto y pierdes todas las variables definidas. Úsalo cuando quieras empezar de cero, no por error.

  1. Cargar el catálogo de Escena Viva en el REPL

Aquí está la aplicación práctica de .load. Vamos a preparar un fichero que contenga solo los datos, sin la parte de presentación ni el filtrado por argumentos, para poder cargarlo limpiamente en el REPL.

Crea src/catalogo-datos.js:

// src/catalogo-datos.js
// Solo los datos del catalogo, sin logica de presentacion.
// En el modulo 2 anadiremos aqui module.exports para poder reutilizarlo
// desde otros ficheros; de momento sirve para cargarlo en el REPL con .load

const catalogo = [
  {
    id: 'evt-001',
    titulo: 'Concierto de Otono',
    sala: 'Teatro Almendra',
    categoria: 'concierto',
    duracionMinutos: 95,
    sesiones: [
      { id: 'ses-001-1', fechaHora: '2026-10-03T20:00:00', aforo: 420, vendidas: 180, precioCentimos: 2500 },
      { id: 'ses-001-2', fechaHora: '2026-10-04T19:00:00', aforo: 420, vendidas: 96,  precioCentimos: 2200 }
    ]
  },
  {
    id: 'evt-002',
    titulo: 'Noche de Monologos',
    sala: 'Sala Boveda',
    categoria: 'humor',
    duracionMinutos: 80,
    sesiones: [
      { id: 'ses-002-1', fechaHora: '2026-10-10T21:30:00', aforo: 120, vendidas: 118, precioCentimos: 1800 },
      { id: 'ses-002-2', fechaHora: '2026-10-11T21:30:00', aforo: 120, vendidas: 45,  precioCentimos: 1800 },
      { id: 'ses-002-3', fechaHora: '2026-10-17T21:30:00', aforo: 120, vendidas: 12,  precioCentimos: 1500 }
    ]
  },
  {
    id: 'evt-003',
    titulo: 'Festival de Jazz de Primavera',
    sala: 'Auditorio Ribera',
    categoria: 'festival',
    duracionMinutos: 240,
    sesiones: [
      { id: 'ses-003-1', fechaHora: '2027-04-17T19:00:00', aforo: 900, vendidas: 640, precioCentimos: 3800 },
      { id: 'ses-003-2', fechaHora: '2027-04-18T19:00:00', aforo: 900, vendidas: 720, precioCentimos: 4200 }
    ]
  }
];

Ahora, desde la raíz del proyecto:

cd escena-viva
node
> .load src/catalogo-datos.js

El REPL muestra el contenido del fichero mientras lo evalúa línea a línea. Al terminar, la constante catalogo existe en tu sesión:

> catalogo.length
3
> catalogo[0].titulo
'Concierto de Otono'

Punto importante: .load no es lo mismo que require. .load copia y evalúa el texto del fichero dentro del contexto del REPL, como si lo hubieras tecleado. require carga un módulo aislado y solo te da lo que ese módulo exporta. Por eso .load funciona aunque el fichero no exporte nada, y por eso es la herramienta ideal para explorar. Los módulos de verdad llegan en el Módulo 2.

  1. Explorar y transformar los datos en vivo

Con el catálogo cargado, empieza lo divertido: probar transformaciones antes de escribirlas en el script.

Ver la forma de los datos

> catalogo.map((e) => e.titulo)
[ 'Concierto de Otono', 'Noche de Monologos', 'Festival de Jazz de Primavera' ]

> catalogo[1].sesiones.length
3

Para inspeccionar un objeto anidado sin que Node lo recorte con [Object]:

> console.dir(catalogo[0], { depth: null })

filter: quedarse con parte del catálogo

> catalogo.filter((e) => e.sala === 'Sala Boveda').map((e) => e.titulo)
[ 'Noche de Monologos' ]

> catalogo.filter((e) => e.duracionMinutos > 90).map((e) => e.titulo)
[ 'Concierto de Otono', 'Festival de Jazz de Primavera' ]

flatMap: aplanar todas las sesiones

Las sesiones están anidadas dentro de cada evento. Para trabajar con todas a la vez:

> const sesiones = catalogo.flatMap((e) => e.sesiones)
undefined
> sesiones.length
7

Y si necesitamos saber a qué evento pertenece cada una, añadimos el dato al aplanar:

> const sesiones = catalogo.flatMap((e) => e.sesiones.map((s) => ({ ...s, evento: e.titulo, sala: e.sala })))
undefined
> sesiones[0]
{
  id: 'ses-001-1',
  fechaHora: '2026-10-03T20:00:00',
  aforo: 420,
  vendidas: 180,
  precioCentimos: 2500,
  evento: 'Concierto de Otono',
  sala: 'Teatro Almendra'
}

El operador ... (propagación) copia todas las propiedades del objeto original y añade las nuevas. Lo estudiaremos formalmente en la siguiente lección.

reduce: totales de negocio

> sesiones.reduce((suma, s) => suma + s.aforo, 0)
3000

> sesiones.reduce((suma, s) => suma + s.vendidas, 0)
1811

> const ingresos = sesiones.reduce((suma, s) => suma + s.vendidas * s.precioCentimos, 0)
undefined
> ingresos
6428600
> ingresos / 100
64286

Sesenta y cuatro mil doscientos ochenta y seis euros de recaudación acumulada. Este es exactamente el tipo de pregunta que respondes en veinte segundos en el REPL y que tardarías cinco minutos en programar, guardar y ejecutar.

sort y find

> sesiones.filter((s) => s.sala === 'Auditorio Ribera').map((s) => s.id)
[ 'ses-003-1', 'ses-003-2' ]

> [...sesiones].sort((a, b) => b.precioCentimos - a.precioCentimos)[0].id
'ses-003-2'

> sesiones.find((s) => s.vendidas / s.aforo < 0.2)
{ id: 'ses-002-3', ... }

Nota el [...sesiones] antes de sort: sort modifica el array original. Copiarlo primero evita alterar los datos que estás explorando. Es un error clásico que el REPL te ayuda a descubrir.

Ocupación por sala

> const porSala = {}
undefined
> for (const s of sesiones) { porSala[s.sala] = (porSala[s.sala] ?? 0) + s.vendidas }
undefined
> porSala
{ 'Teatro Almendra': 276, 'Sala Boveda': 175, 'Auditorio Ribera': 1360 }
> console.table(porSala)

El operador ?? devuelve el valor de la derecha solo si el de la izquierda es null o undefined. Lo veremos en detalle en la próxima lección.

  1. El modo .editor para funciones de varias líneas

Escribir una función de diez líneas en el REPL línea a línea es incómodo: si te equivocas en la tercera, no puedes volver atrás. Para eso está .editor.

> .editor
// Entering editor mode (Ctrl+D to finish, Ctrl+C to cancel)
function resumenSala(eventos, sala) {
  const sesiones = eventos
    .filter((evento) => evento.sala === sala)
    .flatMap((evento) => evento.sesiones);

  const aforo = sesiones.reduce((total, s) => total + s.aforo, 0);
  const vendidas = sesiones.reduce((total, s) => total + s.vendidas, 0);

  return {
    sala,
    sesiones: sesiones.length,
    aforo,
    vendidas,
    ocupacion: `${Math.round((vendidas / aforo) * 100)}%`
  };
}

Pulsa Ctrl+D para salir del modo editor y evaluar todo el bloque de golpe. Luego pruébala:

> resumenSala(catalogo, 'Teatro Almendra')
{ sala: 'Teatro Almendra', sesiones: 2, aforo: 840, vendidas: 276, ocupacion: '33%' }

> ['Teatro Almendra', 'Sala Boveda', 'Auditorio Ribera'].map((s) => resumenSala(catalogo, s))
> console.table(['Teatro Almendra', 'Sala Boveda', 'Auditorio Ribera'].map((s) => resumenSala(catalogo, s)))
┌─────────┬────────────────────┬──────────┬───────┬──────────┬───────────┐
│ (index) │ sala               │ sesiones │ aforo │ vendidas │ ocupacion │
├─────────┼────────────────────┼──────────┼───────┼──────────┼───────────┤
│ 0       │ 'Teatro Almendra'  │ 2        │ 840   │ 276      │ '33%'     │
│ 1       │ 'Sala Boveda'      │ 3        │ 360   │ 175      │ '49%'     │
│ 2       │ 'Auditorio Ribera' │ 2        │ 1800  │ 1360     │ '76%'     │
└─────────┴────────────────────┴──────────┴───────┴──────────┴───────────┘

Cuando la función funciona, .save informes/exploracion.js guarda la sesión y ya tienes el código listo para pegarlo en src/.

Ctrl+D termina, Ctrl+C cancela. Confundirlos hace perder el bloque entero. En Windows, si Ctrl+D no responde, prueba con Ctrl+Z seguido de Enter.

  1. Ejecutar código suelto sin REPL: node -e y node -p

A veces solo quieres una respuesta y no una sesión interactiva. Node acepta código directamente desde la línea de comandos.

Opción Significado Imprime el resultado
node -e "código" evaluate: ejecuta el código No (usa console.log)
node -p "código" print: ejecuta e imprime el valor Sí, automáticamente
# -e: hay que imprimir explícitamente
node -e "console.log(2 + 2)"
# 4

# -p: imprime el valor de la expresión
node -p "2 + 2"
# 4

# Consultar la versión desde un script de despliegue
node -p "process.version"
# v24.5.0

# Comprobar si un paquete está instalado y qué versión tiene
node -p "require('express/package.json').version"

Un caso muy práctico: procesar un fichero JSON desde la terminal sin instalar herramientas adicionales.

# Cuántos eventos hay en el fichero semilla
node -p "require('./datos/eventos.json').length"

# Títulos de los eventos, uno por línea
node -e "require('./datos/eventos.json').forEach(e => console.log(e.titulo))"

Node sabe leer un .json con require y devolverlo ya convertido en objeto. Este truco te resolverá muchas comprobaciones rápidas a partir del Módulo 3, cuando el fichero datos/eventos.json sea la fuente real del catálogo.

También puedes encadenar el REPL con una tubería, aunque en ese caso no verás el símbolo >:

echo "console.log(process.platform)" | node
# linux

  1. Peculiaridades: promesas, await de nivel superior y otras sorpresas

Promesas y await

El REPL de Node soporta await en el nivel superior, algo que en un fichero .js de tipo CommonJS no se puede hacer. Esto lo convierte en un banco de pruebas excelente para código asíncrono (tema del Módulo 2).

> const fs = require('node:fs/promises')
undefined
> const texto = await fs.readFile('src/catalogo-datos.js', 'utf8')
undefined
> texto.length
1893

Si no usas await, el REPL te muestra la promesa, no su valor:

> fs.readFile('src/catalogo-datos.js', 'utf8')
Promise { <pending> }

Poco después el REPL imprime el resultado resuelto por su cuenta, lo que puede parecer que "aparece de la nada". No te alarmes: es el REPL informando de que la promesa se ha resuelto.

Otras peculiaridades que conviene conocer

Situación Qué ocurre Por qué
{ a: 1 } devuelve 1 El REPL interpreta {} como un bloque, no como un objeto Envuélvelo en paréntesis: ({ a: 1 })
let x = 1 dos veces da error Redeclaración en el mismo ámbito Usa var, o .clear, o cambia de nombre
Un array largo se recorta con ... 73 more items Límite de impresión del inspector console.dir(arr, { maxArrayLength: null })
Objetos profundos muestran [Object] Profundidad de inspección por defecto: 2 console.dir(obj, { depth: null })
El REPL no ve las variables de un fichero cargado con require require aísla el módulo Usa .load para explorar
Un error no cierra el REPL Los errores se capturan y se muestran Es intencionado: puedes seguir trabajando

Prueba tú mismo la primera:

> { titulo: 'Concierto de Otono' }
'Concierto de Otono'
> ({ titulo: 'Concierto de Otono' })
{ titulo: 'Concierto de Otono' }

En el primer caso, JavaScript lee { como inicio de bloque y titulo: como una etiqueta; el valor del bloque es la última expresión. Con paréntesis, se fuerza la interpretación como objeto literal.

  1. Cuándo usar el REPL y cuándo no

El REPL es una herramienta de exploración, no de construcción. Esta tabla resume la elección correcta:

Necesidad Herramienta adecuada
Comprobar qué devuelve una expresión REPL
Explorar la forma de unos datos desconocidos REPL (.load + console.dir)
Probar una transformación antes de escribirla REPL o .editor
Consultar la API de un módulo REPL + Tab
Una comprobación puntual desde un script de shell node -p
Código que debe repetirse o versionarse Un fichero en src/
Lógica de más de 30 líneas Un fichero, aunque la hayas prototipado en el REPL
Entender por qué falla un programa complejo en un punto concreto El depurador (Módulo 9)
Garantizar que algo sigue funcionando mañana Pruebas automatizadas (Módulo 9)

La regla es sencilla: si vas a necesitar el resultado más de una vez, no pertenece al REPL. Prototipa en el REPL, guarda con .save, refina en un fichero.

Y sobre el depurador: el REPL te dice qué hace una expresión aislada; el depurador te deja detener un programa en marcha, en la línea que quieras, e inspeccionar todas sus variables en ese instante. Son complementarios, no alternativos.

Errores Comunes y Consejos

Error 1: alarmarse por el undefined tras cada declaración. Es el valor de la expresión, no un fallo. const x = 5 no produce ningún valor.

Error 2: escribir { clave: valor } esperando un objeto. El REPL lo lee como un bloque. Envuélvelo en paréntesis.

Error 3: quedarse atrapado en el símbolo .... Has dejado una llave, un paréntesis o una comilla sin cerrar. Sal con .break o Ctrl+C.

Error 4: confundir .load con require. .load evalúa el texto en tu sesión (ves todas las variables). require devuelve solo lo exportado.

Error 5: usar .clear sin querer. Borra todas las variables de la sesión. Si has invertido tiempo en cargar y transformar datos, primero .save.

Error 6: ordenar con sort y estropear el array original. sort, reverse, splice, push y pop modifican el array. Copia con [...array] antes de ordenar.

Consejo 1: usa _ sin miedo. Encadenar _ es la forma más rápida de refinar un cálculo paso a paso.

Consejo 2: .save al terminar una exploración productiva. Guardar en informes/ la sesión te evita reconstruir el razonamiento mañana.

Consejo 3: el Tab es tu documentación. Antes de buscar en internet qué métodos tiene path, escribe path. y pulsa Tab dos veces.

Consejo 4: mantén un REPL abierto en una pestaña de la terminal. Con el catálogo cargado, resolver dudas cuesta segundos.

Ejercicios

Ejercicio 1: exploración guiada del catálogo

Abre el REPL, carga src/catalogo-datos.js con .load y responde a estas preguntas usando una sola expresión cada vez. Anota el comando y el resultado.

  1. ¿Cuántas sesiones hay en total en el catálogo?
  2. ¿Cuál es el precio medio de una entrada, en euros?
  3. ¿Qué eventos tienen más de dos sesiones?
  4. ¿Cuántas entradas quedan sin vender en todo el catálogo?
  5. ¿Cuál es la sesión más cara y a qué evento pertenece?

Ejercicio 2: función en modo editor

Usando .editor, escribe una función sesionesPorMes(eventos) que devuelva un objeto donde cada clave sea el mes en formato AAAA-MM y el valor el número de sesiones programadas ese mes. Pruébala con el catálogo y muestra el resultado con console.table. Después guarda la sesión en informes/exploracion-meses.js con .save.

Pista: sesion.fechaHora es una cadena ISO como '2026-10-03T20:00:00', así que el mes se puede extraer con slice(0, 7).

Ejercicio 3: comprobaciones desde la terminal

Sin abrir el REPL, escribe los comandos node -e o node -p que resuelvan lo siguiente:

  1. Imprimir la versión mayor de Node instalada (solo el número, por ejemplo 24).
  2. Imprimir cuántos milisegundos faltan desde ahora hasta el Concierto de Otono (2026-10-03T20:00:00).
  3. Convertir 4200 céntimos a la cadena '42.00'.
  4. Comprobar si la carpeta informes existe en el proyecto (imprimir true o false).

Soluciones

Solución 1

> .load src/catalogo-datos.js

# 1. Total de sesiones
> catalogo.reduce((total, e) => total + e.sesiones.length, 0)
7

# 2. Precio medio en euros
> catalogo.flatMap(e => e.sesiones).reduce((s, x, i, a) => s + x.precioCentimos / a.length, 0) / 100
25.428571428571427

Alternativa más legible, en dos pasos con _:

> catalogo.flatMap(e => e.sesiones).map(s => s.precioCentimos)
[ 2500, 2200, 1800, 1800, 1500, 3800, 4200 ]
> _.reduce((a, b) => a + b, 0) / _.length / 100
25.428571428571427

(17.800 céntimos entre 7 sesiones = 2.542,86 céntimos, es decir 25,43 EUR.)

# 3. Eventos con mas de dos sesiones
> catalogo.filter(e => e.sesiones.length > 2).map(e => e.titulo)
[ 'Noche de Monologos' ]

# 4. Entradas sin vender
> catalogo.flatMap(e => e.sesiones).reduce((t, s) => t + (s.aforo - s.vendidas), 0)
1189

# 5. Sesion mas cara y su evento
> catalogo.flatMap(e => e.sesiones.map(s => ({ ...s, evento: e.titulo })))
       .sort((a, b) => b.precioCentimos - a.precioCentimos)[0]
{ id: 'ses-003-2', ..., precioCentimos: 4200, evento: 'Festival de Jazz de Primavera' }

Aquí sort sobre el resultado de flatMap es seguro: ese array es nuevo, no el original.

Solución 2

> .editor
function sesionesPorMes(eventos) {
  const conteo = {};

  for (const evento of eventos) {
    for (const sesion of evento.sesiones) {
      // '2026-10-03T20:00:00'.slice(0, 7) === '2026-10'
      const mes = sesion.fechaHora.slice(0, 7);
      conteo[mes] = (conteo[mes] ?? 0) + 1;
    }
  }

  return conteo;
}

Ctrl+D y a probar:

> sesionesPorMes(catalogo)
{ '2026-10': 5, '2027-04': 2 }

> console.table(sesionesPorMes(catalogo))
┌─────────┬────────┐
│ (index) │ Values │
├─────────┼────────┤
│ 2026-10 │ 5      │
│ 2027-04 │ 2      │
└─────────┴────────┘

> .save informes/exploracion-meses.js
Session saved to: informes/exploracion-meses.js

Ten en cuenta que .save guarda toda la sesión, incluidas las líneas fallidas. Revisa el fichero antes de reutilizarlo.

Solución 3

# 1. Version mayor de Node
node -p "process.versions.node.split('.')[0]"
# 24

# 2. Milisegundos hasta el Concierto de Otono
node -p "new Date('2026-10-03T20:00:00') - new Date()"
# 4483200000  (varía según cuándo lo ejecutes)

# 3. Centimos a cadena con dos decimales
node -p "(4200 / 100).toFixed(2)"
# 42.00

# 4. Comprobar si existe la carpeta informes
node -p "require('node:fs').existsSync('informes')"
# true

En el punto 2, restar dos objetos Date los convierte automáticamente a milisegundos desde época y devuelve la diferencia. Si prefieres días:

node -p "Math.ceil((new Date('2026-10-03T20:00:00') - Date.now()) / 86400000) + ' dias'"

Conclusión

El REPL es el laboratorio de Node.js: un entorno donde probar ideas cuesta cero. Has aprendido a arrancarlo con node, a interpretar el undefined de las declaraciones, a encadenar cálculos con _, a moverte con Tab y el historial, y a manejar los comandos de punto —.help, .editor, .save, .load, .break, .clear, .exit—.

Lo has aplicado a Escena Viva creando src/catalogo-datos.js y cargándolo con .load para explorar en vivo: aplanar sesiones con flatMap, calcular la recaudación acumulada con reduce, detectar la sesión más cara con sort o descubrir que hay 1.189 entradas por vender. También has visto node -e y node -p para comprobaciones desde la terminal, y las peculiaridades que confunden al principio: los objetos literales que necesitan paréntesis, las promesas pendientes y el await de nivel superior.

Y sobre todo tienes claro el límite: el REPL sirve para explorar; lo que debe repetirse va a un fichero, y lo que debe garantizarse va a un test.

En esa exploración han aparecido, casi de contrabando, herramientas del JavaScript moderno: flatMap, el operador de propagación ..., la coalescencia nula ??, las funciones flecha y la desestructuración. En la siguiente lección, JavaScript Moderno para Node.js, las estudiaremos una por una y con calma, siempre resolviendo un problema real del catálogo de Escena Viva. Es el repaso que sostendrá todo el código que escribas de aquí al final del curso.

Curso de Node.js: De Principiante a Avanzado

Módulo 1: Introducción a Node.js

Módulo 2: Conceptos Básicos

Módulo 3: Sistema de Archivos y E/S

Módulo 4: HTTP y Servidores Web

Módulo 5: NPM y Gestión de Paquetes

Módulo 6: Framework Express.js

Módulo 7: Bases de Datos y ORMs

Módulo 8: Autenticación y Autorización

Módulo 9: Pruebas y Depuración

Módulo 10: Temas Avanzados

Módulo 11: Despliegue y DevOps

Módulo 12: Proyectos del Mundo Real

© Copyright 2026. Todos los derechos reservados