arguments.callee.caller
属性被弃用?它在JavaScript中被添加后又被弃用,但在ECMAScript中完全被省略。一些浏览器(Mozilla,IE)一直支持它,并且没有计划删除支持。其他浏览器(Safari,Opera)已经采用了对其的支持,但是旧版浏览器上的支持不可靠。
有没有一个很好的理由将这个有用的功能置于不稳定状态?
(或者说,是否有更好的方法来获取调用函数的句柄?)
arguments.callee.caller
属性被弃用?早期版本的 JavaScript 不允许使用命名函数表达式,因此我们无法创建递归函数表达式:
// 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;
});
然而,这实际上是一个非常糟糕的解决方案,因为它(与其他参数、被调用者和调用者问题相结合)使得在一般情况下无法进行内联和尾递归(你可以通过跟踪等方式在选择性的情况下实现,但即使最佳代码也是次优的,因为需要检查否则不必要的内容)。另一个主要问题是递归调用将会获得不同的 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 3通过允许命名函数表达式来解决了这些问题,例如:
无论如何,EcmaScript 3通过允许命名函数表达式来解决了这些问题,例如:
[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
不会调用未知函数,则无法内联 f
。基本上意味着任何可能可以轻松内联的调用点都会积累大量保护,请看:
function f(a, b, c, d, e) { return a ? b * c : d * e; }
如果 JavaScript 解释器不能保证在调用时所有提供的参数都是数字,则它需要在内联代码之前插入所有参数的检查,否则无法将该函数内联。
现在在这种特殊情况下,一个聪明的解释器应该能够重新排列检查顺序,使其更优化,并且不检查任何不会使用的值。 然而,在许多情况下这是不可能的,因此无法内联。
this
是全局作用域的情况下保留this
的值。在所有其他情况下,this
的值将在第一次递归调用后发生改变,因此我认为你回答中提到this
保留的部分不太正确。 - JLRishearguments.callee.caller
并没有被弃用,尽管它使用了Function.caller
属性(arguments.callee
只是给您当前函数的引用)。
Function.caller
虽然不符合ECMA3标准,但在所有目前主流浏览器中都被实现。arguments.caller
已经过时,推荐使用Function.caller
,并且一些主流浏览器(如Firefox 3)未实现该方法。因此,尽管情况不太理想,但如果想要在Javascript中跨所有主流浏览器访问调用函数,则可以使用Function.caller
属性,在命名函数引用上直接访问或通过arguments.callee
属性在匿名函数中访问。
arguments.callee
,这让我也很难过,但最好不再使用它。 - Gras Doublearguments.callee.caller
已被弃用:“另一个被弃用的功能是arguments.callee.caller
,更具体地说是Function.caller
。”(来源) - thdoanFunction.caller
现在也已经被弃用了。 - Ooker只是一个扩展。在递归过程中,“this”的值会发生变化。在下面(修改后)的例子中,阶乘得到了{foo:true}对象。
[1,2,3,4,5].map(function factorial(n) {
console.log(this);
return (!(n>1))? 1 : factorial(n-1)*n;
}, {foo:true} );
第一次调用阶乘函数时获取对象,但对于递归调用则不是这样。
this
,请写成factorial.call(this, n-1)
。实际上,在编写递归代码时,我通常发现没有this
,或者this
指的是树中的某个节点,实际上它的改变是好的。 - Stijn de Witt在使用new Function
的情况下,它仍然可以在js strict mode
/ type="module"中工作。但是被kapersky
反病毒软件检测到有恶意软件。
<script type="module">
let fn = new Function(`e`,`
new Function('console.log(arguments.callee.caller)')()
`)
fn(5)
</script>
new Function
和 eval()
基本上是一个新的上下文。非模块化,非严格模式。 - Evert