Как вы узнаете функцию вызова в JavaScript?

866
function main()
{
   Hello();
}

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

Есть ли способ узнать стек вызовов?

Рэй Лу
источник
63
Я надеюсь, что это просто, чтобы помочь вам в отладке. Различное поведение в зависимости от вызывающего абонента - плохая идея.
О.Дж.
Когда это будет полезно для отладки?
Андерсон Грин
33
@AndersonGreen, когда у вас есть, например, метод визуализации шаблона по умолчанию, и вы видите, что он вызывается дважды. Вместо того, чтобы прочесывать 1000-й LoC или проходить через отладчик, вы можете просто увидеть, какой был стек в то время.
tkone
28
чтобы увидеть трассировку стека, используйте console.trace () для chrome. хотя не знаю о других
lukas.pukenis 10.10.13
5
Почему это плохая идея?
Джейкоб Шнайдер

Ответы:

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

Обратите внимание, что эта функция является нестандартной , из Function.caller:

Нестандартный
Эта функция является нестандартной и не соответствует стандарту. Не используйте его на рабочих сайтах, выходящих в Интернет: он не будет работать для каждого пользователя. Также могут быть большие несовместимости между реализациями, и поведение может измениться в будущем.


Ниже приводится старый ответ 2008 года, который больше не поддерживается в современном Javascript:

function Hello()
{
    alert("caller is " + arguments.callee.caller.toString());
}
Грег Хьюгилл
источник
254
arguments.callee.caller.nameполучит имя функции.
Ракета Хазмат
137
«К свойствам« вызывающего »,« вызываемого »и« аргументов »нельзя обращаться к функциям строгого режима или объектам аргументов для вызовов к ним» - они устарели в ES5 и удалены в строгом режиме.
ThatGuy
12
Это будет работать, только если вы не используете строгий режим. Так что удаление 'use strict';может помочь.
Пворб
23
argumentsМОЖЕТ быть доступным изнутри функции в строгом режиме, было бы глупо отрицать это. просто не из функции. аргументы извне. Кроме того, если у вас есть именованный аргумент, форма arguments [i] не будет отслеживать изменения, которые вы вносите в именованную версию внутри функции.
rvr_jon
41
Этот метод устарел с тех пор, как этот пост был включен в список в 2011 году. Теперь предпочтительным методом является Function.caller (по состоянию на 2015 год).
Грег
152

Трассировки стека

Вы можете найти всю трассировку стека, используя специфический для браузера код. Хорошо, что кто-то уже сделал это ; Вот код проекта на GitHub .

