为什么 EcmaScript 5 严格模式要花这么大的功夫来限制标识符“eval”

发布于 2024-08-15 08:50:34 字数 237 浏览 2 评论 0原文

根据 规范(附录 C),严格模式代码几乎无法执行任何可能分配名为 eval 的任何标识符的操作。我可以理解,人们可能想要限制实际 eval 函数的使用,但我不明白限制名称的使用有什么目的?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

丘比特射中我 2024-08-22 08:50:34

bobince基本上是正确的。 (我从事 Mozilla JS 引擎 SpiderMonkey 的工作,在其中实现了 ES5 的各个部分,并在时间允许的情况下关注 ECMAScript 讨论。)您(实现者和读者)确实希望 eval 成为规范eval,并且您希望 arguments 成为规范的arguments。使用严格模式你基本上可以得到这个。

但我要指出的是,ES5 在这里的限制并不如预期的那么多。首先,稍微纠正一下 bobince,即使使用严格模式,你也不能确定 eval 是原始的 eval 函数:

"use strict";
this.eval = function() { return "ohai"; };
eval("7 + 10"); // "ohai"

这是众所周知的(在 JS 爱好者中)全局对象错误:脚本使用跨脚本共享、可命名和可修改的全局对象来解析名称。如果您无法引用绑定全局变量的对象,则不会出现此问题。 ES6 很可能会通过另一个始终作用于整个脚本的选择加入系统(可能是像 MIME 类型一样的带外系统,但目前还不清楚)来解决这个问题。

但即使没有可命名、可变的全局对象,您仍然会遇到问题,因为严格模式的作用域可以限定为函数:

function outer()
{
  var eval = function() { return "kthxbai"; };
  function inner()
  {
    "use strict";
    return eval("2 + 5");
  }
  return inner();
}
outer(); // "kthxbai"

即使存在严格模式,这些问题也存在,并且它们最早要到 ES6 才会消失,因为它将可能会删除全局对象并无条件强制执行严格模式限制。

所以严格模式下的eval还是有点奇怪,因为它可以引用not-eval。但处理这并不是什么大问题 - 在运行时,实现可以检查真正的 eval ,如果失败,只需执行语法碰巧使用 以外的名称时会执行的操作评估。这需要对像 eval(...) 这样的表达式进行特殊处理。但无论如何,任何好的实现都会这样做,因为 eval 的非静态可观察行为(改变局部变量和参数,引入新变量[现在在严格模式下进行了去除——严格模式下的变量声明模式评估代码是评估代码的本地],等等),所以它不是真正的负担。

值得注意的是,这些都不适用于作为虚假特殊形式的参数。要么通过函数作用域使用严格模式,在这种情况下,您会看到该函数的参数,而不是在外部分配的参数作用域,或者通过全局作用域获得它,在这种情况下,参数没有特殊行为。 (为什么禁止在全局严格模式代码中改变参数?可能很简单,而且迫使开发人员在任何地方都将它们视为一种特殊形式,但我不确定。)

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.)

笑,眼淚并存 2024-08-22 08:50:34

我只能推测,但在我看来,ES5-strict 是说 eval 和 argument 应该被视为原始语法,而不是标识符。这两个功能应该在语法级别实现是合理的,因为它们具有普通函数无法重现的神奇时髦魔法行为。

(特别是eval可能会写入调用它的函数中的局部变量,并且写入arguments会奇怪地改变与参数相对应的局部变量的值。尽管这种行为看起来谢天谢地,在严格模式下会消失。)

出于兼容性原因,ES5 实际上无法使 evalarguments 语法化。因此,他们会尽其所能,也就是说,标识符 arguments 始终引用 arguments magic,而标识符 eval 始终专门引用eval 魔法。

如果 JS 引擎能够确定函数是否包含魔法,它还可以提高优化的可能性。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文