คุณหาฟังก์ชั่นผู้โทรใน JavaScript ได้อย่างไร?


865
function main()
{
   Hello();
}

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

มีวิธีหา call stack หรือไม่?


63
ฉันหวังว่านี่เป็นเพียงเพื่อช่วยคุณในการแก้ไขข้อบกพร่อง พฤติกรรมแปรผันตามผู้โทรเป็นความคิดที่ไม่ดี
OJ

สิ่งนี้จะเป็นประโยชน์สำหรับการดีบักเมื่อใด
Anderson Green

33
@AndersonGreen เมื่อคุณได้รับเช่นวิธีการแสดงผลแม่แบบเริ่มต้นและดูว่ามันถูกเรียกสองครั้ง แทนที่จะทำการรวมกับ LoC 1000s หรือการก้าวผ่านตัวดีบักคุณจะเห็นว่าสแต็คนั้นคืออะไรในเวลานั้น
tkone

28
เพื่อดูการติดตามสแต็กใช้ console.trace () สำหรับโครเมี่ยม ไม่ทราบเกี่ยวกับผู้อื่น
lukas.pukenis

5
ทำไมนี่เป็นความคิดที่ไม่ดี
Jacob Schneider

คำตอบ:


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

โปรดทราบว่าคุณลักษณะนี้ไม่ได้มาตรฐานจากFunction.caller:

ไม่ใช่มาตรฐาน
คุณสมบัตินี้ไม่ได้มาตรฐานและไม่ได้อยู่ในเส้นทางมาตรฐาน ห้ามใช้กับไซต์ที่ผลิตซึ่งหันหน้าไปทางเว็บ: มันจะไม่ทำงานสำหรับผู้ใช้ทุกคน อาจมีความไม่ลงรอยกันระหว่างการใช้งานกับพฤติกรรมที่อาจมีการเปลี่ยนแปลงในอนาคต


ต่อไปนี้เป็นคำตอบเก่าจาก 2008 ซึ่งไม่ได้รับการสนับสนุนใน Javascript สมัยใหม่อีกต่อไป:

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

254
arguments.callee.caller.nameจะได้รับชื่อฟังก์ชั่น
Rocket Hazmat

137
"'ผู้เรียก', 'callee' และคุณสมบัติ 'อาร์กิวเมนต์' อาจไม่สามารถเข้าถึงได้ในฟังก์ชั่นโหมดเข้มงวดหรือวัตถุอาร์กิวเมนต์สำหรับการโทรหาพวกเขา" - พวกเขาเลิกใช้งานใน ES5 และลบออกในโหมดเข้มงวด
ThatGuy

12
มันจะใช้งานได้หากคุณไม่ได้ใช้โหมดเข้มงวด ดังนั้นการลบ'use strict';อาจช่วยได้
pvorb

23
argumentsสามารถเข้าถึงได้จากภายในฟังก์ชั่นในโหมดเข้มงวดมันจะโง่ที่จะคัดค้าน ไม่ได้มาจาก function.arguments จากภายนอก นอกจากนี้หากคุณมีอาร์กิวเมนต์ชื่อรูปแบบอาร์กิวเมนต์ [i] ของมันจะไม่ติดตามการเปลี่ยนแปลงที่คุณทำกับรุ่นที่มีชื่อภายในฟังก์ชัน
rvr_jon

41
วิธีนี้ล้าสมัยเนื่องจากโพสต์นี้มีการระบุไว้ในปี 2011 วิธีที่ต้องการคือตอนนี้ Function.caller (ตั้งแต่ปี 2015)
เกร็ก

152

StackTrace

คุณสามารถค้นหาการติดตามสแต็กทั้งหมดโดยใช้รหัสเฉพาะของเบราว์เซอร์ สิ่งที่ดีคือคนที่ทำแล้วมัน ; นี่คือรหัสโครงการบน 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) ที่จะได้รับมากกว่านั้นโดยไม่ได้รับรหัสฟังก์ชั่นทั้งหมด


รหัสฟังก์ชั่นผู้โทร

