¿Cuál es la vida útil de la función anónima de javascript?
anonymous-function lifetime (2)
Si escribo esto en alcance global:
(function(){})();
¿Se crea la función anónima cuando la instrucción se ejecuta y se destruye inmediatamente después de que se ejecuta la instrucción?
Como dijo t.niese, las probabilidades son que el motor optimizará esa función por completo. Así que asumamos que tiene algún código:
// At global scope
(function(){ console.log("Hi there"); })();
El motor no puede garantizar que ese código no arrojará un error (por ejemplo, si reemplazó la console
con otra cosa), por lo que estoy bastante seguro de que no se puede alinear con eso.
Ahora la respuesta es: depende.
Desde un nivel de idioma / especificación, todo el código en una unidad de compilación (aproximadamente: script) se analiza cuando la unidad de compilación se carga por primera vez. Luego, esa función se crea cuando el código lo alcanza en la ejecución paso a paso, se ejecuta después de crearse (lo que implica crear un contexto de ejecución para la llamada), y es inmediatamente elegible para la recolección de basura cuando se realiza (junto con el contexto de ejecución) Porque nada tiene una referencia a ello. Pero eso es solo teoría / especificación de alto nivel.
Desde la perspectiva de un motor JavaScript:
- La función se analiza antes de ejecutar cualquier código. El resultado de ese análisis (código de bytes o código de máquina) se asociará con esa expresión de función. Esto no espera a que la ejecución llegue a la función, se hace antes (en el fondo en V8 [el motor de Google en Chrome y Node.js]).
- Una vez que la función ha sido ejecutada y nada más puede referirse a ella:
- El objeto de función y el contexto de ejecución asociado con llamarlo son ambos elegibles para GC. Cuándo y cómo sucede eso depende del motor de JavaScript.
- Lo que deja el código subyacente de la función, ya sea bytecode (versiones modernas de V8 con Ignition, posiblemente otras) o código de máquina compilado (versiones anteriores de V8 con Full-codegen, otras). Si el motor de JavaScript puede deshacerse de ese código de bytes o código de máquina dependerá del motor. No tengo conocimiento específico de que los motores desechen el código de bytecode o el código de máquina para las funciones que nunca se pueden alcanzar de nuevo. Sé que el equipo de V8 dedica mucho tiempo a reducir el impacto de la memoria y lanzar ese código parece ser una fruta de bajo rendimiento. :-) Y que han dedicado específicamente tiempo a optimizar el impacto del código de inicio único.
Aquí hay un par de artículos en el blog V8 que hacen una lectura interesante:
- 26 de marzo de 2018: Recopilación de antecedentes.
- details
- 23 de agosto de 2016: Activación del intérprete de Ignition (información no actualizada y actualizada en el artículo de mayo de 2017)
- Ignition (parte de la información de Encendido parece estar un poco desactualizada en relación con el artículo de mayo de 2017 anterior)
Si escribo esto en una función:
function foo() { var a=1; (function(){})(); a++; }
¿Existe la función anónima hasta que devuelve foo, o simplemente existe durante la ejecución de esa declaración?
Supongamos de nuevo que hay una console.log
en esa función, y que estoy en lo correcto ( es una suposición de mi parte) que el hecho de que se basa en un global ( console
) grabable significa que no puede simplemente estar en línea.
La respuesta de alto nivel / especificación es la misma: la función se analiza cuando el script se carga, se crea cuando se alcanza, se ejecuta y es elegible para GC cuando se termina de ejecutar. Pero una vez más, eso es sólo un concepto de alto nivel.
Las cosas son probablemente diferentes a nivel del motor:
- El código se analizará antes de que se ejecute cualquier código en el script.
- Es probable que el código de bytes o el código de máquina se generen antes de que se ejecute cualquier código en la secuencia de comandos, aunque parece recordar algo del blog V8 sobre el análisis pero no compilación inmediata del contenido de las funciones de nivel superior. Sin embargo, no puedo encontrar ese artículo si no estuviera solo en mi cabeza.
- Cuando la ejecución llega a la función, se crea un objeto de función para ella junto con un contexto de ejecución (a menos que el motor esté seguro de que puede optimizar eso sin que sea observable en el código).
- Una vez que finaliza la ejecución, el objeto de función y ese contexto de ejecución son elegibles para GC. (Es posible que hayan estado en la pila, haciendo que GC sea trivial cuando
foo
regrese). - El código subyacente, sin embargo, se queda en la memoria para ser usado de nuevo (y si se usa con suficiente frecuencia, optimizado).
Si escribo esto en alcance global:
(function(){})();
¿Se crea la función anónima cuando la instrucción se ejecuta y se destruye inmediatamente después de que se ejecuta la instrucción?
Si escribo esto en una función:
function foo()
{
var a=1;
(function(){})();
a++;
}
¿Existe la función anónima hasta que devuelve foo, o simplemente existe durante la ejecución de esa declaración?
En este caso particular, la mayoría de los motores optimizarán esa función por completo, porque no hace nada.
Pero asumamos que la función contiene código y de hecho se ejecuta. En este caso, la función existirá todo el tiempo, ya sea como código compilado, como bytecode o como AST para el intérprete.
La parte que no existirá todo el tiempo es el alcance y el posible cierre creado. El alcance creado para esa función y el cierre solo existirá mientras se ejecute la función o exista una referencia a la función con un alcance / cierre específico.
Por lo tanto, la combinación de la referencia de la función y el alcance se asignará en el momento en que la declaración (function(){})();
se ejecuta, y puede ser liberado después de esa declaración. Pero la versión compilada de function(){}
todavía puede existir en la memoria para su uso posterior.
Para los motores que realizan compilación y optimización justo a tiempo, una función podría incluso existir en diferentes versiones compiladas.
La parte del optimizador JIT + de los motores js modernos es un tema complejo, aquí se puede encontrar una descripción aproximada de v8 html5rocks: Compilación de JavaScript :
En V8, el compilador completo se ejecuta en todo el código y comienza a ejecutar el código lo antes posible, generando rápidamente un código bueno pero no excelente. Este compilador no asume casi nada sobre los tipos en el momento de la compilación; espera que los tipos de variables puedan cambiar y lo harán en el tiempo de ejecución.
En paralelo con el compilador completo, V8 vuelve a compilar las funciones "en caliente" (es decir, las funciones que se ejecutan muchas veces) con un compilador de optimización. [...] En el compilador de optimización, las operaciones se especifican de forma lineal (colocadas directamente donde se llaman). Esto acelera la ejecución (al costo de la huella de memoria), pero también permite otras optimizaciones.
Por lo tanto, puede ser que el código generado no tenga casi similitudes con el original.
Por lo tanto, una expresión de función invocada de inmediato podría incluso optimizarse completamente utilizando la alineación.