Puis-je obtenir le nom de la fonction en cours d'exécution dans JavaScript?

190

Est-il possible de faire cela:

myfile.js:
function foo() {
    alert(<my-function-name>);
    // pops-up "foo"
    // or even better: "myfile.js : foo"
}

J'ai les frameworks Dojo et jQuery dans ma pile, donc si l'un de ceux-ci facilite la tâche, ils sont disponibles.

sprugman
la source
Est-ce que cela répond à votre question? Obtenir le nom de la fonction actuelle en mode strict
Michael Freidgeim

Réponses:

199

Dans ES5 et au-dessus, il n'y a pas d'accès à ces informations.

Dans les anciennes versions de JS, vous pouvez l'obtenir en utilisant arguments.callee.

Vous devrez peut-être analyser le nom, car il contiendra probablement des fichiers indésirables supplémentaires. Cependant, dans certaines implémentations, vous pouvez simplement obtenir le nom en utilisant arguments.callee.name.

Analyse:

function DisplayMyName() 
{
   var myName = arguments.callee.toString();
   myName = myName.substr('function '.length);
   myName = myName.substr(0, myName.indexOf('('));

   alert(myName);
}

Source: Javascript - obtenir le nom de la fonction actuelle .

Mat
la source
En fait, en accordant plus d'attention à votre question, on dirait que vous voudrez peut-être les déchets supplémentaires :)
Matt
23
@Andrew - Vous avez raison, j'aurais dû le dire. C'était un rapide copier / coller / nettoyer quelque chose que j'avais déjà mis en signet, et un oubli de ma part. Merci de l'ajouter à mon message.
Matt
82
Rupture en mode strict ES5.
Raynos
4
Oh ... c'est pourquoi les gens me battent toujours sur la vitesse de réponse. Je n'avais pas pensé à ça.
Erik Reppen
9
si vous utilisez un objet littéral pour vos méthodes et aucun nom de méthode réel, cela ne fonctionnera pas car arguments.callee agit comme une fonction anonyme qui ne portera aucun nom de fonction. Vous devrez vous assurer d'ajouter ce nom de fonction deux fois. Jetez un œil à cet exemple de jsfiddle : jsfiddle.net/ncays . un autre problème avec cela, cependant, est que ce arguments.calleen'est pas autorisé en mode strict.
hellatan
76

Pour les fonctions non anonymes

function foo()
{ 
    alert(arguments.callee.name)
}

Mais dans le cas d'un gestionnaire d'erreurs, le résultat serait le nom de la fonction de gestionnaire d'erreurs, n'est-ce pas?

fforw
la source
2
Fonctionne très bien dans Chrome. Bien mieux que la réponse acceptée.
B Seven
2
Il convient de garder à l'esprit: eslint.org/docs/rules/no-caller > «obsolète dans les futures versions de JavaScript et leur utilisation est interdite dans ECMAScript 5 en mode strict».
Jeremy le
49

Tout ce dont vous avez besoin est simple. Créer une fonction:

function getFuncName() {
   return getFuncName.caller.name
}

Après cela, chaque fois que vous en avez besoin, vous utilisez simplement:

function foo() { 
  console.log(getFuncName())
}

foo() 
// Logs: "foo"
Igor Ostroumov
la source
3
Merci, c'est beaucoup plus élégant que d'analyser une chaîne.
modle13
1
Cela semble être la meilleure réponse!
Sergey
Parfait. C'est à ce moment que JS n'a pas de constantes natives comme PHP le fait avec les constantes Magic ...
stamster
Chrome me donne une erreur de type car la propriété «nom» n'existe pas sur l'appelant. Cependant, l'inspection a révélé que cela fonctionnait:function getFuncName() { return getFuncName.name }
Tom Anderson le
@TomAnderson avec votre changement, vous obtenez maintenant le nom getFuncNameplutôt que le nom de son appelant.
Mark McKenna
32

Selon MDN

Attention: La 5ème édition d'ECMAScript (ES5) interdit l'utilisation d'arguments.callee () en mode strict. Évitez d'utiliser arguments.callee () en donnant un nom aux expressions de fonction ou en utilisant une déclaration de fonction où une fonction doit s'appeler elle-même.