และสรุปคำตอบที่ดีที่สุดที่เหลือ (โดย Pablo Cabrera, nourdine และ Greg Hewgill) ข้ามเบราว์เซอร์เท่านั้นและสิ่งที่ปลอดภัยจริงๆที่คุณสามารถใช้ได้คือ:

arguments.callee.caller.toString();

ซึ่งจะแสดงรหัสของฟังก์ชันผู้โทร น่าเศร้าที่ไม่เพียงพอสำหรับฉันและนั่นคือเหตุผลที่ฉันให้คำแนะนำสำหรับ StackTrace และชื่อฟังก์ชันผู้โทร (แม้ว่าพวกเขาจะไม่ข้ามเบราว์เซอร์)


1
บางทีคุณควรเพิ่มคำตอบของ @ GregFunction.callerต่อ
Zach Lysobey

Function.callerจะไม่ทำงานในโหมดเข้มงวดอย่างไรก็ตาม
Rickard Elimää

54

ฉันรู้ว่าคุณพูดถึง "ใน Javascript" แต่ถ้าจุดประสงค์คือการดีบั๊กฉันคิดว่าการใช้เครื่องมือสำหรับนักพัฒนาของเบราว์เซอร์นั้นง่ายกว่า นี่คือลักษณะที่ปรากฏใน Chrome: ป้อนคำอธิบายรูปภาพที่นี่ เพียงแค่วางดีบักเกอร์ที่คุณต้องการตรวจสอบสแต็ค


3
นี่เป็นคำถามเก่า ... แต่นี่เป็นวิธีที่ถูกต้องที่สุดในการทำสิ่งนี้ในปัจจุบัน
markstewie

53

เพื่อสรุป (และทำให้ชัดเจนขึ้น) ...

รหัสนี้:

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

เทียบเท่ากับสิ่งนี้:

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

เห็นได้ชัดว่าบิตแรกนั้นพกพาได้มากกว่าเนื่องจากคุณสามารถเปลี่ยนชื่อของฟังก์ชั่นพูดจาก "Hello" เป็น "Ciao" และยังคงได้ผลทั้งหมด

