题
function main()
{
Hello();
}
function Hello()
{
// How do you find out the caller function is 'main'?
}
有没有办法找出调用堆栈?
解决方案
function Hello()
{
alert("caller is " + Hello.caller);
}
请注意,此功能非标准,来自 Function.caller
:
<强>非标准强>结果 此功能是非标准功能,不符合标准。不要在面向Web的生产站点上使用它:它不适用于每个用户。实现之间可能存在很大的不兼容性,并且行为可能在将来发生变化。
以下是2008年的旧答案,现代Javascript不再支持:
function Hello()
{
alert("caller is " + arguments.callee.caller.toString());
}
其他提示
栈跟踪
您可以使用特定于浏览器的代码找到整个堆栈跟踪。好消息是有人已经成功了;这是 GitHub上的项目代码。
但并非所有新闻都是好的:
-
获取堆栈跟踪非常慢,所以要小心(阅读这个更多)。
-
您需要将堆栈跟踪的函数名称定义为清晰。因为如果你有这样的代码:
var Klass = function kls() { this.Hello = function() { alert(printStackTrace().join('\n\n')); }; } new Klass().Hello();
Google Chrome会提醒
... kls.Hello ( ...
,但大多数浏览器都会在关键字function
后面找到一个功能名称,并将其视为匿名功能。如果您没有为该功能提供名称Klass
,那么甚至Chrome都无法使用kls
名称。顺便说一句,你可以将选项
{guess: true}
传递给函数printStackTrace,但是我没有找到任何真正的改进。 -
并非所有浏览器都会提供相同的信息。即参数,代码列等
醇>
来电者姓名
顺便说一句,如果您只想要调用函数的名称(在大多数浏览器中,而不是IE),您可以使用:
arguments.callee.caller.name
但请注意,此名称将是<=>关键字后面的名称。我发现没有办法(即使在谷歌浏览器上)获得更多功能而没有获得整个功能的代码。
来电者功能代码
总结其余的最佳答案(由Pablo Cabrera,nourdine和Greg Hewgill撰写)。 您可以使用的唯一跨浏览器且非常安全的事情是:
arguments.callee.caller.toString();
将显示来电者功能的代码。遗憾的是,这对我来说还不够,这就是为什么我给你提供StackTrace和调用函数名称的提示(虽然它们不是跨浏览器)。
回顾一下(并使其更清晰)......
此代码:
function Hello() {
alert("caller is " + arguments.callee.caller.toString());
}
相当于:
function Hello() {
alert("caller is " + Hello.caller.toString());
}
显然第一位更具可移植性,因为您可以更改函数的名称,例如来自<!>“Hello <!>”;到<!>“Ciao <!>”,仍然可以完成所有工作。
在后者中,如果您决定重构被调用函数(Hello)的名称,则必须更改其所有出现次数:(
您可以获得完整的堆栈跟踪:
arguments.callee.caller
arguments.callee.caller.caller
arguments.callee.caller.caller.caller
直到来电者null
。
注意:它会导致递归函数无限循环。
我知道你在Javascript <!>“中提到了<!>”,但如果目的是调试,我认为使用浏览器的开发者工具会更容易。这就是它在Chrome中的外观: 只需将调试器放在要调查堆栈的位置即可。
我通常在Chrome中使用(new Error()).stack
。
好消息是,它还为您提供了调用者调用函数的行号。缺点是它将堆栈的长度限制为10,这就是我首先来到这个页面的原因。
(我在执行期间使用它来在低级构造函数中收集callstack,以便稍后查看和调试,因此设置断点是没有用的,因为它会被命中数千次)
如果您不打算在IE中运行它<!> lt; 11然后 console.trace()适合。
function main() {
Hello();
}
function Hello() {
console.trace()
}
main()
// Hello @ VM261:9
// main @ VM261:4
您可以使用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
function Hello() {
alert(Hello.caller);
}
使用*arguments.callee.caller
更安全,因为arguments.caller
已弃用 ......
看起来这是一个相当解决的问题,但我最近发现在'严格模式'中不允许被调用者,所以为了我自己的使用,我编写了一个类,它将从调用它的位置获取路径。它是小助手lib的一部分,如果你想独立使用代码,可以更改offset用于返回调用者的堆栈跟踪(使用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 Hello() {
console.trace();
}
尝试访问此内容:
arguments.callee.caller.name
只需控制台记录您的错误堆栈。然后你就可以知道你是如何被召唤的
const hello = () => {
console.log(new Error('I was called').stack)
}
const sello = () => {
hello()
}
sello()
我想在这里添加我的小提琴:
http://jsfiddle.net/bladnman/EhUm3/
我测试过这是chrome,safari和IE(10和8)。工作良好。只有一个功能很重要,所以如果你被大小提琴吓到了,请阅读下面的内容。
注意: 有相当数量的我自己的<!>“样板<!>”;在这个小提琴。你可以删除所有这些并使用split's,如果你愿意。这只是一个超安全的<!>我已经依赖的一系列功能。
还有一个<!>“JSFiddle <!>”;那里的模板,我用于许多小提琴,只是简单快速摆弄。
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 with:', ...messages);
} else {
console.log(fnName(logLines[2]), 'called with:', ...messages);
}
} else {
console.log(...messages);
}
}
function foo() {
log('hi', 'there');
}
(function main() {
foo();
}());
如果您只想要功能名称而不是代码,并且想要一个独立于浏览器的解决方案,请使用以下命令:
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]);
只是想让您知道 PhoneGap / Android name
似乎无法正常工作。但是arguments.callee.caller.toString()
会做到这一点。
在这里,除了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>
这是一个获取完整堆栈跟踪的功能:
function stacktrace() {
var f = stacktrace;
var stack = 'Stack trace:';
while (f) {
stack += '\n' + f.name;
f = f.caller;
}
return stack;
}
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中,堆栈将是未定义的 - 您必须抛出一个真正的异常来检索堆栈。
结论:可以确定<!> quot; main <!> quot;是<!>“Hello <!>”的调用者使用callee
对象中的caller
。事实上,它适用于<=> / <=>方法不起作用的情况。它还会显示上下文,即源文件和行号。但是,需要努力使解决方案跨平台。
请尝试以下代码:
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中为我工作。
解决此问题的另一种方法是简单地将调用函数的名称作为参数传递。
例如:
function reformatString(string, callerName) {
if (callerName === "uid") {
string = string.toUpperCase();
}
return string;
}
现在,您可以像这样调用函数:
function uid(){
var myString = "apples";
reformatString(myString, function.name);
}
我的示例使用了对函数名称的硬编码检查,但您可以轻松地使用switch语句或其他逻辑来执行您想要的操作。
据我所知,我们从这样的来源中得到了两种方法 -
-
function whoCalled() { if (arguments.caller == null) console.log('I was called from the global scope.'); else console.log(arguments.caller + ' called me!'); }
-
function myFunc() { if (myFunc.caller == null) { return 'The function was called from the top!'; } else { return 'This function\'s caller was ' + myFunc.caller; } }
醇>
认为你有答案:)。
为什么上述所有解决方案都像火箭科学。同时,它不应该比这个片段更复杂。这个家伙的所有学分
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)]
我试图用这个问题解决问题和当前的赏金。
赏金要求在严格模式下获得调用者,我唯一可以看到这样做的方法是引用严格模式 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();
如果您出于某种原因确实需要该功能并希望它与跨浏览器兼容并且不担心严格的内容并且向前兼容,那么请传递此参考:
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
}
我认为以下代码可能会有所帮助:
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
因为以前的答案都没有像我一样的工作(只是最后一个函数调用者不是字符串或callstack的函数)我在这里发布我的解决方案给那些像我一样的人,并希望这对他们有效:
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();