Comme indiqué, cela ne s'applique que si votre script utilise le "mode strict". C'est principalement pour des raisons de sécurité et malheureusement, il n'y a actuellement aucune alternative pour cela.

Laimis
la source
21

Cela devrait le faire:

var fn = arguments.callee.toString().match(/function\s+([^\s\(]+)/);
alert(fn[1]);

Pour l'appelant, utilisez simplement caller.toString().

Andy E
la source
8
Cela a fonctionné pour moi mais je pense qu'il y a une faute de frappe dans votre expression régulière. J'ai dû supprimer la barre oblique inverse avant le[
declan
4
@declan: oui, vous avez raison. Il est surprenant que personne d'autre n'ait souligné que depuis près de 3 ans, cette réponse a été ici :-)
Andy E
@AndyE probablement personne ne l'a souligné car une fois que nous voyons une expression rationnelle, nous entrons en mode TL; DR et cherchons d'autres réponses;)
BitTickler
11

Cela doit entrer dans la catégorie des "hacks les plus laids du monde", mais c'est parti.

Tout d'abord, l'impression du nom de la fonction actuelle (comme dans les autres réponses) me semble avoir une utilité limitée, car vous savez déjà en quelque sorte quelle est la fonction!

Cependant, trouver le nom de la fonction appelante peut être très utile pour une fonction de trace. C'est avec une expression régulière, mais utiliser indexOf serait environ 3 fois plus rapide:

function getFunctionName() {
    var re = /function (.*?)\(/
    var s = getFunctionName.caller.toString();
    var m = re.exec( s )
    return m[1];
}

function me() {
    console.log( getFunctionName() );
}

me();
James Hugard
la source
solution cool, mais l'appelant de la fonction FYI # n'est pas standard developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
Max Heiber
Connaître le nom de la fonction actuelle peut être essentiel si cette fonction est créée dynamiquement à partir d'une base de données et a besoin d'informations de contexte à l'intérieur de la fonction qui est associée au nom de la fonction.
Paul Chernoch
10

Voici une méthode qui fonctionnera:

export function getFunctionCallerName (){
  // gets the text between whitespace for second part of stacktrace
  return (new Error()).stack.match(/at (\S+)/g)[1].slice(3);
}

Puis dans vos tests:

import { expect } from 'chai';
import { getFunctionCallerName } from '../../../lib/util/functions';

describe('Testing caller name', () => {

    it('should return the name of the function', () => {
      function getThisName(){
        return getFunctionCallerName();
      }

      const functionName = getThisName();

      expect(functionName).to.equal('getThisName');
    });

  it('should work with an anonymous function', () => {


    const anonymousFn = function (){
      return getFunctionCallerName();
    };

    const functionName = anonymousFn();

    expect(functionName).to.equal('anonymousFn');
  });

  it('should work with an anonymous function', () => {
    const fnName = (function (){
      return getFunctionCallerName();
    })();

    expect(/\/util\/functions\.js/.test(fnName)).to.eql(true);
  });

});

Notez que le troisième test ne fonctionnera que si le test est situé dans / util / functions

Anthony
la source
10

La getMyNamefonction dans l'extrait ci-dessous renvoie le nom de la fonction appelante. Il est un hack et repose sur non standard fonction: Error.prototype.stack. Notez que le format de la chaîne retournée par Error.prototype.stackest implémenté différemment dans différents moteurs, donc cela ne fonctionnera probablement pas partout:

function getMyName() {
  var e = new Error('dummy');
  var stack = e.stack
                .split('\n')[2]
                // " at functionName ( ..." => "functionName"
                .replace(/^\s+at\s+(.+?)\s.+/g, '$1' );
                return stack
}

function foo(){
  return getMyName()
}

function bar() {
  return foo()
}

console.log(bar())

A propos d' autres solutions: arguments.callee n'est pas autorisé en mode strict et Function.prototype.callerest non standard et non autorisés en mode strict .

Max Heiber
la source
étendez ceci pour afficher également la position dans la fonction et prendre en charge les fonctions anonymes avec: .replace (/ ^ \ s + at \ s (. +?) (?: \ s. *: |:) (. *?): (. * ?))? $ / g, '$ 1 ($ 2: $ 3)')
kofifus
Function.prototype.caller n'est pas non plus autorisé en mode strict.
fijiaaron
1
Fonctionne parfaitement même pour les fonctions fléchées, réponse sous-estimée
Hao Wu
3

