Bagaimana cara mendapatkan nama fungsi dari dalam fungsi itu?

263

Bagaimana saya bisa mengakses nama fungsi dari dalam fungsi itu?

// 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();
Scott Klarenbach
sumber
baik, di induk, saya kemudian dapat mengakses fungsi lain dengan konvensi, seperti ns [child] [schema] atau ns [child] [dbService]. Tanpanya, saya harus membuat hard code referensi ini di setiap kelas anak.
Scott Klarenbach
mengapa tidak hanya meneruskan fungsi anak sebagai argumen ke orang tua? var parent = new ns.parent (ini);
plodder
karena ada lusinan pencarian seperti itu, dan lusinan anak-anak. Itulah saat ini yang saya lakukan, tetapi itu sama setiap waktu dan akan sempurna jika logika duplikat itu hanya dapat ditempatkan di dalam induk, berdasarkan fungsi yang diturunkan.
Scott Klarenbach
lihat, itu bukan fungsi anak yang saya inginkan, ini adalah konvensi penamaan yang digunakan, karena konvensi penamaan itu dapat digunakan untuk memuat fungsi-fungsi lain yang saat ini tidak didefinisikan pada objek anak, tetapi terkait dengan anak tersebut di seluruh sistem.
Scott Klarenbach
1
@Scott Saya setuju dengan orang lain, Anda mengatur kompleksitas dan struktur kode Anda salah karena harus melakukan ini. Hard-coupling semacam ini adalah keputusan desain yang buruk dan akan membuat kekacauan. @SimeVidas ahli nujum besar Anda :)
Raynos

Jawaban:

163

Dalam ES5, hal terbaik untuk dilakukan adalah:

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

Penggunaan Function.callertidak standar. Function.callerdan arguments.calleekeduanya dilarang dalam mode ketat.

Sunting: jawaban berbasis regex nus di bawah ini mencapai hal yang sama, tetapi memiliki kinerja yang lebih baik!

Di ES6, Anda bisa menggunakannya myFunction.name.

Catatan: Berhati-hatilah bahwa beberapa minifiers JS mungkin membuang nama fungsi, untuk mengompres lebih baik; Anda mungkin perlu mengubah pengaturannya untuk menghindarinya.

Vlad A Ionescu
sumber
Meskipun ini tidak menjawab pertanyaan yang cukup bagi saya (karena fungsi anonim), itu memberi saya ide untuk melakukan ini: fun.toString().substr(0, 100)yang untuk kebutuhan saya akan cukup untuk menemukan fungsi yang dimaksud. Jadi, terima kasih atas inspirasinya!
Campbeln
3
Yup myFunction.nameadalah hal terbaik untuk dilakukan di ES6.
Vlad A Ionescu
15
Apa gunanya myFunction.namejika Anda mengetik nama fungsi? mengalahkan tujuan variabel sihir.
adi518
2
@ adi518: belum tentu .. seandainya Anda memiliki berbagai fungsi dan Anda beralih di atasnya, menggunakan untuk / foreach .. kemudian arr[index].namejuga akan bekerja :)
Debasish Chowdhury
2
@ adi518 Tidak sia-sia. Jika saya mengganti nama fungsi dalam IDE saya, myFunction.namejuga akan diperbarui. Tentu saja, IntelliJ mendukung pengidentifikasian ulang string dalam string, tetapi itu bekerja jauh lebih konsisten dan diam-diam akan menjadi usang jika seseorang lupa untuk mencentang opsi "search in strings". myFunction.nameadalah pilihan yang sedikit lebih baik daripada hardcoding string.
byxor
141

ES6 (terinspirasi oleh jawaban sendy halim di bawah):

myFunction.name

Penjelasan tentang MDN . Pada 2015 bekerja di nodejs dan semua browser utama kecuali IE.

Catatan: Pada fungsi terikat ini akan menghasilkan " bound <originalName>". Anda harus menghapus "terikat" jika Anda ingin mendapatkan nama aslinya.


ES5 (terinspirasi oleh jawaban Vlad):

Jika Anda memiliki referensi ke fungsi, Anda dapat melakukan:

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
}
  • Saya belum menjalankan pengujian unit tentang hal ini, atau perbedaan implementasi yang terverifikasi, tetapi pada prinsipnya itu harus bekerja, jika tidak memberikan komentar.
  • Catatan: tidak akan berfungsi pada fungsi terikat
  • Catatan: itu callerdan calleedianggap sudah usang.

