Come scopri la funzione chiamante in JavaScript?


866
function main()
{
   Hello();
}

function Hello()
{
  // How do you find out the caller function is 'main'?
}

C'è un modo per scoprire lo stack di chiamate?


63
Spero che questo sia solo per aiutarti nel debugging. Il comportamento variabile in base al chiamante è una cattiva idea.
GU.

Quando sarebbe utile per il debug?
Anderson Green,

33
@AndersonGreen quando hai, ad esempio, un metodo di rendering del modello predefinito e vedi che viene chiamato due volte. Invece di sfogliare migliaia di LoC o faticosi passaggi con il debugger, puoi vedere quale era lo stack in quel momento.
dal

28
per vedere la traccia dello stack usa console.trace () per chrome. non so di altri però
lukas.pukenis il

5
Perché questa è una cattiva idea?
Jacob Schneider,

Risposte:


995
function Hello()
{
    alert("caller is " + Hello.caller);
}

Questa funzionalità non è standard , da Function.caller:

Non standard
Questa funzione non è standard e non è su una traccia standard. Non utilizzarlo su siti di produzione rivolti al Web: non funzionerà per tutti gli utenti. Potrebbero esserci anche grandi incompatibilità tra le implementazioni e il comportamento potrebbe cambiare in futuro.


Quella che segue è la vecchia risposta del 2008, che non è più supportata nel moderno Javascript:

function Hello()
{
    alert("caller is " + arguments.callee.caller.toString());
}

254
arguments.callee.caller.nameotterrà il nome della funzione.
Rocket Hazmat,

137
"Non è possibile accedere alle proprietà 'caller', 'callee' e 'argomenti' sulle funzioni in modalità rigorosa o sugli oggetti argomenti per le chiamate" - sono deprecate in ES5 e rimosse in modalità rigorosa.
ThatGuy

12
Funzionerà solo se non si utilizza la modalità rigorosa. Quindi la rimozione 'use strict';potrebbe aiutare.
pvorb,

23
argumentsÈ possibile accedervi dall'interno di una funzione in modalità rigorosa, sarebbe stupido deprecarla. semplicemente non da function.arguments dall'esterno. Inoltre, se si dispone di un argomento denominato, gli argomenti [i] sotto forma non monitoreranno le modifiche apportate alla versione denominata all'interno della funzione.
rvr_jon,

41
Questo metodo è diventato obsoleto da quando questo post è stato elencato nel 2011. Il metodo preferito è ora Function.caller, (dal 2015).
Greg,

152

StackTrace

È possibile trovare l'intera traccia dello stack utilizzando il codice specifico del browser. La cosa buona è che qualcuno l'ha già fatto ; ecco il codice del progetto su GitHub .

