你如何在JavaScript中找到调用函数?

时间:2008-11-11 09:04:39

标签: javascript callstack

function main()
{
   Hello();
}

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

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

32 个答案:

答案 0 :(得分:953)

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

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

  

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


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

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

答案 1 :(得分:146)

栈跟踪

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

但并非所有新闻都是好的:

  1. 获取堆栈跟踪非常慢,所以要小心(请阅读this了解更多信息)。

  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. 并非所有浏览器都会提供相同的信息。即参数,代码列等

  4. <小时/>

    来电者姓名

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

    arguments.callee.caller.name
    

    但请注意,此名称将是function关键字后面的名称。我发现没有办法(即使在谷歌浏览器上)获得更多功能而没有获得整个功能的代码。

    <小时/>

    来电者功能代码

    总结其余的最佳答案(由Pablo Cabrera,nourdine和Greg Hewgill撰写)。 您可以使用的唯一跨浏览器且非常安全的事情是:

    arguments.callee.caller.toString();
    

    将显示来电者功能的代码。遗憾的是,这对我来说还不够,这就是为什么我给你提供StackTrace和调用函数名称的提示(虽然它们不是跨浏览器)。

答案 2 :(得分:50)

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

此代码:

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

相当于:

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

显然第一位更容易移植,因为你可以更改函数的名称,比如从“Hello”到“Ciao”​​,并且仍然可以完成所有工作。

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

答案 3 :(得分:50)

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

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

直到来电者为null

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

答案 4 :(得分:41)

我知道你提到“在Javascript中”,但如果目的是调试,我认为使用浏览器的开发者工具会更容易。这就是它在Chrome中的外观: enter image description here 只需将调试器放在要调查堆栈的位置即可。

答案 5 :(得分:36)

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

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

答案 6 :(得分:27)

如果您不打算在IE中运行它&lt; 11然后console.trace()适合。

function main() {
    Hello();
}

function Hello() {
    console.trace()
}

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

答案 7 :(得分:21)

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

答案 8 :(得分:18)

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

答案 9 :(得分:18)

function Hello() {
    alert(Hello.caller);
}

答案 10 :(得分:16)

看起来这是一个相当解决的问题,但我最近发现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('.');
  };
}

答案 11 :(得分:12)

我会这样做:

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

答案 12 :(得分:11)

尝试访问此内容:

arguments.callee.caller.name

答案 13 :(得分:10)

只需控制台记录您的错误堆栈。然后你就可以知道你是如何被召唤的

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

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

sello()
&#13;
&#13;
&#13;

答案 14 :(得分:7)

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

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

我测试过这是chrome,safari和IE(10和8)。工作良好。只有一个功能很重要,所以如果你被大小提琴吓到了,请阅读下面的内容。

注意: 在这个小提琴中有相当数量的我自己的“样板”。你可以删除所有这些并使用split's,如果你愿意。这只是我过去依赖的一套超安全的“功能”。

还有一个“JSFiddle”模板,我用它来制作许多小提琴,只需快速摆弄。

答案 15 :(得分: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]);

答案 16 :(得分:6)

2018更新

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

以下功能似乎可以在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 with:', ...messages);
    } else {
      console.log(fnName(logLines[2]), 'called with:', ...messages);
    }
  } else {
    console.log(...messages);
  }
}

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

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

答案 17 :(得分:5)

只是想让您知道,在 PhoneGap / Android name似乎无法正常工作。但arguments.callee.caller.toString()可以解决问题。

答案 18 :(得分:4)

此处,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>

答案 19 :(得分:4)

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

以下是一个例子:

&#13;
&#13;
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();
&#13;
&#13;
&#13;

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

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

结论:可以确定&#34; main&#34;是&#34;你好&#34;使用stack对象中的Error。事实上,它适用于callee / caller方法不起作用的情况。它还会显示上下文,即源文件和行号。但是,需要努力使解决方案跨平台。

答案 20 :(得分:4)

这是get full stacktrace的函数:

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

答案 21 :(得分:3)

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

答案 22 :(得分: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中为我工作。

答案 23 :(得分:1)

为什么上述所有解决方案都像火箭科学。同时,它不应该比这个片段更复杂。这个家伙的所有学分

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

答案 24 :(得分:1)

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

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

例如,以下内容属于非标准版,但已经过之前(2016年3月29日)和当前(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();

答案 25 :(得分: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.caller

    function myFunc()
    {
       if (myFunc.caller == null) {
          return 'The function was called from the top!';
       }
       else
       {
          return 'This function\'s caller was ' + myFunc.caller;
        }
    }
    
  3. 认为你有答案:)。

答案 26 :(得分:1)

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

例如:

function reformatString(string, callerName) {

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

    return string;
}

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

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

    reformatString(myString, function.name);
}

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

答案 27 :(得分:0)

对我非常有用,您可以选择要返回多少功能:

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

答案 28 :(得分:0)

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

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

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

答案 29 :(得分:0)

由于先前的答案都无法像我想要的那样工作(仅获取最后一个函数调用者而不是字符串或调用堆栈的函数),我在这里将解决方案发布给了像我这样的人,并希望这对他们有用:

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

答案 30 :(得分: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

答案 31 :(得分:0)

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

Execute