その関数内から関数名を取得するにはどうすればよいですか?


263

関数内から関数名にアクセスするにはどうすればよいですか?

// parasitic inheritance
var ns.parent.child = function() {
  var parent = new ns.parent();
  parent.newFunc = function() {

  }
  return parent;
}

var ns.parent = function() {
  // at this point, i want to know who the child is that called the parent
  // ie
}

var obj = new ns.parent.child();

それから、親では、ns [child] [schema]やns [child] [dbService]などの規則によって他の関数にアクセスできます。それがなければ、すべての子クラスでこれらの参照をハードコーディングする必要があります。
スコットクラレンバッハ2010

子関数を引数として親に渡さないのはなぜですか?var parent = new ns.parent(this);
プロダー2010

何十ものそのようなルックアップと何十もの子供がいるからです。それが現在私がやっていることですが、毎回同じで、派生した関数に基づいて、重複するロジックを親の中に一度だけ配置できれば完璧です。
スコットクラレンバッハ2010

参照、それは私が欲しい子関数ではなく、使用されている命名規則です。その命名規則は、現在子オブジェクトで定義されていないが、システム全体でその子に関連している他の関数をロードするために使用できるためです。
スコットクラレンバッハ2010

1
@スコット私は他のすべての人に同意します。あなたがあなたの複雑さとコード構造を管理することは、これを行う必要があるのは間違っています。この種のハードカップリングは設計上の決定として不適切であり、混乱を招きます。@SimeVidasあなたの素晴らしいネクロマンサー:)
レイノス

回答:


163

ES5では、最善の方法は次のとおりです。

function functionName(fun) {
  var ret = fun.toString();
  ret = ret.substr('function '.length);
  ret = ret.substr(0, ret.indexOf('('));
  return ret;
}

使用Function.callerは非標準です。Function.callerarguments.callee厳格モードではどちらも禁止されています。

編集:以下のnusの正規表現ベースの答えは同じことを実現しますが、パフォーマンスが向上します!

ES6では、そのまま使用できますmyFunction.name

注:一部のJS縮小機能は、より適切に圧縮するために関数名を破棄する場合があることに注意してください。これを回避するには、設定を微調整する必要がある場合があります。


これは(無名関数のために)私にとっては十分な答えにはなりませんでしたが、これを行う考えが浮かびました。fun.toString().substr(0, 100)私のニーズでは、問題の関数を見つけるのに十分です。だから、インスピレーションをありがとう!
Campbeln 2015

3
はいmyFunction.name、ES6で実行するのが最善です。
Vlad A Ionescu 2015年

15
myFunction.name関数名を入力するとどうなるのですか?マジック変数の目的を無効にします。
adi518

2
@ adi518:必ずしもそうではない..関数の配列があり、それを反復処理し、for / foreach ..thenを使用するとします。その後、arr[index].name:)
Debasish Chowdhury

2
@ adi518それは役に立たないわけではありません。IDE内で関数の名前を変更している場合myFunction.nameも更新されます。確かに、intelliJは文字列内の識別子の名前変更をサポートしていますが、これは一貫性が大幅に低下し、誰かが「文字列内の検索」オプションにチェックを付け忘れた場合は黙って古くなります。myFunction.name文字列をハードコーディングするよりも少し良い選択です。
byxor

141

ES6(以下のセンディハリムの回答に触発された):

myFunction.name

MDNの説明。2015年現在、nodejsとIEを除くすべての主要ブラウザーで動作します。

注:バインドされた関数では、これは " bound <originalName>"になります。元の名前を取得したい場合は、「バインド済み」を取り除く必要があります。


ES5(ヴラッドの答えに触発された):

関数への参照がある場合、次のことができます。

