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
Related
In an arrow function there is no this binding. So, arrow functions lexically resolve this reference. If we execute a function in different scope from where the function was defined, it remembers this reference. Does this behaviour happens due to closure ?
I know that function can remember its lexical environment by closure but in chrome developer tool this doesnot appear in closure but in local scope. Why does this happens ?
function outerScope(outerVar){
let innerFun = () =>{
console.log(outerVar);
console.log(this);
}
return innerFun;
}
let innerFun = outerScope.call({test: "testing this reference"},"outerVar");
innerFun();
Does this behaviour happens due to closure ?
No
Your statement implies it's a side-effect of other mechanisms. It is deliberately part of the specification.
Section 14.2 Arrow Function Definitions deals with the subject and 14.2.17 Runtime Semantics: Evaluation has the following note attached:
An ArrowFunction does not define local bindings for arguments, super, this, or new.target. Any reference to arguments, super, this, or new.target within an ArrowFunction must resolve to a binding in a lexically enclosing environment. Typically this will be the Function Environment of an immediately enclosing function. [...]
(the rest of the note concerns calls to super and methods)
The note is not the definition but serves as a reminder. The rason is scattered through several different places* in the spec but the most relevant is that functions have an particular internal slot that section 9.2 ECMAScript Function Objects defines as:
Internal Slot
Type
Description
[[ThisMode]]
(lexical, strict, global)
Defines how this references are interpreted within the formal parameters and code body of the function. lexical means that this refers to the this value of a lexically enclosing function. strict means that the this value is used exactly as provided by an invocation of the function. global means that a this value of undefined is interpreted as a reference to the global object.
* the "several different places" are just about arrow functions having the [[ThisMode]] internal slot set to lexical.
This is the reason arrow functions use this from the lexical binding at their definition place rather than closures having an effect.
It might also be worth noting that "closure" is ultimately not a very useful term in many cases - every function definition forms a closure. A lot of the times there the mechanism leads to nothing special, so it's not really worth discussing it. It's only interesting (and worth mentioning) when a function uses variables defined outside of it. The ones it "closes over":
const a = 42; //<--+----+
// | |
function f() {// | |
return a; // --+ |
}// |
// |
function g() { } // ----+
Both f and g will have access to the variable a because they are defined in the same environment - the mechanism for their creation does not change based on their body. This can be easily verified (you need to open the browser developer tools for the breakpoint to trigger):
const a = 42;
function f() {
return a;
}
function g() { debugger; }
g();
With that said, g is simply not worth discussing in the vast majority of cases, as the closure mechanism doesn't have an impact.
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() {
}
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).
Looking at the doT.js source:
https://raw.github.com/olado/doT/master/doT.js
What does this do?
(function(){ return this || (0,eval)('this'); }()).doT = doT;
To me it looks like it's creating a global var, window.doT. If that's all it's doing, then why not:
window.doT = doT;
?
It's getting a reference to the global object, in order to assign doT to it. This is generally done because with a JavaScript library/framework/etc, its one global identifier needs to be exposed to the outside world.
As for why it's not simply window.doT = doT;, it's because the global object isn't always window, for example, in a non-browser environment. It's also possible to have window assigned to somewhere else at the point this code is executed.
How it works
If this is already truthy, for example, an object such as window, it will return that. It's likely it will be window (at least in the browser), as a plain function call should have its ThisBinding set to the global object. Otherwise, it will execute eval() in the global scope because an indirect call to eval() will set its scope to global, as opposed to the calling environment's scope.
To achieve an indirect call, you have to invoke eval() indirectly, i.e. you can't just call it with eval(). You can use (0, eval) to invoke it. This relies on the comma operator returning the last evaluated expression, in this case eval. It doesn't matter what the preceding operands are. Similarly, (0||eval)() would work.
As for why the body is this, that is the argument to eval(), that is the code to be executed as a string. It will return the this in the global scope, which is always the global object.
It's not really relevant nowadays, but in older IEs, you'd need to use execScript() to execute code in the global scope. I can't remember exactly what versions of IE this was necessary for.
Only adding a bit to the accepted answer as the last question in the condensed comments view "wouldn't (eval)('this') work as well?" has its answer lost in the comment chatter. Simple test:
> foo = 123
< 123
> (function() { const foo = 234; console.log(
foo, // baseline
eval('foo'), // direct eval using local scope
(eval)('foo'), // (eval) still direct
(1, eval)('foo'), // indirect by comma-operator
(false||eval)('foo')); // indirect by or-operator
})()
< 234 234 234 123 123
It shows that
(eval) is not the same as (0, eval) (or, here, (1,eval)) because the comma operator is indirect whereas the parenthesis is not indirect, just a syntactic grouping, the mere parenthesis does not pass its argument through an operation, so it remains direct.
the (false||eval) approach relies on the argument before being falsy, therefore the comma operator is better, it matters less what that first value is.
To me the comma-operator was the biggest aha here, that's why I was dwelling on it a bit.
I have a concern also, i.e., that this trick would by accident be used from a function that might be added to a prototype as a method. In that case the first this is no longer the global object. Which is why, to be really safe, in my opinion one should just stick with the (0,eval)('this') expression only.
One of the guarantees that strict mode provides is that in strict function code, the identifier arguments always refers to that function's Arguments object.
function fn () {
'use strict';
// malicious code
arguments // still refers to the function's Arguments object
}
So, no matter what code is injected at // malicious code, the arguments identifier is immutably bound to the function's Arguments object during the entire function invocation.
I was wondering if the same guarantees are provided for the eval identifier, i.e. does the eval identifier with guarantee refer to the built-in global eval function at all times?
I'd like to point out that the above mentioned guarantee is not provided if our strict code is nested within non-strict code. Non-strict code is allowed to create local "eval" bindings, or to mutate the global "eval" binding. (Also, if another non-strict program uses the same global object (as in a web-page containing multiple scripts), the above mentioned guarantee is also not provided.)
So, for the sake of this question, I'd like to define the following scenario:
our program is stand-alone, i.e. it doesn't share its global object with any other program,
our program consists of a single strict IIFE, like so:
(function () {
'use strict';
// malicious code
eval // does it still refer to the built-in global eval function?
}());
Given these conditions, is it possible to inject code at \\ malicious code, that will change the value of the eval identifier?
Theoretically it should not be possible to reassign the eval identifier to something other than the eval property of the global object, or mask it with a local variable, according to annex C:
The identifier eval or arguments may not appear as the LeftHandSideExpression of an Assignment operator (11.13) or of a PostfixExpression (11.3) or as the UnaryExpression operated upon by a Prefix Increment (11.4.4) or a Prefix Decrement (11.4.5) operator.
...
It is a SyntaxError if the Identifier "eval" or the Identifier "arguments" occurs as the Identifier in a PropertySetParameterList of a PropertyAssignment that is contained in strict code or if its FunctionBody is strict code (11.1.5).
...
It is a SyntaxError if the identifier eval or arguments appears within a FormalParameterList of a strict mode FunctionDeclaration or FunctionExpression (13.1)
...and so on.
As discussed below, it's possible to change the global eval function by assigning a new value to that property of the global object. A reference to the global object can be obtained by an indirect call to eval in strict mode:
var glob = (0,eval)('this');
You could extend that to something that will work reliably in non-strict mode as well:
var glob = (function(){ return this || (0,eval)('this') }());
...and then assign its eval property to something else.
While eval will still be identical to the eval property of the global object, it won't be the built-in eval anymore, which should meet your conditions.
No, not as far as i can tell, you cannot overwrite the native eval function in strict mode. The code below would give the following error. SyntaxError: Assignment to eval or arguments is not allowed in strict mode
(function () {
'use strict';
eval = function(){ console.log('eval invoked'); }
eval();
}());