如何找到JavaScript中的调用者函数?


865
function main()
{
   Hello();
}

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

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


63
我希望这只是为了帮助您进行调试。根据调用者改变行为是一个坏主意。
OJ。

什么时候对调试有用?
安德森·格林

33
@AndersonGreen,例如,当您使用默认的模板渲染方法并看到它被调用两次时。您无需查看数千个LoC或使用调试器进行繁琐的操作,而可以看到当时的堆栈。
tkone 2012年

28
要查看堆栈跟踪,请对Chrome使用console.trace()。但是不知道其他人
lukas.pukenis

5
为什么这是个坏主意?
雅各布·施耐德

Answers:


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

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

非标准
此功能是非标准的,不在标准范围内。请勿在面向Web的生产站点上使用它:它不适用于每个用户。实现之间也可能存在很大的不兼容性,并且行为将来可能会改变。


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

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

254
arguments.callee.caller.name将获得函数的名称。
Rocket Hazmat

137
“在严格模式功能或调用它们的参数对象上,可能无法访问'caller','callee'和'arguments'属性”-在ES5中已弃用,并在严格模式下将其删除。
ThatGuy

12
仅当您不使用严格模式时,它才有效。因此删除'use strict';可能会有所帮助。
pvorb

23
arguments可以在严格模式下从功能内访问该函数,不建议这样做。只是不是来自外部的function.arguments。另外,如果您有命名实参,则其中的arguments [i]形式将不会跟踪您对该函数内的命名版本所做的更改。
rvr_jon

41
自该帖子于2011年上市以来,该方法已过时。首选方法为Function.caller(截至2015年)。
格雷格

152

堆栈跟踪

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

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

  1. 这实在是太慢得到堆栈跟踪,所以要小心(读更多)。

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

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

    Google Chrome ... kls.Hello ( ...浏览器会发出警报,但是大多数浏览器会在关键字之后加上一个函数名称function,并将其视为匿名函数。Klass如果您不给kls函数指定名称,Chrome甚至无法使用该名称。

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

  3. 并非所有浏览器都为您提供相同的信息。也就是说,参数,代码列等。


呼叫者功能名称

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

arguments.callee.caller.name

但请注意,此名称将是function关键字之后的名称。我发现没有方法(即使在Google Chrome上)也无法获得全部功能的代码。


来电功能码

并总结其余的最佳答案(由Pablo Cabrera,nourdine和Greg Hewgill撰写)。您可以使用的唯一跨浏览器和真正安全的东西是:

arguments.callee.caller.toString();

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


1
也许你应该增加Function.caller@格雷格的答案
扎克Lysobey

Function.caller但是,无法在严格模式下工作。
RickardElimää19年

54

我知道您提到“用Java语言编写”,但是如果目的是调试,我认为仅使用浏览器的开发人员工具会更容易。在Chrome中是这样的: 在此处输入图片说明 只需将调试器放在要调查堆栈的位置即可。


3
这是一个古老的问题……但这绝对是当今最有效的现代方法。
markstewie 2015年

53

回顾(并使其更加清晰)...

此代码:

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

等效于此:

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

显然,第一位操作员更可移植,因为您可以更改函数的名称,例如从“ Hello”更改为“ Ciao”​​,仍然可以使整个过程正常进行。

在后者中,如果您决定重构被调用函数的名称(Hello),则必须更改其所有出现的位置:(


7
arguments.callee.caller在Chrome 25.0.1364.5开发人员中始终为null
Kokizzu 2013年

53

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

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

直到来电者是null

注意:这会在递归函数上造成无限循环。


2
对不起,您的回复很晚,但是我之前没有看到您的评论;仅在递归情况下不起作用,在其他情况下应起作用。
ale5000 '16

45

我通常(new Error()).stack在Chrome中使用。令人高兴的是,这还为您提供了调用方调用该函数的行号。缺点是它将堆栈的长度限制为10,这就是为什么我首先来到此页面的原因。

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


您能否添加一些有关您提供的说明的描述?
abarisone 2015年

6
这是我'use strict';到位后唯一可以工作的东西。给我我需要的信息-谢谢!
杰里米·哈里斯

4
关于堆栈长度的限制...您可以使用“ Error.stackTraceLimit = Infinity”更改该限制。
汤姆(Tom)

(new Error(“ StackLog”))。stack.split(“ \ n”)使其更易于阅读。
Teoman shipahi

36

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

function main() {
    Hello();
}

function Hello() {
    console.trace()
}

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

工作正常!应添加更多了赞成票
Krunal潘卡尔

22

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

以下代码说明了其用法:

function Hello() { return Hello.caller;}

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

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

有关过时的arguments.caller的说明:https : //developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/arguments/caller

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


1
这几天是正确的答案。您不能再做arguments.caller.callee的东西了。希望我们可以将其移到顶部,因为其他所有东西现在都已经过时了。
coblr 2015年

4
似乎在严格模式下是不可能的?Cannot access caller property of a strict mode function
Zach Lysobey 2015年

Function.caller在严格模式下也不适合我。同样,根据MDN,function.caller是非标准的,不应在生产中使用。不过,它可能适用于调试。
jkdev

如果非标准版本可以在Node上运行,我对它没有问题,但是在严格模式下根本不允许这样做(我在节点6.10上进行了测试)。同样适用于“参数”。我收到一条错误消息:“''caller”和“ arguments”是受限制的函数属性,在这种情况下无法访问。”
Tom

21

我会这样做:

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

这很棒!应该被接受为正确的答案,因为其他方法已经过时\不再工作了
Yuval Pruss



16

看来这是一个已解决的问题,但是我最近发现,在“严格模式”下不允许被调用者,因此我自己编写了一个类,该类将从其调用位置获取路径。它是小型帮助程序库一部分,如果要独立使用代码,请更改用于返回调用方堆栈跟踪的偏移量(使用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()在控制台中不适用于我(尚未在文件中尝试过),但似乎有一个合理的想法。无论如何都应该提高可见度。
ninjagecko 2014年

这个主意很棒。我的解析方式有所不同,但是在nw.js应用程序中,这确实是提供我所需要的唯一想法。
安德鲁·格罗斯

请提供一个有关如何调用此函数的示例。
pd_au


11

只需控制台记录您的错误堆栈。然后您就可以知道您的名字了

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

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

sello()


10

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();
}());


4
那是不可思议的,而且令人恐惧。
伊恩(Ian)

我收到了“ foo的呼叫:嗨”,但是foo却没有被“呼叫嗨”,log的名字是“ hi呼叫”
AndrewR 19-10-29

正确,错误消息的语法中有一个“放错位置的修饰符”。它的意思是说“从函数f调用了log,它希望将消息X打印出来”,但要尽可能简洁。
罗凡尼

9

在ES6和严格模式下,使用以下命令获取Caller函数

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

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


要获取被调用方(当前函数名称),请使用: console.log((new Error()).stack.split("\n")[1].trim().split(" ")[1])
VanagaS

7

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

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

我测试了chrome,safari和IE(10和8)。工作良好。只有1个功能很重要,因此,如果您对大提琴感到害怕,请阅读以下内容。

注意:在这个小提琴中有很多我自己的“样板”。您可以删除所有这些,并根据需要使用split的。这只是我要依赖的一组超安全功能。

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


我想知道您是否可以在某些情况下添加“帮助程序”作为原型的扩展,例如:String.prototype.trim = trim;
自闭症患者

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]);



