Как сообщить, кто звонит [dубликат]

function main()
{
   Hello();
}

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

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

720
задан 19 March 2014 в 05:42

28 ответов

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

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

26
ответ дан 15 August 2018 в 14:34
  • 1
    Не могли бы вы добавить немного подробного описания объяснений, которые вы предоставляете? – abarisone 7 May 2015 в 17:35
  • 2
    Это единственное, что я мог бы работать, когда 'use strict'; на месте. Дал мне информацию, в которой я нуждался - спасибо! – Jeremy Harris 30 June 2015 в 18:00
  • 3
    Что касается предела длины стека ... вы можете изменить это с помощью «Error.stackTraceLimit = Infinity». – Tom 7 April 2017 в 19:06

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

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

Пока не будет null.

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

46
ответ дан 15 August 2018 в 14:34
  • 1
    Извините за поздний ответ, но я не видел вашего комментария раньше; только для случая рекурсии он не работает, в других случаях он должен работать. – ale5000 13 May 2016 в 22:47

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

16
ответ дан 15 August 2018 в 14:34

Попробуйте получить доступ к этому:

arguments.callee.caller.name
11
ответ дан 15 August 2018 в 14:34
  • 1
    Возможно, вы имели в виду arguments.callee.caller.name ? Потому что nom неверно. – Mariano Desanze 1 March 2011 в 16:03
  • 2
    ням ням ням! Non! C'est 'name' – Aaria Carter-Weir 4 August 2011 в 14:39

Если вы не собираетесь запускать его в IE & lt; 11 тогда console.trace () подойдет.

function main() {
    Hello();
}

function Hello() {
    console.trace()
}

main()
// Hello @ VM261:9
// main @ VM261:4
17
ответ дан 15 August 2018 в 14:34

Вы можете использовать Function.Caller для вызова вызывающей функции. Старый метод, использующий аргумент.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

Знать Функция.caller нестандартна: https://developer.mozilla.org/en -US / документы / Web / JavaScript / Справочные материалы / Функции / аргументы / вызывающего абонента

19
ответ дан 15 August 2018 в 14:34
  • 1
    Это правильный ответ в наши дни. Вы больше не можете использовать файл arguments.caller.callee. Хотелось бы, чтобы мы переместились на вершину, так как теперь все остальное устарело. – coblr 30 June 2015 в 23:36
  • 2
    Похоже, это невозможно в строгом режиме? [F1] – Zach Lysobey 16 September 2015 в 18:38
  • 3
    Function.caller тоже не работал для меня в строгом режиме. Кроме того, в соответствии с MDN , function.caller является нестандартным и не должен использоваться в производстве. Однако это может работать для отладки. – jkdev 21 September 2015 в 05:02
  • 4
    У меня не было проблем с нестандартными, если он работал в узле, но он просто не разрешен в строгом режиме (я тестировал на узле 6.10). То же самое касается «аргументов». Я получаю сообщение об ошибке: '' 'caller' и 'arguments' являются ограниченными функциями функции и не могут быть доступны в этом контексте. & Quot; – Tom 7 April 2017 в 17:40

StackTrace

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

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

Очень медленно получить трассировку стека, поэтому будьте осторожны (прочтите это для большего). Вам нужно будет определить имена функций для трассировки стека, чтобы они были четкими. Потому что, если у вас есть такой код:
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}, но я не нашел никакого реального улучшения, выполнив это. Не все браузеры предоставляют вам ту же информацию. То есть параметры, столбец кода и т. Д.

Имя функции вызывающего абонента

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

arguments.callee.caller.name

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

Код функции вызывающего абонента

И подводя итог остальным лучшим ответам (Pablo Cabrera, nourdine и Greg Hewgill). Единственный кросс-браузер и действительно безопасная вещь, которую вы можете использовать:

arguments.callee.caller.toString();

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

139
ответ дан 15 August 2018 в 14:34

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

function Hello() {
  console.trace();
}
11
ответ дан 15 August 2018 в 14:34

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

30
ответ дан 15 August 2018 в 14:34
  • 1
    Это старый вопрос ... но это, безусловно, современный, самый эффективный способ сделать это сегодня. – markstewie 6 November 2015 в 02:41

Похоже, это довольно решенный вопрос, но я недавно узнал, что вызываемый не разрешен в «строгом режиме», поэтому для моего собственного использования я написал класс, который получит путь от того, где он вызывается. Это часть небольшой вспомогательной библиотеки, и если вы хотите использовать автономное изменение кода, смещение, используемое для возврата трассировки стека вызывающего (используйте 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('.');
  };
}
13
ответ дан 15 August 2018 в 14:34
  • 1
    Не работает для меня с function a(){ function b(){ function c(){ return ScriptPath(); } return c(); } return b(); } a() в консоли (не пробовал в файле), но, похоже, имеет разумную идею. В любом случае, нужно следить за видимостью. – ninjagecko 2 November 2014 в 14:45
  • 2
    Идея велика. Я разбираюсь по-разному, но в приложениях nw.js это действительно единственная идея, которая дает то, что я ищу. – Andrew Grothe 29 June 2015 в 18:48
  • 3
    Просьба привести пример вызова этой функции. – pd_au 20 June 2018 в 07:51