[1] Saya memasukkannya di sini karena legal dan sering kali alat penyorotan sintaksis gagal memperhitungkan ruang putih antara nama fungsi dan tanda kurung. Di sisi lain, saya tidak mengetahui implementasi .toString () yang akan menyertakan ruang putih di sini, jadi itu sebabnya Anda bisa menghilangkannya.


Sebagai jawaban untuk pertanyaan awal, saya akan meninggalkan warisan parasit dan mencari beberapa pola desain OOP yang lebih tradisional. Saya menulis TidBits.OoJs untuk menulis kode OOP dalam JavaScript dengan nyaman dengan fitur yang meniru C ++ (belum lengkap, tetapi kebanyakan).

Saya melihat dari komentar bahwa Anda ingin menghindari menyampaikan informasi parentkepada konstruktornya. Saya harus mengakui bahwa pola desain tradisional tidak akan menyelamatkan Anda dari yang itu, karena umumnya dianggap hal yang baik untuk membuat ketergantungan Anda jelas dan ditegakkan.

Saya juga menyarankan untuk menjauhi fungsi anonim. Mereka hanya membuat debug dan membuat profil PITA karena semuanya hanya muncul sebagai "fungsi anonim", dan tidak ada manfaatnya bagi mereka yang saya sadari.


sumber
3
RegEx yang bagus! Berikut ini adalah tes kinerja yang menunjukkan bahwa kode Anda adalah yang tercepat dalam banyak kasus. Secara umum tampaknya RegEx mengalahkan JS asli sekitar 2x kecepatan di sini rata-rata. jsperf.com/get-function-name/2
Beejor
@ Thomas Hi, terima kasih telah menunjukkan itu. Saya tidak tahu itu. Fungsi terikat sebenarnya adalah fungsi baru yang membungkus fungsi asli Anda. Standar ecmascript § 19.2.3.2 mendefinisikan bahwa nama fungsi baru harus "terikat" + nama asli. Metode toString tidak akan berfungsi pada fungsi terikat ... Anda harus menghapus kata terikat.
Apa gunanya myFunction.name jika Anda mengetikkan nama fungsi? mengalahkan tujuan variabel sihir.
Borjovsky
Perhatikan bahwa jika Anda menggunakan React Native ini mungkin tidak berfungsi dengan baik. Saya punya proyek RN yang saya kerjakan dengan expo versi 36 dan .nameproperti metode komponen muncul sebagai string "nilai", tidak peduli apa namanya. Cukup aneh, saat menguji dalam aplikasi expo itu baik-baik saja, namun, setelah dikompilasi menjadi aplikasi nyata, itu akan crash secara diam-diam.
Andy Corman
64

