Apuntes DAM
    Tema 4 de 7DWEC · 2º DAW

    Desarrollo Web en Entorno Cliente

    Funciones, Objetos y Clases

    Declaraciones, expresiones y funciones flecha, parámetros, ámbito y closures, funciones de orden superior, objetos, copias y this, clases con campos privados, herencia, módulos, Map y Set.

    70 min lecturaIntermedio

    1. Funciones: tres formas de escribir lo mismo

    Una función es un trozo de código con nombre que recibe datos, hace algo con ellos y, si quieres, devuelve un resultado. Sirve para no repetirte y para dar nombre a las ideas: calcularIva(precio) se entiende mucho mejor que la fórmula escrita en mitad de otro cálculo.

    javascript
    1// 1. Declaración: se puede llamar incluso antes de la línea donde está escrita
    2function precioConIva(precio, iva = 0.21) {
    3  return precio * (1 + iva);
    4}
    5
    6// 2. Expresión: una función guardada en una variable
    7const saludar = function (nombre) {
    8  return "Hola, " + nombre;
    9};
    10
    11// 3. Función flecha: la forma corta, ideal para funciones pequeñas
    12const doble = (n) => n * 2;
    13const sumarTodos = (...numeros) => numeros.reduce((a, b) => a + b, 0);

    Las tres hacen lo mismo en la mayoría de casos. La diferencia práctica es que las declaraciones se pueden usar antes de escribirse (el motor las «eleva» al principio, lo que se llama hoisting) y que las funciones flecha no tienen su propio this, algo que importa dentro de objetos y que veremos más abajo. En el día a día, usa declaraciones para las funciones importantes de tu programa y flechas para los callbacks cortos.

    Fíjate en dos detalles de los parámetros. Pueden tener un valor por defecto (iva = 0.21), que se usa cuando no se pasa ese argumento. Y el parámetro rest (...numeros) recoge todos los argumentos que sobren en un array, lo que permite escribir funciones que aceptan cualquier cantidad de valores.

    2. Ámbito y closures

    Cada bloque de llaves crea un ámbito: las variables declaradas dentro con let o const solo existen ahí. Una función puede leer las variables de los ámbitos que la rodean, pero no al revés. Esto parece un detalle técnico hasta que descubres su consecuencia más interesante: los closures.

    Un closure es una función con mochila

    🎒
    La mochila
    Cuando una función se crea dentro de otra, se lleva consigo las variables que tenía alrededor.
    Aunque pase el tiempo
    La función exterior puede haber terminado hace rato: la interior sigue teniendo acceso a esas variables.
    Y nadie más la abre
    Desde fuera no se puede tocar lo que hay en la mochila. Es la forma clásica de tener datos privados.
    javascript
    1function crearSaludo(saludo) {
    2  return (nombre) => saludo + ", " + nombre;   // la flecha recuerda «saludo»
    3}
    4
    5const hola = crearSaludo("Hola");
    6const buenas = crearSaludo("Buenas tardes");
    7hola("Ana");      // "Hola, Ana"
    8buenas("Luis");   // "Buenas tardes, Luis"

    Los closures están por todas partes aunque no los nombres: cada vez que un manejador de eventos usa una variable declarada fuera de él, o un setTimeout usa una variable del código que lo programó, hay un closure trabajando. Entenderlos es entender por qué tu código funciona (o por qué no).

    3. Funciones que reciben funciones

    En JavaScript las funciones son valores como cualquier otro: se guardan en variables, se meten en arrays y se pasan como argumento. Una función que se pasa a otra para que la llame cuando toque se llama callback, y una función que recibe o devuelve funciones se llama de orden superior. map, filter, addEventListener y setTimeout son de orden superior: tú les dices qué hacer y ellas deciden cuándo.

    javascript
    1function repetir(veces, accion) {
    2  for (let i = 1; i <= veces; i++) accion(i);
    3}
    4
    5repetir(3, (n) => console.log("Vuelta", n));
    6
    7const botones = document.querySelectorAll("button");
    8botones.forEach((boton) => boton.addEventListener("click", () => boton.classList.toggle("activo")));

    4. Objetos

    Un objeto agrupa datos relacionados bajo un mismo nombre, como una ficha: cada dato es una propiedad con su clave y su valor, y las funciones que guarda se llaman métodos. Se accede a las propiedades con punto (alumno.nombre) o con corchetes cuando la clave está en una variable (alumno[campo]).

    javascript
    1const nombre = "Ana";
    2const alumno = {
    3  nombre,                        // atajo de { nombre: nombre }
    4  notas: [7, 8, 6],
    5  media() {
    6    return this.notas.reduce((a, b) => a + b) / this.notas.length;
    7  },
    8};
    9
    10const { notas, media } = alumno;               // desestructuración
    11const copia = { ...alumno, curso: "2º DAW" };  // propagación: copia y añade
    12const datos = structuredClone({ nombre, notas });  // copia profunda (no admite funciones)

    Copiar no es lo mismo que asignar

    Esta es probablemente la fuente de errores más frecuente con objetos. Una variable no contiene el objeto, sino una referencia a él. Si haces const b = a, a y b apuntan al mismo objeto y cambiar uno cambia el otro. La propagación { ...a } crea un objeto nuevo, pero solo copia el primer nivel: si dentro hay un array, ese array sigue siendo compartido. Para una copia completa usa structuredClone(a).

    Quién es this

    Dentro de un método, this es el objeto sobre el que se llamó: en alumno.media(), this es alumno. Las funciones flecha no tienen this propio, sino que usan el del código que las rodea. Por eso no se usan flechas para definir métodos de un objeto, pero sí dentro de ellos, por ejemplo en el callback de un map, donde queremos seguir teniendo el this del método.

    5. Clases

    Cuando necesitas crear muchos objetos con la misma forma y el mismo comportamiento, defines una clase. Si vienes de Java, la sintaxis te resultará familiar, aunque por debajo JavaScript funciona con prototipos: una clase es una forma más cómoda de escribir lo mismo.

    javascript
    1class Cuenta {
    2  #saldo = 0;                          // campo privado: inaccesible desde fuera
    3  static comision = 0.5;               // pertenece a la clase, no a cada objeto
    4
    5  constructor(titular) {
    6    this.titular = titular;
    7  }
    8
    9  ingresar(cantidad) {
    10    if (cantidad <= 0) throw new Error("La cantidad debe ser positiva");
    11    this.#saldo += cantidad;
    12  }
    13
    14  get saldo() {                        // se lee como una propiedad: cuenta.saldo
    15    return this.#saldo;
    16  }
    17}
    18
    19class CuentaJoven extends Cuenta {
    20  ingresar(cantidad) {
    21    super.ingresar(cantidad + 1);      // regalo de 1 € por ingreso
    22  }
    23}
    24
    25const cuenta = new CuentaJoven("Lucía");
    26cuenta.ingresar(50);
    27console.log(cuenta.saldo);            // 51
    • constructor se ejecuta al hacer new y prepara el objeto.
    • #campo es privado de verdad: fuera de la clase ni siquiera se puede leer.
    • get / set permiten que un cálculo se use como si fuera una propiedad.
    • static define miembros de la clase, útiles para constantes y funciones auxiliares.
    • extends y super sirven para heredar y reutilizar el comportamiento del padre.
    ⭐ [JS] 12 Aprende a CREAR CLASES en Javascript!!! - Curso JAVASCRIPT — CodingTube

    6. Módulos, Map y Set

    Cuando un programa crece, meterlo todo en un fichero se vuelve inmanejable. Los módulos permiten dividirlo: cada fichero exporta lo que quiere compartir y los demás lo importan. En el navegador se activan con <script type="module">, y cada módulo tiene su propio ámbito, así que sus variables no chocan con las de otros ficheros.

    carrito.js · app.js
    1// carrito.js
    2export const IVA = 0.21;
    3export function total(lineas) { /* … */ }
    4export default class Carrito { /* … */ }
    5
    6// app.js
    7import Carrito, { IVA, total } from "./carrito.js";

    Por último, dos estructuras que complementan a arrays y objetos. Un Set es una colección sin repetidos: [...new Set(lista)] es la forma más corta de quitar duplicados de un array. Un Map es como un objeto pensado para usarse como diccionario: sus claves pueden ser de cualquier tipo, mantiene el orden de inserción y tiene métodos claros (set, get, has, delete, size).

    Necesito…Usa
    Una lista ordenada con posibles repetidosArray
    Valores únicos, o saber rápido si algo estáSet
    Un diccionario clave → valor que cambia muchoMap
    Una ficha con propiedades fijas (un usuario, un producto)Objeto

    Menos clases de las que crees

    En JavaScript no todo tiene que ser una clase. Muchas veces bastan funciones y objetos literales, que son más cortos y fáciles de probar. Usa clases cuando tengas muchos objetos del mismo tipo con estado propio.

    7. Retos

    Los dos primeros practican closures y reduce; el tercero, clases con campos privados; y el último junta closures y temporizadores en una técnica que se usa en todos los buscadores.

    🧩JS · DOMContadores independientesMedio

    Completa crearContador(inicio) para que devuelva un objeto con tres métodos: incrementar(), decrementar() y valor(). Si no se indica inicio, empieza en 0. Cada contador creado debe llevar su propia cuenta, y el valor no debe poder cambiarse desde fuera sin usar los métodos.

    index.htmlsolo lectura
    script.js
    Vista previa
    🧩JS · DOMAgrupar por categoríaMedio

    Completa agruparPor(lista, clave), que recibe un array de objetos y el nombre de una propiedad, y devuelve un objeto cuyas claves son los valores de esa propiedad y cuyos valores son arrays con los elementos correspondientes, en su orden original.

    index.htmlsolo lectura
    script.js
    Vista previa
    🧩JS · DOMCarrito de la compraDifícil

    Completa la clase Carrito: agregar(nombre, precio, cantidad = 1) añade un producto o, si ya está, suma la cantidad; eliminar(nombre) lo quita; el getter total devuelve el importe redondeado a 2 decimales (número) y el getter unidades devuelve el número total de unidades. Las líneas deben guardarse en un campo privado #lineas.

    index.htmlsolo lectura
    script.js
    Vista previa
    🧩JS · DOMEsperar a que el usuario deje de escribirDifícil

    En un buscador no se quiere lanzar una búsqueda por cada tecla. Completa debounce(fn, ms), que devuelve una nueva función: cada vez que se llama, retrasa la ejecución de fn hasta que pasen ms milisegundos sin nuevas llamadas, y entonces ejecuta fn una sola vez con los argumentos de la última llamada.

    index.htmlsolo lectura
    script.js
    Vista previa

    ¿Has terminado este tema?

    Crea una cuenta gratis para guardar qué temas has terminado, subir de nivel y ganar medallas.

    Guardar mi progreso