(1, eval)('this') vs eval('this') in JavaScript?
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.
return this || (0,eval)('this');
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.
What does (0, eval) () do?
Actually, just see (1,eval)('this') vs eval('this') in JavaScript?, which I've now voted as a duplicate:
.. 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.
(While the following is [mostly] true, it is not specific to eval
usage.)
The comma operator evaluates all the expressions and yields the value of the last expression.
That is, (0, eval)
evaluates to eval
(which is a function-object value), such that the resulting expression is equivalent to eval('this')
.
To see it another way:
var f = (0, eval)
f === eval // true
f('this')
Why Javascript Eval is slower, when it shouldn't be
eval
is basically unoptimizable because the compiler doesn't know what it's doing. Even if you save it into a function, the compiler has to opt out a lot of optimizations, because changing your code in a certain way might break the eval function.
This is why, usually, when you need to do an eval, you do it in another function : that way, the compiler can be sure you didn't modify local scope in the eval
, and optimizes a lot better.
JS VMs are really a lot about heuristics. They try to guess what you want to do, and optimize for the general case. eval
(or new Function
) prevent them to do a lot of that.
Function.new
might be a little faster, because the compiler will know it won't try to modify the scope.
ALSO ! Note that eval
might act a little differently than what you're used to. For example, eval('a')
and (0, eval)('a')
are not the same :
I'll demonstrate it with this
window.a = 5;
void function () {
var a = 1;
eval('a = 2');
console.log(a);
console.log(window.a);
}();
This will print 1
then 5
window.a = 5;
void function () {
var a = 1;
(0,eval)('a = 2'); // <- this line has changed
console.log(a);
console.log(window.a);
}();
Whereas, this will print 1
then 2
.
You can also read this : Global eval, what are the options.
Function Constructor vs eval
That means essentially what new Function('...')
and eval('...')
work in a similar way, in the sense that they will evaluate the string in the argument as a JS expression. However, they have different scoping rules:
new Function()
only has access to the global scopeeval()
has access to the local scope
An example will be this:
- You create a function, say
f
, which isnew Function('console.log(x)')
, and then invoke it. Regardless of which scopef()
is called, it will always log the value ofx
on the global scope - You have
eval('console.log(x)')
. The value ofx
is dependent on the scopeeval()
is in:
const x = 'global x';const f = new Function('console.log(x)');
f(); // logs 'global x'eval('console.log(x)'); // logs 'global x'
// Let's create a function scopefunction test() { const x = 'local x'; const f = new Function('console.log(x)');
f(); // logs 'global x' eval('console.log(x)'); // logs 'local x';}test();
Related Topics
What's the Purpose of Prototype
How to Load Local Script Files as Fallback in Cases Where Cdn Are Blocked/Unavailable
Where to Write to Localstorage in a Redux App
Is Node.Js Native Promise.All Processing in Parallel or Sequentially
What Does $(Function() {} ); Do
Convert String in Dot Notation to Get the Object Reference
Can Es6 Template Literals Be Substituted at Runtime (Or Reused)
Nodejs Callbacks Simple Example
How to Calculate Md5 Hash of a File Using JavaScript
Is the Promise Constructor Callback Executed Asynchronously
How to Get the Day of Week and the Month of the Year
How to Break a String Across More Than One Line of Code in JavaScript
JavaScript - How to Extract Filename from a File Input Control
JavaScript - Get the First Day of the Week from Current Date