ในกรณีหลังในกรณีที่คุณตัดสินใจ refactor ชื่อฟังก์ชั่นที่เรียกใช้ (Hello) คุณจะต้องเปลี่ยนการเกิดขึ้นทั้งหมด :(


7
arguments.callee.caller null ใน Chrome 25.0.1364.5 dev
เสมอ

53

คุณจะได้รับ stacktrace เต็มรูปแบบ:

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

nullจนกระทั่งโทรคือ

หมายเหตุ: มันทำให้เกิดการวนซ้ำไม่สิ้นสุดในฟังก์ชั่นวนซ้ำ


2
ขออภัยที่ตอบช้า แต่ฉันไม่เคยเห็นความคิดเห็นของคุณมาก่อน เฉพาะกรณีเรียกซ้ำไม่ทำงานในกรณีอื่น ๆ ควรใช้งานได้
ale5000

45

ฉันมักจะใช้(new Error()).stackใน Chrome สิ่งที่ดีคือสิ่งนี้ยังช่วยให้คุณมีหมายเลขบรรทัดที่ผู้โทรเรียกฟังก์ชั่น ข้อเสียคือมันจำกัดความยาวของสแต็คไว้ที่ 10 ซึ่งเป็นสาเหตุที่ฉันมาที่หน้านี้ตั้งแต่แรก

(ฉันใช้สิ่งนี้เพื่อรวบรวม callstacks ในตัวสร้างระดับต่ำในระหว่างการดำเนินการเพื่อดูและดีบักในภายหลังดังนั้นการตั้งค่าเบรกพอยต์ไม่ได้ใช้เนื่องจากจะมีการเข้าชมนับพันครั้ง)


คุณช่วยเพิ่มคำอธิบายเพิ่มเติมเล็กน้อยเกี่ยวกับคำอธิบายที่คุณให้ได้ไหม
abarisone

6
นี่เป็นสิ่งเดียวที่ฉันสามารถไปทำงานได้เมื่อ'use strict';อยู่ในที่ ให้ข้อมูลที่ฉันต้องการ - ขอบคุณ!
Jeremy Harris

4
เกี่ยวกับขีดจำกัดความยาวของสแต็ค ... คุณสามารถเปลี่ยนได้ด้วย "Error.stackTraceLimit = Infinity"
ทอม

(ข้อผิดพลาดใหม่ ("StackLog")). stack.split ("\ n") ทำให้การอ่านดีขึ้น
Teoman shipahi

36

หากคุณไม่ต้องการรันใน IE <11 ดังนั้นconsole.trace ()จะเหมาะกับคุณ

function main() {
    Hello();
}

function Hello() {
    console.trace()
}

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

มันใช้งานได้! ควรเพิ่มคะแนนโหวตมากขึ้น
Krunal Panchal

22

คุณสามารถใช้ 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


1
นี่คือคำตอบที่ถูกต้องในวันนี้ คุณไม่สามารถทำข้อโต้แย้ง. caller.callee อีกต่อไป หวังว่าเราจะได้รับสิ่งนี้ย้ายไปด้านบนเนื่องจากสิ่งอื่น ๆ ทั้งหมดที่ล้าสมัยในขณะนี้
coblr

4
ดูเหมือนว่าเป็นไปไม่ได้ในโหมดเข้มงวด? Cannot access caller property of a strict mode function
Zach Lysobey

Function.caller ไม่ทำงานสำหรับฉันในโหมดเข้มงวดเช่นกัน นอกจากนี้ตาม MDN , function.caller ไม่ได้มาตรฐานและไม่ควรใช้ในการผลิต มันอาจใช้งานได้สำหรับการดีบั๊ก
jkdev

ฉันไม่มีปัญหากับการที่ไม่ได้มาตรฐานถ้ามันทำงานในโหนด แต่มันก็ไม่ได้รับอนุญาตในโหมดเข้มงวด (ฉันทดสอบบนโหนด 6.10) เช่นเดียวกับ 'ข้อโต้แย้ง' ฉันได้รับข้อความแสดงข้อผิดพลาด: '' 'ผู้โทร' และ 'อาร์กิวเมนต์' เป็นคุณสมบัติของฟังก์ชันที่ จำกัด และไม่สามารถเข้าถึงได้ในบริบทนี้ "
Tom

21

ฉันจะทำสิ่งนี้:

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

มันใช้งานได้ดีมาก! ควรยอมรับว่าเป็นคำตอบที่ถูกต้องเพราะวิธีอื่น ๆ แก่แล้ว \ ไม่ทำงานอีกต่อไป
Yuval Pruss

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

1
และสำหรับชื่อฟังก์ชันให้ใช้ Hello.caller.name
vanval

เช่นเดียวกับarguments.callee.caller.toString()
user2720864

นี่ควรเป็นคำตอบที่ถูกต้องอย่างน้อยปี 2016
Daniel

สิ่งนี้ไม่ได้อยู่บนแทร็กมาตรฐาน แต่จะทำงานในฐานะของ ECMAScript 5
โอบิน่านาวะควี


18

มันปลอดภัยกว่าที่จะใช้ *arguments.callee.callerตั้งแต่arguments.callerถูกเลิก ...


36
arguments.calleeเลิกใช้แล้วใน ES5 และถูกลบในโหมดเข้มงวด
nyuszika7h

2
มีทางเลือกอื่นหรือไม่? แก้ไข: arguments.calleeเป็นวิธีการแก้ปัญหาที่ดีที่จะมีปัญหาว่าขณะนี้ได้รับการแก้ไขที่ดีกว่าdeveloper.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
แมทธิว

16

ดูเหมือนว่านี่เป็นคำถามที่แก้ไขได้ แต่เมื่อเร็ว ๆ นี้ฉันพบว่าผู้ไม่ได้รับอนุญาตใน 'โหมดเข้มงวด'ดังนั้นสำหรับการใช้งานของฉันเองฉันได้เขียนคลาสที่จะได้รับเส้นทางจากที่ที่มันถูกเรียก มันเป็นส่วนหนึ่งของตัวช่วยเล็ก ๆ 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 อยู่ดีสำหรับการมองเห็น
ninjagecko

ความคิดที่ดีมาก ฉันกำลังวิเคราะห์คำแตกต่างกัน แต่ในแอพ nw.js นี่เป็นความคิดเดียวที่ให้สิ่งที่ฉันกำลังมองหา
Andrew Grothe

โปรดระบุตัวอย่างวิธีเรียกใช้ฟังก์ชันนี้
pd_au


11

เพียงคอนโซลบันทึกข้อผิดพลาดสแต็คของคุณ จากนั้นคุณสามารถรู้ได้ว่าคุณถูกเรียกว่าอย่างไร

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

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

sello()


10

อัพเดต 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 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();
}());


