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 evals 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
Q1: Multiple consecutive javascript statements separated by a comma take the value of the last statement. So:
(1, eval) takes the value of the last one which is a function reference to the eval() function. It apparently does it this way to make the eval() call into an indirect eval call that will be evaluated in the global scope in ES5. Details explained here.
Q2: There must be some environment that doesn't define a global this, but does define eval('this'). That's the only reason I can think of for that.
Related
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 evals 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
Q1: Multiple consecutive javascript statements separated by a comma take the value of the last statement. So:
(1, eval) takes the value of the last one which is a function reference to the eval() function. It apparently does it this way to make the eval() call into an indirect eval call that will be evaluated in the global scope in ES5. Details explained here.
Q2: There must be some environment that doesn't define a global this, but does define eval('this'). That's the only reason I can think of for that.
var a = 1;
var b = {
a : 2,
c : function () {
console.log(this.a);
}
};
b.c(); // logs 2
(b.c)(); // logs 2
(0, b.c)(); // logs 1
The first is understandable, for "this" is pointed to Object "b". But why does the second one log the same result? I thought "this" should be pointed to the global execution context. And the third one, it seems that the comma operator influences the execution context.
You really have a nice corner case there!
My take on it:
the first is straightforward. Just a standard call. The '.' operator lets you call the function setting b as the execution context.
the second is exactly the same thing: the parens are entirely optional and the interpreter is treating the expression inside it as a bound function call. Actually I didn't expect this: I thought the interpreter would be going to reset this to the global object, but actually it's keeping it linked. Probably just so "casual" language users do not freak out.
the third one is more standard (at least for those who live in JavaScript land): as soon as your function is passed on in an expression (in this case by the , operator) the this value is lost. This is because b.c is a Property Reference (deep rabbit hole details in the specification, here, courtesy of T.J.Crowder). So, you are actually passing around the function itself, no more bound to the declaring object. So when you call it this is going to be passed in as the global object.
See it this way: (object.function)() gets simplyfied into object.function(), because the enclosing parens are completely optional; (0, object.function)() is parsed as (expression yielding a function)() which is going to lose the object binding to this, because function is already unbound.
Really nice example!
Refer to Indirect eval call, which gives more details about it.
( 0 , b.c ) ( )
|____| |_____| |_____|
Literal Operator Identifier
|_________________________|
Expression
|______________________________|
PrimaryExpression
|______________________________| |________|
MemberExpression Arguments
|________________________________________________|
CallExpression
We can use the comma operator to fashion an indirect call to b.c which will force it to execute in the global context, the value of a is 1 in the global context.
Also the result of (b.c = b.c)() is 1
> (b.c = b.c)()
1
Speaking in terms of ECMAScript, this is because both — comma operator (in (0, b.c) example) and = operator (in (b.c = b.c) example) perform GetValue on its operands.
Other indirect call formats as below
> (b.c, b.c)()
1
> (1? b.c: 0)()
1
> (__ = b.c)()
1
Eloquent javscript writes this in chapter 10 of the modules chapter:
The most obvious way is the special operator eval, which will execute
a string of code in the current scope. This is usually a bad idea
because it breaks some of the sane properties that scopes normally
have, such as being isolated from the outside world.
Here is the code:
function evalAndReturnX(code) {
eval(code);
return x;
}
console.log(evalAndReturnX("var x = 2"));
console.log(x)
console.log(code)
it outputs:
2
ReferenceError: x is not defined (line 7)
which seems normal? What gives? I don't see any violation of scope?
Eval has some tricky semantics. From mdn:
If you use the eval function indirectly, by invoking it via a
reference other than eval, as of ECMAScript 5 it works at global scope
rather than local scope; this means, for instance, that function
declarations create global functions, and that the code being
evaluated doesn't have access to local variables within the scope
where it's being called.
So in your case, because you're calling eval directly within that function, it defines x within the lexical scope of that function, not available to the global scope. However, if you were to do something like this:
var _eval = eval;
function e() {
_eval("var x = 2");
return x;
}
console.log(e());
console.log(x);
It would work in both cases, since this would define x in the global scope, which would be inherited by the lexical scope of the callee.
See this fiddle for a working example
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???
For an explanation of 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. (ECMA-262 spec 5th ed 10.4.2)
This is the case in all major browsers (including Internet Explorer 10, Chrome 30 and Firefox 24) so I don't think it's a bug. Aren't they both meant to do the same thing, and if not, why is this the case?
Note: yes, I know the "dangers" of using eval() - I simply want to understand the logic behind this :)
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 :)?
There are things like
f.call(...)
f.apply(...)
But then there's this
(1, alert)('Zomg what is this????!!!11')
"1" does not seem to mean much in this context, the following works just fine:
(null, alert)('Zomg what is this????!!!11')
(1, null, alert)('Zomg what is this????!!!11')
(undefined, alert)('Zomg what is this????!!!11')
Could you point to a specific part of ECMAScript which describes that syntax?
You are just using The Comma Operator.
This operator only evaluates its operands from left to right, and returns the value from the second one, for example:
(0, 1); // 1
('foo', 'bar'); // 'bar'
In the context of calling a function, the evaluation of the operand will simply get a value, not a reference, this causes that the this value inside the invoked function point to the global object (or it will be undefined in the new ECMAScript 5 Strict Mode).
For example:
var foo = 'global.foo';
var obj = {
foo: 'obj.foo',
method: function () {
return this.foo;
}
};
obj.method(); // "obj.foo"
(1, obj.method)(); // "global.foo"
As you can see, the first call, which is a direct call, the this value inside the method will properly refer to obj (returning "obj.foo"), the second call, the evaluation made by the comma operator will make the this value to point to the global object (yielding "global.foo").
That pattern has been getting quite popular these days, to make indirect calls to eval, this can be useful under ES5 strict mode, to get a reference to the global object, for example, (imagine you are in a non-browser environment, window is not available):
(function () {
"use strict";
var global = (function () { return this || (1,eval)("this"); })();
})();
In the above code, the inner anonymous function will execute within a strict mode code unit, that will result on having the this value as undefined.
The || operator will now take the second operand, the eval call, which is an indirect call, and it will evaluate the code on the global lexical and variable environment.
But personally, in this case, under strict mode I prefer using the Function constructor to get the global object:
(function () {
"use strict";
var global = Function('return this')();
})();
Functions that are created with the Function constructor are strict only if they start with a Use Strict Directive, they don't "inherit" the strictness of the current context as Function Declarations or Function Expressions do.
It's the comma operator, which evaluates both of its operands and returns the value of the second one.
So, something like (null, alert) evaluates to the alert function, which you can immediately call using parentheses.
It's described in section 11.14 of ECMA-262 (PDF).
The comma operator causes expressions to be evaluated sequentially. Wrapping the expressions in parentheses returns the value of the last expression. So (1, alert)("hello") is functionally equivalent to:
1;
alert("hello");
Off the top of my head, I can't think of a reason to do this.