كيف يمكنك معرفة وظيفة المتصل في جافا سكريبت؟

StackOverflow https://stackoverflow.com/questions/280389

  •  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، والتي لم تعد مدعومة في جافا سكريبت الحديثة:

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

نصائح أخرى

تتبع المكدس

يمكنك العثور على تتبع المكدس بالكامل باستخدام كود خاص بالمتصفح.والشيء الجيد هو شخص ما فعل ذلك بالفعل;هنا هو رمز المشروع على جيثب.

لكن ليست كل الأخبار جيدة:

  1. إن الحصول على تتبع المكدس بطيء جدًا لذا كن حذرًا (اقرأ هذا للمزيد من).

  2. سوف تحتاج إلى تحديد أسماء الوظائف حتى يكون تتبع المكدس مقروءًا.لأنه إذا كان لديك رمز مثل هذا:

    var Klass = function kls() {
       this.Hello = function() { alert(printStackTrace().join('\n\n')); };
    }
    new Klass().Hello();
    

    سوف ينبه جوجل كروم ... 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());
}

ومن الواضح أن الشيء الأول هو أكثر المحمولة، حيث يمكنك تغيير اسم وظيفة، ويقول من "مرحبا" إلى "تشاو"، ولا يزال الحصول على كل شيء في العمل.

في هذا الأخير، في حالة ما إذا قررت ريفاكتور اسم وظيفة الاحتجاج (مرحبا)، قد تضطر إلى تغيير كافة تواجدات لها :(

ويمكنك الحصول على تتبع مكدس الذاكرة المؤقتة الكامل:

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

وحتى المتصل null.

ملحوظة: أن يسبب حلقة لا نهائية على وظائف متكررة

.

وأنا أعلم أنك ذكرت "في جافا سكريبت"، ولكن إذا كان الغرض من ذلك هو تصحيح الأخطاء، وأعتقد أنه من الأسهل لمجرد استخدام أدوات المطورين المتصفح. هذه هي الطريقة التي تبدو في كروم: مجرد قطرة المصحح حيث تريد التحقيق في كومة.

وعادة ما تستخدم (new Error()).stack في كروم. والشيء الجميل هو أن هذا يمنحك أيضا أرقام الأسطر حيث دعا المتصل وظيفة. الجانب السلبي هو أنه يحد من طول كومة إلى 10، وهذا هو السبب جئت إلى هذه الصفحة في المقام الأول.

و(أنا باستخدام هذا لجمع callstacks في منشئ على مستوى منخفض أثناء التنفيذ، لعرض والتصحيح في وقت لاحق، لذلك وضع نقطة توقف ليس الاستخدام نظرا لأنه سيتم ضرب آلاف المرات)

إذا كنت لا تنوي تشغيلها في IE <11 ثم <وأ href = "https://developer.mozilla.org/en-US/docs/Web/API/Console/trace" يختلط = "noreferrer" > 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: // المطور. mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments/caller

كن على علم Function.caller هو غير القياسية: الشبكي: / /developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/caller

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

إنه أكثر أمانًا للاستخدام *arguments.callee.caller منذ arguments.caller يكون إهمال...

ويبدو أن هذا هو تماما مسألة حلها ولكني اكتشفت مؤخرا أن <لأ href = "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/arguments/callee" > لا يسمح المستدعى في "وضع صارم" حتى للاستخدام بلدي كتبت الفئة التي سوف تحصل على المسار من حيث يطلق عليه. انها جزء من مساعد صغير ليب و إذا كنت ترغب في استخدام مستقل كود تغيير الإزاحة المستخدمة للعودة تتبع المكدس المتصل (استخدام 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/

واختبرت هذا هو الكروم، رحلات السفاري وIE (10 و 8). يعمل بشكل جيد. هناك فقط 1 وظيفة ما يهم، حتى إذا كنت تشعر بالخوف من قبل كمان كبير، وقراءة أدناه.

ملحوظة: هناك قدر لا بأس به من بلدي "النمطي" في هذا كمان. يمكنك إزالة كل ذلك واستخدام لتقسيم إذا أردت. انها مجرد "مجموعة فائقة آمنة وظائف جئت إلى الاعتماد عليها.

وهناك أيضا "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]);

فقط أريد أن أخبرك بذلك فون جاب/أندرويد ال name لا يبدو أنه يعمل.لكن arguments.callee.caller.toString() سوف تفعل الخدعة.

وهنا، يتم تجريد كل شيء ولكن functionname من caller.toString()، مع التعبير العادي.

<!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;
}

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 سيتم غير معرفة مكدس - لديك لرمي استثناء الحقيقي لاسترداد كومة

.

والخلاصة: من الممكن لتحديد "الرئيسي" هو المتصل ب "مرحبا" باستخدام 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;
}

وعملت بالنسبة لي في فايرفوكس 21 والكروم 25.

وهناك طريقة أخرى للتغلب على هذه المشكلة هو ببساطة تمرير اسم استدعاء الدالة كمعلمة.

وعلى سبيل المثال:

function reformatString(string, callerName) {

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

    return string;
}

والآن، هل يمكن استدعاء الدالة كما يلي:

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

    reformatString(myString, function.name);
}

ويستخدم بلدي سبيل المثال الاختيار الثابت ترميز اسم وظيفة، ولكن يمكن بسهولة استخدام عبارة التبديل أو بعض المنطق الآخر لتفعل ما تريد هناك.

بقدر ما أعرف، لدينا طريقتان لذلك من مصادر معينة مثل هذا-

  1. الوسيطات.المتصل

    function whoCalled()
    {
        if (arguments.caller == null)
           console.log('I was called from the global scope.');
        else
           console.log(arguments.caller + ' called me!');
    }
    
  2. وظيفة.المتصل

    function myFunc()
    {
       if (myFunc.caller == null) {
          return 'The function was called from the top!';
       }
       else
       {
          return 'This function\'s caller was ' + myFunc.caller;
        }
    }
    

أعتقد أن لديك إجابتك :).

لماذا كل من الحلول المذكورة أعلاه تبدو وكأنها علم الصواريخ. وفي الوقت نفسه، لا ينبغي أن يكون أكثر تعقيدا من هذا المقتطف. كل اعتمادات لهذا الرجل

كيف يمكنك معرفة وظيفة المتصل في جافا سكريبت؟

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) والحالية (1ST أغسطس 2018) من الكروم، والحافة وفايرفوكس.

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

وكما أيا من الأجوبة السابقة يعمل مثل ما كنت تبحث عن (مجرد الحصول على آخر وظيفة المتصل يست وظيفة كسلسلة أو callstack) I منصبي الحل هنا لأولئك الذين هم مثلي، ونأمل أن تعمل لصالحهم:

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