How to comprehend clause 10.5 in ECMA-262 5.1 Edition? - javascript

Recently I read about the ES5 Specification, there's one confusion in Chapter-10 which is about Execution Context. More exactly, the confusion exists in 10.5[ https://ecma-international.org/ecma-262/5.1/#sec-10.5 ].
The clause 10.5 named Declaration Binding Instantiation, it explains how the component VariableEnvironment of Execution Context is generated.Where Im confused is the item-5-iii: " If existingProp .[[Configurable]] is true... ".
What's the purpose for this, why the PropertyDescriptor.[[Value]] is undefined when call [[DefineOwnProperty]] of global object, and how to prove this step with real javascript code?
Or maybe this is a mistake? Here the [[Value]] should be the declared function object?

When a function is declared on the top level, it checks to see if the property name exists on the global object first. If the property does not exist, then:
c. Let funcAlreadyDeclared be the result of calling env’s HasBinding concrete method passing fn as the argument.
d. If funcAlreadyDeclared is false, call env’s CreateMutableBinding concrete method passing fn and configurableBindings as the arguments.
Otherwise, it goes into the e. part that you're looking at:
e. Else if env is the environment record component of the global environment then: ...
So, anywhere inside that e., funcAlreadyDeclared will necessarily be true - the property is already defined, and what remains is to check to see if the property is changeable. The PropertyDescriptor.[[Value]] will necessarily return a full property descriptor, because inside e., we know that the property does exist; that block only runs if funcAlreadyDeclared is true.
On the top level, it checks if the property is configurable, and if so, sets the associated property on the global object. Eg, function foo(){} on the top level will result in window.foo being defined, and this section checks that window.foo can be defined.
Having configurable of true means:
true if and only if the type of this property descriptor may be changed and if the property may be deleted from the corresponding object.
For example, window.top is not configurable, so the [[DefineOwnProperty]] will not run:
console.log(Object.getOwnPropertyDescriptor(window, 'top'));
So, trying to declare a function named top on the top level will throw an error:
function top() {
}

Related

`eval` declaration instantiation when calling context is evaluating formal parameter initializers

The note below the PerformEval abstract operation says:
The eval code cannot instantiate variable or function bindings in the
variable environment of the calling context that invoked the eval if
the calling context is evaluating formal parameter initializers or if
either the code of the calling context or the eval code is strict mode
code. Instead such bindings are instantiated in a new
VariableEnvironment that is only accessible to the eval code. Bindings
introduced by let, const, or class declarations are always
instantiated in a new LexicalEnvironment.
Question:
What is meant by "if the calling context is evaluating formal parameter initializers"? What is a "parameter initializer"?
There is only one other reference to the term "parameter initializer" in the spec in Note 3 of 9.2.10. This note says:
Parameter Initializers may contain direct eval expressions. Any top
level declarations of such evals are only visible to the eval code
(10.2). The creation of the environment for such declarations is
described in 14.1.22.
I interpreted this as meaning that if a parameter initializer expression contains an eval call, then any variables created inside that eval call are not instantiated in the parameter scope. So, in the example below, I was expecting a ReferenceError to be thrown since c shouldn't be instantiated in the parameter scope:
function t(a = eval('var c = 8'), b = () => c) {
console.log(b())
}
t() // 8
But as you can see from the console output, my interpretation isn't correct. Even though c was created inside an eval call, its value is still visible to the funarg defined by b and can thus be printed to the console.
Turns out you found a specification bug! I asked in TC39's Matrix chat and they've created a PR to remove this note:
https://github.com/tc39/ecma262/pull/2428
The behavior in the note used to be correct, but was removed in 2017 in https://github.com/tc39/ecma262/pull/1046

Javascript scoping change

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).

JavaScript time of hoisting

In JavaScript does hoisting occur before or after the creation of the ExecutionContext associated with the function?
I presume before, if so when?
function foo() {
bar();
var x; // When is this hoisted?
function x() {} // ...and when is this hoisted?
};
foo();
Edit: put another way, I believe a graph of objects is instantiated according to the nesting of scopes in the program code, before a single line of code is run. Is the hoisting information stored in this object graph before runtime?
The hoisting happens as part of establishing the execution context:
[[Call]]
When the [[Call]] internal method for a Function object F is called with a this value and a list of arguments, the following steps are taken:
Let funcCtx be the result of establishing a new execution context for function code using the value of F's [[FormalParameters]] internal property, the passed arguments List args, and the this value as described in 10.4.3.
Let result be the result of evaluating the FunctionBody that is the value of F's [[Code]] internal property. If F does not have a [[Code]] internal property or if its value is an empty FunctionBody, then result is (normal, undefined, empty).
[...]
So, when a function is executed, first its execution context is created and then the function body is evaluated. How that happens is described in section 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.
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.
The hoisting happens in step 9 and is explained in detail in section 10.5. Specifically:
5. For each FunctionDeclaration f in code, in source text order do ...
[...]
8. For each VariableDeclaration and VariableDeclarationNoIn d in code, in source text order do ...
So function declarations are hoisted before variable declarations.

How does JavaScript determine when to give a function call a "this" context? [duplicate]

This question already has answers here:
Cases where 'this' is the global Object in Javascript
(4 answers)
Closed 9 years ago.
For obvious reasons, in JavaScript, the following two calls are different:
foo.bar();
var bar = foo.bar;
bar();
Namely, in the first call, this is the foo object. In the second, it's a reference to the global scope. However, the following example is a little less intuitive:
(foo.bar)();
I would expect it to operate the same way as the second example, but it actually operates the same as the first. That is, this references foo, not the global scope.
What are JavaScript's rules for deciding when to make a function call a "method call" and when to simply call the function without a particular this?
EDIT:
As Felix Kling points out in a comment, I'm wondering why the third example doesn't use the window context when theoretically it should simply retrieve the function and call it without the additional context. His example clarifies my question a little:
(true && foo.bar)(); // 'this' refers to the global scope
That's a tricky one and boils down to the inner workings of the ECMAScript standard. The definition of the grouping operator is:
The production PrimaryExpression : ( Expression ) is evaluated as follows:
Return the result of evaluating Expression. This may be of type Reference.
With the added note:
This algorithm does not apply GetValue to the result of evaluating Expression. The principal motivation for this is so that operators such as delete and typeof may be applied to parenthesised expressions.
So this is the key: The result can be of type Reference. A Reference is an internal data type which consists of a base value and a referenced name.
For example evaluating the member expression foo.bar, results in a Reference with base value foo (the object) and the referenced name "bar" (simply a string representation of the identifier).
GetValue(ref) is the internal function which actually accesses the property of the object and returns the value of the property (the function object in this examples). Most operators call GetValue on their operands do resolve these References, but not the grouping operator.
Looking at how CallExpressions are evaluated might also give an idea of how this and References work. For example, one step is:
Let thisValue be the result of calling the ImplicitThisValue concrete method of GetBase(ref).
So, if you have a Reference value and try to call it, the value of this will be set to the base value of the Reference (foo in the above example).
Regarding my example (true && foo.bar)();: The && operator calls GetValue() on both of its operands, so the result of the grouping operator is not a Reference.
Anything of the form foo.bar() is calling the bar method of the foo object.
However in the case of var bar = foo.bar; bar();, you are creating a reference to the method and then calling the method with no context - hence it is given the default global context.
You can (except in old IE) do this: var bar = foo.bar.bind(foo); bar(); to have the function use the foo context.

VariableEnvironment's functions

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).

Categories

Resources