Estoy estudiando THREE.js y noté un patrón donde las funciones se definen así:
var foo = ( function () {
var bar = new Bar();
return function ( ) {
//actual logic using bar from above.
//return result;
};
}());
(Ejemplo, vea el método raycast aquí ).
La variación normal de dicho método se vería así:
var foo = function () {
var bar = new Bar();
//actual logic.
//return result;
};
Comparando la primera versión con la variación normal , la primera parece diferir en que:
- Asigna el resultado de una función autoejecutable.
- Define una variable local dentro de esta función.
- Devuelve la función real que contiene la lógica que hace uso de la variable local.
Entonces, la principal diferencia es que en la primera variación, la barra solo se asigna una vez, en la inicialización, mientras que la segunda variación crea esta variable temporal cada vez que se llama.
Mi mejor conjetura sobre por qué se usa esto es que limita el número de instancias de bar (solo habrá una) y, por lo tanto, ahorra la sobrecarga de administración de memoria.
Mis preguntas:
- ¿Es esta suposición correcta?
- ¿Hay un nombre para este patrón?
- ¿Por qué se usa esto?
javascript
closures
iife
Patrick Klug
fuente
fuente
Respuestas:
Tus suposiciones son casi correctas. Repasemos esos primero.
Esto se denomina expresión de función invocada inmediatamente o IIFE
Esta es la forma de tener campos de objetos privados en JavaScript, ya que no proporciona la
private
palabra clave o la funcionalidad de otra manera.Nuevamente, el punto principal es que esta variable local es privada .
AFAIK, puedes llamar a este patrón Patrón de módulo . Citando:
Comparando esos dos ejemplos, mis mejores conjeturas sobre por qué se usa el primero son:
Pero si solo necesita el objeto vainilla cada vez, entonces este patrón probablemente no agregará ningún valor.
fuente
Limita los costes de inicialización del objeto y, además, garantiza que todas las invocaciones de funciones utilicen el mismo objeto. Esto permite, por ejemplo, que el estado se almacene en el objeto para su uso en futuras invocaciones.
Si bien es posible que limite el uso de la memoria, generalmente el GC recolectará objetos no utilizados de todos modos, por lo que es probable que este patrón no ayude mucho.
Este patrón es una forma específica de cierre .
fuente
No estoy seguro de si este patrón tiene un nombre más correcto, pero me parece un módulo, y la razón por la que se usa es tanto para encapsular como para mantener el estado.
El cierre (identificado por una función dentro de una función) asegura que la función interna tenga acceso a las variables dentro de la función externa.
En el ejemplo que proporcionó, la función interna se devuelve (y se asigna a
foo
) mediante la ejecución de la función externa, lo que significa quetmpObject
continúa viviendo dentro del cierre y varias llamadas a la función internafoo()
operarán en la misma instancia detmpObject
.fuente
La diferencia clave entre su código y el código Three.js es que en el código Three.js la variable
tmpObject
solo se inicializa una vez y luego se comparte con cada invocación de la función devuelta.Esto sería útil para mantener algún estado entre llamadas, similar a cómo
static
se usan las variables en lenguajes similares a C.tmpObject
es una variable privada solo visible para la función interna.Cambia el uso de la memoria, pero no está diseñado para ahorrar memoria.
fuente
Me gustaría contribuir a este interesante hilo extendiéndome al concepto del patrón de módulo revelador, que garantiza que todos los métodos y variables se mantengan privados hasta que se expongan explícitamente.
En el último caso, el método de adición se llamaría Calculator.add ();
fuente
En el ejemplo proporcionado, el primer fragmento utilizará la misma instancia de tmpObject para cada llamada a la función foo (), donde, como en el segundo fragmento, tmpObject será una nueva instancia cada vez.
Una de las razones por las que se pudo haber utilizado el primer fragmento es que la variable tmpObject se puede compartir entre llamadas a foo (), sin que su valor se filtre en el ámbito en el que se declara foo ().
La versión de función no ejecutada inmediatamente del primer fragmento en realidad se vería así:
Sin embargo, tenga en cuenta que esta versión tiene tmpObject en el mismo ámbito que foo (), por lo que podría manipularse más adelante.
Una mejor manera de lograr la misma funcionalidad sería usar un módulo separado:
Módulo 'foo.js':
Módulo 2:
Una comparación entre el rendimiento de un IEF y una función de creador de foo con nombre: http://jsperf.com/ief-vs-named-function
fuente