如何在 JavaScript 中查找调用方函数?

function main()
{
Hello();
}


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

有办法找到调用堆栈吗?

561728 次浏览

请注意,此解决方案已弃用,根据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更安全,因为arguments.caller已弃用

尝试访问此:

arguments.callee.caller.name
function Hello() {alert(Hello.caller);}

为了总结(并使其更清晰)…

此代码:

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

相当于这样:

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

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

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

StackTrace

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

但并不是所有的消息都是好的:

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

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

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

    GoogleChrome会提醒... kls.Hello ( ...,但大多数浏览器会期望在关键字function之后有一个函数名称,并将其视为匿名函数。如果您不将名称kls给函数,甚至Chrome将能够使用Klass名称。

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

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


调用者函数名称

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

arguments.callee.caller.name

但请注意,这个名称将是function关键字之后的名称。我发现没有办法(即使在GoogleChrome上)在没有获得整个函数的代码的情况下获得更多。


来电功能码

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

arguments.callee.caller.toString();

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

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

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

直到调用者是null

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

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

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

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

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

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

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

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

在这里,除了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]);elsealert ("The name of the "+typeof window[name]+" that called is "+name);}</script>

尝试以下代码:

function getStackTrace(){var f = arguments.callee;var ret = [];var item = {};var iter = 0;
while ( f = f.caller ){// Initializeitem = {name: f.name || null,args: [], // Empty array = no arguments passedcallback: f};
// Function argumentsif ( 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工作。

看起来这是一个相当解决的问题,但我最近发现在严格模式下不允许使用callee,所以为了我自己的使用,我写了一个类,它将从调用它的位置获取路径。它是小助手库的一部分,如果你想单独使用代码,更改用于返回调用者堆栈跟踪的偏移量(使用1而不是2)

function ScriptPath() {var scriptPath = '';try {//Throw an error to generate a stack tracethrow new Error();}catch(e) {//Split the stack trace into each linevar stackLines = e.stack.split('\n');var callerIndex = 0;//Now walk though each line until we find a path referencefor(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 returnpathParts = 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('.');};}

只是想让你知道,在PhoneGap/安卓上,name似乎不起作用。但是arguments.callee.caller.toString()会成功的。

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

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

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

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

如果您出于某种原因确实需要该功能,并希望它跨浏览器兼容,而不用担心严格的东西并向前兼容,那么请传递this引用:

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}

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

function main() {Hello();}
function Hello() {console.trace()}
main()// Hello @ VM261:9// main @ VM261:4

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

例如:

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

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

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

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

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

  1. arguments.caller

    function whoCalled(){if (arguments.caller == null)console.log('I was called from the global scope.');elseconsole.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;}}

Think u have your answer :).

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

赏金要求调用者在严格模式下获得,我能看到这一点的唯一方法是引用一个声明为外面的严格模式的函数。

例如,以下是非标准的,但已使用以前(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();

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

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

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

如何在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)]

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

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:1Stock Count: 100

heystewart的回答嘉荣武的回答都提到Error对象可以访问stack

这里有一个例子:

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

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

Safari  : Caller is: main@https://stacksnippets.net/js:14:8Firefox : Caller is: main@https://stacksnippets.net/js:14:3Chrome  : 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方法不起作用的情况下工作。它还将向您显示上下文,即源文件和行号。然而,需要努力使解决方案跨平台。

我会这样做:

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

2018更新

#0在严格模式下被禁止。这里有一个使用(非标准)#1堆栈的替代方案。

下面的函数似乎在Firefox 52和Chrome61-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();}());

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

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

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

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 exampleconsole.log("ex3  =>  " + getCallerName(Main));}
Main();

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

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

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

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

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

请注意,您不能在Node.js中使用Function.caller,而是使用来电显示包。例如:

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}*/}

对我来说很棒,你可以选择你想在函数中返回多少:

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];elseresult = stack[stackIndex].split(' ')[0];return result;}
function main(){Hello();}
function Hello(){new Error().stack}

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

1. console.trace();2. console.log((new Error).stack)
// do the same as #2 just with better view3. console.log((new Error).stack.split("\n"))

您可以在函数中使用debugger;。打开inespect元素并观察调用堆栈;

使用Strict Mode On/Off(JavaScript和TypeScript),如果(!)调用者存在,您可以尝试这个

console.log(`caller:${(new Error()).stack?.split('\n')[2].trim().split(' ')[1]}`)