[F1]
17
ответ дан 15 August 2018 в 14:34
  • 1
    И только для имени функции используйте Hello.caller.name – vanval 28 May 2014 в 01:30
  • 2
    как arguments.callee.caller.toString() – user2720864 20 January 2015 в 12:34
  • 3
    Это должен быть правильный ответ, по крайней мере, на 2016 год – Daniel 24 March 2016 в 20:58
  • 4
    Это не на стандартном треке, но будет работать с ECMAScript 5. – Obinna Nwakwue 26 March 2016 в 20:32

Повторить (и сделать его более ясным) ...

этот код:

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

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

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

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

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

47
ответ дан 15 August 2018 в 14:34

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

arguments.caller
function whoCalled()
{
    if (arguments.caller == null)
       console.log('I was called from the global scope.');
    else
       console.log(arguments.caller + ' called me!');
}
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;
    }
}

Подумайте, у вас есть ваш ответ:).

2
ответ дан 15 August 2018 в 14:34

Здесь все, кроме 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>
3
ответ дан 15 August 2018 в 14:34

Я пытаюсь решить этот вопрос и текущую щедрость с этим вопросом.

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

Например, следующее нестандартное, но было протестировано с предыдущими (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();

2
ответ дан 15 August 2018 в 14:34

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

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

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

Примечание: в этой скрипке есть довольно много моего «шаблона». Вы можете удалить все это и использовать split, если хотите.

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

6
ответ дан 15 August 2018 в 14:34
  • 1
    Интересно, можете ли вы добавить & quot; helpers & quot; как расширения для прототипа в некоторых случаях, например: String.prototype.trim = trim; – autistic 30 March 2016 в 12:06

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

Как узнать функцию звонящего в 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)]
2
ответ дан 15 August 2018 в 14:34
  • 1
    Это то, что я получаю от использования этого: TypeError: свойства «вызывающий», «вызываемый» и «аргументы» могут не доступны для функций строгого режима или объектов аргументов для их вызовов. Любые идеи, как работать в строгом режиме? – barry_allen 29 May 2017 в 06:57

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

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.

2
ответ дан 15 August 2018 в 14:34
  • 1
    Попробуйте это для рекурсивных функций. – daniel1426 1 May 2014 в 22:08

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

Например:

function reformatString(string, callerName) {

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

    return string;
}

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

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

    reformatString(myString, function.name);
}

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

1
ответ дан 15 August 2018 в 14:34
  • 1
    Я считаю, что это также решает проблемы совместимости с браузером по большей части. Но, пожалуйста, проверьте это, прежде чем считать, что это правда! ( начинает потеть ) – GrayedFox 4 March 2016 в 19:30

Я думаю, что следующая часть кода может быть полезна:

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
0
ответ дан 15 August 2018 в 14:34

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

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]);
6
ответ дан 15 August 2018 в 14:34

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

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
}
1
ответ дан 15 August 2018 в 14:34
  • 1
    А потом??? Как вы получаете имя вызывающего абонента, то есть «основной»? – Fred J. 27 September 2016 в 06:06

Просто хочу сообщить вам, что на PhoneGap / Android функция name, похоже, не работает. Но arguments.callee.caller.toString() выполнит трюк.

4
ответ дан 15 August 2018 в 14:34

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

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

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

sello()

8
ответ дан 15 August 2018 в 14:34

Сообщение с 2018 года, когда доступ к caller запрещен:

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

let fnNameMatcher = /([^(]+)@|at ([^(]+) \(/;

function fnName(str) {
  let regexResult = fnNameMatcher.exec(str);
  return regexResult[1] || regexResult[2];
}

export function log(...messages: any[]) {
  let logLines = (new Error().stack).split('\n');
  let callerName = fnName(logLines[1]);

  if (callerName !== null) {
    if (callerName !== 'log') {
      console.log(callerName, ...messages);
    } else {
      console.log(fnName(logLines[2]), ...messages);
    }
  } else {
    console.log(...messages);
  }
}
2
ответ дан 15 August 2018 в 14:34

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

Шаги:

Применить точку останова в вашей последней строке определения функции, которая выполняется хорошо. выполните свой код, когда вы достигнете точки останова, нажмите F10 / F11, так как он переместит вас на следующую строку, которая будет выполнена. Следующей строкой будет строка точно после строки, из которой была вызвана ваша функция. Таким образом вы можете отслеживать вызывающего абонента.
-1
ответ дан 15 August 2018 в 14:34

ответ 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.

Заключение: Можно определить, что «main» - это вызывающий объект «Hello», используя stack в объект Error. Фактически он будет работать в случаях, когда подход callee / caller не работает. Он также покажет вам контекст, то есть исходный файл и номер строки. Однако требуется усилие для решения кросс-платформы.

2
ответ дан 15 August 2018 в 14:34

здесь есть функция, чтобы получить полный стек:

function stacktrace() {
var f = stacktrace;
var stack = 'Stack trace:';
while (f) {
  stack += '\n' + f.name;
  f = f.caller;
}
return stack;
}
3
ответ дан 15 August 2018 в 14:34

Другие вопросы по тегам:

Похожие вопросы: