Як отримати ім’я функції з цієї функції?


263

Як я можу отримати доступ до імені функції зсередини цієї функції?

// 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();

добре, що в батьківській програмі я тоді можу отримати доступ до інших функцій за домовленістю, наприклад, ns [child] [схема] або ns [child] [dbService]. Без цього мені доводиться жорстко кодувати ці посилання в кожному дитячому класі.
Скотт Кларенбах

чому б просто не передати дитині функцію як аргумент батькові? var parent = новий ns.parent (це);
plodder

бо є десятки таких пошуків, і десятки дітей. Наразі це я і роблю, але це щоразу те саме і було б ідеально, якби цю логіку-дублікат можна було просто помістити в батьківську програму один раз, виходячи з похідної функції.
Скотт Кларенбах

бачте, це не дочірня функція, яку я хочу, це використовувана конвенція іменування, оскільки ця конвенція іменування може використовуватися для завантаження інших функцій, які наразі не визначені в дочірньому об'єкті, але пов'язані з цією дочіркою у всій системі.
Скотт Кларенбах

1
@Scott Я згоден з усіма іншими, ваше керування складністю та структурою коду неправильно, щоб це зробити. Цей вид жорсткої муфти є поганим дизайнерським рішенням і збирається заплутати. @SimeVidas ваш чудовий некроман :)
Raynos

Відповіді:


163

У 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 досягає того ж, але має кращі показники!

У ES6 можна просто використовувати myFunction.name.

Примітка. Остерігайтеся, що деякі міні-міні JS можуть скинути назви функцій, щоб краще стискатись; Вам може знадобитися налаштувати їх налаштування, щоб уникнути цього.


Хоча це не відповіло на запитання достатньо для мене (через анонімні функції), воно дало мені ідею зробити це: fun.toString().substr(0, 100)чого для моїх потреб буде достатньо, щоб знайти цю функцію. Тож дякую за натхнення!
Кемпбелн

3
Так myFunction.name, найкраще робити в ES6.
Влад А Іонеску

15
У чому сенс, myFunction.nameякщо ви введете ім’я функції? перемагає призначення магічних змінних.
adi518

2
@ adi518: не обов’язково .. припустимо, у вас є якийсь масив функцій, і ви повторюєте його, використовуючи для / foreach .. тоді arr[index].nameтакож буде працювати :)
Debasish Chowdhury

2
@ adi518 Це не марно. Якщо я перейменую функцію в моєму IDE, myFunction.nameтакож буде оновлено. Звичайно, intelliJ підтримує перейменування ідентифікаторів у рядках, але це працює набагато менш послідовно і мовчки застаріє, якщо хтось забуде поставити галочку під назвою "Пошук у рядках". myFunction.nameє дещо кращим вибором, ніж жорстке кодування рядка.
byxor

141

ES6 (натхненний відповіддю посланого халіма нижче):

myFunction.name

Пояснення на MDN . Станом на 2015 рік працює у nodejs та всіх основних браузерах, крім IE.

Примітка. На пов'язаних функціях це дасть " bound <originalName>". Якщо ви хочете отримати оригінальну назву, вам доведеться зняти "прив'язку".


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 (), яка б тут містила пробіл, тому ви можете опустити його.


Як відповідь на оригінальне запитання, я б відмовився від спадок паразитів і пішов би на деякі більш традиційні моделі дизайну OOP. Я написав TidBits.OoJs, щоб зручно писати код OOP в JavaScript з набором функцій, що імітують C ++ (ще не завершено, але в основному).

З коментарів я бачу, що ви хочете уникнути передачі інформаційних parentпотреб його конструктору. Я мушу визнати, що традиційні моделі дизайну не врятують вас від цього, оскільки, як правило, вважається хорошою справою зробити ваші залежності очевидними та нав'язаними.

Я б також запропонував відмовитися від анонімних функцій. Вони роблять налагодження і профілюють PITA, тому що все відображається як "анонімна функція", і немає користі для них, про які я знаю.


3
Приємний RegEx! Ось тест на ефективність, який показує, що ваш код у багатьох випадках найшвидший. Взагалі, здається, що RegEx в середньому б'є нативної JS приблизно в 2 рази. jsperf.com/get-function-name/2
Beejor

@Tomasz Привіт, дякую, що вказав на це. Я цього не знав. Зв'язана функція - це фактично нова функція, яка охоплює вашу оригінальну функцію. Стандарт емаскрипту § 19.2.3.2 визначає, що назва нової функції має бути "прив'язане" + originalName. Метод toString також не працюватиме на зв'язаних функціях ... Вам доведеться викреслити прив’язане слово.

Який сенс у myFunction.name, якщо ви введете ім’я функції? перемагає призначення магічних змінних.
Боржовський

Зауважте, що якщо ви використовуєте React Native, це може не працювати належним чином. У мене є RN-проект, над яким я працюю з версією 36 expo, і .nameвластивість методу компонентів відображалася як рядок "значення", незалежно від того, як він називався. Як не дивно, під час тестування в додатку expo це було чудово, однак, як тільки складений у реальну програму, він вийде з ладу.
Енді Корман

