为什么在JavaScript中不赞成arguments.callee.caller属性?
为什么在JavaScript中不赞成arguments.callee.caller
属性?
它已被添加,然后在JavaScript中被弃用,但它被ECMAScript完全省略。 一些浏览器(Mozilla,IE)一直支持它,并没有任何计划在地图上删除支持。 其他(Safari,Opera)已经采用了对它的支持,但是在旧版浏览器上的支持是不可靠的。
有没有充分的理由把这个有价值的function放在一边呢?
(或者,有没有更好的方法来获取调用函数的句柄?)
JavaScript的早期版本不允许命名函数expression式,因此我们无法创buildrecursion函数expression式:
// This snippet will work: function factorial(n) { return (!(n>1))? 1 : factorial(n-1)*n; } [1,2,3,4,5].map(factorial); // But this snippet will not: [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; });
然而,这实际上是一个非常糟糕的解决scheme,因为它(与其他参数,被调用者和调用者问题一起)在一般情况下使内联和尾recursion变得不可能(你可以通过跟踪等方式在select的情况下实现它,但是即使是最好的代码由于没有其他必要的检查是次优的)。 另一个主要问题是recursion调用会得到不同的值,例如:
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 3通过允许命名函数expression式解决了这些问题,例如:
[1,2,3,4,5].map(function factorial(n) { return (!(n>1))? 1 : factorial(n-1)*n; });
这有很多好处:
-
该函数可以像你的代码中的其他任何东西一样调用。
-
它不污染名字空间。
-
这个值不会改变。
-
这是更高性能(访问参数对象是昂贵的)。
哎呦,
只是意识到,除了其他的一切问题是关于arguments.callee.caller
,或更具体的Function.caller
。
在任何时候,你都可以find栈中任何函数的最深的调用者,正如我上面所说,看着调用栈有一个主要的影响:它使大量的优化成为不可能的,或者更困难的。
例如。 如果我们不能保证一个函数f
不会调用一个未知的函数,那么就不可能内联f
。 基本上,这意味着任何可能被简单地调用的呼叫站点都会聚集大量的警卫,
function f(a, b, c, d, e) { return a ? b * c : d * e; }
如果js解释器无法保证提供的所有参数都是调用时的数字,则需要插入对所有参数的检查,或者不能内联函数。
现在在这种情况下,聪明的翻译人员应该能够重新排列检查,使其更为优化,而不是检查任何不会被使用的值。 然而在很多情况下这是不可能的,因此不可能内联。
arguments.call ee .call er
不被弃用,虽然它确实使用了Function.call er
属性。 ( arguments.call ee
只会给你一个当前函数的参考)
-
Function.call er
,尽pipe按照ECMA3标准是非标准的,但是在当前所有的主stream浏览器上都可以实现。 -
arguments.call er
已经被弃用,而不是使用Function.call er
,并且在一些当前的主stream浏览器(例如Firefox 3)中没有实现。
所以情况并不理想,但是如果你想通过所有主stream浏览器访问JavaScript中的调用函数,你可以使用Function.call er
属性,直接访问一个命名的函数引用,或者通过一个匿名函数arguments.call ee
属性。
使用命名函数比arguments.callee更好:
function foo () { ... foo() ... }
比…更好
function () { ... arguments.callee() ... }
指定的函数将通过调用者属性访问其调用者 :
function foo () { alert(foo.caller); }
这比…更好
function foo () { alert(arguments.callee.caller); }
弃用是由于目前的ECMAScript devise原则 。
仍然有一个参考的function,而不必硬编码的名称。
只是一个扩展。 “this”的值在recursion期间改变。 在以下(修改)的例子中,阶乘获取{foo:true}对象。
[1,2,3,4,5].map(function factorial(n) { console.log(this); return (!(n>1))? 1 : factorial(n-1)*n; }, {foo:true} );
被称为第一次的阶乘获取对象,但对于recursion调用这不是真的。