Un autre cas d'utilisation pourrait être un répartiteur d'événements lié à l'exécution:

MyClass = function () {
  this.events = {};

  // Fire up an event (most probably from inside an instance method)
  this.OnFirstRun();

  // Fire up other event (most probably from inside an instance method)
  this.OnLastRun();

}

MyClass.prototype.dispatchEvents = function () {
  var EventStack=this.events[GetFunctionName()], i=EventStack.length-1;

  do EventStack[i]();
  while (i--);
}

MyClass.prototype.setEvent = function (event, callback) {
  this.events[event] = [];
  this.events[event].push(callback);
  this["On"+event] = this.dispatchEvents;
}

MyObject = new MyClass();
MyObject.setEvent ("FirstRun", somecallback);
MyObject.setEvent ("FirstRun", someothercallback);
MyObject.setEvent ("LastRun", yetanothercallback);

L'avantage ici est que le répartiteur peut être facilement réutilisé et n'a pas à recevoir la file d'attente de distribution en tant qu'argument, au lieu de cela, il est implicite avec le nom d'invocation ...

Au final, le cas général présenté ici serait "utiliser le nom de la fonction comme argument pour ne pas avoir à le passer explicitement", et cela pourrait être utile dans de nombreux cas, comme le callback optionnel jquery animate (), ou dans les callbacks timeouts / intervalles, (c'est-à-dire que vous ne passez qu'un NOM de fonction).

Sampioline
la source
3

Le nom de la fonction actuelle et la manière dont elle peut être obtenue semblent avoir changé au cours des 10 dernières années, depuis que cette question a été posée.

Maintenant, n'étant pas un développeur Web professionnel qui connaît toutes les histoires de tous les navigateurs ayant jamais existé, voici comment cela fonctionne pour moi dans un navigateur Chrome 2019:

function callerName() {
    return callerName.caller.name;
}
function foo() {
    let myname = callerName();
    // do something with it...
}

Certaines des autres réponses ont rencontré des erreurs de chrome concernant le code javascript strict et ainsi de suite.

BitTickler
la source
Réponse en double de cette (réponse 3 ans avant le vôtre).
MAChitgarha le
1

Puisque vous avez écrit une fonction nommée fooet que vous savez que c'est dans myfile.jspourquoi avez-vous besoin d'obtenir ces informations dynamiquement?

Cela étant dit, vous pouvez utiliser arguments.callee.toString()à l'intérieur de la fonction (il s'agit d'une représentation sous forme de chaîne de la fonction entière) et regexer la valeur du nom de la fonction.

Voici une fonction qui crachera son propre nom:

