2010-09-11 31 views
13

Los valores primitivos se almacenan en una pila en javascript, pero los objetos se almacenan en un montón. Entiendo por qué almacenar primitivas en la pila, pero ¿por qué razón los objetos se almacenan en montones?objetos en javascript

+3

¿Qué te hace pensar que los valores primitivos se almacenan en una pila? Ellos no están. – Pointy

Respuesta

22

En realidad, en JavaScript, incluso las primitivas se almacenan en el montón en lugar de en una pila (ver la nota debajo del salto a continuación, sin embargo). Cuando el control ingresa a una función, se crea un contexto de ejecución (un objeto) para esa llamada a la función, que tiene un objeto variable . Todos los var sy los argumentos para la función (más un par de otras cosas) son propiedades de ese objeto variable anónimo, exactamente como otras propiedades de los objetos nombrados. Se usa una pila de llamadas, pero la especificación no requiere que la pila se use para el almacenamiento variable "local", y los cierres de JavaScript harían que usar una pila como la C, C++, etc., fuera poco práctico. Detalles en the spec.

En su lugar, se utiliza una cadena (lista vinculada). Cuando se refiere a un símbolo no calificado, el intérprete verifica el objeto de la variable para el contexto de ejecución actual para ver si tiene una propiedad para ese nombre. Si es así, se usa; si no, se verifica el siguiente objeto variable en la cadena de alcance (observe que esto está en el orden léxico, no el orden de llamada como una pila de llamadas), y así sucesivamente hasta que se alcanza el contexto de ejecución global (el contexto de ejecución global tiene un objeto variable como cualquier otro contexto de ejecución). El objeto variable para la EC global es el único al que podemos acceder directamente en el código: this apunta a él en el código de alcance global (y en cualquier función llamada sin this explícitamente establecida). (En los navegadores, tenemos otra forma de acceder a él directamente: el objeto de variable global tiene una propiedad llamada window que usa para apuntar a sí mismo).

Responda su pregunta de por qué los objetos se almacenan en el montón: Porque pueden ser creado y lanzado independientemente el uno del otro. C, C++ y otros que usan una pila para variables locales pueden hacerlo porque las variables pueden (y deberían) destruirse cuando la función retorna. Una pila es una buena manera eficiente de hacer eso. Pero los objetos no se crean ni se destruyen de una manera directa; tres objetos creados al mismo tiempo pueden tener ciclos de vida radicalmente diferentes, por lo que una pila no tiene sentido para ellos. Y dado que los locales de JavaScript se almacenan en objetos y esos objetos tienen un ciclo de vida que (potencialmente) no está relacionado con el retorno de la función ... bueno, se entiende la idea. :-) En JavaScript, la pila es prácticamente solo para direcciones de devolución.


Sin embargo, vale la pena señalar que sólo porque las cosas son como se describió anteriormente conceptualmente, eso no quiere decir que tiene un motor a hacerlo de esa manera bajo el capó. Siempre y cuando funcione externamente como se describe en la especificación, las implementaciones (motores) son libres de hacer lo que quieran. Entiendo que el V8 (el motor JavaScript de Google, utilizado en Chrome y en otros lugares) hace cosas muy ingeniosas, como por ejemplo usar la pila para variables locales (e incluso asignaciones de objetos locales dentro de la función) y luego copiarlas en el montón si necesario (por ejemplo, porque el contexto de ejecución o los objetos individuales en él sobreviven a la llamada). Puede ver cómo en la mayoría de los casos, esto minimizaría la fragmentación del montón y recuperaría la memoria utilizada para los temporales más agresiva y eficientemente que confiar en el GC, porque el contexto de ejecución asociado con la mayoría de las llamadas a funciones no necesita sobrevivir a la llamada.Veamos un ejemplo:

function foo() { 
    var n; 

    n = someFunctionCall(); 
    return n * 2; 
} 

function bar() { 
    var n; 

    n = someFunction(); 
    setCallback(function() { 
     if (n === 2) { 
      doThis(); 
     } 
     else { 
      doThat(); 
     } 
    }); 
} 

En lo anterior, al igual que un motor V8 que optimiza de forma agresiva puede detectar que el contexto de ejecución conceptual para una llamada a foo nunca se necesita para sobrevivir cuando foo devoluciones. Entonces V8 sería libre de asignar ese contexto en la pila, y usar un mecanismo basado en la pila para la limpieza.

Por el contrario, el contexto de ejecución creada por una llamada a bar tiene que quedarse después de bar rendimientos, porque hay un cierre (la función anónima que pasamos en setCallback) de confiar en ella. Por lo tanto, al compilar bar (porque V8 compila el código de la máquina sobre la marcha), V8 bien puede usar una estrategia diferente, en realidad asignando el objeto de contexto en el montón.

(Si cualquiera de los anteriores ha usado eval de alguna manera, por cierto, es probable que V8 y otros motores ni siquiera intenten ninguna forma de optimización, porque eval presenta demasiados modos de falla de optimización. Sin embargo, otra razón no para usar eval si no tiene que hacerlo, y casi nunca tiene que hacerlo)

Pero estos son detalles de implementación. Conceptualmente, las cosas son como se describe arriba del descanso.

+0

Sí, tengo la idea :) – alter

+2

@ T.J. Crowder: Lo sé 4 años después del hecho, pero quería agradecerle por una respuesta tan increíble. –

+0

en JavaScript, ¿la memoria y el contexto de ejecución son los mismos? – Nipuna

3

El tamaño de los objetos puede crecer dinámicamente. Por lo tanto, necesitaría ajustar sus requisitos de memoria. Por eso, están almacenados en montón.

+0

Es cierto, pero no puedo visualizar esto. ¿Puedes tomar un ejemplo de objeto y explicar el escenario? – alter

+1

Para pensarlo simplemente, supongamos que tiene un objeto humano con antigüedad y dirección de la propiedad. En JavaScript, puede agregar otra propiedad mediante programación. La estructura del montón es tal, es fácil asignar-desasignar memoria en cualquier ubicación. Dado que la pila crece hacia arriba o hacia abajo secuencialmente, no sería posible asignar memoria adicional, si es necesario, a un objeto que reside en la pila. Espero que esto sea un poco claro para ti. –

0

Tanto los objetos primitivos como los valores se almacenan siempre en algún otro objeto, son propiedades de algún objeto.

No hay un valor/objeto primitivo que no sea propiedad de otro objeto. (La única excepción aquí es el objeto global).