function main()
{
Hello();
}
function Hello()
{
// How do you find out the caller function is 'main'?
}
Czy istnieje sposób na sprawdzenie stosu wywołań?
function main()
{
Hello();
}
function Hello()
{
// How do you find out the caller function is 'main'?
}
Czy istnieje sposób na sprawdzenie stosu wywołań?
Odpowiedzi:
function Hello()
{
alert("caller is " + Hello.caller);
}
Pamiętaj, że ta funkcja jest niestandardowa , ponieważ Function.caller
:
Niestandardowy
Ta funkcja jest niestandardowa i nie jest na standardowym torze. Nie używaj go w witrynach produkcyjnych z dostępem do Internetu: nie będzie działać dla każdego użytkownika. Mogą również występować duże niezgodności między implementacjami, a zachowanie może ulec zmianie w przyszłości.
Poniżej znajduje się stara odpowiedź z 2008 roku, która nie jest już obsługiwana w nowoczesnym JavaScript:
function Hello()
{
alert("caller is " + arguments.callee.caller.toString());
}
arguments.callee.caller.name
otrzyma nazwę funkcji.
'use strict';
może pomóc.
arguments
Można uzyskać do niego dostęp z funkcji w trybie ścisłym, głupotą byłoby przestać to robić. po prostu nie z function.arguments z zewnątrz. Ponadto, jeśli masz nazwany argument, jego forma argumentów [i] nie będzie śledzić zmian, które wprowadzasz w nazwanej wersji wewnątrz funkcji.
Cały ślad stosu można znaleźć za pomocą kodu specyficznego dla przeglądarki. Dobrą rzeczą jest to, że ktoś już to zrobił ; oto kod projektu na GitHub .
Ale nie wszystkie wiadomości są dobre:
Śledzenie stosu jest bardzo powolne, więc bądź ostrożny (przeczytaj to więcej).
Musisz zdefiniować nazwy funkcji, aby ślad stosu był czytelny. Ponieważ jeśli masz taki kod:
var Klass = function kls() {
this.Hello = function() { alert(printStackTrace().join('\n\n')); };
}
new Klass().Hello();
Google Chrome ostrzega, ... kls.Hello ( ...
ale większość przeglądarek oczekuje nazwy funkcji tuż po słowie kluczowym function
i traktuje ją jako funkcję anonimową. Nawet Chrome nie będzie mógł użyć tej Klass
nazwy, jeśli nie podasz kls
jej funkcji.
Nawiasem mówiąc, możesz przejść do funkcji printStackTrace opcję, {guess: true}
ale nie znalazłem żadnej prawdziwej poprawy.
Nie wszystkie przeglądarki podają te same informacje. To znaczy parametry, kolumna kodu itp.
Nawiasem mówiąc, jeśli chcesz tylko nazwę funkcji wywołującej (w większości przeglądarek, ale nie IE), możesz użyć:
arguments.callee.caller.name
Pamiętaj jednak, że ta nazwa będzie następować po function
słowie kluczowym. Nie znalazłem sposobu (nawet w Google Chrome), aby uzyskać więcej bez uzyskania kodu całej funkcji.
I podsumowując pozostałe najlepsze odpowiedzi (autor: Pablo Cabrera, Nourdine i Greg Hewgill). Jedyną i naprawdę bezpieczną rzeczą, jakiej można używać w różnych przeglądarkach, jest:
arguments.callee.caller.toString();
Który pokaże kod funkcji dzwoniącego. Niestety, to mi nie wystarcza i dlatego udzielam wskazówek dotyczących StackTrace i nazwy funkcji wywołującej (chociaż nie są one obsługiwane przez różne przeglądarki).
Function.caller
za @ odpowiedź Grega
Function.caller
nie będzie jednak działać w trybie ścisłym.
Wiem, że wspomniałeś o „w Javascript”, ale jeśli celem jest debugowanie, myślę, że łatwiej jest po prostu użyć narzędzi programistycznych przeglądarki. Tak to wygląda w Chrome: Po prostu upuść debugger w miejscu, w którym chcesz sprawdzić stos.
Podsumowując (i czyniąc to jaśniejszym) ...
ten kod:
function Hello() {
alert("caller is " + arguments.callee.caller.toString());
}
jest równoważne z tym:
function Hello() {
alert("caller is " + Hello.caller.toString());
}
Oczywiście pierwszy bit jest bardziej przenośny, ponieważ możesz zmienić nazwę funkcji, powiedz z „Hello” na „Ciao”, i nadal wszystko działa.
W tym ostatnim przypadku, jeśli zdecydujesz się na refaktoryzację nazwy wywoływanej funkcji (Hello), będziesz musiał zmienić wszystkie jej wystąpienia :(
Możesz uzyskać pełny stacktrace:
arguments.callee.caller
arguments.callee.caller.caller
arguments.callee.caller.caller.caller
Dopóki dzwoniący nie jest null
.
Uwaga: powoduje nieskończoną pętlę funkcji rekurencyjnych.
Zwykle używam (new Error()).stack
w Chrome. Zaletą jest to, że daje to również numery linii, w których dzwoniący wywołał funkcję. Minusem jest to, że ogranicza długość stosu do 10, dlatego przede wszystkim trafiłem na tę stronę.
(Używam tego do zbierania stosów wywołań w konstruktorze niskiego poziomu podczas wykonywania, do przeglądania i debugowania później, więc ustawienie punktu przerwania nie jest przydatne, ponieważ zostanie uderzony tysiące razy)
'use strict';
jest na miejscu. Podaj mi informacje, których potrzebowałem - dzięki!
Jeśli nie zamierzasz go uruchamiać w IE <11, to powinna pasować console.trace () .
function main() {
Hello();
}
function Hello() {
console.trace()
}
main()
// Hello @ VM261:9
// main @ VM261:4
Możesz użyć Function.Caller, aby uzyskać funkcję wywoływania. Stara metoda wykorzystująca argument.caller jest uważana za przestarzałą.
Poniższy kod ilustruje jego użycie:
function Hello() { return Hello.caller;}
Hello2 = function NamedFunc() { return NamedFunc.caller; };
function main()
{
Hello(); //both return main()
Hello2();
}
Uwagi na temat przestarzałego argumentu. Caller: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments/caller
Pamiętaj, Function.caller jest niestandardowy: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/caller
Cannot access caller property of a strict mode function
Zrobiłbym to:
function Hello() {
console.trace();
}
function Hello() {
alert(Hello.caller);
}
arguments.callee.caller.toString()
Jest bezpieczniejszy w użyciu, *arguments.callee.caller
ponieważ arguments.caller
jest przestarzały ...
arguments.callee
jest również przestarzałe w ES5 i usuwane w trybie ścisłym.
arguments.callee
było złym rozwiązaniem problemu, który został teraz lepiej rozwiązany developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Wygląda na to, że jest to dość rozwiązane pytanie, ale niedawno odkryłem, że callee nie jest dozwolony w „trybie ścisłym”, więc dla własnego użytku napisałem klasę, która dostanie ścieżkę od miejsca, w którym się nazywa. Jest to część małej biblioteki pomocnika i jeśli chcesz użyć samodzielnego kodu, zmień przesunięcie użyte do zwrócenia śladu stosu programu wywołującego (użyj 1 zamiast 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 a(){ function b(){ function c(){ return ScriptPath(); } return c(); } return b(); } a()
w konsoli (nie próbowałem w pliku), ale wydaje się mieć rozsądny pomysł. Powinien być mimo to oceniany za widoczność.
Spróbuj uzyskać dostęp do tego:
arguments.callee.caller.name
Wystarczy, że konsola zarejestruje stos błędów. Możesz wtedy wiedzieć, jak się nazywasz
const hello = () => {
console.log(new Error('I was called').stack)
}
const sello = () => {
hello()
}
sello()
caller
jest zabronione w trybie ścisłym . Oto alternatywa przy użyciu (niestandardowego) Error
stosu .
Poniższa funkcja wydaje się wykonywać tę pracę w Firefoksie 52 i Chrome 61-71, chociaż jej implementacja zawiera wiele założeń dotyczących formatu rejestrowania dwóch przeglądarek i powinna być używana ostrożnie, biorąc pod uwagę, że zgłasza wyjątek i prawdopodobnie wykonuje dwa wyrażenia regularne dopasowania przed zakończeniem.
'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();
}());
Zarówno w trybie ES6, jak i ścisłym, skorzystaj z poniższych poleceń, aby uzyskać funkcję dzwoniącego
console.log((new Error()).stack.split("\n")[2].trim().split(" ")[1])
Należy pamiętać, że powyższa linia wyrzuci wyjątek, jeśli nie ma dzwoniącego lub poprzedni stos. Użyj odpowiednio.
console.log((new Error()).stack.split("\n")[1].trim().split(" ")[1])
Chciałem dodać tutaj moje skrzypce:
http://jsfiddle.net/bladnman/EhUm3/
Testowałem to Chrome, Safari i IE (10 i 8). Działa w porządku. Liczy się tylko jedna funkcja, więc jeśli przestraszy Cię wielkie skrzypce, przeczytaj poniżej.
Uwaga: W tym skrzypce jest sporo mojej własnej „płyty kotłowej”. Możesz to wszystko usunąć i użyć splitów, jeśli chcesz. To po prostu bardzo bezpieczny „zestaw funkcji, na których polegam.
Jest tam również szablon „JSFiddle”, którego używam do wielu skrzypiec, aby po prostu szybko skrzypać.
String.prototype.trim = trim;
Jeśli potrzebujesz tylko nazwy funkcji, a nie kodu i chcesz rozwiązania niezależnego od przeglądarki, skorzystaj z następujących opcji:
var callerFunction = arguments.callee.caller.toString().match(/function ([^\(]+)/)[1];
Zauważ, że powyższe zwróci błąd, jeśli nie ma funkcji wywołującej, ponieważ w tablicy nie ma elementu [1]. Aby obejść ten problem, skorzystaj z poniższych:
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]);
Po prostu chcę poinformować, że w dniu PhoneGap / Androidname
robi wydaje się działać. Ale arguments.callee.caller.toString()
załatwi sprawę.
Tutaj wszystko z wyjątkiem functionname
jest usuwane caller.toString()
z 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>
oto funkcja pozwalająca uzyskać pełny stacktrace :
function stacktrace() {
var f = stacktrace;
var stack = 'Stack trace:';
while (f) {
stack += '\n' + f.name;
f = f.caller;
}
return stack;
}
W odpowiedziach heystewart i JiarongWu wspomniano, że Error
obiekt ma dostęp do stack
.
Oto przykład:
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();
Różne przeglądarki pokazują stos w różnych formatach łańcuchów:
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)
Większość przeglądarek ustawi stos var stack = (new Error()).stack
. W Internet Explorerze stos będzie niezdefiniowany - musisz pobrać prawdziwy wyjątek, aby odzyskać stos.
Wnioski: Jest możliwe określenie „główny” to dzwoniący do „Hello” używając stack
w Error
obiekcie. W rzeczywistości zadziała w przypadkach, w których podejście callee
/ caller
nie działa. Pokaże także kontekst, tzn. Plik źródłowy i numer linii. Konieczne są jednak starania, aby rozwiązanie było wieloplatformowe.
Zauważ, że nie możesz używać Function.caller w Node.js, zamiast tego użyj pakietu identyfikatora rozmówcy . Na przykład:
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
}
*/
}
Wypróbuj następujący kod:
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;
}
Pracował dla mnie w Firefox-21 i Chromium-25.
arguments.callee
jest przestarzałe od wielu lat .
Innym sposobem obejścia tego problemu jest po prostu przekazanie nazwy funkcji wywołującej jako parametru.
Na przykład:
function reformatString(string, callerName) {
if (callerName === "uid") {
string = string.toUpperCase();
}
return string;
}
Teraz możesz wywołać funkcję w ten sposób:
function uid(){
var myString = "apples";
reformatString(myString, function.name);
}
Mój przykład wykorzystuje zakodowane sprawdzenie nazwy funkcji, ale możesz łatwo użyć instrukcji switch lub innej logiki, aby zrobić to, co chcesz.
O ile mi wiadomo, mamy na to dwa sposoby z danych źródeł takich jak ten-
function whoCalled()
{
if (arguments.caller == null)
console.log('I was called from the global scope.');
else
console.log(arguments.caller + ' called me!');
}
function myFunc()
{
if (myFunc.caller == null) {
return 'The function was called from the top!';
}
else
{
return 'This function\'s caller was ' + myFunc.caller;
}
}
Myślę, że masz odpowiedź :).
Dlaczego wszystkie powyższe rozwiązania wyglądają jak rakieta. Tymczasem nie powinien być bardziej skomplikowany niż ten fragment. Wszystkie kredyty dla tego faceta
Jak znaleźć funkcję dzwoniącego w 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)]
Próbuję odpowiedzieć zarówno na pytanie, jak i na obecną nagrodę za pomocą tego pytania.
Nagroda wymaga, aby osoba dzwoniąca została uzyskana w trybie ścisłym , a jedynym sposobem, w jaki mogę to zrobić, jest odwołanie się do funkcji zadeklarowanej poza trybem ścisłym.
Na przykład poniższe elementy są niestandardowe, ale zostały przetestowane z wcześniejszymi (29.03.2016) i aktualnymi (1 sierpnia 2018) wersjami Chrome, Edge i 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();
Jeśli naprawdę potrzebujesz tej funkcji z jakiegoś powodu i chcesz, aby była kompatybilna z różnymi przeglądarkami i nie martwiła się o surowe rzeczy i była kompatybilna z przekazywaniem, to przekaż następujące odniesienie:
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
}
Myślę, że następujący fragment kodu może być pomocny:
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);
}
Wykonaj kod:
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();
Dziennik wygląda następująco:
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