Function.prototype.caller

非标准:此特性未标准化。我们不建议在生产环境中使用非标准特性,因为它们浏览器支持有限,并且可能会更改或被移除。但是,在没有标准选项的特定情况下,它们可以是合适的替代方案。

已弃用:此特性不再推荐。虽然某些浏览器可能仍然支持它,但它可能已经从相关的网络标准中删除,可能正在删除过程中,或者可能仅为兼容性目的而保留。请避免使用它,如果可能,请更新现有代码;请参阅本页底部的兼容性表格以指导您的决策。请注意,此特性可能随时停止工作。

注意:严格模式下,访问函数的 caller 会抛出错误 — 该 API 已被移除且没有替代方案。这是为了防止代码能够“遍历调用栈”,这既带来了安全风险,也严重限制了内联和尾调用优化等优化的可能性。有关更多解释,您可以阅读 arguments.callee 弃用的理由

caller 属性是 Function 实例的一个访问器属性,它返回调用此函数的函数。对于 严格模式、箭头函数、异步函数和生成器函数,访问 caller 属性会抛出 TypeError

描述

如果函数 f 是由顶层代码调用的,则 f.caller 的值为 null;否则,它就是调用 f 的函数。如果调用 f 的函数是严格模式函数,则 f.caller 的值也为 null

请注意,ECMAScript 规范仅指定了一个行为,即 Function.prototype 具有一个初始的 caller 访问器,该访问器无条件地为任何 getset 请求抛出 TypeError(称为“毒丸访问器”),并且不允许实现更改此语义,除非是非严格模式的普通函数,在这种情况下,它不能具有严格模式函数的值。caller 属性的实际行为,如果不是抛出错误,则由实现定义。例如,Chrome 将其定义为自己的数据属性,而 Firefox 和 Safari 则扩展了初始的“毒丸”Function.prototype.caller 访问器,以特殊处理非严格函数作为 this 值的情况。

js
(function f() {
  if (Object.hasOwn(f, "caller")) {
    console.log(
      "caller is an own property with descriptor",
      Object.getOwnPropertyDescriptor(f, "caller"),
    );
  } else {
    console.log(
      "f doesn't have an own property named caller. Trying to get f.[[Prototype]].caller",
    );
    console.log(
      Object.getOwnPropertyDescriptor(
        Object.getPrototypeOf(f),
        "caller",
      ).get.call(f),
    );
  }
})();

// In Chrome:
// caller is an own property with descriptor {value: null, writable: false, enumerable: false, configurable: false}

// In Firefox:
// f doesn't have an own property named caller. Trying to get f.[[Prototype]].caller
// null

此属性取代了已废弃的 arguments 对象的 arguments.caller 属性。

特殊的 __caller__ 属性,它返回调用者的激活对象,从而允许重建调用栈,由于安全原因已被移除。

示例

检查函数 caller 属性的值

以下代码检查函数 caller 属性的值。

js
function myFunc() {
  if (myFunc.caller === null) {
    return "The function was called from the top!";
  }
  return `This function's caller was ${myFunc.caller}`;
}

重建调用栈和递归

请注意,在递归的情况下,您无法使用此属性重建调用栈。考虑以下情况:

js
function f(n) {
  g(n - 1);
}
function g(n) {
  if (n > 0) {
    f(n);
  } else {
    stop();
  }
}
f(2);

调用 stop() 时,调用栈将是:

f(2) -> g(1) -> f(1) -> g(0) -> stop()

以下为真:

js
stop.caller === g && f.caller === g && g.caller === f;

因此,如果您尝试在 stop() 函数中像这样获取堆栈跟踪:

js
let f = stop;
let stack = "Stack trace:";
while (f) {
  stack += `\n${f.name}`;
  f = f.caller;
}

循环将永远不会停止。

严格模式 caller

如果调用者是严格模式函数,则 caller 的值为 null

js
function callerFunc() {
  calleeFunc();
}

function strictCallerFunc() {
  "use strict";
  calleeFunc();
}

function calleeFunc() {
  console.log(calleeFunc.caller);
}

(function () {
  callerFunc();
})();
// Logs [Function: callerFunc]

(function () {
  strictCallerFunc();
})();
// Logs null

规范

不属于任何标准。

浏览器兼容性

另见