2.5 Funciones como Valores
Hasta ahora, hemos discutido las funciones como el principal mecanismo de scope en JavaScript. Puede recordar la sintaxis típica de la declaración de funciones de la siguiente manera:
Aunque no parezca obvio de esa sintaxis, foo
es básicamente sólo una variable en el ámbito exterior que incluye una referencia a la función que se está declarando. Es decir, la función en sí es un valor, al igual que sería 42
o [1,2,3]
.
Esto puede sonar como un concepto extraño al principio, así que tómese un momento para reflexionar sobre él. No sólo puede pasar un valor (argumento) a una función, sino que una función en sí puede ser un valor que se asigna a variables, o se pasa a, o se devuelve a otras funciones.
Como tal, un valor de función debe ser pensado como una expresión, al igual que cualquier otro valor o expresión.
Considerar:
La primera expresión de función asignada a la variable foo
se llama anónima porque no tiene nombre.
La segunda expresión de función llamada (bar
), incluso como una referencia a ella también se asigna a la variable x
. Las expresiones de función nombradas son generalmente más preferibles, aunque las expresiones de función anónimas todavía son extremadamente comunes.
Para obtener más información, consulte el título Scope & Closures de esta serie.
Immediately Invoked Function Expressions (IIFEs) - Expresiones de función invocadas Inmediatamente
En el snippet anterior, ninguna de las expresiones de función se ejecutan - podríamos si hubiéramos incluido foo()
o x()
, por ejemplo.
Hay otra forma de ejecutar una expresión de función, que normalmente se denomina expresión de función inmediatamente invocada (IIFE):
El exterior (..)
que rodea la expresión de función (función IIFE () {..})
es sólo un matiz de la gramática JS necesaria para evitar que se trate como una declaración de función normal.
El final ()
al final de la expresión - el }) ()
; - es lo que en realidad ejecuta la expresión de función referenciada inmediatamente antes de ella.
Eso puede parecer extraño, pero no es tan extraño como a primera vista. Considere las similitudes entre foo
y IIFE
aquí:
Como se puede ver, la lista de la función (IIFE () {..})
antes de su ejecución ()
es esencialmente el mismo que incluye foo
antes de su ejecución ()
; En ambos casos, la referencia de función se ejecuta con ()
inmediatamente después de ella.
Debido a que un IIFE
es sólo una función y las funciones crean un ámbito variable, el uso de un IIFE
de esta manera se utiliza a menudo para declarar variables que no afectarán al código circundante fuera del IIFE
:
Los IIFE
también pueden tener valores de retorno:
El valor 42
se devuelve de la función denominada IIFE
que se ejecuta, y luego se asigna a x
.
Closure
El closure es uno de los conceptos más importantes, ya menudo menos comprendidos, en JavaScript. No lo voy a cubrir en detalle aquí y en su lugar le remito al título Scope & Closures de esta serie. Pero quiero decir algunas cosas sobre el para que entiendas el concepto general. Será una de las técnicas más importantes en su nivel de habilidades JS.
Puede pensar en el closure como una forma de "recordar" y seguir accediendo al ámbito de una función (y sus variables) incluso una vez que la función ha terminado de ejecutarse.
Considerar:
La referencia a la función interna add(..)
que se retorna con cada llamada al makeAdder(..)
externo es capaz de recordar cualquier valor x
que se pasó en makeAdder(..)
. Ahora, vamos a usar makeAdder(..)
:
Más información sobre cómo funciona este código:
Cuando llamamos
makeAdder(1)
, obtenemos una referencia a suadd(..)
interna que recuerdax
como1
. Llamamos a esta referencia de funciónplusOne(..)
.Cuando llamamos
makeAdder(10)
, obtenemos otra referencia a suadd(..)
interna que recuerdax
como10
. Llamamos a esta referencia de funciónplusTen(..)
.Cuando llamamos
plusOne(3)
, añade3
(suy
interno) al1
(recordado porx
), y obtenemos4
como el resultado.Cuando llamamos
plusTen(13)
, añade13
(suy
interno) al10
(recordado porx
), y obtenemos23
como el resultado.
No se preocupe si esto parece extraño y confuso al principio - puede serlo! Tomará mucha práctica entenderlo completamente.
Pero confía en mí, una vez que lo hagas, es una de las técnicas más poderosas y útiles en toda la programación. Definitivamente vale la pena el esfuerzo para dejar que su cerebro cocine a fuego lento los closures poco a poco. En la siguiente sección, tendremos un poco más de práctica con los closures.
Modules
El uso más común de los closures en JavaScript es el patrón module. Los modules le permiten definir detalles de implementación privados (variables, funciones) ocultos del mundo exterior, así como una API pública accesible desde el exterior.
Considere:
La función User()
sirve como un ámbito externo que contiene las variables username
y password
, así como la función interna doLogin()
; Estos son todos los detalles internos privados de este módulo de usuario que no se puede acceder desde el mundo exterior.
Advertencia: No estamos llamando a new User()
aquí, a propósito, a pesar del hecho de que probablemente parece más común a la mayoría de los lectores. User()
es sólo una función, no una clase a instanciar, por lo que se llama normalmente. El uso de new
sería inapropiado y realmente desperdiciaría recursos.
Ejecutar User()
crea una instancia del módulo User
- se crea un nuevo ámbito y, por lo tanto, una copia completamente nueva de cada una de estas variables/funciones internas. Asignamos esta instancia a fred
. Si ejecutamos User()
de nuevo, obtendremos una nueva instancia completamente distinta de fred
.
La función interna doLogin()
tiene un closure sobre el username
y password
, lo que significa que conservará su acceso a ellos incluso después de que la función User()
termine de ejecutarse.
PublicAPI
es un objeto con una propiedad/método en él, login
, que es una referencia a la función interna doLogin()
. Cuando devolvemos publicAPI
de User()
, se convierte en la instancia que llamamos fred
.
En este punto, la función externa User()
ha terminado de ejecutarse. Normalmente, se piensa que las variables internas como username
y password
se han ido. Pero aquí no lo han hecho, porque hay un closure en la función login()
que los mantiene vivos.
Es por eso que podemos llamar a fred.login(..)
- lo mismo que llamar a la interna doLogin(..)
- y todavía puede acceder a las variables internas de username
y password
.
Hay una buena probabilidad de que con sólo este breve vistazo al closures y el patrón module, algunos de ellos sean todavía un poco confusos. ¡Está bien! Toma un poco de trabajo para envolver su cerebro alrededor de él.
A partir de aquí, vaya a leer el título Scope & Closures de esta serie para una exploración mucho más profunda.
Last updated