Ma non tutte le notizie sono buone:

  1. È molto lento ottenere la traccia dello stack, quindi fai attenzione (leggi questo per ulteriori informazioni).

  2. Sarà necessario definire i nomi delle funzioni affinché la traccia dello stack sia leggibile. Perché se hai un codice come questo:

    var Klass = function kls() {
       this.Hello = function() { alert(printStackTrace().join('\n\n')); };
    }
    new Klass().Hello();

    Google Chrome avviserà ... kls.Hello ( ...ma la maggior parte dei browser si aspetterà un nome di funzione subito dopo la parola chiave functione la tratterà come una funzione anonima. Nemmeno Chrome sarà in grado di utilizzare il Klassnome se non si assegna il nome klsalla funzione.

    E a proposito, puoi passare alla funzione printStackTrace l'opzione {guess: true}ma non ho trovato alcun reale miglioramento facendo questo.

  3. Non tutti i browser ti forniscono le stesse informazioni. Cioè, parametri, colonna di codice, ecc.


Nome funzione chiamante

A proposito, se vuoi solo il nome della funzione chiamante (nella maggior parte dei browser, ma non IE) puoi usare:

arguments.callee.caller.name

Ma nota che questo nome sarà quello dopo la functionparola chiave. Non ho trovato alcun modo (anche su Google Chrome) per ottenere di più senza ottenere il codice dell'intera funzione.


Codice funzione chiamante

E riassumendo il resto delle migliori risposte (di Pablo Cabrera, nourdine e Greg Hewgill). L'unica cosa cross-browser e davvero sicura che puoi usare è:

arguments.callee.caller.toString();

Che mostrerà il codice della funzione chiamante. Purtroppo, questo non è abbastanza per me, ed è per questo che ti do consigli per StackTrace e la funzione di chiamata Nome (anche se non sono cross-browser).


1
forse si dovrebbe aggiungere Function.callerper @ risposta di Greg
Zach Lysobey

Function.callernon funzionerà in modalità rigorosa, tuttavia.
Rickard Elimää,

54

So che hai menzionato "in Javascript", ma se lo scopo è il debug, penso che sia più semplice utilizzare gli strumenti di sviluppo del tuo browser. Ecco come appare in Chrome: inserisci qui la descrizione dell'immagine rilascia il debugger nel punto in cui desideri esaminare lo stack.


3
Questa è una vecchia domanda ... ma questo è sicuramente il modo più moderno e valido per farlo oggi.
Markstewie,

53

Ricapitolando (e chiarendolo) ...

questo codice:

function Hello() {
    alert("caller is " + arguments.callee.caller.toString());
}

è equivalente a questo:

function Hello() {
    alert("caller is " + Hello.caller.toString());
}

Chiaramente il primo bit è più portatile, dato che puoi cambiare il nome della funzione, dire da "Ciao" a "Ciao", e far funzionare ancora tutto.

In quest'ultimo caso, nel caso in cui decidessi di refactificare il nome della funzione invocata (Hello), dovresti modificarne tutte le occorrenze :(


7
argument.callee.caller sempre null su Chrome 25.0.1364.5 dev
Kokizzu

53

È possibile ottenere lo stacktrace completo:

arguments.callee.caller
arguments.callee.caller.caller
arguments.callee.caller.caller.caller

Fino a quando il chiamante è null .

Nota: provoca un ciclo infinito su funzioni ricorsive.


2
Ci scusiamo per la risposta tardiva, ma non ho visto il tuo commento prima; solo per il caso di ricorsione non funziona, in altri casi dovrebbe funzionare.
ale5000,

45

Di solito uso (new Error()).stack in Chrome. La cosa bella è che questo ti dà anche i numeri di linea in cui il chiamante ha chiamato la funzione. Il rovescio della medaglia è che limita la lunghezza dello stack a 10, motivo per cui sono arrivato a questa pagina in primo luogo.

(Lo sto usando per raccogliere callstacks in un costruttore di basso livello durante l'esecuzione, per visualizzare ed eseguire il debug in un secondo momento, quindi l'impostazione di un breakpoint non è utile poiché verrà colpita migliaia di volte)


Potresti per favore aggiungere qualche altra descrizione sulla spiegazione che fornisci?
abarisone,

6
Questa è l'unica cosa che potrei fare al lavoro quando 'use strict';è a posto. Mi hai dato le informazioni di cui avevo bisogno - grazie!
Jeremy Harris,

5
Per quanto riguarda il limite della lunghezza dello stack ... puoi modificarlo con "Error.stackTraceLimit = Infinity".
Tom,

(nuovo errore ("StackLog")). stack.split ("\ n") rende più piacevole la lettura.
Teoman Shipahi,

36

Se non lo eseguirai in IE <11, allora console.trace () andrebbe bene.

function main() {
    Hello();
}

function Hello() {
    console.trace()
}

main()
// Hello @ VM261:9
// main @ VM261:4

Sta funzionando! Dovrebbero essere aggiunti più voti
positivi

22

È possibile utilizzare Function.Caller per ottenere la funzione di chiamata. Il vecchio metodo che utilizza argomento.caller è considerato obsoleto.

Il seguente codice illustra il suo utilizzo:

function Hello() { return Hello.caller;}

Hello2 = function NamedFunc() { return NamedFunc.caller; };

function main()
{
   Hello();  //both return main()
   Hello2();
}

Note sull'argomento obsoleto.caller: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments/caller

Attenzione: Function.caller non è standard: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/caller


1
Questa è la risposta giusta in questi giorni. Non puoi più fare cose su argomenti.caller.callee. Vorrei che potessimo spostarlo in alto poiché tutte le altre cose sono obsolete ora.
coblr

4
Sembra che questo non sia possibile in modalità rigorosa? Cannot access caller property of a strict mode function
Zach Lysobey,

Anche Function.caller non ha funzionato per me in modalità rigorosa. Inoltre, secondo MDN , function.caller non è standard e non dovrebbe essere usato in produzione. Potrebbe funzionare anche per il debug.
jkdev,

Non ho avuto problemi con non standard se funzionava in Node, ma semplicemente non è consentito in modalità rigorosa (ho testato sul nodo 6.10). Lo stesso vale per "argomenti". Ricevo un messaggio di errore: '' 'caller' e 'argomenti' sono proprietà delle funzioni limitate e non è possibile accedervi in ​​questo contesto. "
Tom

21

Vorrei fare questo:

function Hello() {
  console.trace();
}

Funziona benissimo! dovrebbe essere accettato come la risposta giusta, dato che altri modi sono vecchi \ non funzionano più
Yuval Pruss,



16

Sembra che questa sia una domanda abbastanza risolta, ma di recente ho scoperto che la chiamata non è consentita in "modalità rigorosa", quindi per mio uso ho scritto una classe che otterrà il percorso da dove viene chiamata. Fa parte di una piccola libreria di supporto e se si desidera utilizzare il codice autonomo, modificare l'offset utilizzato per restituire la traccia dello stack del chiamante (utilizzare 1 anziché 2)

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('.');
  };
}

