İşlev adını bu işlevden nasıl alabilirim?


263

Bu işlevin içinden bir işlev adına nasıl erişebilirim?

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

daha sonra, ebeveynte, ns [child] [şema] veya ns [child] [dbService] gibi kurallarla diğer işlevlere erişebilirim. Onsuz, her alt sınıfta bu referansları kodlamak zorundayım.
Scott Klarenbach

neden çocuk işlevini ebeveyn olarak bir argüman olarak iletmiyorsunuz? var ebeveyn = yeni ns.parent (bu);
plodder

çünkü onlarca arama ve onlarca çocuk var. Şu anda yaptığım şey bu, ancak her seferinde aynıdır ve türetilmiş fonksiyona dayanarak, bu yinelenen mantık sadece bir kez ebeveynin içine yerleştirilebiliyorsa mükemmel olurdu.
Scott Klarenbach

bakın, bu istediğim alt işlev değil, kullanılan adlandırma kuralı, çünkü adlandırma kuralı şu anda alt nesnede tanımlanmayan, ancak sistemdeki bu çocukla ilişkili diğer işlevleri yüklemek için kullanılabilir.
Scott Klarenbach

1
@Scott Başkalarına katılıyorum, karmaşıklığınızı ve kod yapınızı yönetmeniz gerekmiyor. Bu tür bir zor bağlantı kötü bir tasarım kararıdır ve ortalığı kirletecektir. @SimeVidas your a great necromancer :)
Raynos

Yanıtlar:


163

ES5'te yapılacak en iyi şey:

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

Kullanımı Function.callerstandart değildir. Function.callerve arguments.calleekatı modda her ikisi de yasaktır.

Düzenleme: nus'un aşağıdaki regex tabanlı cevabı aynı şeyi başarıyor, ancak daha iyi bir performansa sahip!

ES6'da kullanabilirsiniz myFunction.name.

Not: Bazı JS minifüslerinin daha iyi sıkıştırmak için işlev adlarını atabileceğine dikkat edin; Bundan kaçınmak için ayarlarını değiştirmeniz gerekebilir.


Bu benim için soruyu yeterince anlayamasa da (anonim işlevler nedeniyle), bana bunu yapma fikri verdi: fun.toString().substr(0, 100)hangi ihtiyaçlarım için söz konusu işlevi bulmak için yeterli olacak. İlham için teşekkürler!
Campbeln

3
Yup myFunction.name, ES6'da yapılacak en iyi şey.
Vlad A Ionescu

15
myFunction.nameİşlev adını yazmanızın anlamı nedir ? sihirli değişkenlerin amacını yener.
adi518

2
@ adi518: mutlaka .. Eğer fonksiyonların herhangi dizi yoktur herhalde, değil ve sizin için kullanarak, üzerinde yineleme / foreach ozaman arr[index].nameolacak da iş :)
Debasish Chowdhury

2
@ adi518 Yararsız değil. Eğer IDE içindeki fonksiyonu yeniden adlandırıyorsam myFunction.nameda güncellenecektir. Elbette intelliJ, dizelerdeki tanımlayıcıların yeniden adlandırılmasını destekler, ancak bu "dizelerde arama" seçeneğini işaretlemeyi unutursa, bu çok daha az tutarlı çalışır ve sessizce modası geçmiş olur. myFunction.namedizeyi kodlamaktan biraz daha iyi bir seçimdir.
byxor

141

