• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
邹天得
博客园    首页    新随笔    联系   管理    订阅  订阅
为什么不赞成使用caller?

https://www.web-tinker.com/article/20296.html

原文链接:stackoverflow.com
鼠标hover可见原文,如果发现错误请@我以便修正,谢谢大家~

早期的JavaScript版本不允许命名函数表达式,因此你无法让一个函数表达式递归调用。

例如:

function factorial(n) { return (!(n>1))? 1 : factorial(n-1)*n; } [1,2,3,4,5].map(factorial);

这个可以正常工作,但是

[1,2,3,4,5].map(function(n) { return (!(n>1))? 1 : /* what goes here? */ (n-1)*n; });

这个不行。为了解决这个问题加入了arguments.callee,所以你可以这么做

[1,2,3,4,5].map(function(n) { return (!(n>1))? 1 : arguments.callee(n-1)*n; });

然而这是一个很差的解决方案,它(使用arguments、callee、caller引起的问题)通常会使级联和尾递归无法优化(你可以根据实际情况通过跟踪等方法来实现它,但即便是最好的代码也只是次优的,要不然就没必要check了)。 其它主要的问题是这样递归调用会得到不同的this值,例如:

var global = this; var sillyFunction = function (recursed) { if (!recursed) return arguments.callee(true); if (this !== global) alert("This is: " + this); else alert("This is the global"); } sillyFunction();

无论如何,ECMAScript-ED3已经解决了这个问题,允许使用命名函数表达式,例如:

[1,2,3,4,5].map(function factorial(n) { return (!(n>1))? 1 : factorial(n-1)*n; });

这样做有许多好处

  • 函数可以像你的其它内部代码一样调用
  • 它不会污染命名空间
  • this的值不会轻易改变
  • 它有更高的性能(访问arguments需要巨大的性能开销)。

好吧,只是实现了而已,除任何其它东西外,这是关于arguments.callee.caller的问题,或更确切的说是Function.caller的问题。 在任何时候你都可以找到任意函数在栈中的最深调用者,并且就像我前面说的在调用堆栈中有个独立的重大影响: 它让大多数优化变成不可能或变得极为困难。 例如:如果你无法确保一个函数f不会调用一个未知函数,它将无法做内联优化。基本上这就意味着,任何可能已经是普通可内联的调用位置都会积累大量的侦测点,就拿这段代码来说:

function f(a,b,c,d,e) { return a ? b * c : d * e; }

如果JS解释器不能确保所有提供的参数在被调用的时刻都是数字,它就需要在所有参数之前的内联代码中插入check,否则无法对这个函数做内联优化。 现在,在这个特定的情况下聪明的解释器应该能重新排列checks使之更为优化,并且不check任何不会被用到的值。 尽管在许多情况中这是不可能的,并因此成为不可内联的。

posted on 2017-07-09 21:24  邹天得  阅读(188)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3