jueves, 12 de marzo de 2026

Callbacks en Javascript (y Typescript)

 Si En lenguaje C estaba el concepto de función anfitriona y función huésped de modo tal que la función huésped es a la función anfitriona lo que en javascript la función callback es a la función que la llama. ¿Qué sería el equivalente de la función anfitriona en javascript?

En tu analogía con C, la función anfitriona sería, en JavaScript, la función que recibe y ejecuta el callback. Es decir, la que define el contexto de ejecución y decide cuándo y cómo invocar a la función huésped (el callback).

📌 Ejemplo simple

js
function anfitriona(callback) {
  console.log("Soy la función anfitriona");
  callback("mensaje desde la anfitriona");
}

function huesped(mensaje) {
  console.log("Soy la función huésped y recibí:", mensaje);
}

anfitriona(huesped);

👉 Aquí:

  • anfitriona es la función anfitriona: controla el flujo y llama al callback.

  • huesped es la función huésped: se ejecuta dentro del contexto que le da la anfitriona.

📌 Equivalente en React

En React, el patrón es idéntico:

  • El componente padre o la función que maneja un evento es la anfitriona.

  • El callback pasado como prop o handler es la huésped.

tsx
function Padre() {
  const manejarClick = () => console.log("Click manejado en el padre");

  return <Hijo onClick={manejarClick} />;
}

function Hijo({ onClick }) {
  return <button onClick={onClick}>Soy hijo</button>;
}

👉 Padre define la lógica (anfitriona) y Hijo recibe el callback (huésped) que se ejecuta en el contexto del padre.

En resumen: en JavaScript, la función anfitriona es la que recibe y ejecuta el callback, mientras que la función huésped es el callback mismo.

¿Querés que te arme un diagrama visual mostrando la relación anfitriona ↔ huésped en un flujo de ejecución con callbacks?

¿Es convencional la terminología que yo usé, anfitriona y huésped?

Tu terminología de “función anfitriona” y “función huésped” no es convencional en el mundo de JavaScript ni en la literatura técnica habitual. En C sí se usaba en algunos contextos académicos para describir la relación entre funciones que llaman y funciones llamadas, pero en JavaScript la terminología estándar es otra:

  • Función anfitriona → se suele llamar “caller” o simplemente “la función que invoca el callback”.

  • Función huésped → se suele llamar “callback”, “handler” o “callee”.

En documentación y comunidad de JavaScript/React, lo más común es hablar de:

  • “callback function” (la huésped).

  • “higher-order function” o “function that accepts a callback” (la anfitriona).

📌 Ejemplo con terminología convencional

js
function higherOrderFunction(callback) {
  console.log("Soy la función que invoca");
  callback("Soy el callback");
}

👉 Aquí:

  • higherOrderFunction sería la función anfitriona en tu analogía, pero en JS se llama higher-order function.

  • callback es la función huésped, que se ejecuta dentro del contexto de la anfitriona.

En resumen: tu analogía es clara y útil para pensar, pero en JavaScript no se usa esa terminología. Lo convencional es hablar de función de orden superior (anfitriona) y callback (huésped).

¿Querés que te arme una tabla comparativa entre la terminología que vos proponés (anfitriona/huésped) y la terminología estándar en JavaScript (caller/callback/higher-order function)?

Si el caller vuelve a llamar al callback después de que esta ya se ejecutó una vez y modificó el contexto que le fue pasado ¿La caller le vuelve a pasar el contexto primigenio o el modificado?

Muy buena pregunta. En JavaScript (y React también), el comportamiento depende de cómo el caller maneja el contexto que le pasa al callback:

  • El caller es quien decide qué valores o referencias entrega al callback.

  • Si el caller le pasa un objeto o estado mutable y el callback lo modifica, entonces la próxima vez que el caller invoque al callback con esa misma referencia, el callback verá el contexto modificado.

  • Si en cambio el caller genera un nuevo objeto o copia fresca cada vez que llama al callback, entonces el callback recibe el contexto primigenio (o una nueva versión limpia).

📌 Ejemplo con objeto mutable

js
function caller(callback) {
  const contexto = { valor: 1 };
  callback(contexto);
  callback(contexto); // segunda llamada
}

caller(ctx => {
  console.log("Antes:", ctx.valor);
  ctx.valor++;
  console.log("Después:", ctx.valor);
});

👉 Resultado:

  • Primera llamada: Antes: 1, Después: 2

  • Segunda llamada: Antes: 2, Después: 3 El callback recibe el contexto modificado, porque se pasó la misma referencia.

📌 Ejemplo con copia fresca

js
function caller(callback) {
  let contexto = { valor: 1 };
  callback({ ...contexto }); // copia
  callback({ ...contexto }); // otra copia fresca
}

caller(ctx => {
  console.log("Antes:", ctx.valor);
  ctx.valor++;
  console.log("Después:", ctx.valor);
});