64

те, що ви робите, - це присвоєння неназваній функції змінній. вам, ймовірно, потрібно замість цього висловлення функції ( http://kangax.github.com/nfe/ ).

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

однак я не впевнений, скільки це крос-браузер; є проблема з IE6, яка змушує витікати ім'я функції до зовнішньої області. Крім того, аргументи.callee є видалено застарілими і можуть призвести до помилок при використанні strict mode.


1
Це не працює на старих версіях браузера Safari.
Анубхав Гупта

17

Будь-яка 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
Перші console.logжурнали викликів windowабо Objectдля мене залежно від того, де я його запускаю, ні Person.
Bart S

Ви впевнені, що використовували "новий"? Інакше він не може працювати.
roland

14

Це виглядає як найглупіше, що я писав у своєму житті, але це смішно: 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();

Результат:
Chrome:
введіть тут опис зображення

Fitefox:
введіть тут опис зображення

Це рішення створювалося лише для розваги ! Не використовуйте його для реальних проектів. Це не залежить від специфікації ES, це залежить лише від реалізації браузера. Після наступного оновлення chrome / firefox / safari воно може бути порушено.
Більше того, немає помилок (га) обробки - якщо dбуде більше довжини стека - ви отримаєте помилку;
Для шаблону повідомлень про помилки інших підробників - ви отримаєте помилку;
Він повинен працювати для класів ES6 ( .split('.').pop()), але ви можете отримати ерор;


13

Це може допомогти вам:

function foo() { bar(); }

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

запущений foo () виведе "foo" або невизначений, якщо ви дзвоните з анонімної функції.

Він також працює з конструкторами, і в цьому випадку він виводить ім'я виклику конструктора (наприклад, "Foo").

Більше інформації тут: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/Caller

Вони стверджують, що це нестандартно, але також, що він підтримується всіма основними браузерами: Firefox, Safari, Chrome, Opera та IE.


Недоступно в суворому режимі ... ммм
Ka

8

Ви не можете. Функції не мають імен відповідно до стандарту (хоча mozilla має такий атрибут) - вони можуть бути призначені лише змінним з іменами.

Також ваш коментар:

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

знаходиться всередині функції my.namespace.myFunc.getFn

Що ви можете зробити, це повернути конструктор об'єкта, створеного новим

Так ви могли сказати

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

1
мені це потрібно всередині функції, тому що я передаю його по ланцюжку спадкування, і я пропустив ці речі для стислості.
Скотт Кларенбах

1
"це" буде екземпляром, який викликав функцію. Тож якщо ви 'це' всередині батьківської функції, то ви отримаєте екземпляр функції, яка його викликала. Це все, що вам знадобиться - не впевнений, навіщо вам теж потрібне ім'я
plodder

1
добре, що в батьківській програмі я тоді можу отримати доступ до інших функцій за домовленістю, наприклад, ns [child] [схема] або ns [child] [dbService]. Без цього мені доводиться жорстко кодувати ці посилання в кожному дитячому класі.
Скотт Кларенбах

1
мій випадок використання для пошуку імені - це полегшити побудову повідомлень console.error, які вказують, звідки прийшло повідомлення, не повертаючись до дампа стека. наприклад console.error ({'помилка': {self.name reference} + "помилка з вхідним параметром"). Набагато простіше вирізати / вставити повідомлення про помилки, які повторюються в декількох функціях, якщо вам не доведеться щоразу зупиняти та змінювати текст у них. У моєму випадку я повертаю помилки з обіцянками з декількох викликів обіцянок - приємно знати, що обіцянка / функція породила помилку.
Скотт

7

Ви можете використовувати це для браузерів, які підтримують Error.stack (не майже всі, напевно)

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

звичайно, це для поточної функції, але ви розумієте.

щасливі слині, поки ви кодуєте


4

Ви можете використовувати nameвластивість, щоб отримати ім'я функції, якщо ви не використовуєте анонімну функцію

Наприклад:

var Person = function Person () {
  this.someMethod = function () {};
};

Person.prototype.getSomeMethodName = function () {
  return this.someMethod.name;
};

var p = new Person();
// will return "", because someMethod is assigned with anonymous function
console.log(p.getSomeMethodName());

тепер спробуємо з названою функцією

var Person = function Person () {
  this.someMethod = function someMethod() {};
};

тепер ви можете використовувати

// will return "someMethod"
p.getSomeMethodName()

4

Ви можете використовувати назву конструктора на зразок:

{your_function}.prototype.constructor.name

цей код просто повертає ім'я методу.


3

як частина, як ECMAScript 6ви можете використовувати метод Function.name

function doSomething() {}

alert(doSomething.name); // alerts "doSomething"

У ReactJS вам потрібно додати це: console.log (this.doSomething.name);
Bitclaw

2
це поза функцією
Скотт,

3

Ви можете використовуватиFunction.name :

У більшості реалізацій JavaScript, коли ви маєте посилання вашого конструктора в області застосування, ви можете отримати його ім'я рядка з його властивості імені (наприклад, Function.name або Object.constructor.name

Ви можете використовуватиFunction.callee :

Нативний arguments.callerметод застарів, але більшість браузерів підтримує Function.caller, що повертає фактичний об'єкт, що викликає (його код коду): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ Функція / виклик? Redirectlocale = en-US & redirectslug = JavaScript% 2FReference% 2FGlobal_Objects% 2FFunction% 2Fcaller

Ви можете створити карту джерела :

Якщо вам потрібен підпис буквальної функції ("ім'я"), а не сам об'єкт, можливо, вам доведеться вдатися до чогось трохи більш налаштованого, наприклад, до створення посилань на масив значень рядка API, які вам потрібно доступ часто. Ви можете зіставити їх разом, використовуючи Object.keys()масив рядків, або переглянути бібліотеку Mozilla у вихідних картах на GitHub для великих проектів: https://github.com/mozilla/source-map


2

Я знаю, що це старе питання, але останнім часом я стикаюся з подібною проблемою, намагаючись прикрасити деякі методи React Component для налагодження. Як люди вже сказали, arguments.callerі arguments.calleeзаборонено в суворому режимі, який, ймовірно, включений за замовчуванням у вашій трансляції React. Ви можете або відключити його, або мені вдалося придумати ще один хак, оскільки в React всі функції класу названі, ви можете це зробити:

Component.prototype.componentWillMount = function componentWillMount() {
    console.log('Callee name: ', this.__proto__.constructor.toString().substr(0,30));
...
}

1

Це працювало для мене.

function AbstractDomainClass() {
    this.className = function() {
        if (!this.$className) {
            var className = this.constructor.toString();
            className = className.substr('function '.length);
            className = className.substr(0, className.indexOf('('));
            this.$className = className;
        }
        return this.$className;
    }
}

Код тесту:

var obj = new AbstractDomainClass();
expect(obj.className()).toBe('AbstractDomainClass');

1

У мене була подібна проблема, і я вирішив її так:

Function.prototype.myname = function() {
   return this.toString()
       .substr( 0, this.toString().indexOf( "(" ) )
       .replace( "function ", "" ); 
}

Цей код зручніше реалізує одну відповідь, яку я вже читав тут у верхній частині цієї дискусії. Тепер у мене є функція-член, яка отримує ім'я будь-якого об’єкта функції. Ось повний сценарій ...

<script language="javascript" TYPE="text/javascript">

    Function.prototype.myname = function() { 
        return this.toString()
            .substr( 0, this.toString().indexOf( "(" ) )
            .replace("function ", "" ); 
    }
    function call_this( _fn ) { document.write( _fn.myname() ); }
    function _yeaaahhh() { /* do something */ }
    call_this( _yeaaahhh ); 

</script>

1

Якщо я зрозумів, що ви хочете зробити, це те, що я роблю всередині конструктора функцій.

if (!(this instanceof arguments.callee)) {
    throw "ReferenceError: " + arguments.callee.name + " is not defined";
}

2
Примітка:
Застарелий

0

Це буде працювати в ES5, ES6, усіх браузерах та функціях суворого режиму.

Ось як це виглядає з названою функцією.

(function myName() {
  console.log(new Error().stack.split(/\r\n|\r|\n/g)[1].trim());
})();
at myName (<anonymous>:2:15)

Ось як це виглядає з анонімною функцією.

(() => {
  console.log(new Error().stack.split(/\r\n|\r|\n/g)[1].trim());
})();
at <anonymous>:2:15

Це дає різні результати для браузера. Chrome: at myName (<anonymous>:2:15)Firefox:@debugger eval code:3:3
jkdev

(функція myName () {console.log (нова помилка (). stack.split (/ \ r \ n | \ r | \ n / g) [1] .trim (). split ("") [1]) ;}) ();
Зібрі

-2

дивіться тут: http://www.tek-tips.com/viewthread.cfm?qid=1209619

arguments.callee.toString();

здається, підходить для ваших потреб.


3
argument.callee.toString () просто повертає джерело функції.
Скотт Кларенбах

2
Не дозволено: властивості 'caller', 'callee' та 'argument' можуть бути недоступні для функцій суворого режиму або об'єктів аргументів для викликів до них
Ерік Ходонський,

-2

ви можете використовувати Error.stack для відстеження імені функції та точного положення, де ви перебуваєте в ній.

Дивіться stacktrace.js


-3

Простий спосіб отримати ім'я функції з fuction, який ви працюєте.

function x(){alert(this.name)};x()


1
дає мені посібник
Тамір Даніелі

1
Будьте в курсі змісту thisйого в основному може бути будь-що. спробуйте(function(){console.log(this.name);}).bind({name: "put basically anything here even an object maybe a 1TB string maybe a class definition"})()
Вален
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.