ES6 (aşağıdaki sendy halim'in cevabından esinlenmiştir):

myFunction.name

MDN hakkında açıklama . 2015 yılı itibariyle nodejs ve IE dışındaki tüm önemli tarayıcılarda çalışmaktadır.

Not: Bağlı fonksiyonlarda bu " bound <originalName>" verecektir . Eğer orijinal adı almak istiyorsanız "bağlı" soymak zorunda kalacak.


ES5 (Vlad'ın cevabından esinlenerek):

Eğer işleve referans varsa, şunları yapabilirsiniz:

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
}
  • Bu konuda birim testleri yapmadım veya uygulama farklılıklarını doğruladım, ancak prensip olarak yorum bırakmazsa çalışmalıdır.
  • Not: bağlı işlevler üzerinde çalışmaz
  • Not: bu callerve calleekullanımdan kaldırılmış olarak kabul edilir.

[1] Buraya ekliyorum çünkü yasal ve genellikle yeterli sözdizimi vurgulama araçları işlev adı ve parantez arasındaki boşluğu hesaba katmıyor. Öte yandan, .toString () 'in burada beyaz boşluk içerecek herhangi bir uygulamasının farkında değilim, bu yüzden onu atlayabilirsiniz.


Orijinal soruya bir cevap olarak, parazitik kalıtım bırakacak ve daha geleneksel OOP tasarım kalıpları için gideceğim. Ben rahatça C ++ taklit bir özellik kümesi ile JavaScript'te OOP kodu yazmak için bir TidBits.OoJs yazdım (henüz tam değil, ama çoğunlukla).

Yorumlardan, bilgi parentgereksinimlerini yapıcısına iletmekten kaçınmak istediğinizi görüyorum . Geleneksel tasarım modellerinin sizi bunlardan kurtaramayacağını itiraf etmeliyim, çünkü bağımlılıklarınızı açık ve güçlendirmek genellikle iyi bir şeydir.

Ayrıca anonim işlevlerden uzaklaşmayı da öneririm. Sadece hata ayıklama ve bir PITA profilleme yaparlar, çünkü her şey sadece "anonim işlev" olarak görünür ve farkında olduğum hiçbir faydası yoktur.


3
Güzel RegEx! Kodunuzun birçok durumda en hızlı olduğunu gösteren bir performans testi. Genel olarak, RegEx'in yerel JS'yi ortalama olarak yaklaşık 2x hıza çıkardığı görülmektedir. jsperf.com/get-function-name/2
Beejor

@Tomasz Merhaba, belirttiğin için teşekkürler. Bunu bilmiyordum. Bağlı işlev aslında orijinal işlevinizi saran yeni bir işlevdir. Ecmascript standardı § 19.2.3.2, yeni işlevin adının "bağlı" + orijinalAdı olacağını tanımlar. ToString yöntemi, bağlı işlevler üzerinde de çalışmaz ... Bağlı sözcüğü çıkarmanız gerekir.

İşlev adını yazarsanız myFunction.name içindeki nokta nedir? sihirli değişkenlerin amacını yener.
Borjovsky

React Native kullanıyorsanız, bunun düzgün çalışmayabileceğini unutmayın. Expo sürüm 36 ile üzerinde çalıştığım bir RN projesi var ve .namene denir olursa olsun, bir bileşen yöntemi özelliği dize "değer" olarak gösteriliyordu. Garip bir şekilde, expo uygulamasında test ederken iyiydi, ancak bir kez gerçek bir uygulamaya derlendi, sessizce çökecekti.
Andy Corman

64

yaptığınız şey bir değişkene isimsiz işlev atamaktır. bunun yerine büyük olasılıkla adlandırılmış işlev ifadesine ihtiyacınız vardır ( http://kangax.github.com/nfe/ ).

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

Ancak ne kadar çapraz tarayıcı olduğundan emin değilim; IE6'da işlevin adını dış kapsama sızıntı yapan bir sorun var. Ayrıca, arguments.callee bir tür kullanımdan kaldırılmıştır ve kullanıyorsanız hataya neden olur strict mode.


1
Bu, Safari tarayıcısının eski sürümlerinde çalışmaz.
Anubhav Gupta

17

Herhangi biri, işlev adı olan constructorbir özelliği gösterir name. Erişebilir constructorörneğine yoluyla (kullanarak new) ya da bir 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
İlk console.logarama , nerede çalıştığım yere bağlı olarak windowya da Objectbenim için değil Person.
Bart S

"Yeni" kullanarak başlattığınızdan emin misiniz? Aksi takdirde çalışamaz.
roland

14

Hayatımda yazdığım en aptalca şey gibi görünüyor, ama komik: 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- istif derinliği. 0- bu işlev adını döndürür, 1- ebeveyn, vb .;
[0 + d]- sadece anlamak için - ne olur;
firefoxMatch- safari için çalışıyor, ancak test için gerçekten biraz zamanım vardı, çünkü mac'un sahibi sigara içtikten sonra geri döndü ve beni uzaklaştırdı: '(

Test yapmak:

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

gluttony();

Sonuç:
Krom:
resim açıklamasını buraya girin

Fitefox:
resim açıklamasını buraya girin

Bu çözüm sadece eğlence için yaratıldı ! Gerçek projeler için kullanmayın. ES spesifikasyonuna bağlı değildir, sadece tarayıcı gerçekleştirilmesine bağlıdır. Bir sonraki krom / firefox / safari güncellemesinden sonra bozulabilir.
Bundan daha fazla hata (ha) işleme yoktur - dyığın uzunluğundan daha fazla olacaksa - bir hata alırsınız;
Diğer wrowsers hatasının messaage modeli için - bir hata alırsınız;
ES6 sınıfları ( .split('.').pop()) için çalışması gerekir , ancak eşik bir erorr alabilirsiniz;


13

Bu sizin için işe yarayabilir:

function foo() { bar(); }

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

anonim bir işlevden çağırırsanız, foo () öğesi "foo" veya tanımsız çıktı verir.

Yapıcılar ile de çalışır, bu durumda çağıran yapıcı (örneğin, "Foo") adını çıkarır.

Daha fazla bilgi burada: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/Caller

Standart olmadığını iddia ediyorlar, aynı zamanda tüm büyük tarayıcılar tarafından desteklendiğini iddia ediyorlar: Firefox, Safari, Chrome, Opera ve IE.


Katı modda mevcut değil ... mmm
Ka Mok

8

Yapamazsın. Fonksiyonların standarda göre isimleri yoktur (mozilla'nın böyle bir niteliği vardır) - sadece isimleri olan değişkenlere atanabilirler.

Ayrıca yorumunuz:

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

my.namespace.myFunc.getFn fonksiyonunun içinde

Yapabileceğiniz şey, yeni tarafından oluşturulan bir nesnenin yapıcısını döndürmektir.

Yani söyleyebilirsin

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

1
fon içinde ihtiyacım var çünkü kalıtım zincirinden geçiyorum ve kısalık için bu şeyleri atladım.
Scott Klarenbach

1
'this' işlevi başlatan örnek olacaktır. Yani eğer üst fonksiyonun içinde 'bunu' yaparsanız, bu fonksiyonun adını veren fonksiyonun örneğini verecektir. İhtiyacınız olan tek şey bu - neden isme ihtiyacınız olduğuna emin değilim
plodder

1
daha sonra, ebeveynte, ns [child] [şema] veya ns [child] [dbService] gibi kurallarla diğer işlevlere erişebilirim. Onsuz, her alt sınıfta bu referansları kodlamak zorundayım.
Scott Klarenbach

1
adı bulmak için benim durumum, yığın dökümü geri dönmek zorunda kalmadan iletinin nereden geldiğini bildiren console.error iletileri oluşturmak için kolaylaştırmaktır. Örneğin, console.error ({'error': {self.name reference} + "giriş parametresinde hata"). Her seferinde içindeki metni durdurmak ve değiştirmek zorunda kalmazsanız, birden fazla işlevde tekrarlanan hata mesajlarını kesmek / yapıştırmak çok daha kolay hale gelir. Benim durumumda, hangi söz / fonksiyon hatayı oluşturduğunu bilmek güzel - birden fazla söz çağrılarından söz hataları döndürüyorum.
Scott

7

Bunu Error.stack'ı destekleyen tarayıcılar için kullanabilirsiniz (neredeyse hepsi değil, muhtemelen)

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

Tabii ki bu şu anki fonksiyon için, ama fikri anladınız.

kodlarken mutlu drooling


4

nameAnonim bir işlev kullanmıyorsanız, işlev adını almak için özelliği kullanabilirsiniz

Örneğin:

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

şimdi adlandırılmış işlev ile deneyelim

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

şimdi kullanabilirsiniz

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

4

Yapıcı adını aşağıdaki gibi kullanabilirsiniz:

{your_function}.prototype.constructor.name

bu kod bir yöntemin adını döndürür.


3

Parça olarak Function.name yöntemini ECMAScript 6kullanabilirsiniz

function doSomething() {}

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

ReactJS'de şunu eklemeniz gerekir: console.log (this.doSomething.name);
Bitclaw

2
fonksiyonun dışında
Scott

3

Şunları kullanabilirsinizFunction.name :

JavaScript uygulamalarının çoğunda, kurucunuzun kapsamını referans olarak aldıktan sonra, dize adını name özelliğinden (örn. Function.name veya Object.constructor.name) alabilirsiniz.

Şunları kullanabilirsinizFunction.callee :

Yerel arguments.calleryöntem kullanımdan kaldırıldı, ancak çoğu tarayıcı Function.caller, gerçek çağırma nesnesini (kod gövdesini) döndürecek şekilde destekliyor : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ Fonksiyon / arayan? redirectlocale = tr-tR & redirectslug = JavaScript% 2FReference% 2FGlobal_Objects% 2FFunction% 2Fcaller

Bir kaynak harita oluşturabilirsiniz :

İhtiyacınız olan şey nesnenin kendisi değil değişmez işlev imzası ("adı") ise, ihtiyacınız olan API dizesi değerlerinin dizi başvurusunu oluşturmak gibi biraz daha özelleştirilmiş bir şeye başvurmanız gerekebilir. sık erişim. Daha Object.keys()büyük projeler için bunları ve dizelerinizi kullanarak birlikte eşleyebilir veya GitHub'daki Mozilla'nın kaynak haritaları kitaplığına bakabilirsiniz: https://github.com/mozilla/source-map


2

Bunun eski bir soru olduğunu biliyorum ama son zamanlarda bazı React Component'ın yöntemlerini hata ayıklama amacıyla süslemeye çalışırken benzer bir sorunla karşı karşıya kaldım. Gibi insanlar zaten söyledi, arguments.callerve arguments.calleemuhtemelen tepki transpiling varsayılan olarak etkindir katı modda yasaktır. Ya devre dışı bırakabilirsiniz, ya da başka bir hack ile geldim, çünkü React'ta tüm sınıf işlevleri adlandırılmış, aslında bunu yapabilirsiniz:

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

1

Bu benim için çalıştı.

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

Test kodu:

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

1

Benzer bir sorun yaşadım ve şu şekilde çözdüm:

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

Bu kod, burada daha rahat bir şekilde, bu tartışmanın başında zaten okuduğum bir yanıtı uygular. Şimdi herhangi bir işlev nesnesinin adını almak bir üye işlevi var. İşte senaryonun tamamı ...

<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>


0

Bu, ES5, ES6, tüm tarayıcılarda ve katı mod işlevlerinde çalışır.

Adlandırılmış bir işlevle nasıl göründüğü aşağıda açıklanmıştır.

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

Anonim bir işlevle nasıl göründüğü aşağıda açıklanmıştır.

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

Bu, tarayıcı başına farklı sonuçlar üretir. Chrome: at myName (<anonymous>:2:15)Firefox:@debugger eval code:3:3
jkdev

(işlev myName () {console.log (yeni Hata (). stack.split (/ \ r \ n | \ r | \ n / g) [1] .trim (). bölünmüş ("") [1]) ;}) ();
Zibri


-2

fonksiyon adını ve içinde bulunduğunuz yerin tam konumunu izlemek için Error.stack komutunu kullanabilirsiniz.

Bkz. Stacktrace.js


-3

Çalıştırdığınız işlevin içinden işlev adını almanın kolay yolu.

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


1
bana bir GUID verir
Tamir Daniely

1
içeriğinin farkında olmak thistemelde herhangi bir şey olabilir. deneyin(function(){console.log(this.name);}).bind({name: "put basically anything here even an object maybe a 1TB string maybe a class definition"})()
Valen
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.