Does a function expression have its own scope/lexical environment - javascript

I'm reading the Execution Context / Lexical Environment section of the ECMA 262 5 specification. It states the following: (emphasis added)
A Lexical Environment is a specification type used to define the association of Identifiers to specific variables and functions based upon the lexical nesting structure of ECMAScript code. A Lexical Environment consists of an Environment Record and a possibly null reference to an outer Lexical Environment. Usually a Lexical Environment is associated with some specific syntactic structure of ECMAScript code such as a FunctionDeclaration, a WithStatement, or a Catch clause of a TryStatement and a new Lexical Environment is created each time such code is evaluated.
I noticed that it says nothing about creating a lexical environment for Function Expressions. Is a lexical environment created for function expressions, or is one created only for function declarations? Am I missing something?
Edit: I notice that function code will have its own execution context, which is why I'm also confused why function expression is not mentioned in the lexical environment section.

Yes, every function gets (§10.4.3) its own ExecutionContext when it is called (§13.2.1). That new context is initialised with a new LexicalEnvironment (created by NewDeclarativeEnvironment, §10.2.2.2), deriving from the [[Scope]] of the function - i.e. the LexicalEnvironment it was declared/"expressed" in (§13).
As #Pointy pointed out, the sentence you stumpled upon does not exhaustively list them: "…some [structure] such as…".

If a name is included in a FunctionExpression, that name becomes a read-only binding that shadows any outer declarations of the same name. But that binding may itself be shadowed by a formal parameter or local declaration within the function. Such a binding for the function name is only created for named FunctionExpressions and not for anonymous FunctionExpressions or FunctionDeclarations. The name binding for a FunctionDeclaration is created in the surrounding VariableEnvironment.
Here is a more detailed explantion referencing the ES5.1 spec:
There is more than one environment record associated with a function object. Whenever a function is called a new DeclarativeEnvironmentRecord is created to contain the local bindings of that particular function invocation. That record becomes both the VariableEnvironment and the initial LexicalEnvironment of the ExecutionContext that is created for that invocation. This is specified in section 10.4.3.
When this environment record is created its "outer environment" is set to the value of the [[Scope]] internal property of the function object that is being called. (line 5, 10.4.3) The outer environment provide the bindings for all non-local declarations. The [[Scope]] is set when a function object is created (see the semantics in section 13 and also 13.2). So, each distinct invocation of a specific function object has a different local environment but all invocations of that function share the same outer [[Scope]].
For most functions, the captured [[Scope]] is simply the LexicalEnvironment of the ExecutionContext that was active when the function is created. However FunctionExpressions that include an Identifier as the function name have an extra DeclarativeEnvironmentRecord insert at the head of its [[Scope]] chain. (see steps 1-3 of the third algorithm in section 13).
This extra environment record is used to capture the binding for the function name given in the FunctionExpression.

