ChatGPT解决这个技术问题 Extra ChatGPT

如何在 JavaScript 中找出调用者函数?

function main()
{
   Hello();
}

function Hello()
{
  // How do you find out the caller function is 'main'?
}

有没有办法找出调用堆栈?

我希望这只是为了帮助您进行调试。根据调用者改变行为是一个坏主意。
@AndersonGreen 例如,当您拥有默认模板渲染方法并看到它被调用两次时。无需梳理 1000 多个 LoC 或使用调试器进行艰苦的单步调试,您只需查看当时的堆栈是什么。
要查看堆栈跟踪,请使用 console.trace() for chrome。虽然不知道别人
为什么这是个坏主意?
“我希望这只是为了帮助您进行调试。根据调用者改变行为是一个坏主意。”为什么我们应该能够对评论投反对票。他没有问这是不是一个好主意,你也错了。当您想要通知订阅者属性更改而不需要不易重构的魔术字符串时,它在 C# 等其他语言中非常有用。

x
xor

请注意,此解决方案已弃用,根据 MDN 文档不应再使用

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/caller

function Hello()
{
    alert("caller is " + Hello.caller);
}

请注意,此功能是非标准,来自 Function.caller

非标准 此功能是非标准的,不在标准轨道上。不要在面向 Web 的生产站点上使用它:它不适用于每个用户。实现之间也可能存在很大的不兼容性,并且行为可能会在未来发生变化。

以下是 2008 年的旧答案,现代 Javascript 不再支持该答案:

function Hello()
{
    alert("caller is " + arguments.callee.caller.toString());
}

arguments.callee.caller.name 将获得函数的名称。
“'caller'、'callee' 和 'arguments' 属性可能无法在严格模式函数或调用它们的参数对象上访问”——它们在 ES5 中已被弃用,并在严格模式下被删除。
只有在您不使用严格模式时,它才会起作用。因此删除 'use strict'; 可能会有所帮助。
arguments 可以在严格模式下从函数内部访问,弃用它是愚蠢的。只是不是来自外部的function.arguments。此外,如果您有一个命名参数,它的 arguments[i] 形式将不会跟踪您对函数内的命名版本所做的更改。
自 2011 年发布此帖子以来,此方法已过时。首选方法现在是 Function.caller(截至 2015 年)。
i
iconoclast

堆栈跟踪

您可以使用浏览器特定代码找到整个堆栈跟踪。好处是someone already made it;这是project code on GitHub

但并非所有消息都是好消息:

