tl;dr
The second (0, eval)('var a = 1;');
case is in fact not a direct call.
You can see this more prevalently in:
(function(){ "use strict"
var x = eval;
x("var y = 10"); // look at me all indirect
window.y;// 10
eval("var y = 11");
window.y;// still 10, direct call in strict mode gets a new context
})();
The issue can be seen in:
If the eval code is strict code, then (me: fix context)
But strict eval code is defined as:
Eval code is strict eval code if it begins with a Directive Prologue that contains a Use Strict Directive or if the call to eval is a direct call.
Since the call is not direct, the eval code is not strict eval code - and the execution is on global scope.
First of all great question.
"Eval Code" is more general than direct or indirect call to eval
.
Let's check the exact specification for the eval
function
15.1.2.1 eval (x)
When the eval function is called with one argument x, the following steps are taken:
If Type(x) is not String, return x.
Let prog be the ECMAScript code that is the result of parsing x as a Program. If the parse fails, throw a SyntaxError exception (but see also clause 16).
Let evalCtx be the result of establishing a new execution context (10.4.2) for the eval code prog.
Let result be the result of evaluating the program prog.
Exit the running execution context evalCtx, restoring the previous execution context.
...
So, let's explore what 10.4.2 tells us, you cited that - in specific let's look at the first clause:
If there is no calling context or if the eval code is not being evaluated by a direct call (15.1.2.1.1) to the eval function then ... Initialise the execution context as if it was a global execution context
So what is a direct call?
A direct call to the eval function is one that is expressed as a CallExpression that meets the following two conditions:
The Reference that is the result of evaluating the MemberExpression in the CallExpression has an environment record as its base value and its reference name is "eval".
The result of calling the abstract operation GetValue with that Reference as the argument is the standard built-in function defined in 15.1.2.1.
So, what's the MemberExpression
in both cases?
In eval('var a = 1;');
indeed the result of evaluating it has a reference name eval
and calling GetValue
resolution on it returns the built in function.
In (0, eval)('var a = 1;');
the result of evaluating the member expression does not have a reference name eval
. (It does resolve to the built in function on GetValue though).
What are reference names anyway?
Section 8.7 in the spec tells us:
A Reference is a resolved name binding. A Reference consists of three components, the base value, the referenced name and the Boolean valued strict reference flag. The base value is either undefined, an Object, a Boolean, a String, a Number, or an environment record (10.2.1). A base value of undefined indicates that the reference could not be resolved to a binding. The referenced name is a String.
This requires us to look into the GetReferencedName
:
GetReferencedName(V). Returns the referenced name component of the reference V.
So, while the expression (0,eval) === eval
is true, when evaluating the function, this is actually an indirect call because of naming.
May I offer the Function
constructor instead :)?