An instantiated function has a scope. It doesn't matter whether it was instantiated as part of a function declaration statement or a function instantiation expression.
(It's probably more correct to say that an instantiated function has a scope when it is called, and that every call produces a distinct scope.)

Related

Difference between Lexical Environments and Environment Records in modern ecmaScript

I try to understand how closure is working under the hood. So I've read a lot of documentation and so many different websites.
Then I read this Variable Environment vs lexical environment
And in old documentation (2011) https://262.ecma-international.org/5.1/#sec-10.2
there are two different things, LE and ER.
But in modern https://262.ecma-international.org/12.0/#sec-environment-records
There is only one, environment record, and description of it is very similar to the old describe of lexical environments/
So, I am really confusing. Does LE still exist for modern javaScript (ecmaScript 6+)? What the difference between LE and ER?
Let's first go through the definition of Lexical Environment & Environment Record as per different versions of ECMAScript Specification.
From ES2015 till ES2020 Specification:-
1. Lexical Environment:
A Lexical Environment is a specification type used to define the association of Identifiers to specific variables and functions based upon the lexical nesting structure of ECMAScript code.
A lexical environment is consists of two components:
Environment Record
Reference to outer environment
2. Environment Record:
An Environment Record records the identifier bindings that are created within the scope of its associated Lexical Environment. It is referred to as the Lexical Environment's EnvironmentRecord.
Conceptual look using pseudo-code:
context.environment = {
// storage
environmentRecord: {
<identifier> : <value>
<identifier> : <value>
}
// reference to the parent environment
outer: <...>
}
Note: - The [[Environment]] created inside Execution Context is of type Lexical Environment[refer ES2020]
According to 12th Edition ECMAScript2021 Specification:
1. Environment Record
A Environment Record is a specification type used to define the association of Identifiers to specific variables and functions, based upon the lexical nesting structure of ECMAScript code.
Every Environment Record has an [[OuterEnv]] field, which is either null or a reference to an outer Environment Record.
Conceptual look using pseudo-code:
context.environment = {
// storage
<identifier> : <value>
<identifier> : <value>
// reference to the parent environment
outer: <...>
}
Note: - The [[Environment]] created inside Execution Context is of type Environment Record[refer ES2021]
Let's also understand the Structure of execution context
Execution Context:
An Execution Context is a specification device that is used to track the runtime evaluation of the code.
To keeps the track of execution progress of its associated code, it needs various state components like LexicalEnvironment, VariableEnvironment, etc.
In pseudo-code:
ExecutionContext = {
VariableEnvironment: { ... },
LexicalEnvironment: { ... },
// other components
}
Note:
Till ES2020
From ES2021
- The LexicalEnvironment component and VariableEnvironment component of an execution context are always Lexical Environments[refer ES2020]
- The LexicalEnvironment component and VariableEnvironment components of an execution context are always Environment Records[refer ES2021]
So, Yes Lexical Environment still exists for Modern JavaScript[ES2021] but now the [[Environment]] created is of type Environment Records.

JavaScript function scope property - at what point in a closure is it set?

The following function returns an object. It's taken from a MDN document.
This object that is returned has properties that are functions, which reference the "private" function changeBy().
I'm pretty sure that the only way an increment() call will resolve the changeBy() function call is to have access to the scope chain - the scope of the enclosing/parent function.
Q: Is the scope chain property set on the increment() function where it is declared, even though it is declared inside an object, and has not been invoked at that point?
Note
I'm being pointed to How do JavaScript closures work? as a possible duplicate.
The magic is that in JavaScript a function reference also has a secret
reference to the closure it was created in — similar to how delegates
are a method pointer plus a secret reference to an object
Q: The secret bit is what I'm asking about, which is not explained in the answer given in there. Where is this reference stored?
var makeCounter = function() {
var privateCounter = 0;
function changeBy(val) {
privateCounter += val;
}
return {
increment: function() {
changeBy(1);
},
value: function() {
return privateCounter;
}
}
};
var counter1 = makeCounter();
alert(counter1.value()); /* Alerts 0 */
counter1.increment();
alert(counter1.value()); /* Alerts 1 */
I found the answer to the level of detail I was looking for in the ECMAScript specs. This is pretty in-depth, but it explains the function creation/initialization process, and the function initialization process when created within an Object Literal
ECMAScript 5.1 spec
Function Being Created
When a function is being created, as opposed to called, step 9 covers the scope property:
Where the Lexical Environment is specified by [[Scope]], and F is the new Function, and the Scope is the current functions scope/lexical environment:
9: Set the [[Scope]] internal property of F to the value of Scope
Entering Function Code 10.4.3
Let localEnv be the result of calling NewDeclarativeEnvironment passing the value of the [[Scope]] internal property of F as the
argument.
Set the LexicalEnvironment to localEnv.
Function Created in Object Literal 11.1.5
The part I was interested in is "Pass in the LexicalEnvironment of the running execution context as the Scope"
Let closure be the result of creating a new Function object as specified in 13.2 with an empty parameter list and body specified by
FunctionBody. Pass in the LexicalEnvironment of the running execution
context as the Scope. Pass in true as the Strict flag if the
PropertyAssignment is contained in strict code or if its FunctionBody
is strict code.
ECMAScript 2016
Set the [[Environment]] internal slot of F to the value of Scope.
Is the scope chain property set on the increment() function where it is declared, even though it is declared inside an object, and has not been invoked at that point?
Yes.
increment is associated with the lexical environment in which it occurs, and has access to all variables in that environment, even after being "released into the wild" by being returned, whether it's returned directly, or as you are doing, as a value in an object being returned.
The secret bit is what I'm asking about, which is not explained in the answer given in there. Where is this reference stored?
It's written on a post-in stuck to the refrigerator door. No, seriously, it's stored inside the engine as part of its internal data structures while the JS is being parsed and interpreted.
By the way, you're misusing the term "closure". "Closure" does not simply mean "function", anonymous or otherwise. It refers to a particular interaction and behavior between variables in an outside scope (which are "closed over") and functions defined within that scope which reference them ("close over them").

Variable/Lexical environments

As said in sec 10.4.3
The following steps are performed when control enters the execution
context for function code contained in function object F, a caller
provided thisArg, and a caller provided argumentsList:
If the function code is strict code, set the ThisBinding to thisArg.
Else if thisArg is null or undefined, set the ThisBinding to the global object.
Else if Type(thisArg) is not Object, set the ThisBinding to ToObject(thisArg).
Else set the ThisBinding to thisArg.
Let localEnv be the result of calling NewDeclarativeEnvironment passing the value of the [[Scope]] internal property of F as the
argument.
Set the LexicalEnvironment to localEnv.
Set the VariableEnvironment to localEnv.
Let code be the value of F‘s [[Code]] internal property.
Perform Declaration Binding Instantiation using the function code code and argumentsList as described in 10.5.
Consider the following code snippet:
function foo(){
var a={p:'p'};
o={c:'c'};
}
Thus we have the following:
Code of our function isnt a strict code
thisArg is null hence, ThisBinding set to the global object
---
---
I dont understand what bindings will be contains environment record represented by [[Scope]] internal property.
Set the LexicalEnvironment to environment which geted at step 5.
Set the VariableEnvironment to environment which geted at step 5.
Perform declaration binding instatiation.
At step 8 bindings are created in the VariableEnvironment, but not in LexicalEnvironment. But in sec 10.3 said that
When an execution context is created its LexicalEnvironment and
VariableEnvironment components initially have the same value.
Question:
Why just after creation of execution context LexicalEnvironment and VariableEnvironment is still equal in my case above?
I am not sure I understand your question, but this is how I understand sec 10.4.3 :
steps 1 to 4 are dealing with the value of this. Basically, in strict mode, this will be left to a null or undefined value instead of defaulting to the global object (window in case of a browser). This covers the cases when a function is not invoked through usual object or event handler mechanisms.
step 5 to 7 mean that a new naming environment is created each time you enter a function.
It describes the creation of this environment, which is linked to the previous one to form the current name scope.
For each new function, two environments are co-existing.
When a name is resolved, Lexical environment is searched first, then Variable environment. If both searches fail, the process is repeated at the upper level of the environment chain, until the 'catch-all' global scope is encountered. In this scope, all identifiers are handled as properties of the global (window) object. You can picture it as the whole code being enclosed in a with (window) block.
Lexical environment can be seen as a temporary augmentation of the variable scope.
Lexical and Variable environments are functionnally identical until you alter the lexical environment with two specific statements: with or catch. It does not mean they are implemented as identical data structures.
In terms of implementation, you could imagine the lexical environment as an empty list and the variable environment as a list containing all local variable and parameter names.
When a catch or with statement is encountered, the lexical list is populated with new names that will take precedence over the ones stored in the variable list.
catch will simply make its argument available for name resolution (i.e. allow you to reference the exception parameter). No big deal since the new name is just as explicit as a function parameter.
with is a rather more dangerous beast. It will create a new environment with the names of all properties of the object passed as its argument. The scope will consist of the chain of variable environments plus this new lexical environment.
Here the new names available for resolution are 'hidden' inside the object.
For instance:
var a = 'a', b = 'surprise!', o = {a:'a'};
with (o) { a = b; }
console.log (a+" "+b+" "+o.a);
will yield
a surprise! surprise!
a is resolved as o.a since o contains a property named a.
b is not found inside the lexical environment and thus the current variable environment is tried and variable 'b' is found.
This is a pretty dangerous mechanism, because if you believe an object contains a given property while it actually doesn't, you will instead reference a variable outside the current scope.
For instance, a simple typo like this:
with (element.style) {leftt = '10px';}
will set the window.leftt property to '10px', unless you happen to have declared a variable named leftt somewhere in the current scope.
Now if you give silly names like 'i' or 'j' to your object properties, chances are you will clobber a random loop index somewhere up the scope chain while believing you are setting the property of an object.
step 8 describes the parameters binding once the function scope is established. Basically, the parameters are bound with the values and their names are added to the variable environment.
The whole point of keeping two separate environments is that the hoisting mechanism always uses the variable environment chain as a scope.
The idea is that a variable or function should behave as if it had been declared at the top of the current scope block, so for instance a function declared inside a with block should not resolve its names with the object properties exposed by the with statement.
Frankly this is a rather moot point, since ECMA spec does not allow function declarations inside blocks, although most implementations do, with varrying results.
Now for your example:
function foo(){
var a={p:'p'};
o={c:'c'};
}
Your function does not contain any with or catch statements, so the scope chain inside 'foo()' is just a list of two variable environments :
global (a bunch of DOM objects all seen as properties of 'window')
function foo (var a)
once you call foo(),
a will resolve as foo's local variable, will be created as an object with the property p of value 'p' (and garbage collected as soon as you leave foo(), unless you manage to reference it from a persistent variable).
o will not be found in foo's variable environment, so it will be caught by the 'catch-all' global scope and thus resolved as a (new) property of the window object. It will create a window.o.c property with the value 'c'.
Does that answer somewhat to your question?

Value of variable and lexical environment after creating execution context

I have global code snippet:
function foo(){
var a={a:'a'};
var b={b:'b'};
}
What is the value of Variable/LexicalEnvironment just after creation of foo's execution context? I think that VariableEnvironment's environment record must contain bindings foo--> function(){ }, a-->{a: 'a'}, b-->{b: 'b'}, but LexicalEnvironment's environment record must contain a global environment's environment record.
An execution context contains a lexical environment, a variable environment and a this binding.
(source)
So, to clarify:
An execution context contains both a lexical environment and a variable environment
They're different things.
Environment contains two types of environment records
declarative environment record - these directly associate an identifier with a language value.
object environment record - these define the effect ecmascript elements (such as with on the properties of some object.
So - to answer your question.
If we check the spec it clearly states:
When an execution context is created its LexicalEnvironment and VariableEnvironment components initially have the same value. The value of the VariableEnvironment component never changes while the value of the LexicalEnvironment component may change during execution of code within an execution context.
So, in your case of entering the function foo, the process is specified in 10.4.3 "entering function code" the VariableEnvironment is:
Let localEnv be the result of calling NewDeclarativeEnvironment passing the value of the [[Scope]] internal property of F as the argument.
Set the LexicalEnvironment to localEnv.
Set the VariableEnvironment to localEnv.
What is [[Scope]]? It's defined when the function is created and is specified in 13.2 Creating function objects.
So, like we said before, the VariableEnvironment and LexicalEnvironment are identical, they both have an empty (no binding) declarative environment record, and an outer lexical environment reference of the environment to the [[scope]]
So, the [[Scope]] internal property of foo is passed, that is detected by the parsing and contains the references to a and b and the function foo itself.
As already established in your previous question, VariableEnvironment and LexicalEnvironment refer to the same environment. But the environment which LexicalEnvironment refers to might change during the execution of the function. From the spec:
When an execution context is created its LexicalEnvironment and VariableEnvironment components initially have the same value. The value of the VariableEnvironment component never changes while the value of the LexicalEnvironment component may change during execution of code within an execution context.
So here is what happens when foo is about to be executed, in JavaScript-ish pseudo code:
// foo.[[scope]] refers to the global lexical environment
localEnv = new NewDeclarativeEnvironment(foo.[[scope]])
LexicalEnvironment = localEnv
VariableEnvironment = localEnv
env = VariableEnvironment.envRec
env.CreateMutableBinding('a', false)
env.SetMutableBinding('a', undefined, false)
env.CreateMutableBinding('b', false)
env.SetMutableBinding('b', undefined, false)
I.e. the environment record of the lexical environment has two entries, a and b. I don't know why it is necessary to have both, LexicalEnvironment and VariableEnvironment, but that's how the language is designed.

Enclosure Memory Copies

function myClass()
{
//lots and lots of vars and code here.
this.bar = function()
{
//do something with the numerous enclosed myClass vars
}
this.foo = function()
{
alert('Hello'); //don't do anything with the enclosed variables.
}
}
Each instance of myClass gets its own copy of bar and foo, which is why prototyped methods use less memory. However, I would like to know more about the memory use of inner methods.
It seems obvious to me that (1) below must be true. Do you agree?
Not only do distinct myClass instances have their own distinct copies of bar, but they also must have their own distinct copies of the myClass enclosure. (or else how does the bar method keep the myClass variables straight on a per instance basis?)
Now (2) is the question I'm really after.
Since the inner foo method doesn't use anything in the myClass enclosure a natural qustion is: Is javascript smart enough to not keep a myClass enclosure in memory for the use of foo?
This will depend entirely on the implementation not the language. Naive implementations will keep far more around than others. Any answer that is true for e.g. V8 (chrome's JS engine) may not be true for Spidermonkey (Firefox's) or JScript (IE) etc...
In my experience (this is definitely how GHC handles closures), the actual code of the functions (inner or outer) only exists once, but an "environment" mapping variables to values must be kept around for the closure (bar), so no, I wouldn't expect the JS implementation to keep around more than one copy of MyClass, but it will keep around a copy of its environment at least with this.bar, sensible implementations might realize that no environment is actually needed foo is not actually a closure, and thus does not need to keep a copy of the environment from the call to MyClass however do not rely on this behaviour.
Especially since the behavior of JS's eval function which evaluates a string in the same lexical environemnt as the eval call, it is quite likely that JS implementations always keep the lexical environment around.
I'm reasonably certain that any function has the a pointer to the entire state. It's irrelevant whether it references any variables it still has access to them.
Of course you do release that both bar & foo point to the same "entire state" of the object. So it doesn't take any more memory. I guess you have to allocate one more pointer internally so you can point to the "entire state" of the object but optimising for something like that is silly.
Whether it optimises it away is javascript engine specific. By all means read the chromium source and find out.
I'll see if I can dig some quotes out of the spec for you.
Entering Function code from the spec:
10.4.3 Entering Function Code The following steps are performed when
control enters the execution context
for function code contained in
function object F, a caller provided
thisArg, and a caller provided
argumentsList:
If the function code is strict code, set the ThisBinding to thisArg.
Else if thisArg is null or undefined, set the ThisBinding to the
global object.
Else if Type(thisArg) is not Object, set the ThisBinding to
ToObject(thisArg).
Else set the ThisBinding to thisArg.
Let localEnv be the result of calling NewDeclarativeEnvironment
passing the value of the [[Scope]]
internal property of F as the
argument.
Set the LexicalEnvironment to localEnv.
Set the VariableEnvironment to localEnv.
That tracks your environment to NewDeclarativeEnvironment.
NewDeclarativeEnvironment
10.2.2.2 NewDeclarativeEnvironment (E) When the abstract operation
NewDeclarativeEnvironment is called
with either a Lexical Environment or
null as argument E the following steps
are performed:
Let env be a new Lexical Environment.
Let envRec be a new declarative environment record containing no
bindings.
Set env’s environment record to be envRec.
Set the outer lexical environment reference of env to E.
Return env.
This tracks your environment to E which is the [[Scope]] of the function Object
13.2 Creating Function Objects Given an optional parameter list specified
by FormalParameterList, a body
specified by FunctionBody, a Lexical
Environment specified by Scope, and a
Boolean flag Strict, a Function object
is constructed as follows:
Create a new native ECMAScript object and let F be that object.
Set all the internal methods, except for [[Get]], of F as described
in 8.12.
Set the [[Class]] internal property of F to "Function".
Set the [[Prototype]] internal property of F to the standard built-in
Function prototype object as specified
in 15.3.3.1.
Set the [[Get]] internal property of F as described in 15.3.5.4.
Set the [[Call]] internal property of F as described in 13.2.1.
Set the [[Construct]] internal property of F as described in 13.2.2.
Set the [[HasInstance]] internal property of F as described in
15.3.5.3.
Set the [[Scope]] internal property of F to the value of Scope.
Let names be a List containing, in left to right textual or
Turn's out the spec is just telling you things you already know. Every functions contain the Scope. As the other poster mentioned it is upto individual implementation to "optimise" some of the scope away in terms of memory management.
If you ask me you really don't care about this unless you have >million instances of these objects.
The following block from the spec is more applicable:
The outer environment reference is
used to model the logical nesting of
Lexical Environment values. The outer
reference of a (inner) Lexical
Environment is a reference to the
Lexical Environment that logically
surrounds the inner Lexical
Environment. 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 FunctionDeclaration contains two
nested FunctionDeclarations 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.
The two nested functions both have the same reference to the outer Lexical Environment
Well, let's ask some implementations what they do, shall we :-)
Spidermonkey: Internals-Functions -- agrees with other answers. Talks about how closures can be classified.
V8: Are Closures Optimized -- very terse but does mention "static optimizations". Various articles on the web talk about "hidden classes" which, I believe, are how the V8 GC tries to optimize closures.
Sadly those meager links are about all I can find. A direct analysis of the engine source-code is likely required to add more meaningful input.
So, yes, different engines do implement different optimization strategies when they can.

Categories