I found such an explanation why cycles with variable declared with var in node is faster than in chrome:
Within a web browser such as Chrome, declaring the variable i outside
of any function’s scope makes it global and therefore binds it as a
property of the window object. As a result, running this code in a web
browser requires repeatedly resolving the property i within the
heavily populated window namespace in each iteration of the for loop.
In Node.js, however, declaring any variable outside of any function’s
scope binds it only to the module’s own scope (not the window object)
which therefore makes it much easier and faster to resolve.
Curious about a let statement in Ecmascript6: does it make calculations faster using more block scope declared variables in loops or it is just a safety measure against name collisions?
The goal with let was to have better scoping mechanism in JavaScript (no more wrapping things in anonymous functions just for the sake of scoping). Any performance gains are just cherry on the top.
Related
As I understand it, every time a JavaScript program begins running, the engine first creates an execution context, pushes this execution context into the call stack/execution stack, and then it creates a global object (window in the browser and global in Node) as well.
To create the execution context, the engine first goes through the creation phase, where it allocates space in memory for entire function definitions and variable declarations (hoisting). It maintains a reference to the outer scope (this creates the scope chain, but in the global execution context there isn't anything above it), and it also creates the this property within the execution context and sets it to the window object in the browser and module.exports in Node. Lastly, the engine then goes through the execution phase, where it executes the code line by line and assigns a value to each variable.
Am I right in differentiating the global execution context creation from the creation of the global object itself? I view both of them as operations that happen side by side but are not the exact same thing.
Yes, it's fair to say that the global context and the global object are separate concepts. One illustrating distinction is the this binding: a context defines what this refers to (in case of the global context: to the global object); whereas the global object has no property named "this".
At the same time, global context and global object are somewhat coupled insofar as local variables in the former are properties on the latter.
Note that "execution context" is mostly an abstract concept, that means an engine only has to behave "as if" it did what the spec describes. Chances are that high-performance engines will take certain shortcuts (e.g., optimized code might keep some local variables in registers or on the machine stack, never putting them into any context at all).
I understand the semantics that a closure holds a reference to a variable lengthen it's life cycle, makes primitive variables not limited by calling stack, and thus those variables captured by closures should be specially treated.
I also understand variables in same scope could be differently treated depends on whether it was captured by closures in now-days javascript engine. for example,
function foo(){
var a=2;
var b=new Array(a_very_big_number).join('+');
return function(){
console.log(a);
};
}
var b=foo();
as no one hold a reference to b in foo, there's no need to keep b in memory, thus memory used could be released as soon as foo returns(or even never created under furthur optimization).
My question is, why v8 seems to pack all variables referenced by all closures together in each calling context? for example,
function foo(){
var a=0,b=1,c=2;
var zig=function(){
console.log(a);
};
var zag=function(){
console.log(b);
};
return [zig,zag];
}
both zig and zag seems to hold a reference to a and b, even it's apparent that b is not available to zig. This could be awful when b is very big, and zig persists very long.
But stands on the point of view of the implementation, I can not understand why this is a must. Based on my knowledge, without calling eval, the scope chain can be determined before excution, thus the reference relationship can be determined. The engine should aware that when zig is no longer available, nether do a so the engine mark it as garbage.
Both chrome and firefox seems to obey the rule. Does standard say that any implementation must do this? Or this implementation is more practical, more efficient? I'm quite puzzling.
The main obstacle is mutability. If two closures share the same var then they must do so in a way that mutating it from one closure is visible in the other. Hence it is not possible to copy the values of referenced variables into each closure environment, like functional languages would do (where bindings are immutable). You need to share a pointer to a common mutable heap location.
Now, you could allocate each captured variable as a separate cell on the heap, instead of one array holding all. However, that would often be more expensive in space and time because you'd need multiple allocations and two levels of indirection (each closure points to its own closure environment, which points to each shared mutable variable cell). With the current implementation it's just one allocation per scope and one indirection to access a variable (all closures within a single scope point to the same mutable variable array). The downside is that certain life times are longer than you might expect. It's a trade-off.
Other considerations are implementation complexity and debuggability. With dubious features like eval and expectations that debuggers can inspect the scope chain, the scope-based implementation is more tractable.
The standard doesn't say anything about garbage collection, but gives some clues of what should happen.
Reference : Standard
An outer Lexical Environment may, of course, have its own outer
Lexical Environment. A Lexical Environment may serve as the outer
environment for multiple inner Lexical Environments. For example, if a
Function Declaration contains two nested Function Declarations then
the Lexical Environments of each of the nested functions will have as
their outer Lexical Environment the Lexical Environment of the current
execution of the surrounding function."
Section 13 Function definition
step 4: "Let closure be the result of creating a new Function object as specified in 13.2"
Section 13.2 "a Lexical Environment specified by Scope" (scope = closure)
Section 10.2 Lexical Environments:
"The outer reference of a (inner) Lexical Environment is a reference to the Lexical Environment that logically surrounds the inner Lexical Environment.
So, a function will have access to the environment of the parent.
New to closures and the inner workings of JS. I have a somewhat stable grasp of execution contexts and the associated objects within it. And while I know how to identify a closure and what it may yield, I don't quite see where the parent variables reside, once the parent function is popped from the stack.
I'd assume they become properties of the closure's variable object? But you know where that leads.
I don't quite see where the parent variables reside
They reside in scope. How that is expressed in the computer's memory is an implementation detail of the specific JavaScript runtime (which is probably written in C or C++).
A common Javascript pattern is:
(function() {
var localScopedValue = "foobar";
// code... functions...
}());
The self-executing or immediately executed anonymous function creates a scope for everything inside it to prevent pollution of the global scope, and it also theoretically prevents anyone from getting references to either the function object or anything defined inside it from an outside script, browser console, etc. That's the idea, anyway.
However, Javascript being a dynamic language with reflection, etc., I am wondering if there is any way -- no matter how hackish (but not editing any existing code) -- of getting references to locals that are not deliberately exposed from Javascript code.
Reason: pure curiosity / breaking things.
Adding: Things I'm looking at include Function.caller and event listeners, basically anything where items inside the IIFE are known to call/hook out, but I can't put together the complete package.
JS doesn't have "real" reflection - all it has is the ability to access the properties of an object using a variable as a key with myObject[myVar] syntax instead of myObject.myProperty.
This therefore provides no access to lexically scoped private variables.
I watched this: http://www.youtube.com/watch?v=mHtdZgou0qU yesterday, and I've been thinking about how to improve my javascript. I'm trying to keep everything he said in mind when re-writing an animation that looked really choppy in firefox.
One of the things I'm wondering is if for loops add on to the scope chain. Zakas talked a lot about how closures add on to the scope chain, and accessing variables outside of the local scope tends to take longer. With a for loop, since you can declare a variable in the first statement, does this mean that it's adding another scope to the chain? I would assume not, because Zakas also said there is NO difference between do-while, while, and for loops, but it still seems like it would.
Part of the reason I ask is because I often see, in JS libraries, code like:
function foo(){
var x=window.bar,
i=0,
len=x.length;
for(;i<len;i++){
//
}
}
If for loops did add another object onto the chain, then this would be very inefficient code, because all the operations inside the loop (assuming they use i) would be accessing an out-of-scope variable.
Again, if I were asked to bet on it, I would say that they don't, but why then wouldn't the variables used be accessible outside of the loop?
JavaScript does not have block scope and it has variable hoisting, so any variables that appear to be defined in a for loop are not actually defined there.
The reason you see code like provided in your example is because of the hoisting behaviour. The code's author knows about variable hoisting so has declared all the variables for the scope at the start, so it's clear what JavaScript is doing.