web-dev-qa-db-ja.com

JavaScriptの呼び出し元関数はどのようにしてわかりますか?

function main()
{
   Hello();
}

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

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

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

この機能は 非標準 で、 Function.caller からです。

非標準
この機能は標準的なものではなく、標準化されていません。 Webに面している本番サイトでは使用しないでください。すべてのユーザーに対して機能するわけではありません。実装間には大きな非互換性があり、その振る舞いは将来変更されるかもしれません。


以下は、現代のJavascriptではサポートされなくなった2008年の古い回答です。

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

スタックトレース

ブラウザ固有のコードを使用してスタックトレース全体を見つけることができます。良いことは 誰かがすでにそれを作った です。これが GitHubのプロジェクトコード です。

しかし、すべてのニュースが良いわけではありません。

  1. スタックトレースを取得するのは本当に遅いので注意してください(詳しくは this を読んでください)。

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

これは呼び出し側関数の code を表示します。残念ながら、それだけでは十分ではありません。そのため、StackTraceと呼び出し元の関数Nameについてのヒントを紹介します(これらはクロスブラウザーではありません)。

146
Mariano Desanze

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

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

呼び出し元がnullになるまで。

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

50
ale5000

要約すると(そして明確にするために)...

このコード:

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

これと同等です:

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

あなたが関数の名前を変更することができるので、明らかに最初のビットはより移植性があります、「こんにちは」から「Ciao」に言って、そしてまだ全体のことを働かせることができます。

後者の場合、呼び出された関数の名前(Hello)をリファクタリングすることにした場合は、その出現箇所すべてを変更する必要があります。

49
nourdine

私はあなたが「Javascriptで」と言ったことを知っています、しかし目的がデバッグであるならば、私はあなたのブラウザの開発者ツールを使うほうが簡単であると思います。これはChromeでどのように見えるかです: enter image description here スタックを調査したい場所にデバッガをドロップするだけです。

42
Phil

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

(実行中に低レベルのコンストラクタでコールスタックを収集し、後で表示してデバッグするために使用しています。ブレークポイントの設定は何千回もヒットするので役に立ちません。)

34
heystewart

あなたがそれをIE <11の中で実行するつもりでないなら、 console.trace() はそれに合うでしょう。

function main() {
    Hello();
}

function Hello() {
    console.trace()
}

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

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

21
Greg
function Hello() {
    alert(Hello.caller);
}
18
Shadow2531

*arguments.callee.caller 廃止予定 - ...なので、arguments.callerを使用する方が安全です。

18
Pablo Cabrera

これはかなり解決された質問のように見えますが、私は最近 calleeが 'strictモード'では許可されていないことを知りました 私自身の使用のためにそれが呼ばれる場所からパスを取得するクラスを書きました。それは 小さなヘルパlibの一部です そして、スタンドアロンのコードを使用したい場合は、呼び出し側のスタックトレースを返すために使用されるオフセットを変更してください(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('.');
  };
}
16
QueueHammer

私はこれをするだろう:

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

これにアクセスしてみてください。

arguments.callee.caller.name
11
user15566

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

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

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

sello()
10
Prasanna

私はこのためにここに私のフィドルを加えたいと思いました:

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

私はこれをテストしましたクロム、サファリそしてIE(10と8)。正常に動作します。重要な関数は1つしかありません。そのため、大きな問題がある場合は、以下をお読みください。

注:このフィドルには、かなりの量の私自身の「定型句」があります。あなたはそれをすべて削除して、望むならsplit'sを使うことができます。それは私が頼りにするようになった単なる超安全な機能のセットです。

そこには "JSFiddle"テンプレートもあります。これは、簡単に素早く操作するために多くの操作に使用します。

7
bladnman

コードではなく関数名だけが必要で、ブラウザに依存しないソリューションが必要な場合は、次のようにします。

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

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 with:', ...messages);
    } else {
      console.log(fnName(logLines[2]), 'called with:', ...messages);
    }
  } else {
    console.log(...messages);
  }
}

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

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

PhoneGap/Android nameではうまくいっていないようです。しかしarguments.callee.caller.toString()がうまくいくでしょう。

5
Pablo Armentano

これは フルスタックトレースを取得するための関数です

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

ここでは、RegExを使って、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>
4
BrazFlat

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オブジェクトのErrorを使用して、「main」が「Hello」の呼び出し元であると判断することは可能です。 callee/callerアプローチがうまくいかない場合でも、実際にはうまくいきます。コンテキスト、つまりソースファイルと行番号も表示されます。ただし、ソリューションをクロスプラットフォームにするには努力が必要です。

3
Stephen Quan

なぜ上記の解決法のすべてがロケット科学のように見えるのか。その間、それはこの断片より複雑であるべきではありません。この男へのすべてのクレジット

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

私の知る限りでは、我々はこのような与えられたソースからこれのための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;
        }
    }
    

あなたの答えがあると思います:)。

1
Abrar Jahin

次のコードを試してください。

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で働きました。

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

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

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

1
VanagaS

私はこの質問で質問と現在の恵みの両方に取り組もうとしています。

この賞金には、呼び出し側が strict modeで取得されている必要があります。これを確認できる唯一の方法は、 outside strictモードで宣言された関数を参照することです。

たとえば、次のものは非標準ですが、以前の(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();
1
autistic

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

例えば:

function reformatString(string, callerName) {

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

    return string;
}

今、あなたはこのように関数を呼び出すことができます:

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

    reformatString(myString, function.name);
}

私の例では、関数名のハードコードされたチェックを使用していますが、あなたはそこでやりたいことをするために簡単にswitchステートメントや他のロジックを使うことができます。

1
GrayedFox

もしあなたが本当に何らかの理由で機能を必要としていて、それがクロスブラウザ互換であり、厳密なことを心配せずに前方互換であることを望むならば、この参照を渡しなさい:

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
Mario PG

これまでの答えはどれも私が探していたものと同じようには機能しないので(文字列やコールスタックとしての関数ではなく、最後の関数呼び出し元だけを取得する)。

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

私は以下のコードが役に立つかもしれないと思います:

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
0
JiarongWu