Но не все новости хорошие

  1. Очень медленно получить трассировку стека, поэтому будьте осторожны (читайте это подробнее).

  2. Вам нужно будет определить имена функций для трассировки стека, чтобы они были разборчивыми. Потому что, если у вас есть такой код:

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

    Google Chrome выдаст предупреждение, ... kls.Hello ( ...но большинство браузеров будут ожидать имя функции сразу после ключевого слова functionи будут рассматривать его как анонимную функцию. Даже Chrome не сможет использовать Klassимя, если вы не дадите имяkls функции.

    И, кстати, вы можете передать функции printStackTrace эту опцию, {guess: true}но я не нашел никакого реального улучшения, сделав это.

  3. Не все браузеры дают вам одинаковую информацию. То есть параметры, столбец кода и т. Д.


Имя функции вызывающей стороны

Кстати, если вам нужно только имя функции вызывающей стороны (в большинстве браузеров, но не в IE), вы можете использовать:

arguments.callee.caller.name

Но обратите внимание, что это имя будет одним после functionключевого слова. Я не нашел способа (даже в Google Chrome) получить больше, чем без кода всей функции.


Код функции звонящего по телефону

И резюмируя остальные лучшие ответы (Пабло Кабрера, Нурдин и Грег Хьюгилл). Единственная кросс-браузерная и действительно безопасная вещь, которую вы можете использовать:

arguments.callee.caller.toString();

Который покажет код функции звонящего. К сожалению, этого недостаточно для меня, и именно поэтому я даю вам подсказки для StackTrace и функции вызывающей стороны Name (хотя они не являются кросс-браузерными).

Мариано Дезанце
источник
1
возможно, вы должны добавить Function.callerв ответ @ Грег
Зак Lysobey
Function.callerоднако не будет работать в строгом режиме.
Рикард Элимяя
54

Я знаю, что вы упомянули "в Javascript", но если целью является отладка, я думаю, что проще использовать инструменты разработчика вашего браузера. Вот как это выглядит в Chrome: введите описание изображения здесь просто бросьте отладчик, где вы хотите исследовать стек.

Фил
источник
3
Это старый вопрос ... но это, безусловно, самый современный способ сделать это сегодня.
отмечается
53

Подведем итоги (и проясним)

этот код:

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

эквивалентно этому:

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

Очевидно, что первый бит более переносим, ​​поскольку вы можете изменить имя функции, скажем, с «Hello» на «Ciao», и все же заставить все это работать.

В последнем случае, если вы решите провести рефакторинг имени вызываемой функции (Hello), вам придется изменить все ее вхождения :(

nourdine
источник
7
arguments.callee.caller всегда отсутствует в Chrome 25.0.1364.5 dev
Kokizzu
53

Вы можете получить полную трассировку стека:

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

Пока звонилка есть null.

Примечание: это вызывает бесконечный цикл на рекурсивных функциях.

ale5000
источник
2
Извините за поздний ответ, но я не видел ваш комментарий раньше; только для случая рекурсии это не работает, в других случаях это должно работать.
ale5000
45

Я обычно использую (new Error()).stack в Chrome. Приятно то, что это также дает вам номера строк, где вызывающая сторона вызывала функцию. Недостатком является то, что он ограничивает длину стека до 10, поэтому я пришел на эту страницу в первую очередь.

(Я использую это для сбора callstacks в низкоуровневом конструкторе во время выполнения, для просмотра и отладки позже, поэтому установка точки останова не используется, так как она будет срабатывать тысячи раз)

heystewart
источник
Не могли бы вы добавить немного больше описания о вашем объяснении?
abarisone
6
Это единственное, что я могу получить на работе, когда 'use strict';на месте. Дали мне информацию, в которой я нуждался - спасибо!
Джереми Харрис
5
Что касается ограничения длины стека ... вы можете изменить это с помощью «Error.stackTraceLimit = Infinity».
Том
(новая ошибка ("StackLog")). stack.split ("\ n") делает чтение более приятным.
Теоман Шипахи
36

Если вы не собираетесь запускать его в IE <11, тогда будет подходить console.trace () .

function main() {
    Hello();
}

function Hello() {
    console.trace()
}

main()
// Hello @ VM261:9
// main @ VM261:4
gumkins
источник
Работает! Должно быть добавлено больше голосов вверх
Крунал Панчал
22

Вы можете использовать Function.Caller, чтобы получить вызывающую функцию. Старый метод, использующий аргумент arguments.caller, считается устаревшим.

Следующий код иллюстрирует его использование:

function Hello() { return Hello.caller;}

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

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

Заметки об устаревшем аргументе. Caller : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments/caller

Помните, что Function.caller нестандартный: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/caller

Greg
источник
1
Это правильный ответ в эти дни. Вы больше не можете делать arguments.caller.callee. Хотелось бы, чтобы мы перенесли это на вершину, поскольку все остальное уже устарело.
coblr
4
Похоже, это невозможно в строгом режиме? Cannot access caller property of a strict mode function
Зак Лисобей
Function.caller также не работал для меня в строгом режиме. Кроме того, согласно MDN , function.caller является нестандартным и не должен использоваться в производстве. Это может работать для отладки, хотя.
jkdev
У меня не было проблем с нестандартным, если он работал в Node, но он просто не разрешен в строгом режиме (я тестировал на узле 6.10). То же самое относится и к «аргументам». Я получаю сообщение об ошибке: '' 'caller' и 'arguments' - это свойства ограниченной функции, к которым нельзя получить доступ в этом контексте. "
Том
21

Я бы сделал это:

function Hello() {
  console.trace();
}
inorganik
источник
Это работает отлично! должен быть принят как правильный ответ, так как другие способы
Юваль Прусс
19
function Hello() {
    alert(Hello.caller);
}
Shadow2531
источник
1
И только для имени функции используйте Hello.caller.name
vanval
так же, какarguments.callee.caller.toString()
user2720864
Это должен быть правильный ответ, по крайней мере, на 2016 год
Даниэль
Это не соответствует стандартам, но будет работать с ECMAScript 5.
Обинна Нваквю
1
@ Даниель: нет, не должно. См developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
Dan Dăscălescu
18

Это безопаснее в использовании, *arguments.callee.callerтак arguments.callerкак устарела ...

Пабло Кабрера
источник
36
arguments.calleeтакже устарела в ES5 и удалена в строгом режиме.
nyuszika7h
2
Есть ли альтернатива? Изменить: arguments.calleeбыло плохим решением проблемы, которая теперь лучше решена developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Матфея
16

Похоже, это довольно решенный вопрос, но недавно я обнаружил, что вызываемый объект не допускается в «строгом режиме», поэтому для собственного использования я написал класс, который будет получать путь от места его вызова. Это часть небольшой вспомогательной библиотеки, и если вы хотите использовать автономный код, измените смещение, используемое для возврата трассировки стека вызывающей стороны (используйте 1 вместо 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('.');
  };
}
QueueHammer
источник
У меня не работает function a(){ function b(){ function c(){ return ScriptPath(); } return c(); } return b(); } a()в консоли (не пробовал в файле), но, похоже, есть разумная идея. В любом случае следует проголосовать за видимость.
ниндзягецко
Идея отличная. Я разбираю по-разному, но в приложениях nw.js это действительно единственная идея, которая дает то, что я ищу.
Эндрю Грот
Пожалуйста, приведите пример того, как вызывать эту функцию.
pd_au
11

Просто консоль журнала вашей ошибки стека. Вы можете узнать, как вас зовут

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

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

sello()

Prasanna
источник
10

И в ES6, и в строгом режиме используйте следующую функцию, чтобы получить функцию Caller.

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

Обратите внимание, что в приведенной выше строке будет сгенерировано исключение, если нет вызывающей стороны или нет предыдущего стека. Используйте соответственно.

Ванагасом
источник
Чтобы получить вызываемого абонента (имя текущей функции), используйте: console.log((new Error()).stack.split("\n")[1].trim().split(" ")[1])
VanagaS
10

2018 Обновление

callerзапрещено в строгом режиме . Вот альтернатива, использующая (нестандартный) Errorстек .

Следующая функция, кажется, делает работу в Firefox 52 и Chrome 61-71, хотя ее реализация делает много предположений о формате ведения журнала двух браузеров и должна использоваться с осторожностью, учитывая, что она выдает исключение и, возможно, выполняет два регулярных выражения соответствия, прежде чем быть сделано.

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

Rovanion
источник
4
Это невероятно, а также ужасно.
Ян
Я получил "foo call with: hi there", но foo не был вызван с "hi there", log был вызван с "hi there"
AndrewR
Да, в грамматике сообщения об ошибке был «неуместный модификатор». Это означало сказать: «журнал был вызван из функции f, он хотел напечатать сообщение X», но в краткой форме, насколько это возможно.
Рованион
7

Я хотел добавить свою скрипку здесь для этого:

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

Я проверял это хром, сафари и IE (10 и 8). Работает отлично. Имеет значение только 1 функция, поэтому, если вас пугает большая скрипка, читайте ниже.

Примечание: в этой скрипке довольно много моих собственных "шаблонов". Вы можете удалить все это и использовать сплит, если хотите. Это просто ультра-безопасный »набор функций, на которые я полагаюсь.

Там также есть шаблон "JSFiddle", который я использую для многих скрипок, чтобы просто быстро поиграть.

bladnman
источник
Интересно, можете ли вы добавить «помощников» в качестве расширений для прототипа в некоторых случаях, например:String.prototype.trim = trim;
аутист
6

Если вам просто нужно имя функции, а не код, и вы хотите независимое от браузера решение, используйте следующее:

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

Обратите внимание, что приведенное выше вернет ошибку, если нет функции вызывающей стороны, поскольку в массиве нет элемента [1]. Чтобы обойти, используйте ниже:

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]);
JoolzCheat
источник
1
Это устарело в течение многих лет .
Дан Даскалеску
5

