Вопрос

function main()
{
   Hello();
}

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

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

Это было полезно?

Решение

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

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

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


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

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

Другие советы

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

Вы можете найти всю трассировку стека, используя код конкретного браузера.Хорошая вещь кто-то уже сделал это;здесь код проекта на 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 и имени вызывающей функции (хотя они не являются кроссбраузерными).

Подведем итоги (и проясним ситуацию)...

этот код:

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

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

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

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

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

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

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

Пока звонящий не будет null.

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

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

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

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

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

function main() {
    Hello();
}

function Hello() {
    console.trace()
}

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

Вы можете использовать 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

function Hello() {
    alert(Hello.caller);
}

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

Похоже, это довольно решенный вопрос, но недавно я обнаружил, что Вызываемый не допускается в «строгом режиме» , поэтому для собственного использования я написал класс, который получит путь от места его вызова. Это часть небольшой вспомогательной библиотеки , и если вы хотите использовать автономный код, измените смещение, используемое для возврата трассировки стека вызывающей стороны (используйте 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('.');
  };
}

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

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

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

arguments.callee.caller.name

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

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

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

sello()

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

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

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

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

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

Обновление 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 with:', ...messages);
    } else {
      console.log(fnName(logLines[2]), 'called with:', ...messages);
    }
  } else {
    console.log(...messages);
  }
}

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

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

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

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

Просто хочу сообщить, что на PhoneGap / Android name не работает. Но arguments.callee.caller.toString() добьется цели.

Здесь все, кроме 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>

здесь есть функция, позволяющая получить полную версию трассировки стека :

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

ответ Хейстюарта и Ответ ЦзяжунВу оба упомянули, что 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 стек будет неопределенным — вам придется создать настоящее исключение, чтобы получить стек.

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

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

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.

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

Например:

function reformatString(string, callerName) {

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

    return string;
}

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

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

    reformatString(myString, function.name);
}

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

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

Как вы узнаете функция вызова в 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)]

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

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

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

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

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
}

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

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

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

function getCallerName(func)
{
  if (!func) return "anonymous";
  let caller = func.caller;
  if (!caller) return "anonymous";
  caller = caller.toString();
  if (!caller.trim().startsWith("function")) return "anonymous";
  return caller.substring(0, caller.indexOf("(")).replace("function","");
}


//  Example of how to use "getCallerName" function

function Hello(){
console.log("ex1  =>  " + getCallerName(Hello));
}

function Main(){
Hello();

// another example
console.log("ex3  =>  " + getCallerName(Main));
}

Main();

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top