Dominar la entrevista de JavaScript: ¿Qué es un Closure?

«Domina la entrevista de JavaScript» es una serie de posts diseñados para preparar a los candidatos para las preguntas más comunes que probablemente encontrarán cuando soliciten un puesto de JavaScript de nivel medio o superior. Son preguntas que uso frecuentemente en entrevistas reales.

Inicio la serie con la pregunta de los 40 mil dólares. Si respondes mal a esta pregunta, es muy probable que no te contraten. Si te contratan, es muy probable que te contraten como desarrollador junior, independientemente del tiempo que lleves trabajando como desarrollador de software. De media, los desarrolladores junior cobran 40 mil dólares anuales menos que los ingenieros de software más experimentados.

Los cierres son importantes porque controlan lo que está y no está en el ámbito de una función concreta, junto con las variables que se comparten entre las funciones hermanas en el mismo ámbito de contención. Entender cómo las variables y las funciones se relacionan entre sí es fundamental para entender lo que está sucediendo en tu código, tanto en los estilos de programación funcional como en los orientados a objetos.

La razón por la que omitir esta pregunta es tan desventajosa en una entrevista es que los malentendidos sobre el funcionamiento de los cierres son una bandera roja bastante clara que puede revelar una falta de experiencia profunda, no sólo en JavaScript, sino en cualquier lenguaje que dependa mucho de los cierres (Haskell, C#, Python, etc…).

Codificar en JavaScript sin entender los cierres es como intentar hablar inglés sin entender las reglas gramaticales – puede que seas capaz de transmitir tus ideas, pero probablemente de forma un poco torpe.

También serás vulnerable a los malentendidos cuando intentes entender lo que ha escrito otra persona.

No sólo debe saber lo que es un cierre, sino que debe saber por qué es importante, y ser capaz de responder fácilmente a varios posibles casos de uso de los cierres.

Los cierres se utilizan con frecuencia en JavaScript para la privacidad de los datos de los objetos, en los manejadores de eventos y funciones de devolución de llamadas, y en aplicaciones parciales, currying, y otros patrones de programación funcional.

Si no puedes responder a esta pregunta, podría costarte el trabajo, o ~$40k/año.

Prepárate para una rápida continuación: «¿Puedes nombrar dos usos comunes de los cierres?»

¿Qué es un Closure?

Un closure es la combinación de una función agrupada (encerrada) con referencias a su estado circundante (el entorno léxico). En otras palabras, un cierre te da acceso al ámbito de una función externa desde una función interna. En JavaScript, los cierres se crean cada vez que se crea una función, en el momento de la creación de la función.

Para utilizar un cierre, defina una función dentro de otra función y expóngala. Para exponer una función, devuélvela o pásala a otra función.

La función interna tendrá acceso a las variables en el ámbito de la función externa, incluso después de que la función externa haya devuelto.

Uso de cierres (ejemplos)

Entre otras cosas, los cierres se utilizan comúnmente para dar a los objetos privacidad de datos. La privacidad de datos es una propiedad esencial que nos ayuda a programar para una interfaz, no para una implementación. Este es un concepto importante que nos ayuda a construir un software más robusto porque los detalles de la implementación son más propensos a cambiar en formas de ruptura que los contratos de la interfaz.

«Programar para una interfaz, no para una implementación»
Patrones de diseño: Elements of Reusable Object Oriented Software

En JavaScript, los cierres son el principal mecanismo utilizado para permitir la privacidad de datos. Cuando se utilizan cierres para la privacidad de los datos, las variables encerradas sólo están en el ámbito de la función que las contiene (externa). No se puede acceder a los datos desde un ámbito exterior excepto a través de los métodos privilegiados del objeto. En JavaScript, cualquier método expuesto definido dentro del ámbito del cierre es privilegiado. Por ejemplo:

Juega con esto en JSBin. (¿No ves ninguna salida? Copia y pega este HTML en el panel HTML.)

En el ejemplo anterior, el método `.get()` está definido dentro del ámbito de `getSecret()`, lo que le da acceso a cualquier variable de `getSecret()`, y lo convierte en un método privilegiado. En este caso, el parámetro, `secret`.

Los objetos no son la única forma de producir privacidad de datos. Los cierres también pueden usarse para crear funciones con estado cuyos valores de retorno pueden ser influenciados por su estado interno, por ejemplo:

const secret = msg => () => msg;

Disponible en JSBin. (¿No ve ninguna salida? Copie y pegue este HTML en el panel HTML.)

En la programación funcional, los cierres se utilizan frecuentemente para la aplicación parcial & de currying. Esto requiere algunas definiciones:

Aplicación: El proceso de aplicar una función a sus argumentos para producir un valor de retorno.

Aplicación parcial: El proceso de aplicar una función a algunos de sus argumentos. La función parcialmente aplicada se devuelve para su uso posterior. La aplicación parcial fija (aplica parcialmente la función a) uno o más argumentos dentro de la función devuelta, y la función devuelta toma los parámetros restantes como argumentos para completar la aplicación de la función.

La aplicación parcial aprovecha el ámbito de cierre para fijar los parámetros. Puede escribir una función genérica que aplique parcialmente los argumentos a la función de destino. Tendrá la siguiente firma:

partialApply(targetFunction: Function, ...fixedArgs: Any) =>
functionWithFewerParams(...remainingArgs: Any)

Si necesita ayuda para leer la firma anterior, consulte Rtype: Reading Function Signatures.

Tomará una función que toma cualquier número de argumentos, seguida de los argumentos que queremos aplicar parcialmente a la función, y devuelve una función que tomará los argumentos restantes.

Un ejemplo te ayudará. Digamos que tienes una función que suma dos números:

const add = (a, b) => a + b;

Ahora quieres una función que sume 10 a cualquier número. La llamaremos `add10()`. El resultado de `add10(5)` debe ser `15`. Nuestra función `partialApply()` puede hacer que eso ocurra:

const add10 = partialApply(add, 10);
add10(5);

En este ejemplo, el argumento, `10` se convierte en un parámetro fijo recordado dentro del ámbito de cierre de `add10()`.

Veamos una posible implementación de `partialApply()`:

Disponible en JSBin. (¿No ves ninguna salida? Copia y pega este HTML en el panel HTML.)

Como puedes ver, simplemente devuelve una función que conserva el acceso a los argumentos `fixedArgs` que se pasaron a la función `partialApply()`.

Tu turno

Este post tiene un post de vídeo complementario y tareas de práctica para los miembros de EricElliottJS.com. Si ya eres miembro, inicia sesión y practica ahora.

Si no eres miembro, regístrate hoy.

Explora la serie

  • ¿Qué es un Closure?
  • ¿Cuál es la diferencia entre herencia de clases y de prototipos?
  • ¿Qué es una función pura?
  • ¿Qué es la composición de funciones?
  • ¿Qué es la programación funcional?
  • ¿Qué es una promesa?
  • Habilidades blandas
    • Actualizaciones:
      Julio de 2019 – Se ha aclarado la introducción para explicar por qué responder mal a esta pregunta podría costarte un trabajo o mucho dinero en salario.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *