<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
<atom:link href="https://pemid.dev/rss.xml" rel="self" type="application/rss+xml"/>
        <title>El blog de pemid.dev</title>
        <link>https://pemid.dev/</link>
        <description>Artículos sobre desarrollo web y más</description>
        <lastBuildDate>Sat, 12 Sep 2026 04:51:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>es-MX</language>
        <copyright>pemid.dev • © 2026</copyright>
        <item>
            <title><![CDATA[Closures a fondo en JavaScript]]></title>
            <link>https://pemid.dev/blog/closures-a-fondo-en-javascript</link>
            <guid isPermaLink="false">https://pemid.dev/blog/closures-a-fondo-en-javascript</guid>
            <pubDate>Mon, 07 Sep 2026 06:00:00 GMT</pubDate>
            <description><![CDATA[Aprende qué son las closures y otros conceptos del lenguaje JavaScript que hacen posibles su funcionamiento.]]></description>
            <content:encoded><![CDATA[<!-- cspell:ignore funcionHijo entornoLexicoDeFuncionHija --><p>Cuando comencé a aprender sobre las closures, escuché y leí muchas veces explicaciones como esta: <em>&quot;Una closure es una función
que retorna otra función, y la función que se retorna usa variables que se definieron en el cuerpo de la función padre&quot;</em>, o
varias explicaciones que prácticamente se referían a algo similar a esta definición, y aunque en el escenario que se expone en
esta definición se forma una closure, la definición como tal no es 100% correcta y hay muchas otras cosas que podemos aprender
para profundizar en el uso y comportamiento de las closures.</p>
<p>En este artículo exploramos a fondo qué son las closures, así como otros conceptos del lenguaje JavaScript, como el entorno léxico,
que nos ayudarán a comprender el funcionamiento interno de las mismas. Aprenderemos cómo es que estos conceptos se relacionan
directamente con los alcances de bloque(scopes) introducidos en la especificación ECMAScript 6, así como la relación que tienen
con las closures, y cómo el entender estos mecanismos nos ayudarán a comprender cómo es que al usar closures se pueden mantener
&quot;vivas&quot; variables, constantes, parámetros y demás dentro de su alcance, incluso cuando la función donde fueron creadas hayan
terminado su ejecución. Si te interesa conocer todo esto te invito a leer hasta al final porque hay mucha información interesante y
seguro que algo nuevo aprendes hoy.</p>
<h2>¿Qué son las closures?</h2>
<p>Un closure es la combinación de una función agrupada dentro de otra con referencias a su estado adyacente. Veamos el siguiente
ejemplo para entenderlo un poco mejor:</p>
<pre><code class="language-js">function padre() {
  let nombre = &#39;pemid&#39;

  function hijo() {
	  // hijo es la función interna que forma el closure
    console.log(nombre) // Podemos acceder a la variable nombre
  }

  hijo()
}

padre() // Se muestra en la consola &#39;pemid&#39;
</code></pre>
<p>En este ejemplo podemos ver que en la función <code>hijo</code> se lee la variable declarada en la función <code>padre</code>, prácticamente, en este
ejemplo la closure se crea entre al función <code>hijo</code> y la variable declarada en la función <code>padre</code>. Si has programado con JavaScript
o TypeScript durante un tiempo te habrás percatado de que realmente esto se relaciona directamente con otro concepto que también
es muy importante desde inicio que es el <em><strong>scope</strong></em> o alcance, y esto mismo se relaciona con los conceptos de <em><strong>Ámbito Léxico</strong></em>
y <em><strong>Entorno Léxico</strong></em>.</p>
<h3>Alcance con <code>let</code> y <code>const</code></h3>
<p>Antes de conocer lo que son el Ámbito Léxico y el Entorno Léxico, conozcamos el contexto en que estos conceptos fueron concebidos
en JavaScript. En JavaScript tradicional, antes de la llegada de la especificación ECMAScript 6(ES6), las variables eran declaradas
con la palabra reservada <code>var</code>, con estas variables, JavaScript hacia un proceso de <em><strong>hoisting</strong></em>, el cual <em>&quot;elevaba&quot;</em> la
declaración de las variables al inicio de la ejecución del archivo o módulo, lo cual hacía que pudieras usarlas incluso antes de su
declaración o inicialización explícita, tal y como se muestra en el siguiente ejemplo:</p>
<pre><code class="language-js">var x // declarada, inicializada con undefined

console.log(x) // undefined
console.log(y) // undefined
// la variable &quot;y&quot; se puede usar antes de su declaración debido al proceso de hoisting 

x = 5

if (true) {
  var y // Declarada, &quot;hoisteada&quot; e inicializada en undefined
  console.log(x) // 5
}

y = 10

console.log(y) // 10
</code></pre>
<p>Esto como te puedes imaginar, podía causar bugs muy complicados de encontrar. Con la llegada de ES6, JavaScript introdujo nuevas
formas de declarar variables y constantes, esto como sabemos es con las palabras reservadas <code>let</code> y <code>const</code>, con esto llegaron
nuevos conceptos, entre los cuales están las <em><strong>Temporal Dead Zones(TDZ)</strong></em> o zonas muertas temporales, y los <em><strong>scopes</strong></em> o
alcances de bloque.</p>
<p>La TDZ es la zona entre el inicio del bloque y la declaración de una variable, la cual prácticamente solo la vemos cuando declaramos
una variable con <code>let</code>, ya que al momento de declarar una constante con <code>const</code> debemos obligatoriamente inicializarlas con un
valor. Gracias a esto, si intentamos hacer algo parecido a nuestro ejemplo anterior, pero ahora declarando la variable con <code>let</code> en
lugar de <code>var</code>, tendremos el siguiente error:</p>
<pre><code class="language-js">console.log(x) // ReferenceError: Cannot access &#39;x&#39; before initialization

let x

x = 5
</code></pre>
<p>Gracias a la TDZ, ahora si intentamos usar una variable antes de su declaración tendremos este error de
<em><strong><code>Reference Error: cannot access &#39;x&#39; before initialization</code></strong></em>. Es importante que sepamos que la TDZ solo es un mecanismo que
hace que no se pueda acceder a una variable antes de su declaración, mas no de su inicialización, puesto que como ocurre con las
variables declaradas con <code>var</code>, si podemos acceder a ellas incluso antes de su inicialización ya que también JavaScript las
inicializa en <code>undefined</code> como se muestra a continuación:</p>
<pre><code class="language-js">let x

console.log(x) // undefined

x = 5

console.log(x) // 5
</code></pre>
<p>Otro concepto importante que llegó junto con ES6 es el <em><strong>scope</strong></em> o alcance por bloque. Como vimos en nuestro ejemplo usando <code>var</code>,
la variable <code>y</code>, declarada dentro de un bloque, podía ser accedida desde fuera de las llaves --las llaves delimitan el bloque(este
comportamiento sigue existiendo en JavaScript moderno, por eso ya no se recomienda declarar variables con <code>var</code>). El alcance por
bloque se refiere a que las variables o constantes declaradas con <code>let</code> o <code>const</code>, solo pueden ser leídas en el mismo bloque o en
bloques adyacentes <em>&quot;hijos&quot;</em>, pero las variables declaradas en bloques <em>&quot;hijos&quot;</em> no pueden ser leídas o accedidas desde bloques
<em>&quot;padres&quot;</em> o superiores, veamos el siguiente ejemplo para enterderlo mejor:</p>
<pre><code class="language-js">let var1 = &#39;outer&#39;

if (true) {
  let var2 = &#39;inner&#39;
  console.log(var1) // &#39;outer&#39;
  console.log(var2) // &#39;inner&#39;
}

console.log(var1) // &#39;outer&#39;
console.log(var2) // ReferenceError: var2 is not defined
</code></pre>
<p>En este último ejemplo, la variable <code>var1</code> puede ser accedida desde el bloque del <code>if</code> puesto que es una variable que se declaró
en un bloque <em>&quot;padre&quot;</em>(también se puede decir que se declaró en el ámbito externo), sin embargo como vemos al final, cuando
intentamos acceder a <code>var2</code>, que está declarada dentro del bloque <em>&quot;hijo&quot;</em>(dentro de las llaves del <code>if</code>), JavaScript nos arroja
un <em>ReferenceError</em>, pues como mencionamos antes, los bloques <em>&quot;padre&quot;</em> no pueden acceder a las variables declaradas en los bloques
(o ámbitos) <em>&quot;hijo&quot;</em>.</p>
<p>Con esto, ahora los bloques en JavaScript crean ámbitos o <em>scopes</em>, como lo vimos en este último ejemplo, el alcance de la variable
<code>var2</code>, sólo es dentro del bloque entre las llaves de la sentencia <code>if</code>, por este comportamiento llegaron otros conceptos nuevos que
debemos conocer para comprender a fondo las closures, los cuales son el <em><strong>Ámbito/Alcance Léxico</strong></em>(o <em>lexical scope</em>) y el
<em><strong>Entorno Léxico</strong></em>(o <em>lexical environment</em>).</p>
<h3>Ámbito Léxico y Entorno Léxico</h3>
<p>Con la llegada de <code>let</code> y <code>const</code> en ES6, como mencionamos, los bloques crean <em>scopes</em>, con esto, podemos definir el
<em><strong>Ámbito Léxico</strong></em> como un concepto o regla del lenguaje JavaScript que determina la visibilidad y accesibilidad de las variables
según donde se escribieron <em>&quot;físicamente&quot;</em> en el código; por otro lado, el <em><strong>Entorno Léxico</strong></em> es la implementación física que usa
el motor de JavaScript para almacenar esas variables y mantener la referencia al ámbito padre.</p>
<p>En otras palabras, el <em><strong>Ámbito Léxico</strong></em> son las reglas del estándar ECMAScript que determinan cuál es el alcance o <em>scope</em> de las
variables, mientras que el <em><strong>Entorno Léxico</strong></em> es el lugar físico en memoria donde se almacena la estructura de datos de las
variables de una función, el <em><strong>Entorno Léxico</strong></em> se compone de dos elementos:</p>
<ul>
<li>El <em><strong>Registro de Entorno</strong></em>(o <em>Environment Record</em>): Es donde se almacenan las variables, constantes, funciones y parámetros
locales o declarados directamente dentro del scope de la función</li>
<li>Una referencia externa: La referencia o enlace al <em>entorno léxico</em> adyacente superior (<em>Outer Lexical Environment</em>).</li>
</ul>
<p>Prácticamente es el <em>entorno léxico</em> lo que permite que las closures puedan crearse y lo que explica cómo funciona a más bajo nivel
el alcance de las variables y constantes.</p>
<p>Un dato interesante y curioso es que algunas fuentes en internet mencionan que el entorno léxico de una función es almacenado en una
variable oculta llamada <code>[[Environment]]</code>.</p>
<h2>Funciones que retornan funciones</h2>
<p>Veamos de nuevo nuestro primer ejemplo:</p>
<pre><code class="language-js">function padre() {
  let nombre = &#39;pemid&#39;

  function hijo() {
	  // hijo es la función interna que forma el closure
    console.log(nombre) // Podemos acceder a la variable nombre
  }

  hijo()
}

padre() // Se muestra en la consola &#39;pemid&#39;
</code></pre>
<p>En este ejemplo, la variable <code>nombre</code> solo es usada dentro de la función <code>hijo</code> y solo se imprime en consola, una vez la función
<code>padre</code> termina de ejecutarse, la variable <code>nombre</code> es marcada como lista para limpieza por el recolector de basura(<em>garbage collector</em>)
y la liberación de su espacio en memoria, lo que hace que una vez terminada de ejecutarse ya no se pueda volver a acceder a la
variable <code>nombre</code> de ninguna manera.</p>
<p>Ahora bien, llegamos a la parte que se suele enseñar comúnmente cuando se explican las closures en JavaScript, las funciones que
retornan funciones que tienen una referencia a una variable o constante declarada en la función padre, veamos este ejemplo:</p>
<pre><code class="language-js">function padre() {
  let nombre = &#39;pemid&#39;

  function hijo() {
    console.log(nombre)
  }

  return hijo
}

const funcionHijo = padre() // guardamos la referencia a la función hijo
funcionHijo() // &#39;pemid&#39;
funcionHijo() // &#39;pemid&#39;
funcionHijo() // &#39;pemid&#39;
</code></pre>
<p>Podemos acceder a la variable <code>nombre</code> incluso cuando la función donde fue declarada ya terminó su ejecución!!! 🤯</p>
<p>Puede que esto te sorprenda, o puede que no porque es algo que ya has visto muchas veces y sabes que es el comportamiento que tienen
las closures, pero, ¿sabes realmente por qué podemos seguir accediendo a la variable <code>nombre</code> incluso después de que termina de
ejecutarse la función <code>padre</code>?</p>
<p>Ahora que hemos aprendido lo que es el <em>entorno léxico</em>, no es tan complicado entender esto, prácticamente podemos resumir lo que
está pasando con lo siguiente:</p>
<ol>
<li>Se declara la función <code>padre</code>, la cual en su cuerpo declara la variable <code>nombre</code>.</li>
<li>Dentro de la función <code>padre</code> también se declara la función <code>hijo</code>, como vimos antes, en la función <code>hijo</code> se crea un
<em>entorno léxico</em>, en el cual hay una referencia hacia el <em>entorno léxico</em> superior(el de la función <code>padre</code>) y dentro de este
último está la variable <code>nombre</code> que está siendo usada dentro de la función <code>hijo</code>.</li>
<li>La función <code>hijo</code> es retornada por la función <code>padre</code>.</li>
<li>Se declara la constante <code>funcionHijo</code>(<code>const funcionHijo = padre()</code>), ahora en esta constante se guarda lo que retorna la función
<code>padre</code>, que es la referencia a la función <code>hijo</code> que fue declarada dentro de la función <code>padre</code>.</li>
<li>Se termina de ejecutar la función <code>padre</code>, JavaScript intente hacer la limpieza o recolección de las variables declaradas en la
función <code>padre</code> porque ya terminó su ejecución, sin embargo, al ver la variable <code>nombre</code> se da cuenta que esta sigue siendo
referenciada dentro del <em>entorno léxico</em> de la función <code>hijo</code>, la cual sigue teniendo una referencia <em>&quot;viva&quot;</em>, la constante
<code>funcionHijo</code>, por lo que al no poderla destruir, la pasa a la memoria heap, donde vivirá hasta que ya no sea utilizada ni
referenciada en ningún lugar.</li>
<li>Gracias a que esta referencia ahora se queda <em>&quot;viva&quot;</em>, al ejecutar <code>funcionHijo()</code> más adelante, podemos seguir viendo en la
consola <code>&#39;pemid&#39;</code>.</li>
</ol>
<p>Con esto ahora ya conoces más a fondo cómo es que funcionan las closures, ahora bien, solo para complementar esto, podríamos hacer
una limpieza manual de la referencia a la variable <code>nombre</code> de la siguiente manera:</p>
<pre><code class="language-js">function padre() {
  let nombre = &#39;pemid&#39;

  function hijo() {
    console.log(nombre)
  }

  return hijo
}

// reemplazamos const por let para poder cambiar su valor más adelante
let funcionHijo = padre()

funcionHijo() // &#39;pemid&#39;
funcionHijo() // &#39;pemid&#39;
funcionHijo() // &#39;pemid&#39;

// Al cambiar ahora el valor guardado en la variable funcionHijo por undefined
// se pierde la referencia de la función hijo y su entorno léxico, por lo que
// ahora la variable nombre no se usa en ningún lugar por lo que puede ser
// destruida por el garbage collector
funcionHijo = undefined
</code></pre>
<h2>Usando Closures</h2>
<p>Hagamos algunos ejemplos de uso de closures para entender mejor algunas formas de usar closures.</p>
<h3>Creando fábricas de funciones</h3>
<p>Veamos esta función:</p>
<pre><code class="language-js">function crearTablaDeMultiplicar(factor) {
  return function (multiplier) {
    console.log(factor * multiplier)
  }
}
</code></pre>
<p>La función <code>crearTablaDeMultiplicar</code> recibe el parámetro <code>factor</code>, el cual se usa en la función anónima que retorno, esta función
que retorna también recibe un parámetro <code>multiplier</code>, como podemos ver en el código, se imprimirá en consola el producto resultante
entre <code>factor</code> y <code>multiplier</code>, lo cual podemos usar de la siguiente manera:</p>
<pre><code class="language-js">// Tabla del 5
const _5x = crearTablaDeMultiplicar(5)

_5x(1) // 5
_5x(2) // 10
_5x(3) // 15
_5x(4) // 20
_5x(5) // 25
_5x(6) // 30
_5x(7) // 35
_5x(8) // 40
_5x(9) // 45
_5x(10) // 50

// Tabla del 7
const _7x = crearTablaDeMultiplicar(7)

_7x(1) // 7
_7x(2) // 14
_7x(3) // 21
_7x(4) // 28
_7x(5) // 35
_7x(6) // 42
_7x(7) // 49
_7x(8) // 56
_7x(9) // 63
_7x(10) // 70
</code></pre>
<p>En este ejemplo, la función <code>crearTablaDeMultiplicar</code> es una fábrica de funciones, ya que su ejecución crea nuevas funciones con
algunas <em>&quot;configuraciones predeterminadas&quot;</em> que serán compartidas las veces que se ejecute la función, en este caso, la
<em>&quot;configuración predeterminada&quot;</em> es lo que se envía como argumento al parámetro <code>factor</code>.</p>
<p>Como podemos ver, los <em>entornos léxicos</em> no se mezclan, si no que se crea uno cada vez que se guarda una nueva referencia de la
función interna que forma el closure, y de esta forma manteniendo su independencia. De esta manera en la declaración
<code>const _5x = crearTablaDeMultiplicar(5)</code>, en <em>entorno léxico</em> de la función interna guarda el valor de <code>5</code>, que fue el que se le
mandó como argumento al parámetro <code>factor</code>, mientras que en la declaración <code>const _7x = crearTablaDeMultiplicar(7)</code>, el
<em>entorno léxico</em> guarda el valor de <code>7</code>, pues ese fue el valor que se envió como argumento al parámetro <code>factor</code>.</p>
<h3>Ejemplo de uso con HTML</h3>
<p>Imaginemos que tenemos estos elementos HTML:</p>
<pre><code class="language-html">&lt;button id=&quot;size-12&quot;&gt;12&lt;/button&gt;
&lt;button id=&quot;size-14&quot;&gt;14&lt;/button&gt;
&lt;button id=&quot;size-16&quot;&gt;16&lt;/button&gt;
</code></pre>
<p>La finalidad de estos botones es que al presionarlos cambien el tamaño de la fuente según corresponda, hay muchas formas en las que
podríamos abordar este problema, sin embargo, en esta ocasión intentaremos resolverlo con closures, vemos el siguiente código:</p>
<pre><code class="language-js">function makeSizer(size) {
  return function () {
    document.body.style.fontSize = `${size}px`;
  };
}

const setSize12 = makeSizer(12);
const setSize14 = makeSizer(14);
const setSize16 = makeSizer(16);
</code></pre>
<p>En la función <code>makeSizer</code>, como podemos ver, se forma un closure, puesto que en la función interna se hace referencia al parámetro
<code>size</code> que nos llega en la función padre. Con esto podemos ver que la función <code>setSize12</code> guarda en su <em>entorno léxico</em> el valor
de <code>12</code>, por lo que al ejecutar esta función el tamaño de fuente del <em>body</em> se cambiará a <code>12px</code>, lo mismo pasará con las funciones
<code>setSize14</code> y <code>setSize16</code> con los valores <code>14px</code> y <code>16px</code> respectivamente.</p>
<p>Ahora podemos <em>&quot;bindear&quot;</em> estas funciones en los eventos <code>onclick</code> de los botones de la siguiente manera:</p>
<pre><code class="language-js">document.getElementById(&quot;size-12&quot;).onclick = size12;
document.getElementById(&quot;size-14&quot;).onclick = size14;
document.getElementById(&quot;size-16&quot;).onclick = size16;
</code></pre>
<p>En este ejemplo, las closures nos ayudaron a crear una fábrica de funciones y poder reutilizar nuestra lógica para distintos valores.</p>
<h3>Propiedades privadas con ayuda de closures</h3>
<p>En lenguajes cuyo paradigma es la programación orientada a objetos(POO), se pueden crear propiedades privadas, lo que significa que no
se puede acceder a ellas más que dentro de la propia clase.</p>
<p>Antes de ES6 no existían las clases en JavaScript, una forma que se usaba como <em>workaround</em> es emular estas propiedades privadas
creando closures, un ejemplo lo podemos ver a continuación, este ejemplo usa el <em>patrón de diseño de módulo</em>:</p>
<pre><code class="language-js">const counter = (function () {
  let privateCounter = 0

  const changeBy = (value) =&gt; {
    privateCounter += value
  }

  return {
    increment() {
      changeBy(1)
    },
    decrement() {
      changeBy(-1)
    },
    getCounter() {
      return privateCounter
    }
  }
})()

console.log(counter.getCounter()) // 0
counter.increment()
counter.increment()
counter.increment()
console.log(counter.getCounter()) // 3
counter.decrement()
console.log(counter.getCounter()) // 2
console.log(counter.privateCounter) // undefined
</code></pre>
<p>Como podemos observar, en este ejemplo estamos usando una IIFE(<em>Immediately Invoked Function Expression</em>), lo que hace que en la
constante <code>counter</code> se guarde <em>&quot;en automático&quot;</em> lo que retorna esta función. Con este ejemplo podemos ver que realmente las closures
no solo son <em>&quot;funciones que retornan otras funciones...&quot;</em>, sino que como vemos, estamos retornando un objeto con distintos métodos
que a través de las closures mantienen viva la referencia a la variable <code>privateCounter</code> y a la constante <code>changeBy</code>, que a su vez
internamente crea también una closure con la variable <code>privateCounter</code> y como podemos observar en la última línea, no podemos acceder
directamente a <code>privateCounter</code>, sólo a través de la función <code>getCounter</code>, así, emulamos comportamientos de la POO, como el
encapsulamiento, con ayuda de las closures.</p>
<h3>Closures sobre módulos</h3>
<p>Las closures también pueden generarse en distintos módulos, por ejemplo veamos lo siguiente:</p>
<pre><code class="language-js">let foo = 5
export const getFoo = () =&gt; foo
export const setFoo = (value) =&gt; {
  foo = value
}
</code></pre>
<p>Este módulo crea un par de funciones <em>setter</em> y <em>getter</em> sobre la variable <code>foo</code>, lo que hace que aunque esta variable no sea
accesible desde otros módulos, porque no está exportada, sí pueda ser alcanzada y modificada a través de las funciones <code>getFoo</code> y
<code>setFoo</code>, que como podemos ver, han creado un closure con la variable <code>foo</code>:</p>
<pre><code class="language-js">import { getFoo, setFoo } from &quot;./fooModule.js&quot;

console.log(getFoo()) // 5
setFoo(6)
console.log(getFoo()) // 6
</code></pre>
<p>Las closures también pueden ser creadas sobre valores importados y que se consideren <em>&quot;enlazados en vivo&quot;</em>, ya que al cambiar el
valor original cambia el valor importado en consecuencia, esto también es gracias a que los módulos en JavaScript se crean como un
<em>singleton</em>, es decir, en todos los lugares donde se importe el módulo, realmente se estará usando la misma instancia del mismo,
veamos el siguiente ejemplo para comprenderlo mejor:</p>
<pre><code class="language-js">export let bar = 1
export const setBar = (value) =&gt; {
  bar = value
}
</code></pre>
<pre><code class="language-js">import { bar } from &quot;./barModule.js&quot;

// Se crea el closure sobre un enlace en vivo importado
export const getBar = () =&gt; bar 
</code></pre>
<pre><code class="language-js">import { getBar } from &quot;./closureCreator.js&quot;
import { setBar } from &quot;./barModule.js&quot;

console.log(getBar()) // 1
setBar(2);
console.log(getBar()) // 2
</code></pre>
<p>En este ejemplo vemos que la variable <code>bar</code> se crea en <code>barModule.js</code>, y en este mismo módulo se exporta la función <code>setBar</code> para
poder cambiar su valor; en el módulo <code>closureCreator.js</code> se crea el closure dentro de la función <code>getBar</code> y estas dos funciones
exportadas por módulos distintos pueden ser usadas en un tercer módulo <code>index.js</code>, y al modificar la variable con la función <code>setBar</code>
y volver a ejecutar la función <code>getBar</code>, podemos ver que el valor original de <code>bar</code> ha cambiado y la función <code>getBar</code> tiene acceso
al nuevo valor actualizado.</p>
<h2>Cadena de alcance de las closures</h2>
<p>Gracias al <em>entorno léxico</em> que existe dentro de cada función cada vez que se crea una, los bloques anidados pueden acceder hasta
las variables que estén en <em>entornos léxicos</em> de niveles superiores, miremos el siguiente ejemplo:</p>
<pre><code class="language-js">const globalValue = 10

function padre() {
  return function hija() {
    console.log(globalValue)
  }
}

padre()() // 10
</code></pre>
<p>Lo que pasa en este ejemplo es lo siguiente:</p>
<ul>
<li>Se declara la constante <code>globalValue</code> en el <em>entorno léxico</em> global.</li>
<li>La función <code>padre</code> guarda en su <em>entorno léxico</em> dos cosas, el <em>registro de entorno</em>(que en este caso está vacío porque no hay
variables ni parámetros locales de la función <code>padre</code>) y la referencia al <em>entorno léxico</em> externo, que en este caso es el
<em>entorno léxico</em> global.</li>
<li>Dentro de la función <code>padre</code> se crea una nueva función, la función <code>hija</code>, esta función también tiene su propio <em>entorno léxico</em>,
que al igual que la función <code>padre</code>, tiene el <em>registro de entorno</em>, que también está vacío al no haber variables ni parámetros
en el ámbito local de la función <code>hija</code>, y la referencia al <em>entorno léxico</em> superior, que es el de la función <code>padre</code>.</li>
<li>Dentro de la función <code>hija</code> se intente leer la variable <code>globalValue</code>:<ul>
<li>JavaScript primero mira en el <em>registro de entorno</em> de la propia función <code>hija</code>, como no la encuentra, usa la referencia externa
guardada dentro de su <em>entorno léxico</em> para acceder al <em>entorno léxico</em> de la función <code>padre</code>.</li>
<li>Busca la variable <code>globalValue</code> en el <em>registro de entorno</em> de la función <code>padre</code> pero tampoco la encuentra ahí, así que usa la
referencia externa que tiene el <em>entorno léxico</em> de la función <code>padre</code> para acceder al siguiente nivel, que en nuestro ejemplo
es el <em>entorno léxico global</em>.</li>
<li>Al llegar al <em>entorno léxico global</em>, busca en el <em>registro de entorno</em> de este <em>entono léxico</em> la variable <code>globalValue</code>, en
este <em>registro de entorno</em> sí existe, por lo que accede a ela, lee su valor y opera con el mismo, en nuestro ejemplo simplemente
muestra el valor guardado en la consola.</li>
</ul>
</li>
</ul>
<p>Es de esta manera como las funciones o bloques que están muy anidados pueden llegar a leer variables que se encuentren en
<em>registros de entorno</em> de niveles superiores. Un modelo mental que podemos tener al momento de intentar entender este comportamiento
es mirarlo como una lista simplemente enlazada(<em>single linked list</em>), cuyos valores se van enlazando desde el bloque más anidado
hasta el de nivel superior.</p>
<p>Algo que me ayudó a comprender cómo estos valores se van enlazando es viéndolo de la siguiente manera, en nuestro ejemplo:</p>
<pre><code class="language-js">const entornoLexicoDeFuncionHija = {
  // vacío porque no hay variables ni parámetros declarados en la función hija
  registroDeEntorno: {},

  // referencia al entorno léxico de la función padre
  referenciaExterna: {
    // vacío porque no hay variables ni parámetros declarados en la función padre
    registroDeEntorno: {},

    // referencia al entorno léxico global
    referenciaExterna: {
      // en este entorno léxico si está declarada la variable globalValue
      registroDeEntorno: {
        globalValue: 10
      },

      referenciaExterna: null // ya no hay porque este es el entorno léxico global
    }
  }
}
</code></pre>
<p>Aunque esto es un resumen muy abstracto de cómo se van enlazando los <em>entornos léxicos</em>, puede ayudarnos a comprender cómo es que
desde funciones o bloques muy anidados JavaScript puede acceder a variables de entornos superiores.</p>
<p>De manera general y resumida podemos decir que cada bloque tiene 3 alcances:</p>
<ul>
<li>El alcance local (Registro de Entorno)</li>
<li>El alcance adyacente o envolvente (a través de las referencias externas del <em>entorno léxico</em>)</li>
<li>El alcance global (gracias a los enlaces que existen en los <em>entornos léxicos</em> adyacentes)</li>
</ul>
<p>Esto mismo es lo que pasa cuando tenemos muchas closures anidadas de la siguiente manera:</p>
<pre><code class="language-js">const e = 10

function sum(a) {
  return function sum1(b) {
    return function sum2(c) {
      return function sum3(d) {
        return a + b + c + d + e
      }
    }
  } 
}

console.log(suma(1)(2)(3)(4)) // 20
</code></pre>
<p>Gracias a los <em>entornos léxicos</em> enlazados es que la función <code>sum3</code> puede acceder y leer todos los valores de <code>a</code>, <code>b</code>, <code>c</code>, <code>d</code> y <code>e</code>,
a esto es a lo que se conoce como <em><strong>cadena de alcance de las closures</strong></em>, lo cual ahora ya sabemos por qué funciona.</p>
<p>Como dato adicional, la expresión que escribimos en el ejemplo anterior <code>suma(1)(2)(3)(4)</code> está usando una técnica que se llama
<em><strong>Currying</strong></em>, la cual, en JavaScript, también depende de las closures para poder funcionar.</p>
<h2>Recomendaciones y buenas prácticas al usar closures</h2>
<p>Aunque las closures son una característica poderosa y nos pueden ayudar a resolver varios problemas, es necesario tener en cuenta
algunas recomendaciones y buenas prácticas al usarlas, recuerda, un gran poder conlleva una gran responsabilidad.</p>
<ul>
<li><strong>Privatizar datos y variables</strong>: Usa closures para ocultar variables del alcance global. Esto evita que otros scripts modifiquen
tus datos accidentalmente. Define variables dentro de una función y expón solo los métodos para interactuar con ellas.</li>
<li><strong>Evitar fugas de memoria(Memory Leaks)</strong>: Los closures mantienen vivas las variables de su entorno externo, lo que puede impedir
que el recolector de basura libere esa memoria. Limpia las referencias asignando <code>null</code> o <code>undefined</code> a las variables o funciones
que ya no vayas a utilizar.</li>
<li><strong>Crear fábricas de funciones(Factory Functions)</strong>: Aprovecha los closures para generar funciones personalizadas basadas en
argumentos iniciales. Esto promueve la reutilización de código. Pasa un parámetro a la función principal para configurar el entorno
y retorna una nueva función adaptada.</li>
<li><strong>Mantener la legibilidad y evitar el abuso</strong>: Anidar demasiados closures puede hacer que el código sea difícil de leer, depurar
y mantener. Usa closures cuando aporten un beneficio claro(como el encapsulamiento), no por defecto en cada función.</li>
</ul>
<h2>Cierre</h2>
<p>Ahora ya conoces realmente lo que son las closures y cómo funciona a más bajo nivel esta característica del lenguaje JavaScript, sin
embargo, no significa que ahora tengas que usar closures en todas partes, siempre ten en mente que cada nuevo concepto que aprendemos
es una herramienta nueva que agregamos a nuestro arsenal, de esta manera, cuando estemos resolviendo un problema sepamos que esa
herramienta existe y podemos hacer uso de ella para llegar a algunas soluciones si es necesario, aunque también siempre recuerda que
es nuestra labor y responsabilidad como ingenieros de software saber cuando sí usar una herramienta y cuando no, así como las
ventajas y desventajas frente a otras posibles soluciones con otras herramientas distintas.</p>
<p>Espero que este artículo te haya gustado y hayas aprendido algo nuevo, sin más me despido, nos vemos en la próxima entrega.</p>
]]></content:encoded>
            <category>javascript</category>
        </item>
        <item>
            <title><![CDATA[Unknown y Never en TypeScript]]></title>
            <link>https://pemid.dev/blog/unknown-y-never-en-typescript</link>
            <guid isPermaLink="false">https://pemid.dev/blog/unknown-y-never-en-typescript</guid>
            <pubDate>Mon, 15 Jun 2026 06:00:00 GMT</pubDate>
            <description><![CDATA[Una introducción a los tipos unknown y never, sus principales características y posibles usos.]]></description>
            <content:encoded><![CDATA[<p>Regularmente cuando escribimos código TypeScript solemos usar muchos tipos que son muy conocidos como <code>string</code>, <code>number</code>, <code>boolean</code>, entre otros. Sin embargo existen otros tipos menos conocidos y usados, en este espacio hablaremos acerca de dos de ellos, que son <code>unknown</code> y <code>never</code>.</p>
<h2><code>never</code></h2>
<p>El tipo <code>never</code> en TypeScript representa valores que nunca deben ocurrir. Se puede utilizar principalmente para tipar valores de retorno de funciones que nunca terminan su ejecución (ya sea porque lanzan un error o porque tienen un bucle infinito), otro de los usos de <code>never</code> puede ser para asegurar que todas las condiciones que una estructura <code>switch</code> o <code>if/else</code> estén cubiertas, a esto último lo llamamos <em><strong>Comprobación Exhaustiva</strong></em>.</p>
<pre><code class="language-ts">// Lanza un error y nunca llega a su fin
function lanzarError(mensaje: string): never {
    throw new Error(mensaje);
}

// Bucle infinito que tampoco retorna
function bucleInfinito(): never {
    while (true) {
        console.log(&quot;Ejecutando...&quot;);
    }
}

// Comprobación exhaustiva
type Transporte = &#39;avion&#39; | &#39;tren&#39; | &#39;barco&#39;;

function calcularVelocidad(t: Transporte) {
  switch (t) {
    case &#39;avion&#39;: return 900;
    case &#39;tren&#39;:  return 200;
    case &#39;barco&#39;: return 40;
    default:
      // Si manejaste todos los casos, &#39;t&#39; aquí dentro debe ser &#39;never&#39;
      const _checkExhaustivo: never = t;
      return _checkExhaustivo;
  }
}
</code></pre>
<p>Aunque ciertamente será muy extraño que los dos primeros ejemplos los lleguemos a usar alguna vez, sirven para visualizar situaciones en las cuales podemos tipar una función como <code>never</code>.</p>
<h2><code>unknown</code></h2>
<p>Muchas veces me pasó que cuando recién comencé a trabajar con TypeScript, me llegaba a encontrar código que usaba este tipo, y realmente no entendía para qué servía, yo pensaba que si ya teníamos el tipo <code>any</code> - que se podría considerar que es de un tipo que desconocemos - ¿para qué se habrían molestado en agregar el tipo <code>unknown</code>? ¿No?</p>
<p>Pues para entender la razón de ser de este tipo, debemos recordar que el principal objetivo de TypeScript es ayudarnos a que no lleguen errores en tiempo de ejecución por el tipado dinámico que tiene JavaScript, y que podamos darnos cuenta de ellos en tiempo de compilación, esto lo hace agregando una capa encima de JavaScript para tener el <em><strong>&quot;tipado estricto&quot;</strong></em>. Sin embargo tenemos muchas formas de poder engañar al compilador de TypeScript por ejemplo haciendo uso de <code>any</code> o las aserciones de tipo <code>as</code>.</p>
<p>Por esta situación es que existe el tipo <code>unknown</code>, ya que se podría considerar la <em><strong>&quot;Versión Segura&quot;</strong></em> del tipo <code>any</code>. Para entender cómo funciona y cómo nos ayuda, veamos este ejemplo de código.</p>
<pre><code class="language-ts">function procesarDato(dato: any) {
  console.log(dato.toUpperCase())

  console.log(dato * 10)

  console.log(`Hola, mi nombre es ${dato.nombres}`)
}
</code></pre>
<p>Como podemos observar, a pesar de que estamos intentando hacer 3 operaciones diferentes que solo serían posibles con 3 tipos diferentes, TypeScript no nos avisa de nada de las implicaciones que un código como este llegue a producción, que podría causar errores que en teoría el compilador nos debería de avisar, esto porque usamos <code>any</code>. Pero ahora veamos qué pasa si solo cambiamos el tipo a <code>unknown</code>.</p>
<pre><code class="language-ts">function procesarDato(dato: unknown) {
  console.log(dato.toUpperCase()) // error: &#39;dato&#39; is of type &#39;unknown&#39;

  console.log(dato * 10) // error: &#39;dato&#39; is of type &#39;unknown&#39;

  console.log(`Hola, mi nombre es ${dato.nombres}`) // error: &#39;dato&#39; is of type &#39;unknown&#39;
}
</code></pre>
<p>Como nos damos cuenta ahora, TypeScript nos está marcando un error de que el tipo de <code>dato</code> es <code>unknown</code> pero, ¿por qué?. La razón es porque al usar el tipo <code>unknown</code> TypeScript nos &quot;obliga&quot; a hacer comprobaciones previas para poder operar con la variable o constante que está tipada con <code>unknown</code>, en otras palabras, <em><strong>no podemos operar con una variable o constante que esté tipada con unknown hasta que le digamos a TypeScript o le demos &quot;pistas&quot; de con qué tipo de dato estamos operando</strong></em>. Prácticamente necesitaremos aplicar los conceptos de <strong>Type Guards</strong> y <strong>Type Narrowing</strong> para usar el parámetro <code>dato</code>.</p>
<pre><code class="language-ts">function procesarDato(dato: unknown) {
  if (typeof dato === &#39;string&#39;) {
    console.log(dato.toUpperCase())
  }

  if (typeof dato === &#39;number&#39;) {
    console.log(dato * 10)
  }

  if (typeof dato === &#39;object&#39; &amp;&amp; dato !== null &amp;&amp; &#39;nombre&#39; in dato) {
    console.log(`Hola, mi nombre es ${dato.nombre}`)
  }
}
</code></pre>
<p>Gracias a estas comprobaciones (<em>Type Guards</em>) que hicimos en nuestro código, podemos operar con total seguridad con el parámetro <code>dato</code>, asegurándonos también de que los posibles errores en tiempo de ejecución que podríamos haber tenido cuando tipamos con <code>any</code> los mitiguemos.</p>
<h2>Conclusión</h2>
<p>Ahora que conocemos estos conceptos podemos comenzar a escribir código <strong>TypeScript</strong> más seguro y profesional, recuerda esto:</p>
<ul>
<li>Usa <code>unknown</code> en lugar de <code>any</code> para obligarte a validar el tipo de dato antes de operar si no conoces realmente el tipo de dato que tendrá un parámetro, un variable o constante.</li>
<li>Puedes usar <code>never</code> para garantizar que tus estructuras lógicas manejen todos los casos posibles.</li>
</ul>
<p>Si deseas conocer más acerca de estos temas te invito a que veas el video que te dejo a continuación.</p>
<p>::youtube-video{videoId=&quot;w--KT1DlDX4&quot;}
::</p>
]]></content:encoded>
            <category>javascript</category>
        </item>
    </channel>
</rss>