JavaScript で呼び出し元関数を見つけるにはどうすればよいですか?
-
07-07-2019 - |
質問
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に面している本番サイトでは使用しないでください。すべてのユーザーに対して機能するとは限りません。また、実装間に大きな非互換性が存在する可能性があり、今後動作が変更される可能性があります。
以下は、最新のJavascriptでサポートされなくなった2008年の古い回答です。
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
そしてそれを匿名関数として扱います。Chrome ですら使用できません。Klass
名前を言わないなら名前をkls
機能に。ちなみに、関数 printStackTrace にオプションを渡すことができます。
{guess: true}
しかし、それを行っても実際の改善は見られませんでした。すべてのブラウザが同じ情報を提供するわけではありません。つまり、パラメータ、コード列などです。
呼び出し側関数名
ちなみに、呼び出し元関数の名前だけが必要な場合 (IE ではなく、ほとんどのブラウザーで)、次のように使用できます。
arguments.callee.caller.name
ただし、この名前は次の名前になることに注意してください。 function
キーワード。関数全体のコードを取得せずに、それ以上のものを取得する方法は (Google Chrome でも) 見つかりませんでした。
呼び出し側関数コード
残りの最良の回答を要約します (Pablo Cabrera、nourdine、Greg Hewgill による)。 クロスブラウザで本当に安全に使用できるのは次のとおりです。
arguments.callee.caller.toString();
どれが表示されますか コード 呼び出し側関数の。残念ながら、それだけでは十分ではありません。だからこそ、StackTrace と呼び出し元関数 Name に関するヒントを提供します (ただし、これらはクロスブラウザーではありません)。
要約(および明確にする)するには...
このコード:
function Hello() {
alert("caller is " + arguments.callee.caller.toString());
}
これと同等:
function Hello() {
alert("caller is " + Hello.caller.toString());
}
関数の名前は<!> quot; Hello <!> quot;から変更できるので、明らかに最初のビットの方が移植性があります。 <!> quot; Ciao <!> quot;に変更しても、すべてが機能するようになります。
後者では、呼び出された関数(Hello)の名前をリファクタリングすることにした場合、すべての発生を変更する必要があります:(
完全なスタックトレースを取得できます:
arguments.callee.caller
arguments.callee.caller.caller
arguments.callee.caller.caller.caller
呼び出し元がnull
になるまで。
注:再帰関数で無限ループが発生します。
<!> quot; Javascript <!> quot;で言及したことは知っていますが、目的がデバッグである場合は、ブラウザの開発者ツールを使用する方が簡単だと思います。これはChromeでどのように見えるかです: スタックを調査する場所にデバッガをドロップします。
通常、Chromeでは(new Error()).stack
を使用します。
良い点は、これにより、呼び出し元が関数を呼び出した行番号も提供されることです。欠点は、スタックの長さが10に制限されることです。このため、最初にこのページにアクセスしました。
(実行中に低レベルのコンストラクターでコールスタックを収集し、後で表示およびデバッグするためにこれを使用しているため、ブレークポイントを設定することは何千回もヒットするため使用しません)
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
を使用する方が安全です これは非常に解決された質問ですが、最近呼び出し先は「厳格モード」では許可されないので、自分の使用のために、呼び出し先からパスを取得するクラスを作成しました。 小さなヘルパーライブラリの一部であり、スタンドアロンでコードを使用する場合は、呼び出し元のスタックトレースを返すために使用されるオフセット(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 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/
これがクローム、サファリ、およびIE(10および8)であることをテストしました。正常に動作します。重要な機能は1つしかないため、大きなバイオリンに怖がる場合は、以下をお読みください。
注: 私自身のかなりの量の<!> quot; boilerplate <!> quot;このフィドルで。必要に応じてそれらをすべて削除し、分割を使用できます。これは非常に安全な<!>引用です。私が頼るようになった関数のセット。
<!> quot; JSFiddle <!> quot;もあります。そこにあるテンプレートを使って、多くのフィドルで簡単にいじることができます。
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();
}());
コードではなく関数名のみが必要で、ブラウザに依存しないソリューションが必要な場合は、次を使用します。
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()
がトリックを行います。
ここでは、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>
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;を決定することは可能です。 <!> quot; Hello <!> quotの呼び出し元です。 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ステートメントまたは他のロジックを使用して、必要な処理を簡単に行うことができます。
私が知る限り、このような特定のソースからのこれには2つの方法があります-
-
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)]
この質問で質問と現在の報奨金の両方に対処しようとしています。
報奨金は、発信者が strict モードで取得されることを要求します。これを確認できる唯一の方法は、strictモードの outside と宣言された関数を参照することです。
たとえば、以下は非標準ですが、以前のバージョン(2016年3月3日)および現在のバージョン(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
以前の答えは私が探していたもののように機能しないため(文字列またはコールスタックとしての関数ではなく、最後の関数呼び出し元を取得する)
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();