function foo() {
    re = /^function\s+([^(]+)/
    alert(re.exec(arguments.callee.toString())[1]);             
}
Andrew Hare
la source
5
Je travaille sur un gestionnaire d'erreurs et je souhaite signaler la fonction d'appel.
sprugman
1

Une combinaison des quelques réponses que j'ai vues ici. (Testé dans FF, Chrome, IE11)

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

function randomFunction(){
    var proof = "This proves that I found the name '" + functionName() + "'";
    alert(proof);
}

L'appel de randomFunction () alertera une chaîne contenant le nom de la fonction.

Démo JS Fiddle: http://jsfiddle.net/mjgqfhbe/

Buddamus
la source
1

Les informations sont réelles sur l'année 2016.


Résultats pour la déclaration de fonction

Résultat dans l'opéra

>>> (function func11 (){
...     console.log(
...         'Function name:',
...         arguments.callee.toString().match(/function\s+([_\w]+)/)[1])
... })();
... 
... (function func12 (){
...     console.log('Function name:', arguments.callee.name)
... })();
Function name:, func11
Function name:, func12

Résultat dans le Chrome

(function func11 (){
    console.log(
        'Function name:',
        arguments.callee.toString().match(/function\s+([_\w]+)/)[1])
})();

(function func12 (){
    console.log('Function name:', arguments.callee.name)
})();
Function name: func11
Function name: func12

Résultat dans le NodeJS

> (function func11 (){
...     console.log(
.....         'Function name:',
.....         arguments.callee.toString().match(/function\s+([_\w]+)/)[1])
... })();
Function name: func11
undefined
> (function func12 (){
...     console.log('Function name:', arguments.callee.name)
... })();
Function name: func12

Ne fonctionne pas dans Firefox. Non testé sur IE et Edge.


Résultats pour les expressions de fonction

Résultat dans le NodeJS

> var func11 = function(){
...     console.log('Function name:', arguments.callee.name)
... }; func11();
Function name: func11

Résultat dans le Chrome

var func11 = function(){
    console.log('Function name:', arguments.callee.name)
}; func11();
Function name: func11

Ne fonctionne pas dans Firefox, Opera. Non testé sur IE et Edge.

Remarques:

  1. La fonction anonyme n'a pas de sens à vérifier.
  2. Environnement de test

~ $ google-chrome --version
Google Chrome 53.0.2785.116           
~ $ opera --version
Opera 12.16 Build 1860 for Linux x86_64.
~ $ firefox --version
Mozilla Firefox 49.0
~ $ node
node    nodejs  
~ $ nodejs --version
v6.8.1
~ $ uname -a
Linux wlysenko-Aspire 3.13.0-37-generic #64-Ubuntu SMP Mon Sep 22 21:28:38 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
PADYMKO
la source
1
(function f() {
    console.log(f.name);  //logs f
})();

Variation typographique:

function f1() {} 
function f2(f:Function) {
   console.log(f.name);
}

f2(f1);  //Logs f1

Remarque uniquement disponible dans les moteurs conformes ES6 / ES2015. Pour en savoir plus, voir

Ole
la source
0

Voici une seule ligne:

    arguments.callee.toString().split('\n')[0].substr('function '.length).replace(/\(.*/, "").replace('\r', '')

Comme ça:

    function logChanges() {
      let whoami = arguments.callee.toString().split('\n')[0].substr('function '.length).replace(/\(.*/, "").replace('\r', '');
      console.log(whoami + ': just getting started.');
    }
VRMBW
la source
0

Voici une variante de la réponse d' Igor Ostroumov :

Si vous souhaitez l'utiliser comme valeur par défaut pour un paramètre, vous devez envisager un appel de second niveau à 'appelant':

function getFunctionsNameThatCalledThisFunction()
{
  return getFunctionsNameThatCalledThisFunction.caller.caller.name;
}

Cela permettrait dynamiquement une implémentation réutilisable dans plusieurs fonctions.

function getFunctionsNameThatCalledThisFunction()
{
  return getFunctionsNameThatCalledThisFunction.caller.caller.name;
}

function bar(myFunctionName = getFunctionsNameThatCalledThisFunction())
{ 
  alert(myFunctionName);
}

// pops-up "foo"
function foo()
{
  bar();
}

function crow()
{
  bar();
}

foo();
crow();

Si vous voulez aussi le nom du fichier, voici cette solution en utilisant la réponse de F-3000 à une autre question:

function getCurrentFileName()
{
  let currentFilePath = document.scripts[document.scripts.length-1].src 
  let fileName = currentFilePath.split('/').pop() // formatted to the OP's preference

  return fileName 
}

function bar(fileName = getCurrentFileName(),  myFunctionName = getFunctionsNameThatCalledThisFunction())
{
  alert(fileName + ' : ' + myFunctionName);
}

// or even better: "myfile.js : foo"
function foo()
{
  bar();
}
SMAG
la source
-1

Essayer:

alert(arguments.callee.toString());
Deniz Dogan
la source
3
Cela renverrait la fonction entière sous forme de chaîne
Andy E
-7

La réponse est courte: alert(arguments.callee.name);

Basilic
la source
12
«nom» est «nom» en français. Ce type de détail change-t-il entre les versions linguistiques des navigateurs? Je ne pense pas.
argyle