👉 Resultado:

  • Primera llamada: Antes: 1, Después: 2

  • Segunda llamada: Antes: 1, Después: 2 El callback recibe siempre el contexto primigenio, porque el caller genera una copia nueva.

✅ Conclusión

El caller controla si el callback recibe el contexto original modificado o uno nuevo.

  • Si pasa la misma referencia, el callback verá los cambios acumulados.

  • Si pasa una copia o reconstruye el contexto, el callback verá un estado limpio cada vez.

¿Querés que te arme un diagrama visual mostrando cómo cambia el flujo según el caller pase la misma referencia o una copia nueva?

¿Cuál sería el concepto equivalente a bind variables de Java en Javascript?

En Java, el concepto de bind variables suele aparecer en el contexto de JDBC y SQL: son los parámetros que se “bindean” a una sentencia preparada (PreparedStatement) para evitar inyecciones y reutilizar consultas. Es decir, valores que se insertan en un contexto de ejecución sin interpolarlos directamente en el string.

En JavaScript no existe exactamente el mismo término, pero sí hay conceptos equivalentes:

  • Parámetros en consultas preparadas: En librerías como pg (Postgres) o mysql2, se usan placeholders (? o $1) y se pasan valores aparte. Eso es el equivalente directo a bind variables.

    js
    const result = await client.query("SELECT * FROM users WHERE id = $1", [userId]);
    
  • Closures / binding de valores: En el lenguaje mismo, el equivalente más cercano es cuando una función “captura” variables de su entorno léxico. Esas variables quedan “bindeadas” al contexto de la función.

    js
    function makeAdder(x) {
      return function(y) {
        return x + y; // x está "bindeado"
      };
    }
    
  • Function binding (bind): JavaScript tiene el método .bind() para fijar el valor de this y parámetros iniciales en una función.

    js
    const f = function(a, b) { return a + b; }.bind(null, 2);
    console.log(f(3)); // 5
    

📌 Comparación rápida

ContextoJava (bind variables)JavaScript equivalente
SQL / DBPreparedStatement.setInt(1, id)client.query("... WHERE id = $1", [id])
LenguajeVariables bindeadas en ejecuciónClosures que capturan valores
FuncionesNo aplica directamente.bind() para fijar this y args

En resumen:

  • En bases de datos, el equivalente son los parámetros de consultas preparadas.

  • En lenguaje, el equivalente conceptual son las closures y el método .bind().

¿Querés que te arme un ejemplo paralelo mostrando la misma consulta SQL con bind variables en Java y su equivalente en Node.js con Postgres?

Estoy interesado en ciertas propiedades de una figura que no sé si se llama closure, binding de valores o captura del contexto léxico.

Lo que estás describiendo —esa “figura” que te interesa— corresponde en JavaScript al concepto de closure (clausura).

Un closure ocurre cuando una función captura variables de su contexto léxico (el entorno donde fue definida) y las sigue “recordando” incluso después de que ese contexto haya terminado de ejecutarse. Esa captura es lo que en otros lenguajes se llama binding de valores o context binding.

📌 Propiedades clave de un closure

  • Persistencia del contexto léxico: la función mantiene acceso a las variables que estaban en alcance cuando se creó.

  • Encapsulación: podés usar closures para simular variables privadas.

  • Estado compartido: varias funciones definidas en el mismo contexto pueden compartir y modificar esas variables capturadas.

  • Independencia del caller: aunque la función se ejecute en otro lugar, sigue viendo el contexto original.

📌 Ejemplo ilustrativo

js
function crearContador() {
  let cuenta = 0; // variable capturada

  return function() {
    cuenta++;
    return cuenta;
  };
}

const contador = crearContador();
console.log(contador()); // 1
console.log(contador()); // 2
console.log(contador()); // 3

👉 La función interna “huésped” captura la variable cuenta del contexto léxico de crearContador. Aunque crearContador ya terminó, cuenta sigue viva dentro del closure.

✅ Diferencias de términos

  • Closure: el mecanismo en JavaScript.

  • Captura del contexto léxico: la descripción técnica de cómo funciona.

  • Binding de valores: término más usado en bases de datos o en otros lenguajes, pero conceptualmente cercano (asociar un valor a un parámetro/variable).

En resumen: lo que buscás se llama closure en JavaScript, y su propiedad fundamental es que la función conserva acceso al contexto léxico capturado.

¿Querés que te arme un diagrama visual mostrando cómo una función interna captura variables del contexto externo y las mantiene vivas?

SÍ.
Diagrama de closure en JavaScript

Aquí tenés el diagrama que muestra cómo funciona un closure en JavaScript: cómo una función interna captura variables de su contexto léxico y las mantiene vivas incluso después de que la función externa terminó.

