【译】function.caller 被认为是有害的

今天我收到来自微软的 Patrick Kettner 提的这个问题,然而我发现这个问题是我已经回答过的,只不每次的问题稍有不一样而已。前端

Snipaste_2018-03-05_14-09-37.png

最终我发现是本身在第一次看到这个问题的时候理解错了这个问题,而且当别人在 Twitter 上回应的时候我也没有足够重视这个问题。android

Snipaste_2018-03-05_14-10-35.png

最后 Patrick 又提醒我一次,我才发现引发他兴趣的并非 arguments.caller,而是函数对象的 "caller" 这个神秘的属性 ——— 准确来讲是非严格模式下的函数对象。ios

JavaScript 在历史上曾提供了一个有魔力的 foo.caller 属性,它能够返回调用 foo 函数的引用。使用该属性存在着众多问题,例如它可能会因跨域调用产生安全问题、它在复杂的 JavaScript 引擎中实现的不够充分、它难以维护和测试、诸如对闭包的内联插入,逃逸分析和标量替换的优化都变得不可行,甚至在调用 "caller" 的属性访问器时,这些优化在返回的调用函数中也没法实现。git


不少难以想象的事在非严格模式函数中都被限制了。严格模式下函数经过 AddRestrictedFunctionProperties 定义 "caller" 的访问器,当访问该属性的时候会抛出一个类型错误。github

对于非严格模式的函数,目前 EcmaScript 规格中的定义也是很是模糊的,基本上对它没有作任何的规范限制。在章节 16.2 禁止扩展中说到:后端

若是扩展非严格模式或内置函数对象的时候,将对象本身的属性命名为 "caller" ,而且它的值经过 [[Get]] 或者 [[GetOwnProperty]] 定义的话,这种状况下必须保证不是严格模式。若是它是做为一个访问器属性,经过 [[Get]] 属性获取它的值将会返回调用它的函数,那么这个时候不会返回严格模式下的函数。跨域

因此在非严格模式函数下的 "caller" 属性,或多或少彻底实现了既定的行为。惟一的限制是若是有 yield 一个变量,那么这个变量必定不是严格模式下的函数。因此在非严格模式下,给 "caller" 赋一个默认值 42 是一个合理作法。显然实现中并无这么作 —— 尽管有把这个添加到 V8 中的想法,同时如今也极不建议你们使用 foo.caller。安全


这是咱们目前如何在 V8 中实现这些(有误导性的)特性 —— 也正是如何在 Chrome 和 Node.js 中运行的。"caller" 这个属性在非严格模式函数中是一个特殊的访问器,其实现方法 FunctionCallerGetter 在 accessors.cc 源码文件中实现,同时在该文件实现的还有核心的逻辑方法 FindCaller。要理解下面这些规则能够说是比较困难的,但这就是当你在非严格模式下访问 foo.caller时咱们底层代码所作的事:闭包

  1. 首先找到函数 foo 的最近一次的调用,例如 foo 的最后一次还没返回给调用方的调用。
  2. 若是当前 foo 不存在被调用的状况,则当即返回 null。
  3. 若是处于正被调用的状况,咱们经过查看非用户层的 JavaScript 代码的调用状况,找到它的上级调。
  4. 若是经过上述规则没有找到上级调用,咱们直接返回 null。
  5. 若是能找到上级调用,若是它是严格模式的函数或者是咱们不须要访问的 —— 例如来自不一样域的函数 —— 这种状况下咱们也返回 null。
  6. 不然的话,咱们则返回上级调用的闭包。

这里给出了一个它们如何工做的简单例子:ide

如今你对 foo.caller 是怎么工做已经有了一个基本的了解,这里我强烈建议你不要再使用它。正如上述所说的,它基本上是一个不能保证彻底实现的特性。咱们目前仍然会提供支持,但对于 arguments.caller,正如在 crbug.com/691710 提到的同样,咱们可能在某个时间会移除它 —— 由于咱们但愿可以对闭包作逃逸分析和标量替换 —— 因此不要依赖它 —— 同时显然其余 JavaScript 引擎或许根本不支持这种特性。


掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 AndroidiOS前端后端区块链产品设计人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划官方微博知乎专栏

相关文章
相关标签/搜索