4
มันเหลือเชื่อและน่ากลัวมาก
เอียน

ฉันได้รับ "ฟูเรียกด้วย: สวัสดีที่นั่น" แต่ฟูไม่ได้เรียกด้วย "สวัสดีนั่น" บันทึกถูกเรียกด้วย "สวัสดีนั่น"
AndrewR

ขวามี "ตัวแก้ไขที่วางผิดที่" ในไวยากรณ์ของข้อความแสดงข้อผิดพลาด มันหมายถึงการพูดว่า "บันทึกถูกเรียกจากฟังก์ชั่น f มันต้องการข้อความที่พิมพ์ X" แต่ในทางที่สั้นที่สุด
Rovanion

9

ในทั้ง ES6 และโหมดเข้มงวดให้ใช้สิ่งต่อไปนี้เพื่อรับฟังก์ชั่นผู้โทร

console.log((new Error()).stack.split("\n")[2].trim().split(" ")[1])

โปรดทราบว่าบรรทัดข้างต้นจะทำให้เกิดข้อยกเว้นหากไม่มีผู้โทรหรือไม่มีสแต็กก่อนหน้า ใช้อย่างเหมาะสม


ในการรับ callee (ชื่อฟังก์ชั่นปัจจุบัน) ให้ใช้: console.log((new Error()).stack.split("\n")[1].trim().split(" ")[1])
VanagaS

7

ฉันต้องการเพิ่มซอของฉันที่นี่สำหรับสิ่งนี้:

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

ฉันทดสอบนี่คือโครเมียมซาฟารีและ IE (10 และ 8) ทำงานได้ดี มีเพียงฟังก์ชันเดียวที่สำคัญดังนั้นหากคุณกลัวซอตัวใหญ่อ่านด้านล่าง

หมายเหตุ: มีจำนวนพอสมควร "แผ่นเหล็ก" ของฉันเองในซอนี้ คุณสามารถลบสิ่งเหล่านั้นออกทั้งหมดและใช้ตัวแยกหากต้องการ มันเป็นแค่ชุดฟังก์ชั่นที่ปลอดภัยเป็นพิเศษที่ฉันเชื่อถือได้

นอกจากนี้ยังมีเทมเพลต "JSFiddle" ในนั้นที่ฉันใช้สำหรับซอจำนวนมากที่จะเล่นซออย่างรวดเร็ว


ฉันสงสัยว่าคุณสามารถเพิ่ม "ผู้ช่วย" เป็นส่วนขยายสำหรับต้นแบบในบางกรณีตัวอย่างเช่น:String.prototype.trim = trim;
ออทิสติก

6

หากคุณต้องการชื่อฟังก์ชั่นไม่ใช่รหัสและต้องการโซลูชันที่ไม่ขึ้นกับเบราว์เซอร์ให้ใช้ดังต่อไปนี้:

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


5

เพียงแค่ต้องการให้คุณทราบว่าเมื่อPhoneGap / Androidnameไม่ได้ดูเหมือนจะทำงาน แต่arguments.callee.caller.toString()จะทำเคล็ดลับ


4

ที่นี่ทุกสิ่งทุกอย่าง แต่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>

สิ่งนี้ยังคงส่งคืนการประกาศวิธีการทั้งหมด
Maslow

4

นี่คือฟังก์ชั่นเพื่อรับ stacktrace เต็มรูปแบบ :

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

3

คำตอบ heystewart ของและคำตอบของ JiarongWuกล่าวถึงทั้งสองว่าวัตถุที่มีการเข้าถึงErrorstack

