(1, eval)('this') vs eval('this') in JavaScript?
I start to read JavaScript Patterns, some codes confused me.
var global = (function () {
return this || (1, eval)('this');
}());
Here are my questions:
Q1:
(1, eval) === eval
?
Why and how does it work?
Q2: Why not just
var global = (function () {
return this || eval('this');
}());
or
var global = (function () {
return this;
}());
The difference between (1,eval)
and plain old eval
is that the former is a value and the latter is an lvalue. It would be more obvious if it were some other identifier:
var x;
x = 1;
(1, x) = 1; // syntax error, of course!
That is (1,eval)
is an expression that yields eval
(just as say, (true && eval)
or (0 ? 0 : eval)
would), but it's not a reference to eval
.
Why do you care?
Well, the Ecma spec considers a reference to eval
to be a "direct eval call", but an expression that merely yields eval
to be an indirect one -- and indirect eval calls are guaranteed to execute in global scope.
Things I still don't know:
- Under what circumstance does a direct eval call not execute in global scope?
- Under what circumstance can the
this
of a function at global scope not yield the global object?
Some more information can be gleaned here.
EDIT
Apparently, the answer to my first question is, "almost always". A direct eval
executes from the current scope. Consider the following code:
var x = 'outer';
(function() {
var x = 'inner';
eval('console.log("direct call: " + x)');
(1,eval)('console.log("indirect call: " + x)');
})();
Not surprisingly (heh-heh), this prints out:
direct call: inner
indirect call: outer
EDIT
After more experimentation, I'm going to provisionally say that this
cannot be set to null
or undefined
. It can be set to other falsy values (0, '', NaN, false), but only very deliberately.
I'm going to say your source is suffering from a mild and reversible cranio-rectal inversion and might want to consider spending a week programming in Haskell.
The fragment
var global = (function () {
return this || (1, eval)('this');
}());
will correctly evaluate to the global object even in strict mode. In non-strict mode the value of this
is the global object but in strict mode it is undefined
. The expression (1, eval)('this')
will always be the global object.
The reason for this involves the rules around indirect versus direct eval
. Direct calls to eval
has the scope of the caller and the string this
would evaluate to the value of this
in the closure. Indirect eval
s evaluate in global scope as if they were executed inside a function in the global scope.
Since that function is not itself a strict-mode function the global object is passed in as this
and then the expression 'this'
evaluates to the global object. The expression (1, eval)
is just a fancy way to force the eval
to be indirect and return the global object.
A1: (1, eval)('this')
is not the same as eval('this')
because of the special rules around indirect versus direct calls to eval
.
A2: The original works in strict mode, the modified versions do not.
To Q1:
I think this is a good example of comma operator in JS. I like the explanation for comma operator in this article: http://javascriptweblog.wordpress.com/2011/04/04/the-javascript-comma-operator/
The comma operator evaluates both of its operands (from left to right) and returns the value of the second operand.
To Q2:
(1, eval)('this')
is considered as indirect eval call, which in ES5 does execute code globally. So the result will be the global the context.
See http://perfectionkills.com/global-eval-what-are-the-options/#evaling_in_global_scope