Take this factory function:
function factory(){
return factory_level2(){
return function complexCode{ // <= Code of interest
//...
}
}
}
const lvl2 = factory()
lvl2();
Does the manufactured function complexCode get instantiated every time the above code is called? Or is the compiler smart enough to know to optimize it into something like this:
function complexCode{
//...
}
function factory(){
return factory_level2(){
return complexCode // Returns reference rather than instantiates?
}
}
CodePudding user response:
The compiler generally cannot and should not optimize the code into your second example. The reason for this is because the scope of the two code are different - meaning they have different closures.
All modern javascript engine in common use (Spidermonkey, Squirrelfish and V8) will compile the instructions of the functions only once. So if this is what you are asking then yes. They are compiled only once and all calls to them will share the same bytecodes.
However, you used the word "instantiation". In OO languages, a class is normally only compiled once. Just like the description of how functions are compiled in javascript above. However, a class can be instantiated into many objects. All objects belonging to the same class will share the same executable code. However they don't share the memory of the variables they use (properties they have). All variables/properties are unique to each object.
The following is an example of OO in a fictional programming language:
class A {
property i;
property j;
method foo () {
print( i j );
}
}
variable x = new A();
variable y = new B();
x.i = 1;
x.j = 2;
y.i = 10;
y.j = 20;
x.foo(); // should print 3 instead of 30
// because x and y don't share the
// same memory
print( addressof x.foo == addressof y.foo ); // should print true
// because both "foo"
// are actually the same
Javascript functions make the same optimization. In your example factory and factory_level2 and complexCode will be compiled only once. Every call to them will execute code from the same memory address. However, each call will instantiate its own scope - the instance of the scope is the closure.
In most programming languages the behavior of function calls work the same but normally you don't need the concept of closures in some languages because they cannot return functions. Due to the fact that javascript can return a function the scope is instantiated and saved for future use - this long-term instantiation of scope is called a closure. In languages that cannot return functions the temporary activation of scope is called an activation record or also commonly known as the stack frame. The difference between a closure an a stack frame is that the stack frame is deleted at the end of the function call. In languages like javascript the runtime needs to check if the variable in the stack frame is linked to a returned function. If it is then it cannot be deleted because that function may be called in the future. It is thus saved as a closure.
The following is an example in another fictional language:
function A () {
variable i = 0;
return function () {
i ;
print(i);
}
}
variable x = A();
variable y = A();
print( addressof x == addressof y ); // should print true
x(); // prints 1
x(); // prints 2
y(); // prints 1 because even though x and y share the same
// function they don't share the variable i which is
// captured in a closure
Note that all the examples above are in a made up language to illustrate what I'm describing because certain things are difficult or impossible to demonstrate in javascript (such as getting the memory address of a function). However the concepts are true for most modern implementations of javascript (or indeed any implementation of javascript that has had enough pressure to be optimized)
CodePudding user response:
This is technically implementation-dependent. But any decent implementation will only compile the function once and reuse that.
If complexCode() is a closure (i.e. it references variables that are declared in factory() or factory_level2(), all the closure instances will reference the same code object, with different environment objects that contain the variable bindings for that instance.
This is similar to the way class methods are assigned once to the prototype object and reused by all the instances.
