web-dev-qa-db-fra.com

Comment obtenir le nom de la fonction depuis cette fonction?

Comment puis-je accéder à un nom de fonction à partir de cette fonction?

// 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();
233
Scott Klarenbach

Dans ES5, la meilleure chose à faire est de:

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

L'utilisation de Function.caller n'est pas standard. Function.caller et arguments.callee sont tous deux interdits en mode strict.

Edit: la réponse basée sur regex ci-dessous de Nus réalise la même chose, mais a de meilleures performances!

Dans ES6, vous pouvez simplement utiliser myFunction.name.

Remarque: Attention, certains minificateurs JS risquent d’éliminer les noms de fonctions pour mieux compresser. vous devrez peut-être modifier leurs paramètres pour éviter cela.

154
Vlad A Ionescu

ES6 (inspiré par la réponse de sendy halim ci-dessous):

myFunction.name

Explication sur MDN . À partir de 2015, fonctionne dans nodejs et dans tous les principaux navigateurs sauf IE.

Remarque: Sur les fonctions liées, cela donnera "bound <originalName>". Vous devrez supprimer la "reliure" si vous souhaitez obtenir le nom d'origine.


ES5 (inspiré de la réponse de Vlad):

Si vous avez une référence à la fonction, vous pouvez faire:

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
}
  • Je n'ai pas exécuté de tests unitaires sur ce point, ni vérifié les différences de mise en œuvre, mais en principe cela devrait fonctionner, sinon laisser un commentaire.
  • Note: ne fonctionnera pas sur les fonctions liées
  • Remarque: caller et callee sont considérés comme obsolètes.

[1] Je l'inclue ici car c'est légal et souvent assez d'outils de coloration syntaxique ne tiennent pas compte de l'espace blanc entre le nom de la fonction et la parenthèse. D'autre part, je ne suis au courant d'aucune implémentation de .toString () qui inclura un espace blanc ici, c'est pourquoi vous pouvez l'omettre.


En réponse à la question initiale, je laisserais tomber l'héritage parasite et opterais pour des modèles de conception plus traditionnels OOP. J'ai écrit un TidBits.OoJs pour écrire confortablement le code OOP en JavaScript avec un ensemble de fonctionnalités imitant le langage C++ (pas encore complet, mais surtout).

Je vois dans les commentaires que vous souhaitez éviter de transmettre des informations dont parent a besoin à son constructeur. Je dois admettre que les modèles de conception traditionnels ne vous épargneront pas celui-ci, car il est généralement considéré comme une bonne chose de rendre vos dépendances évidentes et appliquées.

Je suggérerais également de s'éloigner des fonctions anonymes. Ils ne font que déboguer et profiler un PITA parce que tout s’affiche en tant que "fonction anonyme" et qu’il n’ya aucun avantage pour eux, à ma connaissance.

124
user1115652

