JavaScriptで呼び出し元関数をどのようにして見つけますか?


866
function main()
{
   Hello();
}

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

呼び出しスタックを見つける方法はありますか?


63
これがデバッグに役立つことを願っています。呼び出し元に基づいて動作を変えることは悪い考えです。
OJ。

これがデバッグに役立つのはいつですか?
アンダーソングリーン

33
たとえば、デフォルトのテンプレートレンダリングメソッドを取得して、2回呼び出されていることを確認すると、@ AndersonGreen。何千ものLoCをくまなく調べたり、デバッガを使って困難な手順を踏んだりするのではなく、その時点でスタックが何であったかを確認できます。
tkone 2012年

28
スタックトレースを表示するには、chromeにconsole.trace()を使用します。ただし、他人については知らない
lukas.pukenis

5
なぜこれが悪い考えなのですか?
Jacob Schneider

回答:


995
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関数の名前を取得します。
ロケットHazmat

137
「 'caller'、 'callee'、および 'arguments'プロパティは、厳密モードの関数またはそれらを呼び出すための引数オブ​​ジェクトではアクセスできません」-これらはES5では非推奨で、厳密モードでは削除されています。
ThatGuy、2011年

12
strictモードを使用していない場合にのみ機能します。したがって、削除'use strict';すると役立つ場合があります。
pvorb 2013

23
argumentsストリクトモードの関数内からアクセスできますが、それを廃止するのは愚かです。外部からのfunction.argumentsからではありません。また、名前付き引数がある場合、arguments [i]形式は、関数内の名前付きバージョンに加えた変更を追跡しません。
rvr_jon 2013年

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名前を付けないと、Chromeでも名前を使用できなくなりますkls関数にを。

    ちなみに、関数printStackTraceにオプションを渡すことができます{guess: true}が、それを行っても実際の改善はありませんでした。

  3. すべてのブラウザが同じ情報を提供するわけではありません。つまり、パラメーター、コード列などです。


呼び出し元関数名

ちなみに、(ほとんどのブラウザでは、IEではなく)呼び出し関数の名前だけが必要な場合は、次のように使用できます。

arguments.callee.caller.name

ただし、この名前はfunctionキーワードの後の名前になることに注意してください。(Google Chromeでさえ)関数全体のコードを取得せずにそれ以上のものを取得する方法は見つかりませんでした。


発信者機能コード

そして、残りの最良の回答を要約します(Pablo Cabrera、nourdine、およびGreg Hewgillによる)。使用できる唯一のクロスブラウザで本当に安全なものは:

arguments.callee.caller.toString();

呼び出し元関数のコードが表示されます。残念なことに、それだけでは十分ではありません。そのため、StackTraceと呼び出し元の関数の名前(クロスブラウザーではありません)のヒントを提供します。


1
おそらく、@ Gregの回答Function.callerごとに追加する必要があります
Zach Lysobey

Function.caller ただし、厳密モードでは機能しません。
RickardElimää19年

54

「Javascriptで」とおっしゃっていましたが、デバッグが目的であれば、ブラウザの開発者ツールを使用する方が簡単だと思います。これはChromeでの表示です: ここに画像の説明を入力してください スタックを調査する場所にデバッガーをドロップするだけです。


3
これは古い質問です...しかし、これは間違いなく今日これを行うための現代で最も有効な方法です。
markstwie 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 devでは常にnull
Kokizzu

53

完全なスタックトレースを取得できます。

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

発信者がnullいるまで。

注:再帰関数で無限ループが発生します。


2
返信が遅れて申し訳ありませんが、私はあなたのコメントを見たことがありません。再帰の場合のみ機能せず、他の場合は機能するはずです。
ale5000 2016年

45

私は通常使用します (new Error()).stack Chromeでします。これは、呼び出し元が関数を呼び出した場所の行番号も提供するので便利です。欠点は、スタックの長さが10に制限されていることです。そのため、最初にこのページにアクセスしました。

(私はこれを使用して、実行中に低レベルのコンストラクターでコールスタックを収集し、後で表示してデバッグするため、数千回ヒットするため、ブレークポイントを設定しても意味がありません)


あなたが提供する説明についてもう少し説明を追加していただけませんか?
abarisone

6
が設置されている場合、これは私が仕事に就くことができる唯一のもの'use strict';です。必要な情報を提供してくれた-ありがとう!
ジェレミーハリス

4
スタックの長さの制限については、「Error.stackTraceLimit = Infinity」で変更できます。
トム

