According to the spec (Annex C), strict-mode code can't do pretty much anything that might assign any identifier with the name eval
. I can understand that one might want to restrict use of the actual eval
function, but I don't see what purpose is served by restricting use of the name?
问题:
回答1:
I can only speculate, but it seems to me that ES5-strict is saying that eval
and arguments
should be considered as raw syntax, not identifiers. It is reasonable that these two features should be implemented at the syntactical level, because they have Amazing Funky Magic behaviours that cannot be reproduced by a normal function.
(In particular eval
may write to local variables in the function that calls it, and writing to arguments
bizarrely changes the values of local variables corresponding to the arguments. Though this behaviour seems to be going away in strict mode, thankfully.)
For compatibility reasons, ES5 can't actually make eval
and arguments
syntactical. So they do the nearest they can, which is to say that the identifier arguments
always refers to arguments
magic and the identifier eval
always exclusively refers to eval
magic.
It could also improve the possibilities for optimisation, if JS engines can be sure whether a function contains magic.
回答2:
bobince is basically correct. (I work on SpiderMonkey, Mozilla's JS engine, have implemented various parts of ES5 in it, and follow ECMAScript discussions as time permits.) You (implementers and readers both) really do want eval
to be the canonical eval
, and you want arguments
to be the canonical arguments
. Using strict mode you can mostly get this.
But I will point out that ES5's restrictions here are not as much as would be desirable. First, and to correct bobince slightly, even with strict mode you can't be sure eval
is the original eval
function:
"use strict";
this.eval = function() { return "ohai"; };
eval("7 + 10"); // "ohai"
This is the well-known (among JS aficionados) global object mistake: that scripts use a global object, shared across scripts, nameable and modifiable, to resolve names. If you couldn't refer to the object where global variables are bound, you wouldn't have this problem. It's likely ES6 will fix this through another opt-in system (possibly out-of-band like a MIME type, but that's unclear yet) always scoped to entire scripts.
But even without the nameable, mutable global object, you still have problems because strict mode can be scoped to functions:
function outer()
{
var eval = function() { return "kthxbai"; };
function inner()
{
"use strict";
return eval("2 + 5");
}
return inner();
}
outer(); // "kthxbai"
These problems exist even in the presence of strict mode, and they won't go away until ES6 at the earliest, as it will probably remove the global object and unconditionally enforce strict mode restrictions.
So eval
in strict mode is still a little weird in that it can refer to not-eval. But handling that is no big deal -- at runtime the implementation can check for the real eval
and if that fails just do what it'd do if the syntax happened to use a name other than eval
. This requires that an expression like eval(...)
be handled specially. But any good implementation did that anyway, because of eval
's non-statically-observable behavior (mutating local variables and arguments, introducing new variables [now de-fanged in strict mode -- variable declarations in strict mode eval code are local to the eval code], and so on), so it's no real burden.
It's worth noting none of this applies to arguments
as a fake special form. Either you have strict mode by way of a function scope, in which case you'd see that function's arguments
rather than arguments
assigned in an outer scope, or you have it by way of a global scope, in which case arguments
has no special behavior. (Why forbid mutation of arguments
in global strict mode code? Probably simplicity, plus forcing developers to treat them as more of a special form everywhere, but I'm not certain.)