JavaScript caller 和 callee 属性深度解析与递归陷阱

作者:拾光

时间:2026-10-29

浏览:454

本文详细解析JavaScript中caller和callee两个属性的定义、行为及区别。caller用于获取调用当前函数的函数引用,顶层调用返回null;callee则指向当前正在执行的函数,常用于匿名函数递归。文章通过代码示例演示了两者在函数调用链中的表现,并重点解决了因变量赋值导致递归中断的问题,提供了使用arguments.callee的标准解决方案。

文章目录

caller属性:定义与行为

近期在学习JavaScript时遇到了caller和callee的相关问题,通过查阅网络资料并整理总结,特此分享相关知识点。

caller属性:该属性返回一个对调用当前function函数的外部函数对象的引用,使用方式为function.caller。

属性说明:对于普通函数而言,caller属性仅在函数执行期间存在定义。若该函数由顶层代码直接调用,则caller属性值为null。


var time = 3 //控制次数,去掉会一直在caller与handleCaller交替不断执行
function caller() {
  caller.caller()//返回调用caller函数的函数引用
}
function handleCaller() {
  if (time > 0){
    time--
    alert(handleCaller.caller)//返回调用handleCaller函数的函数引用
    alert(caller.caller)//返回调用caller函数的函数引用
    caller()
  }
}
handleCaller()

实例分析:在handleCaller首次运行时,两次alert输出均为null。alert(handleCaller.caller)返回null是因为handleCaller由顶层调用;alert(caller.caller)返回null是因为caller的默认初始值为null。随后调用caller()函数,此时caller.caller返回的是调用它的函数(即handleCaller)的引用,通过caller.caller()可再次触发handleCaller。第二次执行handleCaller时,alert(handleCaller.caller)返回caller的代码引用,alert(caller.caller)返回handleCaller的代码。由于调用关系变为handleCaller->caller->handleCaller,两个函数将交替执行。

caller指向调用当前函数的上层函数。需要注意的是,如果函数在全局作用域(即顶层window)中被调用,该属性返回null。接下来通过代码进行验证。


====================
function testCaller(){
if(testCaller.caller == null){
console.log('accessed at global');
}else{
console.log('accessed at ' + testCaller.caller);
}
}

在全局作用域中调用函数。


testCaller(); // accessed at global

在另一个函数内部调用目标函数。


function a(){
testCaller();
}
a(); // accessed at function a(){testCaller();} 

此时,testCaller.caller指向的是函数a的定义。

callee属性:递归与引用

callee属性:返回与arguments对象相关联的函数引用,多用于实现匿名函数的递归调用。

属性说明:网络上常见说法称callee返回正在执行的函数引用。具体而言,每个函数都拥有自己的arguments对象,通常用于存储参数。arguments包含一个callee属性,其初始值即为对应自身的函数引用。当函数执行至该语句时,arguments默认对应当前执行的函数,因此arguments.callee即为当前正在执行的函数的引用。若将其他函数的arguments赋值给变量(如示例中的args),则可通过args.callee()重新调用该函数。


function a(){
  alert(arguments.callee)
  var args = arguments
  function c(){
    alert(arguments.callee)
    args.callee()
  }
  c()
}
a()

实例分析:示例中arguments.callee默认返回当前正在执行的函数引用(在函数a中返回a自身,在函数c中返回c自身)。通过变量args存储函数a的arguments,并在内置函数c中使用args.callee()再次调用函数a。


====================
function a(x){

if(x<=1)
return x;
else
return x + a(x-1);
}
a(12) // 78

这是一个极简的递归示例,运行结果符合预期。

观察另一种调用方式及其潜在问题。


var b = a;
a = null; // 将a回收
b(12); // erro : 'a' is not a function



原因分析:执行b=a后,b=function a(){}。在调用b之前,执行了a=null。因此,在函数a运行过程中,表达式return x + a(x-1)中的a指向null而非函数a本身。这会导致错误。为解决此问题,需调整函数a的写法。


function a(x){
if(x<=1)
return x;
else
return arguments.callee(x-1); // 这句是改变的地方
}

再次调用修改后的代码。


var b = a;
a = null;
b(12); // 78

原因解释:尽管执行了a=null,但函数a内部并未直接引用变量a,而是通过arguments.callee指向当前正在执行的函数。根据定义,arguments.callee返回正在执行的函数引用,因此递归得以正常进行。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章