Как узнать вызывающую функцию в JavaScript?
-
07-07-2019 - |
Вопрос
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.
Но не все новости хорошие:
Трассировка стека получается очень медленно, поэтому будьте осторожны (читайте этот для большего).
Вам нужно будет определить имена функций, чтобы трассировка стека была разборчивой.Потому что если у вас есть такой код:
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) получить больше, не получив код всей функции.
Код функции вызывающего абонента
И суммируем остальные лучшие ответы (Пабло Кабреры, Нурдина и Грега Хьюгилла). Единственная кроссбраузерная и действительно безопасная вещь, которую вы можете использовать, это:
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();