Juegos en Red - Grado en Diseño y Desarrollo de Videojuegos
2026-09-09
JavaScript es el lenguaje fundamental para desarrollo web interactivo y videojuegos web.
Características principales:
Aplicaciones:
| Versión | Año | Características |
|---|---|---|
| ES5 | 2009 | Popularizó JavaScript |
| ES2015 (ES6) | 2015 | Clases, módulos, arrow functions, promises |
| ES2016+ | 2016-presente | Actualizaciones anuales con compatibilidad |
¡Importante!
Aunque la sintaxis recuerda a Java, son lenguajes completamente diferentes.
DOM (Document Object Model)
document.getElementById('boton') accede a un elementoBOM (Browser Object Model)
window.document es el DOMPara videojuegos:
Herramientas de desarrollo:
Backend y comunicación:
Node.js permite ejecutar JavaScript fuera del navegador.
Instalación:
Verificar instalación:
package.json con configuración por defecto (sin preguntas)Estructura recomendada:
mi-juego-web/
├── package.json # Configuración del proyecto
├── webpack.config.js # Configuración de Webpack
├── src/ # Tu código fuente
├── dist/ # Archivos compilados (generado)
└── public/ # HTML y recursos estáticos
└── index.html
Dependencias de producción:
Dependencias de desarrollo:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
// **entry**: Punto de entrada de la aplicación
entry: './src/index.js',
// **output**: Dónde se guarda el bundle generado
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
clean: true // Limpia la carpeta 'dist' antes de cada build
},
// **plugins**: Añade funcionalidades extra (como generar el HTML automáticamente)
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html', // Usa esta plantilla HTML
filename: 'index.html' // Nombre del archivo generado
})
],
// **devServer**: Configuración del servidor de desarrollo con recarga automática
devServer: {
static: {
directory: path.join(__dirname, 'dist')
},
compress: true,
port: 3000,
open: true, // Abre el navegador automáticamente
hot: true // Habilita hot-reload
},
// Configuración para trabajar con archivos JS modernos
module: {
rules: [
{
test: /\.js$/, // Aplica a archivos .js
exclude: /node_modules/, // Ignora node_modules
use: {
loader: 'babel-loader' // Transpila JS moderno a compatible
}
}
]
},
// Modo por defecto si no se especifica en el script
mode: 'development'
};package.json:
{
"scripts": {
// Construye el paquete para producción usando Webpack
"build": "webpack --mode production",
// Inicia el servidor de desarrollo con Webpack Dev Server y habilita hot-reload
"dev": "webpack serve --mode development",
// Inicia el servidor en modo producción (requiere que server.js esté correctamente configurado)
"start": "node server.js",
// Observa los cambios en los archivos y recompila automáticamente (sin servidor)
"watch": "webpack --watch"
}
}let x = 5; x = "texto";)node --use_strict en node.<head>: Se ejecutan antes de cargar el contenido (puede bloquear renderizado)<body>: Mejor práctica - el HTML ya está cargado<html>
<head>
<!-- Script en head - se ejecuta inmediatamente -->
<script src="js/config.js"></script>
</head>
<body>
<!-- Contenido HTML -->
<!-- Scripts al final - mejor rendimiento (HTML ya cargado) -->
<script src="js/game.js"></script>
<!-- async: descarga paralela, ejecuta en cuanto se descarga -->
<script src="js/game.js" async></script>
<!-- defer: descarga paralela, ejecuta después del HTML -->
<script src="js/game.js" defer></script>
</body>
</html>// Escribir en el documento HTML (no recomendado)
document.write('Texto');
// Consola del navegador (debugging) - Pulsa F12 para verla
console.log('Información de debug');
console.error('Error crítico');
console.warn('Advertencia');
// También puedes mostrar objetos
console.log('Jugador:', { nombre: 'Juan', vida: 100 });{} donde se declaran// const - valor inmutable (no se puede reasignar), block scope
const MAX_LIVES = 3;
const GAME_CONFIG = {
width: 800,
height: 600
};
// let - valor mutable (se puede reasignar), block scope
let currentLives = MAX_LIVES;
let playerPosition = { x: 0, y: 0 };
// var - evitar en código nuevo (function scope - problemas)
var oldStyle = "no recomendado";
// Ejemplos de uso
currentLives = 2; // OK - let permite reasignar
MAX_LIVES = 5; // ERROR - const no permite reasignarlet, const): Variable solo existe dentro de las llaves {} donde se declaróvar): Variable existe en toda la función, ignorando bloques {}function ejemploScope() {
if (true) {
let bloqueVariable = "solo aquí";
var funcionVariable = "toda la función";
}
// console.log(bloqueVariable); // ERROR - no existe fuera del if
console.log(funcionVariable); // OK - var tiene function scope
}
// Sin modo estricto
function peligro() {
sinDeclarar = "ups"; // Crea variable global - BAD!
}"", '' o `` (template literals)true o false// Number - enteros y decimales (punto flotante de 64 bits)
let score = 1000;
let health = 75.5;
let infinity = Infinity; // Valor especial
// String - cadenas de caracteres
let playerName = "Jugador1";
// Boolean - verdadero o falso
let isGameRunning = true;
let isPaused = false;
// Tipos especiales
let powerUp = null; // Ausencia intencional - "no hay powerup"
let specialAbility; // undefined - no inicializada aún`): En lugar de comillas normales${}: Inserta variables o expresiones dentro del string${} puedes poner cualquier código JavaScriptlet level = 5;
let experience = 1250;
// Interpolación de strings - inserta valores de variables
let status = `Nivel ${level} - EXP: ${experience}`;
// También puedes usar expresiones dentro de ${}
let progress = `Progreso: ${(experience / 2000) * 100}%`;
// Strings multilínea - se respetan los saltos de línea
let gameInfo = `
Jugador: ${playerName}
Nivel: ${level}
Puntuación: ${score}
Estado: ${score > 1000 ? 'Pro' : 'Novato'}
`;+, -, *, /, % (módulo/resto)>, <, >=, <= comparan valores&& (y), || (o), ! (no) para combinar condiciones// Operadores aritméticos - similares a Java
let damage = baseDamage + bonus;
let remaining = total - used;
let area = width * height;
let average = sum / count;
let remainder = value % modulo; // Resto de división
// Lógicos - combinan condiciones -> Devuelven true or false
let canAct = isAlive && !isStunned; // Y lógico, NO lógico
let shouldRespawn = isDead || health <= 0; // O lógico5 == "5" es true, pero 5 === "5" es false// Igualdad estricta (recomendado) - compara valor Y tipo
if (playerID === targetID) {
// Solo true si ambos tienen el mismo valor Y tipo
}
// Desigualdad estricta - diferente valor O tipo
if (level !== previousLevel) {
// true si son diferentes
}
// Igualdad débil (EVITAR) - hace conversión de tipos
if (score == "100") {
// true - convierte "100" a número 100. Puede causar bugs difíciles de encontrar
}
// Comparación de tipos diferentes
console.log(5 === "5"); // false - número vs string
console.log(5 == "5"); // true - convierte a mismo tipo??): Valor por defecto solo si null o undefined?.): Accede a propiedades sin error si no existen||: || también considera 0, "", false como “falsy”// Nullish coalescing - valor por defecto solo para null/undefined
let playerName = savedName ?? "Jugador Anónimo";
// Si savedName es 0 o "" NO usa el default (solo null/undefined)
// Optional chaining - evita errores si propiedades no existen
let weapon = player.inventory?.equipment?.weapon;
// Si inventory o equipment es null/undefined, retorna undefined sin error
// Logical assignment
playerName ||= "Jugador por defecto"; // Asigna solo si falsy
playerName ??= "Valor por defecto"; // Asigna solo si null/undefined
// Diferencia entre || y ??
let count = 0;
let result1 = count || 10; // 10 (0 es falsy)
let result2 = count ?? 10; // 0 (0 no es null/undefined)false en condicionesfalse, 0, "", null, undefined, NaN"0", [], {}JavaScript considera falso (falsy):
false - booleano falsonull - sin valorundefined - no definido (verificación de existencia)"" - string vacío0 - cero numéricoNaN - “Not a Number”// Creación de arrays - literal (forma más común)
let empty = [];
let numbers = [1, 2, 3, 4, 5];
let mixed = ["texto", 42, true, null]; // Tipos mezclados OK
// Constructor Array (menos común)
let inventory = new Array(10); // Crea array con 10 espacios vacíos
// Acceso y modificación por índice
console.log(numbers[0]); // 1 - primer elemento
numbers[2] = 999; // Modifica tercer elemento
// Arrays dinámicos - se expanden automáticamente
numbers[10] = 100; // Salta del índice 4 al 10
console.log(numbers.length); // 11 - ahora tiene 11 elementos
// Los índices 5-9 quedan vacíos (undefined)// Array de ejemplo
let enemies = [
{ id: 1, health: 100, type: "orc" },
{ id: 2, health: 50, type: "goblin" },
{ id: 3, health: 0, type: "orc" }
];
// filter() - selecciona elementos que cumplen condición
// Retorna nuevo array con enemigos vivos
let aliveEnemies = enemies.filter(enemy => enemy.health > 0);
// [{ id: 1, ... }, { id: 2, ... }]
// Solo los orcos
let orcs = enemies.filter(enemy => enemy.type === "orc");
// map() - transforma cada elemento
// Extrae solo la salud de cada enemigo
let healthValues = enemies.map(enemy => enemy.health);
// [100, 50, 0]true si todos los elementos cumplen condicióntrue si algún elemento cumple condición// find() - retorna PRIMER elemento que cumple condición
let firstOrc = enemies.find(enemy => enemy.type === "orc"); // { id: 1, health: 100, type: "orc" }
// findIndex() - retorna índice del primer match
let orcIndex = enemies.findIndex(enemy => enemy.type === "orc"); // 0
// every() - ¿TODOS cumplen la condición?
let allDead = enemies.every(enemy => enemy.health === 0); // false -> no todos muertos
// some() - ¿ALGUNO cumple la condición?
let someDead = enemies.some(enemy => enemy.health === 0); // true -> al menos un muerto
// reduce() - reduce array a un solo valor
// Suma total de la salud de todos los enemigos
let totalHealth = enemies.reduce((sum, enemy) =>
sum + enemy.health, 0); // 0 es valor inicial
// 150 (100 + 50 + 0)// push() - añadir al final (modifica original)
enemies.push({ id: 4, health: 75, type: "troll" });
// shift() - quitar del principio
let firstEnemy = enemies.shift();
// pop() - quitar del final
let lastEnemy = enemies.pop();
// splice(inicio, cantidad) - eliminar elementos
enemies.splice(1, 2); // Eliminar 2 elementos desde índice 1
// splice(inicio, 0, elemento) - insertar sin eliminar
enemies.splice(1, 0, newEnemy); // Insertar en índice 1
// unshift() - añadir al principio
enemies.unshift({ id: 0, health: 50, type: "scout" });...): Captura “el resto” de elementos// Extraer valores de array a variables
let coordinates = [100, 200];
let [x, y] = coordinates; // x = 100, y = 200
// Ignorar elementos
let [first, , third] = [1, 2, 3]; // first = 1, third = 3
// Rest operator - captura el resto de elementos
let [firstItem, secondItem, ...rest] = inventory;
// firstItem = primer elemento
// secondItem = segundo elemento
// rest = array con todos los demás
// En parámetros de función
function showCoords([x, y]) {
console.log(`X: ${x}, Y: ${y}`);
}true o false{}: Agrupan varias instrucciones// if - else - estructura condicional básica
if (health > 50) { // Siempre recomendado con llaves
// Se ejecuta si la condición es verdadera
statusColor = "green";
} else if (health > 20) {
// Se ejecuta si la primera es falsa y esta es verdadera
statusColor = "yellow";
} else {
// Se ejecuta si todas las anteriores son falsas
statusColor = "red";
}
// Sin llaves para una sola instrucción (no recomendado)
if (isDead) gameOver();// switch - útil cuando comparas misma variable con muchos valores
switch (gameState) {
case "menu":
// Se ejecuta si gameState === "menu"
showMenu();
break; // Sale del switch.
case "rollback": // Como no hay break se ejecuta el siguiente
rollback();
case "playing":
updateGame();
break;
default:
// Se ejecuta si ningún case coincide
handleUnknownState();
}// for tradicional - control total sobre índice
for (let i = 0; i < enemies.length; i++) {
updateEnemy(enemies[i]);
// i = 0, 1, 2, ... hasta length-1
}
// for...of - itera valores (ES2015+) - MÁS LIMPIO
for (let enemy of enemies) {
updateEnemy(enemy); // enemy es el valor directamente
}
// for...in - itera propiedades/claves (para objetos)
for (let key in gameConfig) {
console.log(key, gameConfig[key]);
// key = "width", "height", etc.
}// while - repite mientras la condición sea true
while (isGameRunning && playerLives > 0) {
processGameFrame();
// IMPORTANTE: algo dentro debe cambiar la condición
// o será un loop infinito
}
// do...while - ejecuta AL MENOS una vez
let input;
do {
input = prompt("Ingresa comando:");
} while (input !== "quit");// Declaración tradicional - se puede llamar antes de declarar
function calculateDamage(baseDamage, criticalHit) {
// Verifica si hay golpe crítico
if (criticalHit) {
return baseDamage * 2; // Retorna el doble
}
return baseDamage; // Retorna daño normal
}
// Uso
let damage = calculateDamage(50, true); // 100this: Heredan this del contexto (útil en callbacks)return)// Arrow function completa con llaves
let movePlayer = (deltaX, deltaY) => {
player.x += deltaX; // Mueve en X
player.y += deltaY; // Mueve en Y
};
// Arrow function con una expresión - retorno implícito
let isAlive = (entity) => entity.health > 0;
// Equivalente a: function(entity) { return entity.health > 0; }
// Sin parámetros - paréntesis vacíos
let generateRandomID = () => Math.random().toString(36).slice(2);
// Un parámetro - paréntesis opcionales
let double = x => x * 2;...): Captura argumentos restantes en un array// Parámetros por defecto (ES2015+)
function createEnemy(health = 100, damage = 10) {
return { health, damage };
}
// Si no pasas argumentos, usa los valores por defecto
createEnemy(); // { health: 100, damage: 10 }
createEnemy(50); // { health: 50, damage: 10 }
// Rest parameters - captura argumentos restantes en array
function logMessage(level, ...messages) {
console.log(`[${level}]`, ...messages);
}
logMessage("ERROR", "Falló", "al cargar", "recurso");
// messages = ["Falló", "al cargar", "recurso"]// Closure - función que retorna objeto con métodos
function createCounter(initialValue = 0) {
// Variable privada - solo accesible dentro
let count = initialValue;
// Retorna objeto con métodos que acceden
// a 'count'
return {
// Incrementa y retorna nuevo valor
increment: () => ++count,
// Decrementa y retorna nuevo valor
decrement: () => --count,
// Retorna valor actual
getValue: () => count
};
}// Manejo de errores al cargar partida guardada
try {
// Intenta parsear JSON - puede fallar si está corrupto
let gameData = JSON.parse(savedGameString);
loadGame(gameData); // Carga el juego
} catch (error) {
// Se ejecuta SOLO si hay error en try
console.error('Error loading game:', error.message);
showErrorDialog('No se pudo cargar la partida');
} finally { // Opcional
// Se ejecuta SIEMPRE (con o sin error)
hideLoadingSpinner();
}// Función que valida input y lanza errores si es inválido
function validatePlayerInput(input) {
// Verifica que no esté vacío
if (!input || input.trim() === '') {
throw new Error('El nombre no puede estar vacío');
}
// Verifica longitud máxima
if (input.length > 20) {
throw new Error('El nombre es demasiado largo');
}
// Si pasa las validaciones, retorna input limpio
return input.trim();
}JSON.stringify()// Guardar datos simples (solo strings)
localStorage.setItem('playerName', 'Jugador1');
localStorage.setItem('highScore', '15000');
// Guardar objetos - primero convertir a JSON string
const gameConfig = { volume: 0.8, difficulty: 'normal' };
localStorage.setItem('gameConfig', JSON.stringify(gameConfig));
// Leer datos simples
const playerName = localStorage.getItem('playerName');
// "Jugador1"
// Eliminar un dato específico
localStorage.removeItem('playerName');
// Eliminar TODOS los datos (usar con cuidado)
localStorage.clear();// API idéntica a localStorage
sessionStorage.setItem('tempData', 'valor temporal');
const tempData = sessionStorage.getItem('tempData');
sessionStorage.removeItem('tempData');
sessionStorage.clear();
// Ideal para estado temporal del juego actual
sessionStorage.setItem('currentGameState',
JSON.stringify(gameState));// Escribir Cookie con expiración (30 días desde ahora)
// Sin fecha es la sesión
const fecha = new Date();
fecha.setTime(fecha.getTime() + (30 * 24 * 60 * 60 * 1000));
document.cookie = `highScore=15000; expires=${fecha.toUTCString()}; path=/`;
// Leer cookie (complicado - no hay API directa)
function getCookie(nombre) {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${nombre}=`);
if (parts.length === 2) return parts.pop().split(';').shift();
return null; // No encontrada
}
// Uso
let highScore = getCookie('highScore'); // "15000"
// Eliminar cookie (establecer fecha pasada)
document.cookie = "playerName=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";| Característica | localStorage | sessionStorage | Cookies |
|---|---|---|---|
| Capacidad | ~5-10MB | ~5-10MB | 4KB |
| Persistencia | Hasta eliminar manualmente | Solo sesión actual | Configurable (expires) |
| Envío al servidor | No | No | Sí (automático) |
| API | Síncrona y simple | Síncrona y simple | Manual y compleja |
| Compartido | Entre pestañas | NO (por pestaña) | Entre pestañas |