(新しい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 Panchal

22

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


1
これが最近の正しい答えです。arguments.caller.calleeのようなことはもうできません。他のすべてのものは現在古くなっているので、これをトップに移動させたいと思います。
coblr 2015年

4
厳密モードではこれは不可能のようです?Cannot access caller property of a strict mode function
Zach Lysobey 2015

strictモードでは、Function.callerも機能しませんでした。また、MDNよれば、function.callerは非標準であり、本番環境では使用しないでください。ただし、デバッグには有効です。
jkdev 2015

非標準で問題なく動作した場合はノードで問題はありませんでしたが、厳密モードでは単純に許可されていません(ノード6.10でテストしました)。同じことが「引数」にも当てはまります。エラーメッセージが表示されます: '' 'caller'および 'arguments'は制限された関数プロパティであり、このコンテキストではアクセスできません。」
Tom

21

私はこれを行います:

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

これは素晴らしいです!他の方法が古いので、正しい答えとして受け入れられるべきです\もう機能しません
Yuval Pruss

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

1
そして、関数名だけにHello.caller.nameを使用します
vanval

同じarguments.callee.caller.toString()
user2720864

これは、少なくとも2016年は正解であるはずです
Daniel

この基準トラック上ではなく、ECMAScriptの5のように動作します
Obinna Nwakwue

1
@ダニエル:いいえ、それはすべきではありません。developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…を
Dan Dascalescu


16

これはかなり解決された質問のように見えますが、私は最近、呼び出し先が「ストリクトモード」で許可されていないことを知りました。これは小さなヘルパーライブラリ一部であり、スタンドアロンのコードを使用する場合は、呼び出し元のスタックトレースを返すために使用されるオフセットを変更します(2ではなく1を使用します)。

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アプリでは、これが本当に私が探しているものを与える唯一のアイデアです。
Andrew Grothe 2015年

この関数の呼び出し方法の例を示してください。
pd_au


11

エラースタックをコンソールに記録するだけです。その後、あなたはどのように呼ばれているのかを知ることができます

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

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

sello()


10

ES6モードとStrictモードの両方で、次を使用してCaller関数を取得します

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

呼び出し元または前のスタックがない場合、上記の行は例外をスローすることに注意してください。適宜使用してください。


呼び出し先(現在の関数名)、使用を取得するには: console.log((new Error()).stack.split("\n")[1].trim().split(" ")[1])
VanagaS

10

2018年アップデート

caller厳格モードでは禁止されています。(非標準)スタックを使用した代替方法は次のErrorとおりです

次の関数は、Firefox 52とChrome 61-71で機能するようですが、その実装では2つのブラウザーのログ形式について多くの仮定がなされており、例外をスローして2つの正規表現を実行する可能性があるため、注意して使用する必要があります行われる前のマッチング。

'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は次のように呼び出されました:hi there」を取得しましたが、fooは「hi there」で呼び出されず、ログは「hi there」で呼び出されました
AndrewR

右、エラーメッセージの文法に「誤った修飾子」がありました。これは、「ログが関数fから呼び出され、メッセージXが出力されることを望んでいた」ことを意味しますが、可能な限り簡潔な方法で行います。
Rovanion

7

私はここに私のフィドルを追加したかった:

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

私はこれをクロム、サファリ、IE(10と8)でテストしました。正常に動作します。重要な機能は1つしかないので、大きなフィドルに怖がってしまった場合は、以下をお読みください。

注:このフィドルには、私自身の「定型文」がかなりあります。それをすべて削除し、必要に応じてスプリットを使用できます。これは、私が信頼できるようになった、非常に安全な一連の機能です。

また、そこには「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]);

1
これは何年もの間廃止されてきました。
Dan Dascalescu

5

PhoneGap / Androidではname動作していないように見えることをお知らせください。しかしarguments.callee.caller.toString()、トリックを行います。


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

ここに完全なスタックトレース取得する関数があります:

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て、「main」が「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 2014年


1

この問題を回避するもう1つの方法は、呼び出し元の関数の名前をパラメーターとして渡すだけです。

例えば:

function reformatString(string, callerName) {

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

    return string;
}

これで、次のように関数を呼び出すことができます。

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

    reformatString(myString, function.name);
}

私の例では、関数名のハードコーディングされたチェックを使用していますが、switchステートメントやその他のロジックを使用して、目的の動作を簡単に実行できます。


これにより、ブラウザ間の互換性の問題もほぼ解決されると思います。しかし、それが真であると仮定する前にこれをテストしてください!(発汗し始める
GrayedFox 2016年

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.caller

    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はstrictモードでは機能しません。
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: 'caller'、 'callee'、および 'arguments'プロパティは、厳密モードの関数またはそれらの呼び出しの引数オブ​​ジェクトでアクセスできない場合があります。厳密モードでこれを機能させる方法はありますか?
hard_working_ant 2017年

1

私はこの質問と現在の報奨金の両方にこの質問で対処しようとしています。

バウンティでは、呼び出し元がストリクトモードで取得される必要があります。これが行われたことを確認できる唯一の方法は、ストリクトモードの外部で宣言された関数を参照することです。

たとえば、以下は非標準ですが、以前のバージョン(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();


素晴らしいハックですが、完全に厳密モードになっている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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.