apa yang Anda lakukan adalah menetapkan fungsi tanpa nama ke variabel. Anda mungkin perlu menamai ekspresi fungsi sebagai gantinya ( http://kangax.github.com/nfe/ ).

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

namun saya tidak yakin berapa banyak browser lintas itu; ada masalah dengan IE6 yang membuat nama Anda berfungsi bocor ke lingkup luar. juga, argumen.callee agak usang dan akan menghasilkan kesalahan jika Anda menggunakan strict mode.

wildcard
sumber
1
Ini tidak berfungsi pada browser Safari versi lama.
Anubhav Gupta
17

Apa pun constructormemperlihatkan properti name, yang merupakan nama fungsi. Anda mengakses constructorvia instance (menggunakan new) atau 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
roland
sumber
6
console.logLog panggilan pertama windowatau Objectuntuk saya tergantung di mana saya menjalankannya, bukan Person.
Bart S
Apakah Anda yakin menggunakan instantiated "baru"? Kalau tidak, itu tidak akan berhasil.
roland
14

Sepertinya hal yang paling bodoh, yang saya tulis dalam hidup saya, tapi itu lucu: 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- kedalaman tumpukan. 0- kembalikan nama fungsi ini, 1- orang tua, dll.;
[0 + d]- hanya untuk memahami - apa yang terjadi;
firefoxMatch- bekerja untuk safari, tetapi saya benar-benar memiliki sedikit waktu untuk pengujian, karena pemilik mac telah kembali setelah merokok, dan mengusir saya: '(

Pengujian:

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

gluttony();

Hasil:
Chrome:
masukkan deskripsi gambar di sini

Fitefox:
masukkan deskripsi gambar di sini

Solusi ini dibuat hanya untuk bersenang-senang ! Jangan menggunakannya untuk proyek nyata. Itu tidak tergantung pada spesifikasi ES, itu hanya tergantung pada realisasi browser. Setelah pembaruan chrome / firefox / safari berikutnya mungkin rusak.
Lebih dari itu tidak ada kesalahan (ha) pemrosesan - jika dakan lebih dari panjang tumpukan - Anda akan mendapatkan kesalahan;
Untuk pola pesan kesalahan wrowsers lainnya - Anda akan mendapatkan kesalahan;
Ini harus bekerja untuk kelas ES6 ( .split('.').pop()), tetapi Anda masih bisa mendapatkan kesalahan;

pengguna3335966
sumber
13

Ini mungkin bekerja untuk Anda:

function foo() { bar(); }

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

menjalankan foo () akan menampilkan "foo" atau tidak terdefinisi jika Anda menelepon dari fungsi anonim.

Ia bekerja dengan konstruktor juga, dalam hal ini akan menampilkan nama konstruktor panggilan (misalnya "Foo").

Info lebih lanjut di sini: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/Caller

Mereka mengklaim itu tidak standar, tetapi juga didukung oleh semua browser utama: Firefox, Safari, Chrome, Opera dan IE.

Jacob Mouka
sumber
Tidak tersedia dalam mode ketat ... mmm
Ka Mok
8

Kamu tidak bisa Fungsi tidak memiliki nama sesuai dengan standar (meskipun mozilla memiliki atribut seperti itu) - mereka hanya dapat ditugaskan ke variabel dengan nama.

Juga komentar Anda:

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

ada di dalam fungsi my.namespace.myFunc.getFn

Yang bisa Anda lakukan adalah mengembalikan konstruktor dari objek yang dibuat oleh yang baru

Jadi bisa dibilang begitu

var obj = new my.namespace.myFunc();
console.info(obj.constructor); //my.namespace.myFunc
tukang plodder
sumber
1
saya membutuhkannya di dalam func karena saya melewatkannya di rantai pewarisan dan saya menghilangkan hal itu untuk singkatnya.
Scott Klarenbach
1
'this' akan menjadi instance yang memanggil fungsi. Jadi jika Anda 'ini' di dalam fungsi induk yang akan memberi Anda contoh dari fungsi yang memanggilnya. Itu semua yang Anda butuhkan - tidak yakin mengapa Anda membutuhkan nama juga
plodder
1
baik, di induk, saya kemudian dapat mengakses fungsi lain dengan konvensi, seperti ns [child] [schema] atau ns [child] [dbService]. Tanpanya, saya harus membuat hard code referensi ini di setiap kelas anak.
Scott Klarenbach
1
kasus penggunaan saya untuk menemukan nama adalah untuk membuatnya lebih mudah untuk membangun pesan console.error yang memberitahu dari mana pesan itu berasal tanpa harus kembali ke tumpukan sampah. misalnya console.error ({'error': {referensi self.name} + "" kesalahan dengan parameter input "). Menjadi jauh lebih mudah untuk memotong / menempel pesan kesalahan yang berulang dalam banyak fungsi jika Anda tidak harus berhenti dan mengubah teks di dalamnya setiap kali. Dalam kasus saya, saya mengembalikan kesalahan janji dari beberapa panggilan janji - senang mengetahui apa janji / fungsi yang menghasilkan kesalahan.
Scott
7

Anda dapat menggunakan ini, untuk browser yang mendukung Error.stack (tidak hampir semua, mungkin)

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

tentu saja ini untuk fungsi saat ini, tetapi Anda mendapatkan ide.

senang ngiler sambil kode

no_ripcord
sumber
4

Anda dapat menggunakan nameproperti untuk mendapatkan nama fungsi, kecuali jika Anda menggunakan fungsi anonim

Sebagai contoh:

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

sekarang mari kita coba dengan fungsi bernama

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

sekarang kamu bisa menggunakannya

// will return "someMethod"
p.getSomeMethodName()
sendy halim
sumber
4

Anda dapat menggunakan nama konstruktor seperti:

{your_function}.prototype.constructor.name

kode ini hanya mengembalikan nama metode.

muhamad zolfaghari
sumber
3

sebagai bagian karena ECMAScript 6Anda dapat menggunakan metode Function.name

function doSomething() {}

alert(doSomething.name); // alerts "doSomething"
pleerock
sumber
Di ReactJS Anda perlu menambahkan ini: console.log (this.doSomething.name);
Bitclaw
2
itu di luar fungsi
Scott
3

Anda bisa menggunakanFunction.name :

Di sebagian besar implementasi JavaScript, setelah Anda memiliki referensi konstruktor di lingkup, Anda bisa mendapatkan nama stringnya dari properti namanya (mis. Function.name, atau Object.constructor.name

Anda bisa menggunakanFunction.callee :

arguments.callerMetode asli sudah tidak digunakan lagi, tetapi sebagian besar browser mendukung Function.caller, yang akan mengembalikan objek pemanggilan yang sebenarnya (isi kodenya): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ Fungsi / pemanggil? Redirectlocale = id-AS & redirectslug = JavaScript% 2Fileferensi% 2FGlobal_Objects% 2Fungsi% 2Fcaller

Anda dapat membuat peta sumber :

Jika yang Anda butuhkan adalah tanda tangan fungsi literal ("nama" itu) dan bukan objek itu sendiri, Anda mungkin harus menggunakan sesuatu yang sedikit lebih dikustomisasi, seperti membuat referensi array dari nilai string API yang Anda perlu akses sering. Anda dapat memetakannya bersama-sama menggunakan Object.keys()dan rangkaian string Anda, atau melihat ke perpustakaan peta sumber Mozilla di GitHub, untuk proyek yang lebih besar: https://github.com/mozilla/source-map

Benny
sumber
2

Saya tahu ini adalah pertanyaan lama tetapi akhir-akhir ini saya telah menghadapi beberapa masalah serupa ketika mencoba untuk menghias beberapa metode Komponen React, untuk keperluan debugging. Seperti yang sudah dikatakan orang, arguments.callerdan arguments.calleedilarang dalam mode ketat yang mungkin diaktifkan secara default di React transpiling Anda. Anda dapat menonaktifkannya, atau saya dapat membuat hack lain, karena dalam Bereaksi semua fungsi kelas diberi nama, Anda sebenarnya dapat melakukan ini:

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

Ini berhasil untuk saya.

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

Kode uji:

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

Saya memiliki masalah yang sama dan saya menyelesaikannya sebagai berikut:

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

Kode ini mengimplementasikan, dengan cara yang lebih nyaman, satu jawaban yang sudah saya baca di sini di bagian atas diskusi ini. Sekarang saya memiliki fungsi anggota mengambil nama objek fungsi apa pun. Ini skrip lengkapnya ...

<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>
Sandro Rosa
sumber
1

Jika saya mengerti apa yang ingin Anda lakukan, ini adalah apa yang saya lakukan di dalam konstruktor fungsi.

if (!(this instanceof arguments.callee)) {
    throw "ReferenceError: " + arguments.callee.name + " is not defined";
}
Zibri
sumber
2
Catatan: Tidak digunakan lagi - developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Helcio R. Macedo Pandelo
0

Ini akan bekerja di ES5, ES6, semua browser dan fungsi mode ketat.

Begini tampilannya dengan fungsi bernama.

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

Begini tampilannya dengan fungsi anonim.

(() => {
  console.log(new Error().stack.split(/\r\n|\r|\n/g)[1].trim());
})();
at <anonymous>:2:15
Eric
sumber
Ini menghasilkan hasil yang berbeda per browser. Chrome: at myName (<anonymous>:2:15)Firefox:@debugger eval code:3:3
jkdev
(function myName () {console.log (Kesalahan baru (). stack.split (/ \ r \ n | \ r | \ n / g) [1] .trim (). split ("") [1]) ;}) ();
Zibri
-2

lihat di sini: http://www.tek-tips.com/viewthread.cfm?qid=1209619

arguments.callee.toString();

tampaknya tepat untuk kebutuhan Anda.

Manuel Bitto
sumber
3
arguments.callee.toString () baru saja mengembalikan sumber fungsi.
Scott Klarenbach
2
Tidak diizinkan: properti 'penelepon', 'callee', dan 'argumen' tidak dapat diakses pada fungsi mode ketat atau objek argumen untuk panggilan ke mereka
Eric Hodonsky
-2

Anda dapat menggunakan Error.stack untuk melacak nama fungsi dan posisi persis di mana Anda berada di dalamnya.

Lihat stacktrace.js

kofifus
sumber
-3

Cara mudah untuk mendapatkan nama fungsi dari dalam fungsi yang Anda jalankan.

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

Dave Brown
sumber
1
beri saya GUID
Tamir Daniely
1
Waspadai isi thisitu pada dasarnya bisa apa saja. coba(function(){console.log(this.name);}).bind({name: "put basically anything here even an object maybe a 1TB string maybe a class definition"})()
Valen