Variable/Lexical Environment component of execution context
Question 1:
For what VariableEnvironment component of exection context is needed?
As said in spec LexicalEnvironment component
used to resolve identifier references made by code
within this execution context.
But VariableEnvironment component used to
holds bindings created by VariableStatements and FunctionDeclarations
within this execution context.
Ok, but evaluation of PrimaryExpression:Identifier is execution with LexicalEnvironment component, not VariableEnvironment of running execution context:
Let env be the running execution context‘s LexicalEnvironment.
If the syntactic production that is being evaluated is contained in a strict mode code, then let strict be true, else let strict be false.
Return the result of calling GetIdentifierReference function passing env, Identifier, and strict as arguments.
Thus is VariableEnvironment need for bindings storage only?
Establishing function context is occuring as:
sec. 10.4.3
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.
Where at step 9 bindings add to a VariableEnvironment only. At step 5 [[Scope]] internal property indicates to outer LexicalEnvironment. Thus i have question
Question 2:
Is it true that just after creation of execution context of function code LexicalEnvironement's initial environment record contains the same bindings that environment record of outer LexicalEnvironment?
Consider the following code snippet into the global code?
Example of function code
function bar(){
var b={b:'b'}
var o={o:'o'}
}
bar();
Question 3:
Is it true that just after creation of bar execution context before the start of execution function code VariableEnvironment's environment record will be contain bar-->function bar(){ ... }, b-->{b:'b'}, o--> {o:'o'}, but LexicalEnvironment's environment record
Question 1
VariableEnvironment is the leaf of the scope chain at the start of a function. Since
all variables and functions are hoisted at the top of the current function (or global scope), it defines the scope of identifiers visible strictly inside a given function.
LexicalEnvironment extends VariableEnvironment when you use the constructs with and catch. See my other answer for more details.
Both environments are used for name resolution. LexicalEnvironment is used most of the time, except in function bodies (i.e. a function within a function), where VariableEnvironment (of the parent function) is supposed to be used instead.
As I said in my previous answer, the case is rather unfrequent and various implementations behave differently.
I'm not sure I understand what you mean by 'binding storage'.
Bindings take place when applicable (i.e parameters passing or assignment) once the names are resolved, whichever environment allowed the resolution.
Basically, the names inside a VariableEnvironment are those of local variables (bound to heap objects when the variables are assigned) and parameters (bound to the passed values when the function is called, as described in steps 8-9 of spec 10.4.3).
The names in LexicalEnvironment will be either the name of the exception context for a catch statement or the names of an object's properties for a with statement. They will be bound to their corresponding instances when the statement is evaluated, and disposed of at the end of the statement body.
For instance:
function stick_to_top (element)
{
with (element.style) { top = 0; }
}
stick_to_top (document.getElementById ("wanderer"));
binding of top will occur when the statement top = 0; is executed, during function invokation.
Name resolution will resolve top as element.top using the Lexical context of stick_to_top() enhanced by the with statement that will contain the names of all element.style properties, and resolve element as stick_to_top()'s argument using the Variable context of stick_to_top(), which contains only the name of the parameter element.
In the end, value 0 will be assigned to property style.top of the DOM object having the ID "wanderer" (assuming getElementById() found it, of course).
Now if you're up for a joke, try this :
var tip = { style:{top:1}};
var top = { style:'', invaluable_information:42};
var tap = { };
stick_to_top (tip);
stick_to_top (top);
console.log ("tip %o top %o", tip, top);
stick_to_top (tap);
result:
tip [object Object] top 0
SCRIPT5007: Object expected
tip behaves just like a regular DOM object since we defined a style.top property for it
tap does not have a style property, so tap.style is undefined and the interpreter throws an error when trying to process the with statement.
top is the unlucky one. It does have a style property so the with statement is happly creating an empty Lexical environment. Now identifier top is nowhere to be found inside stick_to_top(), so name resolution goes back to global context where it happily finds an unsuspecting top variable. Inevitable tragedy ensues...
Question 2
Both Lexical and Variable environments are local to a context. The scope is made of a chain of environments, i.e. when resolving a name, if the identifier could not be found in the current environment, parent environments are tried in succession until you reach the toplevel environment where identifiers are considered properties of the global object.
(since you seem to be interrested mostly in variables, I leave aside the function prototype scope for the sake of concision)
So the answer to your question is no. In your example, the lexical environment of the toplevel is the collection of all global variables (i.e. the properties of the window object), while the lexical environment of foo() is identical to its variable environment, containing only the names of the two locals b and o.
Question 3
It will just contain b-->{b:'b'}, o--> {o:'o'}
bar-->function bar(){ ... } will be in the toplevel's lexical context.
the bar identifier will be a synonym of window.bar.
(here again, think of a JavaScript program being enclosed in an implicit with (window) statement).
Related
Regarding VariableEnvironment & LexicalEnvironment, it seems that:
Every function call/execution creates a new execution context, and with it entirely new VariableEnvironment & LexicalEnvironment object instances to represent the code/variables inside that function. (For this post, I’ll shorthand group these two into Var/LexEnvironment)
These new Var/LexEnvironment objects also contain references to already-created Var/LexEnvironment objects representing the function object’s outer scope. This is the scope chain.
VariableEnvironment holds function scope variable state.
LexicalEnvironment holds block { } scope variable state.
Full Closure Process Example:
Say we have two functions: function0 which contains function1.
When function1 is called, a new execution context is created = creates entirely new Var/LexicalEnvironment instances are created from code inside function1:
Only one VariableEnvironment instance, and one LexicalEnvironment instance for each* block { }.
The existing lexical environments (outer) are simply pointed to. (This is what's referred to as 'the scope chain'). Run the function0 that contains function1’s outer lexical environments, and you’ll get completely new instances of outer Var/LexEnvironments.
But function1’s references to outer lexical environments don’t change. They don’t become these new Var/LexEnvironments instances.
A function’s [[Scopes]] property = a representation of the Var/Lexical environments that pertain to this function object = a browser's representation of the [[Environment]] property defined in the ECMA spec.
You can view this by using console.dir(funcName) in browser console.
What's incorrect here? What am I missing?
Tl;dr:
It seems safe to think of everything in JS code as being a property of an execution context (either a global or function or eval() execution context). Why?
every execution context has unique lexical/var environments as properties. (New run = new ex. context = new instances of variableEnv objects = new variables with new references)
and these lexical/var environments contain all your variables (identifier-value mappings).
function function0(sizevar) {
s = sizevar * 2
return function function1() {
console.log(s)
console.log(sizevar);
};
}
var size12 = function0(12);
var size14 = function0(14);
So from the above^, when you return an embedded function1, you’re returning a reference to a function instance/object that’s a property of one specific execution context’s lexical/variable environment.
And when function0() returns function1(), scope chain is tied to an execution context’s state (i.e. its variableEnv), even if that execution context is done executing.
Within the ECMAScript specification, where can we find a clear specification on why let and const are not accessible outside of Lexical Environments created with BlockStatements (as opposed to variables declared with var)?
If BlockStatements now create new lexical environments, then let and const declarations should not create variables accessible outside that lexical environment, but var variables should. I am trying to understand where exactly that behaviour is specified in the latest ECMAScript specification.
From 13.3.1 Let and Const Declarations:
let and const declarations define variables that are scoped to the running execution context's LexicalEnvironment. The variables are created when their containing Lexical Environment is instantiated but may not be accessed in any way until the variable's LexicalBinding is evaluated.
From 13.3.2 Variable Statement:
A var statement declares variables that are scoped to the running execution context's VariableEnvironment. Var variables are created when their containing Lexical Environment is instantiated and are initialized to undefined when created.
As seen, both variable declarations create variables when their containing Lexical Environment is instantiated. Which, in the case of a BlockStatement is when the compiler enters the block.
From 8.3 Execution Contexts:
LexicalEnvironment and VariableEnvironment components of an execution context are always Lexical Environments
As you saw in the description of var, it is scoped to the running execution context's VariableEnvironment. There is a top level VariableEnvironment and then a new one is created when you enter a function and then in this part about Execution Contexts, it says this:
The LexicalEnvironment and VariableEnvironment components of an execution context are always Lexical Environments. When an execution context is created its LexicalEnvironment and VariableEnvironment components initially have the same value.
So, at the start of a function, the LexicalEnvironment and VariableEnvironment are one and the same.
Then, down in 13.2.13 Runtime Semantics: Evaluation Block: { }, you can see that a new LexicalEnvironment is created when you enter the block and the prior one is restored when you leave the block. But, there is no mention of a new VariableEnvironment when you enter or leave a block (because that stays constant within the function).
So, since let and const are scoped to the LexicalEnvironment in which they are declared and that is local to a block, it would not be accessible outside the block.
But, var is scoped to the VariableEnvironment which is only created and scoped to the whole function, not to a block.
let and const variables can't be accessed outside their LexicalEnvironment because their definitions are not in the scope hierarchy as soon as the execution context leaves their block (as soon as you leave the block, their LexicalEnvironment is essentially popped off the stack and is no longer in the scope search chain for the interpreter to find variables).
When the specification adds this:
The [let and const] variables are created when their containing Lexical Environment is instantiated but may not be accessed in any way until the variable's LexicalBinding is evaluated.
This means that you are denied access to them even in their own Lexical Environment until their definition is evaluated. In layman's terms, this means they are not hoisted to the top of their scope like var is and therefore cannot be used until after their definition. This is implemented by not initializing a let or const variable in the LexicalEnvironment until its statement runs and the GetBindingValue() operation which looks up a variable will see that it is not yet initialized and will throw a ReferenceError. var variables are initialized immediately to undefined so they do not cause this ReferenceError.
You can see how this works in this code:
let x = 3;
function test() {
x = 1;
let x = 2;
console.log("hello");
}
test();
At the let x = 3 line a variable x is initialized in the outer LexicalEnvironment.
Then, when you call test(), at the start of that function, a new LexicalEnvironment is created, the new declaration for x in this block is placed into that new LexicalEnvironment, but not yet initialized.
Then, you get to the x = 1 statement. The interpreter looks up x, finds it in the current LexicalEnvironment, but it is uninitialized, so it throws a ReferenceError.
Per the question in your comment:
I've been turning the spec upside down, but have a hard time spotting that VariableEnvironments are only created for functions. Could you perhaps add an answer showing what steps in the spec you follow to reach said conclusion?
You have to just go through all the places in the spec where a VariableEnvironment is created and you will find that the only places this happens is the beginning of a function execution and at the top level.
For example, here's one on those places: PrepareForOrdinaryCall. There are a few others.
But, no place does it ever describe this happening for the start of a block, only the start of a function.
The way these specifications are written, they describe when things do happen, not when they don't happen (which makes some logical sense), but it means to prove something doesn't happen, you have to fail to find anywhere that says it does happen.
I read a lot about javascript scoping but still can't seem to understand why this code (this line the left arrow points to) changing geval scope to be the global scope and not the function scope. geval is invoked inside test2 so I thought it would have the same scope..
const test2 = () => {
var x = 2, y = 4;
console.log(eval('x + y')); // Direct call, uses local scope, result is 6
var geval = eval; // equivalent to calling eval in the global scope
console.log(geval('x + y')); // Indirect call, uses global scope, throws ReferenceError because `x` is undefined
(0, eval)('x + y'); // another example of Indirect call
}
The following excerpts are taken from ECMA-262 7th edition (ECMAScript 2016) – section numbers sometimes differ between versions.
18.2.1.1 Runtime Semantics: PerformEval( x, evalRealm, strictCaller, direct)
...
9. If direct is true, then
a. Let lexEnv be NewDeclarativeEnvironment(ctx's LexicalEnvironment).
b. Let varEnv be ctx's VariableEnvironment.
10. Else,
a. Let lexEnv be NewDeclarativeEnvironment(evalRealm.[[GlobalEnv]]).
b. Let varEnv be evalRealm.[[GlobalEnv]].
So for an indirect call (not calling eval by name) you arrive at step 10 which calls NewDeclarativeEnvironment(E) with the global environment record as parameter value.
NewDeclarativeEnvironment is described in section 8.1.2.2. As expected this creates an environment record for any variables defined using let within script parsed by eval, and sets the "outer lexical environment reference" of that record to the environment record provided as argument.
Step 10.b sets the variable environment record, for named functions and variables declared with var, to the global environment record of the realm eval was called in – meaning window in a browser document.
In short, calling eval indirectly creates a separate environment record for let variables declared within the code being evaluated, whose next outer scope (lexical reference) is the global object, but uses the global object for var and named function declarations.
If you want to have evaluated code inherit the scope of surrounding code, make a direct call using the name eval as the function reference.
The presence of both 9.a and 10.a means that variables declared with let are not retained after a call to eval no matter what the type of call..
The Historical Why (edit)
The behavior of indirect calls is likely the result of deprecating calling eval on an object and removing the eval property from Object.prototype.
From the JavaScript 1.2 reference for Object data type methods:
eval Evaluates a string of JavaScript code in the context of the specified object.
and calling eval on an object in such early versions of JavaScript would result in code evaluation as if it were inside a with (object) {} statement.
The rules for indirect calls replace this behavior with a standard response: if an object method is set to eval the previous behavior of object.eval is not recreated and the scope of evaluated code is the global object exceot for let variables. This is similar to the way creating a function from text with new Function behaves as if it were defined in global scope. It also has the effect that the this value seen by indirect calls to eval is the global object (this values reside in environmental records).
var simpleObject={n:70};
simpleObject.testMethod=function(){
console.log(n);
}
Why do i need to write this.n inside console.log to show the output 70.
The 'this' keyword refers to the object itself, the property n is a member of the object, and thus it must be accessed via that namespace 'this.n'.
However if the variable was declared inside the same function (i.e. testMethod), via 'var n = 0', then within that same closure/scope you could access the variable with just 'n' (without 'this').
But the property n is outside the 'testMethod' function scope, and thus must be accessed via the proper namespace - hence 'this.n'.
JavaScript is a dynamic language. This essentially means that it uses dynamic name resolving and function objects. Thus you can take function from one object and assign it to another. Consider this:
var simpleObject={n:70};
simpleObject.testMethod=function(){console.log(this.n);}
var simpleObject2={foo:42};
simpleObject2.testMethod = simpleObject1.testMethod;
simpleObject.testMethod();
simpleObject2.testMethod();
Both objects here are using the same function, first call will print 70. But second will print undefined.
this is not necessary, it's just a way to do it. Another trivial way would be simpleObject.n.
However, it seems that you want the identifier n to be avaluated to a reference to simpleObject.n.
Identifiers are resolved using property lookups to the binding object of the environment record of the LexicalEnvironment of the running execution context. Initially, it is [[Scope]]:
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:
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.
To achieve what you want, you need to set the running execution context's LexicalEnvironment to NewObjectEnvironment(this, oldEnvironment).
You can achieve that using the width statement:
The with statement adds an object environment record for a
computed object to the lexical environment of the current
execution context. It then executes a statement using this augmented
lexical environment. Finally, it restores the original lexical
environment.
var simpleObject = {n: 70};
simpleObject.testMethod = function(){
with(this) {
console.log(n);
}
};
simpleObject.testMethod(); // 70
However, note that the use of with is discouraged, and won't work in strict mode.
Another way is taking advantage of the fact that the scope in event handler content attributes is the global one shadowed by the document, the form owner, and the element:
Let Scope be the result of NewObjectEnvironment(document, the global environment).
If form owner is not null, let Scope be the result of NewObjectEnvironment(form owner, Scope).
If element is not null, let Scope be the result of NewObjectEnvironment(element, Scope).
Then, you can let your object be an HTML element, and let your method be an internal raw uncompiled handler:
var el = document.createElement('div');
el.n = 70;
el.setAttribute('onclick', 'console.log(n)');
el.click(); // 70
ok this is some code
function myFunc(){
var myvar = 8;
function myFunc2(num){
alert(myvar+num);
}
myFunc2(2);
}
myFunc();
i want to clear my mind so pls correct me if am wrong
i have read allot of articles in stack overflow already but i want to know i understand it well or should i read more.
to my understanding what happens behind the scene is thatin global execution context there it creates function object with the namemyFunc` and its [[scope]] property assigned to global variable object.
and when i call myFunc it creates its own execution context and activation object where all of the function's arguments and function declaration is initialized before any line by line code execution.
when inner function object is created it's internal [[scope]] property is assigned the value of its outer execution context's variable object + global variable object so every function creates its own execution context but before that every function's internal [[scope]] property is assigned first.
i have read allot of articles in stack overflow already but i want to know i understand it well or should i read more.
Here are a couple of pointers based on my understanding of the specification, and based on what sounds unclear on your explanation:
The term "Activation object" was used in ECMAScript 3, but not anymore in the current version of the specification. ES5 uses the term "Lexical Environment" to denote a type (an internal type) consisting of an "Environment Record" value, and possibly a reference to an outer Lexical Environment.
Because of this reference to an outer Lexical Environment, scope can be thought of as a chain. So access to outer scopes (including the global scope) happens through that chain. (When you say that "[[scope]] property is assigned the value of its outer execution context's variable object + global variable object", it sounds like both records are copied into the current function's Lexical Environment, which is not how it happens.)
Hope this helps!