Non funziona con me function a(){ function b(){ function c(){ return ScriptPath(); } return c(); } return b(); } a()nella console (non ho provato in un file), ma sembra avere un'idea ragionevole. Dovrebbe essere comunque votato per la visibilità.
ninjagecko,

L'idea è fantastica. Sto analizzando diversamente, ma nelle app nw.js, questa è davvero l'unica idea che dà quello che sto cercando.
Andrew Grothe,

Fornisci un esempio di come chiamare questa funzione.
pd_au,


11

La console registra il tuo stack di errori. Puoi quindi sapere come ti chiamano

const hello = () => {
  console.log(new Error('I was called').stack)
}

const sello = () => {
  hello()
}

sello()


10

In entrambe le modalità ES6 e Strict, utilizzare quanto segue per ottenere la funzione Caller

console.log((new Error()).stack.split("\n")[2].trim().split(" ")[1])

Si noti che la riga sopra genererà un'eccezione, se non vi è alcun chiamante o stack precedente. Utilizzare di conseguenza.


Per ottenere la chiamata (il nome della funzione corrente), utilizzare: console.log((new Error()).stack.split("\n")[1].trim().split(" ")[1])
VanagaS

10

Aggiornamento 2018

callerè vietato in modalità rigorosa . Ecco un'alternativa usando lo stack (non standard)Error .

La seguente funzione sembra fare il lavoro in Firefox 52 e Chrome 61-71 sebbene la sua implementazione faccia molte ipotesi sul formato di registrazione dei due browser e dovrebbe essere usata con cautela, dato che genera un'eccezione ed eventualmente esegue due regex abbinamenti prima di essere fatto.

'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 log with:', ...messages);
    } else {
      console.log(fnName(logLines[2]), 'called log with:', ...messages);
    }
  } else {
    console.log(...messages);
  }
}

function foo() {
  log('hi', 'there');
}

(function main() {
  foo();
}());


4
È incredibile e anche terrificante.
Ian,

Ho ricevuto "pippo chiamato con: ciao lì", ma pippo non è stato chiamato con "ciao là", il registro è stato chiamato con "ciao là"
Andrew R

Bene, c'era un "modificatore mal posizionato" nella grammatica del messaggio di errore. Voleva dire "il registro veniva chiamato dalla funzione f, voleva che il messaggio X fosse stampato" ma nel modo più breve possibile.
Rovanion,

7

Volevo aggiungere il mio violino qui per questo:

http://jsfiddle.net/bladnman/EhUm3/

Ho testato questo è Chrome, Safari e IE (10 e 8). Funziona bene. C'è solo 1 funzione che conta, quindi se vieni spaventato dal violino grande, leggi di seguito.

Nota: c'è un bel po 'della mia "piastra di caldaia" in questo violino. Puoi rimuovere tutto ciò e usare le divisioni se vuoi. È solo un "insieme di funzioni ultra-sicure su cui sono arrivato a fare affidamento.

C'è anche un modello "JSFiddle" che uso per molti violini semplicemente per giocherellare rapidamente.


Mi chiedo se potresti aggiungere gli "aiutanti" come estensioni per il prototipo in alcuni casi, ad esempio:String.prototype.trim = trim;
autistico

6

Se si desidera solo il nome della funzione e non il codice e si desidera una soluzione indipendente dal browser, utilizzare quanto segue:

var callerFunction = arguments.callee.caller.toString().match(/function ([^\(]+)/)[1];

Si noti che quanto sopra restituirà un errore se non vi è alcuna funzione chiamante in quanto non vi è alcun elemento [1] nell'array. Per aggirare, utilizzare il seguente:

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


5

Voglio solo farvi sapere che il PhoneGap / Android il namedoesnt sembra funzionare. Ma arguments.callee.caller.toString()farà il trucco.


4

Qui, tutto tranne che functionnameviene rimosso caller.toString(), con RegExp.

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

questo sta ancora restituendo l'intera dichiarazione del metodo
Maslow,

4

ecco una funzione per ottenere stacktrace completo :

function stacktrace() {
var f = stacktrace;
var stack = 'Stack trace:';
while (f) {
  stack += '\n' + f.name;
  f = f.caller;
}
return stack;
}

3

La risposta di Heystewart e la risposta di JiarongWu hanno entrambe menzionato che l' Erroroggetto ha accesso a stack.

Ecco un esempio:

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

Browser diversi mostra lo stack in diversi formati di stringa:

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)