获取堆栈跟踪真的很慢,所以要小心(阅读更多信息)。您需要定义函数名称以使堆栈跟踪清晰易读。因为如果你有这样的代码: var Klass = function kls() { this.Hello = function() { alert(printStackTrace().join('\n\n')); };新的克拉斯()。你好();谷歌浏览器会提醒 ... kls.Hello ( ... 但大多数浏览器会期望在关键字 function 之后有一个函数名称,并将其视为匿名函数。如果您使用 Klass 名称,甚至 Chrome 都无法使用不要给函数命名 kls。顺便说一下,您可以将选项 {guess: true} 传递给函数 printStackTrace,但这样做并没有发现任何真正的改进。并非所有浏览器都给您相同的信息,即参数、代码列等。

调用函数名称

顺便说一句,如果您只想要调用函数的名称(在大多数浏览器中,但不是 IE),您可以使用:

arguments.callee.caller.name

但请注意,此名称将位于 function 关键字之后。如果不获取整个函数的代码,我没有办法(即使在 Google Chrome 上)获得更多。

调用函数代码

并总结其余的最佳答案(Pablo Cabrera、nourdine 和 Greg Hewgill)。您可以使用的唯一跨浏览器且真正安全的方法是:

arguments.callee.caller.toString();

这将显示调用者函数的代码。遗憾的是,这对我来说还不够,这就是为什么我为您提供有关 StackTrace 和调用函数名称的提示(尽管它们不是跨浏览器)。


也许您应该为每个 @Greg's answer 添加 Function.caller
但是,Function.caller 不能在严格模式下工作。
a
abarisone

我通常在 Chrome 中使用 (new Error()).stack。好消息是这还为您提供了调用者调用该函数的行号。缺点是它将堆栈的长度限制为 10,这就是我首先来到这个页面的原因。

(我使用它在执行期间收集低级构造函数中的调用堆栈,以便稍后查看和调试,因此设置断点没有用,因为它会被命中数千次)


您能否就您提供的解释添加更多描述?
'use strict'; 到位时,这是我唯一可以开始工作的事情。给我我需要的信息——谢谢!
关于堆栈长度的限制......您可以使用“Error.stackTraceLimit = Infinity”进行更改。
(new Error("StackLog")).stack.split("\n") 使它更易于阅读。
P
Phil

https://i.stack.imgur.com/aBtUp.png


这是一个古老的问题......但这绝对是当今最有效的现代方法。
f
fny

如果你不打算在 IE 中运行它 < 11 然后 console.trace() 将适合。

function main() {
    Hello();
}

function Hello() {
    console.trace()
}

main()
// Hello @ VM261:9
// main @ VM261:4

a
ale5000

您可以获得完整的堆栈跟踪:

arguments.callee.caller
arguments.callee.caller.caller
arguments.callee.caller.caller.caller

直到调用者是 null

注意:它会导致递归函数无限循环。


T
TankorSmash

回顾一下(并使其更清晰)......

这段代码:

function Hello() {
    alert("caller is " + arguments.callee.caller.toString());
}

相当于:

function Hello() {
    alert("caller is " + Hello.caller.toString());
}

显然第一位更便于移植,因为您可以更改函数的名称,例如从“Hello”到“Ciao”,仍然可以让整个事情正常工作。

在后者中,如果您决定重构被调用函数的名称(Hello),您将不得不更改它的所有出现:(


arguments.callee.caller 在 Chrome 25.0.1364.5 dev 上始终为空
这个答案不包括“开”时的 stict 模式
V
VanagaS

在 ES6 和 Strict 模式下,使用以下方式获取 Caller 函数

console.log((new Error()).stack.split("\n")[2].trim().split(" ")[1])

请注意,如果没有调用者或没有先前的堆栈,上述行将引发异常。相应地使用。

要获取被调用者(当前函数名),请使用:

console.log((new Error()).stack.split("\n")[1].trim().split(" ")[1]) 

对象可能未定义,所以添加一个“?”:.stack?
i
inorganik

我会这样做:

function Hello() {
  console.trace();
}

这很好用!应该被接受为正确的答案,因为其他方式是旧的\不再起作用了
@inorganik,你好,你能解释一下你的答案与这个stackoverflow.com/a/34853024/1902296相比有什么新的或不同的地方吗?
@humkins 有更多好的答案很好)
G
Greg

您可以使用 Function.Caller 来获取调用函数。使用 argument.caller 的旧方法被认为是过时的。

下面的代码说明了它的用法:

function Hello() { return Hello.caller;}

Hello2 = function NamedFunc() { return NamedFunc.caller; };

function main()
{
   Hello();  //both return main()
   Hello2();
}

关于过时 argument.caller 的说明:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments/caller

请注意 Function.caller 是非标准的:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/caller


这是这些天的正确答案。你不能再做 arguments.caller.callee 的事情了。希望我们能把它移到顶部,因为所有其他东西现在都已经过时了。
似乎这在严格模式下是不可能的? Cannot access caller property of a strict mode function
Function.caller 在严格模式下也不适用于我。此外,according to MDN,function.caller 是非标准的,不应在生产中使用。不过,它可能适用于调试。
如果它在 Node 中工作,我对非标准没有问题,但在严格模式下根本不允许它(我在节点 6.10 上测试过)。同样适用于“论点”。我收到错误消息:'''caller' 和 'arguments' 是受限制的函数属性,无法在此上下文中访问。”
Q
QueueHammer

看起来这是一个相当已解决的问题,但我最近发现 callee is not allowed in 'strict mode' 所以为了我自己的使用,我编写了一个类,该类将从调用它的位置获取路径。它是 part of a small helper lib,如果您想单独使用代码,请更改用于返回调用者堆栈跟踪的偏移量(使用 1 而不是 2)

function ScriptPath() {
  var scriptPath = '';
  try {
    //Throw an error to generate a stack trace
    throw new Error();
  }
  catch(e) {
    //Split the stack trace into each line
    var stackLines = e.stack.split('\n');
    var callerIndex = 0;
    //Now walk though each line until we find a path reference
    for(var i in stackLines){
      if(!stackLines[i].match(/http[s]?:\/\//)) continue;
      //We skipped all the lines with out an http so we now have a script reference
      //This one is the class constructor, the next is the getScriptPath() call
      //The one after that is the user code requesting the path info (so offset by 2)
      callerIndex = Number(i) + 2;
      break;
    }
    //Now parse the string for each section we want to return
    pathParts = stackLines[callerIndex].match(/((http[s]?:\/\/.+\/)([^\/]+\.js)):/);
  }

  this.fullPath = function() {
    return pathParts[1];
  };

  this.path = function() {
    return pathParts[2];
  };

  this.file = function() {
    return pathParts[3];
  };

  this.fileNoExt = function() {
    var parts = this.file().split('.');
    parts.length = parts.length != 1 ? parts.length - 1 : 1;
    return parts.join('.');
  };
}

在控制台中使用 function a(){ function b(){ function c(){ return ScriptPath(); } return c(); } return b(); } a() 对我不起作用(尚未在文件中尝试过),但似乎有一个合理的想法。无论如何都应该投票以提高知名度。
这个想法很棒。我的解析方式不同,但在 nw.js 应用程序中,这确实是提供我正在寻找的东西的唯一想法。
请提供如何调用此函数的示例。
Throw .. catch 是不必要的。有可能通过 let stack =new Error().stack 获得堆栈。
似乎我之前的评论是错误的,需要 thow ... catch 根据 github.com/stacktracejs/stacktrace.js/blob/master/… 在 IE 浏览器中获取堆栈。
S
Shadow2531
function Hello() {
    alert(Hello.caller);
}

仅对于函数名称使用 Hello.caller.name
arguments.callee.caller.toString() 相同
这应该是正确的答案,至少在 2016 年
这不在标准轨道上,但将从 ECMAScript 5 开始工作。
S
Stephen Quan

heystewart's answerJiarongWu's answer 都提到 Error 对象可以访问 stack

这是一个例子:

函数 main() { 你好(); } function Hello() { try { throw new Error(); } catch (err) { 让堆栈 = err.stack; // NB stack === "Error\n at Hello ...\n at main ... \n...." let m = stack.match(/.*?Hello.*?\n(.* ?)\n/); if (m) { 让 caller_name = m[1]; console.log("来电者是:", caller_name); } } } 主要的();

不同的浏览器以不同的字符串格式显示堆栈:

Safari  : Caller is: main@https://stacksnippets.net/js:14:8
Firefox : Caller is: main@https://stacksnippets.net/js:14:3
Chrome  : Caller is:     at main (https://stacksnippets.net/js:14:3)
IE Edge : Caller is:    at main (https://stacksnippets.net/js:14:3)
IE      : Caller is:    at main (https://stacksnippets.net/js:14:3)

大多数浏览器将使用 var stack = (new Error()).stack 设置堆栈。在 Internet Explorer 中,堆栈将是未定义的 - 您必须抛出一个真正的异常来检索堆栈。

结论:使用 Error 对象中的 stack 可以确定“main”是“Hello”的调用者。事实上,它适用于 callee / caller 方法不起作用的情况。它还将向您显示上下文,即源文件和行号。但是,需要努力使解决方案跨平台。


a
alex

使用 *arguments.callee.caller 更安全,因为 arguments.caller弃用...


arguments.callee 在 ES5 中也被弃用,并在严格模式下被移除。
有替代方案吗?编辑:arguments.callee 是一个糟糕的解决方案,现在已经得到了更好的解决developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
R
Rovanion

2018 更新

caller is forbidden in strict mode。这是使用(非标准)Error stack 的替代方法。

以下函数似乎在 Firefox 52 和 Chrome 61-71 中完成了这项工作,尽管它的实现对两个浏览器的日志格式做了很多假设,并且应该谨慎使用,因为它会引发异常并可能执行两个正则表达式匹配之前完成。

'使用严格'; const fnNameMatcher = /([^(]+)@|at ([^(]+) \(/; function fnName(str) { const regexResult = fnNameMatcher.exec(str); return regexResult[1] || regexResult[ 2]; } function log(...messages) { const logLines = (new Error().stack).split('\n'); const callerName = fnName(logLines[1]); if (callerName !== null) { if (callerName !== 'log') { console.log(callerName, '调用日志:', ...messages); } else { console.log(fnName(logLines[2]), '调用记录:', ...messages); } } else { console.log(...messages); } } function foo() { log('hi', 'there'); } (function main() {富();}());


这太不可思议了,也太可怕了。
我得到了“foo call with: hi there”,但 foo 不是用“hi there”调用的,log 是用“hi there”调用的
对,错误信息的语法中有一个“错位修饰符”。它的意思是“从函数 f 调用日志,它希望打印消息 X”,但要尽可能简洁。
P
Prasanna

只需控制台记录您的错误堆栈。然后你就可以知道你是怎么被叫到的

const hello = () => { console.log(new Error('我被叫').stack) } const sello = () => { hello() } sello()


B
Brad

尝试访问这个:

arguments.callee.caller.name

b
bladnman

我想在这里添加我的小提琴:

http://jsfiddle.net/bladnman/EhUm3/

我测试了这是 chrome、safari 和 IE(10 和 8)。工作正常。只有 1 个功能很重要,所以如果你被大小提琴吓到了,请阅读下文。

注意:这个小提琴中有很多我自己的“样板”。如果您愿意,您可以删除所有这些并使用拆分。这只是我开始依赖的一组“超安全”功能。

那里还有一个“JSFiddle”模板,我用于许多小提琴来简单地快速摆弄。


我想知道您是否可以在某些情况下添加“助手”作为原型的扩展,例如:String.prototype.trim = trim;
J
JoolzCheat

如果您只想要函数名称而不是代码,并且想要一个独立于浏览器的解决方案,请使用以下内容:

var callerFunction = arguments.callee.caller.toString().match(/function ([^\(]+)/)[1];

请注意,如果没有调用函数,则上述将返回错误,因为数组中没有 [1] 元素。要解决此问题,请使用以下命令:

var callerFunction = (arguments.callee.caller.toString().match(/function ([^\(]+)/) === null) ? 'Document Object Model': arguments.callee.caller.toString().match(/function ([^\(]+)/)[1], arguments.callee.toString().match(/function ([^\(]+)/)[1]);

这不是三元运算符的用途,您要重新计算所有内容三次!这就是变量的用途,
P
Pablo Armentano

只是想让您知道,在 PhoneGap/Android 上,name 似乎不起作用。但是 arguments.callee.caller.toString() 可以解决问题。


A
Alexis Pigeon

在这里,除了 functionname 之外的所有内容都使用 RegExp 从 caller.toString() 中删除。

<!DOCTYPE html>
<meta charset="UTF-8">
<title>Show the callers name</title><!-- This validates as html5! -->
<script>
main();
function main() { Hello(); }
function Hello(){
  var name = Hello.caller.toString().replace(/\s\([^#]+$|^[^\s]+\s/g,'');
  name = name.replace(/\s/g,'');
  if ( typeof window[name] !== 'function' )
    alert ("sorry, the type of "+name+" is "+ typeof window[name]);
  else
    alert ("The name of the "+typeof window[name]+" that called is "+name);
}
</script>

这仍然返回整个方法声明
佚名

这是 get full stacktrace 的函数:

function stacktrace() {
var f = stacktrace;
var stack = 'Stack trace:';
while (f) {
  stack += '\n' + f.name;
  f = f.caller;
}
return stack;
}

n
ns16

请注意,您不能在 Node.js 中使用 Function.caller,而是使用 caller-id 包。例如:

var callerId = require('caller-id');

function foo() {
    bar();
}
function bar() {
    var caller = callerId.getData();
    /*
    caller = {
        typeName: 'Object',
        functionName: 'foo',
        filePath: '/path/of/this/file.js',
        lineNumber: 5,
        topLevelFlag: true,
        nativeFlag: false,
        evalFlag: false
    }
    */
}

I
Israel

对我来说效果很好,您可以选择要返回多少功能:

function getCaller(functionBack= 0) {
    const back = functionBack * 2;
    const stack = new Error().stack.split('at ');
    const stackIndex = stack[3 + back].includes('C:') ? (3 + back) : (4 + back);
    const isAsync = stack[stackIndex].includes('async');
    let result;
    if (isAsync)
      result = stack[stackIndex].split(' ')[1].split(' ')[0];
    else
      result = stack[stackIndex].split(' ')[0];
    return result;
}

A
Anna Do

我可以在 2021 年使用这些并获取从调用者函数开始的堆栈:

1. console.trace();
2. console.log((new Error).stack)

// do the same as #2 just with better view
3. console.log((new Error).stack.split("\n")) 

D
Diego Augusto Molina

试试下面的代码:

function getStackTrace(){
  var f = arguments.callee;
  var ret = [];
  var item = {};
  var iter = 0;

  while ( f = f.caller ){
      // Initialize
    item = {
      name: f.name || null,
      args: [], // Empty array = no arguments passed
      callback: f
    };

      // Function arguments
    if ( f.arguments ){
      for ( iter = 0; iter<f.arguments.length; iter++ ){
        item.args[iter] = f.arguments[iter];
      }
    } else {
      item.args = null; // null = argument listing not supported
    }

    ret.push( item );
  }
  return ret;
}

在 Firefox-21 和 Chromium-25 中为我工作。


试试这个递归函数。
arguments.callee 已成为 deprecated for many years
G
GrayedFox

解决此问题的另一种方法是简单地将调用函数的名称作为参数传递。

例如:

function reformatString(string, callerName) {

    if (callerName === "uid") {
        string = string.toUpperCase();
    }

    return string;
}

现在,您可以像这样调用该函数:

function uid(){
    var myString = "apples";

    reformatString(myString, function.name);
}

我的示例使用了对函数名称的硬编码检查,但您可以轻松地使用 switch 语句或其他一些逻辑来执行您想要的操作。


我相信这在很大程度上也解决了跨浏览器兼容性问题。但请先测试一下,然后再假设它是真的! (开始出汗)
A
Abrar Jahin

据我所知,我们从这样的给定来源有两种方法-

arguments.caller function whoCalled() { if (arguments.caller == null) console.log('我是从全局范围调用的。'); else console.log(arguments.caller + '打电话给我!'); } Function.caller function myFunc() { if (myFunc.caller == null) { return '函数是从顶部调用的!'; } else { return '这个函数的调用者是 ' + myFunc.caller; } }

认为你有你的答案:)。


这是 deprecated for many years,并且 Function.caller 在严格模式下不起作用。
C
Community

为什么上面的所有解决方案看起来都像火箭科学。同时,它不应该比这个片段更复杂。所有功劳归于这个人

How do you find out the caller function in JavaScript?

var stackTrace = function() {

    var calls = [];
    var caller = arguments.callee.caller;

    for (var k = 0; k < 10; k++) {
        if (caller) {
            calls.push(caller);
            caller = caller.caller;
        }
    }

    return calls;
};

// when I call this inside specific method I see list of references to source method, obviously, I can add toString() to each call to see only function's content
// [function(), function(data), function(res), function(l), function(a, c), x(a, b, c, d), function(c, e)]

这就是我使用这个得到的:TypeError:'caller'、'callee'和'arguments'属性可能无法在严格模式函数或调用它们的参数对象上访问。任何想法如何在严格模式下工作?
吴家荣

我认为以下代码段可能会有所帮助:

window.fnPureLog = function(sStatement, anyVariable) {
    if (arguments.length < 1) { 
        throw new Error('Arguments sStatement and anyVariable are expected'); 
    }
    if (typeof sStatement !== 'string') { 
        throw new Error('The type of sStatement is not match, please use string');
    }
    var oCallStackTrack = new Error();
    console.log(oCallStackTrack.stack.replace('Error', 'Call Stack:'), '\n' + sStatement + ':', anyVariable);
}

执行代码:

window.fnPureLog = function(sStatement, anyVariable) {
    if (arguments.length < 1) { 
        throw new Error('Arguments sStatement and anyVariable are expected'); 
    }
    if (typeof sStatement !== 'string') { 
        throw new Error('The type of sStatement is not match, please use string');
    }
    var oCallStackTrack = new Error();
    console.log(oCallStackTrack.stack.replace('Error', 'Call Stack:'), '\n' + sStatement + ':', anyVariable);
}

function fnBsnCallStack1() {
    fnPureLog('Stock Count', 100)
}

function fnBsnCallStack2() {
    fnBsnCallStack1()
}

fnBsnCallStack2();

日志如下所示:

Call Stack:
    at window.fnPureLog (<anonymous>:8:27)
    at fnBsnCallStack1 (<anonymous>:13:5)
    at fnBsnCallStack2 (<anonymous>:17:5)
    at <anonymous>:20:1 
Stock Count: 100

这非常有用,因为(无论如何在 Chrome 上)转储未修改的新 Error() 对象会显示 SOURCE 文件/行信息(例如 TypeScript 信息)。但是,在替换之后,您将获得 ACTUAL 转译的 JavaScript 信息。我不知道为什么只替换一个字符串会这样做,但这是一个巧妙的意外。此外,使用转译的代码,它会显示代码来自的实际 URL,而不仅仅是模块名称。在任何一种情况下提取输出的第三行都可以在原始源代码或转译的 JavaScript 中生成调用者的位置。