Czy mogę uzyskać nazwę aktualnie uruchomionej funkcji w JavaScript?

184

Czy można to zrobić:

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

Mam w swoim stosie frameworki Dojo i jQuery, więc jeśli któreś z nich ułatwią, są dostępne.

sprugman
źródło

Odpowiedzi:

196

W ES5 i nowszych nie ma dostępu do tych informacji.

W starszych wersjach JS można go uzyskać za pomocą arguments.callee.

Być może będziesz musiał przeanalizować nazwę, ponieważ prawdopodobnie będzie zawierać dodatkowe śmieci. Chociaż w niektórych implementacjach możesz po prostu uzyskać nazwę za pomocą arguments.callee.name.

Rozbiór gramatyczny zdania:

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

   alert(myName);
}

Źródło: Javascript - pobierz aktualną nazwę funkcji .

Matt
źródło
Właściwie zwracając większą uwagę na twoje pytanie, wygląda na to, że możesz chcieć dodatkowych śmieci :)
Matt
23
@Andrew - Masz rację, powinienem to powiedzieć. Było to szybkie kopiowanie / wklejanie / czyszczenie czegoś, co już wcześniej dodałem do zakładek i niedopatrzenie z mojej strony. Dziękuję za dodanie go do mojego postu.
Matt
81
Przerywa w trybie ścisłym ES5.
Raynos
4
Och ... dlatego ludzie zawsze biją mnie z szybkością odpowiadania. Nie myślałem o tym.
Erik Reppen
9
jeśli używasz literału obiektu do swoich metod i nie ma rzeczywistej nazwy metody, to nie będzie działać jako argumenty. callee działa jak anonimowa funkcja, która nie będzie nosić żadnej nazwy funkcji. Musisz się upewnić, że dwukrotnie dodasz nazwę tej funkcji. Spójrz na ten przykład jsfiddle : jsfiddle.net/ncays . Innym problemem jest jednak to, że arguments.calleenie jest dozwolone w trybie ścisłym.
hellatan
75

Dla funkcji nieanonimowych

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

Ale w przypadku procedury obsługi błędów wynikiem będzie nazwa funkcji obsługi błędów, prawda?

fforw
źródło
2
Działa świetnie w Chrome. O wiele lepsza niż zaakceptowana odpowiedź.
B, 7
1
Warto pamiętać: eslint.org/docs/rules/no-caller > „przestarzałe w przyszłych wersjach JavaScript, a ich stosowanie jest zabronione w ECMAScript 5 w trybie ścisłym”.
Jeremy
44

Wszystko, czego potrzebujesz, jest proste. Utwórz funkcję:

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

Następnie, gdy tylko potrzebujesz, po prostu użyj:

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

foo() 
// Logs: "foo"
Igor Ostroumov
źródło
3
Dzięki, to jest o wiele bardziej eleganckie niż parsowanie sznurka.
modle13
1
To chyba najlepsza odpowiedź!
Siergiej
Idealny. Wtedy JS nie ma stałych natywnych, jak PHP ze stałymi magicznymi ...
stamster
Chrome wyświetla błąd typu, ponieważ właściwość „nazwa” nie istnieje dla osoby dzwoniącej. Jednak kontrola wykazała, że ​​zadziałało:function getFuncName() { return getFuncName.name }
Tom Anderson
@TomAnderson ze zmianą, teraz otrzymujesz nazwę getFuncNamezamiast nazwy osoby dzwoniącej.
Mark McKenna
30

Według MDN

Ostrzeżenie: 5. wydanie ECMAScript (ES5) zabrania używania arguments.callee () w trybie ścisłym. Unikaj używania arguments.callee (), nadając wyrażeniom funkcji nazwę, lub użyj deklaracji funkcji, w której funkcja musi się wywoływać.

Jak wspomniano, dotyczy to tylko sytuacji, gdy skrypt używa „trybu ścisłego”. Wynika to głównie ze względów bezpieczeństwa i niestety obecnie nie ma na to alternatywy.

Laimis
źródło
21

To powinno to zrobić:

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

Dla dzwoniącego wystarczy użyć caller.toString().

Andy E.
źródło
8
To zadziałało dla mnie, ale myślę, że w twoim wyrażeniu regularnym jest literówka. Musiałem usunąć ukośnik przed[
declan
4
@declan: tak, masz rację. Zaskakujące, że nikt inny nie zauważył, że przez prawie 3 lata ta odpowiedź była tutaj :-)
Andy E
@AndyE prawdopodobnie nikt tego nie zauważył, ponieważ kiedy zobaczymy wyrażenie regularne, wchodzimy w tryb TL; DR i szukamy innych odpowiedzi;)
BitTickler
11

To musi należeć do kategorii „najbrzydszych hacków na świecie”, ale proszę bardzo.

Po pierwsze, wydrukowanie nazwy bieżącej funkcji (jak w innych odpowiedziach) wydaje się mieć dla mnie ograniczone zastosowanie, ponieważ już wiesz, co to jest funkcja!

Jednak znalezienie nazwy funkcji wywołującej może być bardzo przydatne dla funkcji śledzenia. Jest to z wyrażeniem regularnym, ale użycie indexOf byłoby około 3 razy szybsze:

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
źródło
fajne rozwiązanie, ale funkcja wywołująca FYI # to niestandardowy developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Max Heiber
Znajomość nazwy bieżącej funkcji może być niezbędna, jeśli funkcja ta jest tworzona dynamicznie z bazy danych i potrzebuje informacji kontekstowych wewnątrz funkcji, która jest przypisana do nazwy funkcji.
Paul Chernoch
9