La maggior parte dei browser imposterà lo stack con var stack = (new Error()).stack . In Internet Explorer lo stack non sarà definito: devi recuperare una vera eccezione per recuperare lo stack.

Conclusione: è possibile determinare "principale" è il chiamante di "Ciao" utilizzando stackl' Erroroggetto nell'oggetto. In effetti funzionerà nei casi in cui l' approccio callee/ callernon funziona. Ti mostrerà anche il contesto, cioè il file sorgente e il numero di riga. Tuttavia è necessario uno sforzo per rendere la soluzione multipiattaforma.


2

Nota che non puoi usare Function.caller in Node.js, usa invece il pacchetto id chiamante . Per esempio:

var callerId = require('caller-id');

function foo() {
    bar();
}
function bar() {
    var caller = callerId.getData();
    /*
    caller = {
        typeName: 'Object',
        functionName: 'foo',
        filePath: '/path/of/this/file.js',
        lineNumber: 5,
        topLevelFlag: true,
        nativeFlag: false,
        evalFlag: false
    }
    */
}

1

Prova il seguente codice:

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

Ha funzionato per me in Firefox-21 e Chromium-25.


Prova questo per le funzioni ricorsive.
daniel1426,

arguments.calleeè stato deprecato per molti anni .
Dan Dascalescu il

1

Un altro modo per aggirare questo problema è semplicemente passare il nome della funzione chiamante come parametro.

Per esempio:

function reformatString(string, callerName) {

    if (callerName === "uid") {
        string = string.toUpperCase();
    }

    return string;
}

Ora puoi chiamare la funzione in questo modo:

function uid(){
    var myString = "apples";

    reformatString(myString, function.name);
}

Il mio esempio usa un controllo codificato del nome della funzione, ma potresti facilmente usare un'istruzione switch o qualche altra logica per fare quello che vuoi lì.


Credo che ciò risolva anche problemi di compatibilità tra browser, per la maggior parte. Ma per favore prova questo prima di dare per scontato che sia vero! ( inizia a sudare )
GrayedFox

1

Per quanto ne so, abbiamo 2 modi per farlo da determinate fonti come questa-

  1. arguments.caller

    function whoCalled()
    {
        if (arguments.caller == null)
           console.log('I was called from the global scope.');
        else
           console.log(arguments.caller + ' called me!');
    }
  2. Function.caller

    function myFunc()
    {
       if (myFunc.caller == null) {
          return 'The function was called from the top!';
       }
       else
       {
          return 'This function\'s caller was ' + myFunc.caller;
        }
    }

Pensi di avere la tua risposta :).


Questo è stato deprecato per molti anni e Function.caller non funziona in modalità rigorosa.
Dan Dascalescu il

1

Perché tutte le soluzioni sopra sembrano una scienza missilistica. Nel frattempo, non dovrebbe essere più complicato di questo frammento. Tutti i crediti per questo ragazzo

Come scopri la funzione chiamante in JavaScript?

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

3
Questo è quello che ottengo usando questo: TypeError: non è possibile accedere alle proprietà 'caller', 'callee' e 'argomenti' sulle funzioni in modalità rigorosa o sugli oggetti argomenti per le chiamate. Qualche idea su come farlo in modo rigoroso?
hard_working_ant

1

Sto tentando di affrontare sia la domanda che l'attuale generosità con questa domanda.

La generosità richiede che il chiamante sia ottenuto in modalità rigorosa e l'unico modo in cui riesco a vederlo è facendo riferimento a una funzione dichiarata al di fuori della modalità rigorosa.

Ad esempio, quanto segue non è standard ma è stato testato con le versioni precedenti (29/03/2016) e attuali (1 agosto 2018) di Chrome, Edge e 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();



0

Se hai davvero bisogno della funzionalità per qualche motivo e desideri che sia compatibile con più browser e non preoccuparti di cose rigorose e sia compatibile con le versioni successive, passa un riferimento:

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
}

0

Penso che il seguente pezzo di codice possa essere utile:

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

Eseguire il codice:

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

Il registro è simile al seguente:

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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.