4

在这里,除以外的所有内容都通过RegExp functionname从中剥离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>

这仍然返回整个方法声明
Maslow

4

这是一个获取完整stacktrace的函数:

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

3

heystewart的答案JiarongWu的答案都提到Error对象可以访问stack

这是一个例子:

function main() {
  Hello();
}

function Hello() {
  var stack;
  try {
    throw new Error();
  } catch (e) {
    stack = e.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中的“主”来确定“ Hello”的调用方 Error。实际上,在callee/ caller方法不起作用的情况下它将起作用。它还将显示上下文,即源文件和行号。但是,需要付出很多努力才能使解决方案跨平台。


2

请注意,您不能在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
    }
    */
}

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


尝试此递归函数。
daniel1426

arguments.callee弃用很多年了
Dan Dascalescu

1

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

例如:

function reformatString(string, callerName) {

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

    return string;
}

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

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

    reformatString(myString, function.name);
}

我的示例使用对功能名称的硬编码检查,但是您可以轻松地使用switch语句或其他逻辑来完成所需的操作。


我相信这在大多数情况下也可以解决跨浏览器的兼容性问题。但是请先测试一下,然后再假设它是真的!(开始出汗
GrayedFox

1

据我所知,我们有2种方法可以从这样的特定来源获得此信息-

  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;
        }
    }

想你有你的答案:)。


已经不推荐使用它很多年了,Function.caller在严格模式下不起作用。
Dan Dascalescu

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)]

3
这就是我使用此方法得到的结果:TypeError:“调用者”,“被调用者”和“参数”属性可能无法在严格模式函数或调用它们的参数对象上访问。有什么想法如何在严格模式下工作吗?
hard_working_ant

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();


不错的技巧,但不适用于完全处于严格模式下的ES5模块
Dan Dascalescu

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
}

0

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

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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.