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?
Each boolean and number variable takes 8 bytes of memory.
Javascript allocates a contiguous memory for 'arr' after reading the first statement. Then javascript reads the second statement and allocates the memory for that element in memory. But here is the thing, it will not allocate memory for index 3 to index 49. Instead it will just allocate memory for index 50.
When things (string, array, object) are created, memory is allocated to them in JavaScript. Unlike Low-Level Programming Language C, JavaScript does not have alloc(), malloc(), free() primitive to do a job for them. Memory is automatically freed when that is no longer useful. This process is called Garbage Collection.
Variables are named memory locations. When we declare a variable in JavaScript, it means that a memory location is allocated to store some value and that location can be accessed using the variable name.
It's actually a very interesting area of JavaScript, and there are at least two answers:
In terms of the specification: 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 lexical environment for that call is created, which has something called an environment record. To keep things simple, I'm going to refer to them both together as the "binding object" (there's a good reason they're separate in the specification, though; if you want to get deeper into it, set aside a few hours and read through the spec). The binding object contains bindings 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). A binding is a combination of a name (like a
) and the current value for the binding (along with a couple of flags we don't need to worry about here). An unqualified reference within the function (e.g., the foo
in foo
, but not the foo
in obj.foo
, which is qualified) is first checked against the binding object to see if it matches a binding on it; if it does, that binding is used. When a closure survives the function returning (which can happen for several reasons), the binding object for that function call is retained in memory because the closure has a reference to the binding object in place where it was created. So in specification terms, it's all about objects.
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. They may even use the stack for locals that do get used by the closure, but then move them into an binding object when the function returns so the closure continues to have access to them. (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 binding object gets created with these bindings (according to the spec):
a
and b
— the arguments to the functionc
— a local variable declared in the functionbar
— a function declared within the functionWhen foo
executes the statement c = a + b;
, it's referencing the c
, a
, and b
bindings on the binding 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 binding object for that specific call to foo
, the binding object survives (whereas in the normal case, there would be no outstanding references to it and so it would be available for garbage collection).
Later, when we call bar
, a new binding object for that call is created with (amongst other things) a binding called d
— the argument to bar
. That new binding object gets a parent binding object: The one attached to bar
. Together they form a "scope chain". Unqualified references within bar
are first checked against the binding object for that call to bar
, so for instance, d
resolves to the d
binding on the binding object for the call to bar
. But an unqualified reference that doesn't match a binding on that binding object is then then checked against its parent binding object in the scope chain, which is the binding object for the call to foo
that created bar
. Since that has a binding for c
, that's the binding used for the identifier c
within bar
. E.g., in rough terms:
+−−−−−−−−−−−−−−−−−−−−−−−−−−−+ | global binding object | +−−−−−−−−−−−−−−−−−−−−−−−−−−−+ | .... | +−−−−−−−−−−−−−−−−−−−−−−−−−−−+ ^ | chain | +−−−−−−−−−−−−−−−−−−−−−−−−−−−+ | `foo` call binding object | +−−−−−−−−−−−−−−−−−−−−−−−−−−−+ | a = 1 | | b = 2 | | c = 3 | | bar = (function) | +−−−−−−−−−−−−−−−−−−−−−−−−−−−+ ^ | chain | +−−−−−−−−−−−−−−−−−−−−−−−−−−−+ | `bar` call binding object | +−−−−−−−−−−−−−−−−−−−−−−−−−−−+ | d = 3 | +−−−−−−−−−−−−−−−−−−−−−−−−−−−+
Fun fact: This scope chain is how global variables work in JavaScript. Note the "global binding object" in the above. So in a function, if you use an identifier that isn't in the binding object for that function call, and isn't in any of the other binding objects between that and the global binding object, if the global binding object has a binding for it, the global binding is used. Voilà, global variables. (ES2015 made this a bit more interesting by having two layers to the global binding object: A layer used by old-fashioned global declarations like var
and function declarations, and a layer used by newer ones like let
, const
, and class
. The difference is that the older layer also creates properties on the global object, which you kind of access via window
on browsers, but the newer layer doesn't. So a global let
declaration doesn't create a window
property, but a global var
declaration does.)
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 binding object for a function call, and the spec makes clear that it's perfectly fine if the binding 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 binding 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With