Oto sposób, który zadziała:

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

Następnie w twoich testach:

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

});

Zauważ, że trzeci test będzie działał tylko wtedy, gdy będzie on zlokalizowany w / util / functions

Anthony
źródło
7

getMyNameFunkcja we fragmencie poniżej zwraca nazwę funkcji wywołującej. Jest to hack i opiera się na niestandardowym funkcji: Error.prototype.stack. Zwróć uwagę, że format zwracanego ciągu Error.prototype.stackjest różnie zaimplementowany w różnych silnikach, więc prawdopodobnie nie będzie to działać wszędzie:

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

O innych rozwiązaniach: arguments.callee nie jest dozwolone w trybie ścisłym i Function.prototype.callerjest niestandardowe i niedozwolone w trybie ścisłym .

Max Heiber
źródło
rozszerz to, aby wyświetlać pozycję w funkcji i obsługiwać funkcje anonimowe za pomocą: .replace (/ ^ \ s + at \ s (. +?) (?: \ s. *: |:) (. *?): (. * ?))? $ / g, '1 $ (2 $: 3 $)')
kofifus
Funkcja Function.prototype.caller nie jest również dozwolona w trybie ścisłym.
fijiaaron
1
Działa idealnie nawet dla funkcji strzałek, niedoceniana odpowiedź
Hao Wu,
3

Innym przypadkiem użycia może być dyspozytor zdarzeń związany w czasie wykonywania:

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

Zaletą jest to, że dyspozytor może być łatwo ponownie użyty i nie musi odbierać kolejki wysyłki jako argumentu, zamiast tego jest implikowany z nazwą wywołania ...

W końcu ogólny przypadek przedstawiony tutaj to „użycie nazwy funkcji jako argumentu, abyś nie musiał przekazywać jej jawnie”, a to może być przydatne w wielu przypadkach, takich jak opcjonalne wywołanie zwrotne jquery animate (), lub w wywołaniach zwrotnych limitów czasu / interwałów (tzn. przekazujesz tylko NAZWĘ funcion).

Sampiolina
źródło
2

Nazwa obecnej funkcji i sposób jej uzyskania wydaje się zmieniać w ciągu ostatnich 10 lat, odkąd zadano to pytanie.

Teraz, nie będąc profesjonalnym programistą, który zna wszystkie historie wszystkich przeglądarek, jakie kiedykolwiek istniały, oto jak to działa dla mnie w przeglądarce Chrome 2019:

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

W niektórych innych odpowiedziach wystąpiły błędy Chrome dotyczące ścisłego kodu javascript i nie tylko.

BitTickler
źródło
1

Ponieważ napisałeś funkcję o nazwie fooi wiesz, myfile.jsdlaczego właśnie dlatego potrzebujesz dynamicznie uzyskiwać te informacje?

Biorąc to pod uwagę, możesz użyć arguments.callee.toString()wewnątrz funkcji (jest to ciąg znaków reprezentujący całą funkcję) i ponownie wyrejestrować wartość nazwy funkcji.

Oto funkcja, która wypluje swoją nazwę:

function foo() {
    re = /^function\s+([^(]+)/
    alert(re.exec(arguments.callee.toString())[1]);             
}
Andrew Hare
źródło
5
Pracuję nad modułem obsługi błędów i chcę zgłosić funkcję wywołującą.
sprugman
1

Kombinacja kilku odpowiedzi, które tu widziałem. (Testowane w 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);
}

Wywołanie randomFunction () zaalarmuje ciąg zawierający nazwę funkcji.

JS Fiddle Demo: http://jsfiddle.net/mjgqfhbe/

Buddamus
źródło
1

Informacje są aktualne w 2016 roku.


Wyniki dla deklaracji funkcji

Wynik w Operze

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

Wynik w 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

Wynik w 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

Nie działa w przeglądarce Firefox. Nie przetestowano w IE i Edge.


Wyniki dla wyrażeń funkcyjnych

Wynik w NodeJS

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

Wynik w Chrome

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

Nie działa w przeglądarce Firefox, Opera. Nie przetestowano w IE i Edge.

Uwagi:

  1. Funkcja anonimowa nie ma sensu sprawdzać.
  2. Środowisko testowe

~ $ 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
źródło
1
(function f() {
    console.log(f.name);  //logs f
})();

Odmiana maszynopisu:

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

f2(f1);  //Logs f1

Uwaga dostępna tylko w silnikach zgodnych z ES6 / ES2015. Więcej patrz

Ole
źródło
0

Oto jeden liniowiec:

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

Lubię to:

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

To wariant odpowiedzi Igora Ostroumova :

Jeśli chcesz użyć go jako wartości domyślnej parametru, musisz rozważyć wywołanie drugiego poziomu do „dzwoniącego”:

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

Umożliwiłoby to dynamicznie implementację wielokrotnego użytku w wielu funkcjach.

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

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

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

function crow()
{
  bar();
}

foo();
crow();

Jeśli chcesz również nazwę pliku, oto rozwiązanie wykorzystujące odpowiedź F-3000 na inne pytanie:

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
źródło
-1

Próbować:

alert(arguments.callee.toString());
Deniz Dogan
źródło
3
To zwróciłoby całą funkcję jako ciąg
Andy E
-7

Odpowiedź jest krótka: alert(arguments.callee.name);

Bazylia
źródło
12
„nom” to „nazwa” w języku francuskim. Czy tego rodzaju szczegóły zmieniają się między wersjami językowymi przeglądarek? Nie sądzę.
argyle