Просто хочу , чтобы вы знали , что на PhoneGap / Androidname , кажется, работает оленья кожа. Но arguments.callee.caller.toString()сделаем свое дело.

Пабло Арментано
источник
4

Здесь все , но functionnameудаляется из caller.toString(), с 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>
BrazFlat
источник
это все еще возвращает полное объявление метода
Маслоу
3

Ответ heystewart в и ответ JiarongWu в обоих упоминается , что Errorобъект имеет доступ к stack.

Вот пример:

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

Разные браузеры показывают стек в разных форматах строки:

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)

Большинство браузеров устанавливают стек с var stack = (new Error()).stack . В Internet Explorer стек не определен - вам нужно создать реальное исключение для извлечения стека.

Вывод: можно определить, что "main" является вызывающей стороной "Hello", используя объект stackin Error. На самом деле это будет работать в тех случаях, когда callee/ callerподход не работает. Он также покажет вам контекст, то есть исходный файл и номер строки. Однако необходимо приложить усилия, чтобы сделать решение кроссплатформенным.

Стивен Куан
источник
2

Обратите внимание, что вы не можете использовать Function.caller в Node.js, вместо этого используйте пакет caller-id . Например:

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
    }
    */
}
ns16
источник
1

Попробуйте следующий код:

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

У меня работали в Firefox-21 и Chromium-25.

