JavaScript Basico
Los cimientos de la interactividad web. Aprende las variables, tipos de datos, operadores, estructuras de control y funciones que forman la base de todo programa en JavaScript.
Variables
Las variables son contenedores con nombre que almacenan datos. En JavaScript moderno existen tres formas de declarar variables, pero solo dos se recomiendan. const se usa para valores que no van a cambiar (constantes), y let para valores que si van a mutar durante la ejecucion del programa. La regla practica es simple: usá const por defecto, y solo cambá a let cuando necesites reasignar la variable.
// const — no se puede reasignar
const PI = 3.14159;
const nombre = "WebForge";
// PI = 3; // TypeError: Assignment to constant variable
// let — se puede reasignar
let contador = 0;
contador = 1; // OK
contador += 10; // OK, ahora vale 11
// var — NO usar en codigo moderno (explicado abajo)
var viejo = "evita esto";
La diferencia clave entre const y let es que const no permite la reasignacion. Ojo: esto no significa que el valor sea inmutable. Si const guarda un objeto o un array, si podes modificar sus propiedades o elementos internos, lo que no podes es reasignar la variable completa a un nuevo valor.
const usuario = { nombre: "Ana", edad: 25 };
usuario.edad = 26; // OK, modificamos una propiedad
usuario.email = "ana@mail"; // OK, agregamos una propiedad
// usuario = {}; // ERROR, no se puede reasignar
const colores = ["rojo", "azul"];
colores.push("verde"); // OK, mutamos el array
// colores = []; // ERROR
var es la forma antigua de declarar variables (pre-ES6) y tiene problemas serios: tiene function scope en vez de block scope, permite redeclarar la misma variable sin error, y su declaracion se eleva al tope del scope (hoisting) con valor undefined, lo que puede causar bugs dificiles de rastrear. En codigo moderno simplemente no se usa.
// Problemas de var:
// 1. Permite redeclarar (fuente de bugs)
var x = 10;
var x = 20; // sin error, sobreescribe silenciosamente
// 2. No respeta el block scope
if (true) {
var saludo = "hola";
}
console.log(saludo); // "hola" — se filtra fuera del if
// Con let, la variable queda contenida en el bloque
if (true) {
let despedida = "chau";
}
// console.log(despedida); // ReferenceError
// 3. Hoisting con undefined
console.log(y); // undefined (no error, pero confuso)
var y = 5;
// Con let/const: hoisting pero en "Temporal Dead Zone"
// console.log(z); // ReferenceError
let z = 5;
Regla de oro
Usá const siempre que puedas. Cambiá a let solo cuando la variable necesite ser reasignada. Nunca uses var. Esto hace tu código más predecible, evita bugs de scope y comunica la intención claramente a quien lea el código.
Tipos de datos
JavaScript es un lenguaje de tipado dinamico: no declarás el tipo de una variable, el tipo lo determina el valor que le asignás y puede cambiar en cualquier momento. Los tipos se dividen en dos categorías: los primitivos (inmutables, se pasan por valor) y el object (mutable, se pasa por referencia). Los primitivos son string, number, boolean, null, undefined, symbol y bigint.
// === PRIMITIVOS ===
// String — texto, entre comillas simples, dobles o backticks
const texto = "Hola mundo";
const nombre = 'Ana';
const saludo = `Hola ${nombre}`; // template literal
// Number — enteros y decimales (no hay tipo int/float separado)
const entero = 42;
const decimal = 3.14;
const negativo = -10;
const cientifico = 2.5e4; // 25000
// Boolean — true o false
const activo = true;
const visible = false;
// null — ausencia intencional de valor
const resultado = null; // "no hay valor aca, y es a proposito"
// undefined — variable declarada sin valor asignado
let sinValor;
console.log(sinValor); // undefined
// Symbol — identificador unico e inmutable
const id = Symbol("id");
const id2 = Symbol("id");
console.log(id === id2); // false (siempre son unicos)
// BigInt — numeros enteros muy grandes
const gran = 9007199254740991n; // sufijo "n"
const suma = gran + 1n;
// === OBJETO (no es primitivo) ===
// Object literal
const persona = {
nombre: "Carlos",
edad: 30,
activo: true
};
// Array (es un tipo especial de objeto)
const numeros = [1, 2, 3, 4, 5];
// typeof para consultar el tipo
typeof "hola"; // "string"
typeof 42; // "number"
typeof true; // "boolean"
typeof null; // "object" (bug historico de JS)
typeof undefined; // "undefined"
typeof {}; // "object"
typeof []; // "object" (los arrays son objetos)
typeof Symbol(); // "symbol"
typeof 42n; // "bigint"
Gotcha: typeof null
typeof null devuelve "object" en vez de "null". Es un bug histórico de JavaScript que lleva ahi desde la primera version y nunca se corrigio por compatibilidad. Para verificar null usá comparación estricta: valor === null.
La diferencia entre null y undefined confunde a muchos principiantes. undefined significa que una variable existe pero no tiene valor asignado (es el valor por defecto). null es un valor explicito que vos asignás para indicar "vacío intencionalmente". En la practica, usá undefined para "aun no definido" y null para "vacío a propósito".
let x; // undefined (no asignada)
let y = undefined; // undefined (explicito, pero raro de ver)
let z = null; // null (intencional)
// Casos donde aparece undefined:
const obj = {};
console.log(obj.nombre); // undefined (propiedad inexistente)
function sinReturn() {}
console.log(sinReturn()); // undefined (funcion sin return)
const arr = [1, 2, 3];
console.log(arr[10]); // undefined (indice fuera de rango)
Es importante entender la diferencia entre pasar por valor (primitivos) y pasar por referencia (objetos). Cuando asignás un primitivo a otra variable, se copia el valor. Cuando asignás un objeto o array, ambas variables apuntan al mismo dato en memoria, por lo que modificar una afecta a la otra.
// Primitivos: se copia el valor
let a = 10;
let b = a; // b recibe una copia de 10
b = 20;
console.log(a); // 10 (no se afecto)
// Objetos: se comparte la referencia
const original = [1, 2, 3];
const copia = original; // ambas apuntan al mismo array
copia.push(4);
console.log(original); // [1, 2, 3, 4] — se modifico!
// Para copiar un array sin compartir referencia:
const copiaReal = [...original]; // spread
const copiaReal2 = original.slice(); // slice sin args
const copiaReal3 = Array.from(original); // Array.from
// Para copiar un objeto (shallow copy):
const objOriginal = { a: 1, b: 2 };
const objCopia = { ...objOriginal }; // spread
const objCopia2 = Object.assign({}, objOriginal); // assign
Operadores
Los operadores son símbolos que realizan operaciones sobre valores y variables. Se agrupan en categorías según lo que hacen: los aritméticos para matemáticas básicas, los de comparación para evaluar condiciones, los lógicos para combinar expresiones booleanas, y el operador ternario como atajo de if/else.
Los operadores aritméticos son los más directos: suma, resta, multiplicacion, division, modulo (resto de division) y exponenciacion. Además existen los operadores de asignacion compuesta (+=, -=, *=, /=, %=) que abrevian la reasignacion. El operador ++ y -- incrementan o decrementan en 1.
// Aritmeticos
5 + 3 // 8 (suma)
10 - 4 // 6 (resta)
3 * 4 // 12 (multiplicacion)
15 / 4 // 3.75 (division)
15 % 4 // 3 (modulo / resto)
2 ** 10 // 1024 (exponenciacion)
// Asignacion compuesta
let x = 10;
x += 5; // x = 15
x -= 3; // x = 12
x *= 2; // x = 24
x /= 4; // x = 6
x %= 4; // x = 2
// Incremento / decremento
let i = 0;
i++; // i = 1 (post-incremento)
++i; // i = 2 (pre-incremento)
i--; // i = 1
// Concatenacion de strings con +
"Hola" + " " + "mundo"; // "Hola mundo"
const nombre = "Ana";
"Hola " + nombre; // "Hola Ana"
Los operadores de comparacion devuelven siempre un booleano (true o false). La diferencia entre === (igualdad estricta) y == (igualdad flexible) es crítica: === compara valor y tipo sin conversion, mientras que == intenta convertir los tipos antes de comparar, lo que genera resultados inesperados. La regla es usar siempre === (y !== para desigualdad).
// Igualdad estricta (SIEMPRE usar esta)
5 === 5 // true
5 === "5" // false (numero vs string)
null === null // true
undefined === undefined // true
null === undefined // false
// Igualdad flexible (EVITAR — resultados confusos)
5 == "5" // true (convierte el string a numero)
0 == false // true (ambos se convierten a 0)
"" == false // true (ambos se convierten a 0)
null == undefined // true (caso especial historico)
// Comparacion
5 > 3 // true
5 < 3 // false
5 >= 5 // true
3 <= 2 // false
"a" > "b" // false (compara por orden alfabetico / Unicode)
Evita == y !=
Los operadores de igualdad flexible (== / !=) aplican coercion de tipos y producen resultados dificiles de predecir. Usá siempre === y !==. La única excepción aceptable es null == undefined para verificar "nulo o indefinido" de una vez, pero valor == null es más claro que escribir valor === null || value === undefined.
Los operadores lógicos (&&, ||, !) combinan o invierten expresiones booleanas. Además del uso en condiciones, && y || se usan muchísimo para short-circuit evaluation: && devuelve el primer valor falsy, || devuelve el primer valor truthy. Esto permite patrones como valores por defecto o acceso seguro a propiedades. El operador ternario (condicion ? valorA : valorB) es un if/else en una sola línea, ideal para asignaciones condicionales simples.
// Logicos
true && true // true
true && false // false
false || true // true
false || false // false
!true // false
// Short-circuit como valor por defecto
const nombre = "";
const display = nombre || "Anonimo"; // "Anonimo"
// Short-circuit para ejecutar solo si hay valor
const usuario = { nombre: "Ana" };
usuario && console.log(usuario.nombre); // "Ana"
// Ternario — if/else en una linea
const edad = 20;
const puedeVotar = edad >= 18 ? "Si" : "No"; // "Si"
// Ternario anidado (usar con moderacion, puede ser ilegible)
const nota = 85;
const resultado = nota >= 90 ? "A"
: nota >= 80 ? "B"
: nota >= 70 ? "C"
: "Reprobado"; // "B"
// Negacion
const activo = false;
if (!activo) {
console.log("Esta inactivo");
}
Condicionales
Los condicionales permiten que tu programa tome decisiones: ejecutar un bloque de código u otro según si una condición se cumple o no. La estructura más comun es if / else if / else, que evalúa condiciones en orden y ejecuta el primer bloque cuya condición sea true. Si ninguna se cumple, entra al else final (que es opcional).
const hora = 14;
if (hora < 12) {
console.log("Buenos dias");
} else if (hora < 18) {
console.log("Buenas tardes"); // Se ejecuta esta
} else {
console.log("Buenas noches");
}
// Multiples condiciones con && y ||
const usuario = { edad: 25, rol: "admin", activo: true };
if (usuario.activo && usuario.rol === "admin") {
console.log("Acceso total concedido");
}
if (usuario.edad < 18 || usuario.rol === "admin") {
console.log("Acceso permitido");
}
// Valor truthy/falsy en condiciones
// Falsy: false, 0, "", null, undefined, NaN
// Todo lo demas es truthy
const valor = "hola";
if (valor) {
console.log("Entró porque 'hola' es truthy");
}
const vacio = "";
if (!vacio) {
console.log("Entró porque '' es falsy");
}
El switch es otra forma de escribir condicionales cuando comparás una sola variable contra muchos valores posibles. Es más legible que una cadena de else if cuando todos comparan la misma expresión. Es importante recordar que switch usa comparación estricta (===) internamente, y que necesitás break en cada case para evitar que la ejecución "se caiga" al siguiente caso (fall-through). El default es opcional y funciona como el else final.
const dia = "miercoles";
switch (dia) {
case "lunes":
case "martes":
case "miercoles":
case "jueves":
case "viernes":
console.log("Dia laboral");
break;
case "sabado":
case "domingo":
console.log("Fin de semana");
break;
default:
console.log("Dia no reconocido");
}
// Switch con agrupacion (cases sin break comparten logica)
const metodo = "GET";
switch (metodo) {
case "GET":
console.log("Obtener recurso");
break;
case "POST":
console.log("Crear recurso");
break;
case "PUT":
case "PATCH":
console.log("Actualizar recurso");
break;
case "DELETE":
console.log("Eliminar recurso");
break;
default:
console.log(`Metodo ${metodo} no soportado`);
}
if vs switch: cuando usar cada uno
Usá if/else cuando las condiciones son diferentes entre si o involucran rangos/comparaciones complejas. Usá switch cuando comparás una sola expresión contra múltiples valores exactos. Para casos de dos opciones, el operador ternario es más conciso que un if/else completo.
Loops (bucles)
Los bucles permiten repetir un bloque de código múltiples veces. JavaScript tiene varios tipos de loops, cada uno con sus casos de uso ideales. El for clásico es el más versátil y el que necesitás conocer bien. El while es mejor cuando no sabés cuántas iteraciones vas a necesitar de antemano. Y los loops modernos for...of y for...in son más legibles para iterar colecciones.
El for clásico tiene tres partes: inicializacion (let i = 0), condición (i < array.length) y actualización (i++). Se ejecuta mientras la condición sea verdadera. Es el loop más flexible porque controlás exactamente dónde empezar, cuándo parar y cómo avanzar.
// for clasico
const frutas = ["manzana", "banana", "naranja"];
for (let i = 0; i < frutas.length; i++) {
console.log(`${i}: ${frutas[i]}`);
}
// 0: manzana
// 1: banana
// 2: naranja
// Recorrer al reves
for (let i = frutas.length - 1; i >= 0; i--) {
console.log(frutas[i]);
}
// Iterar de 2 en 2
for (let i = 0; i < 10; i += 2) {
console.log(i); // 0, 2, 4, 6, 8
}
// break — salir del loop prematuramente
for (let i = 0; i < 100; i++) {
if (i === 5) break;
console.log(i); // 0, 1, 2, 3, 4
}
// continue — saltar a la siguiente iteracion
for (let i = 0; i < 5; i++) {
if (i === 2) continue;
console.log(i); // 0, 1, 3, 4
}
El while repite el bloque mientras la condición sea verdadera. Se usa cuando no sabés cuántas iteraciones necesitas. Su variante do...while ejecuta el bloque al menos una vez y despues evalúa la condición, lo que útil para validaciones de input o menús.
// while — evalua la condicion ANTES de cada iteracion
let contador = 0;
while (contador < 5) {
console.log(contador); // 0, 1, 2, 3, 4
contador++;
}
// Cuidado con el loop infinito!
// while (true) { /* nunca termina */ }
// do...while — ejecuta AL MENOS UNA VEZ
let opcion;
do {
opcion = prompt("Elegi una opcion (1-3):");
} while (opcion < 1 || opcion > 3);
// Garantiza que el prompt se muestra al menos una vez
Los loops modernos son más legibles y menos propensos a errores. for...of itera sobre los valores de cualquier iterable (arrays, strings, Maps, Sets, NodeLists) y es el loop que deberías usar por defecto para recorrer arrays. for...in itera sobre las claves (nombres de propiedades) de un objeto y está diseñado para objetos, no para arrays (donde puede dar problemas si el prototipo fue extendido).
// for...of — itera VALORES de iterables (arrays, strings, etc.)
const colores = ["rojo", "verde", "azul"];
for (const color of colores) {
console.log(color); // "rojo", "verde", "azul"
}
// Con indice (entries)
for (const [indice, color] of colores.entries()) {
console.log(`${indice}: ${color}`);
}
// Con strings
for (const letra of "hola") {
console.log(letra); // "h", "o", "l", "a"
}
// for...in — itera CLAVES de objetos
const persona = { nombre: "Ana", edad: 25, ciudad: "BA" };
for (const clave in persona) {
console.log(`${clave}: ${persona[clave]}`);
}
// "nombre: Ana", "edad: 25", "ciudad: BA"
// NO usar for...in con arrays (puede incluir claves del prototipo)
const nums = [10, 20, 30];
for (const i in nums) {
console.log(i); // "0", "1", "2" (strings, no numeros!)
}
// Mejor usa for...of para arrays
Que loop usar
Para arrays y strings usá for...of (o métodos como .forEach(), .map(), .filter() que veremos en ES6+). Para objetos usá for...in o Object.entries(). Para control fino del índice usá for clásico. Para condiciones desconocidas usá while. En la sección de ES6+ Moderno vamos a profundizar en los métodos funcionales de arrays que reemplazan a la mayoría de los loops.
Funciones
Las funciones son bloques de código reutilizables que realizan una tarea específica. Son la base de la organización en cualquier programa: te permiten evitar repetir código, abstraer lógica compleja y darle nombre a las operaciones. JavaScript tiene varias formas de definir funciones, pero todas comparten los mismos conceptos fundamentales: reciben parámetros como entrada, ejecutan un bloque de código, y pueden devolver un valor con return.
Existen dos formas principales de declarar funciones. La declaración de función usa la palabra clave function y tiene hoisting: podes llamarla antes de declararla porque el motor la "sube" al tope del scope. La expresión de función asigna una función anónima a una variable y no tiene hoisting: tenés que declararla antes de usarla. En la práctica moderna, las arrow functions (que veremos en ES6+) reemplazan a las expresiones de función en la mayoría de los casos.
// 1. Declaracion de funcion (tiene hoisting)
function saludar(nombre) {
return `Hola, ${nombre}!`;
}
console.log(saludar("Ana")); // "Hola, Ana!"
// La podes llamar antes de declararla por el hoisting:
console.log(sumar(2, 3)); // 5
function sumar(a, b) {
return a + b;
}
// 2. Expresion de funcion (NO tiene hoisting)
const restar = function(a, b) {
return a - b;
};
console.log(restar(10, 3)); // 7
// Expresion de funcion nombrada (mejor para debugging)
const multiplicar = function multiplicar(a, b) {
return a * b;
};
// 3. Arrow function (veremos en detalle en ES6+)
const dividir = (a, b) => a / b;
Los parámetros son las variables que recibe la función. En JavaScript, si llamás una función con menos argumentos de los que espera, los parámetros faltantes valen undefined. Si pasás más de los que declaramos, los extras se ignoran. Podes acceder a todos los argumentos pasados con el objeto arguments (solo en funciones clásicas, no en arrow functions), pero en código moderno se prefieren los parámetros rest (...args).
// Parametros con valores por defecto
function saludar(nombre = "Mundo") {
return `Hola, ${nombre}!`;
}
saludar(); // "Hola, Mundo!"
saludar("Carlos"); // "Hola, Carlos!"
// Parametros rest — agrupa los args extra en un array
function sumarTodos(...numeros) {
return numeros.reduce((total, n) => total + n, 0);
}
sumarTodos(1, 2, 3, 4); // 10
sumarTodos(10, 20); // 30
// Argumentos faltantes valen undefined
function ejemplo(a, b, c) {
console.log(a, b, c);
}
ejemplo(1, 2); // 1 2 undefined
// Parametros de mas se ignoran
ejemplo(1, 2, 3, 4, 5); // 1 2 3
La palabra clave return termina la ejecución de la función y devuelve un valor al llamador. Si no hay return (o está vacío), la función devuelve undefined. Una función puede tener múltiples return para diferentes caminos de ejecución, pero es buena practica que cada función tenga un solo punto de salida claro cuando sea posible.
// Multiples return
function esMayorDeEdad(edad) {
if (typeof edad !== "number" || edad < 0) {
return "Edad invalida"; // early return
}
if (edad >= 18) {
return true;
}
return false;
}
// Sin return — devuelve undefined
function soloLog(mensaje) {
console.log(mensaje);
// sin return, devuelve undefined
}
const resultado = soloLog("hola");
console.log(resultado); // undefined
// Funciones que devuelven objetos (cuidado con las llaves)
// Esto NO funciona: () => { clave: valor } // las llaves se leen como bloque
// Usa parentesis: () => ({ clave: valor })
const crearUsuario = (nombre, edad) => ({ nombre, edad });
console.log(crearUsuario("Ana", 25)); // { nombre: "Ana", edad: 25 }
// Funcion como parametro de otra funcion (callback)
function procesar(array, transformar) {
const resultado = [];
for (const item of array) {
resultado.push(transformar(item));
}
return resultado;
}
const numeros = [1, 2, 3, 4];
const dobles = procesar(numeros, n => n * 2); // [2, 4, 6, 8]
const textos = procesar(numeros, n => `Num: ${n}`); // ["Num: 1", ...]
Buenas practicas con funciones
Una función debería hacer una sola cosa y tener un nombre que describa lo que hace (calcularTotal, no procesar). Mantenélas cortas (menos de 20 líneas como regla general). Evitá efectos secundarios (modificar variables externas) cuando sea posible. Los parámetros por defecto y el early return ayudan a que el código sea más legible y robusto.
Probá en MiniDevTools
Si querés experimentar con lo que vimos en esta sección, probá JSON Formatter & Viewer, Character Counter, Text Case Converter, UUID Generator o Timestamp Converter.