function functionName( func )
{
    // Match:
    // - ^          the beginning of the string
    // - function   the word 'function'
    // - \s+        at least some white space
    // - ([\w\$]+)  capture one or more valid JavaScript identifier characters
    // - \s*        optionally followed by white space (in theory there won't be any here,
    //              so if performance is an issue this can be omitted[1]
    // - \(         followed by an opening brace
    //
    var result = /^function\s+([\w\$]+)\s*\(/.exec( func.toString() )

    return  result  ?  result[ 1 ]  :  '' // for an anonymous function there won't be a match
}
  • 私はこれについて単体テストを実行していない、または実装の違いを確認していませんが、コメントを残さない場合でも、原則的には機能するはずです。
  • 注:バインドされた関数では機能しません
  • 注:callerおよびcalleeは非推奨と見なされます。

[1] これは合法であり、多くの場合、十分な構文強調表示ツールが関数名と括弧の間の空白を考慮できないため、ここに含めます。一方、ここに空白を含める.toString()の実装については知りません。そのため、省略できます。


元の質問への回答として、私は寄生継承を削除し、いくつかのより伝統的なOOP設計パターンを採用します。私はTidBits.OoJsを書いて、C ++を模倣した機能セットを備えたJavaScriptでOOPコードを快適に書きました(まだ完全ではありませんが、ほとんどの場合)。

コメントから、parent必要な情報をコンストラクタに渡さないようにしたいと思います。従来のデザインパターンでは、依存関係を明確にして強制することは一般的には良いことだと考えられているので、そのパターンからそれを救うことはできません。

また、無名関数から離れることをお勧めします。すべてが「無名関数」として表示されるだけで、PITAのデバッグとプロファイリングのみを行い、私が認識している利点はありません。


3
素敵なRegEx!以下は、多くの場合、コードが最速であることを示すパフォーマンステストです。一般に、RegExはネイティブのJSを平均して約2倍の速度で上回っています。jsperf.com/get-function-name/2
Beejor

@Tomaszこんにちは、それを指摘してくれてありがとう。知らなかった。バインドされた関数は、実際には元の関数をラップする新しい関数です。ecmascript標準§19.2.3.2は、新しい関数の名前が「バインドされた」+ originalNameであることを定義しています。toStringメソッドもバインドされた関数では機能しません...バインドされた単語を取り除く必要があります。

関数名を入力した場合のmyFunction.nameの意味は何ですか?マジック変数の目的を無効にします。
ボルジョフスキー2018

React Nativeを使用している場合、これは正しく動作しない可能性があることに注意してください。expoバージョン36で作業しているRNプロジェクトがあり、コンポーネントメソッドの.nameプロパティが、それが何と呼ばれても文字列「value」として表示されていました。奇妙なことに、expoアプリでのテストは問題ありませんでしたが、実際のアプリケーションにコンパイルすると、サイレントクラッシュしました。
アンディコーマン

64

名前を付けていない関数を変数に代入しています。おそらく、代わりに名前付き関数式が必要です(http://kangax.github.com/nfe/)。

var x = function x() {
    console.log( arguments.callee.name );
}
x();

しかし、それがどれだけのクロスブラウザかはわかりません。IE6には、関数名が外部スコープにリークする問題があります。また、arguments.calleeは非推奨になり、を使用してstrict modeいる場合はエラーになります。


1
これは古いバージョンのSafariブラウザでは機能しません。
Anubhav Gupta

17

Any は、関数名でconstructorあるpropertyを公開しますnameconstructorインスタンスにアクセスするには(を使用new)またはprototype

function Person() {
  console.log(this.constructor.name); //Person
}

var p = new Person();
console.log(p.constructor.name); //Person

console.log(Person.prototype.constructor.name);  //Person

6
最初のconsole.log通話記録はwindowObject実行場所によって異なりPersonます。
Bart S

「new」を使用してインスタンス化してもよろしいですか?それ以外の場合は機能しません。
ローランド2017

14

それは私が私の人生で書いた最も愚かなもののように見えますが、それは面白いです:D

function getName(d){
  const error = new Error();
  const firefoxMatch = (error.stack.split('\n')[0 + d].match(/^.*(?=@)/) || [])[0];
  const chromeMatch = ((((error.stack.split('at ') || [])[1 + d] || '').match(/(^|\.| <| )(.*[^(<])( \()/) || [])[2] || '').split('.').pop();
  const safariMatch = error.stack.split('\n')[0 + d];

  // firefoxMatch ? console.log('firefoxMatch', firefoxMatch) : void 0;
  // chromeMatch ? console.log('chromeMatch', chromeMatch) : void 0;
  // safariMatch ? console.log('safariMatch', safariMatch) : void 0;

  return firefoxMatch || chromeMatch || safariMatch;
}

d-スタックの深さ。0-この関数名を返します1-親など;
[0 + d]-ただ理解するために-何が起こるか;
firefoxMatch-サファリで動作しますが、Macの所有者が喫煙後に戻ってきて、私を追い払ったので、私には実際にテストする時間が少しありました: '(

テスト:

function limbo(){
  for(let i = 0; i < 4; i++){
    console.log(getName(i));
  }
}
function lust(){
  limbo();
}
function gluttony(){
  lust();
}

gluttony();

結果:
Chrome:
ここに画像の説明を入力してください

Fitefox:
ここに画像の説明を入力してください

このソリューションは、楽しみのためだけに作成されました!実際のプロジェクトには使用しないでください。ESの仕様には依存せず、ブラウザの実現にのみ依存します。次のchrome / firefox / safariの更新後、壊れる可能性があります。
それ以上の場合、エラー(ha)処理はありません- dスタックの長さを超える場合-エラーが発生します。
他のWrowsersエラーのメッセージパターンの場合-エラーが発生します。
ES6クラス(.split('.').pop())で機能する必要がありますが、エラーが発生する可能性があります。


13

これはあなたのために働くかもしれません:

function foo() { bar(); }

function bar() { console.log(bar.caller.name); }

foo()を実行すると、無名関数から呼び出した場合、 "foo"またはundefinedが出力されます。

コンストラクターでも機能します。その場合、呼び出し元のコンストラクターの名前(たとえば、「Foo」)が出力されます。

詳細はこちら:https : //developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/Caller

彼らはそれが非標準であると主張しますが、それはまたすべての主要なブラウザーでサポートされていると述べています:Firefox、Safari、Chrome、OperaそしてIE。


厳密モードでは使用できません... mmm
Ka Mok

8

できません。関数には標準に従って名前がありません(ただし、mozillaにはそのような属性があります)。これらの関数は、名前を持つ変数にのみ割り当てることができます。

またあなたのコメント:

// access fully qualified name (ie "my.namespace.myFunc")

関数my.namespace.myFunc.getFn内にあります

あなたができることは、newによって作成されたオブジェクトのコンストラクタを返すことです

だからあなたは言うことができます

var obj = new my.namespace.myFunc();
console.info(obj.constructor); //my.namespace.myFunc

1
私はそれを継承チェーンに渡していて、簡潔にするためにそれらを省略しているので、私はfuncの中にそれを必要とします。
スコットクラレンバッハ2010

1
「これ」は、関数を呼び出したインスタンスになります。したがって、親関数内で「this」を呼び出すと、それを呼び出した関数のインスタンスが表示されます。必要なのはそれだけです-名前がなぜ必要なのかわからない
plodder

1
それから、親では、ns [child] [schema]やns [child] [dbService]などの規則によって他の関数にアクセスできます。それがなければ、すべての子クラスでこれらの参照をハードコーディングする必要があります。
スコットクラレンバッハ2010

1
名前を見つけるための私のユースケースは、スタックダンプに戻さなくても、メッセージの送信元を示すconsole.errorメッセージを簡単に作成できるようにすることです。例:console.error({'error':{self.name reference} + "error with input parameter")。複数の関数で繰り返されるエラーメッセージを、その中のテキストを毎回停止して変更する必要がない場合は、切り取り/貼り付けがはるかに簡単になります。私の場合、複数のpromise呼び出しからpromiseエラーを返しています。どのpromise / functionがエラーを生成したかを知っておくと便利です。
スコット

7

Error.stackをサポートするブラウザーでは、これを使用できます(おそらくほとんどすべてではありません)

function WriteSomeShitOut(){ 
  var a = new Error().stack.match(/at (.*?) /);
  console.log(a[1]);
} 
WriteSomeShitOut();

もちろん、これは現在の関数に対するものですが、あなたはアイデアを理解します。

あなたがコーディングしながらよだれを垂らして幸せ


4

name無名関数を使用している場合を除き、プロパティを使用して関数名を取得できます

例えば:

var Person = function Person () {
  this.someMethod = function () {};
};

Person.prototype.getSomeMethodName = function () {
  return this.someMethod.name;
};

var p = new Person();
// will return "", because someMethod is assigned with anonymous function
console.log(p.getSomeMethodName());

名前付き関数で試してみましょう

var Person = function Person () {
  this.someMethod = function someMethod() {};
};

今、あなたは使うことができます

// will return "someMethod"
p.getSomeMethodName()

4

次のようなコンストラクタ名を使用できます。

{your_function}.prototype.constructor.name

このコードは、単にメソッドの名前を返します。


3

Function.nameメソッドECMAScript 6を使用できる一部として

function doSomething() {}

alert(doSomething.name); // alerts "doSomething"

ReactJSでは、これを追加する必要があります:console.log(this.doSomething.name);
Bitclaw 2017

2
それは関数の外です
スコット

3

あなたが使うことができますFunction.name

JavaScriptのほとんどの実装では、コンストラクターの参照がスコープ内にあると、その文字列名をnameプロパティ(たとえば、Function.name、またはObject.constructor.name)から取得できます。

あなたが使うことができますFunction.callee

ネイティブarguments.callerメソッドは廃止されましたが、ほとんどのブラウザーはをサポートしておりFunction.caller、実際の呼び出しオブジェクト(コード本体)を返します:https : //developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ Function / caller?redirectlocale = en-US&redirectslug = JavaScript%2FReference%2FGlobal_Objects%2FFunction%2Fcaller

ソースマップを作成できます

オブジェクト自体ではなく、リテラル関数シグネチャ(その「名前」)が必要な場合は、必要なAPI文字列値の配列参照を作成するなど、もう少しカスタマイズしたものに頼る必要があるかもしれません。頻繁にアクセスします。Object.keys()文字列の配列を使用してそれらを一緒にマッピングするか、GitHubでMozillaのソースマップライブラリを調べて、より大きなプロジェクトを作成できます:https : //github.com/mozilla/source-map


2

私はこれが古い質問であることを知っていますが、最近、デバッグのためにいくつかのReactコンポーネントのメソッドを装飾しようとしているときに、同様の問題に直面しています。人々がすでに言ったように、arguments.callerそしてarguments.callee厳密なモードでは禁止されています。これはおそらくReactトランスパイリングでデフォルトで有効になっています。あなたはそれを無効にすることができます、または私は別のハックを思い付くことができました、なぜならReactではすべてのクラス関数が命名されているので、実際にこれを行うことができます:

Component.prototype.componentWillMount = function componentWillMount() {
    console.log('Callee name: ', this.__proto__.constructor.toString().substr(0,30));
...
}

1

これでうまくいきました。

function AbstractDomainClass() {
    this.className = function() {
        if (!this.$className) {
            var className = this.constructor.toString();
            className = className.substr('function '.length);
            className = className.substr(0, className.indexOf('('));
            this.$className = className;
        }
        return this.$className;
    }
}

テストコード:

var obj = new AbstractDomainClass();
expect(obj.className()).toBe('AbstractDomainClass');

1

同様の問題があり、次のように解決しました:

Function.prototype.myname = function() {
   return this.toString()
       .substr( 0, this.toString().indexOf( "(" ) )
       .replace( "function ", "" ); 
}

このコードは、より快適な方法で、このディスカッションの冒頭ですでに読んだ1つの応答を実装しています。これで、任意の関数オブジェクトの名前を取得するメンバー関数ができました。ここに完全なスクリプトがあります...

<script language="javascript" TYPE="text/javascript">

    Function.prototype.myname = function() { 
        return this.toString()
            .substr( 0, this.toString().indexOf( "(" ) )
            .replace("function ", "" ); 
    }
    function call_this( _fn ) { document.write( _fn.myname() ); }
    function _yeaaahhh() { /* do something */ }
    call_this( _yeaaahhh ); 

</script>


0

これは、ES5、ES6、すべてのブラウザー、および厳格モード機能で機能します。

これが名前付き関数でどのように見えるかです。

(function myName() {
  console.log(new Error().stack.split(/\r\n|\r|\n/g)[1].trim());
})();
at myName (<anonymous>:2:15)

これが無名関数でどのように見えるかです。

(() => {
  console.log(new Error().stack.split(/\r\n|\r|\n/g)[1].trim());
})();
at <anonymous>:2:15

これにより、ブラウザごとに異なる結果が生成されます。Chrome:at myName (<anonymous>:2:15)Firefox:@debugger eval code:3:3
jkdev

(関数myName(){console.log(new Error()。stack.split(/ \ r \ n | \ r | \ n / g)[1] .trim()。split( "")[1]) ;})();
Zibri

-2

ここを見てください:http : //www.tek-tips.com/viewthread.cfm?qid=1209619

arguments.callee.toString();

あなたのニーズに合っているようです。


3
arguments.callee.toString()は、関数のソースを返すだけです。
スコットクラレンバッハ2010

2
許可されない:ストリクトモード関数またはそれらを呼び出すための引数オブ​​ジェクトでは、「caller」、「callee」、および「arguments」プロパティにアクセスできません
Eric Hodonsky


-3

実行中の関数内から関数名を取得する簡単な方法。

function x(){alert(this.name)};x()


1
GUIDを教えてくれ
Tamir Daniely

1
this基本的に何でもかまいませんのでご注意ください。試してみる(function(){console.log(this.name);}).bind({name: "put basically anything here even an object maybe a 1TB string maybe a class definition"})()
ヴァレン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.