นี่คือตัวอย่าง:

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วิธีไม่ทำงาน มันจะแสดงบริบทเช่นไฟล์ต้นฉบับและหมายเลขบรรทัด อย่างไรก็ตามต้องใช้ความพยายามในการสร้างโซลูชันข้ามแพลตฟอร์ม


2

หมายเหตุคุณไม่สามารถใช้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
    }
    */
}

1

ลองรหัสต่อไปนี้:

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


ลองใช้ฟังก์ชันการเรียกซ้ำ
daniel1426

arguments.calleeได้รับการเลิกใช้มานานหลายปี
Dan Dascalescu

1

อีกวิธีหนึ่งในการแก้ไขปัญหานี้คือเพียงส่งชื่อฟังก์ชั่นการโทรเป็นพารามิเตอร์

ตัวอย่างเช่น:

function reformatString(string, callerName) {

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

    return string;
}

ตอนนี้คุณสามารถเรียกใช้ฟังก์ชันดังนี้:

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

    reformatString(myString, function.name);
}

ตัวอย่างของฉันใช้การตรวจสอบอย่างหนักของชื่อฟังก์ชั่น แต่คุณสามารถใช้คำสั่ง switch หรือตรรกะอื่น ๆ เพื่อทำสิ่งที่คุณต้องการได้อย่างง่ายดาย


ฉันเชื่อว่านี่จะช่วยแก้ปัญหาความเข้ากันได้ของเบราว์เซอร์ข้ามส่วนใหญ่ แต่โปรดทดสอบก่อนที่จะสมมติว่าเป็นจริง! ( เริ่มที่จะเหงื่อ )
GrayedFox

1

เท่าที่ฉันรู้เรามี 2 ทางสำหรับสิ่งนี้จากแหล่งที่มาเช่นนี้ -

  1. arguments.caller

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

คิดว่าคุณมีคำตอบของคุณ :)


สิ่งนี้เลิกใช้มาหลายปีแล้วและ Function.caller ไม่ทำงานในโหมดเข้มงวด
Dan Dascalescu

1

เหตุใดการแก้ปัญหาทั้งหมดข้างต้นจึงดูเหมือนวิทยาศาสตร์จรวด ในขณะเดียวกันก็ไม่ควรซับซ้อนกว่าตัวอย่างนี้ เครดิตทั้งหมดให้กับผู้ชายคนนี้

คุณหาฟังก์ชั่นผู้โทรใน 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)]

3
นี่คือสิ่งที่ฉันได้รับเมื่อใช้สิ่งนี้: TypeError: คุณสมบัติ 'ผู้โทร', 'callee' และ 'อาร์กิวเมนต์' อาจไม่สามารถเข้าถึงได้ในฟังก์ชั่นโหมดเข้มงวดหรือวัตถุอาร์กิวเมนต์สำหรับการโทรหาพวกเขา ความคิดใด ๆ วิธีการทำงานในโหมดเข้มงวด?
hard_working_ant

1

ฉันพยายามที่จะตอบทั้งคำถามและความโปรดปรานปัจจุบันกับคำถามนี้

เงินรางวัลต้องการให้ผู้โทรได้รับในโหมดเข้มงวดและวิธีเดียวที่ฉันสามารถเห็นสิ่งนี้ทำได้คือการอ้างถึงฟังก์ชั่นที่ประกาศภายนอกโหมดเข้มงวด

ตัวอย่างเช่นต่อไปนี้เป็นแบบไม่ได้มาตรฐาน แต่ได้รับการทดสอบกับเวอร์ชันก่อนหน้า (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();


แฮ็คที่ดี แต่จะไม่ทำงานสำหรับโมดูล ES5 ซึ่งอยู่ในโหมดเข้มงวดทั้งหมด
Dan Dascalescu

0

หากคุณต้องการฟังก์ชั่นการใช้งานจริงด้วยเหตุผลบางอย่างและต้องการให้สามารถทำงานร่วมกับเบราว์เซอร์ข้ามเบราว์เซอร์ได้และไม่ต้องกังวลกับสิ่งที่เข้มงวด

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
}

0

ฉันคิดว่ารหัสชิ้นต่อไปนี้อาจมีประโยชน์:

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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.