جافاسكريبت - كيف تحصل على اسم الدالة من داخل تلك الدالة؟


كيف يمكنني الوصول إلى اسم دالة من داخل تلك الدالة؟

// parasitic inheritance
var ns.parent.child = function() {
  var parent = new ns.parent();
  parent.newFunc = function() {

  }
  return parent;
}

var ns.parent = function() {
  // at this point, i want to know who the child is that called the parent
  // ie
}

var obj = new ns.parent.child();

الإجابة رقم 1

في ES6، يمكنك فقط استخدام myFunction.name.

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

في ES5، أفضل شيء يمكن فعله هو:

function functionName(fun) {
  var ret = fun.toString();
  ret = ret.substr('function '.length);
  ret = ret.substr(0, ret.indexOf('('));
  return ret;
}

استخدام Function.caller غير معياري. كل من Function.caller وarguments.callee محظوران في الوضع الصارم.

تعديل: إجابة nus المعتمدة على التعبيرات النمطية أدناه تحقق الشيء نفسه، لكنها تتمتع بأداء أفضل!

الإجابة رقم 2

ES6 (مستوحى من إجابة سندي حليم أدناه):

myFunction.name

تفسير على MDN. اعتبارًا من عام 2015 يعمل في Node.js وجميع المتصفحات الرئيسية ما عدا IE.

ملاحظة: على الدوال المربوطة، سيعطي هذا "bound <originalName>". سيتعين عليك إزالة "bound " إذا كنت تريد الحصول على الاسم الأصلي.

ES5 (مستوحى من إجابة فلاد):

إذا كان لديك مرجع إلى الدالة، يمكنك أن تفعل:

function functionName( func )
{
    // Match:
    // - ^          the beginning of the string
    // - function   the word 'function'
    // - \s+        at least some white space
    // - ([\w\$]+)  capture one or more valid JavaScript identifier characters
    // - \s*        optionally followed by white space (in theory there won't be any here,
    //              so if performance is an issue this can be omitted[1]
    // - \(         followed by an opening brace
    //
    var result = /^function\s+([\w\$]+)\s*\(/.exec( func.toString() )

    return  result  ?  result[ 1 ]  :  '' // for an anonymous function there won't be a match
}
  • لم أجرِ اختبارات الوحدة على هذا، أو أتحقق من اختلافات التنفيذ، لكن من حيث المبدأ يجب أن يعمل، وإن لم يعمل اترك تعليقاً.
  • ملاحظة: لن يعمل على الدوال المرتبطة
  • ملاحظة: يُعتبر كل من caller و callee مهملين.

[1] أدرجه هنا لأنه قانوني وغالبًا ما تفشل أدوات تمييز الصياغة في أخذ المسافة البيضاء بين اسم الدالة والأقواس في الاعتبار. من ناحية أخرى، لست على علم بأي تنفيذ لـ .toString() سيشمل الفراغ هنا، لذا يمكنك تجاهله.

كإجابة على السؤال الأصلي، سأترك الوراثة الطفيلية وأتجه نحو بعض أنماط تصميم البرمجة الكائنية التقليدية أكثر. كتبت TidBits.OoJs لكتابة كود البرمجة الكائنية في جافا سكريبت بشكل مريح مع مجموعة ميزات تحاكي C++ (لم تكتمل بعد، لكنها معظمها موجودة).

أرى من التعليقات أنك ترغب في تجنب تمرير المعلومات التي يحتاجها parent إلى الباني الخاص به. يجب أن أعترف بأن أنماط التصميم التقليدية لن تنقذك من ذلك، لأنه يُعتبر عمومًا شيئًا جيدًا أن تجعل تبعياتك واضحة وملزمة.

أود أيضًا أن أقترح الابتعاد عن الدوال المجهولة. فهي تجعل تصحيح الأخطاء وتحليل الأداء أمرًا صعبًا للغاية لأن كل شيء يظهر فقط باسم "دالة مجهولة"، وليس هناك فائدة منها على حد علمي.

الإجابة رقم 3

ما تفعله هو تعيين دالة غير مسماة لمتغير. ربما تحتاج بدلاً من ذلك إلى تعبير دالة مسماة (http://kangax.github.com/nfe/)

var x = function x() {
    console.log( arguments.callee.name );
}
x();

ومع ذلك، لست متأكدًا من مدى توافق ذلك مع المتصفحات المختلفة؛ هناك مشكلة مع IE6 تجعل اسم الدالة يتسرب إلى النطاق الخارجي. أيضًا، arguments.callee أصبح نوعًا ما مهملًا وسيؤدي إلى خطأ إذا كنت تستخدم الوضع الصارم.

الإجابة رقم 4

يبدو كأغبى شيء كتبته في حياتي، لكنه مضحك :D

function getName(d){
  const error = new Error();
  const firefoxMatch = (error.stack.split('\n')[0 + d].match(/^.*(?=@)/) || [])[0];
  const chromeMatch = ((((error.stack.split('at ') || [])[1 + d] || '').match(/(^|\.| <| )(.*[^(<])( \()/) || [])[2] || '').split('.').pop();
  const safariMatch = error.stack.split('\n')[0 + d];

  // firefoxMatch ? console.log('firefoxMatch', firefoxMatch) : void 0;
  // chromeMatch ? console.log('chromeMatch', chromeMatch) : void 0;
  // safariMatch ? console.log('safariMatch', safariMatch) : void 0;
  
  return firefoxMatch || chromeMatch || safariMatch;
}

d - عمق المكدس. 0 - إرجاع اسم هذه الدالة، 1 - الأب، إلخ.;
[0 + d] - فقط للفهم - ما الذي يحدث؛
firefoxMatch - يعمل مع سفاري، لكن لم يكن لدي وقت فعلي للاختبار، لأن صاحب الماك عاد بعد التدخين، وطردني :'(

اختبار :

function limbo(){
  for(let i = 0; i < 4; i++){
    console.log(getName(i));
  }
}
function lust(){
  limbo();
}
function gluttony(){
  lust();
}

gluttony();

النتيجة:
كروم:

فايتفوكس:

تم إنشاء هذا الحل فقط للمتعة! لا تستخدمه للمشاريع الحقيقية. فهو لا يعتمد على مواصفات ES، بل يعتمد فقط على تنفيذ المتصفح. بعد التحديث القادم لجوجل كروم/فايرفوكس/سفاري قد يتوقف عن العمل.
علاوة على ذلك، لا يوجد معالجة للأخطاء (ها) - إذا كانت قيمة d أكبر من طول الكومة - ستحصل على خطأ;
بالنسبة لأنماط رسائل الأخطاء في المتصفحات الأخرى - ستحصل على خطأ;
يجب أن يعمل مع فصول ES6 (.split('.').pop())، ولكن لا يزال بإمكانك الحصول على خطأ;

الإجابة رقم 5

أي constructor يكشف عن خاصية name، والتي هي اسم الدالة. يمكنك الوصول إلى constructor عبر نسخة (باستخدام new) أو عبر prototype:

function Person() {
  console.log(this.constructor.name); //Person
}

var p = new Person();
console.log(p.constructor.name); //Person

console.log(Person.prototype.constructor.name);  //Person

الإجابة رقم 6

قد ينجح هذا معك:

function foo() { bar(); }

function bar() { console.log(bar.caller.name); }

تشغيل foo() سيخرج "foo" أو undefined إذا استدعيت من دالة مجهولة.

يعمل أيضًا مع البُناة، وفي هذه الحالة سيُخرج اسم الباني المستدعي (على سبيل المثال "Foo").

مزيد من المعلومات هنا: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/Caller

يدّعون أنه غير قياسي، ولكن أيضًا أنه مدعوم من جميع المتصفحات الكبرى: فايرفوكس، سفاري، كروم، أوبرا وإنترنت إكسبلورر.

الإجابة رقم 7

يمكنك استخدام هذا، للمتصفحات التي تدعم Error.stack (ليس جميعها بالطبع، على الأرجح)

function WriteSomeShitOut(){ 
  var a = new Error().stack.match(/at (.*?) /);
  console.log(a[1]);
} 
WriteSomeShitOut();

بالطبع هذا مخصص للدالة الحالية، ولكنك فهمت الفكرة.

سعيد يسيل اللعاب أثناء الترميز

الإجابة رقم 8

لا يمكنك ذلك. الدوال ليس لها أسماء وفقًا للمعيار (على الرغم من أن موزيلا لديها مثل هذا السمة) - يمكن إسنادها فقط إلى متغيرات لها أسماء.

أيضًا تعليقك:

// access fully qualified name (ie "my.namespace.myFunc")

يقع داخل الدالة my.namespace.myFunc.getFn

ما يمكنك فعله هو إرجاع الباني لكائن تم إنشاؤه بواسطة new

لذلك يمكنك القول

var obj = new my.namespace.myFunc();
console.info(obj.constructor); //my.namespace.myFunc