🔑 Lo esencial que refleja la imagen:

  • El contexto léxico define las variables (ej. let cuenta = 0).

  • El closure captura esas variables y las conserva.

  • En la ejecución, cada llamada a la función interna sigue accediendo y modificando ese mismo estado.

En otras palabras, un closure es como un “contenedor con memoria” que acompaña a la función interna allá donde vaya.

¿Querés que te arme también un ejemplo aplicado a React, mostrando cómo un closure puede encapsular estado o lógica sin necesidad de usar useState?

¿Hay algún mecanismo implementado en el intérprete de Javascript (hablo del entorno de ejecución) por el cual este detecte cuando una función interna (es decir un callee) no puede ser invocada nunca más y así libere la memoria asociada al contexto capturado?

Sí, el intérprete de JavaScript tiene mecanismos de recolección de basura que detectan cuándo una función interna (closure) ya no puede ser invocada y libera el contexto capturado. Esto ocurre automáticamente cuando no hay más referencias vivas al closure.

🧠 ¿Cómo detecta el entorno de ejecución que un closure ya no es necesario?

JavaScript usa un garbage collector basado en alcance y referencias. El motor (como V8 en Chrome o SpiderMonkey en Firefox) sigue estas reglas:

  • Si una función interna (closure) ya no está referenciada por ninguna parte del programa, entonces su contexto léxico capturado también se vuelve inaccesible.

  • El recolector de basura detecta que no hay caminos desde el "root set" (objetos globales, pila de ejecución, etc.) hacia ese closure.

  • En ese momento, libera tanto la función como el entorno léxico capturado.

📌 Ejemplo práctico

js
function crearContador() {
  let cuenta = 0;
  return function() {
    cuenta++;
    return cuenta;
  };
}

let contador = crearContador(); // closure activo
contador(); // usa 'cuenta'

contador = null; // se pierde la referencia

👉 Cuando hacés contador = null, el closure ya no puede ser invocado. El motor detecta que no hay más referencias vivas y libera tanto la función como la variable cuenta.

🔍 ¿Qué pasa si hay referencias indirectas?

Si el closure fue guardado en un array, objeto, evento, o timer, mientras exista una referencia, el contexto capturado no se libera. Solo se recolecta cuando todas las rutas de acceso desaparecen.

✅ Conclusión técnica

  • Sí hay un mecanismo automático: el garbage collector detecta closures inaccesibles y libera su contexto.

  • No necesitás liberar manualmente el entorno léxico capturado.

  • Pero sí podés causar fugas de memoria si mantenés referencias innecesarias a closures (por ejemplo, en listeners que nunca se limpian).

¿Querés que te arme un diagrama visual del ciclo de vida de un closure, mostrando cuándo se crea, cuándo se mantiene y cuándo se libera por el recolector?

Sí.

Aquí tenés el diagrama que muestra el ciclo de vida de un closure en JavaScript: desde su creación, pasando por su captura de contexto léxico, hasta su eventual liberación por el recolector de basura cuando ya no hay referencias vivas.

🔍 Este flujo te ayuda a visualizar cómo el entorno de ejecución detecta que una función interna (callee) ya no puede ser invocada y libera la memoria asociada.

¿Querés que lo complemente con un ejemplo aplicado a React, donde un closure encapsula lógica sin necesidad de useState?

Sí.

Aquí tenés un ejemplo aplicado a React que muestra cómo un closure encapsula lógica sin necesidad de usar useState. El closure mantiene el contexto léxico y conserva valores entre invocaciones, igual que en el ejemplo puro de JavaScript:

📌 Ejemplo en React con closure

tsx
import React from "react";

function useContadorClosure() {
  let cuenta = 0; // variable capturada por el closure

  return () => {
    cuenta++;
    return cuenta;
  };
}

export default function App() {
  const contador = useContadorClosure();

  return (
    <div>
      <button onClick={() => console.log(contador())}>
        Incrementar con closure
      </button>
    </div>
  );
}

🔍 Qué pasa aquí

  • useContadorClosure devuelve una función que captura la variable cuenta.

  • Cada vez que hacés click, el closure incrementa y recuerda el valor anterior.

  • No usamos useState: el estado está encapsulado en el closure.

✅ Propiedades que se ven en acción

  • Captura del contexto léxico: cuenta sigue viva aunque useContadorClosure ya terminó.

  • Persistencia: el closure mantiene el valor entre invocaciones.

  • Encapsulación: el componente no expone directamente cuenta, solo la función que lo manipula.

Esto demuestra cómo los closures pueden actuar como una forma de “estado privado” en React, aunque en la práctica se prefiere useState o useReducer porque están integrados con el ciclo de renderizado.

¿Querés que te arme un diagrama visual mostrando cómo el closure dentro de un hook personalizado encapsula la variable y la mantiene viva entre renders?

No hay comentarios: