代码之家  ›  专栏  ›  技术社区  ›  Ray Lu

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

  •  766
  • Ray Lu  · 技术社区  · 17 年前
    function main()
    {
       Hello();
    }
    
    function Hello()
    {
      // How do you find out the caller function is 'main'?
    }
    

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

    29 回复  |  直到 7 年前
        1
  •  1044
  •   Hearen    7 年前
    function Hello()
    {
        alert("caller is " + Hello.caller);
    }
    

    非标准的 Function.caller :

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


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

    function Hello()
    {
        alert("caller is " + arguments.callee.caller.toString());
    }
    
        2
  •  159
  •   iconoclast    13 年前

    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 并将其视为匿名函数。甚至连Chrome都不能使用 Klass kls 对函数进行修改。

      {guess: true} 但我没有发现这样做有什么真正的进步。


    调用函数名

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

    arguments.callee.caller.name
    

    但请注意,此名称将位于 关键词。我发现(甚至在谷歌Chrome上)没有办法在没有获得整个函数的代码的情况下获得更多。


    调用函数代码

    并总结了其余的最佳答案(由Pablo Cabrera、nourdine和Greg Hewgill撰写)。

    arguments.callee.caller.toString();
    

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

        3
  •  67
  •   Phil    11 年前

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

        4
  •  63
  •   abarisone    11 年前

    我通常使用 (new Error()).stack 镀铬的。

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

        5
  •  53
  •   ale5000    10 年前

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

    直到打电话的人 null .

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

        6
  •  51
  •   TankorSmash    10 年前

    此代码:

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

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

    显然,第一位更便于移植,因为您可以更改函数的名称,从“Hello”改为“Ciao”,并且仍然可以使整个功能正常工作。

    在后一种情况下,如果您决定重构被调用函数的名称(Hello),则必须更改所有出现的函数:(

        7
  •  47
  •   fny    8 年前

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

    function main() {
        Hello();
    }
    
    function Hello() {
        console.trace()
    }
    
    main()
    // Hello @ VM261:9
    // main @ VM261:4
    
        8
  •  26
  •   inorganik    8 年前

    function Hello() {
      console.trace();
    }
    
        9
  •  25
  •   VanagaS    6 年前

    在ES6和Strict模式下,使用以下命令获取调用者函数

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

    请注意,如果没有调用方或没有上一个堆栈,那么上面的行将抛出一个异常。相应地使用。

    console.log((new Error()).stack.split("\n")[1].trim().split(" ")[1]) 
    
        10
  •  23
  •   Greg    10 年前

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

    function Hello() { return Hello.caller;}
    
    Hello2 = function NamedFunc() { return NamedFunc.caller; };
    
    function main()
    {
       Hello();  //both return main()
       Hello2();
    }
    

    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments/caller

    注意函数。调用方是非标准的: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/caller

        11
  •  21
  •   QueueHammer    12 年前

    看起来这个问题已经解决了,但我最近发现 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('.');
      };
    }
    
        12
  •  20
  •   Shadow2531    17 年前
    function Hello() {
        alert(Hello.caller);
    }
    
        13
  •  18
  •   alex    15 年前

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

        14
  •  16
  •   Rovanion    6 年前

    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 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();
    }());
        15
  •  16
  •   TOPKAT    6 年前

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

    结论:使用 堆栈 callee / caller 这种方法行不通。它还将显示上下文,即源文件和行号。但是,需要努力使解决方案跨平台。

        16
  •  12
  •   Prasanna    8 年前

    const hello = () => {
      console.log(new Error('I was called').stack)
    }
    
    const sello = () => {
      hello()
    }
    
    sello()
        17
  •  11
  •   Brad    13 年前

    尝试访问以下内容:

    arguments.callee.caller.name
    
        18
  •  7
  •   bladnman    13 年前

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

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

    注: 这把小提琴里有相当多我自己的“样板”。如果愿意,您可以删除所有这些内容并使用split。这只是我所依赖的一组非常安全的函数。

        19
  •  6
  •   JoolzCheat    14 年前

    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]);
    
        20
  •  5
  •   Pablo Armentano    12 年前

    我只是想让你知道 name 似乎不起作用。但是 arguments.callee.caller.toString() 我会成功的。

        21
  •  4
  •   Alexis Pigeon Shawn Skelton    13 年前

    这里,除了这个 functionname caller.toString() ,使用RegExp。

    <!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>
    
        22
  •  4
  •   user586399 user586399    10 年前

    get full stacktrace :

    function stacktrace() {
    var f = stacktrace;
    var stack = 'Stack trace:';
    while (f) {
      stack += '\n' + f.name;
      f = f.caller;
    }
    return stack;
    }
    
        23
  •  4
  •   ns16 Darkato    6 年前

    注意,您不能使用 Function.caller 在Node.js中,使用 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
        }
        */
    }
    
        24
  •  1
  •   Diego Augusto Molina    13 年前

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

        25
  •  1
  •   GrayedFox    10 年前

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

    function reformatString(string, callerName) {
    
        if (callerName === "uid") {
            string = string.toUpperCase();
        }
    
        return string;
    }
    

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

    function uid(){
        var myString = "apples";
    
        reformatString(myString, function.name);
    }
    

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

        26
  •  1
  •   S M Abrar Jahin    10 年前

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

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

        27
  •  1
  •   Community Mohan Dere    9 年前

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

    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)]
    
        28
  •  1
  •   吴家荣    9 年前

    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
    
        29
  •  1
  •   autistic    8 年前

    赏金要求在 模式,我能看到这一点的唯一方法是引用一个声明的函数 外部

    例如,以下为非标准版本,但已在Chrome、Edge和Firefox的早期(2016年3月29日)和当前(2018年8月1日)版本中进行了测试。

    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();
        30
  •  1
  •   Israel    5 年前

    对我来说效果很好,您可以选择在函数中返回多少:

    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;
    }
    
    推荐文章