vous assignez une fonction sans nom à une variable. vous avez probablement besoin d’expression de fonction nommée à la place ( http://kangax.github.com/nfe/ ).

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

cependant, je ne suis pas sûr de la quantité de navigation croisée qui existe. Il y a un problème avec IE6 qui fait que le nom de votre fonction fuit dans l'étendue externe. de plus, arguments.callee est en quelque sorte obsolète et entraînera une erreur si vous utilisez strict mode.

54
wildcard

N'importe quel constructor expose une propriété name, qui est le nom de la fonction. Vous accédez à la constructor via une instance (en utilisant new) ou une 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
17
roland

Cela pourrait fonctionner pour vous:

function foo() { bar(); }

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

lancer foo () affichera "foo" ou undefined si vous appelez depuis une fonction anonyme.

Il fonctionne également avec les constructeurs, auquel cas il afficherait le nom du constructeur appelant (par exemple, "Foo").

Plus d'infos ici: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/Caller

Ils prétendent que ce n'est pas standard, mais aussi qu'il est supporté par tous les principaux navigateurs: Firefox, Safari, Chrome, Opera et IE.

14
Jacob Mouka

Cela ressemble à la chose la plus stupide, celle que j’ai écrite dans ma vie, mais c’est drôle: 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 - profondeur de la pile. 0 - renvoie le nom de cette fonction, 1 - parent, etc.
[0 + d] - juste pour comprendre - ce qui se passe;
firefoxMatch - fonctionne pour le safari, mais j'avais vraiment un peu de temps pour faire des tests, car le propriétaire de mac était revenu après avoir fumé et m'avait chassé: '(

Essai:

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

gluttony();

Résultat:
Chrome:
enter image description here

Fitefox:
enter image description here

Cette solution ne créait que juste pour le fun ! Ne l'utilisez pas pour de vrais projets. Cela ne dépend pas de la spécification ES, cela dépend uniquement de la réalisation du navigateur. Après la prochaine mise à jour de chrome/firefox/safari, il est peut-être cassé.
Plus que cela, il n'y a pas d'erreur (ha) en cours de traitement - si d sera plus que la longueur de la pile - vous obtiendrez une erreur;
Pour le modèle de message d'erreur des autres rédacteurs - vous obtiendrez une erreur;
Cela doit fonctionner pour les classes ES6 (.split('.').pop()), mais vous pouvez toujours obtenir un erorr;

9
user3335966

Tu ne peux pas. Les fonctions n'ont pas de noms conformes à la norme (bien que mozilla ait un tel attribut) - elles ne peuvent être attribuées qu'à des variables portant des noms.

Aussi votre commentaire:

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

se trouve dans la fonction my.namespace.myFunc.getFn

Ce que vous pouvez faire est de retourner le constructeur d’un objet créé par new

Alors tu pourrais dire

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

Vous pouvez utiliser la propriété name pour obtenir le nom de la fonction, sauf si vous utilisez une fonction anonyme.

Par exemple:

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

essayons maintenant avec la fonction nommée

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

maintenant tu peux utiliser

// will return "someMethod"
p.getSomeMethodName()
4
sendy halim

Vous pouvez utiliser Function.name:

Dans la plupart des implémentations de JavaScript, une fois que vous avez la référence de votre constructeur dans l'étendue, vous pouvez obtenir son nom de chaîne à partir de sa propriété name (par exemple, Function.name ou Object.constructor.name.

Vous pouvez utiliser Function.callee:

La méthode native arguments.caller est obsolète, mais la plupart des navigateurs prennent en charge Function.caller, qui renverra l’objet appelant (son corps de code): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/Caller?redirectlocale=en-US&redirectlug=JavaScript%2FReference%2FGlobal_Objects%2FFunction%2Fcaller

Vous pouvez créer une carte source :

Si vous avez besoin de la signature de fonction littérale (le "nom" de celle-ci) et non de l'objet lui-même, vous devrez peut-être recourir à quelque chose d'un peu plus personnalisé, comme créer une référence à un tableau des valeurs de chaîne d'API dont vous aurez besoin. accès fréquemment. Vous pouvez les mapper ensemble en utilisant Object.keys() et votre tableau de chaînes, ou consulter la bibliothèque de cartes source de Mozilla sur GitHub, pour des projets plus importants: https://github.com/ mozilla/source-map

3
Benny

dans le cadre de ECMAScript 6, vous pouvez utiliser la méthode Function.name

function doSomething() {}

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

Vous pouvez utiliser le nom du constructeur comme:

{your_function}.prototype.constructor.name

ce code retourne simplement le nom d'une méthode.

3
muhamad zolfaghari

Vous pouvez l'utiliser, mais ce n'est pas pour tous les navigateurs, seulement ceux qui supportent Error.stack

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

Bien sûr, cela s’applique à la fonction actuelle, mais vous voyez l’idée.

À votre santé!

3
no_ripcord

Je sais que c’est une vieille question, mais dernièrement, j’ai rencontré un problème similaire en essayant de décorer certaines méthodes de React Component, à des fins de débogage. Comme on l'a déjà dit, arguments.caller et arguments.callee sont interdits en mode strict, ce qui est probablement activé par défaut dans votre React transpiling. Vous pouvez le désactiver, ou j'ai pu créer un autre hack, car dans React toutes les fonctions de classe sont nommées, vous pouvez réellement faire ceci:

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

Cela a fonctionné pour moi.

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;
    }
}

Code de test:

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

J'ai eu un problème similaire et je l'ai résolu comme suit:

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

Ce code implémente, de manière plus confortable, une réponse que j'ai déjà lue ici au début de cette discussion. Maintenant, j'ai une fonction membre récupérant le nom de n'importe quel objet de fonction. Voici le script complet ...

<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>
1
Sandro Rosa

Si j'ai compris ce que vous vouliez faire, c'est ce que je fais dans un constructeur de fonctions.

if (!(this instanceof arguments.callee)) {
    throw "ReferenceError: " + arguments.callee.name + " is not defined";
}
1
Zibri

Cela fonctionnera dans ES5, ES6, tous les navigateurs et les fonctions du mode strict.

Voici à quoi ça ressemble avec une fonction nommée.

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

Voici à quoi ça ressemble avec une fonction anonyme.

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