970
function main()
{
   Hello();
}

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

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

4

35 回答 35

1055

请注意,此解决方案已弃用,根据 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());
}
于 2008-11-11T09:07:16.327 回答
160

堆栈跟踪

您可以使用浏览器特定代码找到整个堆栈跟踪。好在有人已经做到了;这是GitHub 上的项目代码

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

  1. 获取堆栈跟踪真的很慢,所以要小心(阅读更多信息)。

  2. 您需要定义函数名称以使堆栈跟踪清晰易读。因为如果你有这样的代码:

    var Klass = function kls() {
       this.Hello = function() { alert(printStackTrace().join('\n\n')); };
    }
    new Klass().Hello();
    

    谷歌浏览器会发出警报... kls.Hello ( ...,但大多数浏览器会期望在关键字之后有一个函数名称function,并将其视为匿名函数。Klass如果您不为函数命名,甚至 Chrome 都无法使用该名称kls

    顺便说一句,您可以将选项传递给函数 printStackTrace,{guess: true}但这样做并没有发现任何真正的改进。

  3. 并非所有浏览器都能为您提供相同的信息。即参数、代码列等。


调用函数名称

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

arguments.callee.caller.name

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


调用函数代码

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

arguments.callee.caller.toString();

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

于 2010-09-24T16:38:24.757 回答
70

我知道您提到“在 Javascript 中”,但如果目的是调试,我认为使用浏览器的开发人员工具会更容易。这就是它在 Chrome 中的外观: 在此处输入图像描述 只需将调试器放到您想要调查堆栈的位置。

于 2015-02-03T00:28:59.773 回答
67

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

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

于 2015-05-07T14:15:15.223 回答
53

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

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

直到来电者是null.

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

于 2010-10-28T22:33:07.903 回答
51

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

这段代码:

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

相当于:

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

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

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

于 2009-04-06T13:47:43.603 回答
49

如果您不打算在 IE < 11 中运行它,那么console.trace()将适合。

function main() {
    Hello();
}

function Hello() {
    console.trace()
}

main()
// Hello @ VM261:9
// main @ VM261:4
于 2016-01-18T10:58:13.617 回答
28

我会这样做:

function Hello() {
  console.trace();
}
于 2017-11-16T22:40:47.353 回答
28

在 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]) 
于 2019-07-14T01:09:25.160 回答
23

您可以使用 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

于 2015-06-06T21:01:16.960 回答
21

看起来这是一个非常解决的问题,但我最近发现在“严格模式”下不允许被调用者,所以为了我自己的使用,我编写了一个类,该类将从调用它的位置获取路径。它是一个小助手库的一部分,如果您想独立使用代码,请更改用于返回调用者堆栈跟踪的偏移量(使用 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('.');
  };
}
于 2014-03-04T07:14:20.333 回答
20
function Hello() {
    alert(Hello.caller);
}
于 2008-11-11T10:56:21.177 回答
18

*arguments.callee.caller由于arguments.caller弃用,因此使用起来更安全...

于 2008-11-11T09:50:39.297 回答
18

heystewart 的回答JiarongWu 的回答都提到该Error对象可以访问stack.

这是一个例子:

function main() {
  Hello();
}

function Hello() {
  var stack = new Error().stack;
  // N.B. stack === "Error\n  at Hello ...\n  at main ... \n...."
  var m = stack.match(/.*?Hello.*?\n(.*?)\n/);
  if (m) {
    var caller_name = m[1];
    console.log("Caller is:", caller_name)
  }
}

main();

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

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 中,堆栈将是未定义的 - 您必须抛出一个真正的异常来检索堆栈。

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

于 2017-07-13T05:36:43.987 回答
17

2018 更新

caller在严格模式下被禁止。这是使用(非标准)Error堆栈的替代方法。

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

'use strict';
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, 'called log with:', ...messages);
    } else {
      console.log(fnName(logLines[2]), 'called log with:', ...messages);
    }
  } else {
    console.log(...messages);
  }
}

function foo() {
  log('hi', 'there');
}

(function main() {
  foo();
}());

于 2018-02-26T10:02:40.740 回答
12

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

const hello = () => {
  console.log(new Error('I was called').stack)
}

const sello = () => {
  hello()
}

sello()

于 2018-03-14T09:49:23.917 回答
11

