function main()
{
Hello();
}
function Hello()
{
// How do you find out the caller function is 'main'?
}
มีวิธีหา call stack หรือไม่?
function main()
{
Hello();
}
function Hello()
{
// How do you find out the caller function is 'main'?
}
มีวิธีหา call stack หรือไม่?
คำตอบ:
function Hello()
{
alert("caller is " + Hello.caller);
}
โปรดทราบว่าคุณลักษณะนี้ไม่ได้มาตรฐานจากFunction.caller
:
ไม่ใช่มาตรฐาน
คุณสมบัตินี้ไม่ได้มาตรฐานและไม่ได้อยู่ในเส้นทางมาตรฐาน ห้ามใช้กับไซต์ที่ผลิตซึ่งหันหน้าไปทางเว็บ: มันจะไม่ทำงานสำหรับผู้ใช้ทุกคน อาจมีความไม่ลงรอยกันระหว่างการใช้งานกับพฤติกรรมที่อาจมีการเปลี่ยนแปลงในอนาคต
ต่อไปนี้เป็นคำตอบเก่าจาก 2008 ซึ่งไม่ได้รับการสนับสนุนใน Javascript สมัยใหม่อีกต่อไป:
function Hello()
{
alert("caller is " + arguments.callee.caller.toString());
}
arguments.callee.caller.name
จะได้รับชื่อฟังก์ชั่น
'use strict';
อาจช่วยได้
arguments
สามารถเข้าถึงได้จากภายในฟังก์ชั่นในโหมดเข้มงวดมันจะโง่ที่จะคัดค้าน ไม่ได้มาจาก function.arguments จากภายนอก นอกจากนี้หากคุณมีอาร์กิวเมนต์ชื่อรูปแบบอาร์กิวเมนต์ [i] ของมันจะไม่ติดตามการเปลี่ยนแปลงที่คุณทำกับรุ่นที่มีชื่อภายในฟังก์ชัน
คุณสามารถค้นหาการติดตามสแต็กทั้งหมดโดยใช้รหัสเฉพาะของเบราว์เซอร์ สิ่งที่ดีคือคนที่ทำแล้วมัน ; นี่คือรหัสโครงการบน 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();
ซึ่งจะแสดงรหัสของฟังก์ชันผู้โทร น่าเศร้าที่ไม่เพียงพอสำหรับฉันและนั่นคือเหตุผลที่ฉันให้คำแนะนำสำหรับ StackTrace และชื่อฟังก์ชันผู้โทร (แม้ว่าพวกเขาจะไม่ข้ามเบราว์เซอร์)
Function.caller
จะไม่ทำงานในโหมดเข้มงวดอย่างไรก็ตาม
ฉันรู้ว่าคุณพูดถึง "ใน Javascript" แต่ถ้าจุดประสงค์คือการดีบั๊กฉันคิดว่าการใช้เครื่องมือสำหรับนักพัฒนาของเบราว์เซอร์นั้นง่ายกว่า นี่คือลักษณะที่ปรากฏใน Chrome: เพียงแค่วางดีบักเกอร์ที่คุณต้องการตรวจสอบสแต็ค
เพื่อสรุป (และทำให้ชัดเจนขึ้น) ...
รหัสนี้:
function Hello() {
alert("caller is " + arguments.callee.caller.toString());
}
เทียบเท่ากับสิ่งนี้:
function Hello() {
alert("caller is " + Hello.caller.toString());
}
เห็นได้ชัดว่าบิตแรกนั้นพกพาได้มากกว่าเนื่องจากคุณสามารถเปลี่ยนชื่อของฟังก์ชั่นพูดจาก "Hello" เป็น "Ciao" และยังคงได้ผลทั้งหมด
ในกรณีหลังในกรณีที่คุณตัดสินใจ refactor ชื่อฟังก์ชั่นที่เรียกใช้ (Hello) คุณจะต้องเปลี่ยนการเกิดขึ้นทั้งหมด :(
คุณจะได้รับ stacktrace เต็มรูปแบบ:
arguments.callee.caller
arguments.callee.caller.caller
arguments.callee.caller.caller.caller
null
จนกระทั่งโทรคือ
หมายเหตุ: มันทำให้เกิดการวนซ้ำไม่สิ้นสุดในฟังก์ชั่นวนซ้ำ
ฉันมักจะใช้(new Error()).stack
ใน Chrome สิ่งที่ดีคือสิ่งนี้ยังช่วยให้คุณมีหมายเลขบรรทัดที่ผู้โทรเรียกฟังก์ชั่น ข้อเสียคือมันจำกัดความยาวของสแต็คไว้ที่ 10 ซึ่งเป็นสาเหตุที่ฉันมาที่หน้านี้ตั้งแต่แรก
(ฉันใช้สิ่งนี้เพื่อรวบรวม callstacks ในตัวสร้างระดับต่ำในระหว่างการดำเนินการเพื่อดูและดีบักในภายหลังดังนั้นการตั้งค่าเบรกพอยต์ไม่ได้ใช้เนื่องจากจะมีการเข้าชมนับพันครั้ง)
'use strict';
อยู่ในที่ ให้ข้อมูลที่ฉันต้องการ - ขอบคุณ!
หากคุณไม่ต้องการรันใน IE <11 ดังนั้นconsole.trace ()จะเหมาะกับคุณ
function main() {
Hello();
}
function Hello() {
console.trace()
}
main()
// Hello @ VM261:9
// main @ VM261:4
คุณสามารถใช้ Function.Caller เพื่อรับฟังก์ชั่นการโทร วิธีการเก่าที่ใช้ argument.caller ถือว่าล้าสมัย
รหัสต่อไปนี้แสดงให้เห็นถึงการใช้งาน:
function Hello() { return Hello.caller;}
Hello2 = function NamedFunc() { return NamedFunc.caller; };
function main()
{
Hello(); //both return main()
Hello2();
}
หมายเหตุเกี่ยวกับ argument.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
Cannot access caller property of a strict mode function
ฉันจะทำสิ่งนี้:
function Hello() {
console.trace();
}
function Hello() {
alert(Hello.caller);
}
arguments.callee.caller.toString()
มันปลอดภัยกว่าที่จะใช้ *arguments.callee.caller
ตั้งแต่arguments.caller
ถูกเลิก ...
arguments.callee
เลิกใช้แล้วใน ES5 และถูกลบในโหมดเข้มงวด
arguments.callee
เป็นวิธีการแก้ปัญหาที่ดีที่จะมีปัญหาว่าขณะนี้ได้รับการแก้ไขที่ดีกว่าdeveloper.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
ดูเหมือนว่านี่เป็นคำถามที่แก้ไขได้ แต่เมื่อเร็ว ๆ นี้ฉันพบว่าผู้ไม่ได้รับอนุญาตใน 'โหมดเข้มงวด'ดังนั้นสำหรับการใช้งานของฉันเองฉันได้เขียนคลาสที่จะได้รับเส้นทางจากที่ที่มันถูกเรียก มันเป็นส่วนหนึ่งของตัวช่วยเล็ก ๆ libและถ้าคุณต้องการใช้รหัสแบบสแตนด์อโลนเปลี่ยนออฟเซ็ตที่ใช้เพื่อส่งกลับสแต็กการติดตามของผู้เรียก (ใช้ 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 a(){ function b(){ function c(){ return ScriptPath(); } return c(); } return b(); } a()
ในคอนโซล (ยังไม่ได้ลองเป็นไฟล์) แต่ดูเหมือนจะมีความคิดที่สมเหตุสมผล ควร upvoted อยู่ดีสำหรับการมองเห็น
เพียงคอนโซลบันทึกข้อผิดพลาดสแต็คของคุณ จากนั้นคุณสามารถรู้ได้ว่าคุณถูกเรียกว่าอย่างไร
const hello = () => {
console.log(new Error('I was called').stack)
}
const sello = () => {
hello()
}
sello()
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();
}());
ในทั้ง ES6 และโหมดเข้มงวดให้ใช้สิ่งต่อไปนี้เพื่อรับฟังก์ชั่นผู้โทร
console.log((new Error()).stack.split("\n")[2].trim().split(" ")[1])
โปรดทราบว่าบรรทัดข้างต้นจะทำให้เกิดข้อยกเว้นหากไม่มีผู้โทรหรือไม่มีสแต็กก่อนหน้า ใช้อย่างเหมาะสม
console.log((new Error()).stack.split("\n")[1].trim().split(" ")[1])
ฉันต้องการเพิ่มซอของฉันที่นี่สำหรับสิ่งนี้:
http://jsfiddle.net/bladnman/EhUm3/
ฉันทดสอบนี่คือโครเมียมซาฟารีและ IE (10 และ 8) ทำงานได้ดี มีเพียงฟังก์ชันเดียวที่สำคัญดังนั้นหากคุณกลัวซอตัวใหญ่อ่านด้านล่าง
หมายเหตุ: มีจำนวนพอสมควร "แผ่นเหล็ก" ของฉันเองในซอนี้ คุณสามารถลบสิ่งเหล่านั้นออกทั้งหมดและใช้ตัวแยกหากต้องการ มันเป็นแค่ชุดฟังก์ชั่นที่ปลอดภัยเป็นพิเศษที่ฉันเชื่อถือได้
นอกจากนี้ยังมีเทมเพลต "JSFiddle" ในนั้นที่ฉันใช้สำหรับซอจำนวนมากที่จะเล่นซออย่างรวดเร็ว
String.prototype.trim = trim;
หากคุณต้องการชื่อฟังก์ชั่นไม่ใช่รหัสและต้องการโซลูชันที่ไม่ขึ้นกับเบราว์เซอร์ให้ใช้ดังต่อไปนี้:
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 / Androidname
ไม่ได้ดูเหมือนจะทำงาน แต่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>
นี่คือฟังก์ชั่นเพื่อรับ stacktrace เต็มรูปแบบ :
function stacktrace() {
var f = stacktrace;
var stack = 'Stack trace:';
while (f) {
stack += '\n' + f.name;
f = f.caller;
}
return stack;
}
คำตอบ 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" โดยใช้ stack
ในError
วัตถุ ในความเป็นจริงมันจะทำงานในกรณีที่callee
/ caller
วิธีไม่ทำงาน มันจะแสดงบริบทเช่นไฟล์ต้นฉบับและหมายเลขบรรทัด อย่างไรก็ตามต้องใช้ความพยายามในการสร้างโซลูชันข้ามแพลตฟอร์ม
หมายเหตุคุณไม่สามารถใช้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
}
*/
}
ลองรหัสต่อไปนี้:
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 หรือตรรกะอื่น ๆ เพื่อทำสิ่งที่คุณต้องการได้อย่างง่ายดาย
เท่าที่ฉันรู้เรามี 2 ทางสำหรับสิ่งนี้จากแหล่งที่มาเช่นนี้ -
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;
}
}
คิดว่าคุณมีคำตอบของคุณ :)
เหตุใดการแก้ปัญหาทั้งหมดข้างต้นจึงดูเหมือนวิทยาศาสตร์จรวด ในขณะเดียวกันก็ไม่ควรซับซ้อนกว่าตัวอย่างนี้ เครดิตทั้งหมดให้กับผู้ชายคนนี้
คุณหาฟังก์ชั่นผู้โทรใน 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