I understand about how eval()
works in non-strict contexts, however the case of using eval()
in strict mode has completely befuddled me. When eval()
is called directly in the global scope, variables are kept inside the new eval()
scope:
'use strict';
eval('var a = 1;');
console.log(a); // ReferenceError: a is not defined
However, if I perform an indirect call to eval()
in the global scope (should be the same thing, right?), it acts as though it is not in strict mode (if you don't believe me, see this JSFiddle):
'use strict';
(0, eval)('var a = 1;'); // indirect call to eval
console.log(a); // 1???
If you don't understand what (0, eval)
does, see Why does google main page use (0, obj.func)(args) syntax?.
At least according to my understanding of how eval()
is supposed to work in strict mode, it is meant to (no matter whether eval()
is called directly or indirectly) create a new scope for variables defined in the eval()
call, however this doesn't seem to be the case here. The specification says the following:
10.4.2 Entering Eval Code
The following steps are performed when control enters the execution context for eval code:
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,
a. Initialise the execution context as if it was a global execution context using the eval code as C as described in 10.4.1.1.
Else,
a. Set the ThisBinding to the same value as the ThisBinding of the calling execution context.
b. Set the LexicalEnvironment to the same value as the LexicalEnvironment of the calling execution context.
c. Set the VariableEnvironment to the same value as the VariableEnvironment of the calling execution context.If the eval code is strict code, then
a. Let strictVarEnv be the result of calling NewDeclarativeEnvironment passing the LexicalEnvironment as the argument.
b. Set the LexicalEnvironment to strictVarEnv.
c. Set the VariableEnvironment to strictVarEnv.Perform Declaration Binding Instantiation as described in 10.5 using the eval code.
This is the case in all major browsers, including (but not limited to) Internet Explorer 10, Chrome 30 and Firefox 24 - as all of them have the same behaviour, I don't think it's likely that it's a bug. Aren't they both meant to do the same thing, and if not, why is this the case?
Note: please don't tell me to not use eval()
(yes, I know the "dangers" of using eval()
) - I simply want to understand the logic behind this, which is utterly confusing to me.
tl;dr
The second
(0, eval)('var a = 1;');
case is in fact not a direct call.You can see this more prevalently in:
The issue can be seen in:
But strict eval code is defined as:
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
functionSo, let's explore what 10.4.2 tells us, you cited that - in specific let's look at the first clause:
So what is a direct call?
So, what's the
MemberExpression
in both cases?In
eval('var a = 1;');
indeed the result of evaluating it has a reference nameeval
and callingGetValue
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 nameeval
. (It does resolve to the built in function on GetValue though).What are reference names anyway?
Section 8.7 in the spec tells us:
This requires us to look into the
GetReferencedName
: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 :)?