尝试访问这个:

arguments.callee.caller.name
于 2008-11-11T10:19:20.237 回答
7

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

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

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

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

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

于 2012-09-07T15:32:01.747 回答
6

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

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]);
于 2012-03-15T03:46:28.250 回答
5

只是想让你知道在PhoneGap/Androidname似乎没有工作。但arguments.callee.caller.toString()会成功的。

于 2014-04-08T17:59:18.403 回答
4

在这里,除了 之外的所有内容都被RegExpfunctionname剥离。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>
于 2012-09-12T08:35:44.517 回答
4

这是一个获取完整堆栈跟踪的函数:

function stacktrace() {
var f = stacktrace;
var stack = 'Stack trace:';
while (f) {
  stack += '\n' + f.name;
  f = f.caller;
}
return stack;
}
于 2016-04-13T11:12:59.750 回答
4

请注意,您不能在 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
    }
    */
}
于 2020-02-24T06:55:09.210 回答
1

试试下面的代码:

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 中为我工作。

于 2013-06-22T20:33:26.763 回答
1

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

例如:

function reformatString(string, callerName) {

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

    return string;
}

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

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

    reformatString(myString, function.name);
}

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

于 2016-03-04T15:29:18.073 回答
1

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

  1. arguments.caller

    function whoCalled()
    {
        if (arguments.caller == null)
           console.log('I was called from the global scope.');
        else
           console.log(arguments.caller + ' called me!');
    }
    
  2. 函数调用者

    function myFunc()
    {
       if (myFunc.caller == null) {
          return 'The function was called from the top!';
       }
       else
       {
          return 'This function\'s caller was ' + myFunc.caller;
        }
    }
    

认为你有你的答案:)。

于 2016-03-28T05:34:40.950 回答
1

我试图用这个问题来解决这个问题和当前的赏金。

赏金要求在严格模式下获得调用者,我可以看到这样做的唯一方法是引用在严格模式之外声明的函数。

例如,以下内容是非标准的,但已使用之前 (29/03/2016) 和当前 (2018 年 8 月 1 日) 版本的 Chrome、Edge 和 Firefox 进行了测试。

function caller()
{
   return caller.caller.caller;
}

'use strict';
function main()
{
   // Original question:
   Hello();
   // Bounty question:
   (function() { console.log('Anonymous function called by ' + caller().name); })();
}

function Hello()
{
   // How do you find out the caller function is 'main'?
   console.log('Hello called by ' + caller().name);
}

main();

于 2016-03-29T06:40:45.853 回答
1

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

如何在 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)]
于 2017-02-07T17:52:15.847 回答
1

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

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
于 2017-04-28T07:27:45.343 回答
1

由于以前的答案都不像我正在寻找的那样工作(只获取最后一个函数调用者而不是作为字符串或调用堆栈的函数)我在这里为像我这样的人发布我的解决方案,并希望这对他们有用:

function getCallerName(func)
{
  if (!func) return "anonymous";
  let caller = func.caller;
  if (!caller) return "anonymous";
  caller = caller.toString();
  if (!caller.trim().startsWith("function")) return "anonymous";
  return caller.substring(0, caller.indexOf("(")).replace("function","");
}


//  Example of how to use "getCallerName" function

function Hello(){
console.log("ex1  =>  " + getCallerName(Hello));
}

function Main(){
Hello();

// another example
console.log("ex3  =>  " + getCallerName(Main));
}

Main();

于 2018-10-22T09:19:36.073 回答
1

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

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;
}
于 2020-12-06T13:34:01.883 回答
1

我可以在 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")) 
于 2021-11-04T10:03:46.237 回答
0

如果您出于某种原因确实需要该功能并希望它与跨浏览器兼容并且不必担心严格的内容并且向前兼容,那么请传递此参考:

function main()
{
   Hello(this);
}

function Hello(caller)
{
    // caller will be the object that called Hello. boom like that... 
    // you can add an undefined check code if the function Hello 
    // will be called without parameters from somewhere else
}
于 2015-11-03T01:22:49.130 回答
0
function main()
{
   Hello();
}

function Hello()
{
  new Error().stack
}
于 2021-10-29T12:48:00.373 回答
0

您可以debugger;在函数中使用。打开无关元素并监视调用堆栈;

于 2022-01-29T11:33:37.683 回答