Диего Аугусто Молина
источник
Попробуйте это для рекурсивных функций.
daniel1426
arguments.calleeбыл осужден в течение многих лет .
Дан Даскалеску
1

Другой способ обойти эту проблему - просто передать имя вызывающей функции в качестве параметра.

Например:

function reformatString(string, callerName) {

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

    return string;
}

Теперь вы можете вызвать функцию следующим образом:

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

    reformatString(myString, function.name);
}

В моем примере используется жестко закодированная проверка имени функции, но вы можете легко использовать оператор switch или какую-то другую логику, чтобы делать то, что вы хотите.

GrayedFox
источник
Я полагаю, что это также решает проблемы совместимости с различными браузерами, по большей части. Но, пожалуйста, проверьте это, прежде чем предположить, что это правда! ( начинает потеть )
GrayedFox
1

Насколько я знаю, у нас есть два пути для этого из данных источников, как это

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

Думаю, у тебя есть ответ :).

Абрар Джахин
источник
Это не рекомендуется в течение многих лет , и Function.caller не работает в строгом режиме.
Дан Даскалеску
1

Почему все приведенные выше решения выглядят как ракетостроение. Между тем, это не должно быть сложнее, чем этот фрагмент. Все кредиты этому парню

Как вы узнаете функцию вызова в 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
Это то, что я получаю, используя это: TypeError: свойства caller, callee и arguments не могут быть доступны для функций строгого режима или объектов аргументов для их вызова. Есть идеи, как работать в строгом режиме?
hard_working_ant
1

Я пытаюсь ответить и на этот вопрос, и на текущую награду.

Щедрость требует, чтобы вызывающий был получен в строгом режиме, и единственный способ увидеть это - обратиться к функции, объявленной вне строгого режима.

Например, следующее является нестандартным, но было протестировано с предыдущими (29/03/2016) и текущими (1 августа 2018 года) версиями Chrome, Edge и 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();

аутистический
источник
Хороший взлом, но не будет работать для модулей ES5, которые полностью в строгом режиме .
Дан Даскалеску
0

Если вам действительно нужна функциональность по какой-то причине и вы хотите, чтобы она была совместима с разными браузерами, не беспокоились о строгих вещах и были совместимы с прямой версией, передайте следующую ссылку:

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

Я думаю, что следующий фрагмент кода может быть полезным:

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

Выполните код:

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

Журнал выглядит так:

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
JiarongWu
источник