How variables are allocated memory in Javascript?
I would like to know how local variables are allocated memory in javascript. In C and C++ local variables are stored on stack. Is it the same in javascript? or everything is stored in heap?
It's actually a very interesting area of Javascript. Details in the spec, but: Javascript's way of handling local variables is quite different from the way C does it. When you call a function, amongst other things a "variable environment" for that call is created, which has something called a "binding object". (Call it the "variable object" for short; saying "the binding object of the variable environment" is just a tad bit long-winded!) The variable object has properties for the arguments to the function, all local variables declared in the function, and all functions declared within the function (along with a couple of other things). Unqualified references (eg, the foo
in foo
, not obj.foo
) within the function are first checked against the variable object to see if they match properties on it; if they do, those properties are used.
When a closure survives the function returning (which can happen for several reasons), the variable object for that function call is retained in memory by the reference from the closure. At first glance, that would suggest that the stack isn't used for local variables; in fact, modern JavaScript engines are quite smart, and may (if it's worthwhile) use the stack for locals that aren't actually used by the closure. (Naturally, the stack is still used for keeping track of return addresses and such.)
Here's an example:
function foo(a, b) {
var c;
c = a + b;
function bar(d) {
alert("d * c = " + (d * c));
}
return bar;
}
var b = foo(1, 2);
b(3); // alerts "d * c = 9"
When we call foo
, a variable object gets created with these properties:
a
and b
— the arguments to the function c
— a local variable declared in the function bar
— a function declared within the function When foo
executes the statement c = a + b;
, it's referencing the c
, a
, and b
properties on the variable object for that call to foo
. When foo
returns a reference to the bar
function declared inside it, bar
survives the call to foo
returning. Since bar
has a (hidden) reference to the variable object for that specific call to foo
, the variable object survives (whereas in the normal case, it would have no outstanding references and so would be available for garbage collection).
Later, when we call bar
, a new variable object for that call is created with (amongst other things) a property called d
— the argument to bar
. Unqualified references within bar
are first checked against the variable object for that call; so for instance, d
resolves to the d
property on the variable object for the call to bar
. But an unqualified reference that doesn't match a property on its variable object is then checked against the next variable object in the "scope chain" for bar
, which is the variable object for the call to foo
. And since that has a property c
, that's the property used within bar
. Eg, in rough terms:
+----------------------------+ | `foo` call variable object | | -------------------------- | | a = 1 | | b = 2 | | c = 3 | | bar = (function) | +----------------------------+ ^ | chain | +----------------------------+ | `bar` call variable object | | -------------------------- | | d = 3 | +----------------------------+
Implementations are free to use whatever mechanism they want under the covers to make the above seem to happen. It's impossible to get direct access to the variable object for a function call, and the spec makes clear that it's perfectly fine if the variable object is just a concept, rather than a literal part of the implementation. A simple implementation may well just literally do what the spec says; a more complicated one may use a stack when there are no closures involved (for the speed benefit), or may always use a stack but then "tear off" the variable object needed for a closure when popping the stack. The only way to know in any specific case is to look at their code. :-)
More about closures, the scope chain, etc. here:
Unfortunatelly the answer is: It depends.
There was a big shift in recent javascript engines that started to optimize much better than they used to. The answer used to be: "Local variables are stored in heap-allocated stack frames for closures to work". It is not so simple anymore.
There has been (or used to be like 20-30 years ago) research for Scheme implementations and closure optimization (JavaScript inherited pretty much Scheme closures, except for continuations that make it even trickier).
I do not have the paper links ready, but if you do not have incredibly efficient garbage collector you need to use stack as well. The tricky part is then dealing with closures, which need to have variables heap-allocated. For that different strategies are used. The result is a hybrid where:
this field is changing really fast in several competing engines, so the answer will probably still be "it depends"
In addition, in new versions of the language we will be seeing features like let
and const
that actually make it easier for engines to optimize the allocation decisions. Especially immutability helps very much, since you can copy values freely off the stack (and make then part of the closure object for example) without resolving collisions of changing variables from different closures.
上一篇: 内存分配堆栈和堆